isync-1.5.1/0000755000175000001440000000000014764113614006407 5isync-1.5.1/configure0000755000175000001440000062626014764113017010247 #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.72 for isync 1.5.1. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2023 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 ${ZSH_VERSION+y} && (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 e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 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 \${ZSH_VERSION+y} && (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 e in #( e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; 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 case e in #( e) exitcode=1; echo positional parameters were not saved. ;; esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || 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" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else case e in #( e) as_have_required=no ;; esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi ;; esac fi 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi ;; esac 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=`printf "%s\n" "$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 || printf "%s\n" 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 case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac 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 case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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 || printf "%s\n" 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 ' t clear :clear 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" || { printf "%s\n" "$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 } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated 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='isync' PACKAGE_TARNAME='isync' PACKAGE_VERSION='1.5.1' PACKAGE_STRING='isync 1.5.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS RELEASE_DATE KEYCHAIN_LIBS with_mdconvert_FALSE with_mdconvert_TRUE Z_LIBS DB_LIBS SASL_LIBS SSL_LIBS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG SOCK_LIBS PERL am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE am__xargs_n am__rm_f_notfound AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V CSCOPE ETAGS CTAGS 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_os target_vendor target_cpu target host_os host_vendor host_cpu host build_os build_vendor build_cpu build 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 am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_maintainer_mode enable_dependency_tracking with_ssl with_sasl with_zlib with_macos_keychain ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR' # 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 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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$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" ;; *) printf "%s\n" "$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 || printf "%s\n" 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 isync 1.5.1 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/isync] --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] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of isync 1.5.1:";; 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") --disable-maintainer-mode disable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-ssl=PATH where to look for SSL [detect] --with-sasl=PATH where to look for SASL [detect] --with-zlib use zlib [detect] --with-macos-keychain Support macOS keychain Some influential environment variables: 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 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 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 the package provider. _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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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 configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. 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 printf "%s\n" "$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 isync configure 1.5.1 generated by GNU Autoconf 2.72 Copyright (C) 2023 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 conftest.beam 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\"" printf "%s\n" "$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 printf "%s\n" "$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 case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac 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_run LINENO # ---------------------- # Try to run 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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else case e in #( e) printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status ;; esac 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_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.beam 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\"" printf "%s\n" "$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 printf "%s\n" "$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 case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac 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_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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 (void); below. */ #include #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 (void); /* 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 (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >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 isync $as_me 1.5.1, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw _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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "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=`printf "%s\n" "$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=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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 printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$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 printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (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; } /* C89 style stringification. */ #define noexpand_stringify(a) #a const char *stringified = noexpand_stringify(arbitrary+token=sequence); /* C89 style token pasting. Exercises some of the corner cases that e.g. old MSVC gets wrong, but not very hard. */ #define noexpand_concat(a,b) a##b #define expand_concat(a,b) noexpand_concat(a,b) extern int vA; extern int vbee; #define aye A #define bee B int *pvA = &expand_concat(v,aye); int *pvbee = &noexpand_concat(v,bee); /* 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 do not provoke an error unfortunately, instead are silently treated as an "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 is necessary to write \x00 == 0 to get something that is 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 **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' /* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif // See if C++-style comments work. #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Work around memory leak warnings. free (ia); // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' /* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="compile missing install-sh config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; esac 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. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" 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,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 printf "%s\n" "$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=`printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run '${MAKE-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 ac_config_headers="$ac_config_headers autodefs.h" # 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 printf %s "checking target system type... " >&6; } if test ${ac_cv_target+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5 fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 printf "%s\n" "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- am__api_version='1.17' # 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. 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 ;; esac fi if test ${ac_cv_path_install+y}; 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$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' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether sleep supports fractional seconds" >&5 printf %s "checking whether sleep supports fractional seconds... " >&6; } if test ${am_cv_sleep_fractional_seconds+y} then : printf %s "(cached) " >&6 else case e in #( e) if sleep 0.001 2>/dev/null then : am_cv_sleep_fractional_seconds=yes else case e in #( e) am_cv_sleep_fractional_seconds=no ;; esac fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_sleep_fractional_seconds" >&5 printf "%s\n" "$am_cv_sleep_fractional_seconds" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking filesystem timestamp resolution" >&5 printf %s "checking filesystem timestamp resolution... " >&6; } if test ${am_cv_filesystem_timestamp_resolution+y} then : printf %s "(cached) " >&6 else case e in #( e) # Default to the worst case. am_cv_filesystem_timestamp_resolution=2 # Only try to go finer than 1 sec if sleep can do it. # Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work, # - 1 sec is not much of a win compared to 2 sec, and # - it takes 2 seconds to perform the test whether 1 sec works. # # Instead, just use the default 2s on platforms that have 1s resolution, # accept the extra 1s delay when using $sleep in the Automake tests, in # exchange for not incurring the 2s delay for running the test for all # packages. # am_try_resolutions= if test "$am_cv_sleep_fractional_seconds" = yes; then # Even a millisecond often causes a bunch of false positives, # so just try a hundredth of a second. The time saved between .001 and # .01 is not terribly consequential. am_try_resolutions="0.01 0.1 $am_try_resolutions" fi # In order to catch current-generation FAT out, we must *modify* files # that already exist; the *creation* timestamp is finer. Use names # that make ls -t sort them differently when they have equal # timestamps than when they have distinct timestamps, keeping # in mind that ls -t prints the *newest* file first. rm -f conftest.ts? : > conftest.ts1 : > conftest.ts2 : > conftest.ts3 # Make sure ls -t actually works. Do 'set' in a subshell so we don't # clobber the current shell's arguments. (Outer-level square brackets # are removed by m4; they're present so that m4 does not expand # ; be careful, easy to get confused.) if ( set X `ls -t conftest.ts[12]` && { test "$*" != "X conftest.ts1 conftest.ts2" || test "$*" != "X conftest.ts2 conftest.ts1"; } ); then :; else # 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". printf "%s\n" ""Bad output from ls -t: \"`ls -t conftest.ts[12]`\""" >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "ls -t produces unexpected output. Make sure there is not a broken ls alias in your environment. See 'config.log' for more details" "$LINENO" 5; } fi for am_try_res in $am_try_resolutions; do # Any one fine-grained sleep might happen to cross the boundary # between two values of a coarser actual resolution, but if we do # two fine-grained sleeps in a row, at least one of them will fall # entirely within a coarse interval. echo alpha > conftest.ts1 sleep $am_try_res echo beta > conftest.ts2 sleep $am_try_res echo gamma > conftest.ts3 # We assume that 'ls -t' will make use of high-resolution # timestamps if the operating system supports them at all. if (set X `ls -t conftest.ts?` && test "$2" = conftest.ts3 && test "$3" = conftest.ts2 && test "$4" = conftest.ts1); then # # Ok, ls -t worked. If we're at a resolution of 1 second, we're done, # because we don't need to test make. make_ok=true if test $am_try_res != 1; then # But if we've succeeded so far with a subsecond resolution, we # have one more thing to check: make. It can happen that # everything else supports the subsecond mtimes, but make doesn't; # notably on macOS, which ships make 3.81 from 2006 (the last one # released under GPLv2). https://bugs.gnu.org/68808 # # We test $MAKE if it is defined in the environment, else "make". # It might get overridden later, but our hope is that in practice # it does not matter: it is the system "make" which is (by far) # the most likely to be broken, whereas if the user overrides it, # probably they did so with a better, or at least not worse, make. # https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html # # Create a Makefile (real tab character here): rm -f conftest.mk echo 'conftest.ts1: conftest.ts2' >conftest.mk echo ' touch conftest.ts2' >>conftest.mk # # Now, running # touch conftest.ts1; touch conftest.ts2; make # should touch ts1 because ts2 is newer. This could happen by luck, # but most often, it will fail if make's support is insufficient. So # test for several consecutive successes. # # (We reuse conftest.ts[12] because we still want to modify existing # files, not create new ones, per above.) n=0 make=${MAKE-make} until test $n -eq 3; do echo one > conftest.ts1 sleep $am_try_res echo two > conftest.ts2 # ts2 should now be newer than ts1 if $make -f conftest.mk | grep 'up to date' >/dev/null; then make_ok=false break # out of $n loop fi n=`expr $n + 1` done fi # if $make_ok; then # Everything we know to check worked out, so call this resolution good. am_cv_filesystem_timestamp_resolution=$am_try_res break # out of $am_try_res loop fi # Otherwise, we'll go on to check the next resolution. fi done rm -f conftest.ts? # (end _am_filesystem_timestamp_resolution) ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_filesystem_timestamp_resolution" >&5 printf "%s\n" "$am_cv_filesystem_timestamp_resolution" >&6; } # This check should not be cached, as it may vary across builds of # different projects. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 printf %s "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). am_build_env_is_sane=no am_has_slept=no rm -f conftest.file for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file if ( 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 test "$2" = conftest.file ); then am_build_env_is_sane=yes break fi # Just in case. sleep "$am_cv_filesystem_timestamp_resolution" am_has_slept=yes done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_build_env_is_sane" >&5 printf "%s\n" "$am_build_env_is_sane" >&6; } if test "$am_build_env_is_sane" = no; then as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi # 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 test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1 then : else case e in #( e) ( sleep "$am_cv_filesystem_timestamp_resolution" ) & am_sleep_pid=$! ;; esac 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=`printf "%s\n" "$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 MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 ('*'coreutils) '* | \ *'BusyBox '* | \ '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 ;; esac fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use plain mkdir -p, # in the hope it doesn't have the bugs of ancient mkdir. MKDIR_P='mkdir -p' fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 ;; esac fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 AM_DEFAULT_VERBOSITY=1 # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else case e in #( e) if printf "%s\n" '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 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } AM_BACKSLASH='\' am__rm_f_notfound= if (rm -f && rm -fr && rm -rf) 2>/dev/null then : else case e in #( e) am__rm_f_notfound='""' ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking xargs -n works" >&5 printf %s "checking xargs -n works... " >&6; } if test ${am_cv_xargs_n_works+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "`echo 1 2 3 | xargs -n2 echo`" = "1 2 3" then : am_cv_xargs_n_works=yes else case e in #( e) am_cv_xargs_n_works=no ;; esac fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_xargs_n_works" >&5 printf "%s\n" "$am_cv_xargs_n_works" >&6; } if test "$am_cv_xargs_n_works" = yes then : am__xargs_n='xargs -n' else case e in #( e) am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "" "$am__xargs_n_arg"; done; }' ;; esac fi 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='isync' VERSION='1.5.1' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h # 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 -' # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi if test -z "$ETAGS"; then ETAGS=etags fi if test -z "$CSCOPE"; then CSCOPE=cscope fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 printf %s "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test ${enable_maintainer_mode+y} then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else case e in #( e) USE_MAINTAINER_MODE=yes ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 printf "%s\n" "$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 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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 fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$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. printf "%s\n" "$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 -version; 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\"" printf "%s\n" "$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 printf "%s\n" "$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 (void) { ; 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$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+y} && 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 case e in #( e) ac_file='' ;; esac fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$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 case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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 case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$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; } ;; esac fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$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 (void) { FILE *f = fopen ("conftest.out", "w"); if (!f) return 1; 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$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 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use '--host'. See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext \ conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$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 case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$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; } ;; esac fi rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else case e in #( e) ac_compiler_gnu=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else case e in #( e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; 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 ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _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 conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 ;; esac 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 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$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 DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 printf "%s\n" "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test ${enable_dependency_tracking+y} 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 depcc="$CC" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CC_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 thus: # 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 ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 printf "%s\n" "$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 if test "$GCC" = yes; then warnings=" -Wall -Wextra -Wshadow -Wcast-qual -Wformat=2 -Wformat-signedness -Wformat-nonliteral -Wstrict-prototypes -Wno-overlength-strings " CFLAGS="$CFLAGS -pipe -std=c11 -pedantic $(echo $warnings)" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void fkt(void) { int a = 42; // c99 comment for (int i = 0; i < a; i++) {} // declaration inside for() int b; // declaration after code } // c11 anonymous structs/unions struct base { int a; }; union deriv { struct base gen; struct { int a; int b; }; }; _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) as_fn_error $? "compiler does not support required C11 features" "$LINENO" 5 ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PERL+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$PERL"; then ac_cv_prog_PERL="$PERL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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_PERL="perl" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi PERL=$ac_cv_prog_PERL if test -n "$PERL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 printf "%s\n" "$PERL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$PERL" = "x"; then as_fn_error $? "perl not found" "$LINENO" 5 fi need_perl=5.14 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether perl is recent enough" >&5 printf %s "checking whether perl is recent enough... " >&6; } if test ${ob_cv_perl_ver+y} then : printf %s "(cached) " >&6 else case e in #( e) if $PERL -e "use v$need_perl;" 2> /dev/null; then ob_cv_perl_ver=yes else ob_cv_perl_ver=no fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ob_cv_perl_ver" >&5 printf "%s\n" "$ob_cv_perl_ver" >&6; } if test "x$ob_cv_perl_ver" = "xno"; then as_fn_error $? "perl is too old, need v$need_perl" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether strftime supports %z" >&5 printf %s "checking whether strftime supports %z... " >&6; } if test ${ob_cv_strftime_z+y} then : printf %s "(cached) " >&6 else case e in #( e) if test "$cross_compiling" = yes then : ob_cv_strftime_z="yes (assumed)" else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(void) { time_t t = 0; char buf[32]; strftime(buf, sizeof(buf), "%z", localtime(&t)); return !(buf[0] == '+' || buf[0] == '-'); } _ACEOF if ac_fn_c_try_run "$LINENO" then : ob_cv_strftime_z=yes else case e in #( e) ob_cv_strftime_z=no ;; esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ob_cv_strftime_z" >&5 printf "%s\n" "$ob_cv_strftime_z" >&6; } if test "x$ob_cv_strftime_z" = x"no"; then as_fn_error $? "libc lacks necessary feature" "$LINENO" 5 fi ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" if test "x$ac_cv_header_poll_h" = xyes then : printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" if test "x$ac_cv_header_sys_select_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf" if test "x$ac_cv_func_vasprintf" = xyes then : printf "%s\n" "#define HAVE_VASPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strnlen" "ac_cv_func_strnlen" if test "x$ac_cv_func_strnlen" = xyes then : printf "%s\n" "#define HAVE_STRNLEN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memrchr" "ac_cv_func_memrchr" if test "x$ac_cv_func_memrchr" = xyes then : printf "%s\n" "#define HAVE_MEMRCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm" if test "x$ac_cv_func_timegm" = xyes then : printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fwrite_unlocked" "ac_cv_func_fwrite_unlocked" if test "x$ac_cv_func_fwrite_unlocked" = xyes then : printf "%s\n" "#define HAVE_FWRITE_UNLOCKED 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 printf %s "checking for socket in -lsocket... " >&6; } if test ${ac_cv_lib_socket_socket+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char socket (void); int main (void) { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_socket=yes else case e in #( e) ac_cv_lib_socket_socket=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 printf "%s\n" "$ac_cv_lib_socket_socket" >&6; } if test "x$ac_cv_lib_socket_socket" = xyes then : SOCK_LIBS="-lsocket" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnsl" >&5 printf %s "checking for inet_ntoa in -lnsl... " >&6; } if test ${ac_cv_lib_nsl_inet_ntoa+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (void); int main (void) { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_nsl_inet_ntoa=yes else case e in #( e) ac_cv_lib_nsl_inet_ntoa=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_inet_ntoa" >&5 printf "%s\n" "$ac_cv_lib_nsl_inet_ntoa" >&6; } if test "x$ac_cv_lib_nsl_inet_ntoa" = xyes then : SOCK_LIBS="$SOCK_LIBS -lnsl" fi have_ipv6=true sav_LIBS=$LIBS LIBS="$LIBS $SOCK_LIBS" for ac_func in getaddrinfo inet_ntop do : as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_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 `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1 _ACEOF else case e in #( e) have_ipv6=false ;; esac fi done LIBS=$sav_LIBS if $have_ipv6; then printf "%s\n" "#define HAVE_IPV6 1" >>confdefs.h fi have_ssl_paths= # Check whether --with-ssl was given. if test ${with_ssl+y} then : withval=$with_ssl; ob_cv_with_ssl=$withval fi if test "x$ob_cv_with_ssl" != xno; then case $ob_cv_with_ssl in ""|yes) 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 printf "%s\n" "$PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else case e in #( e) 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 printf "%s\n" "$ac_pt_PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 printf %s "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi if test "x$PKG_CONFIG" != "x" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking OpenSSL presence with pkg-config" >&5 printf %s "checking OpenSSL presence with pkg-config... " >&6; } if $PKG_CONFIG --exists openssl; then SSL_LIBS=`$PKG_CONFIG --libs-only-l openssl` SSL_LDFLAGS=`$PKG_CONFIG --libs-only-L openssl` SSL_CPPFLAGS=`$PKG_CONFIG --cflags-only-I openssl` have_ssl_paths=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found" >&5 printf "%s\n" "found" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } fi fi ;; *) SSL_LDFLAGS=-L$ob_cv_with_ssl/lib$libsuff SSL_CPPFLAGS=-I$ob_cv_with_ssl/include ;; esac if test -z "$have_ssl_paths"; then sav_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $SSL_LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else case e in #( e) 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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char dlopen (void); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else case e in #( e) ac_cv_lib_dl_dlopen=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : LIBDL=-ldl fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for X509_cmp in -lcrypto" >&5 printf %s "checking for X509_cmp in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_X509_cmp+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char X509_cmp (void); int main (void) { return X509_cmp (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_X509_cmp=yes else case e in #( e) ac_cv_lib_crypto_X509_cmp=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_X509_cmp" >&5 printf "%s\n" "$ac_cv_lib_crypto_X509_cmp" >&6; } if test "x$ac_cv_lib_crypto_X509_cmp" = xyes then : LIBCRYPTO=-lcrypto fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_connect in -lssl" >&5 printf %s "checking for SSL_connect in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_connect+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char SSL_connect (void); int main (void) { return SSL_connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ssl_SSL_connect=yes else case e in #( e) ac_cv_lib_ssl_SSL_connect=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_connect" >&5 printf "%s\n" "$ac_cv_lib_ssl_SSL_connect" >&6; } if test "x$ac_cv_lib_ssl_SSL_connect" = xyes then : SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes fi LDFLAGS=$sav_LDFLAGS fi sav_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_ssl_h" = xyes then : else case e in #( e) have_ssl_paths= ;; esac fi CPPFLAGS=$sav_CPPFLAGS if test -z "$have_ssl_paths"; then if test -n "$ob_cv_with_ssl"; then as_fn_error $? "OpenSSL libs and/or includes were not found where specified" "$LINENO" 5 fi else printf "%s\n" "#define HAVE_LIBSSL 1" >>confdefs.h CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" LDFLAGS="$LDFLAGS $SSL_LDFLAGS" fi fi have_sasl_paths= # Check whether --with-sasl was given. if test ${with_sasl+y} then : withval=$with_sasl; ob_cv_with_sasl=$withval fi if test "x$ob_cv_with_sasl" != xno; then case $ob_cv_with_sasl in ""|yes) ;; *) SASL_LDFLAGS=-L$ob_cv_with_sasl/lib$libsuff SASL_CPPFLAGS=-I$ob_cv_with_sasl/include ;; esac if test -z "$have_sasl_paths"; then sav_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $SASL_LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sasl_client_init in -lsasl2" >&5 printf %s "checking for sasl_client_init in -lsasl2... " >&6; } if test ${ac_cv_lib_sasl2_sasl_client_init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsasl2 $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char sasl_client_init (void); int main (void) { return sasl_client_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sasl2_sasl_client_init=yes else case e in #( e) ac_cv_lib_sasl2_sasl_client_init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl2_sasl_client_init" >&5 printf "%s\n" "$ac_cv_lib_sasl2_sasl_client_init" >&6; } if test "x$ac_cv_lib_sasl2_sasl_client_init" = xyes then : SASL_LIBS="-lsasl2" have_sasl_paths=yes fi LDFLAGS=$sav_LDFLAGS fi sav_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS" ac_fn_c_check_header_compile "$LINENO" "sasl/sasl.h" "ac_cv_header_sasl_sasl_h" "$ac_includes_default" if test "x$ac_cv_header_sasl_sasl_h" = xyes then : else case e in #( e) have_sasl_paths= ;; esac fi CPPFLAGS=$sav_CPPFLAGS if test -z "$have_sasl_paths"; then if test -n "$ob_cv_with_sasl"; then as_fn_error $? "SASL libs and/or includes were not found where specified" "$LINENO" 5 fi else printf "%s\n" "#define HAVE_LIBSASL 1" >>confdefs.h CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS" LDFLAGS="$LDFLAGS $SASL_LDFLAGS" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Berkeley DB >= 4.1" >&5 printf %s "checking for Berkeley DB >= 4.1... " >&6; } if test ${ac_cv_berkdb4+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_berkdb4=no sav_LIBS=$LIBS LIBS="$LIBS -ldb" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { DB *db; db_create(&db, 0, 0); db->truncate(db, 0, 0, 0); db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_berkdb4=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$sav_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_berkdb4" >&5 printf "%s\n" "$ac_cv_berkdb4" >&6; } if test "x$ac_cv_berkdb4" = xyes; then DB_LIBS="-ldb" printf "%s\n" "#define USE_DB 1" >>confdefs.h fi have_zlib= # Check whether --with-zlib was given. if test ${with_zlib+y} then : withval=$with_zlib; ob_cv_with_zlib=$withval fi if test "x$ob_cv_with_zlib" != xno; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 printf %s "checking for deflate in -lz... " >&6; } if test ${ac_cv_lib_z_deflate+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lz $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. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char deflate (void); int main (void) { return deflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_deflate=yes else case e in #( e) ac_cv_lib_z_deflate=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 printf "%s\n" "$ac_cv_lib_z_deflate" >&6; } if test "x$ac_cv_lib_z_deflate" = xyes then : ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = xyes then : have_zlib=1 Z_LIBS="-lz" printf "%s\n" "#define HAVE_LIBZ 1" >>confdefs.h fi fi fi if test "x$ac_cv_berkdb4" = xyes; then with_mdconvert_TRUE= with_mdconvert_FALSE='#' else with_mdconvert_TRUE='#' with_mdconvert_FALSE= fi case $target_os in darwin*) darwin=yes ;; *) darwin=no ;; esac # Check whether --with-macos-keychain was given. if test ${with_macos_keychain+y} then : withval=$with_macos_keychain; have_macos_keychain=$withval else case e in #( e) have_macos_keychain=$darwin ;; esac fi if test "x$have_macos_keychain" != xno; then if test $darwin = no; then as_fn_error $? "Cannot use macOS Keychain outside macOS." "$LINENO" 5 fi have_macos_keychain=yes printf "%s\n" "#define HAVE_MACOS_KEYCHAIN 1" >>confdefs.h KEYCHAIN_LIBS="-Wl,-framework,Security,-framework,CoreFoundation" fi RELEASE_DATE=`date -r $0 +%F` ac_config_files="$ac_config_files Makefile src/Makefile src/mbsync.1 src/mdconvert.1 isync.spec" 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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+y} || &/ 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$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=`printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 printf %s "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; esac 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 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 "${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 "${with_mdconvert_TRUE}" && test -z "${with_mdconvert_FALSE}"; then as_fn_error $? "conditional \"with_mdconvert\" 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" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$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 ${ZSH_VERSION+y} && (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 e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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 case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac 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 case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac 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 || printf "%s\n" 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 # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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=`printf "%s\n" "$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 || printf "%s\n" 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_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated 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 isync $as_me 1.5.1, which was generated by GNU Autoconf 2.72. 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 the package provider." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ isync config.status 1.5.1 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" Copyright (C) 2023 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 ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$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=`printf "%s\n" "$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 ) printf "%s\n" "$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 \printf "%s\n" "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 printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _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 "autodefs.h") CONFIG_HEADERS="$CONFIG_HEADERS autodefs.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/mbsync.1") CONFIG_FILES="$CONFIG_FILES src/mbsync.1" ;; "src/mdconvert.1") CONFIG_FILES="$CONFIG_FILES src/mdconvert.1" ;; "isync.spec") CONFIG_FILES="$CONFIG_FILES isync.spec" ;; *) 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+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers test ${CONFIG_COMMANDS+y} || 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=`printf "%s\n" "$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 '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$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 || printf "%s\n" 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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$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"; } && { printf "%s\n" "$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 printf "%s\n" "$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 { printf "%s\n" "/* $configure_input */" >&1 \ && 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$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 printf "%s\n" "/* $configure_input */" >&1 \ && 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 || printf "%s\n" 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) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 printf "%s\n" "$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. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE=\"gmake\" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See 'config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } if test -n "$have_ssl_paths"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SSL" >&5 printf "%s\n" "Using SSL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Not using SSL" >&5 printf "%s\n" "Not using SSL" >&6; } fi if test -n "$have_sasl_paths"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using SASL" >&5 printf "%s\n" "Using SASL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Not using SASL" >&5 printf "%s\n" "Not using SASL" >&6; } fi if test -n "$have_zlib"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using zlib" >&5 printf "%s\n" "Using zlib" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Not using zlib" >&5 printf "%s\n" "Not using zlib" >&6; } fi if test "x$ac_cv_berkdb4" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Berkeley DB" >&5 printf "%s\n" "Using Berkeley DB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Not using Berkeley DB" >&5 printf "%s\n" "Not using Berkeley DB" >&6; } fi if test $darwin = yes; then if test "x$have_macos_keychain" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using macOS Keychain" >&5 printf "%s\n" "Using macOS Keychain" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Not using macOS Keychain" >&5 printf "%s\n" "Not using macOS Keychain" >&6; } fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } isync-1.5.1/ChangeLog0000644000175000001440000000017114764103741010100 This file is empty for a lack of a target audience. Have a look at the NEWS file for a summary of user-visible changes. isync-1.5.1/test-driver0000755000175000001440000001213714764113020010520 #! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2024-06-19.01; # UTC # Copyright (C) 2011-2024 Free Software Foundation, Inc. # # 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 # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <. GNU Automake home page: . General help using GNU software: . END } test_name= # Used for reporting. log_file= # Where to save the output of the test script. trs_file= # Where to save the metadata of the test run. expect_failure=no color_tests=no collect_skipped_logs=yes enable_hard_errors=yes while test $# -gt 0; do case $1 in --help) print_usage; exit $?;; --version) echo "test-driver (GNU Automake) $scriptversion"; exit $?;; --test-name) test_name=$2; shift;; --log-file) log_file=$2; shift;; --trs-file) trs_file=$2; shift;; --color-tests) color_tests=$2; shift;; --collect-skipped-logs) collect_skipped_logs=$2; shift;; --expect-failure) expect_failure=$2; shift;; --enable-hard-errors) enable_hard_errors=$2; shift;; --) shift; break;; -*) usage_error "invalid option: '$1'";; *) break;; esac shift done missing_opts= test x"$test_name" = x && missing_opts="$missing_opts --test-name" test x"$log_file" = x && missing_opts="$missing_opts --log-file" test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" if test x"$missing_opts" != x; then usage_error "the following mandatory options are missing:$missing_opts" fi if test $# -eq 0; then usage_error "missing argument" fi if test $color_tests = yes; then # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. red='' # Red. grn='' # Green. lgn='' # Light green. blu='' # Blue. mgn='' # Magenta. std='' # No color. else red= grn= lgn= blu= mgn= std= fi do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' trap "st=129; $do_exit" 1 trap "st=130; $do_exit" 2 trap "st=141; $do_exit" 13 trap "st=143; $do_exit" 15 # Test script is run here. We create the file first, then append to it, # to ameliorate tests themselves also writing to the log file. Our tests # don't, but others can (automake bug#35762). : >"$log_file" "$@" >>"$log_file" 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=$collect_skipped_logs;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>"$log_file" # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: isync-1.5.1/depcomp0000755000175000001440000005621714764113020007706 #! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2024-06-19.01; # UTC # Copyright (C) 1999-2024 Free Software Foundation, Inc. # 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. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . GNU Automake home page: . General help using GNU software: . EOF exit $? ;; -v | --v*) echo "depcomp (GNU Automake) $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interference from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsolete pre-3.x GCC compilers. ## but also to in-use compilers like IBM xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: isync-1.5.1/TODO0000644000175000001440000000760714405565305007031 f{,data}sync() usage could be optimized by batching the calls. automatically resume upon transient errors, e.g. "connection reset by peer" or timeout after some data was already transmitted. possibly also try to handle Exchange's "glitches" somehow. uidvalidity lock timeout handling would be a good idea. should complain when multiple Channels match the same folders. should complain about nonsensical combinations like Sync Pull + Create Both. propagate folder deletions even when the folders are non-empty. - verify that "most" of the folders in the Channel are still there. - refuse to delete unpropagated messages when trashing on the remote side. - refuse to delete far side if it has unpropagated messages. symmetry? add option to suppress complaints about folders that would need creation (but not deleted ones). add message expiration based on arrival date (message date would be too unreliable). MaxAge; probably mutually exclusive to MaxMessages. add alternative treatments of expired messages. ExpiredMessageMode: Prune (delete messages like now), Keep (just don't sync) and Archive (move to separate folder - ArchiveSuffix, default .archive). add support for event notification callbacks. make it possible to have different mailbox names for far and near side in Patterns. - use far:near for the pattern - supporting names with colons requires and extension of the parser to report which parts of an argument were quoted - this makes Groups mostly useless, as they are mostly a workaround for this function being missing so far - this is needed for move detection, which would work only within one Channel - this supersedes MapInbox add regexp-based mailbox path rewriting to the drivers. user would provide expressions for both directions. every transformation would be immediately verified with the inverse transform. PathDelimiter and Flatten would become special cases of this. add daemon mode. primary goal: keep imap password in memory. also: idling mode. parallel fetching of multiple mailboxes. TLS session resumption becomes interesting then as well. imap_set_msg_flags() & imap_trash_msg(): group commands for efficiency. add streaming from fetching to storing. this is complicated by the IMAP target needing the final size in advance, which we can't know in a single pass when newline translation is necessary. handle custom flags (keywords). this is impeded by there being no Maildir-side standard. make use of IMAP CONDSTORE extension (rfc4551; CHANGEDSINCE FETCH Modifier); make use of IMAP QRESYNC extension (rfc5162) to avoid SEARCH to find vanished messages. make use of IMAP CAPABILITY APPENDLIMIT= extension (rfc7889; fastmail & gmail). this is really useful only for IMAP <=> IMAP syncs: saves FETCH BODY. the message could still become oversized due to conversion. dummy messages resulting from MaxSize should contain a dump of the original message's MIME structure and its (reasonably sized) text parts. don't SELECT boxes unless really needed; in particular not for appending, and in write-only mode not before changes are made. problem: UIDVALIDITY change detection is delayed, significantly complicating matters. some error messages are unhelpful in non-verbose mode due to missing context. possibly use ^[[1m to highlight error messages. consider alternative approach to trashing: instead of the current trash-before- expunge done by mbsync, let MUAs do the trashing (as modern ones typically do). mbsync wouldn't do any trashing by itself, but should track the moves for optimization. additionally, there should be a mode to move trashed messages to the remote store. TrashMode Internal|External, AbsorbRemoteTrash. a yet different approach to trashing is treating the trash like a normal mailbox. however, this implies a huge working set. consider optional use of messages-id (and X-GM-MSGID): - detection of message moves between folders - recovery from loss of sync state, migration from other tools isync-1.5.1/NEWS0000644000175000001440000004743414764104220007033 1.5.1 (2025-03-11) ================== Improvements: - mbsync-get-cert now supports STARTTLS; new option -s - Zero-sized messages from IMAP Stores are now accepted - UIDVALIDITY change recovery is now attempted even if both sides of the Channel are affected - The sync summary at the end is more concise again - Cosmetic improvements to some console output Bug Fixes: - Fixed IMAP INBOX not being properly recognized with some servers - Fixed Maildir INBOX nested into Path not being implicitly listed - Fixed crash when resuming message propagation with MaxMessages - Fixed --list-stores hanging after synchronous error - Fixed --dry-run without --debug-driver not being really dry - Fixed building from pristine git clones - Fixed building from shallow git clones 1.5.0 (2024-08-02) ================== Compatibility Concerns: - The reference point for relative local paths in the configuration file is now the file's containing directory - Maildir Path cannot be nested into Inbox anymore (this was never documented) - Renamed the ReNew/--renew/-N options to Upgrade/--upgrade/-u and Delete/--delete/-d to Gone/--gone/-g - Superseded SSLVersions option with TLSVersions, and disabled TLS v1.0 and v1.1 by default - Renamed SSLType option to TLSType - Placeholders will be now created for messages exceeding MaxSize even if they are flagged on the source side - Placeholder upgrades no longer pull flag updates along unless also requested - New messages which we are about to expunge from the source side are not propaged any more even if the target side would keep them - Tunnel is now consistently assumed to be secure, so some warnings are gone New Features: - Changed default config & state locations to follow the XDG basedir spec; the old locations remain supported - Added support for IMAP mailbox names with non-ASCII characters - Added support for Maildir Paths with suffixes (not ending with a slash) - Made the Channel side to expire with MaxMessages configurable - MaxMessages and MaxSize can be used together now - The unfiltered list of mailboxes in each Store can be printed now - A proper summary is now printed prior to exiting. This includes expunges, which are now included in the progress as well. - Added support for mirroring deletions more accurately; option ExpungeSolo - Added new sync operation 'Old' to retry previously skipped messages - Added --ext-exit option to indicate with the exit code whether Stores were modified - Added --dry-run option Improvements: - Added support for the LITERAL- IMAP extension, which improves upload performance with f.ex. GMail somewhat - Improved error handling when attempting to store too big messages on f.ex. GMail - Malformed messages with incomplete headers will be propagated now - A notice is now emitted if the server does not support race-free Trash - Improved checking for invalid command lines - Options not supported due to the build configuration are still recognized now, to make error messages more helpful - The progress indicator is rate-limited now - Various improvements to the debugging output - Vastly extended the autotest suite Bug Fixes: - Worked around "unexpected EOF" error messages at end of TLS connections; affects f.ex. GMail - Worked around protocol corruption issue with iCloud (mail.me.com) - Fixed missing CAPABILITY command after logging in if the server does not report updated capabilities automatically (affects f.ex. MS Exchange) - Fixed CopyArrivalDate failing on some date strings - Fixed propagation of new messages to non-UIDPLUS servers - Fixed Timeout being ignored by DNS host resolution - Fixed broken Tunnel potentially causing SIGPIPE - Fixed Tunnel leaving behind zombie processes - Fixed expunges not being propagated at all if the first run after they occurred did not include --delete - Fixed MaxMessages being exceeded when only --new was used - Fixed messages being instantly expired despite being important when only --new was used - Trash-ing failures now prevent expunging and cause a non-zero exit code - Fixed placeholders being needlessly trashed - Fixed TrashNewOnly and TrashRemoteNew omitting messages for which only a placeholder was synced - Fixed TrashRemoteNew omitting messages which exceed MaxSize - Fixed TrashRemoteNew not using race-free expunge - Optimized some places with unnecessarily high CPU usage - Fixed unnecessary network usage by non-selective uni-directional syncs when no placeholders are present - Fixed crash when Patterns yields nothing when built with new compilers - Fixed crash when all flag propagations to a mailbox failed - Fixed handling of errors during opening mailboxes - Removed useless "lost track of ... messages" warnings when resuming after an interruption - Fixed many minor bugs in corner cases, mostly when resuming after interruptions 1.4.4 (2021-12-03) ================== Bug Fixes: - Fixed CVE-2021-3657: multiple buffer overflows on excessively large IMAP literals - Fixed CVE-2021-44143: buffer overflow on header-less messages - Fixed crash on certain malformed messages in Maildirs - Fixed somewhat spurious notice about conflicting changes on messages marked as deleted 1.4.3 (2021-07-29) ================== Improvements: - Arbitrarily large GSSAPI authentication tokens are supported now - Symlink loops in Maildirs are detected now 1.4.2 (2021-06-06) ================== Improvements: - INBOX miss-casing in Path setting is tolerated now Bug Fixes: - Fixed CVE-2021-3578: possible remote code execution - Fixed crash on invalid CAPABILITY response code 1.3.6 (2021-06-06) ================== Bug Fixes: - Fixed CVE-2021-3578 1.4.1 (2021-02-21) ================== Bug Fixes: - Fixed CVE-2021-20247: IMAP LIST/LSUB path traversal vulnerability - Fixed UIDVALIDITY change recovery potentially leading to data loss - Fixed spurious "unexpected FETCH response" errors with some servers - Worked around FastMail sending malformed PERMANENTFLAGS 1.3.5 (2021-02-21) ================== Bug Fixes: - Fixed CVE-2021-20247 1.4.0 (2021-02-03) ================== Compatibility Concerns: - The 'isync' compatibility wrapper was removed - A C11 compiler is required for building now - The validity of the config file is checked more strictly now, including: - Appearance of options in unexpected places - The capitalization of INBOX - The new TLSv1.3 flag must be added to SSLVersions if the option is used, unless disabling that version is desired (which is unlikely) - Removed support for the obsolete/insecure SSL v3 - The use of Master/Slave terminology has been deprecated New Features: - The IMAP '$Forwarded' / Maildir 'P' (passed) flag is supported now - Support for configuring a TLS cipher string was added - IMAP mailbox subscriptions are supported now - The IMAP user query can be scripted now - Added built-in support for the macOS Keychain - Messages excluded by MaxSize will now result in placeholders Bug Fixes: - IMAP protocol errors are handled more robustly now - Fixed support for SASL's built-in EXTERNAL mechanism - Improved reliability of synchronization when resuming interrupted runs - Fixed MaxSize being ignored under certain circumstances when only one of New and ReNew was requested - Fixed a network inefficiency occurring with server-side mailboxes that receive new messages only via mbsync 1.3.4 (2021-02-03) ================== Bug Fixes: - Fixed regression in handling NAMESPACE 'INBOX.', introduced in v1.3.2 1.3.3 (2020-08-04) ================== Improvements: - PassCmd supports even bigger XOAUTH2 tokens now Bug Fixes: - Fixed crash on syncing multiple Channels which refer to different Stores which use a common IMAPAccount - Fixed crash on IMAP connection breaking down while using -Dd 1.3.2 (2020-07-08) ================== Improvements: - Increased PassCmd buffer size to accommodate XOAUTH2 tokens Bug Fixes: - Improved SSL error handling - Improved resilience to IMAP server bugs relating to FETCH - Fixed handling of non-uppercase IMAP iNbOx spellings - Fixed timeouts while uploading big messages - Fixed parsing of NIL hierarchy delimiters in IMAP LIST responses - Fixed crash when using Tunnel in an IPv6-enabled build - Fixed libcrypto detection from OpenSSL 1.1+ without pkg-config - Made --debug-crash work with Yama ptrace protection 1.3.1 (2019-05-28) ================== Improvements: - SSL now uses SNI, which for example GMail requires - The perl 5.14 requirement is now made explicit Bug Fixes: - Fixed fallbacks for missing UIDPLUS extension (with e.g. DavMail) - Fixed UIDVALIDITY recovery with really long Message-id headers - Fixed GSSAPI authentication with Kerberos - Fixed support for IMAP servers which do not sort search results (e.g., poczta.o2.pl) - Fixed CopyArrivalDate on platforms without glibc - Fixed useless SASL warnings with certain plugins - Improved OpenBSD support - Fixed a bunch of compiler warnings 1.3.0 (2017-10-01) ================== Compatibility Concerns: - If you have Maildir sub-folders, you need to update the configuration to specify the naming style - get-cert was renamed to mbsync-get-cert to avoid namespace pollution - Attempts to enable SSL v2 now produce warnings. They were already ineffective due to OpenSSL having removed the support a while ago. New Features: - Network timeout handling has been added - Support for proper Maildir++ and a Maildir sub-folder naming style without extra dots have been added - Support for TLS client certificates was added - Support for recovering from baseless UID validity changes was added - The option DisableExtension for working around IMAP (server) bugs was added Improvements: - Opening an IMAP Store is now immediately canceled when the opposite Store failed to open Bug Fixes: - Fixed handling of UIDs upwards of 2^31 - Fixed mailboxes being skipped with certain combinations of Patterns and Path when the same IMAP Store is used in multiple Channels - Pattern INBOX* is not complained about any more if no Path is defined - Trashing messages now also resumes after an interruption, rather than starting from scratch - Fixed spurious "TUID lost" warnings after interruption - Fixed various corner cases when resuming syncing after interruption. The test suite exercises this much more thoroughly now. 1.2.3 (2017-10-01) ================== Improvements: - Enabled TLS 1.1 and 1.2 by default Bug Fixes: - Fixed cross-build with OpenSSL when using pkg-config - Fixed GCC 7 -Wimplicit-fallthrough warnings 1.2.2 (2017-08-05) ================== Improvements: - Added support for OpenSSL 1.1 - IMAP UIDs up to 2^31 are now supported (as opposed to 10^9 before) Bug Fixes: - Fixed spurious reports about decompression errors - Fixed error reporting in both SSL server certificate validation and IMAP COMPRESS support - Fixed use of IMAP LOGIN authentication in some configurations - Fixed handling of IMAP NAMESPACE without hierarchy delimiter - Fixed fallback to IPv4 when the kernel is built without IPv6 support 1.2.1 (2015-11-08) ================== Improvements: - The dependencies on Berkeley DB and zlib (and the features they enable) can be explicitly disabled now - It is now possible to nest a Maildir Store's Path into its Inbox Bug Fixes: - Fixed IPv6-enabled builds crashing upon host name resolution failure - Fixed updating Maildir flags crashing on OpenBSD - Fixed deadlocks with NFS home directories - Fixed SASL authentication with GSS-API - Fixed messages being uploaded again and again with some servers - Fixed duplicate mailboxes with IMAP NAMESPACE "INBOX." - Fixed some problems in the IMAP command submission code - Fixed build with static libdb, libnsl, and libsocket - Fixed -DN not implying -Dn 1.2.0 (2015-04-03) ================== Compatibility concerns: - The 'isync' compatibility wrapper is now deprecated - Excess arguments in the config file are not silently ignored any more - An IMAP Path/NAMESPACE rooted in INBOX won't be handled specially any more (the INBOX. prefix was never stripped). This means that some Master/Slave/Patterns may need adjustment. - The SSL/TLS configuration has been re-designed. SSL is now explicitly enabled or disabled - "use SSL if available" is gone. Notice: Tunnels are assumed to be secure and thus default to no SSL. The use of the system certificate store can be disabled now. - The default output is a lot less verbose now. The meanings of the -V and -D options changed significantly. New Features: - Support for SASL (flexible authentication) has been added - Support for Windows file systems has been added by making a replacement for the colon configurable - Support for compressed data transfer (IMAP COMPRESS) has been added - Folder deletions can be propagated now - The peak memory usage can be limited now Improvements: - The LITERAL+ IMAP extension will not be used any more when uploading big messages, to enable early rejection of big uploads - A dysfunctional Store (e.g., bad password) will now be tried only once, not for every Channel it is used in Bugfixes: - Fixed handling of some exceptional IMAP protocol conditions - Fixed spurious "unhandled SSL error 6" messages - Fixed failure to create Maildir Inbox 1.1.3 (2015-04-03) ================== Bug Fixes: - Fixed bogus "unexpected command continuation request" - Fixed out-of-Path INBOX never being matched by Patterns - Fixed escaping in PassCmd in mbsyncrc.sample 1.1.2 (2015-01-18) ================== Improvements: - Configurations with only TLS 1.1/1.2 are now actually possible - For security reasons, RequireSSL is not ignored anymore even when Tunnel is used - you'll probably need to explicitly disable it - Maildir Stores with only Inbox but no Path are now permitted - IMAP Stores with an explicitly empty Path are now permitted Bug Fixes: - Fixed acceptance of trusted SSL host certificates - Fixed duplication of uploaded messages with some IMAP servers (in particular, seznam.cz) - Fixed some memory management mistakes, some of which would cause crashes - Fixed garbage at end of folder names when using Patterns with some servers - Fixed Patterns misbehaving with certain server-provided namespaces 1.1.1 (2014-06-01) ================== Bug Fixes: - Fixed build on FreeBSD - Several fixes relating to badly handled IMAP responses, including some crashes - Fixed folder handling in isync compatibility wrapper - Added pedantic error handling in some rather unlikely places - Man page fixes - Some cosmetical fixes 1.1.0 (2013-12-18) ================== New Features: - Added support for hierarchical mailboxes in Patterns - Added full support for IMAP pipelining (streaming, parallelization). This is considerably faster especially with high-latency networks. - Added IPv6 support - IMAP password query can be scripted now; option PassCmd - Message arrival dates can be propagated now; option CopyArrivalDate - Added ExpireUnread option Improvements: - Data safety in case of system crashes was improved - Faster and hopefully more reliable support for IMAP servers without the UIDPLUS extension (e.g., M$ Exchange) - More automatic handling of SSL certificates - MaxMessages was made vastly more useful Bug Fixes: (rumored) 1.0.6 (2013-02-20) ================== Bug Fixes: - Fixed CVE-2013-0289: missing SSL subject verification - Fixed 64-bit cleanness 1.0.5 (2012-04-28) ================== Improvements: - In the wrapper, give the implicitly created IMAP Account config the name of the Store Bug Fixes: - Fixed crash if neither Host nor Tunnel are specified - Fixed handling of failure to store messages - Fixed hang after failed start_tls - Fixed memory access error (used memcpy for overlapping regions) 1.0.4 (2008-02-23) ================== - Bug Fixes 1.0.3 (2006-11-03) ================== - Bug Fixes 1.0.2 (2006-02-25) ================== - Bug Fixes 1.0.1 (2005-03-28) ================== - Bug Fixes 1.0.0 (2004-09-15) ================== This is essentially a rewrite. Synchronization state storage concept, configuration and command line changed entirely. But you needn't to worry about the upgrade, as a fully automated migration path is provided, even for users of isync 0.7 and below. Still, you should re-read the manual to be able to take full advantage of the new features: - The supported mailbox types can be freely paired now. A possible application of this is using a local IMAP server to access mailboxes that are not natively supported yet. - Message deletions (expunges) are now propagated both ways, so there is no need for using mutt with maildir_trash any more - Additional trash options added - 'OneToOne' was replaced by something more flexible - Added partial support for IMAP pipelining (streaming, parallelization). This makes flag change propagation much faster - this affects every message that becomes Seen/Read. 0.9.2 (2003-12-07) ================== - Bug Fixes 0.9.1 (2003-05-07) ================== - Bug Fixes 0.9 (2003-05-05) ================ Compatibility Concerns: - -C now creates both local and remote boxes; the new -L and -R create only local/remote ones. New Features: - Added Tunnel directive to allow the user to specify a shell command to run to set up an IMAP connection in place of a TCP socket (e.g., to run over an SSH session) - Added PREAUTH support (useful mostly in conjunction with Tunnel) - Added 'OneToOne' configuration option: ignore any Mailbox specifications and instead pick up all mailboxes from the local MailDir and remote Folder, and map them 1:1 onto each other according to their names Improvements: - Messages marked deleted are not uploaded when we are going to expunge - Locally generated messages are not re-fetched after uploading even if the UIDPLUS extension is not supported by the server - --quiet is now really quiet 0.8 (2002-01-28) ================ Compatibility Concerns: - In order to fix the problem where messages copied from one mailbox to another were not uploaded to the new mailbox, the way Isync stores the UID for each message needed to be changed. As a result, you _MUST_ delete all the messages in the local maildir box before using this version. Otherwise it will upload every message to the server thinking it is a new mail. 0.7 (2001-11-20) ================ New Features: - Added 'MaxMessages' configuration option to allow tracking of only the most recently added message in the local mailbox - Added --create (-C) command line option to force creation of the local maildir-style mailbox if it doesn't already exist 0.6 (2001-10-31) ================ New Features: - Added 'Delete' configuration option to correspond to the -d command line option - Added -a (--all) command line option to synchronize all mailboxes 0.5 (2001-06-13) ================ New Features: - Updated SSL support - Added CRAM authentication support - Added MailDir configuration option to specify the default location of local mailboxes when relative paths are used - Added support for uploading local messages to the IMAP server - Added CopyDeletedTo configuration option to cause isync to move deleted messages to a particular mailbox on the server when they are expunged 0.4 (2000-12-31) ================ New Features: - Added MaxSize configuration option to limit downloading of new messages from the server to less than some threshold Improvements: - More robust --fast option works without using \Recent flags, so the previous problem with multiple accesses killing these flags is no longer a problem - RFC2060 obsoleted RFC822.PEEK; use BODY.PEEK[] instead which does the same job - Stop requesting UID in FETCH when doing UID FETCH (RFC2060 states that it is automatically returned) 0.3 (2000-12-21) ================ New Features: - Added support for building RPMs Bug Fixes: - Fixed failure to clean up temp maildir files when the fetch of a new message failed - Fixed invalid assumption of order of the flags returned by "UID FETCH" 0.2 (2000-12-21) ================ New Features: - Added SSL support 0.1 (2000-12-20) ================ - Initial release isync-1.5.1/isync.spec.in0000644000175000001440000000162613701307132010730 Summary: Utility to synchronize IMAP mailboxes with local maildir folders Name: isync Version: @VERSION@ Release: 1 License: GPL Group: Applications/Internet Source: @PACKAGE@-@VERSION@.tar.gz URL: http://@PACKAGE@.sf.net/ Packager: Oswald Buddenhagen BuildRoot: /var/tmp/%{name}-buildroot %description isync is a command line utility which synchronizes mailboxes; currently Maildir and IMAP4 mailboxes are supported. New messages, message deletions and flag changes can be propagated both ways. It is useful for working in disconnected mode, such as on a laptop or with a non-permanent internet collection (dIMAP). %prep %setup %build %configure %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name} %clean rm -rf $RPM_BUILD_ROOT %files %doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample %{_bindir}/* %{_mandir}/man1/* isync-1.5.1/compile0000755000175000001440000001670514764113020007705 #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2024-06-19.01; # UTC # Copyright (C) 1999-2024 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* | MSYS*) 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/* | msys/*) 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 | *.lo | *.[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 . GNU Automake home page: . General help using GNU software: . EOF exit $? ;; -v | --v*) echo "compile (GNU Automake) $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ clang-cl | *[/\\]clang-cl | clang-cl.exe | *[/\\]clang-cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.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 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: isync-1.5.1/isync.spec0000644000175000001440000000160614764113037010332 Summary: Utility to synchronize IMAP mailboxes with local maildir folders Name: isync Version: 1.5.1 Release: 1 License: GPL Group: Applications/Internet Source: isync-1.5.1.tar.gz URL: http://isync.sf.net/ Packager: Oswald Buddenhagen BuildRoot: /var/tmp/%{name}-buildroot %description isync is a command line utility which synchronizes mailboxes; currently Maildir and IMAP4 mailboxes are supported. New messages, message deletions and flag changes can be propagated both ways. It is useful for working in disconnected mode, such as on a laptop or with a non-permanent internet collection (dIMAP). %prep %setup %build %configure %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name} %clean rm -rf $RPM_BUILD_ROOT %files %doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample %{_bindir}/* %{_mandir}/man1/* isync-1.5.1/install-sh0000755000175000001440000003611514764113020010330 #!/bin/sh # install - install a program, script, or datafile scriptversion=2024-06-19.01; # 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 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= 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 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. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -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 By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Report bugs to . GNU Automake home page: . General help using GNU software: ." 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;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -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 (GNU Automake) $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=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi 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. 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 dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # 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 # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. 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 incompatibility 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 if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # 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=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_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 && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $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 # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # 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 "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$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 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: isync-1.5.1/config.sub0000755000175000001440000010511614764113020010305 #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-03' # 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: # https://git.savannah.gnu.org/cgit/config.git/plain/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. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -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-2022 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 ;; *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 # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | 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* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # 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) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ | linux-musl* | linux-relibc* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$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 ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) 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 ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: isync-1.5.1/aclocal.m40000644000175000001440000016774014764113016010202 # generated automatically by aclocal 1.17 -*- Autoconf -*- # Copyright (C) 1996-2024 Free Software Foundation, Inc. # 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. # 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. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72],, [m4_warning([this file was generated for autoconf 2.72. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [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. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2024 Free Software Foundation, Inc. # # 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. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.17' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.17], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.17])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2024 Free Software Foundation, Inc. # # 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. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2024 Free Software Foundation, Inc. # # 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. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [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_$1_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 m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [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 thus: # 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_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2024 Free Software Foundation, Inc. # # 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. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # 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. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2024 Free Software Foundation, Inc. # # 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. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) 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 AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([_AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl AC_REQUIRE([_AM_PROG_RM_F]) AC_REQUIRE([_AM_PROG_XARGS_N]) dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _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"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl 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 AC_SUBST([install_sh])]) # Copyright (C) 2003-2024 Free Software Foundation, Inc. # # 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. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [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 AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2024 Free Software Foundation, Inc. # # 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. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2024 Free Software Foundation, Inc. # # 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. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # 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 AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && 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]) 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_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2022-2024 Free Software Foundation, Inc. # # 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. # _AM_PROG_RM_F # --------------- # Check whether 'rm -f' without any arguments works. # https://bugs.gnu.org/10828 AC_DEFUN([_AM_PROG_RM_F], [am__rm_f_notfound= AS_IF([(rm -f && rm -fr && rm -rf) 2>/dev/null], [], [am__rm_f_notfound='""']) AC_SUBST(am__rm_f_notfound) ]) # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2024 Free Software Foundation, Inc. # # 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. # _AM_SLEEP_FRACTIONAL_SECONDS # ---------------------------- AC_DEFUN([_AM_SLEEP_FRACTIONAL_SECONDS], [dnl AC_CACHE_CHECK([whether sleep supports fractional seconds], am_cv_sleep_fractional_seconds, [dnl AS_IF([sleep 0.001 2>/dev/null], [am_cv_sleep_fractional_seconds=yes], [am_cv_sleep_fractional_seconds=no]) ])]) # _AM_FILESYSTEM_TIMESTAMP_RESOLUTION # ----------------------------------- # Determine the filesystem's resolution for file modification # timestamps. The coarsest we know of is FAT, with a resolution # of only two seconds, even with the most recent "exFAT" extensions. # The finest (e.g. ext4 with large inodes, XFS, ZFS) is one # nanosecond, matching clock_gettime. However, it is probably not # possible to delay execution of a shell script for less than one # millisecond, due to process creation overhead and scheduling # granularity, so we don't check for anything finer than that. (See below.) AC_DEFUN([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION], [dnl AC_REQUIRE([_AM_SLEEP_FRACTIONAL_SECONDS]) AC_CACHE_CHECK([filesystem timestamp resolution], am_cv_filesystem_timestamp_resolution, [dnl # Default to the worst case. am_cv_filesystem_timestamp_resolution=2 # Only try to go finer than 1 sec if sleep can do it. # Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work, # - 1 sec is not much of a win compared to 2 sec, and # - it takes 2 seconds to perform the test whether 1 sec works. # # Instead, just use the default 2s on platforms that have 1s resolution, # accept the extra 1s delay when using $sleep in the Automake tests, in # exchange for not incurring the 2s delay for running the test for all # packages. # am_try_resolutions= if test "$am_cv_sleep_fractional_seconds" = yes; then # Even a millisecond often causes a bunch of false positives, # so just try a hundredth of a second. The time saved between .001 and # .01 is not terribly consequential. am_try_resolutions="0.01 0.1 $am_try_resolutions" fi # In order to catch current-generation FAT out, we must *modify* files # that already exist; the *creation* timestamp is finer. Use names # that make ls -t sort them differently when they have equal # timestamps than when they have distinct timestamps, keeping # in mind that ls -t prints the *newest* file first. rm -f conftest.ts? : > conftest.ts1 : > conftest.ts2 : > conftest.ts3 # Make sure ls -t actually works. Do 'set' in a subshell so we don't # clobber the current shell's arguments. (Outer-level square brackets # are removed by m4; they're present so that m4 does not expand # ; be careful, easy to get confused.) if ( set X `[ls -t conftest.ts[12]]` && { test "$[]*" != "X conftest.ts1 conftest.ts2" || test "$[]*" != "X conftest.ts2 conftest.ts1"; } ); then :; else # 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_ECHO_UNQUOTED( ["Bad output from ls -t: \"`[ls -t conftest.ts[12]]`\""], [AS_MESSAGE_LOG_FD]) AC_MSG_FAILURE([ls -t produces unexpected output. Make sure there is not a broken ls alias in your environment.]) fi for am_try_res in $am_try_resolutions; do # Any one fine-grained sleep might happen to cross the boundary # between two values of a coarser actual resolution, but if we do # two fine-grained sleeps in a row, at least one of them will fall # entirely within a coarse interval. echo alpha > conftest.ts1 sleep $am_try_res echo beta > conftest.ts2 sleep $am_try_res echo gamma > conftest.ts3 # We assume that 'ls -t' will make use of high-resolution # timestamps if the operating system supports them at all. if (set X `ls -t conftest.ts?` && test "$[]2" = conftest.ts3 && test "$[]3" = conftest.ts2 && test "$[]4" = conftest.ts1); then # # Ok, ls -t worked. If we're at a resolution of 1 second, we're done, # because we don't need to test make. make_ok=true if test $am_try_res != 1; then # But if we've succeeded so far with a subsecond resolution, we # have one more thing to check: make. It can happen that # everything else supports the subsecond mtimes, but make doesn't; # notably on macOS, which ships make 3.81 from 2006 (the last one # released under GPLv2). https://bugs.gnu.org/68808 # # We test $MAKE if it is defined in the environment, else "make". # It might get overridden later, but our hope is that in practice # it does not matter: it is the system "make" which is (by far) # the most likely to be broken, whereas if the user overrides it, # probably they did so with a better, or at least not worse, make. # https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html # # Create a Makefile (real tab character here): rm -f conftest.mk echo 'conftest.ts1: conftest.ts2' >conftest.mk echo ' touch conftest.ts2' >>conftest.mk # # Now, running # touch conftest.ts1; touch conftest.ts2; make # should touch ts1 because ts2 is newer. This could happen by luck, # but most often, it will fail if make's support is insufficient. So # test for several consecutive successes. # # (We reuse conftest.ts[12] because we still want to modify existing # files, not create new ones, per above.) n=0 make=${MAKE-make} until test $n -eq 3; do echo one > conftest.ts1 sleep $am_try_res echo two > conftest.ts2 # ts2 should now be newer than ts1 if $make -f conftest.mk | grep 'up to date' >/dev/null; then make_ok=false break # out of $n loop fi n=`expr $n + 1` done fi # if $make_ok; then # Everything we know to check worked out, so call this resolution good. am_cv_filesystem_timestamp_resolution=$am_try_res break # out of $am_try_res loop fi # Otherwise, we'll go on to check the next resolution. fi done rm -f conftest.ts? # (end _am_filesystem_timestamp_resolution) ])]) # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_REQUIRE([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION]) # This check should not be cached, as it may vary across builds of # different projects. AC_MSG_CHECKING([whether build environment is sane]) # 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]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; 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). am_build_env_is_sane=no am_has_slept=no rm -f conftest.file for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file if ( 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 test "$[]2" = conftest.file ); then am_build_env_is_sane=yes break fi # Just in case. sleep "$am_cv_filesystem_timestamp_resolution" am_has_slept=yes done AC_MSG_RESULT([$am_build_env_is_sane]) if test "$am_build_env_is_sane" = no; then AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= AS_IF([test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1],, [dnl ( sleep "$am_cv_filesystem_timestamp_resolution" ) & am_sleep_pid=$! ]) AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2024 Free Software Foundation, Inc. # # 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. # _AM_SILENT_RULES # ---------------- # Enable less verbose build rules support. AC_DEFUN([_AM_SILENT_RULES], [AM_DEFAULT_VERBOSITY=1 AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [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]) AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl dnl Delay evaluation of AM_DEFAULT_VERBOSITY to the end to allow multiple calls dnl to AM_SILENT_RULES to change the default value. AC_CONFIG_COMMANDS_PRE([dnl case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; esac if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi ])dnl ]) # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Set the default verbosity level to DEFAULT ("yes" being less verbose, "no" or # empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_REQUIRE([_AM_SILENT_RULES]) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1])]) # Copyright (C) 2001-2024 Free Software Foundation, Inc. # # 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. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # 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. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2024 Free Software Foundation, Inc. # # 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. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2024 Free Software Foundation, Inc. # # 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. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test x$am_uid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current UID is ok, but dist-ustar might not work]) elif test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test x$gm_gid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current GID is ok, but dist-ustar might not work]) elif test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR # Copyright (C) 2022-2024 Free Software Foundation, Inc. # # 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. # _AM_PROG_XARGS_N # ---------------- # Check whether 'xargs -n' works. It should work everywhere, so the fallback # is not optimized at all as we never expect to use it. AC_DEFUN([_AM_PROG_XARGS_N], [AC_CACHE_CHECK([xargs -n works], am_cv_xargs_n_works, [dnl AS_IF([test "`echo 1 2 3 | xargs -n2 echo`" = "1 2 3"], [am_cv_xargs_n_works=yes], [am_cv_xargs_n_works=no])]) AS_IF([test "$am_cv_xargs_n_works" = yes], [am__xargs_n='xargs -n'], [dnl am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "$@" "$am__xargs_n_arg"; done; }' ])dnl AC_SUBST(am__xargs_n) ]) m4_include([acinclude.m4]) isync-1.5.1/autodefs.h.in0000644000175000001440000000515614764113017010723 /* autodefs.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the 'fwrite_unlocked' function. */ #undef HAVE_FWRITE_UNLOCKED /* Define to 1 if you have the 'getaddrinfo' function. */ #undef HAVE_GETADDRINFO /* Define to 1 if you have the 'inet_ntop' function. */ #undef HAVE_INET_NTOP /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* if your libc has IPv6 support */ #undef HAVE_IPV6 /* if you have the SASL libraries */ #undef HAVE_LIBSASL /* if you have the OpenSSL libraries */ #undef HAVE_LIBSSL /* if you have the zlib library */ #undef HAVE_LIBZ /* Define to 1 if you have the macOS Keychain Services API. */ #undef HAVE_MACOS_KEYCHAIN /* Define to 1 if you have the 'memrchr' function. */ #undef HAVE_MEMRCHR /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* 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 to 1 if you have the 'strnlen' function. */ #undef HAVE_STRNLEN /* 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_TYPES_H /* Define to 1 if you have the 'timegm' function. */ #undef HAVE_TIMEGM /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the 'vasprintf' function. */ #undef HAVE_VASPRINTF /* 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 /* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* if Berkeley DB should be used */ #undef USE_DB /* Version number of package */ #undef VERSION isync-1.5.1/configure.ac0000644000175000001440000001720314740005110010601 # SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen dnl SPDX-License-Identifier: GPL-2.0-or-later m4_syscmd([./version.sh]) AC_INIT([isync], m4_chomp(m4_sinclude([VERSION]))) AC_CONFIG_HEADERS([autodefs.h]) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE AM_MAINTAINER_MODE AC_PROG_CC if test "$GCC" = yes; then warnings=" -Wall -Wextra -Wshadow -Wcast-qual -Wformat=2 -Wformat-signedness -Wformat-nonliteral -Wstrict-prototypes -Wno-overlength-strings " CFLAGS="$CFLAGS -pipe -std=c11 -pedantic $(echo $warnings)" fi AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void fkt(void) { int a = 42; // c99 comment for (int i = 0; i < a; i++) {} // declaration inside for() int b; // declaration after code } // c11 anonymous structs/unions struct base { int a; }; union deriv { struct base gen; struct { int a; int b; }; }; ])], , [AC_MSG_ERROR([compiler does not support required C11 features])]) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" AC_CHECK_PROG(PERL, perl, perl) if test "x$PERL" = "x"; then AC_MSG_ERROR([perl not found]) fi need_perl=5.14 AC_CACHE_CHECK([whether perl is recent enough], ob_cv_perl_ver, [ if $PERL -e "use v$need_perl;" 2> /dev/null; then ob_cv_perl_ver=yes else ob_cv_perl_ver=no fi ]) if test "x$ob_cv_perl_ver" = "xno"; then AC_MSG_ERROR([perl is too old, need v$need_perl]) fi AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include int main(void) { time_t t = 0; char buf[32]; strftime(buf, sizeof(buf), "%z", localtime(&t)); return !(buf[0] == '+' || buf[0] == '-'); } ]])], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])]) if test "x$ob_cv_strftime_z" = x"no"; then AC_MSG_ERROR([libc lacks necessary feature]) fi AC_CHECK_HEADERS(poll.h sys/select.h) AC_CHECK_FUNCS(vasprintf strnlen memrchr timegm fwrite_unlocked) AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"]) AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"]) AC_SUBST(SOCK_LIBS) have_ipv6=true sav_LIBS=$LIBS LIBS="$LIBS $SOCK_LIBS" AC_CHECK_FUNCS(getaddrinfo inet_ntop, , [have_ipv6=false]) LIBS=$sav_LIBS if $have_ipv6; then AC_DEFINE(HAVE_IPV6, 1, [if your libc has IPv6 support]) fi have_ssl_paths= AC_ARG_WITH(ssl, AS_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]), [ob_cv_with_ssl=$withval]) if test "x$ob_cv_with_ssl" != xno; then case $ob_cv_with_ssl in ""|yes) dnl Detect the pkg-config tool, as it may have extra info about the openssl dnl installation we can use. I *believe* this is what we are expected to do dnl on really recent Redhat Linux hosts. PKG_PROG_PKG_CONFIG if test "x$PKG_CONFIG" != "x" ; then AC_MSG_CHECKING([OpenSSL presence with pkg-config]) if $PKG_CONFIG --exists openssl; then SSL_LIBS=`$PKG_CONFIG --libs-only-l openssl` SSL_LDFLAGS=`$PKG_CONFIG --libs-only-L openssl` SSL_CPPFLAGS=`$PKG_CONFIG --cflags-only-I openssl` have_ssl_paths=yes AC_MSG_RESULT([found]) else AC_MSG_RESULT([not found]) fi fi ;; *) SSL_LDFLAGS=-L$ob_cv_with_ssl/lib$libsuff SSL_CPPFLAGS=-I$ob_cv_with_ssl/include ;; esac if test -z "$have_ssl_paths"; then sav_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $SSL_LDFLAGS" AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl]) AC_CHECK_LIB(crypto, X509_cmp, [LIBCRYPTO=-lcrypto]) AC_CHECK_LIB(ssl, SSL_connect, [SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes]) LDFLAGS=$sav_LDFLAGS fi sav_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" AC_CHECK_HEADER(openssl/ssl.h, , [have_ssl_paths=]) CPPFLAGS=$sav_CPPFLAGS if test -z "$have_ssl_paths"; then if test -n "$ob_cv_with_ssl"; then AC_MSG_ERROR([OpenSSL libs and/or includes were not found where specified]) fi else AC_DEFINE(HAVE_LIBSSL, 1, [if you have the OpenSSL libraries]) CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" LDFLAGS="$LDFLAGS $SSL_LDFLAGS" fi fi AC_SUBST(SSL_LIBS) have_sasl_paths= AC_ARG_WITH(sasl, AS_HELP_STRING([--with-sasl[=PATH]], [where to look for SASL [detect]]), [ob_cv_with_sasl=$withval]) if test "x$ob_cv_with_sasl" != xno; then case $ob_cv_with_sasl in ""|yes) dnl FIXME: Try various possible paths here... ;; *) SASL_LDFLAGS=-L$ob_cv_with_sasl/lib$libsuff SASL_CPPFLAGS=-I$ob_cv_with_sasl/include ;; esac if test -z "$have_sasl_paths"; then sav_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $SASL_LDFLAGS" AC_CHECK_LIB(sasl2, sasl_client_init, [SASL_LIBS="-lsasl2" have_sasl_paths=yes]) LDFLAGS=$sav_LDFLAGS fi sav_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS" AC_CHECK_HEADER(sasl/sasl.h, , [have_sasl_paths=]) CPPFLAGS=$sav_CPPFLAGS if test -z "$have_sasl_paths"; then if test -n "$ob_cv_with_sasl"; then AC_MSG_ERROR([SASL libs and/or includes were not found where specified]) fi else AC_DEFINE(HAVE_LIBSASL, 1, [if you have the SASL libraries]) CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS" LDFLAGS="$LDFLAGS $SASL_LDFLAGS" fi fi AC_SUBST(SASL_LIBS) AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4, [ac_cv_berkdb4=no sav_LIBS=$LIBS LIBS="$LIBS -ldb" AC_LINK_IFELSE([AC_LANG_PROGRAM( [#include ], [DB *db; db_create(&db, 0, 0); db->truncate(db, 0, 0, 0); db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0); ])], [ac_cv_berkdb4=yes], []) LIBS=$sav_LIBS ]) if test "x$ac_cv_berkdb4" = xyes; then AC_SUBST([DB_LIBS], ["-ldb"]) AC_DEFINE(USE_DB, 1, [if Berkeley DB should be used]) fi have_zlib= AC_ARG_WITH(zlib, AS_HELP_STRING([--with-zlib], [use zlib [detect]]), [ob_cv_with_zlib=$withval]) if test "x$ob_cv_with_zlib" != xno; then AC_CHECK_LIB([z], [deflate], [AC_CHECK_HEADER(zlib.h, [have_zlib=1 AC_SUBST([Z_LIBS], ["-lz"]) AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])] )] ) fi AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes) case $target_os in darwin*) darwin=yes ;; *) darwin=no ;; esac AC_ARG_WITH( macos-keychain, [AS_HELP_STRING([--with-macos-keychain], [Support macOS keychain])], [have_macos_keychain=$withval], [have_macos_keychain=$darwin]) if test "x$have_macos_keychain" != xno; then if test $darwin = no; then AC_MSG_ERROR([Cannot use macOS Keychain outside macOS.]) fi have_macos_keychain=yes AC_DEFINE(HAVE_MACOS_KEYCHAIN, 1, [Define to 1 if you have the macOS Keychain Services API.]) AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"]) fi RELEASE_DATE=`date -r $0 +%F` AC_SUBST(RELEASE_DATE) AC_CONFIG_FILES([Makefile src/Makefile src/mbsync.1 src/mdconvert.1 isync.spec]) AC_OUTPUT AC_MSG_RESULT() if test -n "$have_ssl_paths"; then AC_MSG_RESULT([Using SSL]) else AC_MSG_RESULT([Not using SSL]) fi if test -n "$have_sasl_paths"; then AC_MSG_RESULT([Using SASL]) else AC_MSG_RESULT([Not using SASL]) fi if test -n "$have_zlib"; then AC_MSG_RESULT([Using zlib]) else AC_MSG_RESULT([Not using zlib]) fi if test "x$ac_cv_berkdb4" = xyes; then AC_MSG_RESULT([Using Berkeley DB]) else AC_MSG_RESULT([Not using Berkeley DB]) fi if test $darwin = yes; then if test "x$have_macos_keychain" = xyes; then AC_MSG_RESULT([Using macOS Keychain]) else AC_MSG_RESULT([Not using macOS Keychain]) fi fi AC_MSG_RESULT() isync-1.5.1/Makefile.am0000644000175000001440000000167414764103741010373 # SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later SUBDIRS = src bin_SCRIPTS = mbsync-get-cert EXTRA_DIST = LICENSES VERSION debian isync.spec $(bin_SCRIPTS) cov-scan: clean /opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE) tar cavf isync-cov.tar.xz cov-int deb: CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign dist-hook: find $(distdir)/debian \( -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf -cd $(distdir)/debian && test -f .gitignore && rm -rf `cut -c2- .gitignore` .gitignore dist-sign: dist gpg -b -a $(PACKAGE)-$(VERSION).tar.gz rpm: dist CFLAGS="-O2 -mtune=core2" rpmbuild --clean -ta $(PACKAGE)-$(VERSION).tar.gz rpm-ia32: dist CFLAGS="-O2 -m32 -march=i686" rpmbuild --target i686-unknown-linux --clean -ta $(PACKAGE)-$(VERSION).tar.gz doc_DATA = README TODO NEWS ChangeLog AUTHORS isync-1.5.1/debian/0000755000175000001440000000000014764113614007631 5isync-1.5.1/debian/rules0000755000175000001440000000023013164125323010615 #!/usr/bin/make -f %: dh $@ --with=autoreconf override_dh_auto_install: dh_auto_install $(RM) $(CURDIR)/debian/isync/usr/share/doc/isync/ChangeLog isync-1.5.1/debian/source/0000755000175000001440000000000013164125071011122 5isync-1.5.1/debian/source/format0000644000175000001440000000001413164125071012250 3.0 (quilt) isync-1.5.1/debian/compat0000644000175000001440000000000213164125071010740 9 isync-1.5.1/debian/control0000644000175000001440000000253513701307132011147 Source: isync Section: mail Priority: optional Maintainer: Nicolas Boullis Uploaders: Theodore Y. Ts'o , Alessandro Ghedini Standards-Version: 3.9.8 Build-Depends: debhelper (>= 9), dh-autoreconf, libdb-dev, libsasl2-dev, libssl-dev, pkg-config, zlib1g-dev Vcs-Git: https://anonscm.debian.org/git/collab-maint/isync.git Vcs-Browser: https://anonscm.debian.org/gitweb/?p=collab-maint/isync.git Homepage: http://isync.sourceforge.net/ Package: isync Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Suggests: mutt Description: IMAP and MailDir mailbox synchronizer mbsync/isync is a command line application which synchronizes mailboxes; currently Maildir and IMAP4 mailboxes are supported. New messages, message deletions and flag changes can be propagated both ways. isync is suitable for use in IMAP-disconnected mode. . Features: * Fine-grained selection of synchronization operations to perform * Synchronizes single mailboxes or entire mailbox collections * Partial mirrors possible: keep only the latest messages locally * Trash functionality: backup messages before removing them IMAP features: * Security: supports TLS/SSL via imaps: (port 993) and STARTTLS; SASL for authentication * Supports NAMESPACE for simplified configuration * Pipelining for maximum speed isync-1.5.1/debian/changelog0000644000175000001440000003112714660617110011422 isync (1.2.3-0) unstable; urgency=low * Upload to unstable (with urgency=low) -- Oswald Buddenhagen Sun, 01 Oct 2017 12:12:12 +0000 isync (1.2.1-2) unstable; urgency=low * Upload to unstable (with urgency=low) * Don't call uupdate after uscan * Import patch to fix build with OpenSSL 1.1 (Closes: #828357) * Bump Standards-Version to 3.9.8 (no changes needed) * Add pkg-config to Build-Depends * Update Vcs-* URLs * Fix spelling-error-in-binary -- Alessandro Ghedini Sat, 19 Nov 2016 17:14:42 +0000 isync (1.2.1-1) experimental; urgency=medium [ Evgeni Golov ] * New upstream release. * Explicitly Build-Depend on zlib1g-dev -- Alessandro Ghedini Sat, 09 Jan 2016 12:56:39 +0000 isync (1.2.0-1) experimental; urgency=medium * New upstream release - Only show sync progress by default (Closes: #765052) * Enable libsasl support -- Alessandro Ghedini Mon, 06 Apr 2015 13:42:24 +0200 isync (1.1.2-1) unstable; urgency=medium * New upstream release * Bump Standards-Version to 3.9.6 (no changes needed) -- Alessandro Ghedini Sun, 01 Feb 2015 20:42:25 +0100 isync (1.1.1-1) unstable; urgency=medium * New upstream release - Don't lie about the default of User (Closes: #744389) - Don't forget to reset message counts when skipping scan (Closes: #744259) - Rework maildir store mapping (Closes: #737708) * Drop 01_fix-manpages.patch (merged upstream) * Drop 02_fix-empty-folder-sync.patch (merged upstream) -- Alessandro Ghedini Tue, 03 Jun 2014 21:00:44 +0200 isync (1.1.0-2) unstable; urgency=medium * Drop 02_fix-duplicate-changelog.patch (rm the file after installation instead) * Update 01_fix-manpages.patch * Add 02_fix-empty-folder-sync.patch (Closes: #738873) -- Alessandro Ghedini Fri, 14 Feb 2014 20:41:49 +0100 isync (1.1.0-1) unstable; urgency=low * New upstream release (Closes: #674403) - Fix overlapping memcpy (Closes: #650373) - Fix segfault while syncing mailboxes (Closes: #411120) - Fix segfault when invoked with arguments without configuration (Closes: #727239) * Bump debhelper compat level, update Build-Depends * Switch to short-form dh rules, remove useless files * Switch to 3.0 (quilt) source format * Remove empty patches/ directory * Drop local source modifications * Update short/long descriptions * Add 01_fix-manpages.patch to fix manpage errors and typos * Add Homepage field * Update copyright file to Copyright-Format 1.0 * Add Vcs-* fields * Add 02_fix-duplicate-changelog.patch to avoid duplicate changelog install * Add myself to Uploaders * Bump Standards-Version to 3.9.5 (no changes needed) * Use dh-autoreconf instead of autotools-dev -- Alessandro Ghedini Sun, 12 Jan 2014 16:35:52 +0100 isync (1.0.4-2.2) unstable; urgency=low * Non-maintainer upload. * Apply upstream patch for CVE-2013-0289. Fix incorrect server's SSL x509.v3 certificate validation when performing IMAP synchronization. (Closes: #701052) -- Salvatore Bonaccorso Sun, 24 Feb 2013 09:27:55 +0100 isync (1.0.4-2.1) unstable; urgency=low * Non-maintainer upload. * Drop debconf note that deals with a pre-Etch transition. Closes: #492194 -- Christian Perrier Sat, 25 Oct 2008 08:40:52 +0200 isync (1.0.4-2) unstable; urgency=low * Change the libdb4.4-dev build-dependency to libdb-dev. Thanks Luk for pointing this. (Closes: #499165) -- Nicolas Boullis Wed, 17 Sep 2008 23:58:58 +0200 isync (1.0.4-1) unstable; urgency=low * The second "thanks Christian" release. * New upstream release. - Accept empty "* SEARCH" response. (Closes: #413336) - Quote user name in generated config. (Closes: #456783) * Explain the isync->mbsync change in the package description. (Closes: #430648) * Fix the debian/watch file that lacked the version and action fields. * Disable the upstream "deb-clean" stuff in the top-level Makefile, as in breaks cleaning the build directory. * Bump Standards-Version to 3.7.3. (No change required.) -- Nicolas Boullis Sat, 03 May 2008 01:42:55 +0200 isync (1.0.3-3.1) unstable; urgency=low * Non-maintainer upload to fix pending l10n issues. * Debconf translations: - Portuguese. Closes: #418283 - Italian. Closes: #418246 - Dutch. Closes: #422244 - Spanish. Closes: #426184 - Finnish. Closes: #468214 - Galician. Closes: #470529 * [Lintian] Do not include debian revision in the build dependency for libssl-dev * [Lintian] No longer ignore errors from "make distclean" -- Christian Perrier Wed, 12 Mar 2008 07:24:01 +0100 isync (1.0.3-3) unstable; urgency=low * The "thanks Christian" release. * Update German debconf templates translation. Thanks to Erik Schanze (for the translation) and Christian Perrier (for forwarding the translation). (Closes: #407615) -- Nicolas Boullis Mon, 5 Feb 2007 00:17:15 +0100 isync (1.0.3-2.1) unstable; urgency=low * Non-maintainer upload with maintainer's permission * Debconf templates translations: - French updated by me - Brazilian Portuguese translation added - Czech translation added. Closes: #403473 - Russian translation added. Closes: #403510 - Vietnamese translation added - Norwegian Bokmål translation added. Closes: #403523 -- Christian Perrier Sun, 17 Dec 2006 15:31:04 +0100 isync (1.0.3-2) unstable; urgency=low * Back to unstable, with permission from Steve Langasek. (Message-ID: <20061121015225.GF28035@borges.dodds.net>) * Rewrite the debconf note, thanks to the debian-l10n-english team (and especially MJ Ray). * Also add some information about the new version into NEWS.Debian. * Remove the information about the need to set the T (trashed) flag from README.Debian. * Also install the isyncrc.sample sample configuration file. * Bump Standards-Version to 3.7.2. (No change required.) -- Nicolas Boullis Tue, 5 Dec 2006 00:34:54 +0100 isync (1.0.3-1) experimental; urgency=low * New upstream release. (Closes: #315423) - Isync now supports breaking and linking threads. (Closes: #177280) - It also supports unflagging messages. (Closes: #111286) - IMAP commands are sent asynchronously. (Closes: #226222) * Kill the old debconf question about upgrades from pre-0.8 versions. * Use the (now obsolete) swedish and portuguese translations anyway. (Closes: #337771, #378891) * New debconf note that warns about upgrades from pre-1.0 versions. * Add a build dependency on po-debconf. -- Nicolas Boullis Sun, 19 Nov 2006 15:04:31 +0100 isync (0.9.2-4) unstable; urgency=low * Add Czech debconf translation, thanks to Martin Šín. (Closes: #317571) * Build with the newest libssl-dev. * Load the debconf library in postinst to ensure that everything works as expected, thanks to lintian for noticing the problem and to Josselin Mouette for pointing to the right doc. * Fix a bashism in the config script, thanks to lintian. * Update the postal address of the FSF in the copyright file. * Bump Standards-Version to 3.6.2. (No change required.) -- Nicolas Boullis Mon, 10 Oct 2005 01:37:50 +0200 isync (0.9.2-3) unstable; urgency=low * Bump build-dependency from libdb4.0-dev to libdb4.2-dev, thanks to Andreas Jochens. (Closes: #280268) -- Nicolas Boullis Tue, 9 Nov 2004 18:21:12 +0100 isync (0.9.2-2) unstable; urgency=low * Add german debconf templates translation, thanks to Erik Schanze. (Closes: #267675) -- Nicolas Boullis Tue, 24 Aug 2004 00:32:32 +0200 isync (0.9.2-1) unstable; urgency=low * New upstream release. - Password prompt now includes the mailbox/server. (Closes: #92893) * Backported from CVS: - A few prinf converted to info (disabled with -q). - A few other printf converted to warn (disabled with -q -q) to be able to disable the warning when SSL is not available. (Closes: #228086) - Update the manpage accordingly (about -q). - Improve the manpage (about using isync with mutt). * Add Theodore Y. Ts'o as a co-maintainter. -- Nicolas Boullis Tue, 13 Apr 2004 02:12:42 +0200 isync (0.9.1-4) unstable; urgency=low * The "Why do I keep adding such stupid bugs?" release. * Remove the extra parenthesis that caused UID FETCH syntax errors, thanks to Niels den Otter for pointing the bug and giving the solution. (Closes: #224803) * Use configure's --build and --host options to prevent wrong optimizations (such as building for sparc64 rather than for sparc). -- Nicolas Boullis Wed, 7 Jan 2004 01:06:53 +0100 isync (0.9.1-3) unstable; urgency=low * Do not segfault when using both tunneled end non-tunneled connections, thanks to Nik A. Melchior for reporting and for his patch. (Closes: #220667) * Save uid of messages when interrupted, thanks to Theodore Y. Ts'o for reporting and for his patch. (Closes: #220346) * Do not get the sizes of the messages if unneeded (if MaxSize=0). -- Nicolas Boullis Thu, 18 Dec 2003 00:55:04 +0100 isync (0.9.1-2) unstable; urgency=low * Add french debconf templates translation, thanks to Christian Perrier. (Closes: #218118) -- Nicolas Boullis Mon, 3 Nov 2003 18:50:56 +0100 isync (0.9.1-1) unstable; urgency=low * New maintainer. (Closes: #180050) * New upstream release. - With the new option -R, isync is now able to create non-existent remote mailboxes. (Closes: #170388) * Update debian/copyright to match the current copyright: - Add Oswald Buddenhagen as copyright owner. - Add special exception for OpenSSL. * Add support for noopt in $DEB_BUILD_OPTIONS in debian/rules. * Switch to po-debconf. * Remove sample.isyncrc from debian/docs: no need to have it both as a doc and as an example. * Move package from section non-US/main (?) to mail. (Closes: #154216) * Update versioned build-dependency on debhelper to >= 4.1.16. * Bump Standards-Version to 3.6.1. (No change required.) -- Nicolas Boullis Tue, 14 Oct 2003 22:02:20 +0200 isync (0.8-4) unstable; urgency=low * Orphaned the package, as I no longer use it. -- Joey Hess Thu, 6 Feb 2003 15:46:38 -0500 isync (0.8-3) unstable; urgency=low * New upstream maintainer; updated copyright file web site address, and watch file. NB: new upstream has not made any new releases yet. -- Joey Hess Sat, 1 Feb 2003 16:03:49 -0500 isync (0.8-2) unstable; urgency=low * Only reset debconf question if user chooses to abort upgrade. Closes: #167363 * Don't open lock files O_EXCL. As seen in upstream cvs. * Description and build-deps updates. * Added README.Debian with notes on mutt integration. -- Joey Hess Fri, 1 Nov 2002 18:02:44 -0500 isync (0.8-1) unstable; urgency=low * New upstream release. Closes: #134080 **WARNING** You need to remove all the messages in your local folder if you were previously using another version of isync or else you will end up with duplicate messages on your IMAP server. * Has better support for uploading locally added messages. Closes: #120272 * Added a debconf question with some info about this that lets you abort the upgrade. * Added NEWS.Debian with same info. * New maintainer. * Removed upstream debianization stuff. * Updated copyright file. * Updated to current policy throughout. * Added uscan watch file. * Updated build-deps. * Now that isync needs berkeley databases, go with db4, so I don't have to transition from db3 later. * Fix fd leak (forgot to close tmp dir in maildir). Closes: #150762 -- Joey Hess Tue, 29 Oct 2002 17:02:14 -0500 isync (0.7-1) unstable; urgency=low * New upstream version (Closes: #121312, #92051). * Rumors say this might fix bugs #102255 and #120272, but I have no test setup right now, so I'm leaving them open. * Updated standards-version. -- Tommi Virtanen Sat, 5 Jan 2002 16:13:35 +0200 isync (0.5-1) unstable; urgency=low * New upstream version (Closes: #98642). * Install sample.isyncrc too (Closes: #90464). -- Tommi Virtanen Sat, 23 Jun 2001 01:19:07 +0300 isync (0.4-1) unstable; urgency=low * Initial Release. -- Tommi Virtanen Sat, 10 Mar 2001 18:43:35 +0200 isync-1.5.1/debian/watch0000644000175000001440000000006313164125071010572 version=3 http://sf.net/isync/ isync-(.*)\.tar\.gz isync-1.5.1/debian/README.Debian0000644000175000001440000000120213701307132011573 A note from isync's web site: isync can be integrated into Mutt fairly easily with a few hooks: folder-hook ~A bind index $ folder-hook +maildir 'macro index $ "!mbsync the_channel:maildir\n"' where the_channel is the Channel used to sync this mailbox, and maildir is the name of the local mailbox itself. This works well so long as you are not modifying the IMAP mailbox outside of Mutt. However, if you are using another mail program simultaneously, Mutt will have the wrong idea of the local mailbox flags and messages will start disappearing from its index display (don't worry, they are still on disk). isync-1.5.1/debian/copyright0000644000175000001440000000242014405565305011502 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: isync Source: http://isync.sourceforge.net Files: * Copyright: 2000-2002, Michael R. Elkins 2002-2022, Oswald Buddenhagen 2004, Theodore Y. Ts'o License: GPL-2+ Files: debian/* Copyright: 2013, Alessandro Ghedini License: GPL-2+ License: GPL-2+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. . This package 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, mbsync may be linked with the OpenSSL library, despite that library's more restrictive license. . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". isync-1.5.1/src/0000755000175000001440000000000014764113614007176 5isync-1.5.1/src/main_list.c0000644000175000001440000000777114746121676011264 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #include "main_p.h" typedef struct store_ent { struct store_ent *next; store_conf_t *conf; } store_ent_t; typedef struct { core_vars_t *cvars; store_conf_t *store; driver_t *drv; store_t *ctx; store_ent_t *storeptr; int cben, done; } list_vars_t; static store_ent_t * add_store( store_ent_t ***storeapp, store_conf_t *store ) { store_ent_t *se = nfzalloc( sizeof(*se) ); se->conf = store; **storeapp = se; *storeapp = &se->next; return se; } static void do_list_stores( list_vars_t *lvars ); static void list_next_store( list_vars_t *lvars ); void list_stores( core_vars_t *cvars, char **argv ) { list_vars_t lvars[1]; store_ent_t *strs = NULL, **strapp = &strs; store_conf_t *store; memset( lvars, 0, sizeof(*lvars) ); lvars->cvars = cvars; if (!stores) { fputs( "No stores defined.\n", stderr ); cvars->ret = 1; return; } if (!*argv) { // Implicit --all for (store = stores; store; store = store->next) add_store( &strapp, store ); } else { for (; *argv; argv++) { for (store = stores; store; store = store->next) { if (!strcmp( store->name, *argv )) { add_store( &strapp, store ); goto gotstr; } } error( "No store named '%s' defined.\n", *argv ); cvars->ret = 1; gotstr: ; } } if (cvars->ret) return; lvars->storeptr = strs; do_list_stores( lvars ); main_loop(); } static void list_store_bad( void *aux ) { list_vars_t *lvars = (list_vars_t *)aux; lvars->done = 1; lvars->drv->cancel_store( lvars->ctx ); lvars->cvars->ret = 1; list_next_store( lvars ); } static void advance_store( list_vars_t *lvars ) { store_ent_t *nstr = lvars->storeptr->next; free( lvars->storeptr ); lvars->storeptr = nstr; } static void list_store_connected( int sts, void *aux ); static void do_list_stores( list_vars_t *lvars ) { while (lvars->storeptr) { lvars->store = lvars->storeptr->conf; lvars->drv = lvars->store->driver; int st = lvars->drv->get_fail_state( lvars->store ); if (st != FAIL_TEMP) { info( "Skipping %sfailed store %s.\n", (st == FAIL_WAIT) ? "temporarily " : "", lvars->store->name ); lvars->cvars->ret = 1; goto next; } uint dcaps = lvars->drv->get_caps( NULL ); store_t *ctx = lvars->drv->alloc_store( lvars->store, "" ); if ((DFlags & DEBUG_DRV) || ((DFlags & FORCEASYNC(F)) && !(dcaps & DRV_ASYNC))) { lvars->drv = &proxy_driver; ctx = proxy_alloc_store( ctx, "", DFlags & FORCEASYNC(F) ); } lvars->ctx = ctx; lvars->drv->set_callbacks( ctx, NULL, list_store_bad, lvars ); info( "Opening store %s...\n", lvars->store->name ); lvars->cben = lvars->done = 0; lvars->drv->connect_store( lvars->ctx, list_store_connected, lvars ); if (!lvars->done) { lvars->cben = 1; return; } next: advance_store( lvars ); } cleanup_mainloop(); } static void list_next_store( list_vars_t *lvars ) { if (lvars->cben) { advance_store( lvars ); do_list_stores( lvars ); } } static void list_done_store( list_vars_t *lvars ) { lvars->done = 1; lvars->drv->free_store( lvars->ctx ); list_next_store( lvars ); } static void list_store_listed( int sts, string_list_t *boxes, void *aux ); static void list_store_connected( int sts, void *aux ) { list_vars_t *lvars = (list_vars_t *)aux; switch (sts) { case DRV_CANCELED: return; case DRV_OK: lvars->drv->list_store( lvars->ctx, LIST_INBOX | LIST_PATH_MAYBE, list_store_listed, lvars ); break; default: lvars->cvars->ret = 1; list_done_store( lvars ); break; } } static void list_store_listed( int sts, string_list_t *boxes, void *aux ) { list_vars_t *lvars = (list_vars_t *)aux; string_list_t *box; switch (sts) { case DRV_CANCELED: return; case DRV_OK: printf( "===== %s:\n", lvars->ctx->conf->name ); for (box = boxes; box; box = box->next) puts( box->string ); break; default: lvars->cvars->ret = 1; break; } list_done_store( lvars ); } isync-1.5.1/src/sync_msg_cvt.c0000644000175000001440000001335414405565305011766 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #include "sync_p.h" static void copy_msg_bytes( char **out_ptr, const char *in_buf, uint *in_idx, uint in_len, int in_cr, int out_cr ) { char *out = *out_ptr; uint idx = *in_idx; if (out_cr != in_cr) { if (out_cr) { for (char c, pc = 0; idx < in_len; idx++) { if (((c = in_buf[idx]) == '\n') && (pc != '\r')) *out++ = '\r'; *out++ = c; pc = c; } } else { for (char c, pc = 0; idx < in_len; idx++) { if (((c = in_buf[idx]) == '\n') && (pc == '\r')) out--; *out++ = c; pc = c; } } } else { memcpy( out, in_buf + idx, in_len - idx ); out += in_len - idx; idx = in_len; } *out_ptr = out; *in_idx = idx; } char * copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) { char *in_buf = vars->data.data; uint in_len = vars->data.len; uint idx = 0, sbreak = 0, ebreak = 0, break2 = UINT_MAX; uint lines = 0, hdr_crs = 0, bdy_crs = 0, app_cr = 0, extra = 0; uint add_subj = 0, fix_tuid = 0, fix_subj = 0, fix_hdr = 0, end_hdr = 0; if (vars->srec) { for (;;) { uint start = idx; uint line_cr = 0; uint got_line = 0; char pc = 0; while (idx < in_len) { char c = in_buf[idx++]; if (c == '\n') { if (pc == '\r') line_cr = 1; got_line = 1; break; } pc = c; } if (!ebreak && starts_with_upper( in_buf + start, (int)(in_len - start), "X-TUID: ", 8 )) { extra = (sbreak = start) - (ebreak = idx); if (!vars->minimal) break; continue; } if (break2 == UINT_MAX && vars->minimal && starts_with_upper( in_buf + start, (int)(in_len - start), "SUBJECT:", 8 )) { break2 = start + 8; if (break2 < in_len && in_buf[break2] == ' ') break2++; } hdr_crs += line_cr; if (got_line) { lines++; if (idx - line_cr - 1 != start) continue; // Empty line => end of headers } else { // The line is incomplete. if (pc == '\r') idx--; // For simplicity, move back before trailing CR if (idx != start) { // The line is non-empty, so schedule completing it fix_hdr = 1; // ... and put our headers after it. (It would seem easier // to prepend them, as then we could avoid the fixing - but // the line might be a continuation. We could also prepend // it to _all_ pre-exiting headers, but then we would risk // masking an (incorrectly present) leading 'From ' header.) start = idx; } end_hdr = 1; } if (!ebreak) { sbreak = ebreak = start; fix_tuid = fix_hdr; fix_hdr = 0; } if (vars->minimal) { in_len = idx; if (break2 == UINT_MAX) { break2 = start; add_subj = 1; fix_subj = fix_hdr; fix_hdr = 0; } } else { fix_hdr = 0; end_hdr = 0; } break; } app_cr = out_cr && (!in_cr || hdr_crs || !lines); if (fix_tuid || fix_subj || fix_hdr) extra += app_cr + 1; if (end_hdr) extra += app_cr + 1; extra += 8 + TUIDL + app_cr + 1; } if (out_cr != in_cr) { for (char pc = 0; idx < in_len; idx++) { char c = in_buf[idx]; if (c == '\n') { lines++; if (pc == '\r') bdy_crs++; } pc = c; } extra -= hdr_crs + bdy_crs; if (out_cr) extra += lines; } uint dummy_msg_len = 0; char dummy_msg_buf[256]; static const char dummy_pfx[] = "[placeholder] "; static const char dummy_subj[] = "Subject: [placeholder] (No Subject)"; static const char dummy_msg[] = "Having a size of %s, this message is over the MaxSize limit.%s" "Flag it and sync again (Sync mode Upgrade) to fetch its real contents.%s"; static const char dummy_flag[] = "%s" "The original message is flagged as important.%s"; if (vars->minimal) { char sz[32]; if (vars->msg->size < 1024000) sprintf( sz, "%dKiB", (int)(vars->msg->size >> 10) ); else sprintf( sz, "%.1fMiB", vars->msg->size / 1048576. ); const char *nl = app_cr ? "\r\n" : "\n"; dummy_msg_len = (uint)sprintf( dummy_msg_buf, dummy_msg, sz, nl, nl ); if (vars->data.flags & F_FLAGGED) { vars->data.flags &= ~F_FLAGGED; dummy_msg_len += (uint)sprintf( dummy_msg_buf + dummy_msg_len, dummy_flag, nl, nl ); } extra += dummy_msg_len; extra += add_subj ? strlen(dummy_subj) + app_cr + 1 : strlen(dummy_pfx); } #define ADD_NL() \ do { \ if (app_cr) \ *out_buf++ = '\r'; \ *out_buf++ = '\n'; \ } while (0) vars->data.len = in_len + extra; if (vars->data.len > INT_MAX) { free( in_buf ); return "is too big after conversion"; } char *out_buf = vars->data.data = nfmalloc( vars->data.len ); idx = 0; if (vars->srec) { if (break2 < sbreak) { copy_msg_bytes( &out_buf, in_buf, &idx, break2, in_cr, out_cr ); memcpy( out_buf, dummy_pfx, strlen(dummy_pfx) ); out_buf += strlen(dummy_pfx); } copy_msg_bytes( &out_buf, in_buf, &idx, sbreak, in_cr, out_cr ); if (fix_tuid) ADD_NL(); memcpy( out_buf, "X-TUID: ", 8 ); out_buf += 8; memcpy( out_buf, vars->srec->tuid, TUIDL ); out_buf += TUIDL; ADD_NL(); idx = ebreak; if (break2 != UINT_MAX && break2 >= sbreak) { copy_msg_bytes( &out_buf, in_buf, &idx, break2, in_cr, out_cr ); if (!add_subj) { memcpy( out_buf, dummy_pfx, strlen(dummy_pfx) ); out_buf += strlen(dummy_pfx); } else { if (fix_subj) ADD_NL(); memcpy( out_buf, dummy_subj, strlen(dummy_subj) ); out_buf += strlen(dummy_subj); ADD_NL(); } } } copy_msg_bytes( &out_buf, in_buf, &idx, in_len, in_cr, out_cr ); if (vars->minimal) { if (end_hdr) { if (fix_hdr) ADD_NL(); ADD_NL(); } memcpy( out_buf, dummy_msg_buf, dummy_msg_len ); } free( in_buf ); return NULL; } isync-1.5.1/src/driver.c0000644000175000001440000000425114405565305010557 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "driver.h" store_conf_t *stores; driver_t *drivers[N_DRIVERS] = { &maildir_driver, &imap_driver }; void cleanup_drivers( void ) { for (int t = 0; t < N_DRIVERS; t++) drivers[t]->cleanup(); } // Keep the MESSAGE_FLAGS in sync (grep that)! const char MsgFlags[] = { 'D', 'F', 'P', 'R', 'S', 'T' }; static void make_flags( uchar flags, char *buf ) { uint i, d; for (i = d = 0; i < as(MsgFlags); i++) if (flags & (1 << i)) buf[d++] = MsgFlags[i]; buf[d] = 0; } flag_str_t fmt_flags( uchar flags ) { flag_str_t buf; make_flags( flags, buf.str ); return buf; } flag_str_t fmt_lone_flags( uchar flags ) { flag_str_t buf; if (!flags) { buf.str[0] = '-'; buf.str[1] = 0; } else { make_flags( flags, buf.str ); } return buf; } void free_generic_messages( message_t *msgs ) { while (msgs) { message_t *tmsg = msgs->next; free( msgs->msgid ); free( msgs ); msgs = tmsg; } } void parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type ) { if (!strcasecmp( "Trash", cfg->cmd )) { store->trash = nfstrdup( cfg->val ); } else if (!strcasecmp( "TrashRemoteNew", cfg->cmd )) { store->trash_remote_new = parse_bool( cfg ); } else if (!strcasecmp( "TrashNewOnly", cfg->cmd )) { store->trash_only_new = parse_bool( cfg ); } else if (!strcasecmp( "MaxSize", cfg->cmd )) { store->max_size = parse_size( cfg ); } else if (!strcasecmp( "MapInbox", cfg->cmd )) { store->map_inbox = nfstrdup( cfg->val ); } else if (!strcasecmp( "Flatten", cfg->cmd )) { const char *p; for (p = cfg->val; *p; p++) { if (*p == '/') { error( "%s:%d: flattened hierarchy delimiter cannot contain the canonical delimiter '/'\n", cfg->file, cfg->line ); cfg->err = 1; return; } } store->flat_delim = nfstrdup( cfg->val ); } else { error( "%s:%d: keyword '%s' is not recognized in %s sections\n", cfg->file, cfg->line, cfg->cmd, type ); cfg->rest = NULL; cfg->err = 1; } } isync-1.5.1/src/main.c0000644000175000001440000003453714405565305010222 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "main_p.h" #include #include #include #ifdef __linux__ # include #endif static void ATTR_NORETURN version( void ) { puts( PACKAGE " " VERSION ); exit( 0 ); } static void ATTR_NORETURN usage( int code ) { fputs( PACKAGE " " VERSION " - mailbox synchronizer\n" "Copyright (C) 2000-2002 Michael R. Elkins \n" "Copyright (C) 2002-2022 Oswald Buddenhagen \n" "Copyright (C) 2004 Theodore Ts'o \n" "usage:\n" " " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n" " -a, --all operate on all defined channels\n" " -l, --list list mailboxes instead of syncing them\n" " -ls, --list-stores raw listing of stores' mailboxes\n" " -n, --new propagate new messages\n" " -g, --gone propagate message disappearances (expunges)\n" " -f, --flags propagate message flag changes\n" " -u, --upgrade upgrade placeholders to full messages\n" " -L, --pull propagate from far to near side\n" " -H, --push propagate from near to far side\n" " -C, --create propagate creations of mailboxes\n" " -R, --remove propagate deletions of mailboxes\n" " -X, --expunge expunge deleted messages\n" " -x, --expunge-solo expunge deleted messages that are not paired\n" " -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n" " -D, --debug debugging modes (see manual)\n" " -y, --dry-run do not actually modify anything\n" " -e, --ext-exit return extended exit code\n" " -V, --verbose display what is happening\n" " -q, --quiet don't display progress counters\n" " -v, --version display version\n" " -h, --help display this help message\n" "\nIf neither --pull nor --push are specified, both are active.\n" "If neither --new, --gone, --flags, nor --upgrade are specified, all are\n" "active. Direction and operation can be concatenated like --pull-new, etc.\n" "--create, --remove, --expunge, and --expunge-solo can be suffixed with" "-far/-near.\n" "See the man page for details.\n" "\nSupported mailbox formats are: IMAP4rev1, Maildir\n" "\nCompile time options:\n" #ifdef HAVE_LIBSSL " +HAVE_LIBSSL" #else " -HAVE_LIBSSL" #endif #ifdef HAVE_LIBSASL " +HAVE_LIBSASL" #else " -HAVE_LIBSASL" #endif #ifdef HAVE_LIBZ " +HAVE_LIBZ" #else " -HAVE_LIBZ" #endif #ifdef USE_DB " +USE_DB" #else " -USE_DB" #endif #ifdef HAVE_IPV6 " +HAVE_IPV6\n" #else " -HAVE_IPV6\n" #endif , code ? stderr : stdout ); exit( code ); } static int child_pipe[2]; static notifier_t child_notifier; static void childHandler( int n ATTR_UNUSED ) { // We can't just reap everything here, as we might steal children // from popen(). Let the main loop handle it synchronously instead. char dummy = 0; write( child_pipe[1], &dummy, 1 ); } static void childReaper( int events ATTR_UNUSED, void *aux ATTR_UNUSED ) { char dummy; while (read( child_pipe[0], &dummy, 1 ) == 1) {} while (waitpid( -1, NULL, WNOHANG ) > 0) {} } #ifdef __linux__ static void ATTR_NORETURN crashHandler( int n ) { int dpid; char pbuf[10], pabuf[20]; close( 0 ); open( "/dev/tty", O_RDWR ); dup2( 0, 1 ); dup2( 0, 2 ); error( "*** " EXE " caught signal %d. Starting debugger ...\n", n ); #ifdef PR_SET_PTRACER int pip[2]; if (pipe( pip ) < 0) { perror( "pipe()" ); exit( 3 ); } #endif switch ((dpid = fork())) { case -1: perror( "fork()" ); break; case 0: #ifdef PR_SET_PTRACER close( pip[1] ); read( pip[0], pbuf, 1 ); close( pip[0] ); #endif sprintf( pbuf, "%d", Pid ); sprintf( pabuf, "/proc/%d/exe", Pid ); execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 ); perror( "execlp()" ); _exit( 1 ); default: #ifdef PR_SET_PTRACER prctl( PR_SET_PTRACER, (ulong)dpid ); close( pip[1] ); close( pip[0] ); #endif waitpid( dpid, NULL, 0 ); break; } exit( 3 ); } #endif void countStep( void ) { if (!--JLimit) exit( 100 ); } int main( int argc, char **argv ) { core_vars_t mvars[1]; char *config = NULL, *opt, *ochar; int oind, cops = 0, op, ms_warn = 0, renew_warn = 0, delete_warn = 0; tzset(); init_timers(); gethostname( Hostname, sizeof(Hostname) ); if ((ochar = strchr( Hostname, '.' ))) *ochar = 0; Pid = getpid(); if (!(Home = getenv("HOME"))) { fputs( "Fatal: $HOME not set\n", stderr ); return 1; } arc4_init(); memset( mvars, 0, sizeof(*mvars) ); for (oind = 1, ochar = NULL; ; ) { if (!ochar || !*ochar) { if (oind >= argc) break; if (argv[oind][0] != '-') break; if (argv[oind][1] == '-') { opt = argv[oind++] + 2; if (!*opt) break; if (!strcmp( opt, "config" )) { if (oind >= argc) { error( "--config requires an argument.\n" ); return 1; } config = argv[oind++]; } else if (starts_with( opt, -1, "config=", 7 )) { config = opt + 7; } else if (!strcmp( opt, "all" )) { mvars->all = 1; } else if (!strcmp( opt, "list" )) { mvars->list = 1; } else if (!strcmp( opt, "list-stores" )) { mvars->list_stores = 1; } else if (!strcmp( opt, "help" )) { usage( 0 ); } else if (!strcmp( opt, "version" )) { version(); } else if (!strcmp( opt, "quiet" )) { if (Verbosity > VERYQUIET) Verbosity--; } else if (!strcmp( opt, "verbose" )) { Verbosity = VERBOSE; } else if (starts_with( opt, -1, "debug", 5 )) { opt += 5; if (!*opt) op = DEBUG_ALL; else if (!strcmp( opt, "-crash" )) op = DEBUG_CRASH; else if (!strcmp( opt, "-driver" )) op = DEBUG_DRV; else if (!strcmp( opt, "-driver-all" )) op = DEBUG_DRV | DEBUG_DRV_ALL; else if (!strcmp( opt, "-maildir" )) op = DEBUG_MAILDIR; else if (!strcmp( opt, "-main" )) op = DEBUG_MAIN; else if (!strcmp( opt, "-net" )) op = DEBUG_NET; else if (!strcmp( opt, "-net-all" )) op = DEBUG_NET | DEBUG_NET_ALL; else if (!strcmp( opt, "-sync" )) op = DEBUG_SYNC; else goto badopt; DFlags |= op; } else if (!strcmp( opt, "dry-run" )) { DFlags |= DRYRUN; } else if (!strcmp( opt, "ext-exit" )) { DFlags |= EXT_EXIT; } else if (!strcmp( opt, "pull" )) { cops |= XOP_PULL, mvars->ops[F] |= XOP_HAVE_TYPE; } else if (!strcmp( opt, "push" )) { cops |= XOP_PUSH, mvars->ops[F] |= XOP_HAVE_TYPE; } else if (starts_with( opt, -1, "create", 6 )) { opt += 6; op = OP_CREATE | XOP_HAVE_CREATE; lcop: if (!*opt) cops |= op; else if (!strcmp( opt, "-far" )) mvars->ops[F] |= op; else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy mvars->ops[F] |= op, ms_warn = 1; else if (!strcmp( opt, "-near" )) mvars->ops[N] |= op; else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy mvars->ops[N] |= op, ms_warn = 1; else goto badopt; mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE | XOP_HAVE_EXPUNGE_SOLO); } else if (starts_with( opt, -1, "remove", 6 )) { opt += 6; op = OP_REMOVE | XOP_HAVE_REMOVE; goto lcop; } else if (starts_with( opt, -1, "expunge-solo", 12 )) { opt += 12; op = OP_EXPUNGE_SOLO | XOP_HAVE_EXPUNGE_SOLO; goto lcop; } else if (starts_with( opt, -1, "expunge", 7 )) { opt += 7; op = OP_EXPUNGE | XOP_HAVE_EXPUNGE; goto lcop; } else if (!strcmp( opt, "no-expunge-solo" )) { mvars->ops[F] |= XOP_EXPUNGE_SOLO_NOOP | XOP_HAVE_EXPUNGE_SOLO; } else if (!strcmp( opt, "no-expunge" )) { mvars->ops[F] |= XOP_EXPUNGE_NOOP | XOP_HAVE_EXPUNGE; } else if (!strcmp( opt, "no-create" )) { mvars->ops[F] |= XOP_CREATE_NOOP | XOP_HAVE_CREATE; } else if (!strcmp( opt, "no-remove" )) { mvars->ops[F] |= XOP_REMOVE_NOOP | XOP_HAVE_REMOVE; } else if (!strcmp( opt, "noop" )) { mvars->ops[F] |= XOP_TYPE_NOOP | XOP_HAVE_TYPE; } else if (starts_with( opt, -1, "pull", 4 )) { op = XOP_PULL; lcac: opt += 4; if (!*opt) { cops |= op; } else if (*opt == '-') { opt++; goto rlcac; } else { goto badopt; } } else if (starts_with( opt, -1, "push", 4 )) { op = XOP_PUSH; goto lcac; } else { op = 0; rlcac: if (!strcmp( opt, "new" )) { op |= OP_NEW; } else if (!strcmp( opt, "old" )) { op |= OP_OLD; } else if (!strcmp( opt, "upgrade" )) { op |= OP_UPGRADE; } else if (!strcmp( opt, "renew" )) { renew_warn = 1; op |= OP_UPGRADE; } else if (!strcmp( opt, "gone" )) { op |= OP_GONE; } else if (!strcmp( opt, "delete" )) { delete_warn = 1; op |= OP_GONE; } else if (!strcmp( opt, "flags" )) { op |= OP_FLAGS; } else if (!strcmp( opt, "full" )) { op |= OP_MASK_TYPE; } else { badopt: error( "Unknown option '%s'\n", argv[oind - 1] ); return 1; } switch (op & XOP_MASK_DIR) { case XOP_PULL: mvars->ops[N] |= op & OP_MASK_TYPE; break; case XOP_PUSH: mvars->ops[F] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } mvars->ops[F] |= XOP_HAVE_TYPE; } continue; } ochar = argv[oind++] + 1; if (!*ochar) { error( "Invalid option '-'\n" ); return 1; } } switch (*ochar++) { case 'a': mvars->all = 1; break; case 'l': if (*ochar == 's') mvars->list_stores = 1, ochar++; else mvars->list = 1; break; case 'c': if (oind >= argc) { error( "-c requires an argument.\n" ); return 1; } config = argv[oind++]; break; case 'C': op = OP_CREATE | XOP_HAVE_CREATE; cop: if (*ochar == 'f') mvars->ops[F] |= op, ochar++; else if (*ochar == 'm') // Pre-1.4 legacy mvars->ops[F] |= op, ms_warn = 1, ochar++; else if (*ochar == 'n') mvars->ops[N] |= op, ochar++; else if (*ochar == 's') // Pre-1.4 legacy mvars->ops[N] |= op, ms_warn = 1, ochar++; else if (*ochar == '-') ochar++; else cops |= op; mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE | XOP_HAVE_EXPUNGE_SOLO); break; case 'R': op = OP_REMOVE | XOP_HAVE_REMOVE; goto cop; case 'x': op = OP_EXPUNGE_SOLO | XOP_HAVE_EXPUNGE_SOLO; goto cop; case 'X': op = OP_EXPUNGE | XOP_HAVE_EXPUNGE; goto cop; case 'F': cops |= XOP_PULL | XOP_PUSH; mvars->ops[F] |= XOP_HAVE_TYPE; break; case '0': mvars->ops[F] |= XOP_TYPE_NOOP | XOP_HAVE_TYPE; break; case 'n': case 'o': case 'd': case 'g': case 'f': case 'N': case 'u': --ochar; op = 0; cac: for (;; ochar++) { if (*ochar == 'n') op |= OP_NEW; else if (*ochar == 'o') op |= OP_OLD; else if (*ochar == 'g') op |= OP_GONE; else if (*ochar == 'd') op |= OP_GONE, delete_warn = 1; else if (*ochar == 'f') op |= OP_FLAGS; else if (*ochar == 'u') op |= OP_UPGRADE; else if (*ochar == 'N') op |= OP_UPGRADE, renew_warn = 1; else break; } if (op & OP_MASK_TYPE) { switch (op & XOP_MASK_DIR) { case XOP_PULL: mvars->ops[N] |= op & OP_MASK_TYPE; break; case XOP_PUSH: mvars->ops[F] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } } else { cops |= op; } mvars->ops[F] |= XOP_HAVE_TYPE; break; case 'L': op = XOP_PULL; goto cac; case 'H': op = XOP_PUSH; goto cac; case 'q': if (Verbosity > VERYQUIET) Verbosity--; break; case 'V': Verbosity = VERBOSE; break; case 'D': for (op = 0; *ochar; ochar++) { switch (*ochar) { case 'C': op |= DEBUG_CRASH; break; case 'd': op |= DEBUG_DRV; break; case 'D': op |= DEBUG_DRV | DEBUG_DRV_ALL; break; case 'm': op |= DEBUG_MAILDIR; break; case 'M': op |= DEBUG_MAIN; break; case 'n': op |= DEBUG_NET; break; case 'N': op |= DEBUG_NET | DEBUG_NET_ALL; break; case 's': op |= DEBUG_SYNC; break; default: error( "Unknown -D flag '%c'\n", *ochar ); return 1; } } if (!op) op = DEBUG_ALL; DFlags |= op; break; case 'y': DFlags |= DRYRUN; break; case 'e': DFlags |= EXT_EXIT; break; case 'T': for (; *ochar; ) { switch (*ochar++) { case 'a': DFlags |= FORCEASYNC(F); break; case 'A': DFlags |= FORCEASYNC(F) | FORCEASYNC(N); break; case 'j': DFlags |= KEEPJOURNAL; break; case 'J': DFlags |= FORCEJOURNAL; break; case 's': JLimit = strtol( ochar, &ochar, 10 ); break; case 'u': DFlags |= FAKEDUMBSTORE; break; case 'x': DFlags |= FAKEEXPUNGE; break; case 'z': DFlags |= ZERODELAY; break; default: error( "Unknown -T flag '%c'\n", *(ochar - 1) ); return 1; } } break; case 'v': version(); case 'h': usage( 0 ); default: error( "Unknown option '-%c'\n", *(ochar - 1) ); return 1; } } if (ms_warn) warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" ); if (renew_warn) warn( "Notice: --renew/-N are deprecated; use --upgrade/-u instead.\n" ); if (delete_warn) warn( "Notice: --delete/-d are deprecated; use --gone/-g instead.\n" ); if (DFlags & DEBUG_ANY) { Verbosity = VERBOSE; fputs( PACKAGE " " VERSION " called with:", stdout ); for (op = 1; op < argc; op++) printf( " '%s'", argv[op] ); puts( "" ); } else if (Verbosity >= TERSE && isatty( 1 )) { DFlags |= PROGRESS; } #ifdef __linux__ if (DFlags & DEBUG_CRASH) { signal( SIGSEGV, crashHandler ); signal( SIGBUS, crashHandler ); signal( SIGILL, crashHandler ); } #endif if (merge_ops( cops, mvars->ops, NULL )) return 1; if (load_config( config )) return 1; signal( SIGPIPE, SIG_IGN ); if (pipe( child_pipe )) { perror( "pipe" ); return 1; } fcntl( child_pipe[0], F_SETFL, O_NONBLOCK ); fcntl( child_pipe[1], F_SETFL, O_NONBLOCK ); init_notifier( &child_notifier, child_pipe[0], childReaper, NULL ); conf_notifier( &child_notifier, 0, POLLIN ); struct sigaction sa = { 0 }; sa.sa_handler = childHandler; sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; sigaction( SIGCHLD, &sa, NULL ); if (mvars->list_stores) list_stores( mvars, argv + oind ); else sync_chans( mvars, argv + oind ); return mvars->ret; } void cleanup_mainloop( void ) { cleanup_drivers(); wipe_notifier( &child_notifier ); } isync-1.5.1/src/sync.h0000644000175000001440000000473014652507516010253 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #ifndef SYNC_H #define SYNC_H #include "driver.h" #define F 0 // far side #define N 1 // near side #define sync_op_enum(fn) \ fn(OP, NEW) \ fn(OP, OLD) \ fn(OP, UPGRADE) \ fn(OP, GONE) \ fn(OP, FLAGS) \ fn(OP, EXPUNGE) \ fn(OP, EXPUNGE_SOLO) \ fn(OP, CREATE) \ fn(OP, REMOVE) \ \ fn(XOP, PUSH) \ fn(XOP, PULL) \ fn(XOP, HAVE_TYPE) /* Aka mode; have at least one of dir and type (see below) */ \ /* The following must all have the same bit shift from the corresponding OP_* flags. */ \ fn(XOP, HAVE_EXPUNGE) \ fn(XOP, HAVE_EXPUNGE_SOLO) \ fn(XOP, HAVE_CREATE) \ fn(XOP, HAVE_REMOVE) \ /* ... until here. */ \ fn(XOP, TYPE_NOOP) \ /* ... and here again from scratch. */ \ fn(XOP, EXPUNGE_NOOP) \ fn(XOP, EXPUNGE_SOLO_NOOP) \ fn(XOP, CREATE_NOOP) \ fn(XOP, REMOVE_NOOP) DEFINE_PFX_BIT_ENUM(sync_op_enum) #define OP_DFLT_TYPE (OP_NEW | OP_UPGRADE | OP_GONE | OP_FLAGS) #define OP_MASK_TYPE (OP_DFLT_TYPE | OP_OLD) // Asserted in the target side ops #define XOP_MASK_DIR (XOP_PUSH | XOP_PULL) DECL_BIT_FORMATTER_FUNCTION(ops, sync_op_enum) typedef struct channel_conf { struct channel_conf *next; const char *name; store_conf_t *stores[2]; const char *boxes[2]; const char *sync_state; string_list_t *patterns; int ops[2]; int max_messages; // For near side only. int expire_side; signed char expire_unread; char use_internal_date; } channel_conf_t; typedef struct group_conf { struct group_conf *next; const char *name; string_list_t *channels; } group_conf_t; extern channel_conf_t global_conf; extern channel_conf_t *channels; extern group_conf_t *groups; extern uint BufferLimit; extern int new_total[2], new_done[2]; extern int flags_total[2], flags_done[2]; extern int trash_total[2], trash_done[2]; extern int expunge_total[2], expunge_done[2]; extern const char *str_fn[2], *str_hl[2]; #define SYNC_OK 0 /* assumed to be 0 */ #define SYNC_FAIL 1 #define SYNC_BAD(fn) (4<<(fn)) #define BOX_POSSIBLE -1 #define BOX_ABSENT 0 #define BOX_PRESENT 1 /* All passed pointers must stay alive until cb is called. */ void sync_boxes( store_t *ctx[], const char * const names[], int present[], channel_conf_t *chan, void (*cb)( int sts, void *aux ), void *aux ); #endif isync-1.5.1/src/drv_imap.c0000644000175000001440000032531514764020762011075 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-FileCopyrightText: 2004 Theodore Y. Ts'o // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "imap_p.h" #include "socket.h" #include #include #ifdef HAVE_LIBSASL # include # include #endif #ifdef HAVE_MACOS_KEYCHAIN # include #endif #ifdef HAVE_LIBSSL enum { SSL_None, SSL_STARTTLS, SSL_IMAPS }; #endif typedef struct imap_server_conf { struct imap_server_conf *next; char *name; server_conf_t sconf; char *user; char *user_cmd; char *pass; char *pass_cmd; int max_in_progress; uint cap_mask; string_list_t *auth_mechs; #ifdef HAVE_LIBSSL char ssl_type; #endif #ifdef HAVE_MACOS_KEYCHAIN char use_keychain; #endif char failed; } imap_server_conf_t; typedef union imap_store_conf { store_conf_t gen; struct { STORE_CONF imap_server_conf_t *server; char *path; // Note: this may be modified after the delimiter is determined. char delimiter; char use_namespace; char use_lsub; }; } imap_store_conf_t; typedef union imap_store imap_store_t; enum { AtomNil, AtomString, AtomLiteral, AtomChunkedLiteral }; typedef struct { int (*enter)( imap_store_t *ctx ); int (*leave)( imap_store_t *ctx ); int (*atom)( imap_store_t *ctx, char *val, uint len, int type ); int (*done)( imap_store_t *ctx, int resp ); } parse_list_cb_t; typedef struct { parse_list_cb_t *callback; uint need_bytes; int level, chunked, big_literal; enum { NotLiteral, AtomicLiteral, ChunkedLiteral } in_literal; const char *err; } parse_list_state_t; typedef enum { NsInit, Ns1st, Ns1stNs, Ns1stDelim, NsDone } ParseNsState; typedef enum { FetchInit, FetchAttrib, FetchUid, FetchFlags, FetchFlagVal, FetchDate, FetchSize, FetchBody, FetchBodyChunk, FetchHeaders, FetchHeaderFields, FetchHeaderBracket, FetchHeaderCont } ParseFetchState; typedef enum { ListInit, ListAttrib, ListSkip } ParseListState; typedef enum { PermflagsInit, PermflagsFlags, PermflagsHasFwd } ParsePermflagsState; typedef struct imap_cmd imap_cmd_t; union imap_store { store_t gen; struct { STORE(union imap_store) const char *label; // foreign const char *name; char *prefix; uint ref_count; uint opts; enum { SST_BAD, SST_HALF, SST_GOOD } state; // The trash folder's existence is not confirmed yet enum { TrashUnknown, TrashChecking, TrashKnown } trashnc; // What kind of BODY-less FETCH response we're expecting enum { FetchNone, FetchMsgs, FetchUidNext } fetch_sts; uint got_namespace:1; uint has_forwarded:1; uint capability_hack:1; char delimiter[2]; // Hierarchy delimiter char *ns_prefix, ns_delimiter; // NAMESPACE info string_list_t *boxes; // _list results char listed; // was _list already run with these flags? // note that the message counts do _not_ reflect stats from msgs, // but mailbox totals. int total_msgs, recent_msgs; uint uidvalidity, uidnext; imap_messages_t msgs; struct { // pending FETCH result char *body, *msgid; time_t date; uint seq, uid, size, body_len; uchar flags, status; char tuid[TUIDL]; } fetch; uint caps; // CAPABILITY results string_list_t *auth_mechs; parse_list_state_t parse_list_sts; const char *parse_list_what; char *parse_list_cmd; union { ParseNsState ns_state; ParseFetchState fetch_state; ParseListState list_state; ParsePermflagsState permflags_state; int any_state; // We always init this one to zero }; // Command queue imap_cmd_t *pending, **pending_append; imap_cmd_t *in_progress, **in_progress_append; int nexttag, num_in_progress; uint buffer_mem; // Memory currently occupied by buffers in the queue // Used during sequential operations like connect enum { GreetingPending = 0, GreetingBad, GreetingOk, GreetingPreauth } greeting; int expectBYE; // LOGOUT is in progress int expectEOF; // received LOGOUT's OK or unsolicited BYE int canceling; // imap_cancel_cmds() is in progress union { void (*imap_open)( int sts, void *aux ); void (*imap_cancel)( void *aux ); } callbacks; void *callback_aux; #ifdef HAVE_LIBSASL sasl_conn_t *sasl; int sasl_cont; #endif void (*expunge_callback)( message_t *msg, void *aux ); void (*bad_callback)( void *aux ); void *drv_callback_aux; conn_t conn; // This is BIG, so put it last }; }; #define IMAP_CMD \ struct imap_cmd *next; \ char *cmd; \ int tag; \ \ struct { \ /* Will be called on each continuation request until it resets this pointer. \ * Needs to invoke bad_callback and return -1 on error, otherwise return 0. */ \ int (*cont)( imap_store_t *ctx, imap_cmd_t *cmd, const char *prompt ); \ void (*done)( imap_store_t *ctx, imap_cmd_t *cmd, int response ); \ char *data; \ uint data_len; \ uint uid; /* to identify fetch responses */ \ char high_prio; /* if command is queued, put it at the front of the queue. */ \ char to_trash; /* we are storing to trash, not current. */ \ char create; /* create the mailbox if we get an error which suggests so. */ \ char failok; /* Don't complain about NO (@1) / BAD (@2) response. */ \ } param; struct imap_cmd { IMAP_CMD }; #define IMAP_CMD_SIMPLE \ IMAP_CMD \ void (*callback)( int sts, void *aux ); \ void *callback_aux; typedef union { imap_cmd_t gen; struct { IMAP_CMD_SIMPLE }; } imap_cmd_simple_t; typedef union { imap_cmd_simple_t gen; struct { IMAP_CMD_SIMPLE msg_data_t *msg_data; }; } imap_cmd_fetch_msg_t; typedef union { imap_cmd_t gen; struct { IMAP_CMD void (*callback)( int sts, uint uid, void *aux ); void *callback_aux; }; } imap_cmd_out_uid_t; typedef union { imap_cmd_t gen; struct { IMAP_CMD void (*callback)( int sts, message_t *msgs, void *aux ); void *callback_aux; imap_message_t **out_msgs; uint uid; }; } imap_cmd_find_new_t; #define IMAP_CMD_REFCOUNTED_STATE \ uint ref_count; \ int ret_val; typedef struct { IMAP_CMD_REFCOUNTED_STATE } imap_cmd_refcounted_state_t; typedef union { imap_cmd_t gen; struct { IMAP_CMD imap_cmd_refcounted_state_t *state; }; } imap_cmd_refcounted_t; #define CAP(cap) (ctx->caps & (1 << (cap))) enum CAPABILITY { IMAP4REV1, NOLOGIN, #ifdef HAVE_LIBSASL SASLIR, #endif #ifdef HAVE_LIBSSL STARTTLS, #endif UIDPLUS, LITERALPLUS, LITERALMINUS, MOVE, NAMESPACE, UTF8_ACCEPT, UTF8_ONLY, COMPRESS_DEFLATE }; static const struct { const char *str; uint len; } cap_list[] = { { "IMAP4REV1", 9 }, { "LOGINDISABLED", 13 }, #ifdef HAVE_LIBSASL { "SASL-IR", 7 }, #endif #ifdef HAVE_LIBSSL { "STARTTLS", 8 }, #endif { "UIDPLUS", 7 }, { "LITERAL+", 8 }, { "LITERAL-", 8 }, { "MOVE", 4 }, { "NAMESPACE", 9 }, { "UTF8=ACCEPT", 11 }, { "UTF8=ONLY", 9 }, { "COMPRESS=DEFLATE", 16 }, }; #define RESP_OK 0 #define RESP_NO 1 #define RESP_CANCEL 2 static INLINE void imap_ref( imap_store_t *ctx ) { ++ctx->ref_count; } static int imap_deref( imap_store_t *ctx ); static void imap_invoke_bad_callback( imap_store_t *ctx ); // Keep the MESSAGE_FLAGS in sync (grep that)! static const struct { const char *str; const char *ustr; uint len; } Flags[] = { { "\\Draft", "\\DRAFT", 6 }, // 'D' { "\\Flagged", "\\FLAGGED", 8 }, // 'F' { "$Forwarded", "$FORWARDED", 10 }, // 'P' { "\\Answered", "\\ANSWERED", 9 }, // 'R' { "\\Seen", "\\SEEN", 5 }, // 'S' { "\\Deleted", "\\DELETED", 8 }, // 'T' }; static imap_cmd_t * new_imap_cmd( uint size ) { imap_cmd_t *cmd = nfmalloc( size ); memset( &cmd->param, 0, sizeof(cmd->param) ); return cmd; } #define INIT_IMAP_CMD(type, cmdp, cb, aux) \ type *cmdp = (type *)new_imap_cmd( sizeof(*cmdp) ); \ cmdp->callback = cb; \ cmdp->callback_aux = aux; static void done_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd, int response ) { if (cmd->param.data) { free( cmd->param.data ); cmd->param.data = NULL; // This needs to happen before calling back. ctx->buffer_mem -= cmd->param.data_len; } cmd->param.done( ctx, cmd, response ); free( cmd->cmd ); free( cmd ); } static void send_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd ) { int litplus = 0, iovcnt = 3; conn_iovec_t iov[5]; char tagbuf[16]; cmd->tag = ++ctx->nexttag; uint tbufl = nfsnprintf( tagbuf, sizeof(tagbuf), "%d ", cmd->tag ); uint cmdl = strlen( cmd->cmd ); if (cmd->param.data) { assert( cmdl > 2 && !memcmp( cmd->cmd + cmdl - 2, "+}", 2 ) ); if ((!cmd->param.to_trash || ctx->trashnc != TrashUnknown) && ((CAP(LITERALPLUS) && cmd->param.data_len <= 100*1024) || (CAP(LITERALMINUS) && cmd->param.data_len <= 4*1024))) { litplus = 1; } else { cmd->cmd[cmdl - 2] = '}'; cmd->cmd[cmdl - 1] = 0; cmdl--; } } if (DFlags & DEBUG_NET) { if (ctx->num_in_progress) printf( "(%d in progress) ", ctx->num_in_progress ); if (starts_with( cmd->cmd, -1, "LOGIN", 5 )) printf( "%s>>> %sLOGIN \r\n", ctx->label, tagbuf ); else if (starts_with( cmd->cmd, -1, "AUTHENTICATE PLAIN", 18 )) printf( "%s>>> %sAUTHENTICATE PLAIN \r\n", ctx->label, tagbuf ); else printf( "%s>>> %s%s\r\n", ctx->label, tagbuf, cmd->cmd ); fflush( stdout ); } iov[0].buf = tagbuf; iov[0].len = tbufl; iov[0].takeOwn = KeepOwn; iov[1].buf = cmd->cmd; iov[1].len = cmdl; iov[1].takeOwn = KeepOwn; iov[2].buf = "\r\n"; iov[2].len = 2; iov[2].takeOwn = KeepOwn; if (litplus) { if (DFlags & DEBUG_NET) { if (DFlags & DEBUG_NET_ALL) { printf( "%s>>>>>>>>>\n", ctx->label ); fwrite( cmd->param.data, cmd->param.data_len, 1, stdout ); printf( "%s>>>>>>>>>\n", ctx->label ); } else { printf( "%s>>>>> (%u bytes omitted)\n", ctx->label, cmd->param.data_len ); } fflush( stdout ); } iov[3].buf = cmd->param.data; iov[3].len = cmd->param.data_len; iov[3].takeOwn = GiveOwn; cmd->param.data = NULL; ctx->buffer_mem -= cmd->param.data_len; iov[4].buf = "\r\n"; iov[4].len = 2; iov[4].takeOwn = KeepOwn; iovcnt = 5; } socket_write( &ctx->conn, iov, iovcnt ); if (cmd->param.to_trash && ctx->trashnc == TrashUnknown) ctx->trashnc = TrashChecking; cmd->next = NULL; *ctx->in_progress_append = cmd; ctx->in_progress_append = &cmd->next; ctx->num_in_progress++; socket_expect_activity( &ctx->conn, 1 ); } static int cmd_sendable( imap_store_t *ctx, imap_cmd_t *cmd ) { if (ctx->conn.write_buf) { /* Don't build up a long queue in the socket, so we can * control when the commands are actually sent. * This allows reliable cancelation of pending commands, * injecting commands in front of other pending commands, * and keeping num_in_progress accurate. */ return 0; } if (ctx->in_progress) { /* If the last command in flight ... */ imap_cmd_t *cmdp = (imap_cmd_t *)((char *)ctx->in_progress_append - offsetof(imap_cmd_t, next)); if (cmdp->param.cont || cmdp->param.data) { /* ... is expected to trigger a continuation request, we need to * wait for that round-trip before sending the next command. */ return 0; } } if (cmd->param.to_trash && ctx->trashnc == TrashChecking) { /* Don't build a queue of MOVE/COPY/APPEND commands that may all fail. */ return 0; } if (ctx->num_in_progress >= ctx->conf->server->max_in_progress) { /* Too many commands in flight. */ return 0; } return 1; } static void flush_imap_cmds( imap_store_t *ctx ) { imap_cmd_t *cmd; if ((cmd = ctx->pending) && cmd_sendable( ctx, cmd )) { if (!(ctx->pending = cmd->next)) ctx->pending_append = &ctx->pending; send_imap_cmd( ctx, cmd ); } } static void cancel_pending_imap_cmds( imap_store_t *ctx ) { imap_cmd_t *cmd; while ((cmd = ctx->pending)) { if (!(ctx->pending = cmd->next)) ctx->pending_append = &ctx->pending; done_imap_cmd( ctx, cmd, RESP_CANCEL ); } } static void cancel_sent_imap_cmds( imap_store_t *ctx ) { imap_cmd_t *cmd; socket_expect_activity( &ctx->conn, 0 ); while ((cmd = ctx->in_progress)) { ctx->in_progress = cmd->next; /* don't update num_in_progress and in_progress_append - store is dead */ done_imap_cmd( ctx, cmd, RESP_CANCEL ); } } static void submit_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd ) { assert( ctx ); assert( ctx->bad_callback ); assert( cmd ); assert( cmd->param.done ); if ((ctx->pending && !cmd->param.high_prio) || !cmd_sendable( ctx, cmd )) { if (ctx->pending && cmd->param.high_prio) { cmd->next = ctx->pending; ctx->pending = cmd; } else { cmd->next = NULL; *ctx->pending_append = cmd; ctx->pending_append = &cmd->next; } } else { send_imap_cmd( ctx, cmd ); } } static void imap_exec( imap_store_t *ctx, imap_cmd_t *cmdp, void (*done)( imap_store_t *ctx, imap_cmd_t *cmd, int response ), const char *fmt, ... ) { va_list ap; if (!cmdp) cmdp = new_imap_cmd( sizeof(*cmdp) ); cmdp->param.done = done; va_start( ap, fmt ); cmdp->cmd = xvasprintf( fmt, ap ); va_end( ap ); submit_imap_cmd( ctx, cmdp ); } static void transform_box_response( int *response ) { switch (*response) { case RESP_CANCEL: *response = DRV_CANCELED; break; case RESP_NO: *response = DRV_BOX_BAD; break; default: *response = DRV_OK; break; } } static void imap_done_simple_box( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)cmd; transform_box_response( &response ); cmdp->callback( response, cmdp->callback_aux ); } static void transform_msg_response( int *response ) { switch (*response) { case RESP_CANCEL: *response = DRV_CANCELED; break; case RESP_NO: *response = DRV_MSG_BAD; break; default: *response = DRV_OK; break; } } static void imap_done_simple_msg( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)cmd; transform_msg_response( &response ); cmdp->callback( response, cmdp->callback_aux ); } static imap_cmd_refcounted_state_t * imap_refcounted_new_state( uint sz ) { imap_cmd_refcounted_state_t *sts = nfmalloc( sz ); sts->ref_count = 1; /* so forced sync does not cause an early exit */ sts->ret_val = DRV_OK; return sts; } #define INIT_REFCOUNTED_STATE(type, sts, cb, aux) \ type *sts = (type *)imap_refcounted_new_state( sizeof(type) ); \ sts->callback = cb; \ sts->callback_aux = aux; static imap_cmd_t * imap_refcounted_new_cmd( imap_cmd_refcounted_state_t *sts ) { imap_cmd_refcounted_t *cmd = (imap_cmd_refcounted_t *)new_imap_cmd( sizeof(*cmd) ); cmd->state = sts; sts->ref_count++; return &cmd->gen; } #define DONE_REFCOUNTED_STATE_FINALIZE(sts, finalize) \ if (!--sts->ref_count) { \ finalize \ sts->callback( sts->ret_val, sts->callback_aux ); \ free( sts ); \ } #define DONE_REFCOUNTED_STATE(sts) \ DONE_REFCOUNTED_STATE_FINALIZE(sts, ) #define DONE_REFCOUNTED_STATE_ARGS(sts, finalize, ...) \ if (!--sts->ref_count) { \ finalize \ sts->callback( sts->ret_val, __VA_ARGS__, sts->callback_aux ); \ free( sts ); \ } static void transform_refcounted_box_response( imap_cmd_refcounted_state_t *sts, int response ) { switch (response) { case RESP_CANCEL: sts->ret_val = DRV_CANCELED; break; case RESP_NO: if (sts->ret_val == DRV_OK) /* Don't override cancelation. */ sts->ret_val = DRV_BOX_BAD; break; } } static void transform_refcounted_msg_response( imap_cmd_refcounted_state_t *sts, int response ) { switch (response) { case RESP_CANCEL: sts->ret_val = DRV_CANCELED; break; case RESP_NO: if (sts->ret_val == DRV_OK) /* Don't override cancelation. */ sts->ret_val = DRV_MSG_BAD; break; } } static const char * imap_strchr( const char *s, char tc ) { for (;; s++) { char c = *s; if (c == '\\') c = *++s; if (!c) return NULL; if (c == tc) return s; } } static char * next_arg( char **ps, uint *len ) { char *ret, *s, *d; char c; assert( ps ); s = *ps; if (!s) return NULL; while (isspace( (uchar)*s )) s++; if (!*s) { *ps = NULL; return NULL; } if (*s == '"') { s++; ret = d = s; while ((c = *s++) != '"') { if (c == '\\') c = *s++; if (!c) { *ps = NULL; return NULL; } *d++ = c; } *d = 0; *len = (uint)(d - ret); if (!*s) s = NULL; } else { ret = s; while ((c = *s)) { if (isspace( (uchar)c )) { *len = (uint)(s - ret); *s++ = 0; if (!*s) s = NULL; goto out; } s++; } *len = (uint)(s - ret); s = NULL; } out: *ps = s; return ret; } enum { LIST_OK, LIST_PARTIAL, LIST_BAD }; static int parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts ) { char *s = *sp, *d, *p; uint bytes; uint n; char c; assert( ctx ); assert( sts ); if (sts->in_literal != NotLiteral) { bytes = sts->need_bytes; if (!bytes) goto getline; if (sts->in_literal == ChunkedLiteral) goto get_chunked; goto get_atomic; } if (!s) { sts->err = "missing value"; return LIST_BAD; } for (;;) { while (isspace( (uchar)*s )) s++; if (sts->level && *s == ')') { s++; if (sts->callback->leave && sts->callback->leave( ctx ) != LIST_OK) goto bail; sts->level--; goto next; } if (*s == '(') { /* sublist */ s++; sts->level++; if (sts->callback->enter && sts->callback->enter( ctx ) != LIST_OK) goto bail; goto next2; } else if (*s == '{') { /* literal */ bytes = strtoul( s + 1, &s, 10 ); if (*s != '}' || *++s) { sts->err = "malformed literal"; goto bail; } if (bytes >= INT_MAX) { sts->err = "excessively large literal - THIS MIGHT BE AN ATTEMPT TO HACK YOU!"; goto bail; } if (sts->chunked) { sts->chunked = 0; assert( sts->callback->atom ); if (sts->callback->atom( ctx, NULL, bytes, AtomChunkedLiteral ) != LIST_OK) goto bail; sts->in_literal = ChunkedLiteral; if (!bytes) goto nobytes; sts->big_literal = 1; get_chunked: n = 1; } else { if (bytes > sizeof(ctx->conn.buf)) { sts->err = "unexpectedly large literal"; goto bail; } sts->in_literal = AtomicLiteral; sts->big_literal = bytes > 64; get_atomic: n = bytes; } if (!(p = socket_read( &ctx->conn, n, bytes, &n ))) goto postpone; if (p == (void *)~0) { badeof: sts->err = "unexpected EOF"; goto bail; } if (DFlags & DEBUG_NET) { if (!sts->big_literal) { xprintf( "%s%.*!s\n", ctx->label, (int)n, p ); } else if (DFlags & DEBUG_NET_ALL) { printf( "%s=========\n", ctx->label ); fwrite( p, n, 1, stdout ); if (n && p[n - 1] != '\n') fputs( "\n(no-nl) ", stdout ); printf( "%s=========\n", ctx->label ); } else { printf( "%s=== (%u bytes omitted)\n", ctx->label, n ); } fflush( stdout ); } if (sts->callback->atom && sts->callback->atom( ctx, p, n, AtomLiteral ) != LIST_OK) goto bail; bytes -= n; if (bytes > 0) goto postpone; nobytes: if (sts->in_literal == ChunkedLiteral && sts->callback->atom( ctx, NULL, 0, AtomLiteral ) != LIST_OK) goto bail; getline: if (!(s = socket_read_line( &ctx->conn ))) goto postpone; if (s == (void *)~0) goto badeof; if (DFlags & DEBUG_NET) { printf( "%s%s\n", ctx->label, s ); fflush( stdout ); } sts->in_literal = NotLiteral; } else if (*s == '"') { /* quoted string */ s++; p = d = s; while ((c = *s++) != '"') { if (c == '\\') c = *s++; if (!c) { sts->err = "unterminated quoted string"; goto bail; } *d++ = c; } if (sts->callback->atom && sts->callback->atom( ctx, p, (uint)(d - p), AtomString ) != LIST_OK) goto bail; } else { /* atom */ p = s; for (; *s && !isspace( (uchar)*s ); s++) if (sts->level && *s == ')') break; if (sts->callback->atom) { int t = AtomString; n = (uint)(s - p); if (equals_upper( p, (int)n, "NIL", 3 )) p = NULL, n = 0, t = AtomNil; if (sts->callback->atom( ctx, p, n, t ) != LIST_OK) goto bail; } } next: if (!sts->level) break; next2: if (!*s) { sts->err = "unterminated list"; goto bail; } } *sp = s; return LIST_OK; postpone: sts->need_bytes = bytes; return LIST_PARTIAL; bail: sts->level = 0; return LIST_BAD; } static void parse_list_init( imap_store_t *ctx, parse_list_cb_t *cb ) { parse_list_state_t *sts = &ctx->parse_list_sts; sts->in_literal = NotLiteral; sts->level = 0; sts->chunked = 0; sts->callback = cb; sts->err = NULL; ctx->any_state = 0; } static int parse_list_continue( imap_store_t *ctx ) { int resp; if ((resp = parse_imap_list( ctx, &ctx->parse_list_cmd, &ctx->parse_list_sts )) != LIST_PARTIAL) { if (ctx->parse_list_sts.callback->done) resp = ctx->parse_list_sts.callback->done( ctx, resp ); if (resp == LIST_BAD) { if (!ctx->parse_list_sts.err) ctx->parse_list_sts.err = "unexpected value"; error( "IMAP error: malformed %s response from %s: %s\n", ctx->parse_list_what, ctx->conn.name, ctx->parse_list_sts.err ); } } return resp; } static int parse_next_list( imap_store_t *ctx, parse_list_cb_t *cb ) { parse_list_init( ctx, cb ); return parse_list_continue( ctx ); } static int parse_list( imap_store_t *ctx, char *s, parse_list_cb_t *cb, const char *what ) { ctx->parse_list_cmd = s; ctx->parse_list_what = what; return parse_next_list( ctx, cb ); } static parse_list_cb_t ignore_one_list_cb = { .done = NULL }; static int ignore_two_done( imap_store_t *ctx, int sts ) { if (sts != LIST_OK) return sts; return parse_next_list( ctx, &ignore_one_list_cb ); } static parse_list_cb_t ignore_two_lists_cb = { .done = ignore_two_done, }; static int namespace_rsp_enter( imap_store_t *ctx ) { // We use only the 1st personal namespace. Making this configurable // would not add value over just specifying Path. switch (ctx->ns_state) { case NsInit: ctx->ns_state = Ns1st; return LIST_OK; case Ns1st: ctx->ns_state = Ns1stNs; return LIST_OK; case NsDone: return LIST_OK; default: return LIST_BAD; } } static int namespace_rsp_leave( imap_store_t *ctx ) { if (ctx->ns_state != NsDone) return LIST_BAD; return LIST_OK; } static int namespace_rsp_atom( imap_store_t *ctx, char *val, uint len, int type ATTR_UNUSED ) { if (ctx->ns_state == Ns1stNs) { if (!val) return LIST_BAD; ctx->ns_prefix = nfstrndup( val, len ); ctx->ns_state = Ns1stDelim; } else if (ctx->ns_state == Ns1stDelim) { if (val) { if (!len) return LIST_BAD; ctx->ns_delimiter = val[0]; } // Namespace response extensions may follow here; we don't care. ctx->ns_state = NsDone; } return LIST_OK; } static int namespace_rsp_done( imap_store_t *ctx, int sts ) { if (sts != LIST_OK) return sts; return parse_next_list( ctx, &ignore_two_lists_cb ); } static parse_list_cb_t namespace_rsp_cb = { .enter = namespace_rsp_enter, .leave = namespace_rsp_leave, .atom = namespace_rsp_atom, .done = namespace_rsp_done, }; static time_t parse_date( const char *str ) { char *end; time_t date; int hours, mins; struct tm datetime; memset( &datetime, 0, sizeof(datetime) ); if (!(end = strptime( str, " %e-%b-%Y %H:%M:%S ", &datetime ))) return -1; if ((date = timegm( &datetime )) == -1) return -1; if (sscanf( end, "%3d%2d", &hours, &mins ) != 2) return -1; return date - (hours * 60 + mins) * 60; } static void parse_fetched_flag( char *val, uint len, uchar *flags, uchar *status ) { if (val[0] != '\\' && val[0] != '$') return; to_upper( val, len ); if (equals( val, len, "\\RECENT", 7 )) { *status |= M_RECENT; return; } for (uint i = 0; i < as(Flags); i++) { if (equals( val, len, Flags[i].ustr, Flags[i].len )) { *flags |= 1 << i; return; } } if (val[0] == '$') return; // Ignore unknown user-defined flags (keywords) if (len > 2 && val[1] == 'X' && val[2] == '-') return; // Ignore system flag extensions warn( "IMAP warning: unknown system flag %.*s\n", (int)len, val ); } static void parse_fetched_header( char *val, uint vlen, uint uid, char *tuid, char **msgid ) { char *end; int off, in_msgid = 0; for (; (end = memchr( val, '\n', vlen )); val = end + 1) { int len = (int)(end - val); vlen -= len + 1; if (len && end[-1] == '\r') len--; if (!len) break; if (starts_with_upper( val, len, "X-TUID: ", 8 )) { if (len != 8 + TUIDL) { warn( "IMAP warning: malformed X-TUID header (UID %u)\n", uid ); continue; } memcpy( tuid, val + 8, TUIDL ); in_msgid = 0; continue; } if (starts_with_upper( val, len, "MESSAGE-ID:", 11 )) { off = 11; } else if (in_msgid) { if (!isspace( val[0] )) { in_msgid = 0; continue; } off = 1; } else { continue; } while (off < len && isspace( val[off] )) off++; if (off == len) { in_msgid = 1; continue; } *msgid = nfstrndup( val + off, (uint)(len - off) ); in_msgid = 0; } } static int str_to_num( const char *val, uint len, uint *outp ) { uint i = 0; uint out = 0; if (!len) return 0; for (;;) { uchar c = val[i] - '0'; if (c > 9) // Covers underflow as well return 0; out += c; if (++i == len) break; if (out >= UINT_MAX / 10) return 0; out *= 10; } *outp = out; return 1; } static int fetch_rsp_enter( imap_store_t *ctx ) { switch (ctx->fetch_state) { case FetchInit: ctx->fetch_state = FetchAttrib; return LIST_OK; case FetchFlags: ctx->fetch_state = FetchFlagVal; return LIST_OK; case FetchHeaders: ctx->fetch_state = FetchHeaderFields; return LIST_OK; default: return LIST_BAD; } } static int fetch_rsp_leave( imap_store_t *ctx ) { switch (ctx->fetch_state) { case FetchAttrib: return LIST_OK; case FetchFlagVal: ctx->fetch_state = FetchAttrib; return LIST_OK; case FetchHeaderFields: ctx->fetch_state = FetchHeaderBracket; return LIST_OK; default: return LIST_BAD; } } static int fetch_rsp_atom( imap_store_t *ctx, char *val, uint len, int type ) { switch (ctx->fetch_state) { case FetchInit: case FetchFlags: case FetchHeaders: return LIST_BAD; case FetchAttrib: if (!len) { ctx->parse_list_sts.err = "bogus attribute name"; return LIST_BAD; } uchar field; to_upper( val, len ); if (equals( val, len, "UID", 3 )) { ctx->fetch_state = FetchUid; return LIST_OK; } else if (equals( val, len, "FLAGS", 5 )) { ctx->fetch_state = FetchFlags; field = M_FLAGS; } else if (equals( val, len, "INTERNALDATE", 12 )) { ctx->fetch_state = FetchDate; field = M_DATE; } else if (equals( val, len, "RFC822.SIZE", 11 )) { ctx->fetch_state = FetchSize; field = M_SIZE; } else if (equals( val, len, "BODY[]", 6 ) || equals( val, len, "BODY[HEADER]", 12 )) { ctx->parse_list_sts.chunked = 1; ctx->fetch_state = FetchBody; field = M_BODY; } else if (equals( val, len, "BODY[HEADER.FIELDS", 18 )) { ctx->fetch_state = FetchHeaders; field = M_HEADER; } else { ctx->parse_list_sts.err = "unexpected attribute"; return LIST_BAD; } if (ctx->fetch.status & field) { ctx->parse_list_sts.err = "duplicated attribute"; return LIST_BAD; } ctx->fetch.status |= field; return LIST_OK; case FetchUid: if (!str_to_num( val, len, &ctx->fetch.uid )) { ctx->parse_list_sts.err = "unable to parse UID"; return LIST_BAD; } break; case FetchFlagVal: if (!len) { ctx->parse_list_sts.err = "unable to parse FLAGS"; return LIST_BAD; } parse_fetched_flag( val, len, &ctx->fetch.flags, &ctx->fetch.status ); return LIST_OK; case FetchDate: if (type != AtomString) { dfail: ctx->parse_list_sts.err = "unable to parse INTERNALDATE"; return LIST_BAD; } val[len] = 0; if ((ctx->fetch.date = parse_date( val )) == -1) goto dfail; break; case FetchSize: if (!str_to_num( val, len, &ctx->fetch.size )) { ctx->parse_list_sts.err = "unable to parse RFC822.SIZE"; return LIST_BAD; } break; case FetchBody: if (type != AtomChunkedLiteral) { ctx->parse_list_sts.err = "BODY is no literal"; return LIST_BAD; } ctx->fetch.body = nfmalloc( len ); ctx->fetch.body_len = 0; ctx->fetch_state = FetchBodyChunk; return LIST_OK; case FetchBodyChunk: if (!len) break; memcpy( ctx->fetch.body + ctx->fetch.body_len, val, len ); ctx->fetch.body_len += len; return LIST_OK; case FetchHeaderFields: // This looks like BODY[HEADER.FIELDS (X-TUID Message-Id)] {content}. // To avoid parsing the bracketed list, we just treat these as separate tokens. return LIST_OK; case FetchHeaderBracket: if (!equals( val, len, "]", 1 )) { bfail: ctx->parse_list_sts.err = "unable to parse BODY[HEADER.FIELDS ...]"; return LIST_BAD; } ctx->fetch_state = FetchHeaderCont; return LIST_OK; case FetchHeaderCont: if (!val) goto bfail; parse_fetched_header( val, len, ctx->fetch.uid, ctx->fetch.tuid, &ctx->fetch.msgid ); break; } ctx->fetch_state = FetchAttrib; return LIST_OK; } static int fetch_rsp_done( imap_store_t *ctx, int sts ) { imap_message_t *cur; msg_data_t *msgdata; imap_cmd_t *cmdp; if (sts != LIST_OK) goto bail; uchar status = ctx->fetch.status; if (!ctx->fetch.uid) { // Ignore async flag updates for now. status &= ~(M_FLAGS | M_RECENT); } else if (status & M_BODY) { for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next) if (cmdp->param.uid == ctx->fetch.uid) goto gotuid; ctx->parse_list_sts.err = "unexpected BODY"; sts = LIST_BAD; goto bail; gotuid: msgdata = ((imap_cmd_fetch_msg_t *)cmdp)->msg_data; msgdata->data = ctx->fetch.body; ctx->fetch.body = NULL; msgdata->len = ctx->fetch.body_len; msgdata->date = ctx->fetch.date; if (status & M_FLAGS) msgdata->flags = ctx->fetch.flags; status &= ~(M_FLAGS | M_RECENT | M_BODY | M_DATE); } else if (ctx->fetch_sts == FetchUidNext) { // Workaround for server not sending UIDNEXT and/or APPENDUID. ctx->uidnext = ctx->fetch.uid + 1; } else if (ctx->fetch_sts == FetchMsgs) { imap_ensure_absolute( &ctx->msgs ); // In case of interleaved EXPUNGE cur = imap_new_msg( & ctx->msgs ); cur->seq = ctx->fetch.seq; cur->uid = ctx->fetch.uid; cur->flags = ctx->fetch.flags; cur->status = status; cur->size = ctx->fetch.size; cur->msgid = ctx->fetch.msgid; ctx->fetch.msgid = NULL; if (ctx->fetch.tuid[0]) memcpy( cur->tuid, ctx->fetch.tuid, TUIDL ); status &= ~(M_FLAGS | M_RECENT | M_SIZE | M_HEADER); } else { // These may come in as a result of STORE FLAGS despite .SILENT. status &= ~(M_FLAGS | M_RECENT); } if (status) { ctx->parse_list_sts.err = "extraneous data"; sts = LIST_BAD; } bail: free( ctx->fetch.body ); ctx->fetch.body = NULL; free( ctx->fetch.msgid ); ctx->fetch.msgid = NULL; return sts; } static parse_list_cb_t fetch_rsp_cb = { .enter = fetch_rsp_enter, .leave = fetch_rsp_leave, .atom = fetch_rsp_atom, .done = fetch_rsp_done, }; static void parse_capability( imap_store_t *ctx, char *cmd ) { char *arg; uint i, argl; free_string_list( ctx->auth_mechs ); ctx->auth_mechs = NULL; ctx->caps = 0x80000000; while ((arg = next_arg( &cmd, &argl ))) { to_upper( arg, argl ); if (starts_with( arg, argl, "AUTH=", 5 )) { add_string_list_n( &ctx->auth_mechs, arg + 5, argl - 5 ); } else { for (i = 0; i < as(cap_list); i++) if (equals( arg, argl, cap_list[i].str, cap_list[i].len )) ctx->caps |= 1 << i; } } ctx->caps &= ~ctx->conf->server->cap_mask; if (!CAP(NOLOGIN)) add_string_list_n( &ctx->auth_mechs, "LOGIN", 5 ); } static int permflags_enter( imap_store_t *ctx ) { if (ctx->permflags_state != PermflagsInit) return LIST_BAD; ctx->permflags_state = PermflagsFlags; return LIST_OK; } static int permflags_atom( imap_store_t *ctx, char *val, uint len, int type ATTR_UNUSED ) { if (ctx->permflags_state == PermflagsHasFwd) return LIST_OK; if (ctx->permflags_state != PermflagsFlags || !val) return LIST_BAD; if (equals( val, len, "\\*", 2 ) || equals_upper( val, len, "$FORWARDED", 10 )) ctx->permflags_state = PermflagsHasFwd; return LIST_OK; } static parse_list_cb_t permflags_cb = { .enter = permflags_enter, .atom = permflags_atom, }; static int parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s ) { char *arg, *earg, *p; uint argl; if (!s || *s != '[') return RESP_OK; /* no response code */ s++; if (!(arg = next_arg( &s, &argl ))) { error( "IMAP error: malformed response code\n" ); return RESP_CANCEL; } to_upper( arg, argl ); if (equals( arg, argl, "UIDVALIDITY", 11 )) { if (!(arg = next_arg( &s, &argl )) || (ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg != ']')) { error( "IMAP error: malformed UIDVALIDITY status\n" ); return RESP_CANCEL; } } else if (equals( arg, argl, "UIDNEXT", 7 )) { if (!(arg = next_arg( &s, &argl )) || (ctx->uidnext = strtoul( arg, &earg, 10 ), *earg != ']')) { error( "IMAP error: malformed UIDNEXT status\n" ); return RESP_CANCEL; } } else if (equals( arg, argl, "CAPABILITY", 10 )) { if (!s || !(p = strchr( s, ']' ))) { error( "IMAP error: malformed CAPABILITY status\n" ); return RESP_CANCEL; } *p = 0; parse_capability( ctx, s ); if (strstr( p + 1, "mac.com IMAP4 service (Oracle Communications Messaging Server" )) ctx->capability_hack = 1; } else if (equals( arg, argl, "ALERT]", 6 )) { /* RFC2060 says that these messages MUST be displayed * to the user */ if (!s) { error( "IMAP error: malformed ALERT status\n" ); return RESP_CANCEL; } for (; isspace( (uchar)*s ); s++); error( "*** IMAP ALERT *** %s\n", s ); } else if (equals( arg, argl, "APPENDUID", 9 )) { // The checks ensure that: // - cmd => this is the final tagged response of a command, at which // point cmd was already removed from ctx->in_progress, so param.uid // is available for reuse. // - !param.uid => the command isn't actually a FETCH. This doesn't // really matter, as the field is safe to overwrite given the // previous condition; it just has no effect for non-APPENDs. if (!cmd || cmd->param.uid) { error( "IMAP error: unexpected APPENDUID status\n" ); return RESP_CANCEL; } if (!(arg = next_arg( &s, &argl )) || (ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg) || !(arg = next_arg( &s, &argl )) || (cmd->param.uid = strtoul( arg, &earg, 10 ), *earg != ']')) { error( "IMAP error: malformed APPENDUID status\n" ); return RESP_CANCEL; } } else if (equals( arg, argl, "PERMANENTFLAGS", 14 )) { parse_list_init( ctx, &permflags_cb ); // Note: we croak on LIST_PARTIAL, as literals are not expected anyway. if (parse_imap_list( ctx, &s, &ctx->parse_list_sts ) != LIST_OK || *s != ']') { error( "IMAP error: malformed PERMANENTFLAGS status\n" ); return RESP_CANCEL; } ctx->has_forwarded = (ctx->permflags_state == PermflagsHasFwd); } return RESP_OK; } static int list_rsp_enter( imap_store_t *ctx ) { if (ctx->list_state != ListInit) return LIST_BAD; ctx->list_state = ListAttrib; return LIST_OK; } static int list_rsp_atom( imap_store_t *ctx, char *val, uint len, int type ATTR_UNUSED ) { if (ctx->list_state == ListSkip) return LIST_OK; if (ctx->list_state != ListAttrib || !val) return LIST_BAD; if (equals_upper( val, len, "\\NOSELECT", 9 )) ctx->list_state = ListSkip; return LIST_OK; } static parse_list_cb_t list2_rsp_cb; static parse_list_cb_t list3_rsp_cb; static int list_rsp_done( imap_store_t *ctx, int sts ) { if (sts != LIST_OK) return sts; if (ctx->list_state == ListSkip) return parse_next_list( ctx, &ignore_two_lists_cb ); return parse_next_list( ctx, &list2_rsp_cb ); } static parse_list_cb_t list_rsp_cb = { .enter = list_rsp_enter, .atom = list_rsp_atom, .done = list_rsp_done, }; static int list2_rsp_enter( imap_store_t *ctx ATTR_UNUSED ) { return LIST_BAD; } static int list2_rsp_atom( imap_store_t *ctx, char *val, uint len, int type ATTR_UNUSED ) { if (!ctx->delimiter[0] && val) ctx->delimiter[0] = len > 0 ? val[0] : 0; return LIST_OK; } static int list2_rsp_done( imap_store_t *ctx, int sts ) { if (sts != LIST_OK) return sts; return parse_next_list( ctx, &list3_rsp_cb ); } static parse_list_cb_t list2_rsp_cb = { .enter = list2_rsp_enter, .atom = list2_rsp_atom, .done = list2_rsp_done, }; // Use this to check whether a full path refers to the actual IMAP INBOX. static int is_inbox( imap_store_t *ctx, const char *arg, int argl ) { if (!starts_with_upper( arg, argl, "INBOX", 5 )) return 0; if (argl > 5 && arg[5] != ctx->delimiter[0]) return 0; return 1; } // Use this to check whether a path fragment collides with the canonical INBOX. static int is_INBOX( imap_store_t *ctx, const char *arg, int argl ) { if (!starts_with( arg, argl, "INBOX", 5 )) return 0; if (argl > 5 && arg[5] != ctx->delimiter[0]) return 0; return 1; } static void normalize_INBOX( imap_store_t *ctx, char *arg, int argl ) { if (is_inbox( ctx, arg, argl )) memcpy( arg, "INBOX", 5 ); } static int list3_rsp_atom( imap_store_t *ctx, char *arg, uint len, int type ATTR_UNUSED ) { string_list_t *narg; int argl = (int)len; uint l; char rarg[1130]; // See imap_utf7_to_utf8() for the origin of that number if (!arg) return LIST_BAD; if (argl > 1000) { warn( "IMAP warning: ignoring unreasonably long mailbox name '%.100s[...]'\n", arg ); return LIST_OK; } // The server might be weird and have a non-uppercase INBOX. It // may legitimately do so, but we need the canonical spelling. normalize_INBOX( ctx, arg, argl ); if ((l = strlen( ctx->prefix ))) { if (!starts_with( arg, argl, ctx->prefix, l )) { if (!is_INBOX( ctx, arg, argl )) return LIST_OK; // INBOX and its subfolders bypass the namespace. } else { arg += l; argl -= l; // A folder named "INBOX" would be indistinguishable from the // actual INBOX after prefix stripping, so drop it. This applies // only to the fully uppercased spelling, as our canonical box // names are case-sensitive (unlike IMAP's INBOX). if (is_INBOX( ctx, arg, argl )) { if (argl > 5) // No need to complain about subfolders as well. warn( "IMAP warning: ignoring INBOX in %s\n", ctx->prefix ); return LIST_OK; } } } if (argl >= 5 && !memcmp( arg + argl - 5, ".lock", 5 )) /* workaround broken servers */ return LIST_OK; if (!(CAP(UTF8_ACCEPT) || CAP(UTF8_ONLY))) { int rargl = imap_utf7_to_utf8( arg, argl, rarg ); if (rargl < 0) { error( "IMAP error: invalid modified-UTF-7 string '%.*s'.\n", argl, arg ); return LIST_BAD; } assert( (uint)rargl < sizeof(rarg) ); arg = rarg; argl = rargl; } if (map_name( arg, argl, (char **)&narg, offsetof(string_list_t, string), ctx->delimiter, "/") < 0) { warn( "IMAP warning: ignoring mailbox %.*s (reserved character '/' in name)\n", argl, arg ); return LIST_OK; } // Validate the normalized name. Technically speaking, we could tolerate // '//' and '/./', and '/../' being forbidden is a limitation of the Maildir // driver, but there isn't really a legitimate reason for these being present. for (const char *p = narg->string, *sp = p;;) { char c; if (!(c = *p) || c == '/') { uint pcl = (uint)(p - sp); if (!pcl) { error( "IMAP warning: ignoring mailbox '%s' due to empty name component\n", narg->string ); free( narg ); return LIST_OK; } if (pcl == 1 && sp[0] == '.') { error( "IMAP warning: ignoring mailbox '%s' due to '.' component\n", narg->string ); free( narg ); return LIST_OK; } if (pcl == 2 && sp[0] == '.' && sp[1] == '.') { error( "IMAP error: LIST'd mailbox name '%s' contains '..' component - THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", narg->string ); free( narg ); return LIST_BAD; } if (!c) break; sp = ++p; } else { ++p; } } narg->next = ctx->boxes; ctx->boxes = narg; return LIST_OK; } static parse_list_cb_t list3_rsp_cb = { .enter = list2_rsp_enter, // (sic!) .atom = list3_rsp_atom, }; static int prepare_name( char **buf, const imap_store_t *ctx, const char *prefix, const char *name ) { uint pl = strlen( prefix ); switch (map_name( name, -1, buf, pl, "/", ctx->delimiter )) { case -1: error( "IMAP error: mailbox name %s contains server's hierarchy delimiter\n", name ); return -1; case -2: error( "IMAP error: server's hierarchy delimiter not known\n" ); return -1; default: memcpy( *buf, prefix, pl ); if (!(CAP(UTF8_ACCEPT) || CAP(UTF8_ONLY))) { char *nbuf = imap_utf8_to_utf7( *buf ); if (!nbuf) { error( "IMAP error: invalid UTF-8 string '%s'\n", *buf ); free( *buf ); return -1; } free( *buf ); *buf = nbuf; } return 0; } } static int prepare_box( char **buf, const imap_store_t *ctx ) { const char *name = ctx->name; const char *pfx = ctx->prefix; if (starts_with_upper( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { if (!memcmp( name, "INBOX", 5 )) { pfx = ""; } else if (!*pfx) { error( "IMAP error: cannot use unqualified '%s'. Did you mean INBOX?\n", name ); return -1; } } return prepare_name( buf, ctx, pfx, name ); } static int prepare_trash( char **buf, const imap_store_t *ctx ) { return prepare_name( buf, ctx, ctx->prefix, ctx->conf->trash ); } static void record_expunge( imap_store_t *ctx, uint seq ) { imap_message_t *eptr = imap_expunge_msg( &ctx->msgs, seq ); if (eptr) ctx->expunge_callback( &eptr->gen, ctx->drv_callback_aux ); } typedef union { imap_cmd_t gen; struct { IMAP_CMD imap_cmd_t *orig_cmd; }; } imap_cmd_trycreate_t; static void imap_open_store_greeted( imap_store_t * ); static void get_cmd_result_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_socket_read( void *aux ) { imap_store_t *ctx = (imap_store_t *)aux; imap_cmd_t *cmdp, **pcmdp; char *cmd, *arg, *arg1, *p; int resp, resp2, tag; uint seq, argl, argl1; conn_iovec_t iov[2]; for (;;) { if (ctx->parse_list_sts.level) { resp = parse_list_continue( ctx ); listret: if (resp == LIST_PARTIAL) { socket_expect_bytes( &ctx->conn, ctx->parse_list_sts.need_bytes ); return; } if (resp == LIST_BAD) break; continue; } if (!(cmd = socket_read_line( &ctx->conn ))) { socket_expect_bytes( &ctx->conn, 0 ); return; } if (cmd == (void *)~0) { if (!ctx->expectEOF) error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); /* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */ break; } if (DFlags & DEBUG_NET) { printf( "%s%s\n", ctx->label, cmd ); fflush( stdout ); } if (!(arg = next_arg( &cmd, &argl ))) { error( "IMAP error: empty response\n" ); break; } if (*arg == '*') { if (!(arg = next_arg( &cmd, &argl ))) { error( "IMAP error: malformed untagged response\n" ); break; } to_upper( arg, argl ); if (ctx->greeting == GreetingPending && equals( arg, argl, "PREAUTH", 7 )) { parse_response_code( ctx, NULL, cmd ); ctx->greeting = GreetingPreauth; dogreet: imap_ref( ctx ); imap_open_store_greeted( ctx ); if (imap_deref( ctx )) return; } else if (equals( arg, argl, "OK", 2 )) { parse_response_code( ctx, NULL, cmd ); if (ctx->greeting == GreetingPending) { ctx->greeting = GreetingOk; goto dogreet; } } else if (equals( arg, argl, "BYE", 3 )) { if (!ctx->expectBYE) { ctx->greeting = GreetingBad; error( "IMAP error: unexpected BYE response: %s\n", cmd ); /* We just wait for the server to close the connection now. */ ctx->expectEOF = 1; socket_expect_eof( &ctx->conn ); } else { /* We still need to wait for the LOGOUT's tagged OK. */ } } else if (ctx->greeting == GreetingPending) { error( "IMAP error: bogus greeting response %s\n", arg ); break; } else if (equals( arg, argl, "NO", 2 )) { warn( "Warning from IMAP server: %s\n", cmd ); } else if (equals( arg, argl, "BAD", 3 )) { error( "Error from IMAP server: %s\n", cmd ); } else if (equals( arg, argl, "CAPABILITY", 10 )) { parse_capability( ctx, cmd ); } else if (equals( arg, argl, "LIST", 4 ) || equals( arg, argl, "LSUB", 4 )) { resp = parse_list( ctx, cmd, &list_rsp_cb, "LIST" ); goto listret; } else if (equals( arg, argl, "NAMESPACE", 9 )) { resp = parse_list( ctx, cmd, &namespace_rsp_cb, "NAMESPACE" ); goto listret; } else if ((arg1 = next_arg( &cmd, &argl1 ))) { to_upper( arg1, argl1 ); if (equals( arg1, argl1, "EXISTS", 6 )) { ctx->total_msgs = atoi( arg ); } else if (equals( arg1, argl1, "EXPUNGE", 7 )) { if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1) { badseq: error( "IMAP error: malformed sequence number '%s'\n", arg ); break; } record_expunge( ctx, seq ); ctx->total_msgs--; } else if (equals( arg1, argl1, "RECENT", 6 )) { ctx->recent_msgs = atoi( arg ); } else if (equals( arg1, argl1, "FETCH", 5 )) { memset( &ctx->fetch, 0, sizeof(ctx->fetch) ); if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1) goto badseq; ctx->fetch.seq = seq; resp = parse_list( ctx, cmd, &fetch_rsp_cb, "FETCH" ); goto listret; } } else { error( "IMAP error: unrecognized untagged response '%s'\n", arg ); break; /* this may mean anything, so prefer not to spam the log */ } continue; } else if (!ctx->in_progress) { error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" ); break; /* this may mean anything, so prefer not to spam the log */ } else if (*arg == '+') { socket_expect_activity( &ctx->conn, 0 ); /* There can be any number of commands in flight, but only the last * one can require a continuation, as it enforces a round-trip. */ cmdp = (imap_cmd_t *)((char *)ctx->in_progress_append - offsetof(imap_cmd_t, next)); if (cmdp->param.data) { if (cmdp->param.to_trash) ctx->trashnc = TrashKnown; /* Can't get NO [TRYCREATE] any more. */ if (DFlags & DEBUG_NET) { if (DFlags & DEBUG_NET_ALL) { printf( "%s>>>>>>>>>\n", ctx->label ); fwrite( cmdp->param.data, cmdp->param.data_len, 1, stdout ); printf( "%s>>>>>>>>>\n", ctx->label ); } else { printf( "%s>>>>> (%u bytes omitted)\n", ctx->label, cmdp->param.data_len ); } fflush( stdout ); } iov[0].buf = cmdp->param.data; iov[0].len = cmdp->param.data_len; iov[0].takeOwn = GiveOwn; cmdp->param.data = NULL; ctx->buffer_mem -= cmdp->param.data_len; iov[1].buf = "\r\n"; iov[1].len = 2; iov[1].takeOwn = KeepOwn; socket_write( &ctx->conn, iov, 2 ); } else if (cmdp->param.cont) { if (cmdp->param.cont( ctx, cmdp, cmd )) return; } else { error( "IMAP error: unexpected command continuation request\n" ); break; } socket_expect_activity( &ctx->conn, 1 ); } else { tag = atoi( arg ); for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next) if (cmdp->tag == tag) goto gottag; error( "IMAP error: unexpected tag %s\n", arg ); break; gottag: if (!(*pcmdp = cmdp->next)) ctx->in_progress_append = pcmdp; if (!--ctx->num_in_progress) socket_expect_activity( &ctx->conn, 0 ); if (!(arg = next_arg( &cmd, &argl ))) { error( "IMAP error: malformed tagged response\n" ); break; } to_upper( arg, argl ); if (equals( arg, argl, "OK", 2 )) { if (cmdp->param.to_trash) ctx->trashnc = TrashKnown; /* Can't get NO [TRYCREATE] any more. */ resp = RESP_OK; } else { if (equals( arg, argl, "NO", 2 )) { if (cmdp->param.create && cmd && starts_with_upper( cmd, -1, "[TRYCREATE]", 11 )) { /* APPEND or UID COPY */ imap_cmd_trycreate_t *cmd2 = (imap_cmd_trycreate_t *)new_imap_cmd( sizeof(*cmd2) ); cmd2->orig_cmd = cmdp; cmd2->param.high_prio = 1; p = strchr( cmdp->cmd, '"' ); imap_exec( ctx, &cmd2->gen, get_cmd_result_p2, "CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ); continue; } resp = RESP_NO; if (cmdp->param.failok) // SELECT goto doresp; } else /*if (equals( arg, argl, "BAD", 3 ))*/ { if (cmdp->param.failok == 2 && cmd && starts_with_upper( cmd, -1, "[TOOBIG]", 8 )) { // APPEND resp = RESP_NO; // Fall through - we still complain } else { resp = RESP_CANCEL; } } error( "IMAP command '%s' returned an error: %s\n", starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ? "LOGIN " : starts_with( cmdp->cmd, -1, "AUTHENTICATE PLAIN", 18 ) ? "AUTHENTICATE PLAIN " : cmdp->cmd, cmd ? cmd : "" ); } doresp: if ((resp2 = parse_response_code( ctx, cmdp, cmd )) > resp) resp = resp2; imap_ref( ctx ); if (resp == RESP_CANCEL) imap_invoke_bad_callback( ctx ); done_imap_cmd( ctx, cmdp, resp ); if (imap_deref( ctx )) return; if (ctx->canceling && !ctx->in_progress) { ctx->canceling = 0; ctx->callbacks.imap_cancel( ctx->callback_aux ); return; } } flush_imap_cmds( ctx ); } imap_invoke_bad_callback( ctx ); } static void get_cmd_result_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response ) { imap_cmd_trycreate_t *cmdp = (imap_cmd_trycreate_t *)cmd; imap_cmd_t *ocmd = cmdp->orig_cmd; if (response != RESP_OK) { done_imap_cmd( ctx, ocmd, response ); } else { ctx->uidnext = 1; if (ocmd->param.to_trash) ctx->trashnc = TrashKnown; ocmd->param.create = 0; ocmd->param.high_prio = 1; submit_imap_cmd( ctx, ocmd ); } } /******************* imap_cancel_store *******************/ static void imap_cancel_store( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; #ifdef HAVE_LIBSASL sasl_dispose( &ctx->sasl ); #endif socket_close( &ctx->conn ); cancel_sent_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx ); free( ctx->ns_prefix ); free_string_list( ctx->auth_mechs ); free_generic_messages( &ctx->msgs.head->gen ); free_string_list( ctx->boxes ); imap_deref( ctx ); } static int imap_deref( imap_store_t *ctx ) { if (!--ctx->ref_count) { free( ctx ); return -1; } return 0; } static void imap_set_callbacks( store_t *gctx, void (*exp_cb)( message_t *, void * ), void (*bad_cb)( void * ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; ctx->expunge_callback = exp_cb; ctx->bad_callback = bad_cb; ctx->drv_callback_aux = aux; } static void imap_invoke_bad_callback( imap_store_t *ctx ) { ctx->bad_callback( ctx->drv_callback_aux ); } /******************* imap_free_store *******************/ static imap_store_t *unowned; static void imap_cancel_unowned( void *gctx ) { imap_store_t *store, **storep; for (storep = &unowned; (store = *storep); storep = &store->next) { if (store == gctx) { *storep = store->next; break; } } imap_cancel_store( gctx ); } static void imap_free_store( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; assert( !ctx->pending && !ctx->in_progress ); if (ctx->state == SST_BAD) { imap_cancel_store( gctx ); return; } reset_imap_messages( &ctx->msgs ); imap_set_callbacks( gctx, NULL, imap_cancel_unowned, gctx ); ctx->next = unowned; unowned = ctx; } /******************* imap_cleanup *******************/ static void imap_cleanup_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_cleanup( void ) { imap_store_t *ctx, *nctx; for (ctx = unowned; ctx; ctx = nctx) { nctx = ctx->next; imap_set_callbacks( &ctx->gen, NULL, (void (*)( void * ))imap_cancel_store, ctx ); ctx->expectBYE = 1; imap_exec( ctx, NULL, imap_cleanup_p2, "LOGOUT" ); } } static void imap_cleanup_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) { imap_cancel_store( &ctx->gen ); } else if (response == RESP_OK) { ctx->expectEOF = 1; socket_expect_eof( &ctx->conn ); } } /******************* imap_open_store *******************/ static void imap_open_store_connected( int, void * ); #ifdef HAVE_LIBSSL static void imap_open_store_tlsstarted1( int, void * ); #endif static void imap_open_store_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_authenticate( imap_store_t * ); #ifdef HAVE_LIBSSL static void imap_open_store_authenticate_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_tlsstarted2( int, void * ); static void imap_open_store_authenticate_p3( imap_store_t *, imap_cmd_t *, int ); #endif static void imap_open_store_authenticate2( imap_store_t * ); static void imap_open_store_authenticate2_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_authenticate2_p3( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_compress( imap_store_t * ); #ifdef HAVE_LIBZ static void imap_open_store_compress_p2( imap_store_t *, imap_cmd_t *, int ); #endif static void imap_open_store_enable_utf8( imap_store_t * ); static void imap_open_store_enable_utf8_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_namespace( imap_store_t * ); static void imap_open_store_namespace_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_store_namespace2( imap_store_t * ); static void imap_open_store_finalize( imap_store_t * ); #ifdef HAVE_LIBSSL static void imap_open_store_ssl_bail( imap_store_t * ); #endif static void imap_open_store_bail( imap_store_t *, int ); static store_t * imap_alloc_store( store_conf_t *conf, const char *label ) { imap_store_conf_t *cfg = (imap_store_conf_t *)conf; imap_server_conf_t *srvc = cfg->server; imap_store_t *ctx, **ctxp; /* First try to recycle a whole store. */ for (ctxp = &unowned; (ctx = *ctxp); ctxp = &ctx->next) { if (ctx->state == SST_GOOD && ctx->conf == cfg) { *ctxp = ctx->next; goto gotstore; } } /* Then try to recycle a server connection. */ for (ctxp = &unowned; (ctx = *ctxp); ctxp = &ctx->next) { if (ctx->conf->server == srvc) { *ctxp = ctx->next; free_string_list( ctx->boxes ); ctx->boxes = NULL; ctx->listed = 0; /* One could ping the server here, but given that the idle timeout * is at least 30 minutes, this sounds pretty pointless. */ ctx->state = SST_HALF; goto gotsrv; } } /* Finally, schedule opening a new server connection. */ ctx = nfzalloc( sizeof(*ctx) ); ctx->driver = &imap_driver; ctx->ref_count = 1; socket_init( &ctx->conn, &srvc->sconf, (void (*)( void * ))imap_invoke_bad_callback, imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx ); ctx->in_progress_append = &ctx->in_progress; ctx->pending_append = &ctx->pending; gotsrv: ctx->conf = cfg; gotstore: ctx->label = label; return &ctx->gen; } static void imap_connect_store( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; if (ctx->state == SST_GOOD) { cb( DRV_OK, aux ); } else { ctx->callbacks.imap_open = cb; ctx->callback_aux = aux; if (ctx->state == SST_HALF) imap_open_store_namespace( ctx ); else socket_connect( &ctx->conn, imap_open_store_connected ); } } static void imap_open_store_connected( int ok, void *aux ) { imap_store_t *ctx = (imap_store_t *)aux; if (!ok) imap_open_store_bail( ctx, FAIL_WAIT ); #ifdef HAVE_LIBSSL else if (ctx->conf->server->ssl_type == SSL_IMAPS) socket_start_tls( &ctx->conn, imap_open_store_tlsstarted1 ); #endif else socket_expect_activity( &ctx->conn, 1 ); } #ifdef HAVE_LIBSSL static void imap_open_store_tlsstarted1( int ok, void *aux ) { imap_store_t *ctx = (imap_store_t *)aux; if (!ok) imap_open_store_ssl_bail( ctx ); else socket_expect_activity( &ctx->conn, 1 ); } #endif static void imap_open_store_greeted( imap_store_t *ctx ) { socket_expect_activity( &ctx->conn, 0 ); if (!ctx->caps) imap_exec( ctx, NULL, imap_open_store_p2, "CAPABILITY" ); else imap_open_store_authenticate( ctx ); } static void imap_open_store_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) imap_open_store_bail( ctx, FAIL_FINAL ); else if (response == RESP_OK) imap_open_store_authenticate( ctx ); } static void imap_open_store_authenticate( imap_store_t *ctx ) { #ifdef HAVE_LIBSSL imap_server_conf_t *srvc = ctx->conf->server; #endif if (!CAP(IMAP4REV1)) { error( "IMAP error: Server does not support IMAP4rev1\n" ); imap_open_store_bail( ctx, FAIL_FINAL ); return; } if (ctx->greeting != GreetingPreauth) { #ifdef HAVE_LIBSSL if (srvc->ssl_type == SSL_STARTTLS) { if (CAP(STARTTLS)) { imap_exec( ctx, NULL, imap_open_store_authenticate_p2, "STARTTLS" ); return; } else { error( "IMAP error: SSL support not available\n" ); imap_open_store_bail( ctx, FAIL_FINAL ); return; } } #endif imap_open_store_authenticate2( ctx ); } else { #ifdef HAVE_LIBSSL if (srvc->ssl_type == SSL_STARTTLS) { error( "IMAP error: SSL support not available\n" ); imap_open_store_bail( ctx, FAIL_FINAL ); return; } #endif imap_open_store_compress( ctx ); } } #ifdef HAVE_LIBSSL static void imap_open_store_authenticate_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) imap_open_store_bail( ctx, FAIL_FINAL ); else if (response == RESP_OK) socket_start_tls( &ctx->conn, imap_open_store_tlsstarted2 ); } static void imap_open_store_tlsstarted2( int ok, void *aux ) { imap_store_t *ctx = (imap_store_t *)aux; if (!ok) imap_open_store_ssl_bail( ctx ); else imap_exec( ctx, NULL, imap_open_store_authenticate_p3, "CAPABILITY" ); } static void imap_open_store_authenticate_p3( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) imap_open_store_bail( ctx, FAIL_FINAL ); else if (response == RESP_OK) imap_open_store_authenticate2( ctx ); } #endif static char * cred_from_cmd( const char *cred, const char *cmd, const char *srv_name ) { FILE *fp; int ret; char buffer[8192]; // Hopefully more than enough room for XOAUTH2, etc. tokens if (*cmd == '+') { flushn(); cmd++; } if (!(fp = popen( cmd, "r" ))) { pipeerr: sys_error( "Skipping account %s, %s failed", srv_name, cred ); return NULL; } if (!fgets( buffer, sizeof(buffer), fp )) buffer[0] = 0; if ((ret = pclose( fp )) < 0) goto pipeerr; if (ret) { if (WIFSIGNALED( ret )) error( "Skipping account %s, %s crashed\n", srv_name, cred ); else error( "Skipping account %s, %s exited with status %d\n", srv_name, cred, WEXITSTATUS( ret ) ); return NULL; } if (!buffer[0]) { error( "Skipping account %s, %s produced no output\n", srv_name, cred ); return NULL; } buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */ return nfstrdup( buffer ); } static const char * ensure_user( imap_server_conf_t *srvc ) { if (!srvc->user) { if (srvc->user_cmd) { srvc->user = cred_from_cmd( "UserCmd", srvc->user_cmd, srvc->name ); } else { error( "Skipping account %s, no user\n", srvc->name ); } } return srvc->user; } static const char * ensure_password( imap_server_conf_t *srvc ) { if (!srvc->pass) { if (srvc->pass_cmd) { srvc->pass = cred_from_cmd( "PassCmd", srvc->pass_cmd, srvc->name ); #ifdef HAVE_MACOS_KEYCHAIN } else if (srvc->use_keychain) { void *password_data; UInt32 password_length; OSStatus ret = SecKeychainFindInternetPassword( NULL, // keychainOrArray strlen( srvc->sconf.host ), srvc->sconf.host, 0, NULL, // securityDomain strlen( srvc->user ), srvc->user, 0, NULL, // path 0, // port - we could use it, but it seems pointless kSecProtocolTypeIMAP, kSecAuthenticationTypeDefault, &password_length, &password_data, NULL ); // itemRef if (ret != errSecSuccess) { CFStringRef errmsg = SecCopyErrorMessageString( ret, NULL ); error( "Looking up Keychain failed: %s\n", CFStringGetCStringPtr( errmsg, kCFStringEncodingUTF8 ) ); CFRelease( errmsg ); return NULL; } srvc->pass = nfstrndup( password_data, password_length ); SecKeychainItemFreeContent( NULL, password_data ); #endif /* HAVE_MACOS_KEYCHAIN */ } else { flushn(); char prompt[80]; sprintf( prompt, "Password (%s): ", srvc->name ); char *pass = getpass( prompt ); if (!pass) { perror( "getpass" ); exit( 1 ); } if (!*pass) { error( "Skipping account %s, no password\n", srvc->name ); return NULL; } /* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */ srvc->pass = nfstrdup( pass ); } } return srvc->pass; } #ifdef HAVE_LIBSASL static sasl_callback_t sasl_callbacks[] = { { SASL_CB_USER, NULL, NULL }, { SASL_CB_AUTHNAME, NULL, NULL }, { SASL_CB_PASS, NULL, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; static int process_sasl_interact( sasl_interact_t *interact, imap_server_conf_t *srvc ) { const char *val; for (;; ++interact) { switch (interact->id) { case SASL_CB_LIST_END: return 0; case SASL_CB_USER: // aka authorization id - who to act as case SASL_CB_AUTHNAME: // who is really logging in val = ensure_user( srvc ); break; case SASL_CB_PASS: val = ensure_password( srvc ); break; default: error( "Error: Unknown SASL interaction ID\n" ); return -1; } if (!val) return -1; interact->result = val; interact->len = strlen( val ); } } static int process_sasl_step( imap_store_t *ctx, int rc, const char *in, uint in_len, sasl_interact_t *interact, const char **out, uint *out_len ) { imap_server_conf_t *srvc = ctx->conf->server; while (rc == SASL_INTERACT) { if (process_sasl_interact( interact, srvc ) < 0) return -1; rc = sasl_client_step( ctx->sasl, in, in_len, &interact, out, out_len ); } if (rc == SASL_CONTINUE) { ctx->sasl_cont = 1; } else if (rc == SASL_OK) { ctx->sasl_cont = 0; } else { error( "Error performing SASL authentication step: %s\n", sasl_errdetail( ctx->sasl ) ); return -1; } return 0; } static int decode_sasl_data( const char *prompt, char **in, uint *in_len ) { if (prompt) { int rc; uint prompt_len = strlen( prompt ); /* We're decoding, the output will be shorter than prompt_len. */ *in = nfmalloc( prompt_len ); rc = sasl_decode64( prompt, prompt_len, *in, prompt_len, in_len ); if (rc != SASL_OK) { free( *in ); error( "Error decoding SASL prompt: %s\n", sasl_errstring( rc, NULL, NULL ) ); return -1; } } else { *in = NULL; *in_len = 0; } return 0; } static int encode_sasl_data( const char *out, uint out_len, char **enc, uint *enc_len ) { int rc; uint enc_len_max = ((out_len + 2) / 3) * 4 + 1; *enc = nfmalloc( enc_len_max ); rc = sasl_encode64( out, out_len, *enc, enc_len_max, enc_len ); if (rc != SASL_OK) { free( *enc ); error( "Error encoding SASL response: %s\n", sasl_errstring( rc, NULL, NULL ) ); return -1; } return 0; } static int do_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmdp ATTR_UNUSED, const char *prompt ) { int rc, ret, iovcnt = 0; uint in_len, out_len, enc_len; const char *out; char *in, *enc; sasl_interact_t *interact = NULL; conn_iovec_t iov[2]; if (!ctx->sasl_cont) { error( "Error: IMAP wants more steps despite successful SASL authentication.\n" ); goto bail; } if (decode_sasl_data( prompt, &in, &in_len ) < 0) goto bail; rc = sasl_client_step( ctx->sasl, in, in_len, &interact, &out, &out_len ); ret = process_sasl_step( ctx, rc, in, in_len, interact, &out, &out_len ); free( in ); if (ret < 0) goto bail; if (out) { if (encode_sasl_data( out, out_len, &enc, &enc_len ) < 0) goto bail; iov[0].buf = enc; iov[0].len = enc_len; iov[0].takeOwn = GiveOwn; iovcnt = 1; if (DFlags & DEBUG_NET) { printf( "%s>+> %s\n", ctx->label, enc ); fflush( stdout ); } } else { if (DFlags & DEBUG_NET) { printf( "%s>+>\n", ctx->label ); fflush( stdout ); } } iov[iovcnt].buf = "\r\n"; iov[iovcnt].len = 2; iov[iovcnt].takeOwn = KeepOwn; iovcnt++; socket_write( &ctx->conn, iov, iovcnt ); return 0; bail: imap_open_store_bail( ctx, FAIL_FINAL ); return -1; } static void done_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_OK && ctx->sasl_cont) { sasl_interact_t *interact = NULL; const char *out; uint out_len; int rc = sasl_client_step( ctx->sasl, NULL, 0, &interact, &out, &out_len ); if (process_sasl_step( ctx, rc, NULL, 0, interact, &out, &out_len ) < 0) warn( "Warning: SASL reported failure despite successful IMAP authentication. Ignoring...\n" ); else if (out_len > 0) warn( "Warning: SASL wants more steps despite successful IMAP authentication. Ignoring...\n" ); } imap_open_store_authenticate2_p2( ctx, NULL, response ); } #endif static void imap_open_store_authenticate2( imap_store_t *ctx ) { imap_server_conf_t *srvc = ctx->conf->server; string_list_t *mech, *cmech; int auth_login = 0; int skipped_login = 0; #ifdef HAVE_LIBSASL const char *saslavail; char saslmechs[1024], *saslend = saslmechs; int want_external = 0; #endif // Ensure that there are no leftovers from previous runs. This is needed in case // the credentials have a timing dependency or otherwise lose validity after use. if (srvc->user_cmd) { free( srvc->user ); srvc->user = NULL; } if (srvc->pass_cmd) { free( srvc->pass ); srvc->pass = NULL; } info( "Logging in...\n" ); for (mech = srvc->auth_mechs; mech; mech = mech->next) { int any = equals( mech->string, -1, "*", 1 ); for (cmech = ctx->auth_mechs; cmech; cmech = cmech->next) { if (any || !strcasecmp( mech->string, cmech->string )) { if (!strcasecmp( cmech->string, "LOGIN" )) { #ifdef HAVE_LIBSSL if (ctx->conn.ssl || ctx->conn.conf->tunnel || !any) #else if (ctx->conn.conf->tunnel || !any) #endif auth_login = 1; else skipped_login = 1; #ifdef HAVE_LIBSASL } else { uint len = strlen( cmech->string ); if (saslend + len + 2 > saslmechs + sizeof(saslmechs)) oob(); *saslend++ = ' '; memcpy( saslend, cmech->string, len + 1 ); saslend += len; if (!strcasecmp( cmech->string, "EXTERNAL" )) want_external = 1; #endif } } } } #ifdef HAVE_LIBSASL if (saslend != saslmechs) { int rc; uint out_len = 0; char *enc = NULL; const char *gotmech = NULL, *out = NULL; sasl_interact_t *interact = NULL; imap_cmd_t *cmd; static int sasl_inited; if (!sasl_inited) { rc = sasl_client_init( sasl_callbacks ); if (rc != SASL_OK) { saslbail: error( "Error initializing SASL client: %s\n", sasl_errstring( rc, NULL, NULL ) ); goto bail; } sasl_inited = 1; } rc = sasl_client_new( "imap", srvc->sconf.host, NULL, NULL, NULL, 0, &ctx->sasl ); if (rc != SASL_OK) { if (rc == SASL_NOMECH) goto notsasl; if (!ctx->sasl) goto saslbail; error( "Error initializing SASL context: %s\n", sasl_errdetail( ctx->sasl ) ); goto bail; } // The built-in EXTERNAL mechanism wants the authentication id to be set // even before instantiation; consequently it won't prompt for it, either. // While this clearly makes sense on the server side, it arguably does not // on the client side. Ah, well ... if (want_external && ensure_user( srvc )) { rc = sasl_setprop( ctx->sasl, SASL_AUTH_EXTERNAL, srvc->user ); if (rc != SASL_OK ) { error( "Error setting SASL authentication id: %s\n", sasl_errdetail( ctx->sasl ) ); goto bail; } } rc = sasl_client_start( ctx->sasl, saslmechs + 1, &interact, CAP(SASLIR) ? &out : NULL, &out_len, &gotmech ); if (rc == SASL_NOMECH) goto notsasl; if (gotmech) info( "Authenticating with SASL mechanism %s...\n", gotmech ); /* Technically, we are supposed to loop over sasl_client_start(), * but it just calls sasl_client_step() anyway. */ if (process_sasl_step( ctx, rc, NULL, 0, interact, CAP(SASLIR) ? &out : NULL, &out_len ) < 0) goto bail; if (out) { if (!out_len) enc = nfstrdup( "=" ); /* A zero-length initial response is encoded as padding. */ else if (encode_sasl_data( out, out_len, &enc, NULL ) < 0) goto bail; } cmd = new_imap_cmd( sizeof(*cmd) ); cmd->param.cont = do_sasl_auth; ctx->caps = 0; imap_exec( ctx, cmd, done_sasl_auth, enc ? "AUTHENTICATE %s %s" : "AUTHENTICATE %s", gotmech, enc ); free( enc ); return; notsasl: if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", " ", "", &saslavail, NULL, NULL ) != SASL_OK) saslavail = "(none)"; if (!auth_login) { error( "IMAP error: selected SASL mechanism(s) not available;\n" " selected:%s\n available: %s\n", saslmechs, saslavail ); goto skipnote; } info( "NOT using available SASL mechanism(s): %s\n", saslavail ); sasl_dispose( &ctx->sasl ); } #endif if (auth_login) { if (!ensure_user( srvc ) || !ensure_password( srvc )) goto bail; #ifdef HAVE_LIBSSL if (!ctx->conn.ssl && !ctx->conn.conf->tunnel) #endif warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); ctx->caps = 0; imap_exec( ctx, NULL, imap_open_store_authenticate2_p2, "LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass ); return; } error( "IMAP error: server supports no acceptable authentication mechanism\n" ); #ifdef HAVE_LIBSASL skipnote: #endif if (skipped_login) error( "Note: not using LOGIN because connection is not encrypted;\n" " use 'AuthMechs LOGIN' explicitly to force it.\n" ); bail: imap_open_store_bail( ctx, FAIL_FINAL ); } static void imap_open_store_authenticate2_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) { imap_open_store_bail( ctx, FAIL_FINAL ); } else if (response == RESP_OK) { // iCloud (imap.mail.me.com) apparently runs the real server behind a // proxy that injects XAPPLEPUSHSERVICE into (and deletes STARTTLS from) // the server's outgoing data stream following occurrences of CAPABILITY. // This process is rather indiscriminate, so it will mess up IMAP // literals if it is not deactivated in time by issuing a (redundant) // CAPABILITY command after logging in. if (!ctx->caps || ctx->capability_hack) imap_exec( ctx, NULL, imap_open_store_authenticate2_p3, "CAPABILITY" ); else imap_open_store_compress( ctx ); } } static void imap_open_store_authenticate2_p3( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) imap_open_store_bail( ctx, FAIL_FINAL ); else if (response == RESP_OK) imap_open_store_compress( ctx ); } static void imap_open_store_compress( imap_store_t *ctx ) { #ifdef HAVE_LIBZ if (CAP(COMPRESS_DEFLATE)) { imap_exec( ctx, NULL, imap_open_store_compress_p2, "COMPRESS DEFLATE" ); return; } #endif imap_open_store_enable_utf8( ctx ); } #ifdef HAVE_LIBZ static void imap_open_store_compress_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) { /* We already reported an error, but it's not fatal to us. */ imap_open_store_enable_utf8( ctx ); } else if (response == RESP_OK) { socket_start_deflate( &ctx->conn ); imap_open_store_enable_utf8( ctx ); } } #endif static void imap_open_store_enable_utf8( imap_store_t *ctx ) { if (CAP(UTF8_ACCEPT) || CAP(UTF8_ONLY)) { // We just assume that a server that announces UTF8= also supports ENABLE. imap_exec( ctx, NULL, imap_open_store_enable_utf8_p2, "ENABLE UTF8=ACCEPT" ); } else { imap_open_store_namespace( ctx ); } } static void imap_open_store_enable_utf8_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) { imap_open_store_bail( ctx, FAIL_FINAL ); } else if (response == RESP_OK) { imap_open_store_namespace( ctx ); } } static void imap_open_store_namespace( imap_store_t *ctx ) { imap_store_conf_t *cfg = ctx->conf; ctx->state = SST_HALF; ctx->prefix = cfg->path; ctx->delimiter[0] = cfg->delimiter; if (((!ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && CAP(NAMESPACE)) { /* get NAMESPACE info */ if (!ctx->got_namespace) imap_exec( ctx, NULL, imap_open_store_namespace_p2, "NAMESPACE" ); else imap_open_store_namespace2( ctx ); return; } imap_open_store_finalize( ctx ); } static void imap_open_store_namespace_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) { if (response == RESP_NO) { imap_open_store_bail( ctx, FAIL_FINAL ); } else if (response == RESP_OK) { ctx->got_namespace = 1; imap_open_store_namespace2( ctx ); } } static void imap_open_store_namespace2( imap_store_t *ctx ) { if (!ctx->prefix && ctx->conf->use_namespace) ctx->prefix = ctx->ns_prefix; if (!ctx->delimiter[0]) ctx->delimiter[0] = ctx->ns_delimiter; imap_open_store_finalize( ctx ); } static void imap_open_store_finalize( imap_store_t *ctx ) { ctx->state = SST_GOOD; if (!ctx->prefix) ctx->prefix = ""; else normalize_INBOX( ctx, ctx->prefix, -1 ); ctx->trashnc = TrashUnknown; ctx->callbacks.imap_open( DRV_OK, ctx->callback_aux ); } #ifdef HAVE_LIBSSL static void imap_open_store_ssl_bail( imap_store_t *ctx ) { /* This avoids that we try to send LOGOUT to an unusable socket. */ socket_close( &ctx->conn ); imap_open_store_bail( ctx, FAIL_FINAL ); } #endif static void imap_open_store_bail( imap_store_t *ctx, int failed ) { ctx->conf->server->failed = (char)failed; ctx->callbacks.imap_open( DRV_STORE_BAD, ctx->callback_aux ); } /******************* imap_open_box *******************/ static int imap_select_box( store_t *gctx, const char *name ) { imap_store_t *ctx = (imap_store_t *)gctx; assert( !ctx->pending && !ctx->in_progress ); reset_imap_messages( &ctx->msgs ); ctx->name = name; return DRV_OK; } static const char * imap_get_box_path( store_t *gctx ATTR_UNUSED ) { return NULL; } typedef union { imap_cmd_t gen; struct { IMAP_CMD void (*callback)( int sts, uint uidvalidity, void *aux ); void *callback_aux; }; } imap_cmd_open_box_t; static void imap_open_box_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_box_p3( imap_store_t *, imap_cmd_t *, int ); static void imap_open_box_p4( imap_store_t *, imap_cmd_open_box_t *, int ); static void imap_open_box( store_t *gctx, void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; char *buf; if (prepare_box( &buf, ctx ) < 0) { cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } ctx->uidvalidity = UIDVAL_BAD; ctx->uidnext = 0; INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cb, aux) cmd->param.failok = 1; imap_exec( ctx, &cmd->gen, imap_open_box_p2, "SELECT \"%\\s\"", buf ); free( buf ); } static void imap_open_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd; if (response != RESP_OK || ctx->uidnext) { imap_open_box_p4( ctx, cmdp, response ); return; } assert( ctx->fetch_sts == FetchNone ); ctx->fetch_sts = FetchUidNext; INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cmdp->callback, cmdp->callback_aux) imap_exec( ctx, &cmd->gen, imap_open_box_p3, "UID FETCH * (UID)" ); } static void imap_open_box_p3( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd; ctx->fetch_sts = FetchNone; if (!ctx->uidnext) { if (ctx->total_msgs) { error( "IMAP error: querying server for highest UID failed\n" ); imap_open_box_p4( ctx, cmdp, RESP_NO ); return; } // This is ok, the box is simply empty. ctx->uidnext = 1; } imap_open_box_p4( ctx, cmdp, response ); } static void imap_open_box_p4( imap_store_t *ctx, imap_cmd_open_box_t *cmdp, int response ) { transform_box_response( &response ); cmdp->callback( response, ctx->uidvalidity, cmdp->callback_aux ); } static uint imap_get_uidnext( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; return ctx->uidnext; } static xint imap_get_supported_flags( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; return ctx->has_forwarded ? 255 : (255 & ~F_FORWARDED); } /******************* imap_create_box *******************/ static void imap_create_box( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; char *buf; if (prepare_box( &buf, ctx ) < 0) { cb( DRV_BOX_BAD, aux ); return; } INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux) imap_exec( ctx, &cmd->gen, imap_done_simple_box, "CREATE \"%\\s\"", buf ); free( buf ); } /******************* imap_delete_box *******************/ static int imap_confirm_box_empty( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; return ctx->total_msgs ? DRV_BOX_BAD : DRV_OK; } static void imap_delete_box_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_delete_box( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux) imap_exec( ctx, &cmd->gen, imap_delete_box_p2, "CLOSE" ); } static void imap_delete_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)gcmd; char *buf; if (response != RESP_OK) { imap_done_simple_box( ctx, &cmdp->gen, response ); return; } if (prepare_box( &buf, ctx ) < 0) { imap_done_simple_box( ctx, &cmdp->gen, RESP_NO ); return; } INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cmdp->callback, cmdp->callback_aux) imap_exec( ctx, &cmd->gen, imap_done_simple_box, "DELETE \"%\\s\"", buf ); free( buf ); } static int imap_finish_delete_box( store_t *gctx ATTR_UNUSED ) { return DRV_OK; } /******************* imap_load_box *******************/ static uint imap_prepare_load_box( store_t *gctx, uint opts ) { imap_store_t *ctx = (imap_store_t *)gctx; if (!CAP(UIDPLUS)) opts &= ~OPEN_UID_EXPUNGE; ctx->opts = opts; return opts; } enum { WantSize = 1, WantTuids = 2, WantMsgids = 4 }; typedef struct { uint first, last; int flags; } imap_range_t; #define MAX_RANGES 4 static void imap_set_range( imap_range_t *ranges, uint *nranges, int low_flags, int high_flags, uint maxlow ) { if (low_flags != high_flags) { for (uint r = 0; r < *nranges; r++) { if (ranges[r].first > maxlow) break; /* Range starts above split point; so do all subsequent ranges. */ if (ranges[r].last < maxlow) continue; /* Range ends below split point; try next one. */ if (ranges[r].last != maxlow) { /* Range does not end exactly at split point; need to split. */ if (*nranges == MAX_RANGES) oob(); memmove( &ranges[r + 1], &ranges[r], ((*nranges)++ - r) * sizeof(*ranges) ); ranges[r].last = maxlow; ranges[r + 1].first = maxlow + 1; } break; } } for (uint r = 0; r < *nranges; r++) ranges[r].flags |= (ranges[r].last <= maxlow) ? low_flags : high_flags; } typedef union { imap_cmd_refcounted_state_t gen; struct { IMAP_CMD_REFCOUNTED_STATE void (*callback)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ); void *callback_aux; }; } imap_load_box_state_t; static void imap_submit_load( imap_store_t *, const char *, int, imap_load_box_state_t * ); static void imap_submit_load_p3( imap_store_t *ctx, imap_load_box_state_t * ); static void imap_load_box( store_t *gctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs, void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; char buf[1000]; if (!ctx->total_msgs) { free( excs.data ); cb( DRV_OK, NULL, 0, 0, aux ); } else { assert( ctx->fetch_sts == FetchNone ); ctx->fetch_sts = FetchMsgs; INIT_REFCOUNTED_STATE(imap_load_box_state_t, sts, cb, aux) for (uint i = 0; i < excs.size; ) { for (int bl = 0; i < excs.size && bl < 960; i++) { if (bl) buf[bl++] = ','; bl += sprintf( buf + bl, "%u", excs.data[i] ); uint j = i; for (; i + 1 < excs.size && excs.data[i + 1] == excs.data[i] + 1; i++) {} if (i != j) bl += sprintf( buf + bl, ":%u", excs.data[i] ); } imap_submit_load( ctx, buf, shifted_bit( ctx->opts, (int)OPEN_PAIRED_IDS, WantMsgids ), sts ); } if (maxuid == UINT_MAX) maxuid = ctx->uidnext - 1; if (maxuid >= minuid) { imap_range_t ranges[MAX_RANGES]; ranges[0].first = minuid; ranges[0].last = maxuid; ranges[0].flags = 0; uint nranges = 1; if (ctx->opts & OPEN_OLD_SIZE) { if (ctx->opts & OPEN_NEW_SIZE) ranges[0].flags = WantSize; else imap_set_range( ranges, &nranges, WantSize, 0, newuid ); } else if (ctx->opts & OPEN_NEW_SIZE) { imap_set_range( ranges, &nranges, 0, WantSize, newuid ); } if (ctx->opts & OPEN_FIND) imap_set_range( ranges, &nranges, 0, WantTuids, finduid - 1 ); if (ctx->opts & OPEN_PAIRED_IDS) imap_set_range( ranges, &nranges, WantMsgids, 0, pairuid ); for (uint r = 0; r < nranges; r++) { sprintf( buf, "%u:%u", ranges[r].first, ranges[r].last ); imap_submit_load( ctx, buf, ranges[r].flags, sts ); } } free( excs.data ); imap_submit_load_p3( ctx, sts ); } } static void imap_submit_load_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_submit_load( imap_store_t *ctx, const char *buf, int flags, imap_load_box_state_t *sts ) { imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_submit_load_p2, "UID FETCH %s (UID%s%s%s%s%s%s%s)", buf, (ctx->opts & OPEN_FLAGS) ? " FLAGS" : "", (flags & WantSize) ? " RFC822.SIZE" : "", (flags & (WantTuids | WantMsgids)) ? " BODY.PEEK[HEADER.FIELDS (" : "", (flags & WantTuids) ? "X-TUID" : "", !(~flags & (WantTuids | WantMsgids)) ? " " : "", (flags & WantMsgids) ? "MESSAGE-ID" : "", (flags & (WantTuids | WantMsgids)) ? ")]" : ""); } static void imap_submit_load_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response ) { imap_load_box_state_t *sts = (imap_load_box_state_t *)((imap_cmd_refcounted_t *)cmd)->state; transform_refcounted_box_response( &sts->gen, response ); imap_submit_load_p3( ctx, sts ); } static void imap_submit_load_p3( imap_store_t *ctx, imap_load_box_state_t *sts ) { DONE_REFCOUNTED_STATE_ARGS(sts, { ctx->fetch_sts = FetchNone; if (sts->ret_val == DRV_OK) imap_ensure_relative( &ctx->msgs ); }, &ctx->msgs.head->gen, ctx->total_msgs, ctx->recent_msgs) } /******************* imap_fetch_msg *******************/ static void imap_fetch_msg_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data, int minimal, void (*cb)( int sts, void *aux ), void *aux ) { INIT_IMAP_CMD(imap_cmd_fetch_msg_t, cmd, cb, aux) cmd->param.uid = msg->uid; cmd->msg_data = data; data->data = NULL; imap_exec( (imap_store_t *)ctx, &cmd->gen.gen, imap_fetch_msg_p2, "UID FETCH %u (%s%sBODY.PEEK[%s])", msg->uid, !(msg->status & M_FLAGS) ? "FLAGS " : "", (data->date== -1) ? "INTERNALDATE " : "", minimal ? "HEADER" : "" ); } static void imap_fetch_msg_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_fetch_msg_t *cmd = (imap_cmd_fetch_msg_t *)gcmd; if (response == RESP_OK && !cmd->msg_data->data) { // The UID FETCH succeeded, but there is no message with this UID. // The corresponding EXPUNGE response has been received by this time, // so the message is already marked as dead. response = RESP_NO; } imap_done_simple_msg( ctx, gcmd, response ); } /******************* imap_set_msg_flags *******************/ static uint imap_make_flags( int flags, char *buf ) { const char *s; uint i, d; for (i = d = 0; i < as(Flags); i++) { if (flags & (1 << i)) { buf[d++] = ' '; for (s = Flags[i].str; *s; s++) buf[d++] = *s; } } buf[0] = '('; buf[d++] = ')'; return d; } typedef union { imap_cmd_refcounted_state_t gen; struct { IMAP_CMD_REFCOUNTED_STATE void (*callback)( int sts, void *aux ); void *callback_aux; message_t *msg; int add, del; }; } imap_set_msg_flags_state_t; static void imap_set_flags_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_set_flags_p3( imap_set_msg_flags_state_t * ); static void imap_flags_helper( imap_store_t *ctx, uint uid, char what, int flags, imap_set_msg_flags_state_t *sts ) { char buf[256]; buf[imap_make_flags( flags, buf )] = 0; imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_set_flags_p2, "UID STORE %u %cFLAGS.SILENT %s", uid, what, buf ); } static void imap_set_msg_flags( store_t *gctx, message_t *msg, uint uid, int add, int del, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; assert( add || del ); INIT_REFCOUNTED_STATE(imap_set_msg_flags_state_t, sts, cb, aux) sts->msg = msg; sts->add = add; sts->del = del; if (add) imap_flags_helper( ctx, uid, '+', add, sts ); if (del) imap_flags_helper( ctx, uid, '-', del, sts ); imap_set_flags_p3( sts ); } static void imap_set_flags_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_set_msg_flags_state_t *sts = (imap_set_msg_flags_state_t *)((imap_cmd_refcounted_t *)cmd)->state; transform_refcounted_msg_response( &sts->gen, response); imap_set_flags_p3( sts ); } static void imap_set_flags_p3( imap_set_msg_flags_state_t *sts ) { DONE_REFCOUNTED_STATE_FINALIZE(sts, { if (sts->msg) sts->msg->flags = (sts->msg->flags & ~sts->del) | sts->add; }) } /******************* imap_close_box *******************/ #define IMAP_CMD_EXPUNGE \ void (*callback)( int sts, int reported, void *aux ); \ void *callback_aux; typedef union { imap_cmd_refcounted_state_t gen; struct { IMAP_CMD_REFCOUNTED_STATE IMAP_CMD_EXPUNGE }; } imap_expunge_state_t; typedef union { imap_cmd_t gen; struct { IMAP_CMD IMAP_CMD_EXPUNGE }; } imap_cmd_close_t; static void imap_close_box_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_close_box_p3( imap_expunge_state_t * ); static void imap_close_box_simple_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_close_box( store_t *gctx, void (*cb)( int sts, int reported, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; assert( !ctx->pending && !ctx->in_progress ); if (ctx->opts & OPEN_UID_EXPUNGE) { INIT_REFCOUNTED_STATE(imap_expunge_state_t, sts, cb, aux) imap_message_t *msg, *fmsg, *nmsg; int bl; char buf[1000]; for (msg = ctx->msgs.head; ; ) { for (bl = 0; msg && bl < 960; msg = msg->next) { if ((msg->status & M_DEAD) || !(msg->status & M_EXPUNGE)) continue; if (bl) buf[bl++] = ','; bl += sprintf( buf + bl, "%u", msg->uid ); fmsg = msg; for (; (nmsg = msg->next); msg = nmsg) { if (nmsg->status & M_DEAD) { // Messages that jump a gap interrupt the range, even expunged ones. if (nmsg->seq) break; } else { if (nmsg->seq > 1) break; if (!(nmsg->flags & M_EXPUNGE)) break; } } if (msg != fmsg) bl += sprintf( buf + bl, ":%u", msg->uid ); } if (!bl) break; imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_close_box_p2, "UID EXPUNGE %s", buf ); } imap_close_box_p3( sts ); } else { /* This is inherently racy: it may cause messages which other clients * marked as deleted to be expunged without being trashed. */ // Note that, to save bandwidth, we don't use EXPUNGE. Also, in many // cases, we wouldn't be able to map the EXPUNGE responses' seq numbers // anyway, due to not having fetched the messages. INIT_IMAP_CMD(imap_cmd_close_t, cmd, cb, aux) imap_exec( ctx, &cmd->gen, imap_close_box_simple_p2, "CLOSE" ); } } static void imap_close_box_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_expunge_state_t *sts = (imap_expunge_state_t *)((imap_cmd_refcounted_t *)cmd)->state; transform_refcounted_box_response( &sts->gen, response ); imap_close_box_p3( sts ); } static void imap_close_box_p3( imap_expunge_state_t *sts ) { DONE_REFCOUNTED_STATE_ARGS(sts, , 1) } static void imap_close_box_simple_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_cmd_close_t *cmdp = (imap_cmd_close_t *)cmd; transform_box_response( &response ); cmdp->callback( response, 0, cmdp->callback_aux ); } /******************* imap_trash_msg *******************/ static void imap_trash_msg( store_t *gctx, message_t *msg, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; char *buf; INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux) cmd->param.create = 1; cmd->param.to_trash = 1; if (prepare_trash( &buf, ctx ) < 0) { cb( DRV_BOX_BAD, aux ); free( cmd ); return; } imap_exec( ctx, &cmd->gen, imap_done_simple_msg, CAP(MOVE) ? "UID MOVE %u \"%\\s\"" : "UID COPY %u \"%\\s\"", msg->uid, buf ); free( buf ); } /******************* imap_store_msg *******************/ static void imap_store_msg_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, void (*cb)( int sts, uint uid, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; char *buf; uint d; char flagstr[128], datestr[64]; d = 0; if (data->flags) { d = imap_make_flags( data->flags, flagstr ); flagstr[d++] = ' '; } flagstr[d] = 0; if (data->date) { DIAG_PUSH DIAG_DISABLE("-Wformat") // configure ensures that %z actually works. strftime( datestr, sizeof(datestr), "\"%d-%b-%Y %H:%M:%S %z\" ", localtime( &data->date ) ); DIAG_POP } else { datestr[0] = 0; } INIT_IMAP_CMD(imap_cmd_out_uid_t, cmd, cb, aux) ctx->buffer_mem += data->len; cmd->param.data_len = data->len; cmd->param.data = data->data; cmd->param.failok = 2; if (to_trash) { cmd->param.create = 1; cmd->param.to_trash = 1; if (prepare_trash( &buf, ctx ) < 0) { cb( DRV_BOX_BAD, 0, aux ); free( cmd ); return; } } else { if (prepare_box( &buf, ctx ) < 0) { cb( DRV_BOX_BAD, 0, aux ); free( cmd ); return; } } imap_exec( ctx, &cmd->gen, imap_store_msg_p2, "APPEND \"%\\s\" %s%s{%u+}", buf, flagstr, datestr, data->len ); free( buf ); } static void imap_store_msg_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response ) { imap_cmd_out_uid_t *cmdp = (imap_cmd_out_uid_t *)cmd; transform_msg_response( &response ); cmdp->callback( response, cmdp->param.uid, cmdp->callback_aux ); } /******************* imap_find_new_msgs *******************/ static void imap_find_new_msgs_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_find_new_msgs_p3( imap_store_t *, imap_cmd_t *, int ); static void imap_find_new_msgs_p4( imap_store_t *, imap_cmd_t *, int ); static void imap_find_new_msgs( store_t *gctx, uint newuid, void (*cb)( int sts, message_t *msgs, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cb, aux) cmd->out_msgs = ctx->msgs.tail; cmd->uid = newuid; // Some servers fail to enumerate recently APPENDed messages without syncing first. imap_exec( ctx, &cmd->gen, imap_find_new_msgs_p2, "CHECK" ); } static void imap_find_new_msgs_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd; if (response != RESP_OK) { imap_done_simple_box( ctx, gcmd, response ); return; } // We appended messages, so we need to re-query UIDNEXT. ctx->uidnext = 0; assert( ctx->fetch_sts == FetchNone ); ctx->fetch_sts = FetchUidNext; INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cmdp->callback, cmdp->callback_aux) cmd->out_msgs = cmdp->out_msgs; cmd->uid = cmdp->uid; imap_exec( ctx, &cmd->gen, imap_find_new_msgs_p3, "UID FETCH * (UID)" ); } static void imap_find_new_msgs_p3( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd; if (response != RESP_OK) { imap_find_new_msgs_p4( ctx, gcmd, response ); return; } if (ctx->uidnext <= cmdp->uid) { if (!ctx->uidnext && ctx->total_msgs) { error( "IMAP error: re-querying server for highest UID failed\n" ); imap_find_new_msgs_p4( ctx, gcmd, RESP_NO ); } else { // The messages evaporated, or the server just didn't register them - // we'll catch that later (via lost TUIDs). // This case is why we do the extra roundtrip instead of simply passing // '*' as the end of the range below - IMAP ranges are unordered, so we // would potentially re-fetch an already loaded message. imap_find_new_msgs_p4( ctx, gcmd, RESP_OK ); } return; } ctx->fetch_sts = FetchMsgs; INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cmdp->callback, cmdp->callback_aux) cmd->out_msgs = cmdp->out_msgs; imap_exec( ctx, &cmd->gen, imap_find_new_msgs_p4, "UID FETCH %u:%u (UID BODY.PEEK[HEADER.FIELDS (X-TUID)])", cmdp->uid, ctx->uidnext - 1 ); } static void imap_find_new_msgs_p4( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *gcmd, int response ) { imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd; ctx->fetch_sts = FetchNone; transform_box_response( &response ); // Note: unlike in load_box(), we don't call imap_ensure_relative() here, // as it's unnecessary. It being called due to unsolicited responses // causes no harm. cmdp->callback( response, &(*cmdp->out_msgs)->gen, cmdp->callback_aux ); } /******************* imap_list_store *******************/ typedef union { imap_cmd_refcounted_state_t gen; struct { IMAP_CMD_REFCOUNTED_STATE void (*callback)( int sts, string_list_t *, void *aux ); void *callback_aux; }; } imap_list_store_state_t; static void imap_list_store_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_list_store_p3( imap_store_t *, imap_list_store_state_t * ); static void imap_list_store( store_t *gctx, int flags, void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; imap_store_conf_t *cfg = ctx->conf; INIT_REFCOUNTED_STATE(imap_list_store_state_t, sts, cb, aux) // ctx->prefix may be empty, "INBOX.", or something else. // 'flags' may be LIST_INBOX, LIST_PATH (or LIST_PATH_MAYBE), or both. 'listed' // already containing a particular value effectively removes it from 'flags'. // This matrix determines what to query, and what comes out as a side effect. // The lowercase letters indicate unnecessary results; the queries are done // this way to have non-overlapping result sets, so subsequent calls create // no duplicates: // // qry \ pfx | empty | inbox | other // ----------+-------+-------+------- // inbox | p [I] | I [p] | I // both | P [I] | I [P] | I + P // path | P [i] | i [P] | P // int pfx_is_empty = !*ctx->prefix; int pfx_is_inbox = !pfx_is_empty && is_inbox( ctx, ctx->prefix, strlen( ctx->prefix ) ); if (((flags & (LIST_PATH | LIST_PATH_MAYBE)) || pfx_is_empty) && !pfx_is_inbox && !(ctx->listed & LIST_PATH)) { ctx->listed |= LIST_PATH; if (pfx_is_empty) ctx->listed |= LIST_INBOX; imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_list_store_p2, "%s \"\" \"%\\s*\"", cfg->use_lsub ? "LSUB" : "LIST", ctx->prefix ); } if (((flags & LIST_INBOX) || pfx_is_inbox) && !pfx_is_empty && !(ctx->listed & LIST_INBOX)) { ctx->listed |= LIST_INBOX; if (pfx_is_inbox) ctx->listed |= LIST_PATH; imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_list_store_p2, "%s \"\" INBOX*", cfg->use_lsub ? "LSUB" : "LIST" ); } imap_list_store_p3( ctx, sts ); } static void imap_list_store_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response ) { imap_list_store_state_t *sts = (imap_list_store_state_t *)((imap_cmd_refcounted_t *)cmd)->state; transform_refcounted_box_response( &sts->gen, response ); imap_list_store_p3( ctx, sts ); } static void imap_list_store_p3( imap_store_t *ctx, imap_list_store_state_t *sts ) { DONE_REFCOUNTED_STATE_ARGS(sts, , ctx->boxes) } /******************* imap_cancel_cmds *******************/ static void imap_cancel_cmds( store_t *gctx, void (*cb)( void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; cancel_pending_imap_cmds( ctx ); if (ctx->in_progress) { ctx->canceling = 1; ctx->callbacks.imap_cancel = cb; ctx->callback_aux = aux; } else { cb( aux ); } } /******************* imap_get_memory_usage *******************/ static uint imap_get_memory_usage( store_t *gctx ) { imap_store_t *ctx = (imap_store_t *)gctx; return ctx->buffer_mem + ctx->conn.buffer_mem; } /******************* imap_get_fail_state *******************/ static int imap_get_fail_state( store_conf_t *gconf ) { return ((imap_store_conf_t *)gconf)->server->failed; } /******************* imap_parse_store *******************/ static imap_server_conf_t *servers, **serverapp = &servers; static int imap_parse_store( conffile_t *cfg, store_conf_t **storep ) { imap_store_conf_t *store; imap_server_conf_t *server, *srv, sserver; const char *type, *name, *arg; unsigned u; int acc_opt = 0; /* Legacy SASL option */ int require_cram = -1; if (!strcasecmp( "IMAPAccount", cfg->cmd )) { server = nfzalloc( sizeof(*server) ); name = server->name = nfstrdup( cfg->val ); *serverapp = server; serverapp = &server->next; store = NULL; *storep = NULL; type = "IMAP account"; } else if (!strcasecmp( "IMAPStore", cfg->cmd )) { store = nfzalloc( sizeof(*store) ); store->driver = &imap_driver; name = store->name = nfstrdup( cfg->val ); store->use_namespace = 1; *storep = &store->gen; memset( &sserver, 0, sizeof(sserver) ); server = &sserver; type = "IMAP store"; } else { return 0; } server->sconf.timeout = 20000; #ifdef HAVE_LIBSSL server->ssl_type = -1; server->sconf.ssl_versions = TLSv1_2 | TLSv1_3; server->sconf.system_certs = 1; #endif server->max_in_progress = INT_MAX; while (getcline( cfg ) && cfg->cmd) { if (!strcasecmp( "Host", cfg->cmd )) { server->sconf.host = nfstrdup( cfg->val ); } else if (!strcasecmp( "User", cfg->cmd )) { server->user = nfstrdup( cfg->val ); } else if (!strcasecmp( "UserCmd", cfg->cmd )) { server->user_cmd = nfstrdup( cfg->val ); } else if (!strcasecmp( "Pass", cfg->cmd )) { server->pass = nfstrdup( cfg->val ); } else if (!strcasecmp( "PassCmd", cfg->cmd )) { server->pass_cmd = nfstrdup( cfg->val ); #ifdef HAVE_MACOS_KEYCHAIN } else if (!strcasecmp( "UseKeychain", cfg->cmd )) { server->use_keychain = parse_bool( cfg ); #endif } else if (!strcasecmp( "Port", cfg->cmd )) { int port = parse_int( cfg ); if ((unsigned)port > 0xffff) { error( "%s:%d: Invalid port number\n", cfg->file, cfg->line ); cfg->err = 1; } else { server->sconf.port = (ushort)port; } } else if (!strcasecmp( "Timeout", cfg->cmd )) { server->sconf.timeout = parse_int( cfg ) * 1000; } else if (!strcasecmp( "PipelineDepth", cfg->cmd )) { if ((server->max_in_progress = parse_int( cfg )) < 1) { error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line ); cfg->err = 1; } } else if (!strcasecmp( "DisableExtension", cfg->cmd ) || !strcasecmp( "DisableExtensions", cfg->cmd )) { arg = cfg->val; do { for (u = 0; u < as(cap_list); u++) { if (equals_upper( arg, -1, cap_list[u].str, cap_list[u].len )) { server->cap_mask |= 1 << u; goto gotcap; } } error( "%s:%d: Unrecognized IMAP extension '%s'\n", cfg->file, cfg->line, arg ); cfg->err = 1; gotcap: ; } while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL ))); #ifdef HAVE_LIBSSL } else if (!strcasecmp( "CertificateFile", cfg->cmd )) { server->sconf.cert_file = expand_strdup( cfg->val, cfg ); if (access( server->sconf.cert_file, R_OK )) { sys_error( "%s:%d: CertificateFile '%s'", cfg->file, cfg->line, server->sconf.cert_file ); cfg->err = 1; } } else if (!strcasecmp( "SystemCertificates", cfg->cmd )) { server->sconf.system_certs = parse_bool( cfg ); } else if (!strcasecmp( "ClientCertificate", cfg->cmd )) { server->sconf.client_certfile = expand_strdup( cfg->val, cfg ); if (access( server->sconf.client_certfile, R_OK )) { sys_error( "%s:%d: ClientCertificate '%s'", cfg->file, cfg->line, server->sconf.client_certfile ); cfg->err = 1; } } else if (!strcasecmp( "ClientKey", cfg->cmd )) { server->sconf.client_keyfile = expand_strdup( cfg->val, cfg ); if (access( server->sconf.client_keyfile, R_OK )) { sys_error( "%s:%d: ClientKey '%s'", cfg->file, cfg->line, server->sconf.client_keyfile ); cfg->err = 1; } } else if (!strcasecmp( "CipherString", cfg->cmd )) { server->sconf.cipher_string = nfstrdup( cfg->val ); } else if (!strcasecmp( "TLSVersions", cfg->cmd )) { arg = cfg->val; do { int or_mask = 0, and_mask = 0, val; if (*arg == '+') { or_mask = ~0; } else if (*arg == '-') { and_mask = ~0; } else { error( "%s:%d: TLSVersions arguments must start with +/-\n", cfg->file, cfg->line ); cfg->err = 1; continue; } arg++; if (!strcmp( "1.0", arg )) { val = TLSv1; } else if (!strcmp( "1.1", arg )) { val = TLSv1_1; } else if (!strcmp( "1.2", arg )) { val = TLSv1_2; } else if (!strcmp( "1.3", arg )) { val = TLSv1_3; } else { error( "%s:%d: Unrecognized TLS version '%s'\n", cfg->file, cfg->line, arg ); cfg->err = 1; continue; } or_mask &= val; and_mask &= val; server->sconf.ssl_versions = (server->sconf.ssl_versions & ~and_mask) | or_mask; } while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL ))); } else if (!strcasecmp( "SSLVersion", cfg->cmd ) || !strcasecmp( "SSLVersions", cfg->cmd )) { static int sslv_warned; if (!sslv_warned) { sslv_warned = 1; warn( "%s: notice: SSLVersions is deprecated. Use TLSVersions instead.\n", cfg->file ); } server->sconf.ssl_versions = 0; arg = cfg->val; do { if (!strcasecmp( "SSLv2", arg )) { warn( "Warning: SSLVersion SSLv2 is no longer supported\n" ); } else if (!strcasecmp( "SSLv3", arg )) { warn( "Warning: SSLVersion SSLv3 is no longer supported\n" ); } else if (!strcasecmp( "TLSv1", arg )) { server->sconf.ssl_versions |= TLSv1; } else if (!strcasecmp( "TLSv1.1", arg )) { server->sconf.ssl_versions |= TLSv1_1; } else if (!strcasecmp( "TLSv1.2", arg )) { server->sconf.ssl_versions |= TLSv1_2; } else if (!strcasecmp( "TLSv1.3", arg )) { server->sconf.ssl_versions |= TLSv1_3; } else { error( "%s:%d: Unrecognized SSL version\n", cfg->file, cfg->line ); cfg->err = 1; } } while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL ))); #else } else if (!strcasecmp( "CertificateFile", cfg->cmd ) || !strcasecmp( "SystemCertificates", cfg->cmd ) || !strcasecmp( "ClientCertificate", cfg->cmd ) || !strcasecmp( "ClientKey", cfg->cmd ) || !strcasecmp( "CipherString", cfg->cmd ) || !strcasecmp( "SSLVersion", cfg->cmd ) || !strcasecmp( "SSLVersions", cfg->cmd ) || !strcasecmp( "TLSVersions", cfg->cmd )) { error( "Error: " EXE " built without OpenSSL; %s is not supported.\n", cfg->cmd ); cfg->err = 1; #endif } else if (!strcasecmp( "TLSType", cfg->cmd )) { goto tlstype; } else if (!strcasecmp( "SSLType", cfg->cmd )) { static int sslt_warned; if (!sslt_warned) { sslt_warned = 1; warn( "%s: notice: SSLType is deprecated. Use TLSType instead.\n", cfg->file ); } tlstype: if (!strcasecmp( "None", cfg->val )) { #ifdef HAVE_LIBSSL server->ssl_type = SSL_None; } else if (!strcasecmp( "STARTTLS", cfg->val )) { server->ssl_type = SSL_STARTTLS; } else if (!strcasecmp( "IMAPS", cfg->val )) { server->ssl_type = SSL_IMAPS; #else } else if (!strcasecmp( "STARTTLS", cfg->val ) || !strcasecmp( "IMAPS", cfg->val )) { error( "Error: " EXE " built without OpenSSL; only TLSType None is supported.\n" ); cfg->err = 1; #endif } else { error( "%s:%d: Invalid TLS type\n", cfg->file, cfg->line ); cfg->err = 1; } } else if (!strcasecmp( "AuthMech", cfg->cmd ) || !strcasecmp( "AuthMechs", cfg->cmd )) { arg = cfg->val; do { add_string_list( &server->auth_mechs, arg ); } while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL ))); } else if (!strcasecmp( "RequireCRAM", cfg->cmd )) { require_cram = parse_bool( cfg ); } else if (!strcasecmp( "Tunnel", cfg->cmd )) { server->sconf.tunnel = nfstrdup( cfg->val ); } else if (store) { if (!strcasecmp( "Account", cfg->cmd )) { for (srv = servers; srv; srv = srv->next) if (srv->name && !strcmp( srv->name, cfg->val )) goto gotsrv; store->server = (void *)~0; error( "%s:%d: unknown IMAP account '%s'\n", cfg->file, cfg->line, cfg->val ); cfg->err = 1; continue; gotsrv: store->server = srv; } else if (!strcasecmp( "UseNamespace", cfg->cmd )) { store->use_namespace = parse_bool( cfg ); } else if (!strcasecmp( "SubscribedOnly", cfg->cmd )) { store->use_lsub = parse_bool( cfg ); } else if (!strcasecmp( "Path", cfg->cmd )) { store->path = nfstrdup( cfg->val ); } else if (!strcasecmp( "PathDelimiter", cfg->cmd )) { if (strlen( cfg->val ) != 1) { error( "%s:%d: Path delimiter must be exactly one character long\n", cfg->file, cfg->line ); cfg->err = 1; continue; } store->delimiter = cfg->val[0]; } else { parse_generic_store( &store->gen, cfg, "IMAPStore" ); } continue; } else { error( "%s:%d: keyword '%s' is not recognized in IMAPAccount sections\n", cfg->file, cfg->line, cfg->cmd ); cfg->rest = NULL; cfg->err = 1; continue; } acc_opt = 1; } if (!store || !store->server) { if (!server->sconf.tunnel && !server->sconf.host) { error( "%s '%s' has neither Tunnel nor Host\n", type, name ); cfg->err = 1; return 1; } if (server->user && server->user_cmd) { error( "%s '%s' has both User and UserCmd\n", type, name ); cfg->err = 1; return 1; } if (server->pass && server->pass_cmd) { error( "%s '%s' has both Pass and PassCmd\n", type, name ); cfg->err = 1; return 1; } #ifdef HAVE_MACOS_KEYCHAIN if (server->use_keychain && (server->pass || server->pass_cmd)) { error( "%s '%s' has UseKeychain enabled despite specifying Pass/PassCmd\n", type, name ); cfg->err = 1; return 1; } #endif #ifdef HAVE_LIBSSL if (server->ssl_type < 0) server->ssl_type = server->sconf.tunnel ? SSL_None : SSL_STARTTLS; #endif if (require_cram >= 0) { if (server->auth_mechs) { error( "%s '%s': The deprecated RequireCRAM option is mutually exclusive with AuthMech.\n", type, name ); cfg->err = 1; return 1; } warn( "Notice: %s '%s': RequireCRAM is deprecated. Use AuthMech instead.\n", type, name ); if (require_cram) add_string_list(&server->auth_mechs, "CRAM-MD5"); } #ifndef HAVE_LIBSASL for (string_list_t *mech = server->auth_mechs; mech; mech = mech->next) { if (strcmp( mech->string, "*" ) && strcasecmp( mech->string, "LOGIN" )) { error( "Error: " EXE " built without LibSASL; only AuthMech LOGIN is supported.\n" ); cfg->err = 1; break; } } #endif if (!server->auth_mechs) add_string_list( &server->auth_mechs, "*" ); if (!server->sconf.port) { server->sconf.port = #ifdef HAVE_LIBSSL server->ssl_type == SSL_IMAPS ? 993 : #endif 143; } } if (store) { if (!store->server) { store->server = nfmalloc( sizeof(sserver) ); memcpy( store->server, &sserver, sizeof(sserver) ); store->server->name = store->name; } else if (acc_opt) { error( "%s '%s' has both Account and account-specific options\n", type, name ); cfg->err = 1; } } return 1; } static uint imap_get_caps( store_t *gctx ATTR_UNUSED ) { return DRV_CRLF | DRV_VERBOSE | DRV_ASYNC; } struct driver imap_driver = { imap_get_caps, imap_parse_store, imap_cleanup, imap_alloc_store, imap_set_callbacks, imap_connect_store, imap_free_store, imap_cancel_store, imap_list_store, imap_select_box, imap_get_box_path, imap_create_box, imap_open_box, imap_get_uidnext, imap_get_supported_flags, imap_confirm_box_empty, imap_delete_box, imap_finish_delete_box, imap_prepare_load_box, imap_load_box, imap_fetch_msg, imap_store_msg, imap_find_new_msgs, imap_set_msg_flags, imap_trash_msg, imap_close_box, imap_cancel_cmds, imap_get_memory_usage, imap_get_fail_state, }; isync-1.5.1/src/mdconvert.c0000644000175000001440000001557614405565305011301 // SPDX-FileCopyrightText: 2004-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later /* * mdconvert - Maildir UID scheme converter */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXE "mdconvert" #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) # define ATTR_NORETURN __attribute__((noreturn)) # define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) #else # define ATTR_NORETURN # define ATTR_PRINTFLIKE(fmt,var) #endif static void ATTR_NORETURN oob( void ) { fputs( "Fatal: buffer too small. Please report a bug.\n", stderr ); abort(); } static void ATTR_PRINTFLIKE(1, 2) sys_error( const char *msg, ... ) { va_list va; char buf[1024]; va_start( va, msg ); if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf)) oob(); va_end( va ); perror( buf ); } static int ATTR_PRINTFLIKE(3, 4) nfsnprintf( char *buf, int blen, const char *fmt, ... ) { int ret; va_list va; va_start( va, fmt ); if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) oob(); va_end( va ); return ret; } static const char *subdirs[] = { "cur", "new" }; static struct flock lck; static DBT key, value; static int convert( const char *box, int altmap ) { DB *db; DIR *d; struct dirent *e; const char *u, *ru; char *p, *s, *dpath, *spath, *dbpath; int i, n, ret, sfd, dfd, bl, ml, uv[2], uid; struct stat st; char buf[_POSIX_PATH_MAX], buf2[_POSIX_PATH_MAX]; char umpath[_POSIX_PATH_MAX], uvpath[_POSIX_PATH_MAX], tdpath[_POSIX_PATH_MAX]; if (stat( box, &st ) || !S_ISDIR(st.st_mode)) { fprintf( stderr, "'%s' is no Maildir mailbox.\n", box ); return 1; } nfsnprintf( umpath, sizeof(umpath), "%s/.isyncuidmap.db", box ); nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", box ); if (altmap) dpath = umpath, spath = uvpath, dbpath = tdpath; else spath = umpath, dpath = uvpath, dbpath = umpath; nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath ); if ((sfd = open( spath, O_RDWR )) < 0) { if (errno != ENOENT) sys_error( "Cannot open %s", spath ); return 1; } if (fcntl( sfd, F_SETLKW, &lck )) { sys_error( "Cannot lock %s", spath ); goto sbork; } if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) { sys_error( "Cannot create %s", tdpath ); goto sbork; } if (db_create( &db, NULL, 0 )) { fputs( "Error: db_create() failed\n", stderr ); goto tbork; } if ((ret = (db->open)( db, NULL, dbpath, NULL, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) { db->err( db, ret, "Error: db->open(%s)", dbpath ); dbork: db->close( db, 0 ); tbork: unlink( tdpath ); close( dfd ); sbork: close( sfd ); return 1; } key.data = (void *)"UIDVALIDITY"; key.size = 11; if (altmap) { if ((n = read( sfd, buf, sizeof(buf) - 1 )) <= 0 || (buf[n] = 0, sscanf( buf, "%d\n%d", &uv[0], &uv[1] ) != 2)) { fprintf( stderr, "Error: cannot read UIDVALIDITY of '%s'.\n", box ); goto dbork; } value.data = uv; value.size = sizeof(uv); if ((ret = db->put( db, NULL, &key, &value, 0 ))) { db->err( db, ret, "Error: cannot write UIDVALIDITY for '%s'", box ); goto dbork; } } else { if ((ret = db->get( db, NULL, &key, &value, 0 ))) { db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box ); goto dbork; } n = sprintf( buf, "%d\n%d\n", ((int *)value.data)[0], ((int *)value.data)[1] ); if (write( dfd, buf, n ) != n) { fprintf( stderr, "Error: cannot write UIDVALIDITY for '%s'.\n", box ); goto dbork; } } again: for (i = 0; i < 2; i++) { bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] ); if (!(d = opendir( buf ))) { sys_error( "Cannot list %s", buf ); goto dbork; } while ((e = readdir( d ))) { if (*e->d_name == '.') continue; nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", e->d_name ); memcpy( buf2, buf, bl ); p = strstr( e->d_name, ",U=" ); if (p) for (u = p, ru = p + 3; isdigit( (unsigned char)*ru ); ru++); else u = ru = strchr( e->d_name, ':' ); if (u) ml = u - e->d_name; else ru = "", ml = sizeof(buf); if (altmap) { if (!p) continue; key.data = e->d_name; key.size = (size_t)(strchr( e->d_name, ',' ) - e->d_name); uid = atoi( p + 3 ); value.data = &uid; value.size = sizeof(uid); if ((ret = db->put( db, NULL, &key, &value, 0 ))) { db->err( db, ret, "Error: cannot write UID for '%s'", box ); goto ebork; } nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s%s", ml, e->d_name, ru ); } else { s = strpbrk( e->d_name, ",:" ); key.data = e->d_name; key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name ); if ((ret = db->get( db, NULL, &key, &value, 0 ))) { if (ret != DB_NOTFOUND) { db->err( db, ret, "Error: cannot read UID for '%s'", box ); goto ebork; } continue; } uid = *(int *)value.data; nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s,U=%d%s", ml, e->d_name, uid, ru ); } if (rename( buf, buf2 )) { if (errno == ENOENT) { closedir( d ); goto again; } sys_error( "Cannot rename %s to %s", buf, buf2 ); ebork: closedir( d ); goto dbork; } } closedir( d ); } db->close( db, 0 ); close( dfd ); if (rename( tdpath, dpath )) { sys_error( "Cannot rename %s to %s", tdpath, dpath ); close( sfd ); return 1; } if (unlink( spath )) sys_error( "Cannot remove %s", spath ); close( sfd ); return 0; } int main( int argc, char **argv ) { int oint, ret, altmap = 0; for (oint = 1; oint < argc; oint++) { if (!strcmp( argv[oint], "-h" ) || !strcmp( argv[oint], "--help" )) { puts( "Usage: " EXE " [-a] mailbox...\n" " -a, --alt convert to alternative (DB based) UID scheme\n" " -n, --native convert to native (file name based) UID scheme (default)\n" " -h, --help show this help message\n" " -v, --version display version" ); return 0; } else if (!strcmp( argv[oint], "-v" ) || !strcmp( argv[oint], "--version" )) { puts( EXE " " VERSION " - Maildir UID scheme converter" ); return 0; } else if (!strcmp( argv[oint], "-a" ) || !strcmp( argv[oint], "--alt" )) { altmap = 1; } else if (!strcmp( argv[oint], "-n" ) || !strcmp( argv[oint], "--native" )) { altmap = 0; } else if (!strcmp( argv[oint], "--" )) { oint++; break; } else if (argv[oint][0] == '-') { fprintf( stderr, "Unrecognized option '%s'. Try " EXE " -h\n", argv[oint] ); return 1; } else { break; } } if (oint == argc) { fprintf( stderr, "Mailbox specification missing. Try " EXE " -h\n" ); return 1; } #if SEEK_SET != 0 lck.l_whence = SEEK_SET; #endif #if F_WRLCK != 0 lck.l_type = F_WRLCK; #endif ret = 0; for (; oint < argc; oint++) ret |= convert( argv[oint], altmap ); return ret; } isync-1.5.1/src/tst_msg_cvt.c0000644000175000001440000003102414405565305011616 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later // // isync test suite // #include "sync_p.h" #define TUID "one two tuid" static_assert( sizeof(TUID) - 1 == TUIDL, "TUID size mismatch" ); static size_t strip_cr( char *buf ) { size_t i, j = 0; char c, pc = 0; for (i = 0; (c = buf[i]); i++) { if (c == '\n' && pc == '\r') j--; buf[j++] = c; pc = c; } buf[j] = 0; return j; } #define NL_UNIX 0 #define NL_ANY 1 #define AS_IS 0 #define ADD_TUID 1 #define FULL 0 #define MINIMAL 1 #define REGULAR 0 #define FLAGGED 1 #define OK_HEADER 0 #define PARTIAL_HEADER 1 #define BIG_SIZE 2345687 #define BIG_SIZE_STR "2.2MiB" #define SEP "=============" static void test( const char *name, const char *in, int scr, int rscr, const char *out, int tcr, int rtcr, int add_tuid, int minimal, int flagged ) { assert( !rscr || scr ); assert( !rtcr || tcr ); assert( !minimal || add_tuid ); assert( !flagged || minimal ); printf( "Testing %s, %s (%s) => %s (%s)%s%s%s ...\n", name, rscr ? "CRLF" : "LF", scr ? "Any" : "Unix", rtcr ? "CRLF" : "LF", tcr ? "Any" : "Unix", add_tuid ? ", add TUID" : "", minimal ? ", minimal" : "", flagged ? ", flagged" : "" ); sync_rec_t srec; message_t msg; copy_vars_t vars; vars.minimal = minimal; if (add_tuid) { vars.srec = &srec; memcpy( vars.srec->tuid, TUID, TUIDL ); if (minimal) { vars.msg = &msg; vars.msg->size = BIG_SIZE; vars.data.flags = flagged ? F_FLAGGED : 0; } } else { vars.srec = 0; } vars.data.data = strdup( in ); vars.data.len = rscr ? strlen( in ) : strip_cr( vars.data.data ); char *orig = strdup( vars.data.data ); const char *err = copy_msg_convert( scr, tcr, &vars ); if (err) { printf( "FAIL: %s!\n", err ); exit( 1 ); } if (!rtcr) { char *tout = strdup( out ); size_t toutl = strip_cr( tout ); if (toutl != vars.data.len || memcmp( vars.data.data, tout, toutl )) { xprintf( "FAIL!\n" SEP " Input " SEP "\n%!&s\n" SEP " Expected output " SEP "\n%!&s\n" SEP " Output " SEP "\n%.*!&s\n" SEP "\n", orig, tout, vars.data.len, vars.data.data ); exit( 1 ); } free( tout ); } else { size_t outl = strlen( out ); if (outl != vars.data.len || memcmp( vars.data.data, out, outl )) { xprintf( "FAIL!\n" SEP " Input " SEP "\n%!&s\n" SEP " Expected output (%u bytes) " SEP "\n%!&s\n" SEP " Actual output (%u bytes) " SEP "\n%.*!&s\n" SEP "\n", orig, outl, out, vars.data.len, vars.data.len, vars.data.data ); exit( 1 ); } } free( orig ); free( vars.data.data ); } static void tests( const char *name, const char *in, const char *out, int add_tuid, int minimal, int flagged, int hdr_sts ) { test( name, in, NL_UNIX, NL_UNIX, out, NL_ANY, NL_ANY, add_tuid, minimal, flagged ); test( name, in, NL_ANY, NL_UNIX, out, NL_UNIX, NL_UNIX, add_tuid, minimal, flagged ); test( name, in, NL_ANY, NL_ANY, out, NL_UNIX, NL_UNIX, add_tuid, minimal, flagged ); // Skip if (scr == tcr && !srec), like copy_msg() does. if (add_tuid) { test( name, in, NL_UNIX, NL_UNIX, out, NL_UNIX, NL_UNIX, ADD_TUID, minimal, flagged ); if (hdr_sts == OK_HEADER) { test( name, in, NL_ANY, NL_UNIX, out, NL_ANY, NL_UNIX, ADD_TUID, minimal, flagged ); } else { // If there are no line breaks to detect the style, the output defaults to CRLF. test( name, in, NL_ANY, NL_UNIX, out, NL_ANY, NL_ANY, ADD_TUID, minimal, flagged ); } test( name, in, NL_ANY, NL_ANY, out, NL_ANY, NL_ANY, ADD_TUID, minimal, flagged ); } } static void fulltests( const char *name, const char *in, const char *out, int add_tuid ) { tests( name, in, out, add_tuid, FULL, REGULAR, OK_HEADER ); } static void fulltests_ih( const char *name, const char *in, const char *out, int add_tuid ) { tests( name, in, out, add_tuid, FULL, REGULAR, PARTIAL_HEADER ); } static void mintests( const char *name, const char *in, const char *out, int flagged ) { tests( name, in, out, ADD_TUID, MINIMAL, flagged, OK_HEADER ); } static void mintests_ih( const char *name, const char *in, const char *out, int flagged ) { tests( name, in, out, ADD_TUID, MINIMAL, flagged, PARTIAL_HEADER ); } #define FROM "From: de\rvil\r\n" #define R_TO "To: me" #define TO R_TO "\r\n" #define R_IN_TUID "X-TUID: garbage" #define IN_TUID R_IN_TUID "\r\n" #define OUT_TUID "X-TUID: " TUID "\r\n" #define R_SUBJECT "Subject: hell" #define SUBJECT R_SUBJECT "\r\n" #define PH_SUBJECT "Subject: [placeholder] hell\r\n" #define NO_SUBJECT "Subject: [placeholder] (No Subject)\r\n" #define BODY "\r\nHi,\r\n\r\n...\r\n" #define PH_BODY "\r\nHaving a size of 2.2MiB, this message is over the MaxSize limit.\r\n" \ "Flag it and sync again (Sync mode Upgrade) to fetch its real contents.\r\n" #define FLAGGED_PH_BODY PH_BODY "\r\nThe original message is flagged as important.\r\n" #define scc static const char int main( void ) { scc in_from_to[] = FROM TO BODY; fulltests( "from / to", in_from_to, in_from_to, AS_IS ); scc out_from_to[] = FROM TO OUT_TUID BODY; fulltests( "from / to", in_from_to, out_from_to, ADD_TUID ); scc in_from_tuid_to[] = FROM IN_TUID TO BODY; scc out_from_tuid_to[] = FROM OUT_TUID TO BODY; fulltests( "from / tuid / to", in_from_tuid_to, out_from_tuid_to, ADD_TUID ); scc out_from_to_ph[] = FROM TO OUT_TUID NO_SUBJECT PH_BODY; mintests( "from / to", in_from_to, out_from_to_ph, REGULAR ); scc out_from_to_flagged_ph[] = FROM TO OUT_TUID NO_SUBJECT FLAGGED_PH_BODY; mintests( "from / to", in_from_to, out_from_to_flagged_ph, FLAGGED ); scc out_from_tuid_to_ph[] = FROM OUT_TUID TO NO_SUBJECT PH_BODY; mintests( "from / tuid / to", in_from_tuid_to, out_from_tuid_to_ph, REGULAR ); scc in_from_subj_to[] = FROM SUBJECT TO BODY; scc out_from_subj_to[] = FROM PH_SUBJECT TO OUT_TUID PH_BODY; mintests( "from / subject / to", in_from_subj_to, out_from_subj_to, REGULAR ); scc in_from_subj_tuid_to[] = FROM SUBJECT IN_TUID TO BODY; scc out_from_subj_tuid_to[] = FROM PH_SUBJECT OUT_TUID TO PH_BODY; mintests( "from / subject / tuid / to", in_from_subj_tuid_to, out_from_subj_tuid_to, REGULAR ); scc in_subj_from_tuid_to[] = SUBJECT FROM IN_TUID TO BODY; scc out_subj_from_tuid_to[] = PH_SUBJECT FROM OUT_TUID TO PH_BODY; mintests( "subject / from / tuid / to", in_subj_from_tuid_to, out_subj_from_tuid_to, REGULAR ); scc in_from_tuid_subj_to[] = FROM IN_TUID SUBJECT TO BODY; scc out_from_tuid_subj_to[] = FROM OUT_TUID PH_SUBJECT TO PH_BODY; mintests( "from / tuid / subject / to", in_from_tuid_subj_to, out_from_tuid_subj_to, REGULAR ); scc in_tuid_from_subj_to[] = IN_TUID FROM SUBJECT TO BODY; scc out_tuid_from_subj_to[] = OUT_TUID FROM PH_SUBJECT TO PH_BODY; mintests( "tuid / from / subject / to", in_tuid_from_subj_to, out_tuid_from_subj_to, REGULAR ); scc in_from_to_b1[] = FROM TO; fulltests( "from / to w/o end", in_from_to_b1, in_from_to_b1, AS_IS ); scc out_from_to_b1[] = FROM TO OUT_TUID; fulltests( "from / to w/o end", in_from_to_b1, out_from_to_b1, ADD_TUID ); scc in_from_tuid_to_b1[] = FROM IN_TUID TO; scc out_from_tuid_to_b1[] = FROM OUT_TUID TO; fulltests( "from / tuid / to w/o end", in_from_tuid_to_b1, out_from_tuid_to_b1, ADD_TUID ); scc in_from_to_tuid_b1[] = FROM TO IN_TUID; scc out_from_to_tuid_b1[] = FROM TO OUT_TUID; fulltests( "from / to / tuid w/o end", in_from_to_tuid_b1, out_from_to_tuid_b1, ADD_TUID ); mintests( "from / to w/o end", in_from_to_b1, out_from_to_ph, REGULAR ); mintests( "from / tuid / to w/o end", in_from_tuid_to_b1, out_from_tuid_to_ph, REGULAR ); scc in_from_subj_to_b1[] = FROM SUBJECT TO; mintests( "from / subject / to w/o end", in_from_subj_to_b1, out_from_subj_to, REGULAR ); scc in_from_subj_tuid_to_b1[] = FROM SUBJECT IN_TUID TO; mintests( "from / subject / tuid / to w/o end", in_from_subj_tuid_to_b1, out_from_subj_tuid_to, REGULAR ); scc in_from_subj_to_tuid_b1[] = FROM SUBJECT TO IN_TUID; scc out_from_subj_to_tuid_b1[] = FROM PH_SUBJECT TO OUT_TUID PH_BODY; mintests( "from / subject / to / tuid w/o end", in_from_subj_to_tuid_b1, out_from_subj_to_tuid_b1, REGULAR ); scc in_from_tuid_subj_to_b1[] = FROM IN_TUID SUBJECT TO; mintests( "from / tuid / subject / to w/o end", in_from_tuid_subj_to_b1, out_from_tuid_subj_to, REGULAR ); scc in_from_tuid_to_subj_b1[] = FROM IN_TUID TO SUBJECT; scc out_from_tuid_to_subj_b1[] = FROM OUT_TUID TO PH_SUBJECT PH_BODY; mintests( "from / tuid / to / subject w/o end", in_from_tuid_to_subj_b1, out_from_tuid_to_subj_b1, REGULAR ); scc in_from_to_b2[] = FROM R_TO "\r"; fulltests( "from / to w/o lf", in_from_to_b2, in_from_to_b2, AS_IS ); scc out_from_to_b2[] = FROM TO OUT_TUID "\r"; fulltests( "from / to w/o lf", in_from_to_b2, out_from_to_b2, ADD_TUID ); scc in_from_tuid_to_b2[] = FROM IN_TUID R_TO "\r"; scc out_from_tuid_to_b2[] = FROM OUT_TUID R_TO "\r"; fulltests( "from / tuid / to w/o lf", in_from_tuid_to_b2, out_from_tuid_to_b2, ADD_TUID ); scc in_from_to_tuid_b2[] = FROM TO R_IN_TUID "\r"; fulltests( "from / to / tuid w/o lf", in_from_to_tuid_b2, out_from_to_tuid_b1, ADD_TUID ); mintests( "from / to w/o lf", in_from_to_b2, out_from_to_ph, REGULAR ); mintests( "from / tuid / to w/o lf", in_from_tuid_to_b2, out_from_tuid_to_ph, REGULAR ); scc in_from_subj_to_b2[] = FROM SUBJECT R_TO "\r"; mintests( "from / subject / to w/o lf", in_from_subj_to_b2, out_from_subj_to, REGULAR ); scc in_from_subj_tuid_to_b2[] = FROM SUBJECT IN_TUID R_TO "\r"; mintests( "from / subject / tuid / to w/o lf", in_from_subj_tuid_to_b2, out_from_subj_tuid_to, REGULAR ); scc in_from_subj_to_tuid_b2[] = FROM SUBJECT TO R_IN_TUID "\r"; mintests( "from / subject / to / tuid w/o lf", in_from_subj_to_tuid_b2, out_from_subj_to_tuid_b1, REGULAR ); scc in_from_tuid_subj_to_b2[] = FROM IN_TUID SUBJECT R_TO "\r"; mintests( "from / tuid / subject / to w/o lf", in_from_tuid_subj_to_b2, out_from_tuid_subj_to, REGULAR ); scc in_from_tuid_to_subj_b2[] = FROM IN_TUID TO R_SUBJECT "\r"; mintests( "from / tuid / to / subject w/o lf", in_from_tuid_to_subj_b2, out_from_tuid_to_subj_b1, REGULAR ); scc in_from_to_b3[] = FROM R_TO; fulltests( "from / to w/o crlf", in_from_to_b3, in_from_to_b3, AS_IS ); fulltests( "from / to w/o crlf", in_from_to_b3, out_from_to_b1, ADD_TUID ); scc in_from_tuid_to_b3[] = FROM IN_TUID R_TO; scc out_from_tuid_to_b3[] = FROM OUT_TUID R_TO; fulltests( "from / tuid / to w/o crlf", in_from_tuid_to_b3, out_from_tuid_to_b3, ADD_TUID ); scc in_from_to_tuid_b3[] = FROM TO R_IN_TUID; fulltests( "from / to / tuid w/o crlf", in_from_to_tuid_b3, out_from_to_tuid_b1, ADD_TUID ); mintests( "from / to w/o crlf", in_from_to_b3, out_from_to_ph, REGULAR ); mintests( "from / tuid / to w/o crlf", in_from_tuid_to_b3, out_from_tuid_to_ph, REGULAR ); scc in_from_subj_to_b3[] = FROM SUBJECT R_TO; mintests( "from / subject / to w/o crlf", in_from_subj_to_b3, out_from_subj_to, REGULAR ); scc in_from_subj_tuid_to_b3[] = FROM SUBJECT IN_TUID R_TO; mintests( "from / subject / tuid / to w/o crlf", in_from_subj_tuid_to_b3, out_from_subj_tuid_to, REGULAR ); scc in_from_subj_to_tuid_b3[] = FROM SUBJECT TO R_IN_TUID; mintests( "from / subject / to / tuid w/o crlf", in_from_subj_to_tuid_b3, out_from_subj_to_tuid_b1, REGULAR ); scc in_from_tuid_subj_to_b3[] = FROM IN_TUID SUBJECT R_TO; mintests( "from / tuid / subject / to w/o crlf", in_from_tuid_subj_to_b3, out_from_tuid_subj_to, REGULAR ); scc in_from_tuid_to_subj_b3[] = FROM IN_TUID TO R_SUBJECT; mintests( "from / tuid / to / subject w/o crlf", in_from_tuid_to_subj_b3, out_from_tuid_to_subj_b1, REGULAR ); scc in_to_b1[] = R_TO "\r"; fulltests_ih( "to w/o lf", in_to_b1, in_to_b1, AS_IS ); scc out_to_b1[] = TO OUT_TUID "\r"; fulltests_ih( "to w/o lf", in_to_b1, out_to_b1, ADD_TUID ); scc out_to_b1_ph[] = TO OUT_TUID NO_SUBJECT PH_BODY; mintests_ih( "to w/o lf", in_to_b1, out_to_b1_ph, REGULAR ); scc in_to_b2[] = R_TO; fulltests_ih( "to w/o crlf", in_to_b2, in_to_b2, AS_IS ); scc out_to_b2[] = TO OUT_TUID; fulltests_ih( "to w/o crlf", in_to_b2, out_to_b2, ADD_TUID ); scc out_to_b2_ph[] = TO OUT_TUID NO_SUBJECT PH_BODY; mintests_ih( "to w/o crlf", in_to_b2, out_to_b2_ph, REGULAR ); scc in_no_hdr[] = BODY; fulltests( "no header", in_no_hdr, in_no_hdr, AS_IS ); scc out_no_hdr[] = OUT_TUID BODY; fulltests( "no header", in_no_hdr, out_no_hdr, ADD_TUID ); scc out_no_hdr_ph[] = OUT_TUID NO_SUBJECT PH_BODY; mintests( "no header", in_no_hdr, out_no_hdr_ph, REGULAR ); scc in_empty[] = ""; fulltests_ih( "empty", in_empty, in_empty, AS_IS ); scc out_empty[] = OUT_TUID; fulltests_ih( "empty", in_empty, out_empty, ADD_TUID ); scc out_empty_ph[] = OUT_TUID NO_SUBJECT PH_BODY; mintests_ih( "empty", in_empty, out_empty_ph, REGULAR ); return 0; } isync-1.5.1/src/mbsyncrc.sample0000644000175000001440000000461114660617110012136 # Global configuration section # Values here are used as defaults for any following Channel section that # doesn't specify them. Expunge None Create Both # More sections follow # # !!!! Note that empty lines delimit sections !!!! MaildirStore local Path ~/Mail/ Trash Trash IMAPStore work Host work.host.com User tehuser Pass xxxxxxxx # Fetch password from gnome-keyring: #PassCmd "gnome-keyring-query get mail_pw" # Fetch password from .netrc: #PassCmd "sed -n -e 's,^machine work\\.host\\.com login tehuser password \\(.*\\),\\1,p' < $HOME/.netrc" # Fetch password from a gpg-encrypted file: #PassCmd "gpg --quiet --for-your-eyes-only --decrypt $HOME/imappassword.gpg" # Fetch password from pwmd (http://pwmd.sourceforge.net/): #PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile" Channel work Far :work: Near :local:work Expunge Near Sync PullNew Push IMAPStore personal Host host.play.com Port 6789 TLSType None Channel personal Far :personal: Near :local:personal Expunge Both MaxMessages 150 MaxSize 200k IMAPStore remote Tunnel "ssh -q host.remote.com /usr/sbin/imapd" Channel remote Far :remote: Near :local:remote Group boxes Channels work personal remote # Due to the divergent Path suffixes, it's possible to have # multiple Stores homed in the same directory. # You could even put them all directly into $HOME. MaildirStore local-personal Path ~/Mail/personal- Inbox ~/Mail/personal-INBOX MaildirStore local-work Path ~/Mail/work- # Just because. Inbox ~/Mail/w0rk_InBoX Channel personal-joined Far :personal: Near :local-personal: Patterns * Channel work-joined Far :work: Near :local-work: Patterns * Group joined personal-joined work-joined IMAPStore st1 Host st1.domain.com AuthMech CRAM-MD5 # Omit if you want to use the system certificate store. CertificateFile ~/.st1-certificate.crt IMAPStore st2 Host imap.another-domain.com Path non-standard/ TLSVersions -1.2 Channel rst Far :st1:somebox Near :st2: IMAPAccount server Host imaps:foo.bar.com # Omit if you want to use the system certificate store. CertificateFile ~/.server-certificate.crt IMAPStore server Account server MapInbox inbox Trash ~/trash TrashRemoteNew yes MaildirStore mirror Path ~/Maildir/ SubFolders Verbatim Channel o2o Far :server: Near :mirror: Patterns % Group partial o2o:inbox,sent-mail,foobar # INBOX => server, INBOX.foo => server.foo, etc. Channel inbox Far :server:INBOX Near :mirror:server Patterns * isync-1.5.1/src/common.h0000644000175000001440000002411714652655054010571 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #ifndef COMMON_H #define COMMON_H #include #include #include #include #include #include #include #include #include #include #include typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #define as(ar) (sizeof(ar)/sizeof(ar[0])) #define stringify__(x) #x #define stringify(x) stringify__(x) // From https://stackoverflow.com/a/62984543/3685191 #define deparen(x) esc_(ish_ x) #define esc_(...) esc__(__VA_ARGS__) #define esc__(...) van_ ## __VA_ARGS__ #define ish_(...) ish_ __VA_ARGS__ #define van_ish_ #define shifted_bit(in, from, to) \ ((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to)) #define BIT_ENUM_VAL(name) \ name, \ name##_dummy = 2 * name - 1, #define DEFINE_BIT_ENUM(list) \ enum list { \ list##_dummy, \ list(BIT_ENUM_VAL) \ }; #define PFX_BIT_ENUM_VAL(pfx, name) \ pfx##_##name, \ pfx##_##name##_dummy = 2 * pfx##_##name - 1, #define PFX_BIT_ENUM_NUM(pfx, name) \ pfx##_##name##_bit, #define DEFINE_PFX_BIT_ENUM(list) \ enum list { \ list##_dummy, \ list(PFX_BIT_ENUM_VAL) \ }; \ enum { \ list(PFX_BIT_ENUM_NUM) \ list##_num_bits \ }; #define BIT_ENUM_STR(pfx, name) \ stringify(name) "\0" #define BIT_ENUM_STR_OFF(pfx, name) \ name##_str_off, \ name##_str_off_dummy = name##_str_off + sizeof(stringify(name)) - 1, #define GET_BIT_ENUM_STR_OFF(pfx, name) \ name##_str_off, #define PFX_BIT_ENUM_STR(pfx, name) \ stringify(pfx) "_" stringify(name) "\0" #define PFX_BIT_ENUM_STR_OFF(pfx, name) \ pfx##_##name##_str_off, \ pfx##_##name##_str_end = pfx##_##name##_str_off + sizeof(stringify(pfx) "_" stringify(name)) - 1, #define GET_PFX_BIT_ENUM_STR_OFF(pfx, name) \ pfx##_##name##_str_off, #define static_assert_bits(list, type, field) \ static_assert( list##_num_bits <= sizeof(((type){ 0 }).field) * 8, \ stringify(type) "::" stringify(field) " is too small" ) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) # define ATTR_UNUSED __attribute__((unused)) # define ATTR_NORETURN __attribute__((noreturn)) # define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) # define ATTR_OPTIMIZE __attribute__((optimize("2"))) #else # define ATTR_UNUSED # define ATTR_NORETURN # define ATTR_PRINTFLIKE(fmt,var) # define ATTR_OPTIMIZE #endif #if defined(__clang__) # define DO_PRAGMA__(text) _Pragma(#text) # define DIAG_PUSH DO_PRAGMA__(clang diagnostic push) # define DIAG_POP DO_PRAGMA__(clang diagnostic pop) # define DIAG_DISABLE(text) DO_PRAGMA__(clang diagnostic ignored text) #elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) # define DO_PRAGMA__(text) _Pragma(#text) # define DIAG_PUSH DO_PRAGMA__(GCC diagnostic push) # define DIAG_POP DO_PRAGMA__(GCC diagnostic pop) # define DIAG_DISABLE(text) DO_PRAGMA__(GCC diagnostic ignored text) #else # define DIAG_PUSH # define DIAG_POP # define DIAG_DISABLE(text) #endif #if __GNUC__ >= 7 || defined(__clang__) # define FALLTHROUGH __attribute__((fallthrough)); #else # define FALLTHROUGH #endif #ifdef __GNUC__ # define INLINE __inline__ #else # define INLINE #endif #define EXE "mbsync" /* main.c */ enum { VERYQUIET, QUIET, TERSE, VERBOSE, }; #define options_enum(fn) \ fn(DEBUG_MAILDIR) \ fn(DEBUG_NET) \ fn(DEBUG_NET_ALL) \ fn(DEBUG_SYNC) \ fn(DEBUG_MAIN) \ fn(DEBUG_DRV) \ fn(DEBUG_DRV_ALL) \ \ fn(DEBUG_CRASH) \ \ fn(PROGRESS) \ \ fn(DRYRUN) \ \ fn(EXT_EXIT) \ \ fn(ZERODELAY) \ fn(KEEPJOURNAL) \ fn(FORCEJOURNAL) \ fn(FORCEASYNC_F) \ fn(FORCEASYNC_N) \ fn(FAKEEXPUNGE) \ fn(FAKEDUMBSTORE) DEFINE_BIT_ENUM(options_enum) #define FORCEASYNC(b) (FORCEASYNC_F << (b)) #define DEBUG_ANY (DEBUG_MAILDIR | DEBUG_NET | DEBUG_SYNC | DEBUG_MAIN | DEBUG_DRV) #define DEBUG_ALL (DEBUG_ANY | DEBUG_CRASH) // Global options extern int Verbosity; extern int DFlags; extern int JLimit, JCount; extern int UseFSync; // Global constants (inited by main()) extern int Pid; extern char Hostname[256]; extern const char *Home; void countStep( void ); void stats( void ); /* util.c */ #ifdef DEBUG_FLAG # define debug(...) \ do { \ if (DFlags & DEBUG_FLAG) \ print( __VA_ARGS__ ); \ } while (0) # define debugn(...) \ do { \ if (DFlags & DEBUG_FLAG) \ printn( __VA_ARGS__ ); \ } while (0) #endif void ATTR_PRINTFLIKE(1, 2) print( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) printn( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) info( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) infon( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) progress( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) notice( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) warn( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) error( const char *, ... ); void ATTR_PRINTFLIKE(1, 0) vsys_error( const char *, va_list va ); void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... ); void flushn( void ); char *xvasprintf( const char *fmt, va_list ap ); void xprintf( const char *fmt, ... ); #if !defined(_POSIX_SYNCHRONIZED_IO) || _POSIX_SYNCHRONIZED_IO <= 0 # define fdatasync fsync #endif void ATTR_PRINTFLIKE(2, 0) vFprintf( FILE *f, const char *msg, va_list va ); void ATTR_PRINTFLIKE(2, 3) Fprintf( FILE *f, const char *msg, ... ); void Fclose( FILE *f, int safe ); typedef struct string_list { struct string_list *next; char string[1]; } string_list_t; void add_string_list_n( string_list_t **list, const char *str, uint len ); void add_string_list( string_list_t **list, const char *str ); void free_string_list( string_list_t *list ); #ifndef HAVE_MEMRCHR void *memrchr( const void *s, int c, size_t n ); #endif #ifndef HAVE_STRNLEN size_t strnlen( const char *str, size_t maxlen ); #endif void to_upper( char *str, uint len ); int starts_with( const char *str, int strl, const char *cmp, uint cmpl ); int starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl ); int equals( const char *str, int strl, const char *cmp, uint cmpl ); int equals_upper( const char *str, int strl, const char *cmp, uint cmpl ); #ifndef HAVE_TIMEGM time_t timegm( struct tm *tm ); #endif void fmt_bits( uint bits, uint num_bits, const char *bit_str, const int *bit_off, char *buf ); #define BIT_FORMATTER_RET(name, list, fn) \ enum { list(fn##_OFF) name##_str_len }; \ struct name##_str { char str[name##_str_len]; }; #define BIT_FORMATTER_PROTO(name, storage) \ storage struct name##_str ATTR_OPTIMIZE /* force RVO */ \ fmt_##name( uint bits ) #define BIT_FORMATTER_IMPL(name, list, fn, storage) \ BIT_FORMATTER_PROTO(name, storage) \ { \ static const char strings[] = list(fn); \ static const int offsets[] = { list(GET_##fn##_OFF) }; \ \ struct name##_str buf; \ fmt_bits( bits, as(offsets), strings, offsets, buf.str ); \ return buf; \ } // Note that this one uses enum labels without prefix ... #define BIT_FORMATTER_FUNCTION(name, list) \ BIT_FORMATTER_RET(name, list, BIT_ENUM_STR) \ BIT_FORMATTER_IMPL(name, list, BIT_ENUM_STR, static) // ... while these ones use enum labels with prefix - this // is not fundamental, but simply because of the use cases. #define DECL_BIT_FORMATTER_FUNCTION(name, list) \ BIT_FORMATTER_RET(name, list, PFX_BIT_ENUM_STR) \ BIT_FORMATTER_PROTO(name, ); #define DEF_BIT_FORMATTER_FUNCTION(name, list) \ BIT_FORMATTER_IMPL(name, list, PFX_BIT_ENUM_STR, ) void *nfmalloc( size_t sz ); void *nfzalloc( size_t sz ); void *nfrealloc( void *mem, size_t sz ); char *nfstrndup( const char *str, size_t nchars ); char *nfstrdup( const char *str ); int ATTR_PRINTFLIKE(2, 0) nfvasprintf( char **str, const char *fmt, va_list va ); int ATTR_PRINTFLIKE(2, 3) nfasprintf( char **str, const char *fmt, ... ); int ATTR_PRINTFLIKE(3, 4) nfsnprintf( char *buf, int blen, const char *fmt, ... ); void ATTR_NORETURN oob( void ); void ATTR_NORETURN oom( void ); int map_name( const char *arg, int argl, char **result, uint reserve, const char *in, const char *out ); int mkdir_p( char *path, int len ); #define DEFINE_ARRAY_TYPE(T) \ typedef struct { \ T *data; \ uint size; \ } T##_array_t; \ typedef union { \ T##_array_t array; \ struct { \ T *data; \ uint size; \ uint alloc; \ }; \ } T##_array_alloc_t; \ static INLINE T *T##_array_append( T##_array_alloc_t *arr ) \ { \ if (arr->size == arr->alloc) { \ arr->alloc = arr->alloc * 2 + 100; \ arr->data = nfrealloc( arr->data, arr->alloc * sizeof(T) ); \ } \ return &arr->data[arr->size++]; \ } #define ARRAY_INIT(arr) \ do { (arr)->data = NULL; (arr)->size = (arr)->alloc = 0; } while (0) #define ARRAY_SQUEEZE(arr) \ do { \ (arr)->data = nfrealloc( (arr)->data, (arr)->size * sizeof((arr)->data[0]) ); \ } while (0) DEFINE_ARRAY_TYPE(uint) void sort_uint_array( uint_array_t array ); int find_uint_array( const uint_array_t array, uint value ); void arc4_init( void ); uchar arc4_getbyte( void ); uint bucketsForSize( uint size ); typedef struct list_head { struct list_head *next, *prev; } list_head_t; typedef struct notifier { struct notifier *next; void (*cb)( int what, void *aux ); void *aux; #ifdef HAVE_POLL_H uint index; #else int fd; short events; #endif } notifier_t; #ifdef HAVE_POLL_H # include #else # define POLLIN 1 # define POLLOUT 4 # define POLLERR 8 #endif void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux ); void conf_notifier( notifier_t *sn, short and_events, short or_events ); short notifier_config( notifier_t *sn ); void wipe_notifier( notifier_t *sn ); typedef struct { list_head_t links; void (*cb)( void *aux ); void *aux; int64_t timeout; } wakeup_t; void init_timers( void ); int64_t get_now( void ); void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux ); void conf_wakeup( wakeup_t *tmr, int timeout ); void wipe_wakeup( wakeup_t *tmr ); static INLINE int ATTR_UNUSED pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != NULL; } void main_loop( void ); #endif isync-1.5.1/src/sync_state.c0000644000175000001440000004465614652507516011461 // SPDX-FileCopyrightText: 2004-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #define DEBUG_FLAG DEBUG_SYNC #include "sync_p.h" #include #include #include #include #define JOURNAL_VERSION "5" const char *str_fn[] = { "far side", "near side" }, *str_hl[] = { "push", "pull" }; BIT_FORMATTER_FUNCTION(sts, srec_sts_enum) static char * clean_strdup( const char *s ) { char *cs = nfstrdup( s ); for (uint i = 0; cs[i]; i++) if (cs[i] == '/') cs[i] = '!'; return cs; } int prepare_state( sync_vars_t *svars ) { channel_conf_t *chan = svars->chan; if (!strcmp( chan->sync_state ? chan->sync_state : global_conf.sync_state, "*" )) { const char *path = svars->drv[N]->get_box_path( svars->ctx[N] ); if (!path) { error( "Error: store '%s' does not support in-box sync state\n", chan->stores[N]->name ); return 0; } nfasprintf( &svars->dname, "%s/." EXE "state", path ); } else { char *cnname = clean_strdup( svars->box_name[N] ); if (chan->sync_state) { nfasprintf( &svars->dname, "%s%s", chan->sync_state, cnname ); } else { char c = FieldDelimiter; char *cfname = clean_strdup( svars->box_name[F] ); nfasprintf( &svars->dname, "%s%c%s%c%s_%c%s%c%s", global_conf.sync_state, c, chan->stores[F]->name, c, cfname, c, chan->stores[N]->name, c, cnname ); free( cfname ); } free( cnname ); char *s; if (!(s = strrchr( svars->dname, '/' ))) { error( "Error: invalid SyncState location '%s'\n", svars->dname ); return 0; } // Note that this may be shorter than the configuration value, // as that may contain a filename prefix. *s = 0; if (mkdir_p( svars->dname, s - svars->dname )) { sys_error( "Error: cannot create SyncState directory '%s'", svars->dname ); return 0; } *s = '/'; } nfasprintf( &svars->jname, "%s.journal", svars->dname ); nfasprintf( &svars->nname, "%s.new", svars->dname ); nfasprintf( &svars->lname, "%s.lock", svars->dname ); return 1; } int lock_state( sync_vars_t *svars ) { struct flock lck; if (DFlags & DRYRUN) return 1; if (svars->lfd >= 0) return 1; memset( &lck, 0, sizeof(lck) ); #if SEEK_SET != 0 lck.l_whence = SEEK_SET; #endif #if F_WRLCK != 0 lck.l_type = F_WRLCK; #endif if ((svars->lfd = open( svars->lname, O_WRONLY | O_CREAT, 0666 )) < 0) { sys_error( "Error: cannot create lock file %s", svars->lname ); return 0; } if (fcntl( svars->lfd, F_SETLK, &lck )) { error( "Error: channel :%s:%s-:%s:%s is locked\n", svars->chan->stores[F]->name, svars->orig_name[F], svars->chan->stores[N]->name, svars->orig_name[N] ); close( svars->lfd ); svars->lfd = -1; return 0; } return 1; } static uchar parse_flags( const char *buf ) { uchar flags = 0; for (uint i = 0, d = 0; i < as(MsgFlags); i++) { if (buf[d] == MsgFlags[i]) { flags |= (1 << i); d++; } } return flags; } int load_state( sync_vars_t *svars ) { sync_rec_t *srec, *nsrec; FILE *jfp; uint ll; uint maxxnuid = 0; char fbuf[16]; // enlarge when support for keywords is added char buf[128], buf1[64], buf2[64]; int xt = svars->chan->expire_side; if ((jfp = fopen( svars->dname, "r" ))) { if (!lock_state( svars )) goto jbail; debug( "reading sync state %s ...\n", svars->dname ); int line = 0; while (fgets( buf, sizeof(buf), jfp )) { line++; if (!(ll = strlen( buf )) || buf[ll - 1] != '\n') { error( "Error: incomplete sync state header entry at %s:%d\n", svars->dname, line ); jbail: fclose( jfp ); return 0; } if (ll == 1) goto gothdr; if (line == 1 && isdigit( buf[0] )) { // Pre-1.1 legacy if (sscanf( buf, "%63s %63s", buf1, buf2 ) != 2 || sscanf( buf1, "%u:%u", &svars->uidval[F], &svars->maxuid[F] ) < 2 || sscanf( buf2, "%u:%u:%u", &svars->uidval[N], &maxxnuid, &svars->maxuid[N] ) < 3) { error( "Error: invalid sync state header in %s\n", svars->dname ); goto jbail; } if (maxxnuid && xt != N) goto sidefail; goto gothdr; } uint uid; if (sscanf( buf, "%63s %u", buf1, &uid ) != 2) { error( "Error: malformed sync state header entry at %s:%d\n", svars->dname, line ); goto jbail; } if (!strcmp( buf1, "FarUidValidity" ) || !strcmp( buf1, "MasterUidValidity" ) /* Pre-1.4 legacy */) { svars->uidval[F] = uid; } else if (!strcmp( buf1, "NearUidValidity" ) || !strcmp( buf1, "SlaveUidValidity" ) /* Pre-1.4 legacy */) { svars->uidval[N] = uid; } else if (!strcmp( buf1, "MaxPulledUid" )) { svars->maxuid[F] = uid; } else if (!strcmp( buf1, "MaxPushedUid" )) { svars->maxuid[N] = uid; } else if (!strcmp( buf1, "MaxExpiredFarUid" ) || !strcmp( buf1, "MaxExpiredMasterUid" ) /* Pre-1.4 legacy */) { if (xt != N) { sidefail: error( "Error: state file %s does not match ExpireSide setting\n", svars->dname ); goto jbail; } svars->maxxfuid = uid; } else if (!strcmp( buf1, "MaxExpiredNearUid" )) { if (xt != F) goto sidefail; svars->maxxfuid = uid; } else if (!strcmp( buf1, "MaxExpiredSlaveUid" )) { // Pre-1.3 legacy if (xt != N) goto sidefail; maxxnuid = uid; } else { error( "Error: unrecognized sync state header entry at %s:%d\n", svars->dname, line ); goto jbail; } } error( "Error: unterminated sync state header in %s\n", svars->dname ); goto jbail; gothdr: debug( " uid val %u/%u, max uid %u/%u, max expired %u\n", svars->uidval[F], svars->uidval[N], svars->maxuid[F], svars->maxuid[N], svars->maxxfuid ); while (fgets( buf, sizeof(buf), jfp )) { line++; if (!(ll = strlen( buf )) || buf[--ll] != '\n') { error( "Error: incomplete sync state entry at %s:%d\n", svars->dname, line ); goto jbail; } buf[ll] = 0; fbuf[0] = 0; uint t1, t2; if (sscanf( buf, "%u %u %15s", &t1, &t2, fbuf ) < 2) { error( "Error: invalid sync state entry at %s:%d\n", svars->dname, line ); goto jbail; } srec = nfzalloc( sizeof(*srec) ); srec->uid[F] = t1; srec->uid[N] = t2; char *s = fbuf; if (*s == '<') { s++; srec->status = S_DUMMY(F); } else if (*s == '>') { s++; srec->status = S_DUMMY(N); } if (*s == '^') { // Pre-1.4 legacy s++; srec->status = S_SKIPPED; } else if (*s == '~' || *s == 'X' /* Pre-1.3 legacy */) { s++; srec->status = S_EXPIRE | S_EXPIRED; } else if (srec->uid[F] == (uint)-1) { // Pre-1.3 legacy srec->uid[F] = 0; srec->status = S_SKIPPED; } else if (srec->uid[N] == (uint)-1) { srec->uid[N] = 0; srec->status = S_SKIPPED; } srec->flags = parse_flags( s ); debug( " entry (%u,%u,%s,%s)\n", srec->uid[F], srec->uid[N], fmt_flags( srec->flags ).str, fmt_sts( srec->status ).str ); *svars->srecadd = srec; svars->srecadd = &srec->next; svars->nsrecs++; } fclose( jfp ); svars->existing = 1; } else { if (errno != ENOENT) { sys_error( "Error: cannot read sync state %s", svars->dname ); return 0; } svars->existing = 0; } // This is legacy support for pre-1.3 sync states. if (maxxnuid) { uint minwuid = UINT_MAX; for (srec = svars->srecs; srec; srec = srec->next) { if ((srec->status & (S_DEAD | S_SKIPPED | S_PENDING)) || !srec->uid[F]) continue; if (srec->status & S_EXPIRED) { if (!srec->uid[N]) { // The expired message was already gone. continue; } // The expired message was not expunged yet, so re-examine it. // This will happen en masse, so just extend the bulk fetch. } else { if (srec->uid[N] && maxxnuid >= srec->uid[N]) { // The non-expired message is in the generally expired range, // so don't make it contribute to the bulk fetch. continue; } // Usual non-expired message. } if (minwuid > srec->uid[F]) minwuid = srec->uid[F]; } svars->maxxfuid = minwuid - 1; } svars->newmaxuid[F] = svars->maxuid[F]; svars->newmaxuid[N] = svars->maxuid[N]; int line = 0; if ((jfp = fopen( svars->jname, "r" ))) { if (!lock_state( svars )) goto jbail; struct stat st; if (!stat( svars->nname, &st ) && fgets( buf, sizeof(buf), jfp )) { debug( "recovering journal ...\n" ); if (!(ll = strlen( buf )) || buf[--ll] != '\n') { error( "Error: incomplete journal header in %s\n", svars->jname ); goto jbail; } buf[ll] = 0; if (!equals( buf, (int)ll, JOURNAL_VERSION, strlen(JOURNAL_VERSION) )) { error( "Error: incompatible journal version" " (got %s, expected " JOURNAL_VERSION ")\n", buf ); goto jbail; } srec = NULL; line = 1; while (fgets( buf, sizeof(buf), jfp )) { line++; if (!(ll = strlen( buf )) || buf[--ll] != '\n') { error( "Error: incomplete journal entry at %s:%d\n", svars->jname, line ); goto jbail; } buf[ll] = 0; char c; int tn, bad; uint t1, t2, t3, t4; switch ((c = buf[0])) { case '#': tn = 0; bad = (sscanf( buf + 2, "%u %u %n", &t1, &t2, &tn ) < 2) || !tn || (ll - (uint)tn != TUIDL + 2); break; case 'N': case 'F': case 'T': case 'P': case '+': case '&': case '-': case '_': case '|': bad = sscanf( buf + 2, "%u %u", &t1, &t2 ) != 2; break; case '<': case '>': case '*': case '%': case '~': case '^': bad = sscanf( buf + 2, "%u %u %u", &t1, &t2, &t3 ) != 3; break; case '$': bad = sscanf( buf + 2, "%u %u %u %u", &t1, &t2, &t3, &t4 ) != 4; break; default: error( "Error: unrecognized journal entry at %s:%d\n", svars->jname, line ); goto jbail; } if (bad) { error( "Error: malformed journal entry at %s:%d\n", svars->jname, line ); goto jbail; } if (c == 'N') { svars->maxuid[t1] = svars->newmaxuid[t1] = t2; debug( " maxuid of %s now %u\n", str_fn[t1], t2 ); } else if (c == 'F') { svars->finduid[t1] = t2; debug( " saved UIDNEXT of %s now %u\n", str_fn[t1], t2 ); } else if (c == 'T') { *uint_array_append( &svars->trashed_msgs[t1] ) = t2; debug( " trashed %u from %s\n", t2, str_fn[t1] ); } else if (c == '|') { svars->uidval[F] = t1; svars->uidval[N] = t2; debug( " UIDVALIDITYs now %u/%u\n", t1, t2 ); } else if (c == '+') { srec = nfzalloc( sizeof(*srec) ); srec->uid[F] = t1; srec->uid[N] = t2; if (svars->newmaxuid[F] < t1) svars->newmaxuid[F] = t1; if (svars->newmaxuid[N] < t2) svars->newmaxuid[N] = t2; debug( " new entry(%u,%u)\n", t1, t2 ); srec->status = S_PENDING; *svars->srecadd = srec; svars->srecadd = &srec->next; svars->nsrecs++; } else { for (nsrec = srec; srec; srec = srec->next) if (srec->uid[F] == t1 && srec->uid[N] == t2) goto syncfnd; for (srec = svars->srecs; srec != nsrec; srec = srec->next) if (srec->uid[F] == t1 && srec->uid[N] == t2) goto syncfnd; error( "Error: journal entry at %s:%d refers to non-existing sync state entry\n", svars->jname, line ); goto jbail; syncfnd: debugn( " entry(%u,%u) ", srec->uid[F], srec->uid[N] ); switch (c) { case '-': debug( "killed\n" ); srec->status = S_DEAD; break; case '#': memcpy( srec->tuid, buf + tn + 2, TUIDL ); debug( "TUID now %." stringify(TUIDL) "s\n", srec->tuid ); break; case '&': debug( "TUID %." stringify(TUIDL) "s lost\n", srec->tuid ); srec->tuid[0] = 0; break; case '<': debug( "far side now %u\n", t3 ); assign_uid( svars, srec, F, t3 ); break; case '>': debug( "near side now %u\n", t3 ); assign_uid( svars, srec, N, t3 ); break; case '*': srec->flags = (uchar)t3; debug( "flags now %s\n", fmt_lone_flags( t3 ).str ); break; case 'P': debug( "deleted dummy\n" ); srec->aflags[F] = srec->aflags[N] = 0; // Clear F_DELETED srec->status = (srec->status & ~S_PURGE) | S_PURGED; break; case '%': srec->pflags = (uchar)t3; debug( "pending flags now %s\n", fmt_lone_flags( t3 ).str ); break; case '~': srec->status = (srec->status & ~S_LOGGED) | t3; if ((srec->status & S_EXPIRED) && svars->maxxfuid < srec->uid[xt^1]) svars->maxxfuid = srec->uid[xt^1]; debug( "status now %s\n", fmt_sts( srec->status ).str ); break; case '_': debug( "has placeholder now\n" ); srec->status = S_PENDING | (!srec->uid[F] ? S_DUMMY(F) : S_DUMMY(N)); break; case '^': tn = (srec->status & S_DUMMY(F)) ? F : N; srec->pflags = (uchar)t3; debug( "upgrading %s placeholder, dummy's flags %s\n", str_fn[tn], fmt_lone_flags( t3 ).str ); srec = upgrade_srec( svars, srec, tn ); break; case '$': tn = !srec->uid[F] ? F : N; srec->aflags[tn] = (uchar)t3; srec->dflags[tn] = (uchar)t4; debug( "flag update for %s now +%s -%s\n", str_fn[tn], fmt_flags( t3 ).str, fmt_flags( t4 ).str ); break; default: assert( !"Unhandled journal entry" ); } } } } fclose( jfp ); sort_uint_array( svars->trashed_msgs[F].array ); sort_uint_array( svars->trashed_msgs[N].array ); } else { if (errno != ENOENT) { sys_error( "Error: cannot read journal %s", svars->jname ); return 0; } } svars->replayed = line; return 1; } static void create_state( sync_vars_t *svars ) { if (!(svars->nfp = fopen( svars->nname, "w" ))) { sys_error( "Error: cannot create new sync state %s", svars->nname ); exit( 1 ); } } void jFprintf( sync_vars_t *svars, const char *msg, ... ) { va_list va; if (!svars->jfp) { if (DFlags & DRYRUN) goto dryout; create_state( svars ); if (!(svars->jfp = fopen( svars->jname, svars->replayed ? "a" : "w" ))) { sys_error( "Error: cannot create journal %s", svars->jname ); exit( 1 ); } setlinebuf( svars->jfp ); if (!svars->replayed) Fprintf( svars->jfp, JOURNAL_VERSION "\n" ); } va_start( va, msg ); vFprintf( svars->jfp, msg, va ); va_end( va ); dryout: countStep(); JCount++; } void save_state( sync_vars_t *svars ) { // If no change was made, the state is also unmodified. if (!svars->jfp && !svars->replayed) return; // jfp is NULL in this case anyway, but we might have replayed. if (DFlags & DRYRUN) return; if (!svars->nfp) create_state( svars ); Fprintf( svars->nfp, "FarUidValidity %u\nNearUidValidity %u\nMaxPulledUid %u\nMaxPushedUid %u\n", svars->uidval[F], svars->uidval[N], svars->maxuid[F], svars->maxuid[N] ); if (svars->maxxfuid) Fprintf( svars->nfp, svars->chan->expire_side == N ? "MaxExpiredFarUid %u\n" : "MaxExpiredNearUid %u\n", svars->maxxfuid ); Fprintf( svars->nfp, "\n" ); for (sync_rec_t *srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; Fprintf( svars->nfp, "%u %u %s%s%s\n", srec->uid[F], srec->uid[N], (srec->status & S_DUMMY(F)) ? "<" : (srec->status & S_DUMMY(N)) ? ">" : "", (srec->status & S_SKIPPED) ? "^" : (srec->status & S_EXPIRED) ? "~" : "", fmt_flags( srec->flags ).str ); } Fclose( svars->nfp, 1 ); if (svars->jfp) Fclose( svars->jfp, 0 ); if (!(DFlags & KEEPJOURNAL)) { // Order is important! if (rename( svars->nname, svars->dname )) warn( "Warning: cannot commit sync state %s\n", svars->dname ); else if (unlink( svars->jname )) warn( "Warning: cannot delete journal %s\n", svars->jname ); } } void delete_state( sync_vars_t *svars ) { if (DFlags & DRYRUN) return; unlink( svars->nname ); unlink( svars->jname ); if (unlink( svars->dname ) || unlink( svars->lname )) { sys_error( "Error: channel %s: sync state cannot be deleted", svars->chan->name ); svars->ret = SYNC_FAIL; } } void assign_uid( sync_vars_t *svars, sync_rec_t *srec, int t, uint uid ) { srec->uid[t] = uid; if (uid == svars->newmaxuid[t] + 1) svars->newmaxuid[t] = uid; if (uid) { if (srec->status & S_UPGRADE) { srec->flags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t]; srec->aflags[t] = srec->dflags[t] = 0; // Cleanup after journal replay } else { srec->flags = srec->pflags; } } srec->status &= ~(S_PENDING | S_UPGRADE); srec->tuid[0] = 0; } void assign_tuid( sync_vars_t *svars, sync_rec_t *srec ) { for (uint i = 0; i < TUIDL; i++) { uchar c = arc4_getbyte() & 0x3f; srec->tuid[i] = (char)(c < 26 ? c + 'A' : c < 52 ? c + 'a' - 26 : c < 62 ? c + '0' - 52 : c == 62 ? '+' : '/'); } JLOG( "# %u %u %." stringify(TUIDL) "s", (srec->uid[F], srec->uid[N], srec->tuid), "new TUID" ); } int match_tuids( sync_vars_t *svars, int t, message_t *msgs ) { message_t *tmsg, *ntmsg = NULL; const char *diag; int num_lost = 0; for (sync_rec_t *srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; if (!srec->uid[t] && srec->tuid[0]) { debug( "pair(%u,%u) TUID %." stringify(TUIDL) "s\n", srec->uid[F], srec->uid[N], srec->tuid ); for (tmsg = ntmsg; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) { diag = (tmsg == ntmsg) ? "adjacently" : "after gap"; goto mfound; } } for (tmsg = msgs; tmsg != ntmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) { diag = "after reset"; goto mfound; } } JLOG( "& %u %u", (srec->uid[F], srec->uid[N]), "TUID lost" ); // Note: status remains S_PENDING. srec->tuid[0] = 0; num_lost++; continue; mfound: tmsg->srec = srec; srec->msg[t] = tmsg; ntmsg = tmsg->next; ASSIGN_UID( srec, t, tmsg->uid, "TUID matched %s", diag ); } } return num_lost; } sync_rec_t * upgrade_srec( sync_vars_t *svars, sync_rec_t *srec, int t ) { // Create an entry and append it to the current one. sync_rec_t *nsrec = nfzalloc( sizeof(*nsrec) ); nsrec->next = srec->next; srec->next = nsrec; if (svars->srecadd == &srec->next) svars->srecadd = &nsrec->next; svars->nsrecs++; // Move the placeholder to the new entry. nsrec->uid[t] = srec->uid[t]; srec->uid[t] = 0; if (srec->msg[t]) { // NULL during journal replay; is assigned later. nsrec->msg[t] = srec->msg[t]; nsrec->msg[t]->srec = nsrec; srec->msg[t] = NULL; } // Mark the original entry for upgrade. srec->status = (srec->status & ~(S_DUMMY(F) | S_DUMMY(N))) | S_PENDING | S_UPGRADE; // Mark the placeholder for nuking. nsrec->status = S_PURGE | (srec->status & (S_DEL(F) | S_DEL(N))); nsrec->aflags[t] = F_DELETED; return nsrec; } isync-1.5.1/src/config.h0000644000175000001440000000171214405565305010535 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #ifndef CONFIG_H #define CONFIG_H #include "common.h" typedef struct { const char *file; FILE *fp; char *buf; int bufl; int line; int err; int ms_warn, renew_warn, delete_warn; int path_len; char *cmd, *val, *rest; } conffile_t; extern char FieldDelimiter; #define ARG_OPTIONAL 0 #define ARG_REQUIRED 1 char *expand_strdup( const char *s, const conffile_t *cfile ); char *get_arg( conffile_t *cfile, int required, int *comment ); char parse_bool( conffile_t *cfile ); int parse_int( conffile_t *cfile ); uint parse_size( conffile_t *cfile ); int getcline( conffile_t *cfile ); int merge_ops( int cops, int ops[], const char *chan_name ); int load_config( const char *filename ); #endif isync-1.5.1/src/drv_proxy_gen.pl0000755000175000001440000001476614660617360012363 #!/usr/bin/perl # # SPDX-FileCopyrightText: 2017-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later # # mbsync - mailbox synchronizer # use strict; use warnings; die("Usage: $0 driver.h drv_proxy.c drv_proxy.inc\n") if ($#ARGV != 2); my ($in_header, $in_source, $out_source) = @ARGV; my %templates; my %defines; my %excluded; my %special; open(my $ins, $in_source) or die("Cannot open $in_source: $!\n"); my $template; my $define; my $conts; while (<$ins>) { if ($template) { if (/^\/\/\# END$/) { $templates{$template} = $conts; $template = undef; } else { $conts .= $_; } } elsif ($define) { if (/^\/\/\# END$/) { $defines{$define} = $conts; $define = undef; } else { ($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_"); $conts .= $_; } } else { if (/^\/\/\# TEMPLATE (\w+)$/) { $template = $1; $conts = ""; } elsif (/^\/\/\# DEFINE (\w+)$/) { $define = $1; $conts = ""; } elsif (/^\/\/\# DEFINE (\w+) (.*)$/) { $defines{$1} = $2; } elsif (/^\/\/\# UNDEFINE (\w+)$/) { $defines{$1} = ""; } elsif (/^\/\/\# EXCLUDE (\w+)$/) { $excluded{$1} = 1; } elsif (/^\/\/\# SPECIAL (\w+)$/) { $special{$1} = 1; } } } close($ins); open(my $inh, $in_header) or die("Cannot open $in_header: $!\n"); my $sts = 0; my $cont = ""; while (<$inh>) { if ($sts == 0) { if (/^struct driver \{$/) { $sts = 1; } } elsif ($sts == 1) { if (/^\};$/) { $sts = 0; } else { $cont .= $_; } } } close($inh); $cont =~ s,(?://.*)?\n, ,g; $cont =~ s,/\*.*?\*/, ,g; $cont =~ s,\h+, ,g; my @ptypes = map { s,^ ,,r } split(/;/, $cont); pop @ptypes; # last one is empty my @cmd_table; sub make_args($) { $_ = shift; s/(?:^|(?<=, ))(?:const )?\w+ \*?//g; return $_; } sub type_to_format($) { $_ = shift; s/xint /\%\#x/g; s/uint /\%u/g; s/int /\%d/g; s/const char \*/\%s/g; return $_; } sub make_format($) { $_ = type_to_format(shift); s/, (\%\#?.)(\w+)/, $2=$1/g; return $_; } sub indent($$) { my ($str, $indent) = @_; return $str =~ s,^(?=.),$indent,smgr; } open(my $outh, ">".$out_source) or die("Cannot create $out_source: $!\n"); for (@ptypes) { /^([\w* ]+)\(\*(\w+)\)\( (.*) \)$/ or die("Cannot parse prototype '$_'\n"); my ($cmd_type, $cmd_name, $cmd_args) = ($1, $2, $3); if (defined($excluded{$cmd_name})) { push @cmd_table, "NULL"; next; } push @cmd_table, "proxy_$cmd_name"; next if (defined($special{$cmd_name})); my $inc_tpl = ""; my %replace; $replace{'name'} = $cmd_name; $replace{'type'} = $cmd_type; $cmd_args =~ s/^store_t \*ctx// or die("Arguments '$cmd_args' don't start with 'store_t *ctx'\n"); if ($cmd_name =~ /^get_/) { $template = "GETTER"; $replace{'fmt'} = type_to_format($cmd_type); } else { my $pass_args; if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) { my $cmd_cb_args = $1; $replace{'decl_cb_args'} = $cmd_cb_args; $replace{'pass_cb_args'} = make_args($cmd_cb_args); if (length($cmd_cb_args)) { my $r_cmd_cb_args = $cmd_cb_args; $r_cmd_cb_args =~ s/^int sts, // or die("Callback arguments of $cmd_name don't start with sts.\n"); $r_cmd_cb_args =~ s/^(.*), $/, $1/; $replace{'print_pass_cb_args'} = make_args($r_cmd_cb_args); $replace{'print_fmt_cb_args'} = make_format($r_cmd_cb_args); $inc_tpl = 'CALLBACK_STS'; } else { $inc_tpl = 'CALLBACK_VOID'; } $pass_args = make_args($cmd_args); $pass_args =~ s/([^, ]+)/cmd->$1/g; my $r_cmd_args = $cmd_args =~ s/, (.*)$/$1, /r; $replace{'decl_state'} = $r_cmd_args =~ s/, /\;\n/gr; my $r_pass_args = make_args($r_cmd_args); $replace{'assign_state'} = $r_pass_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr; $template = "CALLBACK"; } else { $pass_args = make_args($cmd_args); if ($cmd_type eq "void ") { $template = "REGULAR_VOID"; } else { $template = "REGULAR"; $replace{'print_fmt_ret'} = type_to_format($cmd_type); $replace{'print_pass_ret'} = "rv"; } } $replace{'decl_args'} = $cmd_args; $replace{'print_pass_args'} = $replace{'pass_args'} = $pass_args; $replace{'print_fmt_args'} = make_format($cmd_args); } my ($fake_cond, $fake_invoke, $fake_cb_args, $post_invoke) = (undef, "", "", ""); for (keys %defines) { next if (!/^${cmd_name}_(.*)$/); my ($key, $val) = ($1, delete $defines{$_}); if ($key eq 'counted') { $replace{count_step} = "countStep();\n"; } elsif ($key eq 'fakeable') { $fake_cond = "ctx->is_fake"; $replace{print_pass_dry} = ', '.$fake_cond.' ? " [FAKE]" : ""'; } elsif ($key eq 'driable') { $fake_cond = "DFlags & DRYRUN"; $replace{print_pass_dry} = ', ('.$fake_cond.') ? " [DRY]" : ""'; } elsif ($key eq 'fake_invoke') { $fake_invoke = $val; } elsif ($key eq 'fake_cb_args') { $fake_cb_args = $val; } elsif ($key eq 'post_real_invoke') { $post_invoke = $val; } else { $replace{$key} = $val; } } if (defined($fake_cond)) { $replace{print_fmt_dry} = '%s'; if ($inc_tpl eq 'CALLBACK_STS') { $fake_invoke .= "proxy_${cmd_name}_cb( DRV_OK${fake_cb_args}, cmd );\n"; } elsif (length($fake_cb_args)) { die("Unexpected fake callback arguments to $cmd_name\n"); } my $num_fake = $fake_invoke =~ s/^(?=.)/\t/gsm; my $num_real = $post_invoke =~ s/^(?=.)/\t/gsm; my $pre_invoke = "if (".$fake_cond.")"; if ($num_fake > 1 || $num_real) { $pre_invoke .= " {"; $fake_invoke .= "} else {\n"; $post_invoke .= "}\n"; } else { $fake_invoke .= "else\n"; } $replace{pre_invoke} = $pre_invoke."\n".$fake_invoke; $replace{indent_invoke} = "\t"; $replace{post_invoke} = $post_invoke; } my %used; my $text = $templates{$template}; if ($inc_tpl) { if ($inc_tpl eq 'CALLBACK_STS') { if ($replace{print_fmt_cb_args}) { $inc_tpl .= '_FMT'; } else { if ($replace{print_cb_args}) { $inc_tpl .= '_PRN'; } # These may be defined but empty; that's not an error. delete $replace{print_fmt_cb_args}; delete $replace{print_pass_cb_args}; } } $text =~ s/^\t\@print_cb_args_tpl\@\n/$templates{$inc_tpl}/sm; } $text =~ s/^(\h*)\@(\w+)\@\n/$used{$2} = 1; indent($replace{$2} \/\/ "", $1)/smeg; $text =~ s/\@(\w+)\@/$used{$1} = 1; $replace{$1} \/\/ ""/eg; print $outh $text."\n"; my @not_used = grep { !defined($used{$_}) } keys %replace; die("Fatal: unconsumed replacements in $cmd_name: ".join(" ", @not_used)."\n") if (@not_used); } die("Fatal: unconsumed DEFINEs: ".join(" ", keys %defines)."\n") if (%defines); print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n"; close $outh; isync-1.5.1/src/socket.c0000644000175000001440000007563014720613750010563 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-FileCopyrightText: 2004 Theodore Y. Ts'o // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "socket.h" #include #include #include #include #include #include #include #include #ifdef HAVE_LIBSSL # include # include # include # if OPENSSL_VERSION_NUMBER < 0x10100000L \ || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070100fL) # define X509_OBJECT_get0_X509(o) ((o)->data.x509) # define X509_STORE_get0_objects(o) ((o)->objs) # endif #endif enum { SCK_RESOLVING, SCK_CONNECTING, #ifdef HAVE_LIBSSL SCK_STARTTLS, #endif SCK_READY, SCK_EOF }; static void socket_fail( conn_t *conn ) { conn->bad_callback( conn->callback_aux ); } #ifdef HAVE_LIBSSL static void ATTR_PRINTFLIKE(1, 2) print_ssl_errors( const char *fmt, ... ) { char *action; va_list va; ulong err; va_start( va, fmt ); nfvasprintf( &action, fmt, va ); va_end( va ); while ((err = ERR_get_error())) error( "Error while %s: %s\n", action, ERR_error_string( err, NULL ) ); free( action ); } static int print_ssl_socket_errors( const char *func, conn_t *conn ) { ulong err; int num = 0; while ((err = ERR_get_error())) { error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, NULL ) ); num++; } return num; } static int ssl_return( const char *func, conn_t *conn, int ret ) { int err; switch ((err = SSL_get_error( conn->ssl, ret ))) { case SSL_ERROR_NONE: return ret; case SSL_ERROR_WANT_WRITE: conf_notifier( &conn->notify, POLLIN, POLLOUT ); FALLTHROUGH case SSL_ERROR_WANT_READ: return 0; case SSL_ERROR_SSL: print_ssl_socket_errors( func, conn ); break; case SSL_ERROR_SYSCALL: if (print_ssl_socket_errors( func, conn )) break; if (ret == 0) { case SSL_ERROR_ZERO_RETURN: /* Callers take the short path out, so signal higher layers from here. */ conn->state = SCK_EOF; conn->read_callback( conn->callback_aux ); return -1; } sys_error( "Socket error: secure %s %s", func, conn->name ); break; default: error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err ); break; } if (conn->state == SCK_STARTTLS) conn->callbacks.starttls( 0, conn->callback_aux ); else socket_fail( conn ); return -1; } /* Some of this code is inspired by / lifted from mutt. */ static int host_matches( const char *host, const char *pattern ) { if (pattern[0] == '*' && pattern[1] == '.') { pattern += 2; if (!(host = strchr( host, '.' ))) return 0; host++; } return *host && *pattern && !strcasecmp( host, pattern ); } static int verify_hostname( X509 *cert, const char *hostname ) { int i, len, found; X509_NAME *subj; STACK_OF(GENERAL_NAME) *subj_alt_names; char cname[1000]; /* try the DNS subjectAltNames */ found = 0; if ((subj_alt_names = X509_get_ext_d2i( cert, NID_subject_alt_name, NULL, NULL ))) { int num_subj_alt_names = sk_GENERAL_NAME_num( subj_alt_names ); for (i = 0; i < num_subj_alt_names; i++) { GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value( subj_alt_names, i ); if (subj_alt_name->type == GEN_DNS && strlen( (const char *)subj_alt_name->d.ia5->data ) == (size_t)subj_alt_name->d.ia5->length && host_matches( hostname, (const char *)(subj_alt_name->d.ia5->data) )) { found = 1; break; } } sk_GENERAL_NAME_pop_free( subj_alt_names, GENERAL_NAME_free ); } if (found) return 0; /* try the common name */ if (!(subj = X509_get_subject_name( cert ))) { error( "Error, cannot get certificate subject\n" ); return -1; } if ((len = X509_NAME_get_text_by_NID( subj, NID_commonName, cname, sizeof(cname) )) < 0) { error( "Error, cannot get certificate common name\n" ); return -1; } if (strlen( cname ) == (size_t)len && host_matches( hostname, cname )) return 0; error( "Error, certificate owner does not match hostname %s\n", hostname ); return -1; } static int verify_cert_host( const server_conf_t *conf, conn_t *sock ) { int i; long err; X509 *cert; cert = SSL_get_peer_certificate( sock->ssl ); if (!cert) { error( "Error, no server certificate\n" ); return -1; } for (i = 0; i < sk_X509_num( sock->conf->trusted_certs ); i++) { if (!X509_cmp( cert, sk_X509_value( sock->conf->trusted_certs, i ) )) { X509_free( cert ); return 0; } } err = SSL_get_verify_result( sock->ssl ); if (err != X509_V_OK) { error( "SSL error connecting %s: %s\n", sock->name, X509_verify_cert_error_string( err ) ); X509_free( cert ); return -1; } if (!conf->host) { error( "SSL error connecting %s: Neither host nor matching certificate specified\n", sock->name ); X509_free( cert ); return -1; } int ret = verify_hostname( cert, conf->host ); X509_free( cert ); return ret; } static int init_ssl_ctx( const server_conf_t *conf ) { DIAG_PUSH DIAG_DISABLE("-Wcast-qual") // C has no 'mutable' or const_cast<> ... server_conf_t *mconf = (server_conf_t *)conf; DIAG_POP if (conf->SSLContext) return conf->ssl_ctx_valid; #if OPENSSL_VERSION_NUMBER >= 0x10100000L const SSL_METHOD *method = TLS_client_method(); #else const SSL_METHOD *method = SSLv23_client_method(); #endif if (!(mconf->SSLContext = SSL_CTX_new( method ))) { print_ssl_errors( "initializing SSL context" ); return 0; } uint options = SSL_OP_NO_SSLv3; if (!(conf->ssl_versions & TLSv1)) options |= SSL_OP_NO_TLSv1; #ifdef SSL_OP_NO_TLSv1_1 if (!(conf->ssl_versions & TLSv1_1)) options |= SSL_OP_NO_TLSv1_1; #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(conf->ssl_versions & TLSv1_2)) options |= SSL_OP_NO_TLSv1_2; #endif #ifdef SSL_OP_NO_TLSv1_3 if (!(conf->ssl_versions & TLSv1_3)) options |= SSL_OP_NO_TLSv1_3; #endif SSL_CTX_set_options( mconf->SSLContext, options ); if (conf->cipher_string && !SSL_CTX_set_cipher_list( mconf->SSLContext, conf->cipher_string )) { print_ssl_errors( "setting cipher string '%s'", conf->cipher_string ); return 0; } if (!(mconf->trusted_certs = sk_X509_new_null())) oom(); if (conf->cert_file) { X509_STORE *store; if (!(store = X509_STORE_new())) oom(); if (!X509_STORE_load_locations( store, conf->cert_file, NULL )) { print_ssl_errors( "loading certificate file '%s'", conf->cert_file ); return 0; } STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects( store ); for (int i = 0; i < sk_X509_OBJECT_num( objs ); i++) { X509 *cert = X509_OBJECT_get0_X509( sk_X509_OBJECT_value( objs, i ) ); if (cert) { if (X509_check_ca( cert )) { if (!X509_STORE_add_cert( SSL_CTX_get_cert_store( mconf->SSLContext ), cert )) oom(); } else { X509_up_ref( cert ); // Locking failure assumed impossible if (!sk_X509_push( mconf->trusted_certs, cert )) oom(); } } } X509_STORE_free( store ); } if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) { ulong err; while ((err = ERR_get_error())) warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, NULL ) ); } SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL ); if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) { print_ssl_errors( "loading client certificate file '%s'", conf->client_certfile ); return 0; } if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) { print_ssl_errors( "loading client private key '%s'", conf->client_keyfile ); return 0; } mconf->ssl_ctx_valid = 1; return 1; } static void start_tls_p2( conn_t * ); static void start_tls_p3( conn_t *, int ); static void ssl_fake_cb( void * ); void socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) ) { static int ssl_inited; conn->callbacks.starttls = cb; conn->state = SCK_STARTTLS; if (!ssl_inited) { SSL_library_init(); SSL_load_error_strings(); ssl_inited = 1; } if (!init_ssl_ctx( conn->conf )) { start_tls_p3( conn, 0 ); return; } init_wakeup( &conn->ssl_fake, ssl_fake_cb, conn ); if (!(conn->ssl = SSL_new( ((server_conf_t const *)conn->conf)->SSLContext ))) { print_ssl_errors( "initializing SSL connection" ); start_tls_p3( conn, 0 ); return; } if (!SSL_set_tlsext_host_name( conn->ssl, conn->conf->host )) { print_ssl_errors( "setting SSL server host name" ); start_tls_p3( conn, 0 ); return; } if (!SSL_set_fd( conn->ssl, conn->fd )) { print_ssl_errors( "setting SSL socket fd" ); start_tls_p3( conn, 0 ); return; } SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER ); socket_expect_activity( conn, 1 ); start_tls_p2( conn ); } static void start_tls_p2( conn_t *conn ) { if (ssl_return( "connect to", conn, SSL_connect( conn->ssl ) ) > 0) { if (verify_cert_host( conn->conf, conn )) { start_tls_p3( conn, 0 ); } else { info( "Connection is now encrypted\n" ); start_tls_p3( conn, 1 ); } } } static void start_tls_p3( conn_t *conn, int ok ) { socket_expect_activity( conn, 0 ); conn->state = SCK_READY; conn->callbacks.starttls( ok, conn->callback_aux ); } #endif /* HAVE_LIBSSL */ #ifdef HAVE_LIBZ static void z_fake_cb( void * ); static const char * z_err_msg( int code, z_streamp strm ) { /* zlib's consistency in populating z_stream->msg is somewhat * less than stellar. zError() is undocumented. */ return strm->msg ? strm->msg : zError( code ); } void socket_start_deflate( conn_t *conn ) { int result; conn->in_z = nfzalloc( sizeof(*conn->in_z) ); result = inflateInit2( conn->in_z, -15 /* Use raw deflate */ ); if (result != Z_OK) { error( "Fatal: Cannot initialize decompression: %s\n", z_err_msg( result, conn->in_z ) ); abort(); } conn->out_z = nfzalloc( sizeof(*conn->out_z) ); result = deflateInit2( conn->out_z, Z_DEFAULT_COMPRESSION, /* Compression level */ Z_DEFLATED, /* Only valid value */ -15, /* Use raw deflate */ 8, /* Default memory usage */ Z_DEFAULT_STRATEGY /* Don't try to do anything fancy */ ); if (result != Z_OK) { error( "Fatal: Cannot initialize compression: %s\n", z_err_msg( result, conn->out_z ) ); abort(); } init_wakeup( &conn->z_fake, z_fake_cb, conn ); conn->readsz = 0; // This optimization makes no sense past this point } #endif /* HAVE_LIBZ */ static void socket_fd_cb( int, void * ); static void socket_fake_cb( void * ); static void socket_timeout_cb( void * ); static void socket_resolve( conn_t * ); static void socket_connect_one( conn_t * ); static void socket_connect_next( conn_t * ); static void socket_connect_failed( conn_t * ); static void socket_connected( conn_t * ); static void socket_connect_bail( conn_t * ); static void socket_register_internal( conn_t *sock, int fd ) { sock->fd = fd; init_notifier( &sock->notify, fd, socket_fd_cb, sock ); init_wakeup( &sock->fd_fake, socket_fake_cb, sock ); init_wakeup( &sock->fd_timeout, socket_timeout_cb, sock ); } static void socket_open_internal( conn_t *sock, int fd ) { fcntl( fd, F_SETFL, O_NONBLOCK ); socket_register_internal( sock, fd ); } static void socket_close_internal( conn_t *sock ) { wipe_notifier( &sock->notify ); wipe_wakeup( &sock->fd_fake ); wipe_wakeup( &sock->fd_timeout ); close( sock->fd ); sock->fd = -1; } void socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) ) { const server_conf_t *conf = sock->conf; sock->callbacks.connect = cb; /* open connection to server */ if (conf->tunnel) { int a[2]; nfasprintf( &sock->name, "tunnel '%s'", conf->tunnel ); infon( "Starting %s... ", sock->name ); if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) { perror( "socketpair" ); exit( 1 ); } if (fork() == 0) { if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1) _exit( 127 ); close( a[0] ); close( a[1] ); execl( "/bin/sh", "sh", "-c", conf->tunnel, (char *)0 ); _exit( 127 ); } close( a[0] ); socket_open_internal( sock, a[1] ); info( "\vok\n" ); socket_connected( sock ); } else { socket_resolve( sock ); } } static void pipe_write( int fd, void *buf, int len ) { do { int wrote = write( fd, buf, len ); if (wrote < 0) { perror( "write" ); _exit( 1 ); } buf = ((char *)buf) + wrote; len -= wrote; } while (len); } static void socket_resolve( conn_t *sock ) { info( "Resolving %s...\n", sock->conf->host ); int pfd[2]; if (pipe( pfd )) { perror( "pipe" ); exit( 1 ); } switch (fork()) { case -1: perror( "fork" ); exit( 1 ); case 0: break; default: close( pfd[1] ); socket_register_internal( sock, pfd[0] ); sock->state = SCK_RESOLVING; conf_notifier( &sock->notify, 0, POLLIN ); socket_expect_activity( sock, 1 ); return; } #ifdef HAVE_IPV6 struct addrinfo *res, hints = { 0 }; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; int gaierr = getaddrinfo( sock->conf->host, NULL, &hints, &res ); pipe_write( pfd[1], &gaierr, sizeof(gaierr) ); if (gaierr) _exit( 1 ); static_assert( sizeof(((struct addrinfo){ 0 }).ai_family) == sizeof(int), "unexpected size of ai_family" ); static_assert( sizeof(struct in_addr) % sizeof(int) == 0, "unexpected size of struct in_addr" ); static_assert( sizeof(struct in6_addr) % sizeof(int) == 0, "unexpected size of struct in6_addr" ); int nbytes = 0; for (struct addrinfo *cres = res; cres; cres = cres->ai_next) { if (cres->ai_family == AF_INET) { nbytes += sizeof(int) + sizeof(struct in_addr); } else { assert( cres->ai_family == AF_INET6 ); nbytes += sizeof(int) + sizeof(struct in6_addr); } } pipe_write( pfd[1], &nbytes, sizeof(nbytes) ); for (struct addrinfo *cres = res; cres; cres = cres->ai_next) { pipe_write( pfd[1], &cres->ai_family, sizeof(int) ); if (cres->ai_family == AF_INET) pipe_write( pfd[1], &((struct sockaddr_in *)cres->ai_addr)->sin_addr, sizeof(struct in_addr) ); else pipe_write( pfd[1], &((struct sockaddr_in6 *)cres->ai_addr)->sin6_addr, sizeof(struct in6_addr) ); } #else struct hostent *he = gethostbyname( sock->conf->host ); int herrno = he ? 0 : h_errno; pipe_write( pfd[1], &herrno, sizeof(herrno) ); if (!he) _exit( 1 ); static_assert( sizeof(struct in_addr) % sizeof(int) == 0, "unexpected size of struct in_addr" ); int nbytes = 0; for (char **addr = he->h_addr_list; *addr; addr++) nbytes += sizeof(struct in_addr); pipe_write( pfd[1], &nbytes, sizeof(nbytes) ); for (char **addr = he->h_addr_list; *addr; addr++) pipe_write( pfd[1], *addr, sizeof(struct in_addr) ); #endif _exit( 0 ); } static void pipe_read( int fd, void *buf, int len ) { do { int didrd = read( fd, buf, len ); if (didrd < 0) { sys_error( "read" ); exit( 1 ); } if (!didrd) { error( "read: unexpected EOF\n" ); exit( 1 ); } buf = ((char *)buf) + didrd; len -= didrd; } while (len); } static void socket_resolve_finalize( conn_t *sock ) { int errcode; pipe_read( sock->fd, &errcode, sizeof(errcode) ); if (errcode) { #ifdef HAVE_IPV6 const char *err = gai_strerror( errcode ); #else const char *err = hstrerror( errcode ); #endif error( "Error: Cannot resolve server '%s': %s\n", sock->conf->host, err ); socket_close_internal( sock ); socket_connect_bail( sock ); return; } int nbytes; pipe_read( sock->fd, &nbytes, sizeof(nbytes) ); char *addrs = nfmalloc( nbytes ); pipe_read( sock->fd, addrs, nbytes ); sock->curr_addr = sock->addrs = addrs; sock->addrs_end = addrs + nbytes; socket_close_internal( sock ); // Get rid of the pipe socket_connect_one( sock ); } static void socket_resolve_timeout( conn_t *sock ) { error( "Error: Cannot resolve server '%s': timeout.\n", sock->conf->host ); socket_close_internal( sock ); socket_connect_bail( sock ); } static void socket_connect_one( conn_t *sock ) { char *ai = sock->curr_addr; if (ai == sock->addrs_end) { error( "No working address found for %s\n", sock->conf->host ); socket_connect_bail( sock ); return; } union { struct sockaddr any; struct sockaddr_in ip4; #ifdef HAVE_IPV6 struct sockaddr_in6 ip6; #endif } addr; #ifdef HAVE_IPV6 int fam = *(int *)ai; ai += sizeof(int); int addr_len; if (fam == AF_INET6) { addr_len = sizeof(addr.ip6); addr.ip6.sin6_addr = *(struct in6_addr *)ai; addr.ip6.sin6_flowinfo = 0; addr.ip6.sin6_scope_id = 0; ai += sizeof(struct in6_addr); } else { addr_len = sizeof(addr.ip4); #else const int fam = AF_INET; const int addr_len = sizeof(addr.ip4); { #endif addr.ip4.sin_addr = *(struct in_addr *)ai; ai += sizeof(struct in_addr); } sock->curr_addr = ai; #ifdef HAVE_IPV6 if (fam == AF_INET6) { char sockname[64]; inet_ntop( fam, &addr.ip6.sin6_addr, sockname, sizeof(sockname) ); nfasprintf( &sock->name, "%s ([%s]:%hu)", sock->conf->host, sockname, sock->conf->port ); } else #endif { nfasprintf( &sock->name, "%s (%s:%hu)", sock->conf->host, inet_ntoa( addr.ip4.sin_addr ), sock->conf->port ); } int s = socket( fam, SOCK_STREAM, 0 ); if (s < 0) { socket_connect_next( sock ); return; } socket_open_internal( sock, s ); infon( "Connecting to %s... ", sock->name ); addr.any.sa_family = fam; addr.ip4.sin_port = htons( sock->conf->port ); // Aliased for ip6 if (connect( s, &addr.any, addr_len )) { if (errno != EINPROGRESS) { socket_connect_failed( sock ); return; } conf_notifier( &sock->notify, 0, POLLOUT ); socket_expect_activity( sock, 1 ); sock->state = SCK_CONNECTING; info( "\v\n" ); return; } info( "\vok\n" ); socket_connected( sock ); } static void socket_connect_next( conn_t *conn ) { sys_error( "Cannot connect to %s", conn->name ); free( conn->name ); conn->name = NULL; socket_connect_one( conn ); } static void socket_connect_failed( conn_t *conn ) { socket_close_internal( conn ); socket_connect_next( conn ); } static void socket_connected( conn_t *conn ) { free( conn->addrs ); conn->addrs = NULL; conf_notifier( &conn->notify, 0, POLLIN ); socket_expect_activity( conn, 0 ); conn->state = SCK_READY; conn->callbacks.connect( 1, conn->callback_aux ); } static void socket_cleanup_names( conn_t *conn ) { free( conn->addrs ); conn->addrs = NULL; free( conn->name ); conn->name = NULL; } static void socket_connect_bail( conn_t *conn ) { socket_cleanup_names( conn ); conn->callbacks.connect( 0, conn->callback_aux ); } static void dispose_chunk( conn_t *conn ); void socket_close( conn_t *sock ) { if (sock->fd >= 0) socket_close_internal( sock ); socket_cleanup_names( sock ); #ifdef HAVE_LIBSSL if (sock->ssl) { SSL_free( sock->ssl ); sock->ssl = NULL; wipe_wakeup( &sock->ssl_fake ); } #endif #ifdef HAVE_LIBZ if (sock->in_z) { inflateEnd( sock->in_z ); free( sock->in_z ); sock->in_z = NULL; deflateEnd( sock->out_z ); free( sock->out_z ); sock->out_z = NULL; wipe_wakeup( &sock->z_fake ); } #endif while (sock->write_buf) dispose_chunk( sock ); free( sock->append_buf ); sock->append_buf = NULL; } static int prepare_read( conn_t *sock, char **buf, uint *len ) { uint n = sock->offset + sock->bytes; if (!(*len = sizeof(sock->buf) - n)) { error( "Socket error: receive buffer full. Probably protocol error.\n" ); socket_fail( sock ); return -1; } *buf = sock->buf + n; return 0; } static int do_read( conn_t *sock, char *buf, uint len ) { int n; assert( sock->fd >= 0 ); #ifdef HAVE_LIBSSL if (sock->ssl) { if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, (int)len ) )) <= 0) return n; if (n == (int)len && SSL_pending( sock->ssl )) conf_wakeup( &sock->ssl_fake, 0 ); } else #endif { if ((n = read( sock->fd, buf, len )) < 0) { sys_error( "Socket error: read from %s", sock->name ); socket_fail( sock ); } else if (!n) { /* EOF. Callers take the short path out, so signal higher layers from here. */ sock->state = SCK_EOF; sock->read_callback( sock->callback_aux ); } } return n; } static void socket_filled( conn_t *conn, uint len ) { uint off = conn->offset; uint cnt = conn->bytes + len; conn->bytes = cnt; if (conn->wanted) { // Fulfill as much of the request as still fits into the buffer, // but avoid chopping up the actual socket reads if (cnt < conn->wanted && off + cnt < sizeof(conn->buf) - conn->readsz) return; } else { // Need a full line char *s = conn->buf + off; char *p = memchr( s + conn->scanoff, '\n', cnt - conn->scanoff ); if (!p) { conn->scanoff = cnt; if (off && off + cnt >= sizeof(conn->buf) - conn->readsz) { memmove( conn->buf, conn->buf + off, cnt ); conn->offset = 0; } return; } conn->scanoff = (uint)(p - s); } conn->read_callback( conn->callback_aux ); } #ifdef HAVE_LIBZ static void socket_fill_z( conn_t *sock ) { char *buf; uint len; int ret; if (prepare_read( sock, &buf, &len ) < 0) return; sock->in_z->avail_out = len; sock->in_z->next_out = (unsigned char *)buf; ret = inflate( sock->in_z, Z_SYNC_FLUSH ); /* Z_BUF_ERROR happens here when the previous call both consumed * all input and exactly filled up the output buffer. */ if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END) { error( "Error decompressing data from %s: %s\n", sock->name, z_err_msg( ret, sock->in_z ) ); socket_fail( sock ); return; } if (!sock->in_z->avail_out) conf_wakeup( &sock->z_fake, 0 ); if ((len = (uint)((char *)sock->in_z->next_out - buf))) socket_filled( sock, len ); } #endif static void socket_fill( conn_t *sock ) { #ifdef HAVE_LIBZ if (sock->in_z) { int ret; /* The timer will preempt reads until the buffer is empty. */ assert( !sock->in_z->avail_in ); sock->in_z->next_in = (uchar *)sock->z_buf; if ((ret = do_read( sock, sock->z_buf, sizeof(sock->z_buf) )) <= 0) return; sock->in_z->avail_in = (uint)ret; socket_fill_z( sock ); } else #endif { char *buf; uint len; if (prepare_read( sock, &buf, &len ) < 0) return; int n; if ((n = do_read( sock, buf, len )) <= 0) return; // IIR filter for tracking average size of bulk reads. // We use this to optimize the free space at the end of the // buffer, hence the factor of 1.5. if (n >= MIN_BULK_READ) { sock->readsz = (sock->readsz * 3 + n * 3 / 2) / 4; if (sock->readsz > sizeof(sock->buf)) sock->readsz = sizeof(sock->buf); } socket_filled( sock, (uint)n ); } } void socket_expect_activity( conn_t *conn, int expect ) { if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout )) conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 ); } void socket_expect_eof( conn_t *sock ) { #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF // implies HAVE_LIBSSL if (sock->ssl) SSL_set_options( sock->ssl, SSL_OP_IGNORE_UNEXPECTED_EOF ); #endif } void socket_expect_bytes( conn_t *conn, uint len ) { conn->wanted = len; uint off = conn->offset; if (off) { uint cnt = conn->bytes; if (off + len > sizeof(conn->buf) || off + cnt >= sizeof(conn->buf) - conn->readsz) { memmove( conn->buf, conn->buf + off, cnt ); conn->offset = 0; } } } char * socket_read( conn_t *conn, uint min_len, uint max_len, uint *out_len ) { assert( min_len <= sizeof(conn->buf) ); assert( min_len <= max_len ); uint off = conn->offset; uint cnt = conn->bytes; if (cnt < min_len) { if (conn->state == SCK_EOF) return (void *)~0; return NULL; } uint n = (cnt < max_len) ? cnt : max_len; cnt -= n; conn->offset = cnt ? off + n : 0; conn->bytes = cnt; *out_len = n; return conn->buf + off; } char * socket_read_line( conn_t *conn ) { uint off = conn->offset; uint cnt = conn->bytes; char *s = conn->buf + off; char *p = memchr( s + conn->scanoff, '\n', cnt - conn->scanoff ); if (!p) { if (conn->state == SCK_EOF) return (void *)~0; conn->scanoff = cnt; return NULL; } uint n = (uint)(p + 1 - s); cnt -= n; conn->offset = cnt ? off + n : 0; conn->bytes = cnt; conn->scanoff = 0; if (p != s && p[-1] == '\r') p--; *p = 0; return s; } static int do_write( conn_t *sock, char *buf, uint len ) { int n; assert( sock->fd >= 0 ); #ifdef HAVE_LIBSSL if (sock->ssl) return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, (int)len ) ); #endif n = write( sock->fd, buf, len ); if (n < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { sys_error( "Socket error: write to %s", sock->name ); socket_fail( sock ); } else { n = 0; conf_notifier( &sock->notify, POLLIN, POLLOUT ); } } else if (n != (int)len) { conf_notifier( &sock->notify, POLLIN, POLLOUT ); } return n; } static void dispose_chunk( conn_t *conn ) { buff_chunk_t *bc = conn->write_buf; if (!(conn->write_buf = bc->next)) conn->write_buf_append = &conn->write_buf; conn->buffer_mem -= bc->len; free( bc ); } static int do_queued_write( conn_t *conn ) { buff_chunk_t *bc; if (!conn->write_buf) return 0; while ((bc = conn->write_buf)) { int n; uint len = bc->len - conn->write_offset; if ((n = do_write( conn, bc->data + conn->write_offset, len )) < 0) return -1; if (n != (int)len) { conn->write_offset += (uint)n; return 0; } conn->write_offset = 0; dispose_chunk( conn ); } #ifdef HAVE_LIBSSL if (conn->ssl && SSL_pending( conn->ssl )) conf_wakeup( &conn->ssl_fake, 0 ); #endif conn->write_callback( conn->callback_aux ); return -1; } static void do_append( conn_t *conn, buff_chunk_t *bc ) { bc->next = NULL; conn->buffer_mem += bc->len; *conn->write_buf_append = bc; conn->write_buf_append = &bc->next; } /* This is big enough to avoid excessive chunking, but is * sufficiently small to keep SSL latency low with a slow uplink. */ #define WRITE_CHUNK_SIZE 1024 static void do_flush( conn_t *conn ) { buff_chunk_t *bc = conn->append_buf; #ifdef HAVE_LIBZ if (conn->out_z) { uint buf_avail = conn->append_avail; if (!conn->z_written) return; do { int ret; if (!bc) { buf_avail = WRITE_CHUNK_SIZE; bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail ); bc->len = 0; } conn->out_z->next_in = Z_NULL; conn->out_z->avail_in = 0; conn->out_z->next_out = (uchar *)bc->data + bc->len; conn->out_z->avail_out = buf_avail; /* Z_BUF_ERROR cannot happen here, as zlib suppresses the error * both upon increasing the flush level (1st iteration) and upon * a no-op after the output buffer was full (later iterations). */ if ((ret = deflate( conn->out_z, Z_PARTIAL_FLUSH )) != Z_OK) { error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) ); abort(); } bc->len = (uint)((char *)conn->out_z->next_out - bc->data); if (bc->len) { do_append( conn, bc ); bc = NULL; buf_avail = 0; } else { buf_avail = conn->out_z->avail_out; } } while (!conn->out_z->avail_out); conn->append_buf = bc; conn->append_avail = buf_avail; conn->z_written = 0; } else #endif if (bc) { do_append( conn, bc ); conn->append_buf = NULL; #ifdef HAVE_LIBZ conn->append_avail = 0; #endif } } void socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) { int i; uint buf_avail, len, offset = 0, total = 0; buff_chunk_t *bc; for (i = 0; i < iovcnt; i++) total += iov[i].len; if (total >= WRITE_CHUNK_SIZE) { /* If the new data is too big, queue the pending buffer to avoid latency. */ do_flush( conn ); } bc = conn->append_buf; #ifdef HAVE_LIBZ buf_avail = conn->append_avail; #endif while (total) { if (!bc) { /* We don't do anything special when compressing, as there is no way to * predict a reasonable output buffer size anyway - deflatePending() does * not account for consumed but not yet compressed input, and adding up * the deflateBound()s would be a tad *too* pessimistic. */ buf_avail = total > WRITE_CHUNK_SIZE ? total : WRITE_CHUNK_SIZE; bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail ); bc->len = 0; #ifndef HAVE_LIBZ } else { /* A pending buffer will always be of standard size - over-sized * buffers are immediately filled and queued. */ buf_avail = WRITE_CHUNK_SIZE - bc->len; #endif } while (total) { len = iov->len - offset; #ifdef HAVE_LIBZ if (conn->out_z) { int ret; conn->out_z->next_in = (uchar *)iov->buf + offset; conn->out_z->avail_in = len; conn->out_z->next_out = (uchar *)bc->data + bc->len; conn->out_z->avail_out = buf_avail; /* Z_BUF_ERROR is impossible here, as the input buffer always has data, * and the output buffer always has space. */ if ((ret = deflate( conn->out_z, Z_NO_FLUSH )) != Z_OK) { error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) ); abort(); } bc->len = (uint)((char *)conn->out_z->next_out - bc->data); buf_avail = conn->out_z->avail_out; len -= conn->out_z->avail_in; conn->z_written = 1; } else #endif { if (len > buf_avail) len = buf_avail; memcpy( bc->data + bc->len, iov->buf + offset, len ); bc->len += len; buf_avail -= len; } offset += len; total -= len; if (offset == iov->len) { if (iov->takeOwn == GiveOwn) free( iov->buf ); iov++; offset = 0; } if (!buf_avail) { do_append( conn, bc ); bc = NULL; break; } } } conn->append_buf = bc; #ifdef HAVE_LIBZ conn->append_avail = buf_avail; #endif conf_wakeup( &conn->fd_fake, 0 ); } static void socket_fd_cb( int events, void *aux ) { conn_t *conn = (conn_t *)aux; if (conn->state == SCK_RESOLVING) { socket_resolve_finalize( conn ); return; } if ((events & POLLERR) || conn->state == SCK_CONNECTING) { int soerr; socklen_t selen = sizeof(soerr); if (getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, &soerr, &selen )) { perror( "getsockopt" ); exit( 1 ); } errno = soerr; if (conn->state == SCK_CONNECTING) { if (errno) socket_connect_failed( conn ); else socket_connected( conn ); return; } sys_error( "Socket error from %s", conn->name ); socket_fail( conn ); return; } if (events & POLLOUT) conf_notifier( &conn->notify, POLLIN, 0 ); if (pending_wakeup( &conn->fd_timeout )) conf_wakeup( &conn->fd_timeout, conn->conf->timeout ); #ifdef HAVE_LIBSSL if (conn->ssl) { if (conn->state == SCK_STARTTLS) { start_tls_p2( conn ); return; } if (do_queued_write( conn ) < 0) return; socket_fill( conn ); return; } #endif if ((events & POLLOUT) && do_queued_write( conn ) < 0) return; if (events & POLLIN) socket_fill( conn ); } static void socket_fake_cb( void *aux ) { conn_t *conn = (conn_t *)aux; /* Ensure that a pending write gets queued. */ do_flush( conn ); /* If no writes are ongoing, start writing now. */ if (!(notifier_config( &conn->notify ) & POLLOUT)) do_queued_write( conn ); } static void socket_timeout_cb( void *aux ) { conn_t *conn = (conn_t *)aux; if (conn->state == SCK_RESOLVING) { socket_resolve_timeout( conn ); } else if (conn->state == SCK_CONNECTING) { errno = ETIMEDOUT; socket_connect_failed( conn ); } else { error( "Socket error on %s: timeout.\n", conn->name ); socket_fail( conn ); } } #ifdef HAVE_LIBZ static void z_fake_cb( void *aux ) { conn_t *conn = (conn_t *)aux; socket_fill_z( conn ); } #endif #ifdef HAVE_LIBSSL static void ssl_fake_cb( void *aux ) { conn_t *conn = (conn_t *)aux; socket_fill( conn ); } #endif isync-1.5.1/src/util.c0000644000175000001440000005510114720613750010237 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "common.h" #include #include #include #include #include #include int Verbosity = TERSE; int DFlags; int JLimit, JCount; int UseFSync = 1; int Pid; char Hostname[256]; const char *Home; static int need_nl, need_del; void flushn( void ) { if (need_nl) { putchar( '\n' ); fflush( stdout ); need_nl = 0; } else if (need_del) { static const char delstr[] = " " " "; if (need_del > (int)sizeof(delstr) - 1) need_del = (int)sizeof(delstr) - 1; // We could use ^[[K instead, but we assume a dumb terminal. printf( "\r%.*s\r", need_del, delstr ); fflush( stdout ); need_del = 0; } } static void ATTR_PRINTFLIKE(1, 0) vprint( const char *msg, va_list va ) { vprintf( msg, va ); fflush( stdout ); need_nl = 0; } void print( const char *msg, ... ) { va_list va; va_start( va, msg ); vprint( msg, va ); va_end( va ); } static void ATTR_PRINTFLIKE(1, 0) vprintn( const char *msg, va_list va ) { vprint( msg, va ); need_nl = 1; } void printn( const char *msg, ... ) { va_list va; va_start( va, msg ); vprintn( msg, va ); va_end( va ); } void progress( const char *msg, ... ) { va_list va; va_start( va, msg ); need_del = vprintf( msg, va ) - 1; va_end( va ); fflush( stdout ); } static void ATTR_PRINTFLIKE(1, 0) nvprint( const char *msg, va_list va ) { if (*msg == '\v') msg++; else flushn(); vprint( msg, va ); } void info( const char *msg, ... ) { va_list va; if (Verbosity >= VERBOSE) { va_start( va, msg ); nvprint( msg, va ); va_end( va ); } } void infon( const char *msg, ... ) { va_list va; if (Verbosity >= VERBOSE) { va_start( va, msg ); nvprint( msg, va ); va_end( va ); need_nl = 1; } } void notice( const char *msg, ... ) { va_list va; if (Verbosity >= TERSE) { va_start( va, msg ); nvprint( msg, va ); va_end( va ); } } void warn( const char *msg, ... ) { va_list va; if (Verbosity >= QUIET) { flushn(); va_start( va, msg ); vfprintf( stderr, msg, va ); va_end( va ); } } void error( const char *msg, ... ) { va_list va; flushn(); va_start( va, msg ); vfprintf( stderr, msg, va ); va_end( va ); } void vsys_error( const char *msg, va_list va ) { char buf[1024]; int errno_bak = errno; flushn(); if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf)) oob(); errno = errno_bak; perror( buf ); } void sys_error( const char *msg, ... ) { va_list va; va_start( va, msg ); vsys_error( msg, va ); va_end( va ); } // Minimal printf() replacement with custom format sequence(s): // - %\\s // Print backslash-escaped string literals. Note that this does not // automatically add quotes around the printed string, so it is // possible to concatenate multiple segments. // - %!s // Same as %\\s, but non-ASCII characters are (hex-)escaped as well. // - %!&s // Same as %!s, but linefeeds are also printed verbatim for legibility. // TODO: Trade off segments vs. buffer capacity dynamically. #define QPRINTF_SEGS 16 #ifndef QPRINTF_BUFF # define QPRINTF_BUFF 1000 #endif typedef void (*printf_cb)( const char **segs, uint *segls, int nsegs, uint totlen, void *aux ); static void xvprintf_core( const char *fmt, va_list ap, printf_cb cb, void *cb_aux ) { int nsegs = 0; uint totlen = 0; const char *segs[QPRINTF_SEGS]; uint segls[QPRINTF_SEGS]; char buf[QPRINTF_BUFF]; #define ADD_SEG(p, l) \ do { \ if (nsegs == QPRINTF_SEGS) \ oob(); \ segs[nsegs] = p; \ segls[nsegs++] = l; \ totlen += l; \ } while (0) char *d = buf; char *ed = d + sizeof(buf); const char *s = fmt; for (;;) { char c = *fmt; if (!c || c == '%') { uint l = fmt - s; if (l) ADD_SEG( s, l ); if (!c) break; uint maxlen = UINT_MAX; c = *++fmt; if (c == '.') { c = *++fmt; if (c != '*') { fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr ); abort(); } maxlen = va_arg( ap, uint ); c = *++fmt; } int escaped = 0; if (c == '\\') { escaped = 1; c = *++fmt; } else if (c == '!') { escaped = 2; c = *++fmt; if (c == '&') { escaped = 3; c = *++fmt; } } if (c == 'c') { if (d + 1 > ed) oob(); ADD_SEG( d, 1 ); *d++ = (char)va_arg( ap, int ); } else if (c == 's') { s = va_arg( ap, const char * ); if (escaped) { char *bd = d; for (l = 0; l < maxlen && (c = *s); l++, s++) { if (c == '\\' || c == '"') { if (d >= ed) oob(); *d++ = '\\'; } else if (escaped >= 2 && (c < 32 || c > 126)) { switch (c) { case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\a': c = 'a'; break; case '\b': c = 'b'; break; case '\v': c = 'v'; break; case '\f': c = 'f'; break; case '\n': if (escaped == 2) { c = 'n'; break; } if (d + 2 >= ed) oob(); *d++ = '\\'; *d++ = 'n'; *d++ = c; // Keep the actual line break for legibility. continue; default: d += nfsnprintf( d, ed - d, "\\x%02x", (uchar)c ); continue; } if (d >= ed) oob(); *d++ = '\\'; } if (d >= ed) oob(); *d++ = c; } l = d - bd; if (l) ADD_SEG( bd, l ); } else { l = strnlen( s, maxlen ); if (l) ADD_SEG( s, l ); } } else if (c == 'd') { l = nfsnprintf( d, ed - d, "%d", va_arg( ap, int ) ); ADD_SEG( d, l ); d += l; } else if (c == 'u') { l = nfsnprintf( d, ed - d, "%u", va_arg( ap, uint ) ); ADD_SEG( d, l ); d += l; } else { fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr ); abort(); } s = ++fmt; } else { fmt++; } } cb( segs, segls, nsegs, totlen, cb_aux ); } static void xasprintf_cb( const char **segs, uint *segls, int nsegs, uint totlen, void *aux ) { char *d = nfmalloc( totlen + 1 ); *(char **)aux = d; for (int i = 0; i < nsegs; i++) { memcpy( d, segs[i], segls[i] ); d += segls[i]; } *d = 0; } char * xvasprintf( const char *fmt, va_list ap ) { char *out; xvprintf_core( fmt, ap, xasprintf_cb, &out ); return out; } #ifndef HAVE_FWRITE_UNLOCKED # define flockfile(f) # define funlockfile(f) # define fwrite_unlocked(b, l, n, f) fwrite(b, l, n, f) #endif static void xprintf_cb( const char **segs, uint *segls, int nsegs, uint totlen ATTR_UNUSED, void *aux ATTR_UNUSED ) { flockfile( stdout ); for (int i = 0; i < nsegs; i++) fwrite_unlocked( segs[i], 1, segls[i], stdout ); funlockfile( stdout ); } void xprintf( const char *fmt, ... ) { va_list va; va_start( va, fmt ); xvprintf_core( fmt, va, xprintf_cb, NULL ); va_end( va ); } void vFprintf( FILE *f, const char *msg, va_list va ) { int r; r = vfprintf( f, msg, va ); if (r < 0) { sys_error( "Error: cannot write file" ); exit( 1 ); } } void Fprintf( FILE *f, const char *msg, ... ) { va_list va; va_start( va, msg ); vFprintf( f, msg, va ); va_end( va ); } void Fclose( FILE *f, int safe ) { if ((safe && (fflush( f ) || (UseFSync && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) { sys_error( "Error: cannot close file" ); exit( 1 ); } } void add_string_list_n( string_list_t **list, const char *str, uint len ) { string_list_t *elem; elem = nfmalloc( offsetof(string_list_t, string) + len + 1 ); elem->next = *list; *list = elem; memcpy( elem->string, str, len ); elem->string[len] = 0; } void add_string_list( string_list_t **list, const char *str ) { add_string_list_n( list, str, strlen( str ) ); } void free_string_list( string_list_t *list ) { string_list_t *tlist; for (; list; list = tlist) { tlist = list->next; free( list ); } } #ifndef HAVE_VASPRINTF static int vasprintf( char **strp, const char *fmt, va_list ap ) { int len; char tmp[1024]; if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = malloc( len + 1 ))) return -1; if (len >= (int)sizeof(tmp)) vsprintf( *strp, fmt, ap ); else memcpy( *strp, tmp, (size_t)len + 1 ); return len; } #endif #ifndef HAVE_MEMRCHR void * memrchr( const void *s, int c, size_t n ) { u_char *b = (u_char *)s, *e = b + n; while (--e >= b) if (*e == c) return (void *)e; return 0; } #endif #ifndef HAVE_STRNLEN size_t strnlen( const char *str, size_t maxlen ) { const char *estr = memchr( str, 0, maxlen ); return estr ? (size_t)(estr - str) : maxlen; } #endif void to_upper( char *str, uint len ) { for (uint i = 0; i < len; i++) str[i] = toupper( str[i] ); } int starts_with( const char *str, int strl, const char *cmp, uint cmpl ) { if (strl < 0) strl = strnlen( str, cmpl + 1 ); return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl ); } static int equals_upper_impl( const char *str, const char *cmp, uint cmpl ) { for (uint i = 0; i < cmpl; i++) if (toupper( str[i] ) != cmp[i]) return 0; return 1; } int starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl ) { if (strl < 0) strl = strnlen( str, cmpl + 1 ); if ((uint)strl < cmpl) return 0; return equals_upper_impl( str, cmp, cmpl ); } int equals( const char *str, int strl, const char *cmp, uint cmpl ) { if (strl < 0) strl = strnlen( str, cmpl + 1 ); return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl ); } int equals_upper( const char *str, int strl, const char *cmp, uint cmpl ) { if (strl < 0) strl = strnlen( str, cmpl + 1 ); if ((uint)strl != cmpl) return 0; return equals_upper_impl( str, cmp, cmpl ); } #ifndef HAVE_TIMEGM /* Converts struct tm to time_t, assuming the data in tm is UTC rather than local timezone. mktime is similar but assumes struct tm, also known as the "broken-down" form of time, is in local time zone. timegm uses mktime to make the conversion understanding that an offset will be introduced by the local time assumption. mktime_from_utc then measures the introduced offset by applying gmtime to the initial result and applying mktime to the resulting "broken-down" form. The difference between the two mktime results is the measured offset which is then subtracted from the initial mktime result to yield a calendar time which is the value returned. tm_isdst in struct tm is set to 0 to force mktime to introduce a consistent offset (the non DST offset) since tm and tm+o might be on opposite sides of a DST change. Some implementations of mktime return -1 for the nonexistent localtime hour at the beginning of DST. In this event, use mktime(tm - 1hr) + 3600. Schematically mktime(tm) --> t+o gmtime(t+o) --> tm+o mktime(tm+o) --> t+2o t+o - (t+2o - t+o) = t Contributed by Roger Beeman , with the help of Mark Baushke and the rest of the Gurus at CISCO. Further improved by Roger with assistance from Edward J. Sabol based on input by Jamie Zawinski. */ static time_t my_mktime( struct tm *t ) { time_t tl = mktime( t ); if (tl == -1) { t->tm_hour--; tl = mktime( t ); if (tl != -1) tl += 3600; } return tl; } time_t timegm( struct tm *t ) { time_t tl, tb; struct tm *tg; if ((tl = my_mktime( t )) == -1) return tl; tg = gmtime( &tl ); tg->tm_isdst = 0; if ((tb = my_mktime( tg )) == -1) return tb; return tl - (tb - tl); } #endif void fmt_bits( uint bits, uint num_bits, const char *bit_str, const int *bit_off, char *buf ) { uint d = 0; for (uint i = 0, val = 1; i < num_bits; i++, val <<= 1) { if (bits & val) { if (d) buf[d++] = ','; for (const char *s = bit_str + bit_off[i]; *s; s++) buf[d++] = *s; } } buf[d] = 0; } void oob( void ) { fputs( "Fatal: buffer too small. Please report a bug.\n", stderr ); abort(); } int nfsnprintf( char *buf, int blen, const char *fmt, ... ) { int ret; va_list va; va_start( va, fmt ); if (blen <= 0 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen) oob(); va_end( va ); return ret; } void oom( void ) { fputs( "Fatal: Out of memory\n", stderr ); abort(); } void * nfmalloc( size_t sz ) { void *ret; #ifndef __GLIBC__ // The C standard allows NULL returns for zero-sized allocations. if (!sz) sz = 1; #endif if (!(ret = malloc( sz ))) oom(); return ret; } void * nfzalloc( size_t sz ) { void *ret; if (!(ret = calloc( sz, 1 ))) oom(); return ret; } void * nfrealloc( void *mem, size_t sz ) { char *ret; if (!(ret = realloc( mem, sz )) && sz) oom(); return ret; } char * nfstrndup( const char *str, size_t nchars ) { char *ret = nfmalloc( nchars + 1 ); memcpy( ret, str, nchars ); ret[nchars] = 0; return ret; } char * nfstrdup( const char *str ) { return nfstrndup( str, strlen( str ) ); } int nfvasprintf( char **str, const char *fmt, va_list va ) { int ret = vasprintf( str, fmt, va ); if (ret < 0) oom(); return ret; } int nfasprintf( char **str, const char *fmt, ... ) { int ret; va_list va; va_start( va, fmt ); ret = nfvasprintf( str, fmt, va ); va_end( va ); return ret; } /* static struct passwd * cur_user( void ) { char *p; struct passwd *pw; uid_t uid; uid = getuid(); if ((!(p = getenv("LOGNAME")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) && (!(p = getenv("USER")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) && !(pw = getpwuid( uid ))) { fputs ("Cannot determinate current user\n", stderr); return 0; } return pw; } */ /* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */ int map_name( const char *arg, int l, char **result, uint reserve, const char *in, const char *out ) { char *p; int i, ll, num, inl, outl; assert( arg ); if (l < 0) l = strlen( arg ); assert( in ); inl = strlen( in ); if (!inl) { copy: *result = nfmalloc( reserve + l + 1 ); memcpy( *result + reserve, arg, l ); (*result)[reserve + l] = 0; return 0; } assert( out ); outl = strlen( out ); if (equals( in, (int)inl, out, outl )) goto copy; for (num = 0, i = 0; i < l; ) { if (i + inl > l) goto fout; for (ll = 0; ll < inl; ll++) if (arg[i + ll] != in[ll]) goto fout; num++; i += inl; continue; fout: if (outl) { if (i + outl > l) goto fnexti; for (ll = 0; ll < outl; ll++) if (arg[i + ll] != out[ll]) goto fnexti; return -1; } fnexti: i++; } if (!num) goto copy; if (!outl) return -2; *result = nfmalloc( reserve + l + num * (outl - inl) + 1 ); p = *result + reserve; for (i = 0; i < l; ) { if (i + inl > l) goto rnexti; for (ll = 0; ll < inl; ll++) if (arg[i + ll] != in[ll]) goto rnexti; memcpy( p, out, outl ); p += outl; i += inl; continue; rnexti: *p++ = arg[i++]; } *p = 0; return 0; } int mkdir_p( char *path, int len ) { if (!mkdir( path, 0700 ) || errno == EEXIST) return 0; char *p = memrchr( path, '/', (size_t)len ); *p = 0; if (mkdir_p( path, (int)(p - path) )) { *p = '/'; return -1; } *p = '/'; return mkdir( path, 0700 ); } static int compare_uints( const void *l, const void *r ) { uint li = *(const uint *)l, ri = *(const uint *)r; if (li != ri) // Can't subtract, the result might not fit into signed int. return li > ri ? 1 : -1; return 0; } void sort_uint_array( uint_array_t array ) { qsort( array.data, array.size, sizeof(uint), compare_uints ); } int find_uint_array( uint_array_t array, uint value ) { uint bot = 0, top = array.size; while (bot < top) { uint i = (bot + top) / 2; uint elt = array.data[i]; if (elt == value) return 1; if (elt < value) bot = i + 1; else top = i; } return 0; } static struct { uchar i, j, s[256]; } rs; void arc4_init( void ) { int i, fd; uchar j, si, dat[128]; if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) { error( "Fatal: no random number source available.\n" ); exit( 3 ); } if (read( fd, dat, 128 ) != 128) { error( "Fatal: cannot read random number source.\n" ); exit( 3 ); } close( fd ); for (i = 0; i < 256; i++) rs.s[i] = (uchar)i; for (i = j = 0; i < 256; i++) { si = rs.s[i]; j += si + dat[i & 127]; rs.s[i] = rs.s[j]; rs.s[j] = si; } rs.i = rs.j = 0; for (i = 0; i < 256; i++) arc4_getbyte(); } uchar arc4_getbyte( void ) { uchar si, sj; rs.i++; si = rs.s[rs.i]; rs.j += si; sj = rs.s[rs.j]; rs.s[rs.i] = sj; rs.s[rs.j] = si; return rs.s[(si + sj) & 0xff]; } static const uchar prime_deltas[] = { 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3, 1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0 }; uint bucketsForSize( uint size ) { uint base = 4, bits = 2; for (;;) { uint prime = base + prime_deltas[bits]; if (prime >= size) return prime; base <<= 1; bits++; } } static void list_prepend( list_head_t *head, list_head_t *to ) { assert( !head->next ); assert( to->next ); assert( to->prev->next == to ); head->next = to; head->prev = to->prev; head->prev->next = head; to->prev = head; } static void list_unlink( list_head_t *head ) { assert( head->next ); assert( head->next->prev == head); assert( head->prev->next == head); head->next->prev = head->prev; head->prev->next = head->next; head->next = head->prev = NULL; } static notifier_t *notifiers; static int changed; /* Iterator may be invalid now. */ #ifdef HAVE_POLL_H static struct pollfd *pollfds; static uint npolls, rpolls; #else # ifdef HAVE_SYS_SELECT_H # include # endif #endif void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux ) { #ifdef HAVE_POLL_H uint idx = npolls++; if (rpolls < npolls) { rpolls = npolls; pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) ); } pollfds[idx].fd = fd; pollfds[idx].events = 0; /* POLLERR & POLLHUP implicit */ sn->index = idx; #else sn->fd = fd; sn->events = 0; #endif sn->cb = cb; sn->aux = aux; sn->next = notifiers; notifiers = sn; } void conf_notifier( notifier_t *sn, short and_events, short or_events ) { #ifdef HAVE_POLL_H uint idx = sn->index; pollfds[idx].events = (pollfds[idx].events & and_events) | or_events; #else sn->events = (sn->events & and_events) | or_events; #endif } short notifier_config( notifier_t *sn ) { #ifdef HAVE_POLL_H return pollfds[sn->index].events; #else return sn->events; #endif } void wipe_notifier( notifier_t *sn ) { notifier_t **snp; #ifdef HAVE_POLL_H uint idx; #endif for (snp = ¬ifiers; *snp != sn; snp = &(*snp)->next) assert( *snp ); *snp = sn->next; sn->next = NULL; changed = 1; #ifdef HAVE_POLL_H idx = sn->index; memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) ); for (sn = notifiers; sn; sn = sn->next) { if (sn->index > idx) sn->index--; } #endif } #if _POSIX_TIMERS - 0 > 0 static clockid_t clkid; #endif void init_timers( void ) { #if _POSIX_TIMERS - 0 > 0 struct timespec ts; # ifdef CLOCK_BOOTTIME if (!clock_gettime( CLOCK_BOOTTIME, &ts )) { clkid = CLOCK_BOOTTIME; } else # endif # ifdef CLOCK_MONOTONIC_COARSE if (!clock_gettime( CLOCK_MONOTONIC_COARSE, &ts )) { clkid = CLOCK_MONOTONIC_COARSE; } else # endif clkid = CLOCK_MONOTONIC; #endif } int64_t get_now( void ) { #if _POSIX_TIMERS - 0 > 0 struct timespec ts; clock_gettime( clkid, &ts ); return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000; #else struct timeval tv; gettimeofday( &tv, NULL ); return tv.tv_sec * 1000LL + tv.tv_usec / 1000; #endif } static list_head_t timers = { &timers, &timers }; void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux ) { tmr->cb = cb; tmr->aux = aux; tmr->links.next = tmr->links.prev = NULL; } void wipe_wakeup( wakeup_t *tmr ) { if (tmr->links.next) list_unlink( &tmr->links ); } void conf_wakeup( wakeup_t *tmr, int to ) { list_head_t *head, *succ; if (to < 0) { if (tmr->links.next) list_unlink( &tmr->links ); } else { int64_t timeout = to; if (!to) { /* We always prepend null timers, to cluster related events. */ succ = timers.next; } else { timeout += get_now(); /* We start at the end in the expectation that the newest timer is likely to fire last * (which will be true only if all timeouts are equal, but it's an as good guess as any). */ for (succ = &timers; (head = succ->prev) != &timers; succ = head) { if (head != &tmr->links && timeout > ((wakeup_t *)head)->timeout) break; } assert( head != &tmr->links ); } tmr->timeout = timeout; if (succ != &tmr->links) { if (tmr->links.next) list_unlink( &tmr->links ); list_prepend( &tmr->links, succ ); } } } static void event_wait( void ) { list_head_t *head; notifier_t *sn; int m; #ifdef HAVE_POLL_H int timeout = -1; if ((head = timers.next) != &timers) { wakeup_t *tmr = (wakeup_t *)head; int64_t delta = tmr->timeout; if (!delta || (delta -= get_now()) <= 0) { list_unlink( head ); tmr->cb( tmr->aux ); return; } timeout = (int)delta; } switch (poll( pollfds, npolls, timeout )) { case 0: return; case -1: if (errno == EINTR) return; perror( "poll() failed in event loop" ); abort(); default: break; } for (sn = notifiers; sn; sn = sn->next) { uint n = sn->index; if ((m = pollfds[n].revents)) { assert( !(m & POLLNVAL) ); sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux ); if (changed) { changed = 0; break; } } } #else struct timeval *timeout = 0; struct timeval to_tv; fd_set rfds, wfds, efds; int fd; if ((head = timers.next) != &timers) { wakeup_t *tmr = (wakeup_t *)head; int64_t delta = tmr->timeout; if (!delta || (delta -= get_now()) <= 0) { list_unlink( head ); tmr->cb( tmr->aux ); return; } to_tv.tv_sec = delta / 1000; to_tv.tv_usec = delta * 1000; timeout = &to_tv; } FD_ZERO( &rfds ); FD_ZERO( &wfds ); FD_ZERO( &efds ); m = -1; for (sn = notifiers; sn; sn = sn->next) { fd = sn->fd; if (sn->events & POLLIN) FD_SET( fd, &rfds ); if (sn->events & POLLOUT) FD_SET( fd, &wfds ); FD_SET( fd, &efds ); if (fd > m) m = fd; } switch (select( m + 1, &rfds, &wfds, &efds, timeout )) { case 0: return; case -1: if (errno == EINTR) return; perror( "select() failed in event loop" ); abort(); default: break; } for (sn = notifiers; sn; sn = sn->next) { fd = sn->fd; m = 0; if (FD_ISSET( fd, &rfds )) m |= POLLIN; if (FD_ISSET( fd, &wfds )) m |= POLLOUT; if (FD_ISSET( fd, &efds )) m |= POLLERR; if (m) { sn->cb( m, sn->aux ); if (changed) { changed = 0; break; } } } #endif } void main_loop( void ) { while (notifiers || timers.next != &timers) event_wait(); } isync-1.5.1/src/drv_proxy.c0000644000175000001440000003036514652507516011331 // SPDX-FileCopyrightText: 2017-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #define DEBUG_FLAG DEBUG_DRV #include "driver.h" BIT_FORMATTER_FUNCTION(opts, open_flags_enum) typedef struct gen_cmd gen_cmd_t; typedef union proxy_store { store_t gen; struct { STORE(union proxy_store) const char *label; // foreign uint ref_count; driver_t *real_driver; store_t *real_store; gen_cmd_t *pending_cmds, **pending_cmds_append; wakeup_t wakeup; uint fake_nextuid; char is_fake; // Was "created" by dry-run char force_async; void (*expunge_callback)( message_t *msg, void *aux ); void (*bad_callback)( void *aux ); void *callback_aux; }; } proxy_store_t; static void proxy_store_deref( proxy_store_t *ctx ) { if (!--ctx->ref_count) { assert( !pending_wakeup( &ctx->wakeup ) ); free( ctx ); } } static int curr_tag; #define GEN_CMD \ uint ref_count; \ int tag; \ proxy_store_t *ctx; \ gen_cmd_t *next; \ void (*queued_cb)( gen_cmd_t *gcmd ); struct gen_cmd { GEN_CMD }; static gen_cmd_t * proxy_cmd_new( proxy_store_t *ctx, uint sz ) { gen_cmd_t *cmd = nfmalloc( sz ); cmd->ref_count = 2; cmd->tag = ++curr_tag; cmd->ctx = ctx; ctx->ref_count++; return cmd; } static void proxy_cmd_done( gen_cmd_t *cmd ) { if (!--cmd->ref_count) { proxy_store_deref( cmd->ctx ); free( cmd ); } } static void proxy_wakeup( void *aux ) { proxy_store_t *ctx = (proxy_store_t *)aux; gen_cmd_t *cmd = ctx->pending_cmds; assert( cmd ); if (!(ctx->pending_cmds = cmd->next)) ctx->pending_cmds_append = &ctx->pending_cmds; else conf_wakeup( &ctx->wakeup, 0 ); cmd->queued_cb( cmd ); proxy_cmd_done( cmd ); } static void proxy_invoke( gen_cmd_t *cmd, const char *name ) { proxy_store_t *ctx = cmd->ctx; if (ctx->force_async) { debug( "%s[% 2d] Queue %s\n", ctx->label, cmd->tag, name ); cmd->next = NULL; *ctx->pending_cmds_append = cmd; ctx->pending_cmds_append = &cmd->next; conf_wakeup( &ctx->wakeup, 0 ); } else { cmd->queued_cb( cmd ); proxy_cmd_done( cmd ); } } static void proxy_cancel_queued_cmds( proxy_store_t *ctx ) { if (ctx->pending_cmds) { // This would involve directly invoking the result callbacks with // DRV_CANCEL, for which we'd need another set of dispatch functions. // The autotest doesn't need that, so save the effort. error( "Fatal: Faking asynchronous cancelation is not supported.\n" ); abort(); } } #if 0 //# TEMPLATE GETTER static @type@proxy_@name@( store_t *gctx ) { proxy_store_t *ctx = (proxy_store_t *)gctx; @type@rv; @pre_invoke@ @indent_invoke@rv = ctx->real_driver->@name@( ctx->real_store ); @post_invoke@ debug( "%sCalled @name@@print_fmt_dry@, ret=@fmt@\n", ctx->label@print_pass_dry@, rv ); return rv; } //# END //# TEMPLATE REGULAR static @type@proxy_@name@( store_t *gctx@decl_args@ ) { proxy_store_t *ctx = (proxy_store_t *)gctx; @pre_print_args@ debug( "%sEnter @name@@print_fmt_dry@@print_fmt_args@\n", ctx->label@print_pass_dry@@print_pass_args@ ); @print_args@ @type@rv; @pre_invoke@ @indent_invoke@rv = ctx->real_driver->@name@( ctx->real_store@pass_args@ ); @post_invoke@ debug( "%sLeave @name@, ret=@print_fmt_ret@\n", ctx->label, @print_pass_ret@ ); return rv; } //# END //# TEMPLATE REGULAR_VOID static @type@proxy_@name@( store_t *gctx@decl_args@ ) { proxy_store_t *ctx = (proxy_store_t *)gctx; @pre_print_args@ debug( "%sEnter @name@@print_fmt_dry@@print_fmt_args@\n", ctx->label@print_pass_dry@@print_pass_args@ ); @print_args@ @pre_invoke@ @indent_invoke@ctx->real_driver->@name@( ctx->real_store@pass_args@ ); @post_invoke@ debug( "%sLeave @name@\n", ctx->label ); @action@ } //# END //# TEMPLATE CALLBACK_VOID debug( "%s[% 2d] Callback enter @name@\n", ctx->label, cmd->tag ); @print_cb_args@ //# END //# TEMPLATE CALLBACK_STS debug( "%s[% 2d] Callback enter @name@, sts=%d\n", ctx->label, cmd->tag, sts ); //# END //# TEMPLATE CALLBACK_STS_PRN debug( "%s[% 2d] Callback enter @name@, sts=%d\n", ctx->label, cmd->tag, sts ); if (sts == DRV_OK) { @print_cb_args@ } //# END //# TEMPLATE CALLBACK_STS_FMT if (sts == DRV_OK) { debug( "%s[% 2d] Callback enter @name@, sts=" stringify(DRV_OK) "@print_fmt_cb_args@\n", ctx->label, cmd->tag@print_pass_cb_args@ ); @print_cb_args@ } else { debug( "%s[% 2d] Callback enter @name@, sts=%d\n", ctx->label, cmd->tag, sts ); } //# END //# TEMPLATE CALLBACK typedef union { gen_cmd_t gen; struct { GEN_CMD void (*callback)( @decl_cb_args@void *aux ); void *callback_aux; @decl_state@ }; } @name@_cmd_t; static void proxy_@name@_cb( @decl_cb_args@void *aux ) { @name@_cmd_t *cmd = (@name@_cmd_t *)aux; proxy_store_t *ctx = cmd->ctx; @count_step@ @print_cb_args_tpl@ cmd->callback( @pass_cb_args@cmd->callback_aux ); debug( "%s[% 2d] Callback leave @name@\n", ctx->label, cmd->tag ); proxy_cmd_done( &cmd->gen ); } static void proxy_do_@name@( gen_cmd_t *gcmd ) { @name@_cmd_t *cmd = (@name@_cmd_t *)gcmd; proxy_store_t *ctx = cmd->ctx; @pre_print_args@ debug( "%s[% 2d] Enter @name@@print_fmt_dry@@print_fmt_args@\n", ctx->label, cmd->tag@print_pass_dry@@print_pass_args@ ); @print_args@ @pre_invoke@ @indent_invoke@ctx->real_driver->@name@( ctx->real_store@pass_args@, proxy_@name@_cb, cmd ); @post_invoke@ debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->tag ); } static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux ) { proxy_store_t *ctx = (proxy_store_t *)gctx; @name@_cmd_t *cmd = (@name@_cmd_t *)proxy_cmd_new( ctx, sizeof(@name@_cmd_t) ); cmd->queued_cb = proxy_do_@name@; cmd->callback = cb; cmd->callback_aux = aux; @assign_state@ proxy_invoke( &cmd->gen, "@name@" ); } //# END //# UNDEFINE list_store_print_fmt_cb_args //# UNDEFINE list_store_print_pass_cb_args //# DEFINE list_store_print_cb_args for (string_list_t *box = boxes; box; box = box->next) debug( " %s\n", box->string ); //# END //# DEFINE select_box_pre_invoke ctx->is_fake = 0; //# END //# DEFINE create_box_driable 1 //# DEFINE create_box_fake_invoke ctx->is_fake = 1; //# END //# DEFINE create_box_counted 1 //# DEFINE open_box_fakeable 1 //# DEFINE open_box_fake_invoke ctx->fake_nextuid = 1; //# END //# DEFINE open_box_fake_cb_args , 1 //# DEFINE get_uidnext_fakeable 1 //# DEFINE get_uidnext_fake_invoke rv = ctx->fake_nextuid; //# END //# DEFINE get_uidnext_post_real_invoke ctx->fake_nextuid = rv; //# END //# DEFINE get_supported_flags_fakeable 1 //# DEFINE get_supported_flags_fake_invoke rv = 255; //# END //# DEFINE confirm_box_empty_fakeable 1 //# DEFINE confirm_box_empty_fake_invoke rv = 1; //# END //# DEFINE delete_box_driable 1 //# DEFINE delete_box_fake_invoke ctx->is_fake = 0; //# END //# DEFINE delete_box_counted 1 //# DEFINE finish_delete_box_driable 1 //# DEFINE finish_delete_box_fake_invoke rv = DRV_OK; //# END //# DEFINE prepare_load_box_print_fmt_args , opts=%s //# DEFINE prepare_load_box_print_pass_args , fmt_opts( opts ).str //# DEFINE prepare_load_box_print_fmt_ret %s //# DEFINE prepare_load_box_print_pass_ret fmt_opts( rv ).str //# DEFINE load_box_pre_print_args char ubuf[12]; //# END //# DEFINE load_box_print_fmt_args , [%u,%s] (find >= %u, paired <= %u, new > %u) //# DEFINE load_box_print_pass_args , cmd->minuid, (cmd->maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", cmd->maxuid ), ubuf), cmd->finduid, cmd->pairuid, cmd->newuid //# DEFINE load_box_print_args if (cmd->excs.size) { debugn( " excs:" ); for (uint t = 0; t < cmd->excs.size; t++) debugn( " %u", cmd->excs.data[t] ); debug( "\n" ); } //# END //# DEFINE load_box_fakeable 1 //# DEFINE load_box_fake_cb_args , NULL, 0, 0 //# DEFINE load_box_print_fmt_cb_args , total=%d, recent=%d //# DEFINE load_box_print_pass_cb_args , total_msgs, recent_msgs //# DEFINE load_box_print_cb_args for (message_t *msg = msgs; msg; msg = msg->next) { if (msg->status & M_DEAD) continue; debug( " uid=%-5u flags=%-4s size=%-6u tuid=%." stringify(TUIDL) "s\n", msg->uid, (msg->status & M_FLAGS) ? fmt_flags( msg->flags ).str : "?", msg->size, *msg->tuid ? msg->tuid : "?" ); } //# END //# UNDEFINE find_new_msgs_print_fmt_cb_args //# UNDEFINE find_new_msgs_print_pass_cb_args //# DEFINE find_new_msgs_print_cb_args for (message_t *msg = msgs; msg; msg = msg->next) { if (msg->status & M_DEAD) continue; debug( " uid=%-5u tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid ); } //# END //# DEFINE fetch_msg_print_fmt_args , uid=%u, want_flags=%s, want_date=%s //# DEFINE fetch_msg_print_pass_args , cmd->msg->uid, !(cmd->msg->status & M_FLAGS) ? "yes" : "no", cmd->data->date ? "yes" : "no" //# DEFINE fetch_msg_driable 1 //# DEFINE fetch_msg_fake_invoke cmd->data->data = strdup( "" ); cmd->data->len = 0; //# END //# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%lld, size=%u //# DEFINE fetch_msg_print_pass_cb_args , fmt_flags( cmd->data->flags ).str, (long long)cmd->data->date, cmd->data->len //# DEFINE fetch_msg_print_cb_args if (DFlags & DEBUG_DRV_ALL) { printf( "%s=========\n", cmd->ctx->label ); fwrite( cmd->data->data, cmd->data->len, 1, stdout ); printf( "%s=========\n", cmd->ctx->label ); fflush( stdout ); } //# END //# DEFINE store_msg_print_fmt_args , flags=%s, date=%lld, size=%u, to_trash=%s //# DEFINE store_msg_print_pass_args , fmt_flags( cmd->data->flags ).str, (long long)cmd->data->date, cmd->data->len, cmd->to_trash ? "yes" : "no" //# DEFINE store_msg_print_args if (DFlags & DEBUG_DRV_ALL) { printf( "%s>>>>>>>>>\n", ctx->label ); fwrite( cmd->data->data, cmd->data->len, 1, stdout ); printf( "%s>>>>>>>>>\n", ctx->label ); fflush( stdout ); } //# END //# DEFINE store_msg_driable 1 //# DEFINE store_msg_fake_cb_args , cmd->to_trash ? 0 : ctx->fake_nextuid++ //# DEFINE store_msg_counted 1 //# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s //# DEFINE set_msg_flags_print_pass_args , cmd->uid, fmt_flags( cmd->add ).str, fmt_flags( cmd->del ).str //# DEFINE set_msg_flags_driable 1 //# DEFINE set_msg_flags_counted 1 //# DEFINE trash_msg_print_fmt_args , uid=%u //# DEFINE trash_msg_print_pass_args , cmd->msg->uid //# DEFINE trash_msg_driable 1 //# DEFINE trash_msg_counted 1 //# DEFINE close_box_driable 1 //# DEFINE close_box_fake_cb_args , 0 //# DEFINE close_box_counted 1 //# DEFINE cancel_cmds_print_cb_args proxy_cancel_queued_cmds( ctx ); //# END //# DEFINE free_store_print_args proxy_cancel_queued_cmds( ctx ); //# END //# DEFINE free_store_action proxy_store_deref( ctx ); //# END //# DEFINE cancel_store_print_args proxy_cancel_queued_cmds( ctx ); //# END //# DEFINE cancel_store_action proxy_store_deref( ctx ); //# END #endif //# SPECIAL set_callbacks static void proxy_set_callbacks( store_t *gctx, void (*exp_cb)( message_t *, void * ), void (*bad_cb)( void * ), void *aux ) { proxy_store_t *ctx = (proxy_store_t *)gctx; ctx->expunge_callback = exp_cb; ctx->bad_callback = bad_cb; ctx->callback_aux = aux; } static void proxy_invoke_expunge_callback( message_t *msg, proxy_store_t *ctx ) { ctx->ref_count++; debug( "%sCallback enter expunged message %u\n", ctx->label, msg->uid ); ctx->expunge_callback( msg, ctx->callback_aux ); debug( "%sCallback leave expunged message %u\n", ctx->label, msg->uid ); proxy_store_deref( ctx ); } static void proxy_invoke_bad_callback( proxy_store_t *ctx ) { ctx->ref_count++; debug( "%sCallback enter bad store\n", ctx->label ); ctx->bad_callback( ctx->callback_aux ); debug( "%sCallback leave bad store\n", ctx->label ); proxy_store_deref( ctx ); } //# EXCLUDE alloc_store store_t * proxy_alloc_store( store_t *real_ctx, const char *label, int force_async ) { proxy_store_t *ctx; ctx = nfzalloc( sizeof(*ctx) ); ctx->driver = &proxy_driver; ctx->gen.conf = real_ctx->conf; ctx->ref_count = 1; ctx->label = label; ctx->force_async = force_async; ctx->pending_cmds_append = &ctx->pending_cmds; ctx->real_driver = real_ctx->driver; ctx->real_store = real_ctx; ctx->real_driver->set_callbacks( ctx->real_store, (void (*)( message_t *, void * ))proxy_invoke_expunge_callback, (void (*)( void * ))proxy_invoke_bad_callback, ctx ); init_wakeup( &ctx->wakeup, proxy_wakeup, ctx ); return &ctx->gen; } //# EXCLUDE parse_store //# EXCLUDE cleanup //# EXCLUDE get_fail_state #include "drv_proxy.inc" isync-1.5.1/src/mdconvert.1.in0000644000175000001440000000202214653132252011576 .\" SPDX-FileCopyrightText: 2004-2022 Oswald Buddenhagen .\" SPDX-License-Identifier: GPL-2.0-or-later .\" .\" mdconvert - Maildir mailbox UID storage scheme converter . .TH mdconvert 1 @RELEASE_DATE@ "@PACKAGE_STRING@" "User Commands" . .SH NAME mdconvert - Maildir mailbox UID storage scheme converter . .SH SYNOPSIS \fBmdconvert\fR [\fIoptions\fR ...] \fImailbox\fR ... . .SH DESCRIPTION \fBmdconvert\fR converts Maildir mailboxes between the two UID storage schemes supported by \fBmbsync\fR. See \fBmbsync\fR's manual page for details on these schemes. . .SH OPTIONS .TP \fB-a\fR, \fB--alt\fR Convert to the \fBalternative\fR (Berkeley DB based) UID storage scheme. .TP \fB-n\fR, \fB--native\fR Convert to the \fBnative\fR (file name based) UID storage scheme. This is the default. .TP \fB-h\fR, \fB--help\fR Displays a summary of command line options. .TP \fB-v\fR, \fB--version\fR Displays version information. . .SH SEE ALSO mbsync(1) . .SH AUTHOR Written and maintained by Oswald Buddenhagen . isync-1.5.1/src/Makefile.am0000644000175000001440000000314114653132252011145 # SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later mbsync_SOURCES = \ util.c config.c socket.c \ driver.c drv_proxy.c \ drv_imap.c imap_msgs.c imap_utf7.c \ drv_maildir.c \ sync.c sync_state.c sync_msg_cvt.c \ main.c main_sync.c main_list.c noinst_HEADERS = \ common.h config.h socket.h \ driver.h imap_p.h \ sync.h sync_p.h \ main_p.h mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS) $(KEYCHAIN_LIBS) drv_proxy.$(OBJEXT): drv_proxy.inc drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc mdconvert_SOURCES = mdconvert.c mdconvert_LDADD = $(DB_LIBS) if with_mdconvert mdconvert_prog = mdconvert mdconvert_man = mdconvert.1 endif in_man = mbsync.1.in mdconvert.1.in bin_PROGRAMS = mbsync $(mdconvert_prog) # don't forget to update AC_CONFIG_FILES in configure.ac! man_MANS = mbsync.1 $(mdconvert_man) tst_imap_msgs_SOURCES = tst_imap_msgs.c imap_msgs.c util.c tst_imap_utf7_SOURCES = tst_imap_utf7.c imap_utf7.c util.c tst_msg_cvt_SOURCES = tst_msg_cvt.c sync_msg_cvt.c util.c tst_msg_cvt_CFLAGS = -DQPRINTF_BUFF=10000 check_PROGRAMS = tst_imap_msgs tst_imap_utf7 tst_msg_cvt TESTS = $(check_PROGRAMS) tst_timers_SOURCES = tst_timers.c util.c EXTRA_PROGRAMS = tst_timers exampledir = $(docdir)/examples example_DATA = mbsyncrc.sample EXTRA_DIST = drv_proxy_gen.pl run-tests.pl $(example_DATA) $(in_man) CLEANFILES = drv_proxy.inc isync-1.5.1/src/mbsync.1.in0000644000175000001440000010642714720577152011115 .\" SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins .\" SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen .\" SPDX-License-Identifier: GPL-2.0-or-later .\" .\" mbsync - mailbox synchronizer . .TH mbsync 1 @RELEASE_DATE@ "@PACKAGE_STRING@" "User Commands" . .SH NAME mbsync - synchronize IMAP4 and Maildir mailboxes . .SH SYNOPSIS \fBmbsync\fR [\fIoptions\fR ...] {{\fIchannel\fR[\fB:\fIbox\fR[{\fB,\fR|\fB\\n\fR}...]]|\fIgroup\fR} ...|\fB-a\fR} .br \fBmbsync\fR --list-stores [\fIoptions\fR ...] [\fIstore\fR ...] . .SH DESCRIPTION \fBmbsync\fR is a command line application which synchronizes mailboxes; currently Maildir and IMAP4 mailboxes are supported. New messages, message deletions and flag changes can be propagated both ways; the operation set can be selected in a fine-grained manner. .br Synchronization is based on unique message identifiers (UIDs), so no identification conflicts can occur (unlike with some other mail synchronizers). OTOH, \fBmbsync\fR is susceptible to UID validity changes (but will recover just fine if the change is unfounded). Synchronization state is kept in one local text file per mailbox pair; these files are protected against concurrent \fBmbsync\fR processes. Mailboxes can be safely modified while \fBmbsync\fR operates (see \fBINHERENT PROBLEMS\fR below for a minor exception). Multiple replicas of each mailbox can be maintained. . .SH OPTIONS .TP \fB-c\fR, \fB--config\fR \fIfile\fR Read configuration from \fIfile\fR. By default, the configuration is read from $XDG_CONFIG_HOME/isyncrc, and if that does not exist, ~/.mbsyncrc is tried in turn. $XDG_CONFIG_HOME defaults to ~/.config if not set. .TP \fB-a\fR, \fB--all\fR Select all configured Channels. Any Channel/Group specifications on the command line are ignored. .TP \fB-l\fR, \fB--list\fR Don't synchronize anything, but list all mailboxes in the selected Channels and exit. .TP \fB-ls\fR, \fB--list-stores\fR Don't synchronize anything, but list all mailboxes in the selected Stores and exit. If no Stores are specified, all configured ones are listed. These are raw Store contents, not filtered by any Channel's \fBPatterns\fR. This option may be used to verify each Store's configuration. .TP \fB-C\fR[\fBf\fR][\fBn\fR], \fB--create\fR[\fB-far\fR|\fB-near\fR] Override any \fBCreate\fR options from the config file. See below. .TP \fB-R\fR[\fBf\fR][\fBn\fR], \fB--remove\fR[\fB-far\fR|\fB-near\fR] Override any \fBRemove\fR options from the config file. See below. .TP \fB-X\fR[\fBf\fR][\fBn\fR], \fB--expunge\fR[\fB-far\fR|\fB-near\fR] Override any \fBExpunge\fR options from the config file. See below. .TP {\fB-n\fR|\fB-o\fR|\fB-u\fR|\fB-g\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\ {\fB--new\fR|\fB--old\fR|\fB--upgrade\fR|\fB--gone\fR|\fB--flags\fR|\fB--noop\fR|\fB--full\fR} .TP \r{\fB-L\fR|\fB-H\fR}[\fBn\fR][\fBo\fR][\fBu\fR][\fBg\fR][\fBf\fR],\ {\fB--pull\fR|\fB--push\fR}[\fB-new\fR|\fB-old\fR|\fB-upgrade\fR|\fB-gone\fR|\fB-flags\fR] Override any \fBSync\fR options from the config file. See below. .TP \fB-h\fR, \fB--help\fR Display a summary of command line options. .TP \fB-v\fR, \fB--version\fR Display version information. .TP \fB-y\fR, \fB--dry-run\fR Enter simulation mode: the Channel status is queried and all required operations are determined, but no modifications are actually made to either the mailboxes or the state files. .TP \fB-e\fR, \fB--ext-exit\fR Return an extended exit code: Add 32 or 64 to the code if any modifications were made on the far or near side, respectively; these are not mutually exclusive, so the code may be 96 if changes were both pushed and pulled. An error may be reported at the same time, so the code may be for example 65 if some changes were successfully pulled, while others failed. .TP \fB-V\fR, \fB--verbose\fR Enable \fIverbose\fR mode, which displays what is currently happening. .TP \fB-D\fR[\fBC\fR][\fBd\fR|\fBD\fR][\fBm\fR][\fBM\fR][\fBn\fR|\fBN\fR][\fBs\fR]\fR]\fR,\ \fB--debug\fR[\fB-crash\fR|\fB-driver\fR|\fB-driver-all\fR|\fB-maildir\fR|\fB-main\fR|\fB-net\fR|\fB-net-all\fR|\fB-sync\fR] Enable debugging categories: .in +4 \fBC\fR, \fBcrash\fR - use built-in crash handler .br \fBd\fR, \fBdriver\fR - print driver calls (metadata only) .br \fBD\fR, \fBdriver-all\fR - print driver calls (including messages) .br \fBm\fR, \fBmaildir\fR - print maildir debug info .br \fBM\fR, \fBmain\fR - print main debug info .br \fBn\fR, \fBnet\fR - print network traffic (protocol only) .br \fBN\fR, \fBnet-all\fR - print network traffic (including payloads) .br \fBs\fR, \fBsync\fR - print synchronization debug info .in -4 All categories except \fBcrash\fR implicitly enable \fIverbose\fR mode. Without category specification, all categories except net-all are enabled. .TP \fB-q\fR, \fB--quiet\fR Suppress progress counters (this is implicit if stdout is no TTY, or any debugging categories are enabled), notices, and the summary. If specified twice, suppress warning messages as well. . .SH CONFIGURATION The configuration file is mandatory; \fBmbsync\fR will not run without it. Lines starting with a hash mark (\fB#\fR) are comments and are ignored entirely. Configuration items are keywords followed by one or more arguments; arguments containing spaces must be enclosed in double quotes (\fB"\fR), and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped. All keywords (including those used as arguments) are case-insensitive. Bash-like home directory expansion using the tilde (\fB~\fR) is supported in all options which represent local paths. The reference point for relative local paths is the configuration file's containing directory. There are a few global options, the others apply to particular sections. Sections begin with a section-starting keyword and are terminated by an empty line or end of file. Every section defines an object with an identifier unique within that object class. .P There are two basic object classes: Stores and Channels. A Store defines a collection of mailboxes; basically a folder, either local or remote. A Channel connects two Stores, describing the way the two are synchronized. .br There are two auxiliary object classes: Accounts and Groups. An Account describes the connection part of network Stores, so server configurations can be shared between multiple Stores. A Group aggregates multiple Channels to save typing on the command line. .P File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the Store's internal path separators, which may be slashes, periods, etc., or even combinations thereof. .br Mailbox names, OTOH, always use canonical path separators, which are Unix-like forward slashes. . .SS All Stores These options can be used in all supported Store types. .br The term "opposite Store" refers to the other Store within a Channel. .br The special mailbox \fBINBOX\fR exists in every Store; its physical location in the file system is Store type specific. . .TP \fBPath\fR \fIpath\fR The location of the Store in the (server's) file system. If this is no absolute path, the reference point is Store type specific. This string is prepended to the mailbox names addressed in this Store, but is not considered part of them; this is important for \fBPatterns\fR and \fBCreate\fR in the Channels section. Note that you \fBmust\fR append a slash if you want to specify an entire directory. (Default: none) . .TP \fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR] Messages larger than \fIsize\fR will have only a small placeholder message propagated into this Store. This is useful for avoiding downloading messages with large attachments unless they are actually needed. To upgrade the placeholder to the full message, it must be flagged, and the \fBUpgrade\fR operation executed. Caveats: Setting a size limit on a Store you never read directly (which is typically the case for servers) is not recommended, as you may never notice that affected messages were not propagated to it. Also, as flagging is (ab-)used to request an upgrade, changes to the message's flagging state will not be propagated in either direction until after the placeholder is upgraded. .br \fBK\fR and \fBM\fR can be appended to the size to specify KiBytes resp. MeBytes instead of bytes. \fBB\fR is accepted but superfluous. If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR. (Default: \fI0\fR) . .TP \fBMapInbox\fR \fImailbox\fR Create a virtual mailbox (relative to \fBPath\fR) which aliases the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the Channels section, though with a Maildir near side, you probably want to place \fBInbox\fR under \fBPath\fR instead. This virtual mailbox does not support subfolders. . .TP \fBFlatten\fR \fIdelim\fR Flatten the hierarchy within this Store by substituting the canonical hierarchy delimiter \fB/\fR with \fIdelim\fR. This can be useful when the MUA used to access the Store provides suboptimal handling of hierarchical mailboxes, as is the case with \fBMutt\fR. A common choice for the delimiter is \fB.\fR. .br Note that flattened sub-folders of the \fBINBOX\fR always end up under \fBPath\fR, including the "INBOX\fIdelim\fR" prefix. . .TP \fBTrash\fR \fImailbox\fR Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to prior to expunging. See \fBRECOMMENDATIONS\fR and \fBINHERENT PROBLEMS\fR below. (Default: none) . .TP \fBTrashNewOnly\fR \fByes\fR|\fBno\fR When trashing, copy only not yet propagated messages. This makes sense if the opposite Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fBno\fR). (Default: \fBno\fR) . .TP \fBTrashRemoteNew\fR \fByes\fR|\fBno\fR When expunging the opposite Store, copy not yet propagated messages to this Store's \fBTrash\fR. When using this, the opposite Store does not need an own \fBTrash\fR at all, yet all messages are archived. (Default: \fBno\fR) . .SS Maildir Stores The reference point for relative \fBPath\fRs is the configuration file's containing directory. .P As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons. .br The \fBnative\fR scheme is stolen from the latest Maildir patches to \fBc-client\fR and is therefore compatible with \fBpine\fR. The UID validity is stored in a file named .uidvalidity; the UIDs are encoded in the file names of the messages. .br The \fBalternative\fR scheme is based on the UID mapping used by \fBisync\fR versions 0.8 and 0.9.x. The invariant parts of the file names of the messages are used as keys into a Berkeley database named .isyncuidmap.db, which holds the UID validity as well. .br The \fBnative\fR scheme is faster, more space efficient, endianness independent and "human readable", but will be disrupted if a message is copied from another mailbox without getting a new file name; this would result in duplicated UIDs sooner or later, which in turn results in a UID validity change, making synchronization fail. The \fBalternative\fR scheme would fail if a MUA changed a message's file name in a part \fBmbsync\fR considers invariant; this would be interpreted as a message deletion and a new message, resulting in unnecessary traffic. .br \fBMutt\fR is known to work fine with both schemes. .br Use \fBmdconvert\fR to convert mailboxes from one scheme to the other. . .TP \fBMaildirStore\fR \fIname\fR Define the Maildir Store \fIname\fR, opening a section for its parameters. . .TP \fBAltMap\fR \fByes\fR|\fBno\fR Use the \fBalternative\fR UID storage scheme for mailboxes in this Store. This does not affect mailboxes that do already have a UID storage scheme; use \fBmdconvert\fR to change it. See \fBRECOMMENDATIONS\fR below. (Default: \fBno\fR) . .TP \fBInbox\fR \fIpath\fR The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR, but it is allowed to place the \fBINBOX\fR inside the \fBPath\fR. (Default: \fI~/Maildir\fR) . .TP \fBInfoDelimiter\fR \fIdelim\fR The character used to delimit the info field from a message's basename. The Maildir standard defines this to be the colon, but this is incompatible with DOS/Windows file systems. (Default: the value of \fBFieldDelimiter\fR) . .TP \fBSubFolders\fR \fBVerbatim\fR|\fBMaildir++\fR|\fBLegacy\fR The on-disk folder naming style used for hierarchical mailboxes. This option has no effect when \fBFlatten\fR is used. .br Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and \fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths: .br \fBVerbatim\fR - \fIPath\fB/top/sub/subsub\fR and \fIInbox\fB/sub/subsub\fR (this is the style you probably want to use) .br \fBMaildir++\fR - \fIInbox\fB/.top.sub.subsub\fR and \fIInbox\fB/..sub.subsub\fR (this style is compatible with Courier and Dovecot - but note that the mailbox metadata format is \fInot\fR compatible). Note that attempts to set \fBPath\fR are rejected in this mode. .br \fBLegacy\fR - \fIPath\fB/top/.sub/.subsub\fR and \fIInbox\fB/.sub/.subsub\fR (this is \fBmbsync\fR's historical style) .br (Default: unset; will error out when sub-folders are encountered) . .SS IMAP4 Accounts .TP \fBIMAPAccount\fR \fIname\fR Define the IMAP4 Account \fIname\fR, opening a section for its parameters. . .TP \fBHost\fR \fIhost\fR Specify the DNS name or IP address of the IMAP server. .br If \fBTunnel\fR is used, this setting is needed only if \fBTLSType\fR is not \fBNone\fR and \fBCertificateFile\fR is not used, in which case the host name is used for certificate subject verification. . .TP \fBPort\fR \fIport\fR Specify the TCP port number of the IMAP server. (Default: 143 for IMAP, 993 for IMAPS) .br If \fBTunnel\fR is used, this setting is ignored. . .TP \fBTimeout\fR \fItimeout\fR Specify the connect and data timeout for the IMAP server in seconds. Zero means unlimited. (Default: \fI20\fR) . .TP \fBUser\fR \fIusername\fR Specify the login name on the IMAP server. . .TP \fBUserCmd\fR [\fB+\fR]\fIcommand\fR Specify a shell command to obtain a user rather than specifying a user directly. This allows you to script retrieving user names. .br The command must produce exactly one line on stdout; the trailing newline is optional. Prepend \fB+\fR to the command to indicate that it produces TTY output (e.g., a prompt); failure to do so will merely produce messier output. Remember to backslash-escape double quotes and backslashes embedded into the command. . .TP \fBPass\fR \fIpassword\fR Specify the password for \fIusername\fR on the IMAP server. Note that this option is \fInot\fR required. If neither a password nor a password command is specified in the configuration file, \fBmbsync\fR will prompt you for a password. . .TP \fBPassCmd\fR [\fB+\fR]\fIcommand\fR Specify a shell command to obtain a password rather than specifying a password directly. This allows you to use password files and agents. .br See \fBUserCmd\fR above for details. . .TP \fBUseKeychain\fR \fByes\fR|\fBno\fR Whether to use the macOS Keychain to obtain the password. (Default: \fBno\fR) .IP The necessary keychain item can be created this way: .RS .IP .nh .B security add-internet-password \-r imap \-s .I Host .B \-a .I User .B \-w .I password [ .B \-T .I /path/to/mbsync ] .hy .RE . .TP \fBTunnel\fR \fIcommand\fR Specify a command to run to establish a connection rather than opening a TCP socket. This allows you to run an IMAP session over an SSH tunnel, for example. . .TP \fBAuthMechs\fR \fItype\fR ... The list of acceptable authentication mechanisms. In addition to the mechanisms listed in the SASL registry (link below), the legacy IMAP \fBLOGIN\fR mechanism is known. The wildcard \fB*\fR represents all mechanisms that are deemed secure enough for the current \fBTLSType\fR setting. The actually used mechanism is the most secure choice from the intersection of this list, the list supplied by the server, and the installed SASL modules. (Default: \fB*\fR) . .TP \fBTLSType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR} Select the connection security/encryption method: .br \fBNone\fR - no security. This is the default when \fBTunnel\fR is set, as tunnels are usually secure. .br \fBSTARTTLS\fR - security is established via the STARTTLS extension after connecting the regular IMAP port 143. Most servers support this, so it is the default (unless a tunnel is used). .br \fBIMAPS\fR - security is established by starting TLS negotiation right after connecting the secure IMAP port 993. . .TP \fBTLSVersions\fR {\fB+\fR|\fB-\fR}{\fB1.0\fR|\fB1.1\fR|\fB1.2\fR|\fB1.3\fR} ... Add/remove the specified TLS versions to/from the set of acceptable choices. Use old versions only when the server has problems with newer ones. Note that new versions are automatically enabled as soon as OpenSSL supports them, even if \fBmbsync\fR does not recognize them yet. (Default: All starting with 1.2). . .TP \fBSystemCertificates\fR \fByes\fR|\fBno\fR Whether the system's default CA (certificate authority) certificate store should be used to verify certificate trust chains. Disable this if you want to trust only hand-picked certificates. (Default: \fByes\fR) . .TP \fBCertificateFile\fR \fIpath\fR File containing additional X.509 certificates used to verify server identities. It may contain two types of certificates: .RS .IP Host These certificates are matched only against the received server certificate itself. They are always trusted, regardless of validity. A typical use case would be forcing acceptance of an expired certificate. .br These certificates may be obtained using the \fBmbsync-get-cert\fR tool; make sure to verify their fingerprints before trusting them, or transfer them securely from the server's network (if it can be trusted beyond the server itself). .IP CA These certificates are used as trust anchors when building the certificate chain for the received server certificate. They are used to supplant or supersede the system's trust store, depending on the \fBSystemCertificates\fR setting; it is not necessary and not recommended to specify the system's trust store itself here. The trust chains are fully validated. .RE . .TP \fBClientCertificate\fR \fIpath\fR File containing a client certificate to send to the server. \fBClientKey\fR should also be specified. .br Note that client certificate verification is usually not required, so it is unlikely that you need this option. . .TP \fBClientKey\fR \fIpath\fR File containing the private key corresponding to \fBClientCertificate\fR. . .TP \fBCipherString\fR \fIstring\fR Specify OpenSSL cipher string for connections secured with TLS up to version 1.2 (but not 1.3 and above). The format is described in \fBciphers\fR\|(1). (Default: empty, which implies system wide policy). . .TP \fBPipelineDepth\fR \fIdepth\fR Maximum number of IMAP commands which can be simultaneously in flight. Setting this to \fI1\fR disables pipelining. This is mostly a debugging option, but may also be used to limit average bandwidth consumption (GMail may require this if you have a very fast connection), or to spare flaky servers like M$ Exchange. (Default: \fIunlimited\fR) . .TP \fBDisableExtension\fR[\fBs\fR] \fIextension\fR ... Disable the use of specific IMAP extensions. This can be used to work around bugs in servers (and possibly \fBmbsync\fR itself). (Default: empty) . .SS IMAP Stores The reference point for relative \fBPath\fRs is whatever the server likes it to be; probably the user's $HOME or $HOME/Mail on that server. The location of \fBINBOX\fR is up to the server as well and is usually irrelevant. .TP \fBIMAPStore\fR \fIname\fR Define the IMAP4 Store \fIname\fR, opening a section for its parameters. . .TP \fBAccount\fR \fIaccount\fR Specify which IMAP4 Account to use. Instead of defining an Account and referencing it here, it is also possible to specify all the Account options directly in the Store's section - this makes sense if an Account is used for one Store only anyway. . .TP \fBUseNamespace\fR \fByes\fR|\fBno\fR Selects whether the server's first "personal" NAMESPACE should be prefixed to mailbox names. Disabling this makes sense for some broken IMAP servers. This option is meaningless if a \fBPath\fR was specified. (Default: \fByes\fR) . .TP \fBPathDelimiter\fR \fIdelim\fR Specify the server's hierarchy delimiter. (Default: taken from the server's first "personal" NAMESPACE) .br Do \fInot\fR abuse this to re-interpret the hierarchy. Use \fBFlatten\fR instead. . .TP \fBSubscribedOnly\fR \fByes\fR|\fBno\fR Selects whether to synchronize only mailboxes that are subscribed to on the IMAP server. In technical terms, if this option is set, \fBmbsync\fR will use the IMAP command LSUB instead of LIST to look for mailboxes in this Store. This option make sense only in conjunction with \fBPatterns\fR. (Default: \fBno\fR) . .SS Channels .TP \fBChannel\fR \fIname\fR Define the Channel \fIname\fR, opening a section for its parameters. . .TP {\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR] Specify the far resp. near side Store to be connected by this Channel. If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a prefix which is not matched against the patterns, and which is not affected by mailbox list overrides. Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed. . .TP \fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ... Instead of synchronizing only one mailbox pair, synchronize all mailboxes that match the \fIpattern\fR(s). The mailbox names are the same on the far and near side. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything and \fB%\fR matches anything up to the next hierarchy delimiter. Prepending \fB!\fR to a pattern makes it an exclusion. Multiple patterns can be specified (either by supplying multiple arguments or by using \fBPattern\fR multiple times); later matches take precedence. .br Note that \fBINBOX\fR is not matched by wildcards, unless it lives under \fBPath\fR. .br The mailbox list selected by \fBPatterns\fR can be overridden by a mailbox list in a Channel reference (a \fBGroup\fR specification or the command line). .br Example: "\fBPatterns\fR\ \fI%\ !Trash\fR" . .TP \fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR] Analogous to the homonymous option in the Stores section, but applies equally to Far and Near. Note that this actually modifies the Stores, so take care not to provide conflicting settings if you use the Stores in multiple Channels. . .TP \fBMaxMessages\fR \fIcount\fR Sets the maximum number of messages to keep in each near side mailbox. This is useful for mailboxes where you keep a complete archive on the server, but want to mirror only the last messages (for instance, for mailing lists). The messages that were the first to arrive in the mailbox (independently of the actual date of the message) will be deleted first. Messages that are flagged (marked as important) and (by default) unread messages will not be automatically deleted. If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR (Global default: \fI0\fR). . .TP \fBExpireUnread\fR \fByes\fR|\fBno\fR Selects whether unread messages should be affected by \fBMaxMessages\fR. Normally, unread messages are considered important and thus never expired. This ensures that you never miss new messages even after an extended absence. However, if your archive contains large amounts of unread messages by design, treating them as important would practically defeat \fBMaxMessages\fR. In this case you need to enable this option. (Global default: \fBno\fR). . .TP \fBExpireSide\fR \fBFar\fR|\fBNear\fR Selects on which side messages should be expired when \fBMaxMessages\fR is configured. (Global default: \fBNear\fR). . .TP \fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBOld\fR] [\fBUpgrade\fR] [\fBGone\fR] [\fBFlags\fR] [\fBFull\fR]} Select the synchronization operation(s) to perform: .br \fBPull\fR - propagate changes from far to near side. .br \fBPush\fR - propagate changes from near to far side. .br \fBNew\fR - propagate newly appeared messages. .br \fBOld\fR - propagate previously skipped, failed, and expired messages. This has a (relatively) high cost and may repeatedly produce error messages, so it always must be specified explicitly. .br \fBUpgrade\fR - upgrade placeholders to full messages. Useful only with a configured \fBMaxSize\fR. .br \fBGone\fR - propagate message disappearances. This applies only to messages that are actually gone, i.e., were expunged. The affected messages in the opposite Store are marked as deleted only, i.e., they won't be really deleted until that Store is expunged. .br \fBFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as well; this is particularly interesting if you use \fBmutt\fR with the maildir_trash option. .br \fBFull\fR - alias for "\fBNew\fR\ \fBUpgrade\fR\ \fBGone\fR\ \fBFlags\fR". This is the global default. .br \fBNone\fR (\fB--noop\fR on the command line) - don't propagate anything. Useful if you want to expunge only. .IP \fBPull\fR and \fBPush\fR are direction flags, while \fBNew\fR, \fBOld\fR, \fBUpgrade\fR, \fBGone\fR, and \fBFlags\fR are type flags. The two flag classes make up a two-dimensional matrix (a table). Its cells are the individual actions to perform. There are two styles of asserting the cells: .br In the first style, the flags select entire rows/colums in the matrix. Only the cells which are selected both horizontally and vertically are asserted. Specifying no direction is like specifying both directions, and specifying no type is like specifying \fBFull\fR. For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate new messages and flag changes from the far side to the near side, "\fBSync\fR\ \fBNew\fR\ \fBGone\fR" will propagate message arrivals and deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes from the near side to the far side. .br In the second style, direction flags are concatenated with type flags; every compound flag immediately asserts a cell in the matrix. In addition to at least one compound flag, the individual flags can be used as well, but as opposed to the first style, they immediately assert all cells in their respective row/column (with the exception of \fBOld\fR). For example, "\fBSync\fR\ \fBPullNew\fR\ \fBPullGone\fR\ \fBPush\fR" will propagate message arrivals and deletions from the far side to the near side and any changes (except old messages) from the near side to the far side. .br Note that it is not allowed to assert a cell in two ways, e.g. "\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and "\fBSync\fR\ \fBPullNew\fR\ \fBGone\fR\ \fBPush\fR" induce error messages. .br \fBNone\fR may not be combined with any other operation. . .TP \fBCreate\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR} Automatically create missing mailboxes [on the far/near side]. Otherwise print an error message and skip that mailbox pair if a mailbox and the corresponding sync state does not exist. (Global default: \fBNone\fR) . .TP \fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR} Propagate mailbox deletions [to the far/near side]. Otherwise print an error message and skip that mailbox pair if a mailbox does not exist but the corresponding sync state does. .br For MailDir mailboxes it is sufficient to delete the cur/ subdirectory to mark them as deleted. This ensures compatibility with \fBSyncState *\fR. .br Note that for safety, non-empty mailboxes are never deleted. .br (Global default: \fBNone\fR) . .TP \fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR} Permanently remove all messages [on the far/near side] which are marked for deletion. Mutually exclusive with \fBExpungeSolo\fR for the same side. See \fBRECOMMENDATIONS\fR below. (Global default: \fBNone\fR) . .TP \fBExpungeSolo\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR} Permanently remove all messages [on the far/near side] which are both marked for deletion and have no corresponding message in the opposite Store. Together with \fBSync Gone\fR, this allows actual mirroring of expunges. Note, however, that this makes sense only if nothing else expunges the other messages which are marked for deletion. Also note that this does not work for IMAP Stores which do not support the UIDPLUS extension. Mutually exclusive with \fBExpunge\fR for the same side. (Global default: \fBNone\fR) . .TP \fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR} Selects whether their arrival time should be propagated together with the messages. Enabling this makes sense in order to keep the time stamp based message sorting intact. Note that IMAP does not guarantee that the time stamp (termed \fBinternal date\fR) is actually the arrival time, but it is usually close enough. (Global default: \fBno\fR) . .P \fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR, \fBExpungeSolo\fR, \fBMaxMessages\fR, \fBExpireUnread\fR, \fBExpireSide\fR, and \fBCopyArrivalDate\fR can be used before any section for a global effect. The global settings are overridden by Channel-specific options, which in turn are overridden by command line switches. . .TP \fBSyncState\fR {\fB*\fR|\fIpath\fR} Set the location of this Channel's synchronization state files. \fB*\fR means that the state should be saved in a file named .mbsyncstate in the near side mailbox itself; this has the advantage that you do not need to handle the state file separately if you delete the mailbox, but it works only with Maildir mailboxes, obviously. Otherwise this is interpreted as a string to prepend to the near side mailbox name to make up a complete path. Note that you \fBmust\fR append a slash if you want to specify a directory. .br This option can be used outside any section for a global effect. In this case the appended string is made up according to the pattern \fB:\fIfar-store\fB:\fIfar-box\fB_:\fInear-store\fB:\fInear-box\fR (see also \fBFieldDelimiter\fR below). .br (Global default: \fI$XDG_STATE_HOME/isync/\fR, with a fallback to \fI~/.mbsync/\fR if only that exists. $XDG_STATE_HOME defaults to ~/.local/state if not set.) . .SS Groups .TP \fBGroup\fR \fIname\fR [\fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]]] ... Define the Group \fIname\fR, opening a section for its parameters. Note that even though Groups have an own namespace, they will "hide" Channels with the same name on the command line. .br One or more Channels can be specified on the same line. .br If you supply one or more \fIbox\fRes to a \fIchannel\fR, they will be used instead of what is specified in the Channel's Patterns. The same can be done on the command line, except that there newlines can be used as mailbox name separators as well. . .TP \fBChannel\fR[\fBs\fR] \fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]] ... Add the specified Channels to the Group. This option can be specified multiple times within a Group. . .SS Global Options .TP \fBFSync\fR \fByes\fR|\fBno\fR .br Selects whether \fBmbsync\fR performs forced flushing, which determines the level of data safety after system crashes and power outages. Disabling it is reasonably safe for file systems which are mounted with data=ordered mode. Enabling it is a wise choice for file systems mounted with data=writeback, in particular modern systems like ext4, btrfs and xfs. The performance impact on older file systems may be disproportionate. (Default: \fByes\fR) . .TP \fBFieldDelimiter\fR \fIdelim\fR The character to use to delimit fields in the string appended to a global \fBSyncState\fR. \fBmbsync\fR prefers to use the colon, but this is incompatible with DOS/Windows file systems. This option is meaningless for \fBSyncState\fR if the latter is \fB*\fR, obviously. However, it also determines the default of \fBInfoDelimiter\fR. (Global default: \fI;\fR on Windows, \fI:\fR everywhere else) . .TP \fBBufferLimit\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR] The per-Channel, per-direction instantaneous memory usage above which \fBmbsync\fR will refrain from using more memory. Note that this is no absolute limit, as even a single message can consume more memory than this. (Default: \fI10M\fR) . .SH CONSOLE OUTPUT If \fBmbsync\fR's output is connected to a console, it will print progress counters by default. The output will look like this: .P .in +4 C: 1/2 B: 3/4 F: +13/13 *23/42 #0/0 -0/0 N: +0/7 *0/0 #0/0 -0/0 .in -4 .P This represents the cumulative progress over Channels, boxes, and messages affected on the far and near side, respectively. The message counts represent added messages, messages with updated flags, trashed messages, and expunged messages, respectively. No attempt is made to calculate the totals in advance, so they grow over time as more information is gathered. .P Irrespective of output redirection, \fBmbsync\fR will print a summary of the above upon completion, except in quiet mode. . .SH RECOMMENDATIONS Make sure your IMAP server does not auto-expunge deleted messages - it is slow, and semantically somewhat questionable. Specifically, Gmail needs to be configured not to do it. .P By default, \fBmbsync\fR will not delete any messages - expunges are propagated by marking the messages as deleted in the opposite Store. Once you have verified that your setup works, you will typically want to set \fBExpunge\fR to \fBBoth\fR, so that deletions become effective. .P \fBmbsync\fR's built-in trash functionality relies on \fBmbsync\fR doing the expunging of deleted messages. This is the case when it propagates deletions of previously propagated messages, and the trash is on the target Store (typically your IMAP server). .br However, when you intend \fBmbsync\fR to trash messages which were not propagated yet, the MUA must mark the messages as deleted without expunging them (e.g., \fBMutt\fR's \fBmaildir_trash\fR option). Note that most messages are propagated a long time before they are deleted, so this is a corner case you probably do not want to optimize for. This also implies that the \fBTrashNewOnly\fR and \fBTrashRemoteNew\fR options are typically not very useful. .P If your server supports auto-trashing (as Gmail does), it is probably a good idea to rely on that instead of \fBmbsync\fR's trash functionality. If you do that, and intend to synchronize the trash like other mailboxes, you should not use \fBmbsync\fR's \fBTrash\fR option at all. .P Use of the \fBTrash\fR option with M$ Exchange 2013 requires the use of \fBDisableExtension MOVE\fR due to a server bug. .P When using the more efficient default UID mapping scheme, it is important that the MUA renames files when moving them between Maildir folders. Mutt always does that, while mu4e needs to be configured to do it: .br .in +4 (setq mu4e-change-filenames-when-moving t) .in -4 .br The general expectation is that a completely new filename is generated as if the message was new, but stripping the \fB,U=\fIxxx\fR infix is sufficient as well. . .SH INHERENT PROBLEMS Changes done after \fBmbsync\fR has retrieved the message list will not be synchronised until the next time \fBmbsync\fR is invoked. .P Using \fBTrash\fR on IMAP Stores without the UIDPLUS extension (notably, M$ Exchange up to at least 2010) bears a race condition: messages will be lost if they are marked as deleted after the message list was retrieved but before the mailbox is expunged. There is no risk as long as the IMAP mailbox is accessed by only one client (including \fBmbsync\fR) at a time. . .SH FILES .TP \fB$XDG_CONFIG_HOME/isyncrc\fR (usually \fB~/.config/isyncrc\fR) Default configuration file. See also the example file in the documentation directory. .TP \fB$XDG_STATE_HOME/isync/\fR (usually \fB~/.local/state/isync/\fR) Directory containing synchronization state files. .TP .B ~/.mbsyncrc Legacy configuration file. .TP .B ~/.mbsync/ Legacy directory containing synchronization state files. . .SH SEE ALSO mdconvert(1), mutt(1), maildir(5) .P Up to date information on \fBmbsync\fR can be found at http://isync.sf.net/ .P SASL mechanisms are listed at http://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml . .SH AUTHORS Originally written by Michael R. Elkins, rewritten and currently maintained by Oswald Buddenhagen, contributions by many others; see the AUTHORS file for details. isync-1.5.1/src/socket.h0000644000175000001440000001000214713076212010544 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #ifndef SOCKET_H #define SOCKET_H #include "common.h" #ifdef HAVE_LIBZ #include #endif #ifdef HAVE_LIBSSL # include # include enum { TLSv1 = 4, TLSv1_1 = 8, TLSv1_2 = 16, TLSv1_3 = 32 }; #endif typedef struct { char *tunnel; char *host; ushort port; int timeout; #ifdef HAVE_LIBSSL char *cert_file; char *client_certfile; char *client_keyfile; char *cipher_string; char system_certs; char ssl_versions; /* these are actually variables and are leaked at the end */ char ssl_ctx_valid; STACK_OF(X509) *trusted_certs; SSL_CTX *SSLContext; #endif } server_conf_t; typedef struct buff_chunk { struct buff_chunk *next; uint len; char data[1]; } buff_chunk_t; typedef struct { /* connection */ int fd; int state; const server_conf_t *conf; /* needed during connect */ char *addrs, *addrs_end, *curr_addr; // needed during connect; assumed to be int-aligned char *name; #ifdef HAVE_LIBSSL SSL *ssl; wakeup_t ssl_fake; #endif #ifdef HAVE_LIBZ z_streamp in_z, out_z; wakeup_t z_fake; int z_written; #endif void (*bad_callback)( void *aux ); /* async fail while sending or listening */ void (*read_callback)( void *aux ); /* data available for reading */ void (*write_callback)( void *aux ); /* all *queued* data was sent */ union { void (*connect)( int ok, void *aux ); void (*starttls)( int ok, void *aux ); } callbacks; void *callback_aux; notifier_t notify; wakeup_t fd_fake; wakeup_t fd_timeout; /* writing */ buff_chunk_t *append_buf; /* accumulating buffer */ buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */ #ifdef HAVE_LIBZ uint append_avail; /* space left in accumulating buffer */ #endif uint write_offset; /* offset into buffer head */ uint buffer_mem; /* memory currently occupied by buffers in the queue */ /* reading */ uint offset; /* start of filled bytes in buffer */ uint bytes; /* number of filled bytes in buffer */ uint scanoff; /* offset to continue scanning for newline at, relative to 'offset' */ uint wanted; // try to accumulate that many bytes before calling back; 0 => full line uint readsz; // average size of bulk reads from the underlying socket, times 1.5 char buf[100000]; #ifdef HAVE_LIBZ char z_buf[100000]; #endif } conn_t; // Shorter reads are assumed to be limited by round-trips. #define MIN_BULK_READ 1000 /* call this before doing anything with the socket */ static INLINE void socket_init( conn_t *conn, const server_conf_t *conf, void (*bad_callback)( void *aux ), void (*read_callback)( void *aux ), void (*write_callback)( void *aux ), void *aux ) { conn->conf = conf; conn->bad_callback = bad_callback; conn->read_callback = read_callback; conn->write_callback = write_callback; conn->callback_aux = aux; conn->fd = -1; conn->name = NULL; conn->write_buf_append = &conn->write_buf; conn->wanted = 1; conn->readsz = MIN_BULK_READ * 3 / 2; } void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) ); void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) ); void socket_start_deflate( conn_t *conn ); void socket_close( conn_t *sock ); void socket_expect_activity( conn_t *sock, int expect ); void socket_expect_eof( conn_t *sock ); void socket_expect_bytes( conn_t *sock, uint len ); // Don't free return values. These functions never wait. char *socket_read( conn_t *conn, uint min_len, uint max_len, uint *out_len ); char *socket_read_line( conn_t *conn ); typedef enum { KeepOwn = 0, GiveOwn } ownership_t; typedef struct { char *buf; uint len; ownership_t takeOwn; } conn_iovec_t; void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt ); #endif isync-1.5.1/src/sync_p.h0000644000175000001440000001135014652507516010566 // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #define DEBUG_FLAG DEBUG_SYNC #include "sync.h" #define srec_sts_enum(fn) \ fn(S, DEAD) /* ephemeral: the entry was killed and should be ignored */ \ fn(S, EXPIRE) /* the entry is being expired (expire-side message removal scheduled) */ \ fn(S, EXPIRED) /* the entry is expired (expire-side message removal confirmed) */ \ fn(S, NEXPIRE) /* temporary: new expiration state */ \ fn(S, PENDING) /* the entry is new and awaits propagation (possibly a retry) */ \ fn(S, DUMMY_F) /* f/n message is only a placeholder */ \ fn(S, DUMMY_N) \ fn(S, SKIPPED) /* pre-1.4 legacy: the entry was not propagated (message is too big) */ \ fn(S, GONE_F) /* ephemeral: f/n message has been expunged */ \ fn(S, GONE_N) \ fn(S, DEL_F) /* ephemeral: f/n message would be subject to non-selective expunge */ \ fn(S, DEL_N) \ fn(S, DELETE) /* ephemeral: flags propagation is a deletion */ \ fn(S, UPGRADE) /* ephemeral: upgrading placeholder, do not apply MaxSize */ \ fn(S, PURGE) /* ephemeral: placeholder is being nuked */ \ fn(S, PURGED) /* ephemeral: placeholder was nuked */ DEFINE_PFX_BIT_ENUM(srec_sts_enum) #define S_DUMMY(b) (S_DUMMY_F << (b)) #define S_GONE(b) (S_GONE_F << (b)) #define S_DEL(b) (S_DEL_F << (b)) // This is the persistent status of the sync record, with regard to the journal. #define S_LOGGED (S_EXPIRE | S_EXPIRED | S_PENDING | S_DUMMY(F) | S_DUMMY(N) | S_SKIPPED) typedef struct sync_rec { struct sync_rec *next; /* string_list_t *keywords; */ uint uid[2]; message_t *msg[2]; ushort status; uchar flags, pflags, aflags[2], dflags[2]; char tuid[TUIDL]; } sync_rec_t; static_assert_bits(msg_flags_enum, sync_rec_t, flags); static_assert_bits(srec_sts_enum, sync_rec_t, status); typedef struct { int t[2]; void (*cb)( int sts, void *aux ), *aux; char *dname, *jname, *nname, *lname, *box_name[2]; FILE *jfp, *nfp; sync_rec_t *srecs, **srecadd; channel_conf_t *chan; store_t *ctx[2]; driver_t *drv[2]; const char *orig_name[2]; message_t *msgs[2], *new_msgs[2]; uint_array_alloc_t trashed_msgs[2]; int state[2], lfd, ret, existing, replayed, any_expiring; uint ref_count, nsrecs, opts[2]; uint new_pending[2], flags_pending[2], trash_pending[2]; uint maxuid[2]; // highest UID that was already propagated uint oldmaxuid[2]; // highest UID that was already propagated before this run uint newmaxuid[2]; // highest UID that is currently being propagated uint uidval[2]; // UID validity value uint newuidval[2]; // UID validity obtained from driver uint finduid[2]; // TUID lookup makes sense only for UIDs >= this uint maxxfuid; // highest expired UID on full side uchar good_flags[2], bad_flags[2], can_crlf[2]; } sync_vars_t; int prepare_state( sync_vars_t *svars ); int lock_state( sync_vars_t *svars ); int load_state( sync_vars_t *svars ); void save_state( sync_vars_t *svars ); void delete_state( sync_vars_t *svars ); void ATTR_PRINTFLIKE(2, 3) jFprintf( sync_vars_t *svars, const char *msg, ... ); #define JLOG_(pre_commit, log_fmt, log_args, dbg_fmt, ...) \ do { \ if (pre_commit && !(DFlags & FORCEJOURNAL)) { \ debug( "-> (log: " log_fmt ") (" dbg_fmt ")\n", __VA_ARGS__ ); \ } else { \ debug( "-> log: " log_fmt " (" dbg_fmt ")\n", __VA_ARGS__ ); \ jFprintf( svars, log_fmt "\n", deparen(log_args) ); \ } \ } while (0) #define JLOG3(pre_commit, log_fmt, log_args, dbg_fmt) \ JLOG_(pre_commit, log_fmt, log_args, dbg_fmt, deparen(log_args)) #define JLOG4(pre_commit, log_fmt, log_args, dbg_fmt, dbg_args) \ JLOG_(pre_commit, log_fmt, log_args, dbg_fmt, deparen(log_args), deparen(dbg_args)) #define JLOG_SEL(_1, _2, _3, _4, x, ...) x #define JLOG(...) JLOG_SEL(__VA_ARGS__, JLOG4, JLOG3, NO_JLOG2, NO_JLOG1)(0, __VA_ARGS__) #define PC_JLOG(...) JLOG_SEL(__VA_ARGS__, JLOG4, JLOG3, NO_JLOG2, NO_JLOG1)(1, __VA_ARGS__) void assign_uid( sync_vars_t *svars, sync_rec_t *srec, int t, uint uid ); #define ASSIGN_UID(srec, t, nuid, ...) \ do { \ JLOG( "%c %u %u %u", ("<>"[t], srec->uid[F], srec->uid[N], nuid), __VA_ARGS__ ); \ assign_uid( svars, srec, t, nuid ); \ } while (0) void assign_tuid( sync_vars_t *svars, sync_rec_t *srec ); int match_tuids( sync_vars_t *svars, int t, message_t *msgs ); sync_rec_t *upgrade_srec( sync_vars_t *svars, sync_rec_t *srec, int t ); typedef struct copy_vars { void (*cb)( int sts, uint uid, struct copy_vars *vars ); void *aux; sync_rec_t *srec; /* also ->tuid */ message_t *msg; msg_data_t data; int minimal; } copy_vars_t; char *copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ); isync-1.5.1/src/main_p.h0000644000175000001440000000100414405565305010525 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #ifndef MAIN_P_H #define MAIN_P_H #define DEBUG_FLAG DEBUG_MAIN #include "sync.h" typedef struct { int ret; int all; int list; int list_stores; int ops[2]; } core_vars_t; void sync_chans( core_vars_t *cvars, char **argv ); void list_stores( core_vars_t *cvars, char **argv ); void cleanup_mainloop( void ); #endif isync-1.5.1/src/driver.h0000644000175000001440000003010314661340141010550 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #ifndef DRIVER_H #define DRIVER_H #include "config.h" typedef struct driver driver_t; #define FAIL_TEMP 0 /* Retry immediately (also: no error) */ #define FAIL_WAIT 1 /* Retry after some time (if at all) */ #define FAIL_FINAL 2 /* Don't retry until store reconfiguration */ #define STORE_CONF \ struct store_conf *next; \ char *name; \ driver_t *driver; \ const char *flat_delim; \ const char *map_inbox; \ const char *trash; \ uint max_size; /* off_t is overkill */ \ char trash_remote_new, trash_only_new; typedef struct store_conf { STORE_CONF } store_conf_t; extern store_conf_t *stores; /* For message->flags */ // Keep the MESSAGE_FLAGS in sync (grep that)! /* The order is according to alphabetical maildir flag sort */ #define msg_flags_enum(fn) \ fn(F, DRAFT) /* Draft */ \ fn(F, FLAGGED) /* Flagged */ \ fn(F, FORWARDED) /* Passed */ \ fn(F, ANSWERED) /* Replied */ \ fn(F, SEEN) /* Seen */ \ fn(F, DELETED) /* Trashed */ DEFINE_PFX_BIT_ENUM(msg_flags_enum) extern const char MsgFlags[msg_flags_enum_num_bits]; typedef struct { char str[msg_flags_enum_num_bits + 1]; } flag_str_t; flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_flags( uchar flags ); flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_lone_flags( uchar flags ); /* For message->status */ #define msg_sts_enum(fn) \ fn(M, RECENT) /* unsyncable flag; maildir_*() depend on this being bit 0 */ \ fn(M, DEAD) /* expunged */ \ fn(M, EXPUNGE) /* for driver_t->close_box() */ \ fn(M, FLAGS) /* flags are valid */ \ /* The following are only for IMAP FETCH response parsing */ \ fn(M, DATE) \ fn(M, SIZE) \ fn(M, BODY) \ fn(M, HEADER) DEFINE_PFX_BIT_ENUM(msg_sts_enum) #define TUIDL 12 #define MESSAGE(message) \ message *next; \ struct sync_rec *srec; \ char *msgid; /* owned */ \ /* string_list_t *keywords; */ \ uint size; /* zero implies "not fetched" */ \ uint uid; \ uchar flags, status; \ char tuid[TUIDL]; typedef struct message { MESSAGE(struct message) } message_t; static_assert_bits(msg_flags_enum, message_t, flags); static_assert_bits(msg_sts_enum, message_t, status); // For driver_t->prepare_load_box(), which may amend the passed flags. // The drivers don't use the first three, but may set them if loading the // particular range is required to handle some other flag; note that these // ranges may overlap. #define open_flags_enum(fn) \ fn(OPEN, PAIRED) /* Paired messages *in* this store. */ \ fn(OPEN, OLD) /* Messages that should be already propagated *from* this store. */ \ fn(OPEN, NEW) /* Messages (possibly) not yet propagated *from* this store. */ \ fn(OPEN, FIND) \ fn(OPEN, FLAGS) /* Note that fetch_msg() gets the flags regardless. */ \ fn(OPEN, OLD_SIZE) \ fn(OPEN, NEW_SIZE) \ fn(OPEN, PAIRED_IDS) \ fn(OPEN, APPEND) \ fn(OPEN, SETFLAGS) \ fn(OPEN, EXPUNGE) \ /* Expunge only deleted messages we know about. Relies on OPEN_{OLD,NEW,FLAGS} */ \ /* being set externally. The driver may unset it if it can't handle it. */ \ fn(OPEN, UID_EXPUNGE) DEFINE_PFX_BIT_ENUM(open_flags_enum) #define UIDVAL_BAD ((uint)-1) #define STORE(store) \ store *next; \ driver_t *driver; \ store##_conf *conf; /* foreign */ \ uchar racy_trash; typedef struct store { STORE(struct store) } store_t; typedef struct { char *data; uint len; time_t date; uchar flags; } msg_data_t; static_assert_bits(msg_flags_enum, msg_data_t, flags); #define DRV_OK 0 /* Message went missing, or mailbox is full, etc. */ #define DRV_MSG_BAD 1 /* Something is wrong with the current mailbox - probably it is somehow inaccessible. */ #define DRV_BOX_BAD 2 /* Failed to connect store. */ #define DRV_STORE_BAD 3 /* The command has been cancel_cmds()d or cancel_store()d. */ #define DRV_CANCELED 4 /* All memory belongs to the driver's user, unless stated otherwise. */ // All memory passed to driver functions must remain valid until the // respective result callback is invoked. /* This flag says that the driver CAN store messages with CRLFs, not that it must. The lack of it OTOH implies that it CANNOT, and as CRLF is the canonical format, we convert. */ #define DRV_CRLF 1 /* This flag says that the driver will act upon (Verbosity >= VERBOSE). */ #define DRV_VERBOSE 2 /* This flag says that the driver operates asynchronously. */ #define DRV_ASYNC 4 #define LIST_INBOX 1 #define LIST_PATH 2 #define LIST_PATH_MAYBE 4 #define xint uint // For auto-generation of appropriate printf() formats. struct driver { /* Return driver capabilities. */ xint (*get_caps)( store_t *ctx ); /* Parse configuration. */ int (*parse_store)( conffile_t *cfg, store_conf_t **storep ); /* Close remaining server connections. All stores must be discarded first. */ void (*cleanup)( void ); /* Allocate a store with the given configuration. This is expected to * return quickly, and must not fail. */ store_t *(*alloc_store)( store_conf_t *conf, const char *label ); // When exp_cb is invoked, the passed message has been expunged; // its status is M_DEAD now. // When bad_cb is invoked (at most once per store), the store is fubar; // call cancel_store() to dispose of it. void (*set_callbacks)( store_t *ctx, void (*exp_cb)( message_t *msg, void *aux ), void (*bad_cb)( void *aux ), void *aux ); /* Open/connect the store. This may recycle existing server connections. */ void (*connect_store)( store_t *ctx, void (*cb)( int sts, void *aux ), void *aux ); /* Discard the store. Underlying server connection may be kept alive. */ void (*free_store)( store_t *ctx ); /* Discard the store after a bad_callback. The server connections will be closed. * Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */ void (*cancel_store)( store_t *ctx ); /* List the mailboxes in this store. Flags are ORed LIST_* values. * The returned box list remains owned by the driver. */ void (*list_store)( store_t *ctx, int flags, void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux ); /* Invoked before open_box(), this informs the driver which box is to be opened. */ int (*select_box)( store_t *ctx, const char *name ); /* Get the selected box' on-disk path, if applicable, null otherwise. */ const char *(*get_box_path)( store_t *ctx ); /* Create the selected mailbox. */ void (*create_box)( store_t *ctx, void (*cb)( int sts, void *aux ), void *aux ); /* Open the selected mailbox. * Note that this should not directly complain about failure to open. */ void (*open_box)( store_t *ctx, void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux ); /* Return the minimal UID the next stored message will have. */ uint (*get_uidnext)( store_t *ctx ); /* Return the flags that can be stored in the selected mailbox. */ xint (*get_supported_flags)( store_t *ctx ); /* Confirm that the open mailbox is empty. */ int (*confirm_box_empty)( store_t *ctx ); /* Delete the open mailbox. The mailbox is expected to be empty. * Subfolders of the mailbox are *not* deleted. * Some artifacts of the mailbox may remain, but they won't be * recognized as a mailbox any more. */ void (*delete_box)( store_t *ctx, void (*cb)( int sts, void *aux ), void *aux ); /* Remove the last artifacts of the open mailbox, as far as possible. */ int (*finish_delete_box)( store_t *ctx ); /* Invoked before load_box(), this informs the driver which operations (OP_*) * will be performed on the mailbox. The driver may extend the set by implicitly * needed or available operations. Returns this possibly extended set. */ xint (*prepare_load_box)( store_t *ctx, xint opts ); /* Load the message attributes needed to perform the requested operations. * Consider only messages with UIDs between minuid and maxuid (inclusive) * and those named in the excs array (smaller than minuid). * The driver takes ownership of the excs array. * Messages starting with finduid need to have the TUID populated when OPEN_FIND is set. * Messages up to pairuid need to have the Message-Id populated when OPEN_PAIRED_IDS is set. * Messages up to newuid need to have the size populated when OPEN_OLD_SIZE is set; * likewise messages above newuid when OPEN_NEW_SIZE is set. * The returned message list remains owned by the driver and remains valid * until a new box is selected or the store is freed. New messages within * the specified range may be added to the list as a result of invoking * other driver functions. */ void (*load_box)( store_t *ctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs, void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux ); /* Fetch the contents and flags of the given message from the current mailbox. * If minimal is non-zero, fetch only a placeholder for the requested message - * ideally, this is precisely the header, but it may be more. */ void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data, int minimal, void (*cb)( int sts, void *aux ), void *aux ); /* Store the given message to either the current mailbox or the trash folder. * If the new copy's UID can be immediately determined, return it, otherwise 0. */ void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash, void (*cb)( int sts, uint uid, void *aux ), void *aux ); /* Index the messages which have newly appeared in the mailbox, including their * temporary UID headers. This is needed if store_msg() does not guarantee returning * a UID; otherwise the driver needs to implement only the OPEN_FIND flag. * The returned message list remains owned by the driver. */ void (*find_new_msgs)( store_t *ctx, uint newuid, void (*cb)( int sts, message_t *msgs, void *aux ), void *aux ); /* Add/remove the named flags to/from the given message. The message may be either * a pre-fetched one (in which case the in-memory representation is updated), * or it may be identified by UID only. * The operation may be delayed until commit_cmds() is called. */ void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del, void (*cb)( int sts, void *aux ), void *aux ); /* Move the given message from the current mailbox to the trash folder. * This may expunge the original message immediately, but it needn't to. */ void (*trash_msg)( store_t *ctx, message_t *msg, void (*cb)( int sts, void *aux ), void *aux ); /* Expunge deleted messages from the current mailbox and close it. * There is no need to explicitly close a mailbox if no expunge is needed. */ // If reported is true, the expunge callback was called reliably. void (*close_box)( store_t *ctx, void (*cb)( int sts, int reported, void *aux ), void *aux ); /* Cancel queued commands which are not in flight yet; they will have their * callbacks invoked with DRV_CANCELED. Afterwards, wait for the completion of * the in-flight commands. If the store is canceled before this command completes, * the callback will *not* be invoked. */ void (*cancel_cmds)( store_t *ctx, void (*cb)( void *aux ), void *aux ); /* Get approximate amount of memory occupied by the driver. */ uint (*get_memory_usage)( store_t *ctx ); /* Get the FAIL_* state of the driver. */ int (*get_fail_state)( store_conf_t *conf ); }; void free_generic_messages( message_t * ); void parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type ); store_t *proxy_alloc_store( store_t *real_ctx, const char *label, int force_async ); #define N_DRIVERS 2 extern driver_t *drivers[N_DRIVERS]; extern driver_t maildir_driver, imap_driver, proxy_driver; void cleanup_drivers( void ); #endif isync-1.5.1/src/tst_imap_utf7.c0000644000175000001440000000544014405565305012052 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later // // isync test suite // #include "imap_p.h" static struct { const char *utf8, *utf7; } data[] = { { u8"", "" }, { u8"1", "1" }, { u8"word", "word" }, { u8"&", "&-" }, { NULL, "&" }, { NULL, "&-&" }, { u8"&&", "&-&-" }, { u8"1&1", "1&-1" }, { u8"&1&", "&-1&-" }, { u8"\t", "&AAk-" }, { NULL, "&AAk" }, { NULL, "&AA-" }, { NULL, "&*Ak-" }, { NULL, "&&-" }, { u8"m\x7f""ll", "m&AH8-ll" }, { u8"\t&", "&AAk-&-" }, { u8"\t&\t", "&AAk-&-&AAk-" }, { u8"&\t", "&-&AAk-" }, { u8"&\t&", "&-&AAk-&-" }, { u8"ä", "&AOQ-" }, { u8"\x83\x84", NULL }, { u8"\xc3\xc4", NULL }, { u8"\xc3", NULL }, { u8"äö", "&AOQA9g-" }, { u8"äöü", "&AOQA9gD8-" }, { u8"Ḁ", "&HgA-" }, { u8"\xe1\xc8\x80", NULL }, { u8"\xe1\xb8\xf0", NULL }, { u8"\xe1\xb8", NULL }, { u8"\xe1", NULL }, { u8"Ḁḁ", "&HgAeAQ-" }, { u8"😂", "&2D3eAg-" }, { u8"\xf8\x9f\x98\x82", NULL }, { u8"\xf0\xcf\x98\x82", NULL }, { u8"\xf0\x9f\xd8\x82", NULL }, { u8"\xf0\x9f\x98\xe2", NULL }, { u8"\xf0\x9f\x98", NULL }, { u8"\xf0\x9f", NULL }, { u8"\xf0", NULL }, { NULL, "&2D0-" }, { u8"😈😎", "&2D3eCNg93g4-" }, { u8"müll", "m&APw-ll" }, { u8"mü", "m&APw-" }, { u8"über", "&APw-ber" }, }; int main( void ) { int ret = 0; for (uint i = 0; i < as(data); i++) { if (!data[i].utf8) continue; xprintf( "To UTF-7 \"%s\" (\"%!s\") ...\n", data[i].utf8, data[i].utf8 ); char *utf7 = imap_utf8_to_utf7( data[i].utf8 ); if (utf7) { if (!data[i].utf7) { xprintf( "Unexpected success: \"%s\" (\"%!s\")\n", utf7, utf7 ); ret = 1; } else if (strcmp( utf7, data[i].utf7 )) { xprintf( "Mismatch, got \"%s\" (\"%!s\"), want \"%!s\"\n", utf7, utf7, data[i].utf7 ); ret = 1; } free( utf7 ); } else { if (data[i].utf7) { xprintf( "Conversion failure.\n" ); ret = 1; } } } for (uint i = 0; i < as(data); i++) { if (!data[i].utf7) continue; xprintf( "From UTF-7 \"%!s\" ...\n", data[i].utf7 ); int utf7len = strlen( data[i].utf7 ); char utf8buf[1000]; int utf8len = imap_utf7_to_utf8( data[i].utf7, utf7len, utf8buf ); if (utf8len >= 0) { if (!data[i].utf8) { xprintf( "Unexpected success: \"%.*s\" (\"%.*!s\")\n", utf8len, utf8buf, utf8len, utf8buf ); ret = 1; } else { int wantlen = strlen( data[i].utf8 ); if (utf8len != wantlen || memcmp( utf8buf, data[i].utf8, utf8len )) { xprintf( "Mismatch, got \"%.*s\" (\"%.*!s\"), want \"%s\" (\"%!s\")\n", utf8len, utf8buf, utf8len, utf8buf, data[i].utf8, data[i].utf8 ); ret = 1; } } assert( utf8len < utf7len * 9 / 8 + 1 ); } else { if (data[i].utf8) { xprintf( "Conversion failure.\n" ); ret = 1; } } } return ret; } isync-1.5.1/src/run-tests.pl0000755000175000001440000013031614660617522011430 #! /usr/bin/perl -w # # SPDX-FileCopyrightText: 2006-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later # use warnings; use strict; use Carp; $SIG{__WARN__} = \&Carp::cluck; $SIG{__DIE__} = \&Carp::confess; use Cwd; use Clone 'clone'; use File::Path; use File::Temp 'tempdir'; my $use_vg = $ENV{USE_VALGRIND}; my $use_st = $ENV{USE_STRACE}; my $mbsync = getcwd()."/mbsync"; my (@match, $start); for my $arg (@ARGV) { if ($arg eq "+") { $start = 1; } else { push @match, $arg; } } die("Need exactly one test name when using start syntax.\n") if ($start && (@match != 1)); if (!-d "tmp") { unlink "tmp"; my $tdir = tempdir(); symlink $tdir, "tmp" or die "Cannot symlink temp directory: $!\n"; } chdir "tmp" or die "Cannot enter temp directory.\n"; use enum qw(:=1 A..Z); sub mn($) { my ($n) = @_; $n == 0 ? "0" : chr(64 + $n) } sub mf($) { my ($f) = @_; length($f) ? $f : '-' } my $sync_flags = "<>^~DFPRST"; my $msg_flags = "DFPRST*?"; sub process_flag_add($$$$$$) { my ($flgr, $add, $ok_flags, $num, $e, $what) = @_; return if ($add eq ""); for my $flg (split('', $add)) { die("Adding invalid flag '$flg' for $what ".mn($num)." (at $e).\n") if (index($ok_flags, $flg) < 0); my $i = index($$flgr, $flg); die("Adding duplicate flag '$flg' to $what ".mn($num)." (at $e).\n") if ($i >= 0); $$flgr .= $flg; } $$flgr = $ok_flags =~ s/[^$$flgr]//gr; # sort } sub process_flag_del($$$$$) { my ($flgr, $del, $num, $e, $what) = @_; for my $flg (split('', $del)) { my $i = index($$flgr, $flg); die("Removing absent flag '$flg' from $what ".mn($num)." (at $e).\n") if ($i < 0); substr($$flgr, $i, 1) = ''; } } sub process_flag_update($$$$$$$) { my ($flgr, $add, $del, $ok_flags, $num, $e, $what) = @_; process_flag_del($flgr, $del, $num, $e, $what); process_flag_add($flgr, $add, $ok_flags, $num, $e, $what); } sub parse_flags($$$$$) { my ($rflg, $ok_flags, $num, $e, $what) = @_; my $add = $$rflg; $$rflg = ""; process_flag_add($rflg, $add, $ok_flags, $num, $e, $what); } sub parse_flag_update($) { my ($stsr) = @_; my ($add, $del) = ("", ""); while ($$stsr =~ s,^([-+])([^-+]+),,) { if ($1 eq "+") { $add .= $2; } else { $del .= $2; } } return ($add, $del); } # Returns UID. sub create_msg($$$$$) { my ($num, $flg, $bs, $t, $e) = @_; my ($mur, $msr, $n2ur) = (\$$bs{max_uid}, $$bs{messages}, $$bs{num2uid}); $$mur++; if ($flg ne "_") { parse_flags(\$flg, $msg_flags, $num, $e, "$t side"); $$msr{$$mur} = [ $num, $flg ]; } push @{$$n2ur{$num}}, $$mur; return $$mur; } # Returns old UID, new UID. sub parse_msg($$$$$) { my ($num, $sts, $cs, $t, $e) = @_; my $bs = $$cs{$t}; my ($msr, $n2ur) = ($$bs{messages}, $$bs{num2uid}); $$cs{"${t}_trash"}{$num} = 1 if ($sts =~ s,^#,,); my $ouid; my $uids = \@{$$n2ur{$num}}; if ($sts =~ s,^&$,,) { $ouid = 0; } elsif ($sts =~ s,^&(\d+),,) { my $n = int($1); die("Referencing unrecognized instance $n of message ".mn($num)." on $t side (at $e).\n") if (!$n || $n > @$uids); $ouid = $$uids[$n - 1]; } else { $ouid = @$uids ? $$uids[-1] : 0; } my $nuid = ($sts =~ s,^\|,,) ? 0 : $ouid; if ($sts eq "_" or $sts =~ s,^\*,,) { die("Adding already present message ".mn($num)." on $t side (at $e).\n") if (defined($$msr{$ouid})); $nuid = create_msg($num, $sts, $bs, $t, $e); } elsif ($sts =~ s,^\^,,) { die("Duplicating absent message ".mn($num)." on $t side (at $e).\n") if (!defined($$msr{$ouid})); $nuid = create_msg($num, $sts, $bs, $t, $e); } elsif ($sts eq "/") { # Note that we don't delete $$n2ur{$num}, as state entries may # refer to expunged messages. Subject re-use is not supported here. die("Deleting absent message ".mn($num)." from $t side (at $e).\n") if (!delete $$msr{$ouid}); $nuid = 0; } elsif ($sts ne "") { my ($add, $del) = parse_flag_update(\$sts); die("Unrecognized message command '$sts' for $t side (at $e).\n") if ($sts ne ""); die("No message ".mn($num)." present on $t side (at $e).\n") if (!defined($$msr{$ouid})); process_flag_update(\$$msr{$ouid}[1], $add, $del, $msg_flags, $num, $e, "$t side"); } return $ouid, $nuid; } # Returns UID. sub resolv_msg($$$) { my ($num, $cs, $t) = @_; return 0 if (!$num); my $uids = \@{$$cs{$t}{num2uid}{$num}}; die("No message ".mn($num)." present on $t side (in header).\n") if (!@$uids); return $$uids[-1]; } # Returns index, or undef if not found. sub find_ent($$$) { my ($fu, $nu, $ents) = @_; for (my $i = 0; $i < @$ents; $i++) { my $ent = $$ents[$i]; return $i if ($$ent[0] == $fu and $$ent[1] == $nu); } return undef; } sub find_ent_chk($$$$$) { my ($fu, $nu, $cs, $num, $e) = @_; my $enti = find_ent($fu, $nu, $$cs{state}{entries}); die("No state entry $fu:$nu present for ".mn($num)." (at $e).\n") if (!defined($enti)); return $enti; } sub parse_chan($;$) { my ($ics, $ref) = @_; my $cs; if ($ref) { $cs = clone($ref); } else { $cs = { # messages: { uid => [ subject, flags ], ... } far => { max_uid => 0, messages => {}, num2uid => {} }, near => { max_uid => 0, messages => {}, num2uid => {} }, # trashed messages: { subject => is_placeholder, ... } far_trash => { }, near_trash => { }, # entries: [ [ far_uid, near_uid, flags ], ... ] state => { entries => [] } }; } my $ss = $$cs{state}; my $ents = $$ss{entries}; my $enti; for (my ($i, $e) = (3, 1); $i < @$ics; $i += 4, $e++) { my ($num, $far, $sts, $near) = @{$ics}[$i .. $i+3]; my ($ofu, $nfu) = parse_msg($num, $far, $cs, "far", $e); my ($onu, $nnu) = parse_msg($num, $near, $cs, "near", $e); if ($sts =~ s,^\*,,) { $enti = find_ent($nfu, $nnu, $ents); die("State entry $nfu:$nnu already present for ".mn($num)." (at $e).\n") if (defined($enti)); parse_flags(\$sts, $sync_flags, $num, $e, "sync entry"); push @$ents, [ $nfu, $nnu, $sts ]; } elsif ($sts =~ s,^\^,,) { die("No current state entry for ".mn($num)." (at $e).\n") if (!defined($enti)); parse_flags(\$sts, $sync_flags, $num, $e, "sync entry"); splice @$ents, $enti++, 0, ([ $nfu, $nnu, $sts ]); } elsif ($sts eq "/") { $enti = find_ent_chk($ofu, $onu, $cs, $num, $e); splice @$ents, $enti, 1; } elsif ($sts ne "") { my $t = -1; if ($sts =~ s,^<,,) { $t = 0; } elsif ($sts =~ s,^>,,) { $t = 1; } my ($add, $del) = parse_flag_update(\$sts); die("Unrecognized state command '".$sts."' for ".mn($num)." (at $e).\n") if ($sts ne ""); $enti = find_ent_chk($ofu, $onu, $cs, $num, $e); my $ent = $$ents[$enti++]; process_flag_update(\$$ent[2], $add, $del, $sync_flags, $num, $e, "sync entry"); if ($t >= 0) { my $uid = $t ? $nnu : $nfu; $$ent[$t] = ($uid && $$cs{$t ? "near" : "far"}{messages}{$uid}) ? $uid : 0; } } else { $enti = undef; } } $$ss{max_pulled} = resolv_msg($$ics[0], $cs, "far"); $$ss{max_expired_far} = resolv_msg($$ics[1], $cs, "far"); $$ss{max_pushed} = resolv_msg($$ics[2], $cs, "near"); $$ss{max_expired_near} = 0; return $cs; } sub flip_chan($) { my ($cs) = @_; ($$cs{far}, $$cs{near}) = ($$cs{near}, $$cs{far}); ($$cs{far_trash}, $$cs{near_trash}) = ($$cs{near_trash}, $$cs{far_trash}); my $ss = $$cs{state}; ($$ss{max_pulled}, $$ss{max_pushed}) = ($$ss{max_pushed}, $$ss{max_pulled}); ($$ss{max_expired_far}, $$ss{max_expired_near}) = ($$ss{max_expired_near}, $$ss{max_expired_far}); for my $ent (@{$$ss{entries}}) { ($$ent[0], $$ent[1]) = ($$ent[1], $$ent[0]); $$ent[2] =~ tr/<>/>", ".mbsyncrc") or die "Cannot open .mbsyncrc.\n"; print FILE "FSync no MaildirStore far Path \"\" Inbox far ".$$sfx[0]." MaildirStore near Path \"\" Inbox near ".$$sfx[1]." Channel test Far :far: Near :near: SyncState * ".$$sfx[2]; close FILE; } sub killcfg() { unlink $_ for (glob("*.log")); unlink ".mbsyncrc"; } # $run_async, $mbsync_options, $log_file # Return: $exit_code, \@mbsync_output sub runsync($$$) { my ($async, $flags, $file) = @_; my $cmd; if ($use_vg) { $cmd = "valgrind -q --error-exitcode=1 "; } elsif ($use_st) { $cmd = "strace "; } else { $flags .= " -D"; } if ($async == 2) { $flags .= " -TA"; } elsif ($async == 1) { $flags .= " -Ta"; } $cmd .= "$mbsync -Tz $flags -c .mbsyncrc test"; open FILE, "$cmd 2>&1 |"; my @out = ; close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** mbsync exited with signal ".($?&127).", code ".($?>>8)."\n"); if ($file) { open FILE, ">$file" or die("Cannot create $file: $!\n"); print FILE @out; close FILE; } return $?, \@out; } use constant CHOMP => 1; sub readfile($;$) { my ($file, $chomp) = @_; open(FILE, $file) or return; my @nj = ; close FILE; chomp(@nj) if ($chomp); return \@nj; } # $path sub readbox_impl($$) { my ($bn, $cb) = @_; (-d $bn."/tmp" and -d $bn."/new" and -d $bn."/cur") or die "Invalid mailbox '$bn'.\n"; for my $d ("cur", "new") { opendir(DIR, $bn."/".$d) or next; for my $f (grep(!/^\.\.?$/, readdir(DIR))) { open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n"; my ($sz, $num, $ph) = (0); while () { /^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = int($2)); $sz += length($_); } close FILE; if (!defined($num)) { print STDERR "message '$f' in '$bn' has no identifier.\n"; exit 1; } $cb->($num, $ph, $sz, $f); } } } # $path sub readbox($) { my $bn = shift; (-d $bn) or die "No mailbox '$bn'.\n"; my %ms; readbox_impl($bn, sub { my ($num, $ph, $sz, $f) = @_; if ($f !~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) { print STDERR "unrecognided file name '$f' in '$bn'.\n"; exit 1; } my ($uid, $flg) = (int($1), $2); @{$ms{$uid}} = ($num, $flg.($sz > 1000 ? "*" : "").($ph ? "?" : "")); }); my $uidval = readfile($bn."/.uidvalidity", CHOMP); die "Cannot read UID validity of mailbox '$bn': $!\n" if (!$uidval); my $mu = $$uidval[1]; return { max_uid => $mu, messages => \%ms }; } # $path sub readtrash($) { my $bn = shift; (-d $bn) or return {}; my %ms; readbox_impl($bn, sub { my ($num, $ph, undef, undef) = @_; $ms{$num} = $ph; }); return \%ms; } # \%fallback_sync_state sub readstate(;$) { my ($fbss) = @_; my $fn = "near/.mbsyncstate"; if ($fbss) { $fn .= ".new"; return $fbss if (!-s $fn); } my $ls = readfile($fn, CHOMP); if (!$ls) { print STDERR "Cannot read sync state $fn: $!\n"; return; } my @ents; my %ss = ( max_pulled => 0, max_expired_far => 0, max_pushed => 0, max_expired_near => 0, entries => \@ents ); my ($far_val, $near_val) = (0, 0); my %hdr = ( 'FarUidValidity' => \$far_val, 'NearUidValidity' => \$near_val, 'MaxPulledUid' => \$ss{max_pulled}, 'MaxPushedUid' => \$ss{max_pushed}, 'MaxExpiredFarUid' => \$ss{max_expired_far}, 'MaxExpiredNearUid' => \$ss{max_expired_near} ); OUTER: while (1) { while (@$ls) { $_ = shift(@$ls); last OUTER if (!length($_)); if (!/^([^ ]+) (\d+)$/) { print STDERR "Malformed sync state header entry: $_\n"; return; } my $want = delete $hdr{$1}; if (!defined($want)) { print STDERR "Unexpected sync state header entry: $1\n"; return; } $$want = int($2); } print STDERR "Unterminated sync state header.\n"; return; } delete $hdr{'MaxExpiredFarUid'}; # optional field delete $hdr{'MaxExpiredNearUid'}; # ditto my @ky = keys %hdr; if (@ky) { print STDERR "Keys missing from sync state header: @ky\n"; return; } if ($far_val ne '1' or $near_val ne '1') { print STDERR "Unexpected UID validity $far_val $near_val (instead of 1 1)\n"; return; } for (@$ls) { if (!/^(\d+) (\d+) (.*)$/) { print STDERR "Malformed sync state entry: $_\n"; return; } push @ents, [ int($1), int($2), $3 ]; } return \%ss; } # \%fallback_sync_state sub readchan(;$) { my ($fbss) = @_; return { far => readbox("far"), near => readbox("near"), far_trash => readtrash("far_trash"), near_trash => readtrash("near_trash"), state => readstate($fbss) }; } # $box_name, \%box_state sub mkbox($$) { my ($bn, $bs) = @_; rmtree($bn); (mkdir($bn) and mkdir($bn."/tmp") and mkdir($bn."/new") and mkdir($bn."/cur")) or die "Cannot create mailbox $bn.\n"; open(FILE, ">", $bn."/.uidvalidity") or die "Cannot create UID validity for mailbox $bn.\n"; print FILE "1\n$$bs{max_uid}\n"; close FILE; my $ms = $$bs{messages}; for my $uid (keys %$ms) { my ($num, $flg) = @{$$ms{$uid}}; my $big = $flg =~ s/\*//; my $ph = $flg =~ s/\?//; open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local,U=".$uid.":2,".$flg) or die "Cannot create message ".mn($num)." in mailbox $bn.\n"; print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: ".($ph?"[placeholder] ":"").$num."\n\n".(("A"x50)."\n")x($big*30); close FILE; } } # \%sync_state sub mkstate($) { my ($ss) = @_; open(FILE, ">", "near/.mbsyncstate") or die "Cannot create sync state.\n"; print FILE "FarUidValidity 1\nMaxPulledUid ".$$ss{max_pulled}."\n". "NearUidValidity 1\nMaxPushedUid ".$$ss{max_pushed}."\n"; print FILE "MaxExpiredFarUid ".$$ss{max_expired_far}."\n" if ($$ss{max_expired_far}); print FILE "MaxExpiredNearUid ".$$ss{max_expired_near}."\n" if ($$ss{max_expired_near}); print FILE "\n"; for my $ent (@{$$ss{entries}}) { print FILE $$ent[0]." ".$$ent[1]." ".$$ent[2]."\n"; } close FILE; } # \%chan_state sub mkchan($) { my ($cs) = @_; mkbox("far", $$cs{far}); mkbox("near", $$cs{near}); rmtree("far_trash"); rmtree("near_trash"); mkstate($$cs{state}); } # $box_name, \%actual_box_state, \%reference_box_state sub cmpbox($$$) { my ($bn, $bs, $ref_bs) = @_; my $ret = 0; my ($ref_mu, $ref_ms) = ($$ref_bs{max_uid}, $$ref_bs{messages}); my ($mu, $ms) = ($$bs{max_uid}, $$bs{messages}); if ($mu != $ref_mu) { print STDERR "MAXUID mismatch for '$bn': got $mu, wanted $ref_mu\n"; $ret = 1; } for my $uid (sort { $a <=> $b } keys %$ref_ms) { my ($num, $flg) = @{$$ref_ms{$uid}}; my $m = $$ms{$uid}; if (!defined $m) { print STDERR "Missing message $bn:$uid:".mn($num)."\n"; $ret = 1; next; } if ($$m[0] != $num) { print STDERR "Subject mismatch for $bn:$uid:". " got ".mn($$m[0]).", wanted ".mn($num)."\n"; return 1; } if ($$m[1] ne $flg) { print STDERR "Flag mismatch for $bn:$uid:".mn($num).":". " got ".mf($$m[1]).", wanted ".mf($flg)."\n"; $ret = 1; } } for my $uid (sort { $a <=> $b } keys %$ms) { if (!defined($$ref_ms{$uid})) { print STDERR "Excess message $bn:$uid:".mn($$ms{$uid}[0])."\n"; $ret = 1; } } return $ret; } # $box_name, \%actual_box_state, \%reference_box_state sub cmptrash($$$) { my ($bn, $ms, $ref_ms) = @_; my $ret = 0; for my $num (sort { $a <=> $b } keys %$ref_ms) { my $ph = $$ms{$num}; if (!defined($ph)) { print STDERR "Missing message $bn:".mn($num)."\n"; $ret = 1; next; } if ($ph) { print STDERR "Message $bn:".mn($num)." is placeholder\n"; $ret = 1; } } for my $num (sort { $a <=> $b } keys %$ms) { if (!defined($$ref_ms{$num})) { print STDERR "Excess message $bn:".mn($num).($$ms{$num} ? " (is placeholder)" : "")."\n"; $ret = 1; } } return $ret; } sub mapmsg($$) { my ($uid, $bs) = @_; if ($uid) { if (my $msg = $$bs{messages}{$uid}) { return $$msg[0]; } } return 0; } # \%actual_chan_state, \%reference_chan_state sub cmpstate($$) { my ($cs, $ref_cs) = @_; my ($ss, $fbs, $nbs) = ($$cs{state}, $$cs{far}, $$cs{near}); return 1 if (!$ss); my ($ref_ss, $ref_fbs, $ref_nbs) = ($$ref_cs{state}, $$ref_cs{far}, $$ref_cs{near}); return 0 if ($ss == $ref_ss); my $ret = 0; for my $h (['MaxPulledUid', 'max_pulled'], ['MaxExpiredFarUid', 'max_expired_far'], ['MaxPushedUid', 'max_pushed'], ['MaxExpiredNearUid', 'max_expired_near']) { my ($hn, $sn) = @$h; my ($got, $want) = ($$ss{$sn}, $$ref_ss{$sn}); if ($got != $want) { print STDERR "Sync state header entry $hn mismatch: got $got, wanted $want\n"; $ret = 1; } } my $ref_ents = $$ref_ss{entries}; my $ents = $$ss{entries}; for (my $i = 0; $i < @$ents || $i < @$ref_ents; $i++) { my ($ent, $fuid, $nuid, $num); if ($i < @$ents) { $ent = $$ents[$i]; ($fuid, $nuid) = ($$ent[0], $$ent[1]); my ($fnum, $nnum) = (mapmsg($fuid, $fbs), mapmsg($nuid, $nbs)); if ($fnum && $nnum && $fnum != $nnum) { print STDERR "Invalid sync state entry $fuid:$nuid:". " mismatched subjects (".mn($fnum).":".mn($nnum).")\n"; return 1; } $num = $fnum || $nnum; } if ($i == @$ref_ents) { print STDERR "Excess sync state entry $fuid:$nuid (".mn($num).")\n"; return 1; } my $rent = $$ref_ents[$i]; my ($rfuid, $rnuid) = ($$rent[0], $$rent[1]); my $rnum = mapmsg($rfuid, $ref_fbs) || mapmsg($rnuid, $ref_nbs); if ($i == @$ents) { print STDERR "Missing sync state entry $rfuid:$rnuid (".mn($rnum).")\n"; return 1; } if ($fuid != $rfuid || $nuid != $rnuid || $num != $rnum) { print STDERR "Unexpected sync state entry:". " got $fuid:$nuid (".mn($num)."), wanted $rfuid:$rnuid (".mn($rnum).")\n"; return 1; } if ($$ent[2] ne $$rent[2]) { print STDERR "Flag mismatch in sync state entry $fuid:$nuid (".mn($rnum)."):". " got ".mf($$ent[2]).", wanted ".mf($$rent[2])."\n"; $ret = 1; } } return $ret; } # \%actual_chan_state, \%reference_chan_state sub cmpchan($$) { my ($cs, $ref_cs) = @_; my $rslt = 0; $rslt |= cmpbox("far", $$cs{far}, $$ref_cs{far}); $rslt |= cmpbox("near", $$cs{near}, $$ref_cs{near}); $rslt |= cmptrash("far_trash", $$cs{far_trash}, $$ref_cs{far_trash}); $rslt |= cmptrash("near_trash", $$cs{near_trash}, $$ref_cs{near_trash}); $rslt |= cmpstate($cs, $ref_cs); return $rslt; } # \%box_state sub printbox($) { my ($bs) = @_; my ($mu, $ms) = ($$bs{max_uid}, $$bs{messages}); print " [ $mu,\n "; my $frst = 1; for my $uid (sort { $a <=> $b } keys %$ms) { my ($num, $flg) = @{$$ms{$uid}}; if ($frst) { $frst = 0; } else { print ", "; } print mn($num).", ".$uid.", \"".$flg."\""; } print " ],\n"; } # \%sync_state sub printstate($) { my ($ss) = @_; return if (!$ss); print " [ ".$$ss{max_pulled}.", ".$$ss{max_expired_far}.", ". $$ss{max_pushed}.", ".$$ss{max_expired_near}.",\n "; my $frst = 1; for my $ent (@{$$ss{entries}}) { if ($frst) { $frst = 0; } else { print ", "; } print(($$ent[0] // "??").", ".($$ent[1] // "??").", \"".($$ent[2] // "??")."\""); } print " ],\n"; } # \%chan_state sub printchan($) { my ($cs) = @_; printbox($$cs{far}); printbox($$cs{near}); printstate($$cs{state}); } # $run_async, \%source_state, \%target_state, \@channel_configs sub test_impl($$$$) { my ($async, $sx, $tx, $sfx) = @_; mkchan($sx); my ($xopt, $xsopt) = @$sfx > 3 ? ($$sfx[3], " ".$$sfx[3]) : ("", ""); my ($xc, $ret) = runsync($async, "-Tj -TJ".$xsopt, "1-initial.log"); my $rtx = readchan($$sx{state}) if (!$xc); if ($xc || cmpchan($rtx, $tx)) { print "Input:\n"; printchan($sx); print "Options:\n"; print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; if (!$xc) { print "Expected result:\n"; printchan($tx); print "Actual result:\n"; printchan($rtx); } print "Debug output:\n"; print @$ret; exit 1; } my ($nj, $njl, $nje) = (undef, 0, 0); if ($$rtx{state} != $$sx{state}) { $nj = readfile("near/.mbsyncstate.journal"); STEPS: { for (reverse @$ret) { if (/^### (\d+) steps, (\d+) entries ###$/) { $njl = int($1) - 1; $nje = int($2); last STEPS; } } die("Cannot extract step count.\n"); } my ($jxc, $jret) = runsync($async, "-0 --no-expunge".$xsopt, "2-replay.log"); my $jrcs = readstate() if (!$jxc); if ($jxc || cmpstate({ far => $$tx{far}, near => $$tx{near}, state => $jrcs }, $tx)) { print "Journal replay failed.\n"; print "Options:\n"; print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\n"; print "Old State:\n"; printstate($$sx{state}); print "Journal:\n".join("", @$nj)."\n"; if (!$jxc) { print "Expected New State:\n"; printstate($$tx{state}); print "New State:\n"; printstate($jrcs); } print "Debug output:\n"; print @$jret; exit 1; } } my ($ixc, $iret) = runsync($async, $xopt, "3-verify.log"); my $irtx = readchan() if (!$ixc); if ($ixc || cmpchan($irtx, $tx)) { print "Idempotence verification run failed.\n"; print "Input == Expected result:\n"; printchan($tx); print "Options:\n"; print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; if (!$ixc) { print "Actual result:\n"; printchan($irtx); } print "Debug output:\n"; print @$iret; exit 1; } rmtree "near"; rmtree "far"; rmtree "near_trash"; rmtree "far_trash"; for (my $l = 1; $l <= $njl; $l++) { mkchan($sx); my ($nxc, $nret) = runsync($async, "-Ts$l".$xsopt, "4-interrupt.log"); if ($nxc != 100 << 8) { print "Interrupting at step $l/$njl failed.\n"; print "Debug output:\n"; print @$nret; exit 1; } my $pnnj = readfile("near/.mbsyncstate.journal"); ($nxc, $nret) = runsync($async, "-Tj".$xsopt, "5-resume.log"); my $nrtx = readchan($$sx{state}) if (!$nxc); if ($nxc || cmpchan($nrtx, $tx)) { print "Resuming from step $l/$njl failed.\n"; print "Input:\n"; printchan($sx); print "Options:\n"; print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; my $nnj = readfile("near/.mbsyncstate.journal"); my $ln = $#$pnnj; print "Journal:\n".join("", @$nnj[0..$ln])."-------\n".join("", @$nnj[($ln + 1)..$#$nnj])."\n"; print "Full journal:\n".join("", @$nj[0..$nje])."=======\n".join("", @$nj[($nje + 1)..$#$nj])."\n"; if (!$nxc) { print "Expected result:\n"; printchan($tx); print "Actual result:\n"; printchan($nrtx); } print "Debug output:\n"; print @$nret; exit 1; } rmtree "near"; rmtree "far"; rmtree "near_trash"; rmtree "far_trash"; } } # $title, \@source_state, \@target_state, \@channel_configs, $flip_sides sub test($$$$;$) { my ($ttl, $isx, $itx, $sfx, $flip) = @_; if (@match) { if ($start) { return if (index($ttl, $match[0]) < 0); @match = (); } else { return if (!grep { index($ttl, $_) >= 0 } @match); } } print "Testing: ".$ttl." ...\n"; # We don't flip the Store configs, as inverting the Channel config # would be unreasonably complex. writecfg($sfx); my $sx = parse_chan($isx); my $tx = parse_chan($itx, $sx); if ($flip) { flip_chan($sx); flip_chan($tx); } test_impl(0, $sx, $tx, $sfx); test_impl(1, $sx, $tx, $sfx); test_impl(2, $sx, $tx, $sfx); killcfg(); } ################################################################################ # Format of the test defs: # ( max_pulled, max_expired, max_pushed, { subject, far, state, near }... ) # Everything is a delta; for the input, the reference is the empty state. # Common commands: # *f => create with flags, appending at end of list # / => destroy # -f, +f => remove/add flags # Far/near: # Special commands: # _ => create phantom message (reserve UID for expunged message) # ^f => create with flags, duplicating the subject # # => create in trash; deletion may follow # | => use zero UID for state modification, even if msg exists; cmd may follow # & => use zero UID for state identification, even if message exists # &n => use UID of n'th occurrence of subject for state id; command may follow # Special flag suffixes: # * => big # ? => placeholder # State: # Special commands: # <, > => update far/near message link; flag updates may follow # ^f => create with flags, appending right after last command's entry # Special flag prefixes as in actual state file. # Generic syncing tests my @x01 = ( I, 0, I, P, "_", "*", "_", R, "*", "", "", # Skipped/failed messages to prevent maxuid topping S, "", "", "*", A, "*F", "*", "*", B, "*", "*", "*F", C, "*FS", "*", "*F", D, "*", "*", "*", E, "*T", "*", "*", F, "*", "*", "*T", O, "*T", "*T", "*T", Q, "_", "*T", "*T", G, "*F", "*", "_", H, "*FT", "*", "*", I, "_", "*", "*", J, "*T", "", "", K, "*P", "", "", L, "", "", "*T", M, "", "", "*", ); my @O01 = ("", "", ""); my @X01 = ( M, 0, K, P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", "+T", "+T", F, "+T", "+T", "", Q, "", "<", "", G, "+T", ">", "", H, "", "+FT", "+FT", I, "", "<", "+T", L, "*T", "*T", "", M, "*", "*", "", J, "", "*T", "*T", K, "", "*P", "*P", ); test("full", \@x01, \@X01, \@O01); my @O01a = ("", "", "", "-Tu"); test("full (non-UIDPLUS)", \@x01, \@X01, \@O01a); my @O02 = ("", "", "Expunge Both\n"); my @X02 = ( M, 0, K, P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "/", "/", "/", F, "/", "/", "/", O, "/", "/", "/", Q, "", "/", "/", G, "/", "/", "", H, "/", "/", "/", I, "", "/", "/", J, "/", "", "", L, "", "", "/", M, "*", "*", "", K, "", "*P", "*P", ); test("full + expunge both", \@x01, \@X02, \@O02); my @O03 = ("", "", "Expunge Near\n"); my @X03 = ( M, 0, K, P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", ">+T", "/", F, "+T", ">+T", "/", O, "", ">", "/", Q, "", "/", "/", G, "+T", ">", "", H, "", ">+T", "/", I, "", "/", "/", L, "", "", "/", M, "*", "*", "", K, "", "*P", "*P", ); test("full + expunge near side", \@x01, \@X03, \@O03); my @O04 = ("", "", "Sync Pull\n"); my @X04 = ( K, 0, I, P, "", "/", "", # Only because test uses Maildir driver A, "", "+F", "+F", C, "", "+FS", "+S", E, "", "+T", "+T", Q, "", "<", "", H, "", "+FT", "+FT", I, "", "<", "+T", J, "", "*T", "*T", K, "", "*P", "*P", ); test("pull", \@x01, \@X04, \@O04); my @O05 = ("", "", "Sync Flags\n"); my @X05 = ( I, 0, I, P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", "+T", "+T", F, "+T", "+T", "", Q, "", "<", "", H, "", "+FT", "+FT", ); test("flags", \@x01, \@X05, \@O05); my @O06 = ("", "", "Sync Gone\n"); my @X06 = ( I, 0, I, P, "", "/", "", G, "+T", ">", "", Q, "", "<", "", I, "", "<", "+T", ); test("gone", \@x01, \@X06, \@O06); my @O07 = ("", "", "Sync New\n"); my @X07 = ( M, 0, K, L, "*T", "*T", "", M, "*", "*", "", J, "", "*T", "*T", K, "", "*P", "*P", ); test("new", \@x01, \@X07, \@O07); my @O08 = ("", "", "Sync PushFlags PullGone\n"); my @X08 = ( I, 0, I, P, "", "/", "", B, "+F", "+F", "", C, "", "+F", "", F, "+T", "+T", "", Q, "", "<", "", I, "", "<", "+T", ); test("push flags + pull gone", \@x01, \@X08, \@O08); my @O09 = ("", "", "Sync None\nExpunge Both\n"); my @X09 = ( I, 0, I, P, "", "/", "", E, "/", "", "", F, "", "", "/", O, "/", "/", "/", Q, "", "/", "/", H, "/", "", "", J, "/", "", "", L, "", "", "/", ); test("noop + expunge both", \@x01, \@X09, \@O09); my @O0A = ("", "", "Sync None\nExpunge Near\n"); # Sync entries are not updated/pruned, because the far side is not loaded. my @X0A = ( I, 0, I, F, "", "", "/", O, "", "", "/", Q, "", "", "/", L, "", "", "/", ); test("noop + expunge near side", \@x01, \@X0A, \@O0A); # Size restriction tests my @x20 = ( 0, 0, 0, A, "*", "", "", B, "*FPS*", "", "", C, "", "", "*FS*", ); my @O21 = ("MaxSize 1k\n", "MaxSize 1k\n", "Sync Full Old\nExpunge Near\n"); my @X21 = ( C, 0, B, C, "*S?", "*PS", "*PS?", ); test("max size", \@x20, \@X21, \@O21); my @x22 = ( L, 0, V, A, "*", "", "", B, "*PR*", "", "", V, "*FPR*", "", "", C, "*FPR?", "*DP", "*DP?", F, "*S*", "*>", "*?", G, "*S*", "*>S", "*?", H, "*S*", "*>S", "*S?", I, "*S*", "*>", "*S?", J, "**", "*>S", "*S?", K, "**", "*>", "*S?", L, "**", "*>S", "*?", A, "", "*", "*", B, "", "*>DP", "*DFP?", V, "", "*>DP", "*DFP?", ); my @X22 = ( S, 0, V, B, "", ">->D+R", "^PR*", B, "", "", "&1/", V, "", ">->D+FR", "^FPR*", V, "", "", "&1/", C, "^PR*", "<--^+>", "*?", C, "", ">-^+>P", "*P?", ); test("max size (pre-1.4 legacy)", \@x24, \@X24, \@O21); # Expiration tests my @x30 = ( 0, 0, 0, A, "*F", "", "", B, "*", "", "", C, "*S", "", "", D, "*", "", "", E, "*S", "", "", F, "*", "", "", ); my @O31 = ("", "", "MaxMessages 3\n"); my @X31 = ( F, C, F, A, "", "*F", "*F", B, "", "*", "*", D, "", "*", "*", E, "", "*S", "*S", F, "", "*", "*", ); test("max messages", \@x30, \@X31, \@O31); my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n"); my @X32 = ( F, C, F, A, "", "*F", "*F", D, "", "*", "*", E, "", "*S", "*S", F, "", "*", "*", ); test("max messages vs. unread", \@x30, \@X32, \@O32); my @x33 = ( G, D, G, A, "*F", "*F", "*F", B, "*", "*", "*S", C, "*S", "*", "*", D, "*S", "", "", E, "*", "*", "*S", F, "*S", "*S", "*S", G, "*", "*", "*S", H, "*", "", "", I, "*", "", "", ); my @X33 = ( I, F, I, B, "+S", "+~S", "+T", C, "", "+~S", "+ST", E, "+S", "+~S", "+T", F, "", "+~", "+T", G, "+S", "+S", "", H, "", "*", "*", I, "", "*", "*", ); test("max messages + expire - full", \@x33, \@X33, \@O31); my @O31a = ("", "", "MaxMessages 3\nExpireSide Far\n"); test("max messages + expire far - full", \@x33, \@X33, \@O31a, 1); my @O34 = ("", "", "Sync New\nMaxMessages 3\n"); my @X34 = ( I, F, I, B, "", "+~", "+T", E, "", "+~", "+T", F, "", "+~", "+T", H, "", "*", "*", I, "", "*", "*", ); test("max messages + expire - new", \@x33, \@X34, \@O34); my @x35 = ( B, 0, B, A, "*F", "*F", "*F", B, "*", "*", "*", C, "*", "", "", D, "*F", "", "", E, "*", "", "", F, "*", "", "", G, "*", "", "", H, "*F", "", "", ); my @O35 = ("", "", "Sync New\nMaxMessages 3\nExpireUnread yes\n"); my @X35 = ( H, E, H, B, "", "+~", "+T", D, "", "*F", "*F", F, "", "*", "*", G, "", "*", "*", H, "", "*F", "*F", ); test("max messages + expire - too many new", \@x35, \@X35, \@O35); my @x38 = ( F, C, 0, A, "*FS", "*FS", "*S", B, "*FS", "*~S", "*ST", C, "*S", "*~S", "_", D, "*", "*", "*", E, "*", "*", "*", F, "*", "*", "*", ); my @O38 = ("", "", "MaxMessages 3\nExpunge Both\n"); my @X38 = ( F, C, F, A, "-F", "/", "/", B, "", "-~+F", "-T+F", C, "", "/", "", ); test("max messages + expunge", \@x38, \@X38, \@O38); # Expiration with size restriction tests my @x40 = ( P, 0, P, A, "**", "*>", "*?", B, "**", "*>", "*S?", C, "**", "*>S", "*S?", D, "*S*", "*>S", "*S?", E, "*S*", "*>", "*S?", F, "*S*", "*>S", "*?", G, "*S*", "*>", "*?", H, "**", "*>S", "*?", I, "*F*", "*>", "*?", J, "*F*", "*>", "*S?", K, "*F*", "*>S", "*S?", L, "*FS*", "*>S", "*S?", M, "*FS*", "*>", "*S?", N, "*FS*", "*>S", "*?", O, "*FS*", "*>", "*?", P, "*F*", "*>S", "*?", Q, "**", "", "", R, "*FS*", "", "", S, "*S*", "", "", T, "**", "", "", U, "*FS*", "", "", V, "*S*", "", "", ); my @O41 = ("", "", "MaxSize 1k\nMaxMessages 3\nExpireUnread no\n"); my @X41 = ( V, S, V, C, "", "-S", "-S", D, "", "+~", "+T", E, "", "+~S", "+T", F, "-S", "-S", "", G, "", "+~S", "+ST", H, "", "-S", "", K, "", "-S", "-S", M, "", "+S", "", N, "-S", "-S", "", O, "", "+S", "+S", P, "", "-S", "", Q, "", "*>", "*?", R, "", "*>S", "*S?", T, "", "*>", "*?", U, "", "*>S", "*S?", V, "", "*>S", "*S?", ); test("max size without upgrade + max messages", \@x40, \@X41, \@O41); my @x42 = ( T, Q, T, A, "**", "*>", "*F?", B, "**", "*>", "*FS?", C, "**", "*>S", "*FS?", D, "*S*", "*>S", "*FS?", E, "*S*", "*>", "*FS?", F, "*S*", "*>S", "*F?", G, "*S*", "*>", "*F?", H, "**", "*>S", "*F?", I, "*F*", "*>", "*F?", J, "*F*", "*>", "*FS?", K, "*F*", "*>S", "*FS?", L, "*FS*", "*>S", "*FS?", M, "*FS*", "*>", "*FS?", N, "*FS*", "*>S", "*F?", O, "*FS*", "*>", "*F?", P, "*F*", "*>S", "*F?", Q, "*S", "", "", R, "*", "*", "*", S, "*", "*", "*", T, "*", "*", "*", ); my @X43 = ( T, Q, P, A, "", ">->", "^*", A, "&", "^", "&1+T", B, "", ">->", "^*", B, "&", "^", "&1+T", C, "", ">->S", "^*", C, "&", "^", "&1+T", D, "", "/", "", D, "&", "^", "&1+T", E, "", "/", "", E, "&", "^", "&1+T", F, "-S", ">->S", "^*", F, "&", "^", "&1+T", G, "", "/", "", G, "&", "^", "&1+T", H, "", ">->S", "^*", H, "&", "^", "&1+T", I, "", ">->+F", "^F*", I, "&", "^", "&1+T", J, "", ">->+F", "^F*", J, "&", "^", "&1+T", K, "", ">->S+F", "^F*", K, "&", "^", "&1+T", L, "", ">->+F", "^FS*", L, "&", "^", "&1+T", M, "", ">->+FS", "^FS*", M, "&", "^", "&1+T", N, "-S", ">->S+F", "^F*", N, "&", "^", "&1+T", O, "", ">->+FS", "^FS*", O, "&", "^", "&1+T", P, "", ">->S+F", "^F*", P, "&", "^", "&1+T", ); test("max size with upgrade + max messages", \@x42, \@X43, \@O41); # Test for legacy/tampered states with inaccurate maxuid tracking # Joined post-push & post-pull state to have just one test - # there is no way how this could have occurred naturally. my @x60 = ( 0, C, 0, A, "*S", "", "_", B, "*FS", "*FS", "*FS", C, "*S", "", "_", D, "*", "*", "*", E, "*", "*", "*", F, "*", "", "", G, "*", "", "", H, "", "", "*", I, "", "", "_", J, "*", "*", "*", ); my @O61 = ("", "", "Sync Flags\n"); # Need to fetch old messages my @X61 = ( E, C, E, ); test("maxuid topping", \@x60, \@X61, \@O61); my @O62 = ("", "", "Sync Flags\nExpireSide Far"); test("maxuid topping (expire far)", \@x60, \@X61, \@O62, 1); # Tests for refreshing previously skipped/failed/expired messages. # We don't know the flags at the time of the (hypothetical) previous # sync, so we can't know why a particular message is missing. # We test with MaxMessages to cover different behaviors wrt maxxfuid. my @x70 = ( L, E, P, A, "*", "", "", B, "*F", "", "", C, "", "", "*", D, "*F", "*F", "*F", E, "*", "", "_", # MaxExpiredFarUid F, "*", "*", "*", G, "_", "*T", "*T", H, "*T", "*T", "_", I, "", "", "*T", J, "", "", "*", K, "*T", "", "", L, "*", "", "", # MaxPulledUid M, "_", "*T", "*T", N, "*T", "*T", "_", O, "", "", "*T", P, "", "", "*", # MaxPushedUid Q, "*T", "", "", R, "*", "", "", S, "", "", "*", ); my @O71 = ("", "", "Sync New\nMaxMessages 20\nExpireUnread yes\n"); my @X71 = ( S, E, R, N, "", ">", "", S, "*", "*", "", Q, "", "*T", "*T", R, "", "*", "*", ); test("not old", \@x70, \@X71, \@O71); my @O71a = ("", "", "Sync New\nMaxMessages 3\nExpireUnread yes\n"); test("not old + expire", \@x70, \@X71, \@O71a); my @O72 = ("", "", "Sync New Old\nMaxMessages 20\nExpireUnread yes\nExpunge Near\n"); my @X72 = ( S, E, R, G, "", "/", "/", H, "", ">", "", I, "", "", "/", M, "", "/", "/", N, "", ">", "", O, "", "", "/", C, "*", "*", "", J, "*", "*", "", P, "*", "*", "", S, "*", "*", "", B, "", "*F", "*F", L, "", "*", "*", R, "", "*", "*", ); test("old + expunge near", \@x70, \@X72, \@O72); # This omits the entries that cause idempotence problems # due to not counting/expiring newly pushed messages. my @x70a = ( L, E, O, A, "*", "", "", B, "*F", "", "", D, "*F", "*F", "*F", E, "*", "", "_", # MaxExpiredFarUid G, "_", "*T", "*T", H, "*T", "*T", "_", I, "", "", "*T", K, "*T", "", "", L, "*", "", "", # MaxPulledUid M, "_", "*T", "*T", N, "*T", "*T", "_", O, "", "", "*T", # MaxPushedUid Q, "*T", "", "", R, "*", "", "", S, "", "", "*", ); my @O72a = ("", "", "Sync New Old\nMaxMessages 3\nExpireUnread yes\nExpunge Near\n"); my @X72a = ( S, E, R, G, "", "/", "/", H, "", ">", "", I, "", "", "/", M, "", "/", "/", N, "", ">", "", O, "", "", "/", S, "*", "*", "", B, "", "*F", "*F", L, "", "*", "*", R, "", "*", "*", ); test("old + expire + expunge near", \@x70a, \@X72a, \@O72a); my @O73 = ("", "", "Sync Pull New Old\nMaxMessages 20\nExpireUnread yes\n"); my @X73 = ( R, E, P, B, "", "*F", "*F", G, "", "<", "", H, "", ">", "", K, "", "*T", "*T", L, "", "*", "*", M, "", "<", "", N, "", ">", "", Q, "", "*T", "*T", R, "", "*", "*", ); test("pull old", \@x70, \@X73, \@O73); # This is "weird", because expiration overtook pulling. my @x80 = ( D, L, Q, A, "", "", "*", B, "*", "", "", C, "*T", "", "", D, "*F", "", "", # MaxPulledUid E, "_", "*T", "*T", F, "*T", "*T", "_", G, "", "", "*T", H, "", "", "*", I, "*T", "", "", J, "*", "", "", K, "*F", "*F", "*F", L, "*", "*~", "_", # MaxExpiredFarUid M, "*", "*", "*", N, "_", "*T", "*T", O, "*T", "*T", "_", P, "", "", "*T", Q, "", "", "*", # MaxPushedUid R, "*T", "", "", S, "*", "", "", T, "", "", "*T", U, "", "", "*", ); my @X81 = ( U, L, S, F, "", ">", "", L, "", "/", "", O, "", ">", "", T, "*T", "*T", "", U, "*", "*", "", I, "", "*T", "*T", J, "", "*", "*", R, "", "*T", "*T", S, "", "*", "*", ); test("weird not old", \@x80, \@X81, \@O71); my @X81a = ( U, L, S, F, "", ">", "", L, "", "/", "", O, "", ">", "", T, "*T", "*T", "", U, "*", "*", "", I, "", "*T", "*T", R, "", "*T", "*T", S, "", "*", "*", ); test("weird not old + expire", \@x80, \@X81a, \@O71a); my @X82 = ( U, L, S, E, "", "/", "/", F, "", ">", "", G, "", "", "/", L, "", "/", "", N, "", "/", "/", O, "", ">", "", P, "", "", "/", T, "", "", "/", A, "*", "*", "", H, "*", "*", "", Q, "*", "*", "", U, "*", "*", "", D, "", "*F", "*F", J, "", "*", "*", S, "", "*", "*", ); test("weird old + expunge near", \@x80, \@X82, \@O72); my @x80a = ( D, L, Q, B, "*", "", "", C, "*T", "", "", D, "*F", "", "", # MaxPulledUid E, "_", "*T", "*T", F, "*T", "*T", "_", G, "", "", "*T", H, "", "", "*", I, "*T", "", "", K, "*F", "*F", "*F", L, "*", "*~", "_", # MaxExpiredFarUid N, "_", "*T", "*T", O, "*T", "*T", "_", P, "", "", "*T", Q, "", "", "*", # MaxPushedUid R, "*T", "", "", T, "", "", "*T", U, "", "", "*", ); my @X82a = ( U, L, D, E, "", "/", "/", F, "", ">", "", G, "", "", "/", L, "", "/", "", N, "", "/", "/", O, "", ">", "", P, "", "", "/", T, "", "", "/", H, "*", "*", "", Q, "*", "*", "", U, "*", "*", "", D, "", "*F", "*F", ); test("weird old + expire + expunge near", \@x80a, \@X82a, \@O72a); my @O82b = ("", "", "Sync New Old\nMaxMessages 3\nExpireUnread yes\nExpireSide Far\nExpunge Far\n"); my @X82b = ( U, L, D, E, "", "/", "/", F, "", ">", "", G, "", "", "/", L, "", "/", "", N, "", "/", "/", O, "", ">", "", P, "", "", "/", T, "", "", "/", D, "", "*F", "*F", H, "*", "*", "", Q, "*", "*", "", U, "*", "*", "", ); test("weird old + expire far + expunge far", \@x80a, \@X82b, \@O82b, 1); my @X83 = ( S, L, Q, E, "", "<", "", F, "", ">", "", L, "", "/", "", N, "", "<", "", O, "", ">", "", D, "", "*F", "*F", I, "", "*T", "*T", J, "", "*", "*", R, "", "*T", "*T", S, "", "*", "*", ); test("weird pull old", \@x80, \@X83, \@O73); # Messages that would be instantly expunged on the target side. my @x90 = ( C, 0, C, A, "*DRT*", "*>DS", "*DFPS?", B, "*DR*", "*>DS", "*DFPST?", C, "*", "*", "*", D, "*T", "", "", ); my @O91 = ("", "", "Expunge Near\n", "-Tx"); my @X91 = ( D, 0, C, A, "+P", ">+P", "|", A, "&", "^", "+T", B, "+PT", ">+PT", "|", B, "&", "^", "", ); test("doomed", \@x90, \@X91, \@O91); # Trashing my @x10 = ( K, A, K, A, "*", "*~", "*T", B, "*T", "*^", "", C, "*T", "*", "*T", D, "_", "*", "*", E, "*", "*", "_", F, "**", "*>", "*T?", G, "*T?", "*<", "**", H, "**", "*>", "*FT?", I, "*FT?", "*<", "**", J, "**", "*>", "*F?", K, "*F?", "*<", "**", L, "*T", "", "", M, "", "", "*T", R, "", "", "*", # Force maxuid in the interrupt-resume test. S, "*", "", "", ); my @O11 = ("Trash far_trash\n", "Trash near_trash\n", "MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n"); my @X11 = ( R, A, S, A, "", "/", "/", B, "#/", "/", "", C, "#/", "/", "#/", D, "", "/", "#/", E, "#/", "/", "", F, "#/", "/", "/", G, "/", "/", "#/", H, "#/", "/", "/", I, "/", "/", "#/", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1/", "", "", L, "#/", "", "", M, "", "", "#/", R, "*", "*", "", S, "", "*", "*", ); test("trash", \@x10, \@X11, \@O11); my @O12 = ("Trash far_trash\n", "Trash near_trash\nTrashNewOnly true\n", "MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n"); my @X12 = ( R, A, S, A, "", "/", "/", B, "#/", "/", "", C, "#/", "/", "/", D, "", "/", "/", E, "#/", "/", "", F, "#/", "/", "/", G, "/", "/", "#/", H, "#/", "/", "/", I, "/", "/", "#/", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1/", "", "", L, "#/", "", "", M, "", "", "#/", R, "*", "*", "", S, "", "*", "*", ); test("trash only new", \@x10, \@X12, \@O12); my @O13 = ("Trash far_trash\nTrashRemoteNew true\n", "", "MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n"); my @X13 = ( R, A, S, A, "", "/", "/", B, "#/", "/", "", C, "#/", "/", "/", D, "", "/", "/", E, "#/", "/", "", F, "#/", "/", "/", G, "#/", "/", "/", H, "#/", "/", "/", I, "#/", "/", "/", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1/", "", "", L, "#/", "", "", M, "#", "", "/", R, "*", "*", "", S, "", "*", "*", ); test("trash new remotely", \@x10, \@X13, \@O13); my @O14 = ("Trash far_trash\n", "", "Sync Flags Gone\nMaxMessages 20\nExpireUnread yes\nExpireSide Far\nMaxSize 1k\nExpunge Both\n"); my @X14 = ( K, A, K, A, "", "/", "/", B, "/", "/", "", C, "/", "/", "#/", D, "", "/", "#/", E, "/", "/", "", F, "/", "/", "/", G, "/", "/", "#/", H, "/", "/", "/", I, "/", "/", "#/", L, "/", "", "", M, "", "", "#/", ); test("trash near (expire far)", \@x10, \@X14, \@O14, 1); # Test "mirroring" expunges. my @xa0 = ( M, 0, M, # pair A, "*", "*", "*", # expire B, "*", "*", "*S", # expire with del C, "*T", "*", "*S", # pair flag del D, "*T", "*", "*", E, "*", "*", "*T", # pair flag undel F, "*", "*T", "*T", G, "*T", "*T", "*", # pair gone H, "_", "*", "*", I, "*", "*", "_", # upgrade J, "**", "*>", "*F?", K, "*F?", "*<", "**", # doomed upgrade L, "*T*", "*>", "*F?", M, "*F?", "*<", "*T*", # doomed new N, "", "", "*T", O, "*T", "", "", ); my @Oa1 = ("", "", "ExpungeSolo Both\nMaxMessages 1\nExpireUnread false\n"); my @Xa1 = ( N, B, O, B, "+S", "/", "/", C, "+S", "+ST", "+T", # This is weird, but it's not worth handling. D, "", "+T", "+T", E, "+T", "+T", "", F, "", "-T", "-T", G, "-T", "-T", "", H, "", "/", "/", I, "/", "/", "", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1/", "", "", L, "", ">->+T", "^*T", L, "", "", "&1/", M, "^*T", "<-<+T", "", M, "&1/", "", "", N, "*T", "*T", "", O, "", "*T", "*T", ); test("expunge solo both", \@xa0, \@Xa1, \@Oa1); my @Oa2 = ("", "", "ExpungeSolo Near\nMaxMessages 1\nExpireUnread false\n"); my @Xa2 = ( N, B, O, B, "+S", "/", "/", C, "+S", "+ST", "+T", # As above. D, "", "+T", "+T", E, "+T", "+T", "", F, "", "-T", "-T", G, "-T", "-T", "", H, "", "/", "/", I, "+T", ">", "", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1+T", "^", "|", L, "", ">->+T", "^*T", L, "", "", "&1/", M, "^*T", "<-<+T", "", M, "&1+T", "^", "|", N, "*T", "*T", "", O, "", "*T", "*T", ); test("expunge solo near", \@xa0, \@Xa2, \@Oa2); my @Oa3 = ("", "", "Expunge Far\nExpungeSolo Near\nMaxMessages 1\nExpireUnread false\n"); my @Xa3 = ( K, B, J, B, "+S", "/", "/", C, "/", "/", "/", D, "/", "/", "/", E, "/", "/", "/", F, "", "-T", "-T", G, "-T", "-T", "", H, "", "/", "/", I, "/", "/", "", J, "", ">->", "^*", J, "", "", "&1/", K, "^*", "<-<", "", K, "&1/", "", "", L, "/", "/", "/", M, "/", "/", "/", N, "", "", "/", O, "/", "", "", ); test("expunge far & solo near", \@xa0, \@Xa3, \@Oa3); print "OK.\n"; isync-1.5.1/src/main_sync.c0000644000175000001440000004541214746015570011252 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #include "main_p.h" #define nz(a, b) ((a) ? (a) : (b)) static int chans_total, chans_done; static int boxes_total, boxes_done; static int stats_steps; static int64_t stats_stamp; static wakeup_t stats_wakeup; static void print_stats( void ) { char buf[3][80]; char *cs; static int cols = -1; if (cols < 0 && (!(cs = getenv( "COLUMNS" )) || !(cols = atoi( cs )))) cols = 80; int ll = sprintf( buf[2], "C: %d/%d B: %d/%d", chans_done, chans_total, boxes_done, boxes_total ); int cls = (cols - ll - 10) / 2; for (int t = 0; t < 2; t++) { int l = sprintf( buf[t], "+%d/%d *%d/%d #%d/%d -%d/%d", new_done[t], new_total[t], flags_done[t], flags_total[t], trash_done[t], trash_total[t], expunge_done[t], expunge_total[t] ); if (l > cls) buf[t][cls - 1] = '~'; } progress( "\r%s F: %.*s N: %.*s", buf[2], cls, buf[0], cls, buf[1] ); } static void stats_timeout( void *aux ATTR_UNUSED ) { if (stats_steps != -1) { stats_steps = -1; print_stats(); } conf_wakeup( &stats_wakeup, 200 ); } void stats( void ) { if (!(DFlags & PROGRESS)) return; // If the main loop appears to be running, skip the sync path. if (stats_steps < 0) { stats_steps = -2; return; } // Rate-limit the (somewhat) expensive timer queries. if (++stats_steps < 10) return; stats_steps = 0; int64_t now = get_now(); if (now < stats_stamp + 300) return; stats_stamp = now; print_stats(); } static void summary( void ) { if (Verbosity < TERSE) return; if (!boxes_done) return; // Shut up if we errored out early. printf( "Channels: %d Boxes: %d Far: +%d *%d #%d -%d Near: +%d *%d #%d -%d\n", chans_done, boxes_done, new_done[F], flags_done[F], trash_done[F], expunge_done[F], new_done[N], flags_done[N], trash_done[N], expunge_done[N] ); } static int matches( const char *t, const char *p ) { for (;;) { if (!*p) return !*t; if (*p == '*') { p++; do { if (matches( t, p )) return 1; } while (*t++); return 0; } else if (*p == '%') { p++; do { if (*t == '/') return 0; if (matches( t, p )) return 1; } while (*t++); return 0; } else { if (*p != *t) return 0; p++, t++; } } } static int is_inbox( const char *name ) { return starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/'); } static int cmp_box_names( const void *a, const void *b ) { const char *as = *(const char * const *)a; const char *bs = *(const char * const *)b; int ai = is_inbox( as ); int bi = is_inbox( bs ); int di = bi - ai; if (di) return di; return strcmp( as, bs ); } static char ** filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns ) { char **boxarr = NULL; uint num = 0, rnum = 0; uint pfxl = prefix ? strlen( prefix ) : 0; for (; boxes; boxes = boxes->next) { if (!starts_with( boxes->string, -1, prefix, pfxl )) continue; uint fnot = 1, not; for (string_list_t *cpat = patterns; cpat; cpat = cpat->next) { const char *ps = cpat->string; if (*ps == '!') { ps++; not = 1; } else { not = 0; } if (matches( boxes->string + pfxl, ps )) { fnot = not; break; } } if (!fnot) { if (num + 1 >= rnum) boxarr = nfrealloc( boxarr, (rnum = (rnum + 10) * 2) * sizeof(*boxarr) ); boxarr[num++] = nfstrdup( boxes->string + pfxl ); boxarr[num] = NULL; } } if (boxarr) qsort( boxarr, num, sizeof(*boxarr), cmp_box_names ); return boxarr; } static void merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def ) { if (ops[F] & have) { chan->ops[F] &= ~mask; chan->ops[F] |= ops[F] & mask; chan->ops[N] &= ~mask; chan->ops[N] |= ops[N] & mask; } else if (!(chan->ops[F] & have)) { if (global_conf.ops[F] & have) { chan->ops[F] |= global_conf.ops[F] & mask; chan->ops[N] |= global_conf.ops[N] & mask; } else { chan->ops[F] |= def; chan->ops[N] |= def; } } } typedef struct box_ent { struct box_ent *next; char *name; int present[2]; } box_ent_t; typedef struct chan_ent { struct chan_ent *next; channel_conf_t *conf; box_ent_t *boxes; int boxlist; } chan_ent_t; static chan_ent_t * add_channel( chan_ent_t ***chanapp, channel_conf_t *chan, int ops[] ) { chan_ent_t *ce = nfzalloc( sizeof(*ce) ); ce->conf = chan; merge_actions( chan, ops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_DFLT_TYPE ); merge_actions( chan, ops, XOP_HAVE_CREATE, OP_CREATE, 0 ); merge_actions( chan, ops, XOP_HAVE_REMOVE, OP_REMOVE, 0 ); merge_actions( chan, ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 ); merge_actions( chan, ops, XOP_HAVE_EXPUNGE_SOLO, OP_EXPUNGE_SOLO, 0 ); debug( "channel ops (%s):\n far: %s\n near: %s\n", chan->name, fmt_ops( ops[F] ).str, fmt_ops( ops[N] ).str ); for (int t = 0; t < 2; t++) { if (!(~ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO))) { error( "Specified both Expunge and ExpungeSolo for %s of Channel '%s'.\n", str_fn[t], chan->stores[t]->name ); free( ce ); return NULL; } } **chanapp = ce; *chanapp = &ce->next; chans_total++; return ce; } static chan_ent_t * add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] ) { box_ent_t *boxes = NULL, **mboxapp = &boxes, *mbox; int boxlist = 0; char *boxp; if ((boxp = strchr( channame, ':' ))) *boxp++ = 0; channel_conf_t *chan; for (chan = channels; chan; chan = chan->next) if (!strcmp( chan->name, channame )) goto gotchan; error( "No channel or group named '%s' defined.\n", channame ); return NULL; gotchan: if (boxp) { if (!chan->patterns) { error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame ); return NULL; } boxlist = 1; do { char *nboxp = strpbrk( boxp, ",\n" ); size_t boxl; if (nboxp) { boxl = (size_t)(nboxp - boxp); *nboxp++ = 0; } else { boxl = strlen( boxp ); } mbox = nfmalloc( sizeof(*mbox) ); if (boxl) mbox->name = nfstrndup( boxp, boxl ); else mbox->name = nfstrndup( "INBOX", 5 ); mbox->present[F] = mbox->present[N] = BOX_POSSIBLE; mbox->next = NULL; *mboxapp = mbox; mboxapp = &mbox->next; boxes_total++; boxp = nboxp; } while (boxp); } else { if (!chan->patterns) boxes_total++; } chan_ent_t *ce = add_channel( chanapp, chan, ops ); if (!ce) return NULL; ce->boxes = boxes; ce->boxlist = boxlist; return ce; } typedef struct { int t[2]; core_vars_t *cvars; channel_conf_t *chan; driver_t *drv[2]; store_t *ctx[2]; chan_ent_t *chanptr; box_ent_t *boxptr; string_list_t *boxes[2]; char *names[2]; int state[2]; int chan_cben, fnlz_cben, box_cben, box_done; } main_vars_t; #define AUX &mvars->t[t] #define MVARS(aux) \ int t = *(int *)aux; \ main_vars_t *mvars = (main_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(main_vars_t, t)); static void do_sync_chans( main_vars_t *lvars ); void sync_chans( core_vars_t *cvars, char **argv ) { main_vars_t mvars[1]; chan_ent_t *chans = NULL, **chanapp = &chans; memset( mvars, 0, sizeof(*mvars) ); mvars->t[1] = 1; mvars->cvars = cvars; if (!channels) { fputs( "No channels defined. Try 'man " EXE "'\n", stderr ); cvars->ret = 1; return; } if (cvars->all) { for (channel_conf_t *chan = channels; chan; chan = chan->next) { if (!add_channel( &chanapp, chan, cvars->ops )) cvars->ret = 1; if (!chan->patterns) boxes_total++; } } else { for (; *argv; argv++) { for (group_conf_t *group = groups; group; group = group->next) { if (!strcmp( group->name, *argv )) { for (string_list_t *channame = group->channels; channame; channame = channame->next) if (!add_named_channel( &chanapp, channame->string, cvars->ops )) cvars->ret = 1; goto gotgrp; } } if (!add_named_channel( &chanapp, *argv, cvars->ops )) cvars->ret = 1; gotgrp: ; } } if (cvars->ret) return; if (!chans) { fputs( "No channel specified. Try '" EXE " -h'\n", stderr ); cvars->ret = 1; return; } mvars->chanptr = chans; if (!cvars->list && (DFlags & PROGRESS)) { init_wakeup( &stats_wakeup, stats_timeout, NULL ); stats_timeout( NULL ); } do_sync_chans( mvars ); main_loop(); if (!cvars->list) { flushn(); summary(); if (DFlags & EXT_EXIT) { for (int t = 0; t < 2; t++) if (new_done[t] || flags_done[t] || trash_done[t] || expunge_done[t]) cvars->ret |= 32 << t; } } } enum { ST_FRESH, ST_CONNECTED, ST_OPEN, ST_CANCELING, ST_CLOSED, }; static int check_cancel( main_vars_t *mvars ) { return mvars->state[F] >= ST_CANCELING || mvars->state[N] >= ST_CANCELING; } static void store_connected( int sts, void *aux ); static void store_listed( int sts, string_list_t *boxes, void *aux ); static void sync_opened( main_vars_t *mvars, int t ); static void do_sync_boxes( main_vars_t *lvars ); static void done_sync_dyn( int sts, void *aux ); static void done_sync( int sts, void *aux ); static void finalize_sync( main_vars_t *mvars ); static void store_bad( void *aux ) { MVARS(aux) mvars->drv[t]->cancel_store( mvars->ctx[t] ); mvars->state[t] = ST_CLOSED; mvars->cvars->ret = 1; finalize_sync( mvars ); } static void advance_chan( main_vars_t *mvars ) { if (!mvars->cvars->list) { chans_done++; stats(); } chan_ent_t *nchan = mvars->chanptr->next; free( mvars->chanptr ); mvars->chanptr = nchan; } static void do_sync_chans( main_vars_t *mvars ) { while (mvars->chanptr) { stats_steps = 0; // Determine main loop use afresh mvars->chan = mvars->chanptr->conf; info( "Channel %s\n", mvars->chan->name ); for (int t = 0; t < 2; t++) { int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] ); if (st != FAIL_TEMP) { info( "Skipping due to %sfailed %s store %s.\n", (st == FAIL_WAIT) ? "temporarily " : "", str_fn[t], mvars->chan->stores[t]->name ); goto next; } } uint dcaps[2]; for (int t = 0; t < 2; t++) { mvars->drv[t] = mvars->chan->stores[t]->driver; dcaps[t] = mvars->drv[t]->get_caps( NULL ); } const char *labels[2]; if ((DFlags & DEBUG_DRV) || (dcaps[F] & dcaps[N] & DRV_VERBOSE)) labels[F] = "F: ", labels[N] = "N: "; else labels[F] = labels[N] = ""; for (int t = 0; t < 2; t++) { store_t *ctx = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] ); if ((DFlags & (DEBUG_DRV | DRYRUN)) || ((DFlags & FORCEASYNC(t)) && !(dcaps[t] & DRV_ASYNC))) { mvars->drv[t] = &proxy_driver; ctx = proxy_alloc_store( ctx, labels[t], DFlags & FORCEASYNC(t) ); } mvars->ctx[t] = ctx; mvars->drv[t]->set_callbacks( ctx, NULL, store_bad, AUX ); mvars->state[t] = ST_FRESH; } mvars->chan_cben = 0; for (int t = 0; ; t++) { info( "Opening %s store %s...\n", str_fn[t], mvars->chan->stores[t]->name ); mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX ); if (t || check_cancel( mvars )) break; } if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) { mvars->chan_cben = 1; return; } next: advance_chan( mvars ); } cleanup_mainloop(); if (!mvars->cvars->list && (DFlags & PROGRESS)) wipe_wakeup( &stats_wakeup ); } static void sync_next_chan( main_vars_t *mvars ) { if (mvars->chan_cben) { advance_chan( mvars ); do_sync_chans( mvars ); } } static void store_connected( int sts, void *aux ) { MVARS(aux) switch (sts) { case DRV_CANCELED: return; case DRV_OK: mvars->state[t] = ST_CONNECTED; if (check_cancel( mvars )) break; if (!mvars->chanptr->boxlist && mvars->chan->patterns) { int cflags = 0; for (string_list_t *cpat = mvars->chan->patterns; cpat; cpat = cpat->next) { const char *pat = cpat->string; if (*pat != '!') { char buf[8]; int bufl = snprintf( buf, sizeof(buf), "%s%s", nz( mvars->chan->boxes[t], "" ), pat ); int flags = 0; // Partial matches like "INB*" or even "*" are not considered, // except implicitly when the INBOX lives under Path. if (starts_with( buf, bufl, "INBOX", 5 )) { char c = buf[5]; if (!c) { // User really wants the INBOX. flags |= LIST_INBOX; } else if (c == '/') { // Flattened sub-folders of INBOX actually end up in Path. if (mvars->ctx[t]->conf->flat_delim[0]) flags |= LIST_PATH; else flags |= LIST_INBOX; } else if (c == '*' || c == '%') { // It can be both INBOX and Path, but don't require Path to be configured. flags |= LIST_INBOX | LIST_PATH_MAYBE; } else { // It's definitely not the INBOX. flags |= LIST_PATH; } } else { flags |= LIST_PATH; } debug( "pattern '%s' (effective '%s'): %sPath, %sINBOX\n", pat, buf, (flags & LIST_PATH) ? "" : "no ", (flags & LIST_INBOX) ? "" : "no "); cflags |= flags; } } mvars->drv[t]->list_store( mvars->ctx[t], cflags, store_listed, AUX ); return; } sync_opened( mvars, t ); return; default: mvars->cvars->ret = 1; break; } finalize_sync( mvars ); } static void store_listed( int sts, string_list_t *boxes, void *aux ) { MVARS(aux) int fail = 0; switch (sts) { case DRV_CANCELED: return; case DRV_OK: if (check_cancel( mvars )) break; for (string_list_t *box = boxes; box; box = box->next) { if (mvars->ctx[t]->conf->flat_delim[0]) { string_list_t *nbox; if (map_name( box->string, -1, (char **)&nbox, offsetof(string_list_t, string), mvars->ctx[t]->conf->flat_delim, "/" ) < 0) { error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", box->string ); fail = 1; } else { nbox->next = mvars->boxes[t]; mvars->boxes[t] = nbox; } } else { add_string_list( &mvars->boxes[t], box->string ); } } if (fail) { mvars->cvars->ret = 1; break; } if (mvars->ctx[t]->conf->map_inbox) { debug( "adding mapped inbox to %s store: %s\n", str_fn[t], mvars->ctx[t]->conf->map_inbox ); add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox ); } sync_opened( mvars, t ); return; default: mvars->cvars->ret = 1; break; } finalize_sync( mvars ); } static void sync_opened( main_vars_t *mvars, int t ) { mvars->state[t] = ST_OPEN; if (mvars->state[t^1] != ST_OPEN) return; if (!mvars->chanptr->boxlist && mvars->chan->patterns) { mvars->chanptr->boxlist = 2; char **boxes[2]; boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns ); boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns ); box_ent_t **mboxapp = &mvars->chanptr->boxes; for (int mb = 0, sb = 0; ; ) { char *fname = boxes[F] ? boxes[F][mb] : NULL; char *nname = boxes[N] ? boxes[N][sb] : NULL; if (!fname && !nname) break; box_ent_t *mbox = nfmalloc( sizeof(*mbox) ); int cmp; if (!(cmp = !fname - !nname) && !(cmp = cmp_box_names( &fname, &nname ))) { mbox->name = fname; free( nname ); mbox->present[F] = mbox->present[N] = BOX_PRESENT; mb++; sb++; } else if (cmp < 0) { mbox->name = fname; mbox->present[F] = BOX_PRESENT; mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT; mb++; } else { mbox->name = nname; mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT; mbox->present[N] = BOX_PRESENT; sb++; } mbox->next = NULL; *mboxapp = mbox; mboxapp = &mbox->next; boxes_total++; } free( boxes[F] ); free( boxes[N] ); if (!mvars->cvars->list) stats(); } mvars->boxptr = mvars->chanptr->boxes; if (mvars->cvars->list && chans_total > 1) printf( "%s:\n", mvars->chan->name ); mvars->box_done = 0; do_sync_boxes( mvars ); } static void do_sync_boxes( main_vars_t *mvars ) { mvars->box_cben = 0; while (mvars->state[F] == ST_OPEN && mvars->state[N] == ST_OPEN) { if (mvars->chanptr->boxlist) { box_ent_t *mbox = mvars->boxptr; if (!mbox) break; mvars->boxptr = mbox->next; mvars->box_done = 0; if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) { const char *fpfx = nz( mvars->chan->boxes[F], "" ); const char *npfx = nz( mvars->chan->boxes[N], "" ); if (mvars->cvars->list) { printf( "%s%s <=> %s%s\n", fpfx, mbox->name, npfx, mbox->name ); continue; } nfasprintf( &mvars->names[F], "%s%s", fpfx, mbox->name ); nfasprintf( &mvars->names[N], "%s%s", npfx, mbox->name ); sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_dyn, mvars ); } else { if (mvars->cvars->list) { puts( mbox->name ); continue; } mvars->names[F] = mvars->names[N] = mbox->name; sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars ); } } else { if (mvars->cvars->list) { printf( "%s <=> %s\n", nz( mvars->chan->boxes[F], "INBOX" ), nz( mvars->chan->boxes[N], "INBOX" ) ); break; } if (mvars->box_done) break; int present[] = { BOX_POSSIBLE, BOX_POSSIBLE }; sync_boxes( mvars->ctx, mvars->chan->boxes, present, mvars->chan, done_sync, mvars ); } if (!mvars->box_done) { mvars->box_cben = 1; return; } } finalize_sync( mvars ); } static void done_sync_dyn( int sts, void *aux ) { main_vars_t *mvars = (main_vars_t *)aux; free( mvars->names[F] ); free( mvars->names[N] ); done_sync( sts, aux ); } static void done_sync( int sts, void *aux ) { main_vars_t *mvars = (main_vars_t *)aux; boxes_done++; stats(); if (sts) { mvars->cvars->ret = 1; if (sts & SYNC_BAD(F)) mvars->state[F] = ST_CLOSED; if (sts & SYNC_BAD(N)) mvars->state[N] = ST_CLOSED; } mvars->box_done = 1; if (mvars->box_cben) do_sync_boxes( mvars ); } static void sync_finalized( void *aux ); static void finalize_sync( main_vars_t *mvars ) { if (mvars->chanptr->boxlist) { box_ent_t *mbox, *nmbox; for (nmbox = mvars->chanptr->boxes; (mbox = nmbox); ) { nmbox = mbox->next; free( mbox->name ); free( mbox ); } mvars->chanptr->boxes = NULL; mvars->chanptr->boxlist = 0; } mvars->fnlz_cben = 0; for (int t = 0; t < 2; t++) { free_string_list( mvars->boxes[t] ); mvars->boxes[t] = NULL; if (mvars->state[t] == ST_FRESH || mvars->state[t] == ST_OPEN) { mvars->drv[t]->free_store( mvars->ctx[t] ); mvars->state[t] = ST_CLOSED; } else if (mvars->state[t] == ST_CONNECTED) { mvars->state[t] = ST_CANCELING; mvars->drv[t]->cancel_cmds( mvars->ctx[t], sync_finalized, AUX ); } } if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) { mvars->fnlz_cben = 1; return; } sync_next_chan( mvars ); } static void sync_finalized( void *aux ) { MVARS(aux) mvars->drv[t]->free_store( mvars->ctx[t] ); mvars->state[t] = ST_CLOSED; if (mvars->state[t^1] != ST_CLOSED) return; if (mvars->fnlz_cben) sync_next_chan( mvars ); } isync-1.5.1/src/Makefile.in0000644000175000001440000014452014764113020011160 # Makefile.in generated by automake 1.17 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2024 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@ # SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later 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)) am__rm_f = rm -f $(am__rm_f_notfound) am__rm_rf = rm -rf $(am__rm_f_notfound) 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@ target_triplet = @target@ bin_PROGRAMS = mbsync$(EXEEXT) $(am__EXEEXT_1) check_PROGRAMS = tst_imap_msgs$(EXEEXT) tst_imap_utf7$(EXEEXT) \ tst_msg_cvt$(EXEEXT) EXTRA_PROGRAMS = tst_timers$(EXEEXT) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 $(top_srcdir)/VERSION \ $(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)/autodefs.h CONFIG_CLEAN_FILES = mbsync.1 mdconvert.1 CONFIG_CLEAN_VPATH_FILES = @with_mdconvert_TRUE@am__EXEEXT_1 = mdconvert$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(exampledir)" PROGRAMS = $(bin_PROGRAMS) am_mbsync_OBJECTS = util.$(OBJEXT) config.$(OBJEXT) socket.$(OBJEXT) \ driver.$(OBJEXT) drv_proxy.$(OBJEXT) drv_imap.$(OBJEXT) \ imap_msgs.$(OBJEXT) imap_utf7.$(OBJEXT) drv_maildir.$(OBJEXT) \ sync.$(OBJEXT) sync_state.$(OBJEXT) sync_msg_cvt.$(OBJEXT) \ main.$(OBJEXT) main_sync.$(OBJEXT) main_list.$(OBJEXT) mbsync_OBJECTS = $(am_mbsync_OBJECTS) am__DEPENDENCIES_1 = mbsync_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am_mdconvert_OBJECTS = mdconvert.$(OBJEXT) mdconvert_OBJECTS = $(am_mdconvert_OBJECTS) mdconvert_DEPENDENCIES = $(am__DEPENDENCIES_1) am_tst_imap_msgs_OBJECTS = tst_imap_msgs.$(OBJEXT) imap_msgs.$(OBJEXT) \ util.$(OBJEXT) tst_imap_msgs_OBJECTS = $(am_tst_imap_msgs_OBJECTS) tst_imap_msgs_LDADD = $(LDADD) am_tst_imap_utf7_OBJECTS = tst_imap_utf7.$(OBJEXT) imap_utf7.$(OBJEXT) \ util.$(OBJEXT) tst_imap_utf7_OBJECTS = $(am_tst_imap_utf7_OBJECTS) tst_imap_utf7_LDADD = $(LDADD) am_tst_msg_cvt_OBJECTS = tst_msg_cvt-tst_msg_cvt.$(OBJEXT) \ tst_msg_cvt-sync_msg_cvt.$(OBJEXT) tst_msg_cvt-util.$(OBJEXT) tst_msg_cvt_OBJECTS = $(am_tst_msg_cvt_OBJECTS) tst_msg_cvt_LDADD = $(LDADD) tst_msg_cvt_LINK = $(CCLD) $(tst_msg_cvt_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_tst_timers_OBJECTS = tst_timers.$(OBJEXT) util.$(OBJEXT) tst_timers_OBJECTS = $(am_tst_timers_OBJECTS) tst_timers_LDADD = $(LDADD) 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__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/config.Po ./$(DEPDIR)/driver.Po \ ./$(DEPDIR)/drv_imap.Po ./$(DEPDIR)/drv_maildir.Po \ ./$(DEPDIR)/drv_proxy.Po ./$(DEPDIR)/imap_msgs.Po \ ./$(DEPDIR)/imap_utf7.Po ./$(DEPDIR)/main.Po \ ./$(DEPDIR)/main_list.Po ./$(DEPDIR)/main_sync.Po \ ./$(DEPDIR)/mdconvert.Po ./$(DEPDIR)/socket.Po \ ./$(DEPDIR)/sync.Po ./$(DEPDIR)/sync_msg_cvt.Po \ ./$(DEPDIR)/sync_state.Po ./$(DEPDIR)/tst_imap_msgs.Po \ ./$(DEPDIR)/tst_imap_utf7.Po \ ./$(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po \ ./$(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po \ ./$(DEPDIR)/tst_msg_cvt-util.Po ./$(DEPDIR)/tst_timers.Po \ ./$(DEPDIR)/util.Po am__mv = mv -f 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 = 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 = $(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 = $(mbsync_SOURCES) $(mdconvert_SOURCES) \ $(tst_imap_msgs_SOURCES) $(tst_imap_utf7_SOURCES) \ $(tst_msg_cvt_SOURCES) $(tst_timers_SOURCES) DIST_SOURCES = $(mbsync_SOURCES) $(mdconvert_SOURCES) \ $(tst_imap_msgs_SOURCES) $(tst_imap_utf7_SOURCES) \ $(tst_msg_cvt_SOURCES) $(tst_timers_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 ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \ } man1dir = $(mandir)/man1 NROFF = nroff MANS = $(man_MANS) 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)` am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ $$am__collect_skipped_logs \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(IGNORE_SKIPPED_LOGS)'; then \ am__collect_skipped_logs='--collect-skipped-logs no'; \ else \ am__collect_skipped_logs=''; \ fi; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/mbsync.1.in \ $(srcdir)/mdconvert.1.in $(top_srcdir)/depcomp \ $(top_srcdir)/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DB_LIBS = @DB_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KEYCHAIN_LIBS = @KEYCHAIN_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RELEASE_DATE = @RELEASE_DATE@ SASL_LIBS = @SASL_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOCK_LIBS = @SOCK_LIBS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VERSION = @VERSION@ Z_LIBS = @Z_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__rm_f_notfound = @am__rm_f_notfound@ am__tar = @am__tar@ am__untar = @am__untar@ am__xargs_n = @am__xargs_n@ 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@ 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@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ mbsync_SOURCES = \ util.c config.c socket.c \ driver.c drv_proxy.c \ drv_imap.c imap_msgs.c imap_utf7.c \ drv_maildir.c \ sync.c sync_state.c sync_msg_cvt.c \ main.c main_sync.c main_list.c noinst_HEADERS = \ common.h config.h socket.h \ driver.h imap_p.h \ sync.h sync_p.h \ main_p.h mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS) $(KEYCHAIN_LIBS) mdconvert_SOURCES = mdconvert.c mdconvert_LDADD = $(DB_LIBS) @with_mdconvert_TRUE@mdconvert_prog = mdconvert @with_mdconvert_TRUE@mdconvert_man = mdconvert.1 in_man = mbsync.1.in mdconvert.1.in # don't forget to update AC_CONFIG_FILES in configure.ac! man_MANS = mbsync.1 $(mdconvert_man) tst_imap_msgs_SOURCES = tst_imap_msgs.c imap_msgs.c util.c tst_imap_utf7_SOURCES = tst_imap_utf7.c imap_utf7.c util.c tst_msg_cvt_SOURCES = tst_msg_cvt.c sync_msg_cvt.c util.c tst_msg_cvt_CFLAGS = -DQPRINTF_BUFF=10000 TESTS = $(check_PROGRAMS) tst_timers_SOURCES = tst_timers.c util.c exampledir = $(docdir)/examples example_DATA = mbsyncrc.sample EXTRA_DIST = drv_proxy_gen.pl run-tests.pl $(example_DATA) $(in_man) CLEANFILES = drv_proxy.inc all: all-am .SUFFIXES: .SUFFIXES: .c .log .o .obj .test .test$(EXEEXT) .trs $(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) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu 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__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ 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): mbsync.1: $(top_builddir)/config.status $(srcdir)/mbsync.1.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mdconvert.1: $(top_builddir)/config.status $(srcdir)/mdconvert.1.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ 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 \ ; 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) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(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)" && $(am__rm_f) $$files clean-binPROGRAMS: -$(am__rm_f) $(bin_PROGRAMS) clean-checkPROGRAMS: -$(am__rm_f) $(check_PROGRAMS) mbsync$(EXEEXT): $(mbsync_OBJECTS) $(mbsync_DEPENDENCIES) $(EXTRA_mbsync_DEPENDENCIES) @rm -f mbsync$(EXEEXT) $(AM_V_CCLD)$(LINK) $(mbsync_OBJECTS) $(mbsync_LDADD) $(LIBS) mdconvert$(EXEEXT): $(mdconvert_OBJECTS) $(mdconvert_DEPENDENCIES) $(EXTRA_mdconvert_DEPENDENCIES) @rm -f mdconvert$(EXEEXT) $(AM_V_CCLD)$(LINK) $(mdconvert_OBJECTS) $(mdconvert_LDADD) $(LIBS) tst_imap_msgs$(EXEEXT): $(tst_imap_msgs_OBJECTS) $(tst_imap_msgs_DEPENDENCIES) $(EXTRA_tst_imap_msgs_DEPENDENCIES) @rm -f tst_imap_msgs$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tst_imap_msgs_OBJECTS) $(tst_imap_msgs_LDADD) $(LIBS) tst_imap_utf7$(EXEEXT): $(tst_imap_utf7_OBJECTS) $(tst_imap_utf7_DEPENDENCIES) $(EXTRA_tst_imap_utf7_DEPENDENCIES) @rm -f tst_imap_utf7$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tst_imap_utf7_OBJECTS) $(tst_imap_utf7_LDADD) $(LIBS) tst_msg_cvt$(EXEEXT): $(tst_msg_cvt_OBJECTS) $(tst_msg_cvt_DEPENDENCIES) $(EXTRA_tst_msg_cvt_DEPENDENCIES) @rm -f tst_msg_cvt$(EXEEXT) $(AM_V_CCLD)$(tst_msg_cvt_LINK) $(tst_msg_cvt_OBJECTS) $(tst_msg_cvt_LDADD) $(LIBS) tst_timers$(EXEEXT): $(tst_timers_OBJECTS) $(tst_timers_DEPENDENCIES) $(EXTRA_tst_timers_DEPENDENCIES) @rm -f tst_timers$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tst_timers_OBJECTS) $(tst_timers_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_imap.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_maildir.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drv_proxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_msgs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_utf7.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main_list.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main_sync.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdconvert.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sync.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sync_msg_cvt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sync_state.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_imap_msgs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_imap_utf7.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_msg_cvt-util.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst_timers.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @: >>$@ am--depfiles: $(am__depfiles_remade) .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) '$<'` tst_msg_cvt-tst_msg_cvt.o: tst_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-tst_msg_cvt.o -MD -MP -MF $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Tpo -c -o tst_msg_cvt-tst_msg_cvt.o `test -f 'tst_msg_cvt.c' || echo '$(srcdir)/'`tst_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Tpo $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tst_msg_cvt.c' object='tst_msg_cvt-tst_msg_cvt.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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-tst_msg_cvt.o `test -f 'tst_msg_cvt.c' || echo '$(srcdir)/'`tst_msg_cvt.c tst_msg_cvt-tst_msg_cvt.obj: tst_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-tst_msg_cvt.obj -MD -MP -MF $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Tpo -c -o tst_msg_cvt-tst_msg_cvt.obj `if test -f 'tst_msg_cvt.c'; then $(CYGPATH_W) 'tst_msg_cvt.c'; else $(CYGPATH_W) '$(srcdir)/tst_msg_cvt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Tpo $(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tst_msg_cvt.c' object='tst_msg_cvt-tst_msg_cvt.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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-tst_msg_cvt.obj `if test -f 'tst_msg_cvt.c'; then $(CYGPATH_W) 'tst_msg_cvt.c'; else $(CYGPATH_W) '$(srcdir)/tst_msg_cvt.c'; fi` tst_msg_cvt-sync_msg_cvt.o: sync_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-sync_msg_cvt.o -MD -MP -MF $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Tpo -c -o tst_msg_cvt-sync_msg_cvt.o `test -f 'sync_msg_cvt.c' || echo '$(srcdir)/'`sync_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Tpo $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sync_msg_cvt.c' object='tst_msg_cvt-sync_msg_cvt.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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-sync_msg_cvt.o `test -f 'sync_msg_cvt.c' || echo '$(srcdir)/'`sync_msg_cvt.c tst_msg_cvt-sync_msg_cvt.obj: sync_msg_cvt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-sync_msg_cvt.obj -MD -MP -MF $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Tpo -c -o tst_msg_cvt-sync_msg_cvt.obj `if test -f 'sync_msg_cvt.c'; then $(CYGPATH_W) 'sync_msg_cvt.c'; else $(CYGPATH_W) '$(srcdir)/sync_msg_cvt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Tpo $(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sync_msg_cvt.c' object='tst_msg_cvt-sync_msg_cvt.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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-sync_msg_cvt.obj `if test -f 'sync_msg_cvt.c'; then $(CYGPATH_W) 'sync_msg_cvt.c'; else $(CYGPATH_W) '$(srcdir)/sync_msg_cvt.c'; fi` tst_msg_cvt-util.o: util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-util.o -MD -MP -MF $(DEPDIR)/tst_msg_cvt-util.Tpo -c -o tst_msg_cvt-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-util.Tpo $(DEPDIR)/tst_msg_cvt-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='tst_msg_cvt-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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c tst_msg_cvt-util.obj: util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -MT tst_msg_cvt-util.obj -MD -MP -MF $(DEPDIR)/tst_msg_cvt-util.Tpo -c -o tst_msg_cvt-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tst_msg_cvt-util.Tpo $(DEPDIR)/tst_msg_cvt-util.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='tst_msg_cvt-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) $(tst_msg_cvt_CFLAGS) $(CFLAGS) -c -o tst_msg_cvt-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` install-man1: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) 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 # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ output_system_information () \ { \ echo; \ { uname -a | $(AWK) '{ \ printf "System information (uname -a):"; \ for (i = 1; i < NF; ++i) \ { \ if (i != 2) \ printf " %s", $$i; \ } \ printf "\n"; \ }'; } 2>&1; \ if test -r /etc/os-release; then \ echo "Distribution information (/etc/os-release):"; \ sed 8q /etc/os-release; \ elif test -r /etc/issue; then \ echo "Distribution information (/etc/issue):"; \ cat /etc/issue; \ fi; \ }; \ please_report () \ { \ echo "Some test(s) failed. Please report this to $(PACKAGE_BUGREPORT),"; \ echo "together with the test-suite.log file (gzipped) and your system"; \ echo "information. Thanks."; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ output_system_information; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG) for debugging.$${std}";\ if test -n "$(PACKAGE_BUGREPORT)"; then \ please_report | sed -e "s/^/$${col}/" -e s/'$$'/"$${std}"/; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: $(check_PROGRAMS) @$(am__rm_f) $(RECHECK_LOGS) @$(am__rm_f) $(RECHECK_LOGS:.log=.trs) @$(am__rm_f) $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @$(am__rm_f) $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? tst_imap_msgs.log: tst_imap_msgs$(EXEEXT) @p='tst_imap_msgs$(EXEEXT)'; \ b='tst_imap_msgs'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tst_imap_utf7.log: tst_imap_utf7$(EXEEXT) @p='tst_imap_utf7$(EXEEXT)'; \ b='tst_imap_utf7'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) tst_msg_cvt.log: tst_msg_cvt$(EXEEXT) @p='tst_msg_cvt$(EXEEXT)'; \ b='tst_msg_cvt'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(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 $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(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: -$(am__rm_f) $(TEST_LOGS) -$(am__rm_f) $(TEST_LOGS:.log=.trs) -$(am__rm_f) $(TEST_SUITE_LOG) clean-generic: -$(am__rm_f) $(CLEANFILES) distclean-generic: -$(am__rm_f) $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || $(am__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-checkPROGRAMS clean-generic \ mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/config.Po -rm -f ./$(DEPDIR)/driver.Po -rm -f ./$(DEPDIR)/drv_imap.Po -rm -f ./$(DEPDIR)/drv_maildir.Po -rm -f ./$(DEPDIR)/drv_proxy.Po -rm -f ./$(DEPDIR)/imap_msgs.Po -rm -f ./$(DEPDIR)/imap_utf7.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/main_list.Po -rm -f ./$(DEPDIR)/main_sync.Po -rm -f ./$(DEPDIR)/mdconvert.Po -rm -f ./$(DEPDIR)/socket.Po -rm -f ./$(DEPDIR)/sync.Po -rm -f ./$(DEPDIR)/sync_msg_cvt.Po -rm -f ./$(DEPDIR)/sync_state.Po -rm -f ./$(DEPDIR)/tst_imap_msgs.Po -rm -f ./$(DEPDIR)/tst_imap_utf7.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-util.Po -rm -f ./$(DEPDIR)/tst_timers.Po -rm -f ./$(DEPDIR)/util.Po -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-exampleDATA install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 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 ./$(DEPDIR)/config.Po -rm -f ./$(DEPDIR)/driver.Po -rm -f ./$(DEPDIR)/drv_imap.Po -rm -f ./$(DEPDIR)/drv_maildir.Po -rm -f ./$(DEPDIR)/drv_proxy.Po -rm -f ./$(DEPDIR)/imap_msgs.Po -rm -f ./$(DEPDIR)/imap_utf7.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/main_list.Po -rm -f ./$(DEPDIR)/main_sync.Po -rm -f ./$(DEPDIR)/mdconvert.Po -rm -f ./$(DEPDIR)/socket.Po -rm -f ./$(DEPDIR)/sync.Po -rm -f ./$(DEPDIR)/sync_msg_cvt.Po -rm -f ./$(DEPDIR)/sync_state.Po -rm -f ./$(DEPDIR)/tst_imap_msgs.Po -rm -f ./$(DEPDIR)/tst_imap_utf7.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-sync_msg_cvt.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-tst_msg_cvt.Po -rm -f ./$(DEPDIR)/tst_msg_cvt-util.Po -rm -f ./$(DEPDIR)/tst_timers.Po -rm -f ./$(DEPDIR)/util.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-exampleDATA \ uninstall-man uninstall-man: uninstall-man1 .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ check-am clean clean-binPROGRAMS clean-checkPROGRAMS \ clean-generic cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic 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-exampleDATA install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-man1 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 pdf pdf-am ps ps-am recheck tags tags-am \ uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-exampleDATA uninstall-man uninstall-man1 .PRECIOUS: Makefile drv_proxy.$(OBJEXT): drv_proxy.inc drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc # 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: # Tell GNU make to disable its built-in pattern rules. %:: %,v %:: RCS/%,v %:: RCS/% %:: s.% %:: SCCS/s.% isync-1.5.1/src/imap_p.h0000644000175000001440000000326714405565305010544 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #ifndef IMAP_P_H #define IMAP_P_H #include "driver.h" //#define DEBUG_IMAP_MSGS //#define DEBUG_IMAP_UTF7 typedef union imap_message { message_t gen; struct { MESSAGE(union imap_message) union imap_message *prev; // Used to optimize lookup by seq. // This is made relative once the fetches complete - to avoid that // each expunge re-enumerates all subsequent messages. Dead messages // "occupy" no sequence number themselves, but may still jump a gap. // Note that use of sequence numbers to address messages in commands // imposes limitations on permissible pipelining. We don't do that, // so this is of no concern; however, we might miss the closing of // a gap, which would result in a tiny performance hit. uint seq; }; } imap_message_t; typedef struct { imap_message_t *head; imap_message_t **tail; // Bulk changes (which is where performance matters) are assumed to be // reported sequentially (be it forward or reverse), so walking the // sorted linked list from the previously used message is efficient. imap_message_t *cursor_ptr; uint cursor_seq; uint count; } imap_messages_t; imap_message_t *imap_new_msg( imap_messages_t *msgs ); imap_message_t *imap_expunge_msg( imap_messages_t *msgs, uint fseq ); void reset_imap_messages( imap_messages_t *msgs ); void imap_ensure_relative( imap_messages_t *msgs ); void imap_ensure_absolute( imap_messages_t *msgs ); char *imap_utf8_to_utf7( const char *buf ); int imap_utf7_to_utf8( const char *buf, int argl, char *outbuf ); #endif isync-1.5.1/src/sync.c0000644000175000001440000017236714720577152010261 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #include "sync_p.h" channel_conf_t global_conf; channel_conf_t *channels; group_conf_t *groups; uint BufferLimit = 10 * 1024 * 1024; int new_total[2], new_done[2]; int flags_total[2], flags_done[2]; int trash_total[2], trash_done[2]; int expunge_total[2], expunge_done[2]; static void sync_ref( sync_vars_t *svars ) { ++svars->ref_count; } static void sync_deref( sync_vars_t *svars ); static int check_cancel( sync_vars_t *svars ); #define AUX &svars->t[t] #define INV_AUX &svars->t[t^1] #define DECL_SVARS \ int t; \ sync_vars_t *svars #define INIT_SVARS(aux) \ t = *(int *)aux; \ svars = (sync_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(sync_vars_t, t)) #define DECL_INIT_SVARS(aux) \ int t = *(int *)aux; \ sync_vars_t *svars = (sync_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(sync_vars_t, t)) /* operation dependencies: select(x): - load(x): select(x) new(F), new(N), flags(F), flags(N): load(F) & load(N) find_new(x): new(x) trash(x): flags(x) close(x): trash(x) & flags(!x) & find_new(x) & new(!x) // with expunge cleanup: close(F) & close(N) */ #define sync_sts_enum(fn) \ fn(ST, PRESENT) \ fn(ST, CONFIRMED) \ fn(ST, SELECTED) \ fn(ST, FIND_OLD) \ fn(ST, LOADED) \ fn(ST, SENT_FLAGS) \ fn(ST, SENDING_NEW) \ fn(ST, SENT_NEW) \ fn(ST, FIND_NEW) \ fn(ST, FOUND_NEW) \ fn(ST, SENT_TRASH) \ fn(ST, TRASH_BAD) \ fn(ST, CLOSING) \ fn(ST, CLOSED) \ fn(ST, SENT_CANCEL) \ fn(ST, CANCELED) DEFINE_PFX_BIT_ENUM(sync_sts_enum) static uchar sanitize_flags( uchar tflags, sync_vars_t *svars, int t ) { if (Verbosity >= TERSE) { // We complain only once per flag per store - even though _theoretically_ // each mailbox can support different flags according to the IMAP spec. uchar bflags = tflags & ~(svars->good_flags[t] | svars->bad_flags[t]); if (bflags) { notice( "Notice: %s store does not support flag(s) '%s'; not propagating.\n", str_fn[t], fmt_flags( bflags ).str ); svars->bad_flags[t] |= bflags; } } return tflags & svars->good_flags[t]; } enum { COPY_OK, COPY_GONE, COPY_NOGOOD, COPY_CANCELED, COPY_FAIL, }; static void msg_fetched( int sts, void *aux ); static void copy_msg( copy_vars_t *vars ) { DECL_INIT_SVARS(vars->aux); t ^= 1; vars->data.flags = vars->msg->flags; vars->data.date = svars->chan->use_internal_date ? -1 : 0; svars->drv[t]->fetch_msg( svars->ctx[t], vars->msg, &vars->data, vars->minimal, msg_fetched, vars ); } static void msg_stored( int sts, uint uid, void *aux ); static void msg_fetched( int sts, void *aux ) { copy_vars_t *vars = (copy_vars_t *)aux; sync_rec_t *srec = vars->srec; DECL_SVARS; int scr, tcr; switch (sts) { case DRV_OK: INIT_SVARS(vars->aux); if (check_cancel( svars )) { free( vars->data.data ); vars->cb( COPY_CANCELED, 0, vars ); return; } if (srec && (srec->status & S_UPGRADE)) { vars->data.flags = (srec->pflags | srec->aflags[t]) & ~srec->dflags[t]; if (srec->aflags[t] || srec->dflags[t]) { JLOG( "$ %u %u %u %u", (srec->uid[F], srec->uid[N], srec->aflags[t], srec->dflags[t]), "%sing upgrade with flags: +%s -%s", (str_hl[t], fmt_flags( srec->aflags[t] ).str, fmt_flags( srec->dflags[t] ).str) ); } } else { vars->data.flags = sanitize_flags( vars->data.flags, svars, t ); if (srec) { if (srec->status & S_DUMMY(t)) vars->data.flags &= ~F_FLAGGED; if (vars->data.flags) { srec->pflags = vars->data.flags; JLOG( "%% %u %u %u", (srec->uid[F], srec->uid[N], srec->pflags), "%sing with flags %s", (str_hl[t], fmt_lone_flags( srec->pflags ).str) ); } } } scr = svars->can_crlf[t^1]; tcr = svars->can_crlf[t]; if (srec || scr != tcr) { const char *err; if ((err = copy_msg_convert( scr, tcr, vars ))) { error( "Error: message %u from %s %s; skipping.\n", vars->msg->uid, str_fn[t^1], err ); svars->ret |= SYNC_FAIL; vars->cb( COPY_NOGOOD, 0, vars ); return; } } svars->drv[t]->store_msg( svars->ctx[t], &vars->data, !srec, msg_stored, vars ); break; case DRV_CANCELED: vars->cb( COPY_CANCELED, 0, vars ); break; case DRV_MSG_BAD: if (vars->msg->status & M_DEAD) { // The message was expunged under our feet; this is no error. vars->cb( COPY_GONE, 0, vars ); } else { INIT_SVARS(vars->aux); // Driver already reported error. svars->ret |= SYNC_FAIL; vars->cb( COPY_NOGOOD, 0, vars ); } break; default: // DRV_BOX_BAD vars->cb( COPY_FAIL, 0, vars ); break; } } static void msg_stored( int sts, uint uid, void *aux ) { copy_vars_t *vars = (copy_vars_t *)aux; DECL_SVARS; switch (sts) { case DRV_OK: vars->cb( COPY_OK, uid, vars ); break; case DRV_CANCELED: vars->cb( COPY_CANCELED, 0, vars ); break; case DRV_MSG_BAD: INIT_SVARS(vars->aux); // Driver already reported error, but we still need to report the source. error( "Error: %s refuses to store message %u from %s.\n", str_fn[t], vars->msg->uid, str_fn[t^1] ); svars->ret |= SYNC_FAIL; vars->cb( COPY_NOGOOD, 0, vars ); break; default: // DRV_BOX_BAD vars->cb( COPY_FAIL, 0, vars ); break; } } static void sync_bail( sync_vars_t *svars ); static void sync_bail2( sync_vars_t *svars ); static void sync_bail3( sync_vars_t *svars ); static void cancel_done( void *aux ); static void cancel_sync( sync_vars_t *svars ) { int state1 = svars->state[1]; for (int t = 0; ; t++) { if (svars->ret & SYNC_BAD(t)) { cancel_done( AUX ); } else if (!(svars->state[t] & ST_SENT_CANCEL)) { /* ignore subsequent failures from in-flight commands */ svars->state[t] |= ST_SENT_CANCEL; svars->drv[t]->cancel_cmds( svars->ctx[t], cancel_done, AUX ); } if (t || (state1 & ST_CANCELED)) break; } } static void cancel_done( void *aux ) { DECL_INIT_SVARS(aux); svars->state[t] |= ST_CANCELED; if (svars->state[t^1] & ST_CANCELED) { if (svars->nfp) { Fclose( svars->nfp, 0 ); Fclose( svars->jfp, 0 ); } sync_bail( svars ); } } static void store_bad( void *aux ) { DECL_INIT_SVARS(aux); svars->drv[t]->cancel_store( svars->ctx[t] ); svars->ret |= SYNC_BAD(t); cancel_sync( svars ); } static int check_cancel( sync_vars_t *svars ) { return (svars->state[F] | svars->state[N]) & (ST_SENT_CANCEL | ST_CANCELED); } static int check_ret( int sts, void *aux ) { if (sts == DRV_CANCELED) return 1; DECL_INIT_SVARS(aux); if (sts == DRV_BOX_BAD) { svars->ret |= SYNC_FAIL; cancel_sync( svars ); return 1; } return 0; } #define SVARS_CHECK_RET \ if (check_ret( sts, aux )) \ return; \ DECL_INIT_SVARS(aux) // After drv->cancel_cmds() on our side, commands may still complete // successfully, while the other side is already dead. #define SVARS_CHECK_RET_CANCEL \ SVARS_CHECK_RET; \ if (check_cancel( svars )) \ return #define SVARS_CHECK_RET_VARS(type) \ type *vars = (type *)aux; \ if (check_ret( sts, vars->aux )) { \ free( vars ); \ return; \ } \ DECL_INIT_SVARS(vars->aux) static void message_expunged( message_t *msg, void *aux ) { DECL_INIT_SVARS(aux); (void)svars; if (msg->srec) { msg->srec->status |= S_GONE(t); msg->srec->msg[t] = NULL; msg->srec = NULL; } if (msg->status & M_EXPUNGE) { expunge_done[t]++; stats(); } } static void box_confirmed( int sts, uint uidvalidity, void *aux ); static void box_confirmed2( sync_vars_t *svars, int t ); static void box_deleted( int sts, void *aux ); static void box_created( int sts, void *aux ); static void box_opened( int sts, uint uidvalidity, void *aux ); static void box_opened2( sync_vars_t *svars, int t ); static void load_box( sync_vars_t *svars, int t, uint minwuid, uint_array_t mexcs ); void sync_boxes( store_t *ctx[], const char * const names[], int present[], channel_conf_t *chan, void (*cb)( int sts, void *aux ), void *aux ) { sync_vars_t *svars; int t; svars = nfzalloc( sizeof(*svars) ); svars->t[1] = 1; svars->ref_count = 1; svars->cb = cb; svars->aux = aux; svars->ctx[0] = ctx[0]; svars->ctx[1] = ctx[1]; svars->chan = chan; svars->lfd = -1; svars->uidval[0] = svars->uidval[1] = UIDVAL_BAD; svars->srecadd = &svars->srecs; for (t = 0; t < 2; t++) { svars->orig_name[t] = (!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ? "INBOX" : names[t]; if (!ctx[t]->conf->flat_delim[0]) { svars->box_name[t] = nfstrdup( svars->orig_name[t] ); } else if (map_name( svars->orig_name[t], -1, &svars->box_name[t], 0, "/", ctx[t]->conf->flat_delim ) < 0) { error( "Error: canonical mailbox name '%s' contains flattened hierarchy delimiter\n", svars->orig_name[t] ); bail3: svars->ret = SYNC_FAIL; sync_bail3( svars ); return; } svars->drv[t] = ctx[t]->driver; svars->drv[t]->set_callbacks( ctx[t], message_expunged, store_bad, AUX ); svars->can_crlf[t] = (svars->drv[t]->get_caps( svars->ctx[t] ) / DRV_CRLF) & 1; } /* Both boxes must be fully set up at this point, so that error exit paths * don't run into uninitialized variables. */ for (t = 0; t < 2; t++) { switch (svars->drv[t]->select_box( ctx[t], svars->box_name[t] )) { case DRV_STORE_BAD: store_bad( AUX ); return; case DRV_BOX_BAD: goto bail3; } } if (!prepare_state( svars )) { svars->ret = SYNC_FAIL; sync_bail2( svars ); return; } if (!load_state( svars )) { svars->ret = SYNC_FAIL; sync_bail( svars ); return; } sync_ref( svars ); for (t = 0; ; t++) { info( "Opening %s box %s...\n", str_fn[t], svars->orig_name[t] ); if (present[t] == BOX_ABSENT) { box_confirmed2( svars, t ); } else { if (present[t] == BOX_PRESENT) svars->state[t] |= ST_PRESENT; svars->drv[t]->open_box( ctx[t], box_confirmed, AUX ); } if (t || check_cancel( svars )) break; } sync_deref( svars ); } static void box_confirmed( int sts, uint uidvalidity, void *aux ) { if (sts == DRV_CANCELED) return; DECL_INIT_SVARS(aux); if (check_cancel( svars )) return; if (sts == DRV_OK) { svars->state[t] |= ST_PRESENT; svars->newuidval[t] = uidvalidity; } else if (svars->state[t] & ST_PRESENT) { error( "Error: channel %s: %s box %s cannot be opened.\n", svars->chan->name, str_fn[t], svars->orig_name[t] ); svars->ret |= SYNC_FAIL; cancel_sync( svars ); return; } box_confirmed2( svars, t ); } static void box_confirmed2( sync_vars_t *svars, int t ) { svars->state[t] |= ST_CONFIRMED; if (!(svars->state[t^1] & ST_CONFIRMED)) return; sync_ref( svars ); for (t = 0; ; t++) { if (!(svars->state[t] & ST_PRESENT)) { if (!(svars->state[t^1] & ST_PRESENT)) { if (!svars->existing) { error( "Error: channel %s: both far side %s and near side %s cannot be opened.\n", svars->chan->name, svars->orig_name[F], svars->orig_name[N] ); bail: svars->ret = SYNC_FAIL; } else { /* This can legitimately happen if a deletion propagation was interrupted. * We have no place to record this transaction, so we just assume it. * Of course this bears the danger of clearing the state if both mailboxes * temporarily cannot be opened for some weird reason (while the stores can). */ delete_state( svars ); } done: sync_bail( svars ); break; } if (svars->existing) { if (!(svars->chan->ops[t^1] & OP_REMOVE)) { error( "Error: channel %s: %s box %s cannot be opened anymore.\n", svars->chan->name, str_fn[t], svars->orig_name[t] ); goto bail; } if (svars->drv[t^1]->confirm_box_empty( svars->ctx[t^1] ) != DRV_OK) { warn( "Warning: channel %s: %s box %s cannot be opened anymore, and %s box %s is not empty.\n", svars->chan->name, str_fn[t], svars->orig_name[t], str_fn[t^1], svars->orig_name[t^1] ); goto done; } info( "Deleting %s box %s...\n", str_fn[t^1], svars->orig_name[t^1] ); svars->drv[t^1]->delete_box( svars->ctx[t^1], box_deleted, INV_AUX ); } else { if (!(svars->chan->ops[t] & OP_CREATE)) { box_opened( DRV_BOX_BAD, UIDVAL_BAD, AUX ); } else { info( "Creating %s box %s...\n", str_fn[t], svars->orig_name[t] ); svars->drv[t]->create_box( svars->ctx[t], box_created, AUX ); } } } else { box_opened2( svars, t ); } if (t || check_cancel( svars )) break; } sync_deref( svars ); } static void box_deleted( int sts, void *aux ) { SVARS_CHECK_RET_CANCEL; delete_state( svars ); svars->drv[t]->finish_delete_box( svars->ctx[t] ); sync_bail( svars ); } static void box_created( int sts, void *aux ) { SVARS_CHECK_RET_CANCEL; svars->drv[t]->open_box( svars->ctx[t], box_opened, AUX ); } static void box_opened( int sts, uint uidvalidity, void *aux ) { if (sts == DRV_CANCELED) return; DECL_INIT_SVARS(aux); if (check_cancel( svars )) return; if (sts == DRV_BOX_BAD) { error( "Error: channel %s: %s box %s cannot be opened.\n", svars->chan->name, str_fn[t], svars->orig_name[t] ); svars->ret = SYNC_FAIL; sync_bail( svars ); } else { svars->newuidval[t] = uidvalidity; box_opened2( svars, t ); } } static void box_opened2( sync_vars_t *svars, int t ) { store_t *ctx[2]; channel_conf_t *chan; sync_rec_t *srec; uint_array_alloc_t mexcs; uint opts[2], minwuid; svars->state[t] |= ST_SELECTED; if (!(svars->state[t^1] & ST_SELECTED)) return; ctx[0] = svars->ctx[0]; ctx[1] = svars->ctx[1]; chan = svars->chan; if (!lock_state( svars )) { bail: svars->ret = SYNC_FAIL; sync_bail( svars ); return; } int any_dummies[2] = { 0, 0 }; int any_purges[2] = { 0, 0 }; int any_upgrades[2] = { 0, 0 }; int any_old[2] = { 0, 0 }; int any_new[2] = { 0, 0 }; int any_tuids[2] = { 0, 0 }; if (svars->replayed || ((chan->ops[F] | chan->ops[N]) & OP_UPGRADE)) { for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; if (srec->status & S_DUMMY(F)) any_dummies[F]++; else if (srec->status & S_DUMMY(N)) any_dummies[N]++; else if (srec->status & S_SKIPPED) any_dummies[!srec->uid[F] ? F : N]++; if (!svars->replayed) continue; if ((shifted_bit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED) svars->any_expiring = 1; if (srec->status & S_PURGE) { any_purges[srec->uid[F] ? F : N]++; } else if (srec->status & S_PENDING) { t = !srec->uid[F] ? F : N; if (srec->status & S_UPGRADE) any_upgrades[t]++; else if (srec->uid[t^1] <= svars->maxuid[t^1]) any_old[t]++; else any_new[t]++; if (srec->tuid[0]) any_tuids[t]++; } } } opts[F] = opts[N] = 0; for (t = 0; t < 2; t++) { if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t]) { opts[F] |= OPEN_PAIRED | OPEN_PAIRED_IDS; opts[N] |= OPEN_PAIRED | OPEN_PAIRED_IDS; } if (any_purges[t]) { debug( "resuming %d %s purge(s)\n", any_purges[t], str_fn[t] ); opts[t] |= OPEN_SETFLAGS; } if (any_tuids[t]) { debug( "finding %d %sed message(s)\n", any_tuids[t], str_hl[t] ); opts[t] |= OPEN_NEW | OPEN_FIND; svars->state[t] |= ST_FIND_OLD; } if (chan->ops[t] & (OP_GONE | OP_FLAGS)) { opts[t] |= OPEN_SETFLAGS; opts[t^1] |= OPEN_PAIRED; if (chan->ops[t] & OP_FLAGS) opts[t^1] |= OPEN_FLAGS; } if (!any_dummies[t] && (chan->ops[t] & OP_UPGRADE)) { chan->ops[t] &= ~OP_UPGRADE; debug( "no %s dummies; masking Upgrade\n", str_fn[t] ); } if ((chan->ops[t] & (OP_OLD | OP_NEW | OP_UPGRADE)) || any_old[t] || any_new[t] || any_upgrades[t]) { opts[t] |= OPEN_APPEND; if ((chan->ops[t] & OP_OLD) || any_old[t]) { debug( "resuming %s of %d old message(s)\n", str_hl[t], any_old[t] ); opts[t^1] |= OPEN_OLD; if (chan->stores[t]->max_size != UINT_MAX) opts[t^1] |= OPEN_OLD_SIZE; } if ((chan->ops[t] & OP_NEW) || any_new[t]) { debug( "resuming %s of %d new message(s)\n", str_hl[t], any_new[t] ); opts[t^1] |= OPEN_NEW; if (chan->stores[t]->max_size != UINT_MAX) opts[t^1] |= OPEN_NEW_SIZE; } if ((chan->ops[t] & OP_UPGRADE) || any_upgrades[t]) { debug( "resuming %s of %d upgrade(s)\n", str_hl[t], any_upgrades[t] ); if (chan->ops[t] & OP_UPGRADE) opts[t] |= OPEN_PAIRED | OPEN_FLAGS | OPEN_SETFLAGS; opts[t^1] |= OPEN_PAIRED; } if ((chan->ops[t] | chan->ops[t^1]) & OP_EXPUNGE) // Don't propagate doomed msgs opts[t^1] |= OPEN_FLAGS; } if (chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) { opts[t] |= OPEN_EXPUNGE; if (chan->ops[t] & OP_EXPUNGE_SOLO) { opts[t] |= OPEN_OLD | OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE; opts[t^1] |= OPEN_OLD; } else if (chan->stores[t]->trash) { if (!chan->stores[t]->trash_only_new) opts[t] |= OPEN_OLD; opts[t] |= OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE; } else if (chan->stores[t^1]->trash && chan->stores[t^1]->trash_remote_new) { opts[t] |= OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE; } } } // While only new messages can cause expiration due to displacement, // updating flags can cause expiration of already overdue messages. // The latter would also apply when the expired box is the source, // but it's more natural to treat it as read-only in that case. // OP_UPGRADE makes sense only for legacy S_SKIPPED entries. int xt = chan->expire_side; if ((chan->ops[xt] & (OP_OLD | OP_NEW | OP_UPGRADE | OP_FLAGS)) && chan->max_messages) svars->any_expiring = 1; if (svars->any_expiring) { opts[xt] |= OPEN_PAIRED | OPEN_FLAGS; if (any_dummies[xt]) opts[xt^1] |= OPEN_PAIRED | OPEN_FLAGS; else if (chan->ops[xt] & (OP_OLD | OP_NEW | OP_UPGRADE)) opts[xt^1] |= OPEN_FLAGS; } for (t = 0; t < 2; t++) { svars->opts[t] = svars->drv[t]->prepare_load_box( ctx[t], opts[t] ); if (opts[t] & ~svars->opts[t] & OPEN_UID_EXPUNGE) { if (chan->ops[t] & OP_EXPUNGE_SOLO) { error( "Error: Store %s does not support ExpungeSolo.\n", svars->chan->stores[t]->name ); goto bail; } if (!ctx[t]->racy_trash) { ctx[t]->racy_trash = 1; notice( "Notice: Trashing in Store %s is prone to race conditions.\n", svars->chan->stores[t]->name ); } } } ARRAY_INIT( &mexcs ); if ((svars->opts[xt^1] & OPEN_PAIRED) && !(svars->opts[xt^1] & OPEN_OLD) && chan->max_messages) { /* When messages have been expired on one side, the other side's fetch is split into * two ranges: The bulk fetch which corresponds with the most recent messages, and an * exception list of messages which would have been expired if they weren't important. */ debug( "preparing %s selection - max expired %s uid is %u\n", str_fn[xt^1], str_fn[xt^1], svars->maxxfuid ); /* First, find out the lower bound for the bulk fetch. */ minwuid = svars->maxxfuid + 1; /* Next, calculate the exception fetch. */ for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; if (!srec->uid[xt^1]) continue; // No message; other state is irrelevant if (srec->uid[xt^1] >= minwuid) continue; // Message is in non-expired range if ((svars->opts[xt^1] & OPEN_NEW) && srec->uid[xt^1] > svars->maxuid[xt^1]) continue; // Message is in expired range, but new range overlaps that if (!srec->uid[xt] && !(srec->status & S_PENDING)) continue; // Only actually paired up messages matter // The pair is alive, but outside the bulk range *uint_array_append( &mexcs ) = srec->uid[xt^1]; } sort_uint_array( mexcs.array ); } else { minwuid = 1; } sync_ref( svars ); load_box( svars, xt^1, minwuid, mexcs.array ); if (!check_cancel( svars )) load_box( svars, xt, 1, (uint_array_t){ NULL, 0 } ); sync_deref( svars ); } static uint get_seenuid( sync_vars_t *svars, int t ) { uint seenuid = 0; for (sync_rec_t *srec = svars->srecs; srec; srec = srec->next) if (!(srec->status & S_DEAD) && seenuid < srec->uid[t]) seenuid = srec->uid[t]; return seenuid; } static void box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ); static void load_box( sync_vars_t *svars, int t, uint minwuid, uint_array_t mexcs ) { uint maxwuid = 0, pairuid = UINT_MAX; if (svars->opts[t] & OPEN_NEW) { if (svars->opts[t] & OPEN_OLD) { svars->opts[t] |= OPEN_PAIRED; minwuid = 1; } else if (!(svars->opts[t] & OPEN_PAIRED) || (minwuid > svars->maxuid[t] + 1)) { minwuid = svars->maxuid[t] + 1; } maxwuid = UINT_MAX; if (svars->opts[t] & OPEN_PAIRED_IDS) // Implies OPEN_PAIRED pairuid = get_seenuid( svars, t ); } else if (svars->opts[t] & (OPEN_PAIRED | OPEN_OLD)) { uint seenuid = get_seenuid( svars, t ); if (svars->opts[t] & OPEN_OLD) { minwuid = 1; maxwuid = svars->maxuid[t]; if (maxwuid < seenuid) { if (svars->opts[t] & OPEN_PAIRED) maxwuid = seenuid; } else { svars->opts[t] |= OPEN_PAIRED; } } else { // OPEN_PAIRED maxwuid = seenuid; } } else { minwuid = UINT_MAX; } info( "Loading %s box...\n", str_fn[t] ); svars->drv[t]->load_box( svars->ctx[t], minwuid, maxwuid, svars->finduid[t], pairuid, svars->maxuid[t], mexcs, box_loaded, AUX ); } typedef struct { sync_rec_t *srec; uchar flags; } alive_srec_t; static int cmp_srec_far( const void *a, const void *b ) { uint au = (*(const alive_srec_t *)a).srec->uid[F]; uint bu = (*(const alive_srec_t *)b).srec->uid[F]; assert( au && bu ); assert( au != bu ); return au > bu ? 1 : -1; // Can't subtract, the result might not fit into signed int. } static int cmp_srec_near( const void *a, const void *b ) { uint au = (*(const alive_srec_t *)a).srec->uid[N]; uint bu = (*(const alive_srec_t *)b).srec->uid[N]; assert( au && bu ); assert( au != bu ); return au > bu ? 1 : -1; // Can't subtract, the result might not fit into signed int. } typedef struct { void *aux; sync_rec_t *srec; int aflags, dflags; } flag_vars_t; static void flags_set( int sts, void *aux ); static void flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t ); static void msgs_flags_set( sync_vars_t *svars, int t ); static void msg_copied( int sts, uint uid, copy_vars_t *vars ); static void msgs_copied( sync_vars_t *svars, int t ); static void box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ) { sync_rec_t *srec, **srecmap; message_t *tmsg; flag_vars_t *fv; int no[2], del[2]; uchar sflags, nflags, aflags, dflags; uint hashsz, idx; SVARS_CHECK_RET_CANCEL; svars->state[t] |= ST_LOADED; svars->msgs[t] = msgs; info( "%s: %d messages, %d recent\n", str_fn[t], total_msgs, recent_msgs ); if (svars->state[t] & ST_FIND_OLD) { debug( "matching previously copied messages on %s\n", str_fn[t] ); for (; msgs && msgs->uid < svars->finduid[t]; msgs = msgs->next) {} match_tuids( svars, t, msgs ); } debug( "matching messages on %s against sync records\n", str_fn[t] ); hashsz = bucketsForSize( svars->nsrecs * 3 ); srecmap = nfzalloc( hashsz * sizeof(*srecmap) ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; uint uid = srec->uid[t]; if (!uid) continue; idx = (uint)(uid * 1103515245U) % hashsz; while (srecmap[idx]) if (++idx == hashsz) idx = 0; srecmap[idx] = srec; } for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) { if (tmsg->srec) /* found by TUID */ continue; uint uid = tmsg->uid; idx = (uint)(uid * 1103515245U) % hashsz; while ((srec = srecmap[idx])) { if (srec->uid[t] == uid) goto found; if (++idx == hashsz) idx = 0; } continue; found: tmsg->srec = srec; srec->msg[t] = tmsg; } free( srecmap ); if (!(svars->state[t^1] & ST_LOADED)) return; for (t = 0; t < 2; t++) { if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t]) { // This code checks whether the messages with known UIDs are actually the // same messages, as recognized by their Message-IDs. unsigned need = 0, got = 0; debug( "trying to re-approve uid validity of %s\n", str_fn[t] ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; need++; if (!srec->msg[t]) continue; // Message disappeared. // Present paired messages require re-validation. if (!srec->msg[t]->msgid) continue; // Messages without ID are useless for re-validation. if (!srec->msg[t^1]) continue; // Partner disappeared. if (!srec->msg[t^1]->msgid || strcmp( srec->msg[F]->msgid, srec->msg[N]->msgid )) { if (svars->uidval[t^1] != svars->newuidval[t^1]) { error( "Error: channel %s, %s box %s (at UID %u):" " Unable to recover from both-sided UIDVALIDITY change," " as it is genuine on at least one side.\n", svars->chan->name, str_fn[t], svars->orig_name[t], srec->uid[t] ); } else { error( "Error: channel %s, %s box %s (at UID %u): UIDVALIDITY genuinely changed.\n", svars->chan->name, str_fn[t], svars->orig_name[t], srec->uid[t] ); } uvchg: svars->ret |= SYNC_FAIL; cancel_sync( svars ); return; } got++; } // We encountered no messages that contradict the hypothesis that the // UIDVALIDITY change was spurious. // If we got enough messages confirming the hypothesis, we just accept it. // If there aren't quite enough messages, we check that at least 80% of // those previously present are still there and confirm the hypothesis; // this also covers the case of a box that was already empty. if (got < 20 && got * 5 < need * 4) { // Too few confirmed messages. This is very likely in the drafts folder. // A proper fallback would be fetching more headers (which potentially need // normalization) or the message body (which should be truncated for sanity) // and comparing. error( "Error: channel %s, %s box %s: Unable to recover from UIDVALIDITY change.\n", svars->chan->name, str_fn[t], svars->orig_name[t] ); goto uvchg; } notice( "Notice: channel %s, %s box %s: Recovered from change of UIDVALIDITY.\n", svars->chan->name, str_fn[t], svars->orig_name[t] ); svars->uidval[t] = UIDVAL_BAD; } } if (svars->uidval[F] == UIDVAL_BAD || svars->uidval[N] == UIDVAL_BAD) { svars->uidval[F] = svars->newuidval[F]; svars->uidval[N] = svars->newuidval[N]; JLOG( "| %u %u", (svars->uidval[F], svars->uidval[N]), "new UIDVALIDITYs" ); } svars->oldmaxuid[F] = svars->newmaxuid[F]; svars->oldmaxuid[N] = svars->newmaxuid[N]; info( "Synchronizing...\n" ); int xt = svars->chan->expire_side; for (t = 0; t < 2; t++) svars->good_flags[t] = (uchar)svars->drv[t]->get_supported_flags( svars->ctx[t] ); int any_new[2] = { 0, 0 }; debug( "synchronizing old entries\n" ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; debug( "pair (%u,%u)\n", srec->uid[F], srec->uid[N] ); assert( !srec->tuid[0] ); // no[] means that a message is known to be not there. no[F] = !srec->msg[F] && (svars->opts[F] & OPEN_PAIRED); no[N] = !srec->msg[N] && (svars->opts[N] & OPEN_PAIRED); if (no[F] && no[N]) { // It does not matter whether one side was already known to be missing // (never stored [skipped or failed] or expunged [possibly expired]) - // now both are missing, so the entry is superfluous. srec->status = S_DEAD; JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "both missing" ); } else { // del[] means that a message becomes known to have been expunged. del[F] = no[F] && srec->uid[F]; del[N] = no[N] && srec->uid[N]; sync_rec_t *nsrec = srec; for (t = 0; t < 2; t++) { // Do this before possibly upgrading that side. if (srec->msg[t] && (srec->msg[t]->flags & F_DELETED)) srec->status |= S_DEL(t); // Flagging the message on the target side causes an upgrade of the dummy. // We do this first in a separate loop, so flag propagation sees the upgraded // state for both sides. After a journal replay, that would be the case anyway. if ((svars->chan->ops[t] & OP_UPGRADE) && (srec->status & S_DUMMY(t)) && srec->uid[t^1] && srec->msg[t]) { sflags = srec->msg[t]->flags; if (sflags & F_FLAGGED) { sflags &= ~(F_SEEN | F_FLAGGED) | (srec->flags & F_SEEN); // As below. // We save away the dummy's flags, because after an // interruption it may be already gone. srec->pflags = sflags; JLOG( "^ %u %u %u", (srec->uid[F], srec->uid[N], srec->pflags), "upgrading %s placeholder, dummy's flags %s", (str_fn[t], fmt_lone_flags( srec->pflags ).str) ); nsrec = upgrade_srec( svars, srec, t ); } } } for (t = 0; t < 2; t++) { if (srec->status & S_UPGRADE) { // Such records hold orphans by definition, so the del[] cases are irrelevant. if (srec->uid[t]) { // Direction towards the source message. // The placeholder was already detached, so use its saved flags instead. sflags = srec->pflags; goto doflags; } // Direction towards the copy. if (srec->msg[t^1]) { // Flag propagation along placeholder upgrades must be explicitly requested, // and is, at the source, handled like any other flag update. sflags = srec->msg[t^1]->flags; goto doflags; } debug( " no %s\n", str_fn[t^1] ); } else if (del[t]) { // The target was newly expunged, so there is nothing to update. // The deletion is propagated in the opposite iteration. srec->status |= S_GONE(t); } else if (!srec->uid[t]) { // The target was never stored, or was previously expunged, so there // is nothing to update. // Note: the opposite UID must be valid, as otherwise the entry would // have been pruned already. } else if (del[t^1]) { // The source was newly expunged, so possibly propagate the deletion. // The target may be in an unknown state (not fetched). if ((t != xt) && (srec->status & (S_EXPIRE | S_EXPIRED))) { /* Don't propagate deletion resulting from expiration. */ if (~srec->status & (S_EXPIRE | S_EXPIRED)) { // An expiration was interrupted, but the message was expunged since. srec->status |= S_EXPIRE | S_EXPIRED; // Override failed unexpiration attempts. JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "forced expiration commit" ); } JLOG( "%c %u %u 0", ("<>"[xt], srec->uid[F], srec->uid[N]), "%s expired, orphaning %s", (str_fn[xt], str_fn[xt^1]) ); srec->uid[xt] = 0; } else { if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS) && // Ignore deleted flag, as that's what we'll change ourselves ... (((srec->msg[t]->flags & ~F_DELETED) != (srec->flags & ~F_DELETED)) || // ... except for undeletion, as that's the opposite. (!(srec->msg[t]->flags & F_DELETED) && (srec->flags & F_DELETED)))) notice( "Notice: conflicting changes in (%u,%u)\n", srec->uid[F], srec->uid[N] ); if (svars->chan->ops[t] & OP_GONE) { debug( " %sing delete\n", str_hl[t] ); srec->aflags[t] = F_DELETED; srec->status |= S_DELETE; } else { debug( " not %sing delete\n", str_hl[t] ); } } } else if (!srec->msg[t^1]) { // We have no source to work with, because it was never stored, // it was previously expunged, or we did not fetch it. debug( " no %s\n", str_fn[t^1] ); } else { // We have a source. The target may be in an unknown state. sflags = srec->msg[t^1]->flags; doflags: if (svars->chan->ops[t] & OP_FLAGS) { sflags = sanitize_flags( sflags, svars, t ); if ((t != xt) && (srec->status & (S_EXPIRE | S_EXPIRED))) { /* Don't propagate deletion resulting from expiration. */ debug( " %s side expiring\n", str_fn[xt] ); sflags &= ~F_DELETED; } if (srec->status & S_DUMMY(t^1)) { // From placeholders, don't propagate: // - Seen, because the real contents were obviously not seen yet. // However, we do propagate un-seeing. // - Flagged, because it's just a request to upgrade sflags &= ~(F_SEEN | F_FLAGGED) | (srec->flags & F_SEEN); } else if (srec->status & S_DUMMY(t)) { // Don't propagate Flagged to placeholders, as that would be // misunderstood as a request to upgrade next time around. We // could replace the placeholder with one with(out) the flag // notice line (we can't modify the existing one due to IMAP // semantics), but that seems like major overkill, esp. as the // user likely wouldn't even notice the change. So the flag // won't be seen until the placeholder is upgraded - tough luck. sflags &= ~F_FLAGGED; } srec->aflags[t] = sflags & ~srec->flags; srec->dflags[t] = ~sflags & srec->flags; if (srec->aflags[t] || srec->dflags[t]) { debug( " %sing flags: +%s -%s\n", str_hl[t], fmt_flags( srec->aflags[t] ).str, fmt_flags( srec->dflags[t] ).str ); } } } } srec = nsrec; // Minor optimization: skip freshly created placeholder entry. } } for (t = 0; t < 2; t++) { debug( "synchronizing new messages on %s\n", str_fn[t^1] ); int topping = 1; for (tmsg = svars->msgs[t^1]; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; srec = tmsg->srec; if (srec) { // This covers legacy (or somehow corrupted) state files which // failed to track maxuid properly. // Note that this doesn't work in the presence of skipped or // failed messages. We could start keeping zombie entries, but // this wouldn't help with legacy state files. if (topping && svars->newmaxuid[t^1] < tmsg->uid) svars->newmaxuid[t^1] = tmsg->uid; if (srec->status & S_SKIPPED) { // Pre-1.4 legacy only: The message was skipped due to being too big. if (!(svars->chan->ops[t] & OP_UPGRADE)) // OP_OLD would be somewhat logical, too. continue; // The message size was not queried, so this won't be dummified below. srec->status = S_PENDING | S_DUMMY(t); JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - was previously skipped" ); } else { if (!(srec->status & S_PENDING)) { if (srec->uid[t]) continue; // Nothing to do - the message is paired if (!(svars->chan->ops[t] & OP_OLD)) { // This was reported as 'no ' already. // debug( "not re-propagating orphaned message %u\n", tmsg->uid ); continue; } if (t != xt || !(srec->status & S_EXPIRED)) { // Orphans are essentially deletion propagation transactions which // were interrupted midway through by not expunging the target. We // don't re-propagate these, as it would be illogical, and also // make a mess of placeholder upgrades. debug( "ignoring orphaned message %u\n", tmsg->uid ); continue; } if ((!(tmsg->flags & F_FLAGGED) && ((tmsg->flags & F_SEEN) || svars->chan->expire_unread > 0))) { debug( "not re-propagating tracked expired message %u\n", tmsg->uid ); continue; } assert( !(srec->status & S_LOGGED) ); srec->status |= S_PENDING; JLOG( "~ %u %u " stringify(S_PENDING), (srec->uid[F], srec->uid[N]), "re-propagate tracked expired message" ); } else { // Propagation was scheduled, but we got interrupted debug( "unpropagated old message %u\n", tmsg->uid ); if (srec->status & S_UPGRADE) { if (((svars->chan->ops[t] & OP_EXPUNGE) && ((srec->pflags | srec->aflags[t]) & ~srec->dflags[t] & F_DELETED)) || ((svars->chan->ops[t^1] & OP_EXPUNGE) && ((srec->msg[t^1]->flags | srec->aflags[t^1]) & ~srec->dflags[t^1] & F_DELETED))) { // We can't just kill the entry, as we may be propagating flags // (in particular, F_DELETED) towards the real message. // No dummy is actually present, but pretend there is, so the // real message is considered new when trashing. srec->status = (srec->status & ~(S_PENDING | S_UPGRADE)) | S_DUMMY(t); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED), "canceling placeholder upgrade - would be expunged anyway" ); continue; } // Prevent the driver from "completing" the flags, as we'll ignore them anyway. tmsg->status |= M_FLAGS; any_new[t] = 1; continue; } } } } else { // The 1st unknown message which should be known marks the end // of the synced range; more known messages may follow (from an // unidirectional sync in the opposite direction). if (t != xt || tmsg->uid > svars->maxxfuid) topping = 0; const char *what; if (tmsg->uid <= svars->maxuid[t^1]) { // The message should be already paired. It's not, so it was: // - attempted, but failed // - ignored, as it would have been expunged anyway // - paired, but subsequently expired and pruned if (!(svars->chan->ops[t] & OP_OLD)) { debug( "not propagating old message %u\n", tmsg->uid ); continue; } if (topping) { // The message is below the boundary of the bulk range. // We'll sync it only if it has become important meanwhile. if (!(tmsg->flags & F_FLAGGED) && ((tmsg->flags & F_SEEN) || svars->chan->expire_unread > 0)) { debug( "not re-propagating untracked expired message %u\n", tmsg->uid ); continue; } what = "untracked expired message"; } else { what = "old message"; } } else { if (!(svars->chan->ops[t] & OP_NEW)) { debug( "not propagating new message %u\n", tmsg->uid ); continue; } what = "new message"; } srec = nfzalloc( sizeof(*srec) ); *svars->srecadd = srec; svars->srecadd = &srec->next; svars->nsrecs++; srec->status = S_PENDING; srec->uid[t^1] = tmsg->uid; srec->msg[t^1] = tmsg; tmsg->srec = srec; if (svars->newmaxuid[t^1] < tmsg->uid) svars->newmaxuid[t^1] = tmsg->uid; JLOG( "+ %u %u", (srec->uid[F], srec->uid[N]), "%s", what ); } if (((svars->chan->ops[t] | svars->chan->ops[t^1]) & OP_EXPUNGE) && (tmsg->flags & F_DELETED)) { // Yes, we may nuke fresh entries, created only for newmaxuid tracking. // It would be lighter on the journal to log a (compressed) skip, but // this rare case does not justify additional complexity. JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing - would be expunged anyway" ); tmsg->srec = NULL; srec->status = S_DEAD; continue; } if (tmsg->size > svars->chan->stores[t]->max_size && !(srec->status & (S_DUMMY(F) | S_DUMMY(N)))) { srec->status |= S_DUMMY(t); JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - too big" ); } any_new[t] = 1; } } if (svars->any_expiring) { /* Expire excess messages. Important (flagged, unread, or unpropagated) messages * older than the first not expired message are not counted towards the total. */ // Note: When this branch is entered, we have loaded all expire-side messages. debug( "preparing message expiration\n" ); alive_srec_t *arecs = nfmalloc( sizeof(*arecs) * svars->nsrecs ); int alive = 0; for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; // We completely ignore unpaired expire-side messages, as we cannot expire // them without data loss; consequently, we also don't count them. // Note that we also ignore expire-side messages we're currently propagating, // which delays expiration of some messages by one cycle. Otherwise, we'd // have to sequence flag updating after message propagation to avoid a race // with external expunging, and that seems unreasonably expensive. if (!srec->uid[xt^1]) continue; if (!(srec->status & S_PENDING)) { // We ignore unpaired keep-side messages, as there is obviously nothing // to expire in the first place. if (!srec->msg[xt]) continue; nflags = srec->msg[xt]->flags; if (srec->status & S_DUMMY(xt)) { if (!srec->msg[xt^1]) continue; // We need to pull in the real Flagged and Seen even if flag // propagation was not requested, as the placeholder's ones are // useless (except for un-seeing). // This results in the somewhat weird situation that messages // which are not visibly flagged remain unexpired. sflags = srec->msg[xt^1]->flags; aflags = (sflags & ~srec->flags) & (F_SEEN | F_FLAGGED); dflags = (~sflags & srec->flags) & F_SEEN; nflags = (nflags & (~(F_SEEN | F_FLAGGED) | (srec->flags & F_SEEN)) & ~dflags) | aflags; } nflags = (nflags | srec->aflags[xt]) & ~srec->dflags[xt]; } else { if (srec->status & S_UPGRADE) { // The dummy's F & S flags are mostly masked out anyway, // but we may be pulling in the real ones. nflags = (srec->pflags | srec->aflags[xt]) & ~srec->dflags[xt]; } else { if (!srec->msg[xt^1]) continue; nflags = srec->msg[xt^1]->flags; } } if (!(nflags & F_DELETED) || (srec->status & (S_EXPIRE | S_EXPIRED))) { // The message is not deleted, or it is, but only due to being expired. arecs[alive++] = (alive_srec_t){ srec, nflags }; } } // Sort such that the messages which have been in the // complete store longest expire first. qsort( arecs, alive, sizeof(*arecs), (xt == F) ? cmp_srec_near : cmp_srec_far ); int todel = alive - svars->chan->max_messages; debug( "%d alive messages, %d excess - expiring\n", alive, todel ); int unseen = 0; for (int sri = 0; sri < alive; sri++) { srec = arecs[sri].srec; nflags = arecs[sri].flags; if ((nflags & F_FLAGGED) || !((nflags & F_SEEN) || ((void)(todel > 0 && unseen++), svars->chan->expire_unread > 0))) { // Important messages are always fetched/kept. debug( " pair(%u,%u) is important\n", srec->uid[F], srec->uid[N] ); todel--; } else if (todel > 0 || ((srec->status & (S_EXPIRE | S_EXPIRED)) == (S_EXPIRE | S_EXPIRED)) || ((srec->status & (S_EXPIRE | S_EXPIRED)) && (srec->msg[xt]->flags & F_DELETED))) { /* The message is excess or was already (being) expired. */ srec->status |= S_NEXPIRE; debug( " expiring pair(%u,%u)\n", srec->uid[F], srec->uid[N] ); todel--; } } debug( "%d excess messages remain\n", todel ); if (svars->chan->expire_unread < 0 && unseen * 2 > svars->chan->max_messages) { error( "%s: %d unread messages in excess of MaxMessages (%d).\n" "Please set ExpireUnread to decide outcome. Skipping mailbox.\n", svars->orig_name[xt], unseen, svars->chan->max_messages ); svars->ret |= SYNC_FAIL; cancel_sync( svars ); return; } for (int sri = 0; sri < alive; sri++) { srec = arecs[sri].srec; if (!(srec->status & S_PENDING)) { uchar nex = (srec->status / S_NEXPIRE) & 1; if (nex != ((srec->status / S_EXPIRED) & 1)) { /* The record needs a state change ... */ if (nex != ((srec->status / S_EXPIRE) & 1)) { /* ... and we need to start a transaction. */ srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED), "expire %u - begin", nex ); } else { /* ... but the "right" transaction is already pending. */ debug( "-> pair(%u,%u): expire %u (pending)\n", srec->uid[F], srec->uid[N], nex ); } } else { /* Note: the "wrong" transaction may be pending here, * e.g.: S_NEXPIRE = 0, S_EXPIRE = 1, S_EXPIRED = 0. */ } } else { if (srec->status & S_NEXPIRE) { srec->status = S_EXPIRE | S_EXPIRED; JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "expire unborn" ); // If we have so many new messages that some of them are instantly expired, // but some are still propagated because they are important, we need to // ensure explicitly that the bulk fetch limit is upped. if (svars->maxxfuid < srec->uid[xt^1]) svars->maxxfuid = srec->uid[xt^1]; srec->msg[xt^1]->srec = NULL; } } } free( arecs ); } sync_ref( svars ); debug( "synchronizing flags\n" ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; for (t = 0; t < 2; t++) { if (!srec->uid[t]) continue; if (srec->status & S_GONE(t)) { // The message was expunged. No need to call flags_set(), because: // - for S_DELETE and S_PURGE, the entry will be pruned due to both sides being gone // - for regular flag propagations, there is nothing to do // - expirations were already handled above continue; } aflags = srec->aflags[t]; dflags = srec->dflags[t]; if (srec->status & (S_DELETE | S_PURGE)) { if (!aflags) { // This deletion propagation goes the other way round, or // this deletion of a dummy happens on the other side. continue; } } else { /* The trigger is an expiration transaction being ongoing ... */ if ((t == xt) && ((shifted_bit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)) { // ... but the actual action derives from the wanted state - // so that canceled transactions are rolled back as well. if (srec->status & S_NEXPIRE) aflags |= F_DELETED; else dflags |= F_DELETED; } } if ((svars->chan->ops[t] & OP_EXPUNGE) && (((srec->msg[t] ? srec->msg[t]->flags : 0) | aflags) & ~dflags & F_DELETED) && (!svars->ctx[t]->conf->trash || svars->ctx[t]->conf->trash_only_new)) { /* If the message is going to be expunged, don't propagate anything but the deletion. */ srec->aflags[t] &= F_DELETED; aflags &= F_DELETED; srec->dflags[t] = dflags = 0; } if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS)) { /* If we know the target message's state, optimize away non-changes. */ aflags &= ~srec->msg[t]->flags; dflags &= srec->msg[t]->flags; } if (aflags | dflags) { flags_total[t]++; stats(); svars->flags_pending[t]++; fv = nfmalloc( sizeof(*fv) ); fv->aux = AUX; fv->srec = srec; fv->aflags = aflags; fv->dflags = dflags; svars->drv[t]->set_msg_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags, flags_set, fv ); if (check_cancel( svars )) goto out; } else { flags_set_p2( svars, srec, t ); } } } for (t = 0; t < 2; t++) { svars->state[t] |= ST_SENT_FLAGS; msgs_flags_set( svars, t ); if (check_cancel( svars )) goto out; } debug( "propagating new messages\n" ); for (t = 0; t < 2; t++) { if (any_new[t]) { // fsync'ing the UIDNEXT bump is not strictly necessary, but advantageous. svars->finduid[t] = svars->drv[t]->get_uidnext( svars->ctx[t] ); JLOG( "F %d %u", (t, svars->finduid[t]), "save UIDNEXT of %s", str_fn[t] ); svars->new_msgs[t] = svars->msgs[t^1]; } else { svars->state[t] |= ST_SENT_NEW; } } if (any_new[F] | any_new[N]) { // TUID assignment needs to be fsync'd, as otherwise a system crash may // lead to the newly propagated messages becoming duplicated. // Of course, we could assign each TUID only after fetching the message // and fsync it separately, but that would be horribly inefficient. for (srec = svars->srecs; srec; srec = srec->next) if (srec->status & S_PENDING) assign_tuid( svars, srec ); if (UseFSync && svars->jfp) fdatasync( fileno( svars->jfp ) ); } for (t = 0; t < 2; t++) { msgs_copied( svars, t ); if (check_cancel( svars )) goto out; } out: sync_deref( svars ); } static void msg_copied( int sts, uint uid, copy_vars_t *vars ) { DECL_INIT_SVARS(vars->aux); sync_rec_t *srec = vars->srec; switch (sts) { case COPY_OK: if (!uid) // Stored to a non-UIDPLUS mailbox svars->state[t] |= ST_FIND_NEW; else ASSIGN_UID( srec, t, uid, "%sed message", str_hl[t] ); break; case COPY_NOGOOD: case COPY_GONE: srec->status = S_DEAD; JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "%s failed", str_hl[t] ); break; default: // COPY_FAIL cancel_sync( svars ); FALLTHROUGH case COPY_CANCELED: free( vars ); return; } free( vars ); new_done[t]++; stats(); svars->new_pending[t]--; if (check_cancel( svars )) return; msgs_copied( svars, t ); } static void msgs_found_new( int sts, message_t *msgs, void *aux ); static void msgs_new_done( sync_vars_t *svars, int t ); static void sync_close( sync_vars_t *svars, int t ); static void msgs_copied( sync_vars_t *svars, int t ) { message_t *tmsg; sync_rec_t *srec; copy_vars_t *cv; if (svars->state[t] & ST_SENDING_NEW) return; sync_ref( svars ); if (!(svars->state[t] & ST_SENT_NEW)) { for (tmsg = svars->new_msgs[t]; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if ((srec = tmsg->srec) && (srec->status & S_PENDING)) { if (svars->drv[t]->get_memory_usage( svars->ctx[t] ) >= BufferLimit) { svars->new_msgs[t] = tmsg; goto out; } new_total[t]++; stats(); svars->new_pending[t]++; svars->state[t] |= ST_SENDING_NEW; cv = nfmalloc( sizeof(*cv) ); cv->cb = msg_copied; cv->aux = AUX; cv->srec = srec; cv->msg = tmsg; cv->minimal = (srec->status & S_DUMMY(t)); copy_msg( cv ); svars->state[t] &= ~ST_SENDING_NEW; if (check_cancel( svars )) goto out; } } svars->state[t] |= ST_SENT_NEW; } if (svars->new_pending[t]) goto out; sync_close( svars, t^1 ); if (check_cancel( svars )) goto out; if (svars->state[t] & ST_FIND_NEW) { debug( "finding just copied messages on %s\n", str_fn[t] ); svars->drv[t]->find_new_msgs( svars->ctx[t], svars->finduid[t], msgs_found_new, AUX ); } else { msgs_new_done( svars, t ); } out: sync_deref( svars ); } static void msgs_found_new( int sts, message_t *msgs, void *aux ) { SVARS_CHECK_RET; debug( "matching just copied messages on %s\n", str_fn[t] ); int num_lost = match_tuids( svars, t, msgs ); if (num_lost) warn( "Warning: lost track of %d %sed message(s)\n", num_lost, str_hl[t] ); if (check_cancel( svars )) return; msgs_new_done( svars, t ); } static void msgs_new_done( sync_vars_t *svars, int t ) { svars->state[t] |= ST_FOUND_NEW; sync_close( svars, t ); } static void flags_set( int sts, void *aux ) { SVARS_CHECK_RET_VARS(flag_vars_t); sync_rec_t *srec = vars->srec; switch (sts) { case DRV_OK: if (vars->aflags & F_DELETED) srec->status |= S_DEL(t); else if (vars->dflags & F_DELETED) srec->status &= ~S_DEL(t); flags_set_p2( svars, srec, t ); break; } free( vars ); flags_done[t]++; stats(); svars->flags_pending[t]--; if (check_cancel( svars )) return; msgs_flags_set( svars, t ); } static void flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t ) { if (srec->status & S_PURGE) { JLOG( "P %u %u", (srec->uid[F], srec->uid[N]), "deleted dummy" ); srec->status = (srec->status & ~S_PURGE) | S_PURGED; } else if (!(srec->status & S_DELETE)) { uchar nflags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t]; if (srec->flags != nflags) { JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], nflags), "%sed flags %s; were %s", (str_hl[t], fmt_lone_flags( nflags ).str, fmt_lone_flags( srec->flags ).str) ); srec->flags = nflags; } if (t == svars->chan->expire_side) { uchar ex = (srec->status / S_EXPIRE) & 1; uchar exd = (srec->status / S_EXPIRED) & 1; if (ex != exd) { uchar nex = (srec->status / S_NEXPIRE) & 1; if (nex == ex) { if (nex && svars->maxxfuid < srec->uid[t^1]) svars->maxxfuid = srec->uid[t^1]; srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED), "expired %d - commit", nex ); } else { srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE); JLOG( "~ %u %u %d", (srec->uid[F], srec->uid[N], srec->status & S_LOGGED), "expire %d - cancel", nex ); } } } } } typedef struct { void *aux; message_t *msg; } trash_vars_t; static void msg_trashed( int sts, void *aux ); static void msg_rtrashed( int sts, uint uid, copy_vars_t *vars ); static void msgs_flags_set( sync_vars_t *svars, int t ) { message_t *tmsg; sync_rec_t *srec; trash_vars_t *tv; copy_vars_t *cv; if (!(svars->state[t] & ST_SENT_FLAGS) || svars->flags_pending[t]) return; sync_ref( svars ); sync_close( svars, t^1 ); if (check_cancel( svars )) goto out; int only_solo; if (svars->chan->ops[t] & OP_EXPUNGE_SOLO) only_solo = 1; else if (svars->chan->ops[t] & OP_EXPUNGE) only_solo = 0; else goto skip; int xt = svars->chan->expire_side; int expunge_other = (svars->chan->ops[t^1] & OP_EXPUNGE); // Driver-wise, this makes sense only if (svars->opts[t] & OPEN_UID_EXPUNGE), // but the trashing loop uses the result as well. debug( "preparing expunge of %s on %s, %sexpunging %s\n", only_solo ? "solo" : "all", str_fn[t], expunge_other ? "" : "NOT ", str_fn[t^1] ); for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if (!(tmsg->flags & F_DELETED)) { //debug( " message %u is not deleted\n", tmsg->uid ); // Too noisy continue; } debugn( " message %u ", tmsg->uid ); if (only_solo) { if ((srec = tmsg->srec)) { if (!srec->uid[t^1]) { debugn( "(solo) " ); } else if (srec->status & S_GONE(t^1)) { debugn( "(orphaned) " ); } else if (expunge_other && (srec->status & S_DEL(t^1))) { debugn( "(orphaning) " ); } else if (t == xt && (srec->status & (S_EXPIRE | S_EXPIRED))) { // Expiration overrides mirroring, as otherwise the combination // makes no sense at all. debugn( "(expire) " ); } else { debug( "is not solo\n" ); continue; } if (srec->status & S_PENDING) { debug( "is being paired\n" ); continue; } } else { debugn( "(isolated) " ); } } debug( "- expunging\n" ); tmsg->status |= M_EXPUNGE; } int remote, only_new; if (svars->ctx[t]->conf->trash) { only_new = svars->ctx[t]->conf->trash_only_new; debug( "trashing %s on %s locally\n", only_new ? "new" : "all", str_fn[t] ); remote = 0; } else if (svars->ctx[t^1]->conf->trash && svars->ctx[t^1]->conf->trash_remote_new) { debug( "trashing new on %s remotely\n", str_fn[t] ); only_new = 1; remote = 1; } else { goto skip; } for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if (!(tmsg->status & M_EXPUNGE)) { //debug( " message %u is not being expunged\n", tmsg->uid ); // Too noisy continue; } debugn( " message %u ", tmsg->uid ); if ((srec = tmsg->srec)) { if (t == xt && (srec->status & (S_EXPIRE | S_EXPIRED))) { // Don't trash messages that are deleted only due to expiring. // However, this is an unlikely configuration to start with ... debug( "is expired\n" ); continue; } if (srec->status & S_DUMMY(t)) { // This is mostly academical, as trashing being done on the side // where placeholders reside is rather unlikely. debug( "is dummy\n" ); continue; } if (srec->status & S_PURGED) { // As above. debug( "is deleted dummy\n" ); continue; } if (only_new && !(srec->status & (S_DUMMY(t^1) | S_SKIPPED))) { debug( "is not new\n" ); continue; } } if (find_uint_array( svars->trashed_msgs[t].array, tmsg->uid )) { debug( "was already trashed\n" ); continue; } debug( "- trashing\n" ); trash_total[t]++; stats(); svars->trash_pending[t]++; if (!remote) { tv = nfmalloc( sizeof(*tv) ); tv->aux = AUX; tv->msg = tmsg; svars->drv[t]->trash_msg( svars->ctx[t], tmsg, msg_trashed, tv ); } else { cv = nfmalloc( sizeof(*cv) ); cv->cb = msg_rtrashed; cv->aux = INV_AUX; cv->srec = NULL; cv->msg = tmsg; cv->minimal = 0; copy_msg( cv ); } if (check_cancel( svars )) goto out; } skip: svars->state[t] |= ST_SENT_TRASH; sync_close( svars, t ); out: sync_deref( svars ); } static void msg_trashed( int sts, void *aux ) { SVARS_CHECK_RET_VARS(trash_vars_t); switch (sts) { case DRV_OK: JLOG( "T %d %u", (t, vars->msg->uid), "trashed on %s", str_fn[t] ); break; case DRV_MSG_BAD: if (vars->msg->status & M_DEAD) break; // Driver already reported error. svars->ret |= SYNC_FAIL; if (svars->opts[t] & OPEN_UID_EXPUNGE) vars->msg->status &= ~M_EXPUNGE; else svars->state[t] |= ST_TRASH_BAD; break; } free( vars ); trash_done[t]++; stats(); svars->trash_pending[t]--; if (check_cancel( svars )) return; sync_close( svars, t ); } static void msg_rtrashed( int sts, uint uid ATTR_UNUSED, copy_vars_t *vars ) { DECL_INIT_SVARS(vars->aux); t ^= 1; switch (sts) { case COPY_OK: JLOG( "T %d %u", (t, vars->msg->uid), "trashed remotely on %s", str_fn[t^1] ); break; case COPY_GONE: break; case COPY_NOGOOD: if (svars->opts[t] & OPEN_UID_EXPUNGE) vars->msg->status &= ~M_EXPUNGE; else svars->state[t] |= ST_TRASH_BAD; break; default: // COPY_FAIL cancel_sync( svars ); FALLTHROUGH case COPY_CANCELED: free( vars ); return; } free( vars ); trash_done[t]++; stats(); svars->trash_pending[t]--; if (check_cancel( svars )) return; sync_close( svars, t ); } static void box_closed( int sts, int reported, void *aux ); static void box_closed_p2( sync_vars_t *svars, int t ); static void sync_close( sync_vars_t *svars, int t ) { if ((~svars->state[t] & (ST_FOUND_NEW | ST_SENT_TRASH)) || svars->trash_pending[t] || (~svars->state[t^1] & (ST_SENT_NEW | ST_SENT_FLAGS)) || svars->new_pending[t^1] || svars->flags_pending[t^1]) return; if (svars->state[t] & ST_CLOSING) return; svars->state[t] |= ST_CLOSING; if ((svars->chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) && !(DFlags & FAKEEXPUNGE) && !(svars->state[t] & ST_TRASH_BAD)) { if (Verbosity >= TERSE || (DFlags & EXT_EXIT)) { if (svars->opts[t] & OPEN_UID_EXPUNGE) { for (message_t *tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) { if (tmsg->status & M_DEAD) continue; if (tmsg->status & M_EXPUNGE) expunge_total[t]++; } } else { for (sync_rec_t *srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; if (srec->status & S_DEL(t)) expunge_total[t]++; } } stats(); } debug( "expunging %s\n", str_fn[t] ); svars->drv[t]->close_box( svars->ctx[t], box_closed, AUX ); } else { box_closed_p2( svars, t ); } } static void box_closed( int sts, int reported, void *aux ) { SVARS_CHECK_RET_CANCEL; if (!reported) { for (sync_rec_t *srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; // Note that this logic is somewhat optimistic - theoretically, it's // possible that a message was concurrently undeleted before we tried // to expunge it. Such a message would be subsequently re-propagated // by a refresh, and in the extremely unlikely case of this happening // on both sides, we'd even get a duplicate. That's why this is only // a fallback. if (srec->status & S_DEL(t)) { srec->status |= S_GONE(t); expunge_done[t]++; } } stats(); } box_closed_p2( svars, t ); } static void box_closed_p2( sync_vars_t *svars, int t ) { sync_rec_t *srec; svars->state[t] |= ST_CLOSED; if (!(svars->state[t^1] & ST_CLOSED)) return; // All logging done in this function is merely for the journal replay // autotest - the operations are idempotent, and we're about to commit // the new state right afterwards anyway. Therefore, it would also // make no sense to cover it by the interrupt-resume autotest (which // would also add unreasonable complexity, as the maxuid bumps and entry // purge must be consistent). if (DFlags & KEEPJOURNAL) printf( "### %d steps, %d entries ###\n", -JLimit, JCount ); for (t = 0; t < 2; t++) { // Committing maxuid is delayed until all messages were propagated, to // ensure that all pending messages are still loaded next time in case // of interruption - in particular skipping messages would otherwise // up the limit too early. svars->maxuid[t] = svars->newmaxuid[t]; if (svars->maxuid[t] != svars->oldmaxuid[t]) PC_JLOG( "N %d %u", (t, svars->maxuid[t]), "up maxuid of %s", str_fn[t] ); } debug( "purging obsolete entries\n" ); int xt = svars->chan->expire_side; for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; if ((srec->status & S_EXPIRED) && (!srec->uid[xt] || (srec->status & S_GONE(xt))) && svars->maxuid[xt^1] >= srec->uid[xt^1] && svars->maxxfuid >= srec->uid[xt^1]) { PC_JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing expired" ); srec->status = S_DEAD; } else if (!srec->uid[N] || (srec->status & S_GONE(N))) { if (!srec->uid[F] || (srec->status & S_GONE(F))) { PC_JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing" ); srec->status = S_DEAD; } else if (srec->uid[N] && (srec->status & S_DEL(F))) { PC_JLOG( "> %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); srec->uid[N] = 0; } } else if (srec->uid[F] && (srec->status & S_GONE(F)) && (srec->status & S_DEL(N))) { PC_JLOG( "< %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); srec->uid[F] = 0; } } save_state( svars ); sync_bail( svars ); } static void sync_bail( sync_vars_t *svars ) { sync_rec_t *srec, *nsrec; free( svars->trashed_msgs[F].array.data ); free( svars->trashed_msgs[N].array.data ); for (srec = svars->srecs; srec; srec = nsrec) { nsrec = srec->next; free( srec ); } if (svars->lfd >= 0) { unlink( svars->lname ); close( svars->lfd ); } sync_bail2( svars ); } static void sync_bail2( sync_vars_t *svars ) { free( svars->lname ); free( svars->nname ); free( svars->jname ); free( svars->dname ); sync_bail3( svars ); } static void sync_bail3( sync_vars_t *svars ) { free( svars->box_name[F] ); free( svars->box_name[N] ); sync_deref( svars ); } static void sync_deref( sync_vars_t *svars ) { if (!--svars->ref_count) { void (*cb)( int sts, void *aux ) = svars->cb; void *aux = svars->aux; int ret = svars->ret; free( svars ); cb( ret, aux ); } } isync-1.5.1/src/config.c0000644000175000001440000004607614764020644010544 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #define DEBUG_FLAG DEBUG_MAIN #include "config.h" #include "sync.h" #include #include #include #include #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__) char FieldDelimiter = ';'; #else char FieldDelimiter = ':'; #endif DEF_BIT_FORMATTER_FUNCTION(ops, sync_op_enum) char * expand_strdup( const char *s, const conffile_t *cfile ) { struct passwd *pw; const char *p, *q; char *r; if (*s == '~') { s++; if (!*s) { p = NULL; q = Home; } else if (*s == '/') { p = s; q = Home; } else { if ((p = strchr( s, '/' ))) { r = nfstrndup( s, (size_t)(p - s) ); pw = getpwnam( r ); free( r ); } else { pw = getpwnam( s ); } if (!pw) return NULL; q = pw->pw_dir; } nfasprintf( &r, "%s%s", q, p ? p : "" ); return r; } else if (*s != '/') { nfasprintf( &r, "%.*s%s", cfile->path_len, cfile->file, s ); return r; } else { return nfstrdup( s ); } } char * get_arg( conffile_t *cfile, int required, int *comment ) { char *ret, *p, *t; int escaped, quoted; char c; p = cfile->rest; assert( p ); while ((c = *p) && isspace( (uchar)c )) p++; if (!c || c == '#') { if (comment) *comment = (c == '#'); if (required) { error( "%s:%d: parameter missing\n", cfile->file, cfile->line ); cfile->err = 1; } ret = NULL; } else { for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) { p++; if (escaped && c >= 32) { escaped = 0; *t++ = c; } else if (c == '\\') { escaped = 1; } else if (c == '"') { quoted ^= 1; } else if (!quoted && isspace( (uchar)c )) { break; } else { *t++ = c; } } *t = 0; if (escaped) { error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line ); cfile->err = 1; ret = NULL; } if (quoted) { error( "%s:%d: missing closing quote\n", cfile->file, cfile->line ); cfile->err = 1; ret = NULL; } } cfile->rest = p; return ret; } char parse_bool( conffile_t *cfile ) { if (!strcasecmp( cfile->val, "yes" ) || !strcasecmp( cfile->val, "true" ) || !strcasecmp( cfile->val, "on" ) || !strcmp( cfile->val, "1" )) return 1; if (strcasecmp( cfile->val, "no" ) && strcasecmp( cfile->val, "false" ) && strcasecmp( cfile->val, "off" ) && strcmp( cfile->val, "0" )) { error( "%s:%d: invalid boolean value '%s'\n", cfile->file, cfile->line, cfile->val ); cfile->err = 1; } return 0; } int parse_int( conffile_t *cfile ) { char *p; int ret; ret = strtol( cfile->val, &p, 10 ); if (*p) { error( "%s:%d: invalid integer value '%s'\n", cfile->file, cfile->line, cfile->val ); cfile->err = 1; return 0; } return ret; } uint parse_size( conffile_t *cfile ) { char *p; uint ret; ret = strtoul( cfile->val, &p, 10 ); if (*p == 'k' || *p == 'K') ret *= 1024, p++; else if (*p == 'm' || *p == 'M') ret *= 1024 * 1024, p++; if (*p == 'b' || *p == 'B') p++; if (*p) { fprintf (stderr, "%s:%d: invalid size '%s'\n", cfile->file, cfile->line, cfile->val); cfile->err = 1; return 0; } return ret; } static const struct { int op; const char *name; } boxOps[] = { { OP_EXPUNGE, "Expunge" }, { OP_EXPUNGE_SOLO, "ExpungeSolo" }, { OP_CREATE, "Create" }, { OP_REMOVE, "Remove" }, }; static int getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf ) { char *arg; uint i; if (!strcasecmp( "Sync", cfile->cmd )) { arg = cfile->val; do { if (!strcasecmp( "Push", arg )) { *cops |= XOP_PUSH; } else if (!strcasecmp( "Pull", arg )) { *cops |= XOP_PULL; } else if (!strcasecmp( "Upgrade", arg )) { *cops |= OP_UPGRADE; } else if (!strcasecmp( "ReNew", arg )) { cfile->renew_warn = 1; *cops |= OP_UPGRADE; } else if (!strcasecmp( "New", arg )) { *cops |= OP_NEW; } else if (!strcasecmp( "Old", arg )) { *cops |= OP_OLD; } else if (!strcasecmp( "Gone", arg )) { *cops |= OP_GONE; } else if (!strcasecmp( "Delete", arg )) { cfile->delete_warn = 1; *cops |= OP_GONE; } else if (!strcasecmp( "Flags", arg )) { *cops |= OP_FLAGS; } else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg )) { *cops |= OP_MASK_TYPE; } else if (!strcasecmp( "PullUpgrade", arg )) { conf->ops[N] |= OP_UPGRADE; } else if (!strcasecmp( "PullReNew", arg )) { cfile->renew_warn = 1; conf->ops[N] |= OP_UPGRADE; } else if (!strcasecmp( "PullNew", arg )) { conf->ops[N] |= OP_NEW; } else if (!strcasecmp( "PullOld", arg )) { conf->ops[N] |= OP_OLD; } else if (!strcasecmp( "PullGone", arg )) { conf->ops[N] |= OP_GONE; } else if (!strcasecmp( "PullDelete", arg )) { cfile->delete_warn = 1; conf->ops[N] |= OP_GONE; } else if (!strcasecmp( "PullFlags", arg )) { conf->ops[N] |= OP_FLAGS; } else if (!strcasecmp( "PullFull", arg )) { conf->ops[N] |= OP_MASK_TYPE; } else if (!strcasecmp( "PushUpgrade", arg )) { conf->ops[F] |= OP_UPGRADE; } else if (!strcasecmp( "PushReNew", arg )) { cfile->renew_warn = 1; conf->ops[F] |= OP_UPGRADE; } else if (!strcasecmp( "PushNew", arg )) { conf->ops[F] |= OP_NEW; } else if (!strcasecmp( "PushOld", arg )) { conf->ops[F] |= OP_OLD; } else if (!strcasecmp( "PushGone", arg )) { conf->ops[F] |= OP_GONE; } else if (!strcasecmp( "PushDelete", arg )) { cfile->delete_warn = 1; conf->ops[F] |= OP_GONE; } else if (!strcasecmp( "PushFlags", arg )) { conf->ops[F] |= OP_FLAGS; } else if (!strcasecmp( "PushFull", arg )) { conf->ops[F] |= OP_MASK_TYPE; } else if (!strcasecmp( "None", arg ) || !strcasecmp( "Noop", arg )) { conf->ops[F] |= XOP_TYPE_NOOP; } else { error( "%s:%d: invalid Sync arg '%s'\n", cfile->file, cfile->line, arg ); cfile->err = 1; } } while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL ))); conf->ops[F] |= XOP_HAVE_TYPE; } else if (!strcasecmp( "SyncState", cfile->cmd )) { conf->sync_state = !strcmp( cfile->val, "*" ) ? "*" : expand_strdup( cfile->val, cfile ); } else if (!strcasecmp( "CopyArrivalDate", cfile->cmd )) { conf->use_internal_date = parse_bool( cfile ); } else if (!strcasecmp( "MaxMessages", cfile->cmd )) { conf->max_messages = parse_int( cfile ); } else if (!strcasecmp( "ExpireSide", cfile->cmd )) { arg = cfile->val; if (!strcasecmp( "Far", arg )) { conf->expire_side = F; } else if (!strcasecmp( "Near", arg )) { conf->expire_side = N; } else { error( "%s:%d: invalid ExpireSide argument '%s'\n", cfile->file, cfile->line, arg ); cfile->err = 1; } } else if (!strcasecmp( "ExpireUnread", cfile->cmd )) { conf->expire_unread = parse_bool( cfile ); } else { for (i = 0; i < as(boxOps); i++) { if (!strcasecmp( boxOps[i].name, cfile->cmd )) { int op = boxOps[i].op; arg = cfile->val; do { if (!strcasecmp( "Both", arg )) { *cops |= op; } else if (!strcasecmp( "Far", arg )) { conf->ops[F] |= op; } else if (!strcasecmp( "Master", arg )) { // Pre-1.4 legacy conf->ops[F] |= op; cfile->ms_warn = 1; } else if (!strcasecmp( "Near", arg )) { conf->ops[N] |= op; } else if (!strcasecmp( "Slave", arg )) { // Pre-1.4 legacy conf->ops[N] |= op; cfile->ms_warn = 1; } else if (!strcasecmp( "None", arg )) { conf->ops[F] |= op * (XOP_EXPUNGE_NOOP / OP_EXPUNGE); } else { error( "%s:%d: invalid %s arg '%s'\n", cfile->file, cfile->line, boxOps[i].name, arg ); cfile->err = 1; } } while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL ))); conf->ops[F] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE); return 1; } } return 0; } return 1; } int getcline( conffile_t *cfile ) { char *arg; int comment; if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL ))) { error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg ); cfile->err = 1; } while (fgets( cfile->buf, cfile->bufl, cfile->fp )) { cfile->line++; cfile->rest = cfile->buf; if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) { if (comment) continue; return 1; } if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL ))) continue; return 1; } return 0; } static const char * channel_str( const char *chan_name ) { if (!chan_name) return "on the command line"; if (!*chan_name) return "in global config section"; static char buf[100]; nfsnprintf( buf, sizeof(buf), "in Channel '%s'", chan_name ); return buf; } int merge_ops( int cops, int ops[], const char *chan_name ) { int aops, op; uint i; if (!cops && !ops[F] && !ops[N]) // Only to denoise the debug output return 0; debug( "merge ops (%s):\n common: %s\n far: %s\n near: %s\n", channel_str( chan_name ), fmt_ops( cops ).str, fmt_ops( ops[F] ).str, fmt_ops( ops[N] ).str ); aops = ops[F] | ops[N]; if (ops[F] & XOP_HAVE_TYPE) { if (aops & OP_MASK_TYPE) { // PullNew, etc. if (ops[F] & XOP_TYPE_NOOP) { cfl: error( "Conflicting Sync options specified %s.\n", channel_str( chan_name ) ); return 1; } if (aops & cops & OP_MASK_TYPE) { // Overlapping New, etc. ovl: error( "Redundant Sync options specified %s.\n", channel_str( chan_name ) ); return 1; } // Mix in non-overlapping Push/Pull or New, etc. if (cops & XOP_PULL) { if (cops & (XOP_PUSH | OP_MASK_TYPE)) { // Mixing instant effect flags with row/column flags would be confusing, // so instead everything is instant effect. This implies that mixing // direction with type would cause overlaps, so PullNew Push Gone, etc. // is invalid. // Pull Push covers everything, so makes no sense to combine. ivl: error( "Invalid combination of simple and compound Sync options %s.\n", channel_str( chan_name ) ); return 1; } if (ops[N] & OP_DFLT_TYPE) goto ovl; ops[N] |= OP_DFLT_TYPE; } else if (cops & XOP_PUSH) { if (cops & OP_MASK_TYPE) goto ivl; if (ops[F] & OP_DFLT_TYPE) goto ovl; ops[F] |= OP_DFLT_TYPE; } else { ops[F] |= cops & OP_MASK_TYPE; ops[N] |= cops & OP_MASK_TYPE; } } else if (cops & (OP_MASK_TYPE | XOP_MASK_DIR)) { // Pull New, etc. if (ops[F] & XOP_TYPE_NOOP) goto cfl; if (!(cops & OP_MASK_TYPE)) cops |= OP_DFLT_TYPE; else if (!(cops & XOP_MASK_DIR)) cops |= XOP_PULL | XOP_PUSH; if (cops & XOP_PULL) ops[N] |= cops & OP_MASK_TYPE; if (cops & XOP_PUSH) ops[F] |= cops & OP_MASK_TYPE; } } for (i = 0; i < as(boxOps); i++) { op = boxOps[i].op; if (ops[F] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) { if (((aops | cops) & op) && (ops[F] & (op * (XOP_EXPUNGE_NOOP / OP_EXPUNGE)))) { error( "Conflicting %s options specified %s.\n", boxOps[i].name, channel_str( chan_name ) ); return 1; } if (aops & cops & op) { error( "Redundant %s options specified %s.\n", boxOps[i].name, channel_str( chan_name ) ); return 1; } ops[F] |= cops & op; ops[N] |= cops & op; } } debug( " => far: %s\n => near: %s\n", fmt_ops( ops[F] ).str, fmt_ops( ops[N] ).str ); return 0; } int load_config( const char *where ) { conffile_t cfile; store_conf_t *store, **storeapp = &stores; channel_conf_t *channel, **channelapp = &channels; group_conf_t *group, **groupapp = &groups; string_list_t *chanlist, **chanlistapp; char *arg, *p; uint len, max_size; int cops, gcops, glob_ok, fn, i; char path[_POSIX_PATH_MAX], path2[_POSIX_PATH_MAX]; char buf[1024]; if (!where) { int path_len, path_len2; const char *config_home = getenv( "XDG_CONFIG_HOME" ); if (config_home) nfsnprintf( path, sizeof(path), "%s/%nisyncrc", config_home, &path_len ); else nfsnprintf( path, sizeof(path), "%s/.config/%nisyncrc", Home, &path_len ); nfsnprintf( path2, sizeof(path2), "%s/%n.mbsyncrc", Home, &path_len2 ); struct stat st; int ex = !lstat( path, &st ); int ex2 = !lstat( path2, &st ); if (ex2 && !ex) { cfile.file = path2; cfile.path_len = path_len2; } else { if (ex && ex2) warn( "Both %s and %s exist; using the former.\n", path, path2 ); cfile.file = path; cfile.path_len = path_len; } } else { const char *sl = strrchr( where, '/' ); if (!sl) { nfsnprintf( path, sizeof(path), "./%n%s", &cfile.path_len, where ); cfile.file = path; } else { cfile.path_len = sl - where + 1; cfile.file = where; } } info( "Reading configuration file %s\n", cfile.file ); if (!(cfile.fp = fopen( cfile.file, "r" ))) { sys_error( "Cannot open config file '%s'", cfile.file ); return 1; } buf[sizeof(buf) - 1] = 0; cfile.buf = buf; cfile.bufl = sizeof(buf) - 1; cfile.line = 0; cfile.err = 0; cfile.ms_warn = 0; cfile.renew_warn = 0; cfile.delete_warn = 0; cfile.rest = NULL; gcops = 0; glob_ok = 1; global_conf.expire_side = N; global_conf.expire_unread = -1; reloop: while (getcline( &cfile )) { if (!cfile.cmd) continue; for (i = 0; i < N_DRIVERS; i++) { if (drivers[i]->parse_store( &cfile, &store )) { if (store) { if (!store->max_size) store->max_size = UINT_MAX; if (!store->flat_delim) store->flat_delim = ""; *storeapp = store; storeapp = &store->next; *storeapp = NULL; } glob_ok = 0; goto reloop; } } if (!strcasecmp( "Channel", cfile.cmd )) { channel = nfzalloc( sizeof(*channel) ); channel->name = nfstrdup( cfile.val ); channel->max_messages = global_conf.max_messages; channel->expire_side = global_conf.expire_side; channel->expire_unread = global_conf.expire_unread; channel->use_internal_date = global_conf.use_internal_date; cops = 0; max_size = UINT_MAX; while (getcline( &cfile ) && cfile.cmd) { if (!strcasecmp( "MaxSize", cfile.cmd )) { max_size = parse_size( &cfile ); } else if (!strcasecmp( "Pattern", cfile.cmd ) || !strcasecmp( "Patterns", cfile.cmd )) { arg = cfile.val; do { add_string_list( &channel->patterns, arg ); } while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL ))); } else if (!strcasecmp( "Far", cfile.cmd )) { fn = F; goto linkst; } else if (!strcasecmp( "Master", cfile.cmd )) { // Pre-1.4 legacy fn = F; goto olinkst; } else if (!strcasecmp( "Near", cfile.cmd )) { fn = N; goto linkst; } else if (!strcasecmp( "Slave", cfile.cmd )) { // Pre-1.4 legacy fn = N; olinkst: cfile.ms_warn = 1; linkst: if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) { error( "%s:%d: malformed mailbox spec\n", cfile.file, cfile.line ); cfile.err = 1; continue; } *p = 0; for (store = stores; store; store = store->next) { if (!strcmp( store->name, cfile.val + 1 )) { channel->stores[fn] = store; goto stpcom; } } channel->stores[fn] = (void *)~0; error( "%s:%d: unknown store '%s'\n", cfile.file, cfile.line, cfile.val + 1 ); cfile.err = 1; continue; stpcom: if (*++p) channel->boxes[fn] = nfstrdup( p ); } else if (!getopt_helper( &cfile, &cops, channel )) { error( "%s:%d: keyword '%s' is not recognized in Channel sections\n", cfile.file, cfile.line, cfile.cmd ); cfile.rest = NULL; cfile.err = 1; } } if (!channel->stores[F]) { error( "channel '%s' refers to no far side store\n", channel->name ); cfile.err = 1; } if (!channel->stores[N]) { error( "channel '%s' refers to no near side store\n", channel->name ); cfile.err = 1; } if (merge_ops( cops, channel->ops, channel->name )) cfile.err = 1; if (max_size != UINT_MAX && !cfile.err) { if (!max_size) max_size = UINT_MAX; channel->stores[F]->max_size = channel->stores[N]->max_size = max_size; } *channelapp = channel; channelapp = &channel->next; glob_ok = 0; goto reloop; } else if (!strcasecmp( "Group", cfile.cmd )) { group = nfmalloc( sizeof(*group) ); group->name = nfstrdup( cfile.val ); *groupapp = group; groupapp = &group->next; *groupapp = NULL; chanlistapp = &group->channels; *chanlistapp = NULL; while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL ))) { addone: len = strlen( arg ); chanlist = nfmalloc( sizeof(*chanlist) + len ); memcpy( chanlist->string, arg, len + 1 ); *chanlistapp = chanlist; chanlistapp = &chanlist->next; *chanlistapp = NULL; } while (getcline( &cfile ) && cfile.cmd) { if (!strcasecmp( "Channel", cfile.cmd ) || !strcasecmp( "Channels", cfile.cmd )) { arg = cfile.val; goto addone; } else { error( "%s:%d: keyword '%s' is not recognized in Group sections\n", cfile.file, cfile.line, cfile.cmd ); cfile.rest = NULL; cfile.err = 1; } } glob_ok = 0; goto reloop; } else if (!strcasecmp( "FSync", cfile.cmd )) { UseFSync = parse_bool( &cfile ); } else if (!strcasecmp( "FieldDelimiter", cfile.cmd )) { if (strlen( cfile.val ) != 1) { error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line ); cfile.err = 1; } else { FieldDelimiter = cfile.val[0]; if (!ispunct( FieldDelimiter )) { error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line ); cfile.err = 1; } } } else if (!strcasecmp( "BufferLimit", cfile.cmd )) { BufferLimit = parse_size( &cfile ); if (!BufferLimit) { error( "%s:%d: BufferLimit cannot be zero\n", cfile.file, cfile.line ); cfile.err = 1; } } else if (!getopt_helper( &cfile, &gcops, &global_conf )) { error( "%s:%d: '%s' is not a recognized section-starting or global keyword\n", cfile.file, cfile.line, cfile.cmd ); cfile.err = 1; cfile.rest = NULL; while (getcline( &cfile )) if (!cfile.cmd) goto reloop; break; } if (!glob_ok) { error( "%s:%d: global options may not follow sections\n", cfile.file, cfile.line ); cfile.err = 1; } } fclose (cfile.fp); if (cfile.ms_warn) warn( "%s: notice: Master/Slave are deprecated; use Far/Near instead.\n", cfile.file ); if (cfile.renew_warn) warn( "%s: notice: ReNew is deprecated; use Upgrade instead.\n", cfile.file ); if (cfile.delete_warn) warn( "%s: notice: Delete is deprecated; use Gone instead.\n", cfile.file ); cfile.err |= merge_ops( gcops, global_conf.ops, "" ); if (!global_conf.sync_state) { const char *state_home = getenv( "XDG_STATE_HOME" ); if (state_home) nfsnprintf( path, sizeof(path), "%s/isync/", state_home ); else nfsnprintf( path, sizeof(path), "%s/.local/state/isync/", Home ); nfsnprintf( path2, sizeof(path2), "%s/.mbsync/", Home ); struct stat st; int ex = !lstat( path, &st ); int ex2 = !lstat( path2, &st ); if (ex2 && !ex) { global_conf.sync_state = nfstrdup( path2 ); } else { if (ex && ex2) { error( "Error: both %s and %s exist; delete one or set SyncState globally.\n", path, path2 ); cfile.err = 1; } global_conf.sync_state = nfstrdup( path ); } } return cfile.err; } isync-1.5.1/src/tst_imap_msgs.c0000644000175000001440000001074714405565305012144 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later // // isync test suite // #include "imap_p.h" static imap_messages_t smsgs; // from driver.c void free_generic_messages( message_t *msgs ) { message_t *tmsg; for (; msgs; msgs = tmsg) { tmsg = msgs->next; // free( msgs->msgid ); free( msgs ); } } static void dump_messages( void ) { print( "=>" ); uint seq = 0; for (imap_message_t *msg = smsgs.head; msg; msg = msg->next) { seq += msg->seq; if (msg->status & M_DEAD) print( " (%u:%u)", seq, msg->uid ); else print( " %u:%u", seq, msg->uid ); } print( "\n" ); } static void init( uint *in ) { reset_imap_messages( &smsgs ); for (; *in; in++) { imap_message_t *msg = imap_new_msg( &smsgs ); msg->seq = *in; // We (ab)use the initial sequence number as the UID. That's not // exactly realistic, but it's valid, and saves us redundant data. msg->uid = *in; } } static void modify( uint *in ) { for (; *in; in++) { imap_expunge_msg( &smsgs, *in ); #ifdef DEBUG_IMAP_MSGS dump_messages(); #endif } } static void verify( uint *in, const char *name ) { int fails = 0; imap_message_t *msg = smsgs.head; for (;;) { if (msg && *in && msg->uid == *in) { if (msg->status & M_DEAD) { printf( "*** %s: message %u is dead\n", name, msg->uid ); fails++; } else { assert( msg->seq ); } msg = msg->next; in++; } else if (*in && (!msg || msg->uid > *in)) { printf( "*** %s: message %u is missing\n", name, *in ); fails++; in++; } else if (msg) { if (!(msg->status & M_DEAD)) { printf( "*** %s: excess message %u\n", name, msg->uid ); fails++; } msg = msg->next; } else { assert( !*in ); break; } } if (fails) dump_messages(); } static void test( uint *ex, uint *out, const char *name ) { printf( "test %s ...\n", name ); modify( ex ); verify( out, name ); } int main( void ) { static uint arr_0[] = { 0 }; static uint arr_1[] = { 1, 0 }; static uint full_in[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 }; init( full_in ); #if 0 static uint nop[] = { 0 }; static uint nop_out[] = { /* 1, */ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, /* 17, */ 18 /*!*/, 0 }; test( nop, nop_out, "self-test" ); #endif static uint full_ex_fw1[] = { 18, 13, 13, 13, 1, 1, 1, 0 }; static uint full_out_fw1[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 17, 0 }; test( full_ex_fw1, full_out_fw1, "full, forward 1" ); static uint full_ex_fw2[] = { 10, 10, 0 }; static uint full_out_fw2[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 0 }; test( full_ex_fw2, full_out_fw2, "full, forward 2" ); init( full_in ); static uint full_ex_bw1[] = { 18, 17, 16, 15, 14, 13, 5, 4, 3, 0 }; static uint full_out_bw1[] = { 1, 2, 6, 7, 8, 9, 10, 11, 12, 0 }; test( full_ex_bw1, full_out_bw1, "full, backward 1" ); static uint full_ex_bw2[] = { 2, 1, 0 }; static uint full_out_bw2[] = { 6, 7, 8, 9, 10, 11, 12, 0 }; test( full_ex_bw2, full_out_bw2, "full, backward 2" ); static uint hole_wo1_in[] = { 10, 11, 12, 20, 21, 31, 32, 33, 34, 35, 36, 37, 0 }; init( hole_wo1_in ); static uint hole_wo1_ex_1[] = { 31, 30, 29, 28, 22, 21, 11, 2, 1, 0 }; static uint hole_wo1_out_1[] = { 10, 12, 20, 32, 33, 34, 35, 36, 37, 0 }; test( hole_wo1_ex_1, hole_wo1_out_1, "hole w/o 1, backward" ); init( hole_wo1_in ); static uint hole_wo1_ex_2[] = { 1, 1, 9, 18, 18, 23, 23, 23, 23, 0 }; test( hole_wo1_ex_2, hole_wo1_out_1, "hole w/o 1, forward" ); test( arr_1, hole_wo1_out_1, "hole w/o 1, forward 2" ); static uint hole_wo1_ex_4[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; static uint hole_wo1_out_4[] = { 37, 0 }; test( hole_wo1_ex_4, hole_wo1_out_4, "hole w/o 1, forward 3" ); test( arr_1, arr_0, "hole w/o 1, forward 4" ); test( arr_1, arr_0, "hole w/o 1, forward 5" ); static uint hole_w1_in[] = { 1, 10, 11, 12, 0 }; init( hole_w1_in ); static uint hole_w1_ex_1[] = { 11, 10, 2, 1, 0 }; static uint hole_w1_out_1[] = { 12, 0 }; test( hole_w1_ex_1, hole_w1_out_1, "hole w/ 1, backward" ); test( arr_1, hole_w1_out_1, "hole w/ 1, backward 2" ); init( hole_w1_in ); static uint hole_w1_ex_2[] = { 1, 1, 8, 8, 0 }; test( hole_w1_ex_2, hole_w1_out_1, "hole w/ 1, forward" ); static uint hole_w1_ex_4[] = { 1, 1, 1, 1, 1, 1, 1, 0 }; static uint hole_w1_out_4[] = { 12, 0 }; test( hole_w1_ex_4, hole_w1_out_4, "hole w/ 1, forward 2" ); test( arr_1, arr_0, "hole w/ 1, forward 3" ); test( arr_1, arr_0, "hole w/ 1, forward 4" ); return 0; } isync-1.5.1/src/tst_timers.c0000644000175000001440000000411514405565305011460 // SPDX-FileCopyrightText: 2014-2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later // // isync test suite // #include "common.h" typedef struct { int id; int first, other, morph_at, morph_to; int64_t start; wakeup_t timer; wakeup_t morph_timer; } tst_t; static void timer_start( tst_t *timer, int to ) { printf( "starting timer %d, should expire after %d\n", timer->id, to ); timer->start = get_now(); conf_wakeup( &timer->timer, to ); } static void timed_out( void *aux ) { tst_t *timer = (tst_t *)aux; printf( "timer %d expired after %d, repeat %d\n", timer->id, (int)(get_now() - timer->start), timer->other ); if (timer->other >= 0) { timer_start( timer, timer->other ); } else { wipe_wakeup( &timer->timer ); wipe_wakeup( &timer->morph_timer ); free( timer ); } } static void morph_timed_out( void *aux ) { tst_t *timer = (tst_t *)aux; printf( "morphing timer %d after %d\n", timer->id, (int)(get_now() - timer->start) ); timer_start( timer, timer->morph_to ); } static int nextid; int main( int argc, char **argv ) { int i; init_timers(); for (i = 1; i < argc; i++) { char *val = argv[i]; tst_t *timer = nfmalloc( sizeof(*timer) ); init_wakeup( &timer->timer, timed_out, timer ); init_wakeup( &timer->morph_timer, morph_timed_out, timer ); timer->id = ++nextid; timer->first = strtol( val, &val, 0 ); if (*val == '@') { timer->other = timer->first; timer->first = strtol( ++val, &val, 0 ); } else { timer->other = -1; } if (*val == ':') { timer->morph_to = strtol( ++val, &val, 0 ); if (*val != '@') goto fail; timer->morph_at = strtol( ++val, &val, 0 ); } else { timer->morph_at = -1; } if (*val) { fail: fprintf( stderr, "Fatal: syntax error in %s, use [@][:@]\n", argv[i] ); return 1; } timer_start( timer, timer->first ); if (timer->morph_at >= 0) { printf( "timer %d, should morph after %d\n", timer->id, timer->morph_at ); conf_wakeup( &timer->morph_timer, timer->morph_at ); } } main_loop(); return 0; } isync-1.5.1/src/drv_maildir.c0000644000175000001440000015131614661341206011561 // SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins // SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen // SPDX-FileCopyrightText: 2004 Theodore Y. Ts'o // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception /* * mbsync - mailbox synchronizer */ #define DEBUG_FLAG DEBUG_MAILDIR #include "driver.h" #include #include #include #include #include #include #if !defined(_POSIX_SYNCHRONIZED_IO) || _POSIX_SYNCHRONIZED_IO <= 0 # define fdatasync fsync #endif #ifdef USE_DB #include #endif /* USE_DB */ #define SUB_UNSET 0 #define SUB_VERBATIM 1 #define SUB_MAILDIRPP 2 #define SUB_LEGACY 3 typedef union maildir_store_conf { store_conf_t gen; struct { STORE_CONF char *path; char *path_sfx; char *inbox; #ifdef USE_DB int alt_map; #endif /* USE_DB */ char info_delimiter; char sub_style; char failed; char *info_prefix, *info_stop; /* precalculated from info_delimiter */ }; } maildir_store_conf_t; typedef union maildir_message { message_t gen; struct { MESSAGE(union maildir_message) char *base; }; } maildir_message_t; typedef union maildir_store { store_t gen; struct { STORE(union maildir_store) int uvfd, uvok, is_inbox, fresh[3]; uint opts, minuid, maxuid, finduid, pairuid, newuid, uidvalidity, nuid; uint_array_t excs; char *path; // own char *trash; #ifdef USE_DB DB *db; char *usedb; #endif /* USE_DB */ string_list_t *boxes; // _list results char listed; // was _list already run with these flags? // note that the message counts do _not_ reflect stats from msgs, // but mailbox totals. also, don't trust them beyond the initial load. int total_msgs, recent_msgs; maildir_message_t *msgs; maildir_message_t **app_msgs; // Only for testing find_new() wakeup_t lcktmr; void (*expunge_callback)( message_t *msg, void *aux ); void (*bad_callback)( void *aux ); void *callback_aux; }; } maildir_store_t; #ifdef USE_DB static DBT key, value; /* no need to be reentrant, and this saves lots of memset()s */ #endif /* USE_DB */ static struct flock lck; static int MaildirCount; static uchar maildir_parse_flags( const char *info_prefix, const char *base ) { const char *s; uint i; uchar flags; flags = 0; if ((s = strstr( base, info_prefix ))) for (s += 3, i = 0; i < as(MsgFlags); i++) if (strchr( s, MsgFlags[i] )) flags |= (1 << i); return flags; } static int maildir_ensure_path( maildir_store_conf_t *conf ) { if (!conf->path) { error( "Maildir error: store '%s' has no Path\n", conf->name ); conf->failed = FAIL_FINAL; return -1; } return 0; } /* Subdirs of INBOX include a leading slash. */ /* Path includes a trailing slash, Inbox does not. */ static char * maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box ) { char *out, *p; const char *prefix, *infix; uint pl, il, bl, n; char c; if (in_inbox || conf->sub_style == SUB_MAILDIRPP) { prefix = conf->inbox; infix = NULL; il = 0; } else { if (maildir_ensure_path( conf ) < 0) return NULL; prefix = conf->path; infix = conf->path_sfx; il = strlen( infix ) + 1; } pl = strlen( prefix ); for (bl = 0, n = 0; (c = box[bl]); bl++) { if (c == '/') { if (conf->sub_style == SUB_UNSET) { error( "Maildir error: accessing subfolder '%s', but store '%s' does not specify SubFolders style\n", box, conf->name ); return NULL; } n++; } else if (c == '.' && conf->sub_style == SUB_MAILDIRPP) { error( "Maildir error: store '%s', folder '%s': SubFolders style Maildir++ does not support dots in mailbox names\n", conf->name, box ); return NULL; } } switch (conf->sub_style) { case SUB_VERBATIM: n = 0; break; case SUB_MAILDIRPP: n = 2; break; default: /* SUB_LEGACY and SUB_UNSET */ break; } out = nfmalloc( pl + il + bl + n + 1 ); memcpy( out, prefix, pl ); p = out + pl; if (conf->sub_style == SUB_MAILDIRPP) { *p++ = '/'; *p++ = '.'; } else if (il) { *p++ = '/'; memcpy( p, infix, il - 1 ); p += il - 1; } while ((c = *box++)) { if (c == '/') { switch (conf->sub_style) { case SUB_VERBATIM: *p++ = c; break; case SUB_LEGACY: *p++ = c; FALLTHROUGH default: /* SUB_MAILDIRPP */ *p++ = '.'; break; } } else { *p++ = c; } } *p = 0; return out; } static int maildir_validate_path( maildir_store_conf_t *conf ) { struct stat st; if (stat( conf->path, &st ) || !S_ISDIR(st.st_mode)) { error( "Maildir error: cannot open store '%s'\n", conf->path ); conf->failed = FAIL_FINAL; return -1; } return 0; } static void lcktmr_timeout( void *aux ); static store_t * maildir_alloc_store( store_conf_t *gconf, const char *label ATTR_UNUSED ) { maildir_store_t *ctx; ctx = nfzalloc( sizeof(*ctx) ); ctx->driver = &maildir_driver; ctx->gen.conf = gconf; ctx->uvfd = -1; init_wakeup( &ctx->lcktmr, lcktmr_timeout, ctx ); return &ctx->gen; } static void maildir_connect_store( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_conf_t *conf = ctx->conf; if (conf->path && maildir_validate_path( conf ) < 0) { cb( DRV_STORE_BAD, aux ); return; } if (conf->trash && !(ctx->trash = maildir_join_path( conf, 0, conf->trash ))) { cb( DRV_STORE_BAD, aux ); return; } cb( DRV_OK, aux ); } static void free_maildir_messages( maildir_message_t *msg ) { while (msg) { maildir_message_t *tmsg = msg->next; free( msg->base ); free( msg->msgid ); free( msg ); msg = tmsg; } } static void maildir_cleanup( store_t *gctx ) { maildir_store_t *ctx = (maildir_store_t *)gctx; free_maildir_messages( ctx->msgs ); #ifdef USE_DB if (ctx->db) ctx->db->close( ctx->db, 0 ); free( ctx->usedb ); #endif /* USE_DB */ free( ctx->path ); free( ctx->excs.data ); if (ctx->uvfd >= 0) close( ctx->uvfd ); conf_wakeup( &ctx->lcktmr, -1 ); } static void maildir_free_store( store_t *gctx ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_cleanup( gctx ); wipe_wakeup( &ctx->lcktmr ); free( ctx->trash ); free_string_list( ctx->boxes ); free( gctx ); } static void maildir_cleanup_drv( void ) { } static void maildir_set_callbacks( store_t *gctx, void (*exp_cb)( message_t *, void * ), void (*bad_cb)( void * ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; ctx->expunge_callback = exp_cb; ctx->bad_callback = bad_cb; ctx->callback_aux = aux; } static void maildir_invoke_bad_callback( maildir_store_t *ctx ) { ctx->bad_callback( ctx->callback_aux ); } static int maildir_list_maildirpp( maildir_store_t *ctx, int flags, const char *inbox ) { DIR *dir; struct dirent *de; int warned = 0; struct stat st; if (ctx->listed & LIST_PATH) // Implies LIST_INBOX return 0; if (!(ctx->listed & LIST_INBOX)) add_string_list( &ctx->boxes, "INBOX" ); char path[_POSIX_PATH_MAX]; int pathLen = nfsnprintf( path, _POSIX_PATH_MAX, "%s/", inbox ); if (!(dir = opendir( path ))) { if (errno == ENOENT || errno == ENOTDIR) return 0; sys_error( "Maildir error: cannot list %s", path ); return -1; } while ((de = readdir( dir ))) { const char *ent = de->d_name; if (*ent++ != '.' || !*ent) continue; char name[_POSIX_PATH_MAX]; char *effName = name; if (*ent == '.') { if (ctx->listed & LIST_INBOX) continue; if (!*++ent) continue; // The Maildir++ Inbox is technically not under Path (as there is none), so // "*" would never match INBOX*, which is rather unintuitive. Matching INBOX* // implicitly instead makes it consistent with an IMAP Store with an empty Path. } else { if (!(flags & (LIST_PATH | LIST_PATH_MAYBE))) continue; // Explained in maildir_list_recurse(). if (starts_with( ent, -1, "INBOX", 5 ) && (!ent[5] || ent[5] == '.')) { if (!warned) { warned = 1; path[pathLen] = 0; warn( "Maildir warning: ignoring INBOX in %s\n", path ); } continue; } effName += 6; } nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s/cur", de->d_name ); if (!stat( path, &st ) && S_ISDIR(st.st_mode)) { int nl = nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/%s", ent ); for (int i = 6; i < nl; i++) { if (name[i] == '.') name[i] = '/'; } add_string_list( &ctx->boxes, effName ); } } closedir (dir); if (flags & (LIST_PATH | LIST_PATH_MAYBE)) ctx->listed |= LIST_PATH; ctx->listed |= LIST_INBOX; return 0; } static int maildir_list_inbox( maildir_store_t *ctx ); static int maildir_list_recurse( maildir_store_t *ctx, int isBox, const char *inbox, uint inboxLen, char *suffix, int suffixLen, char *path, int pathLen, char *name, int nameLen ) { DIR *dir; int style = ctx->conf->sub_style; int pl, nl; struct dirent *de; struct stat st; if (!(dir = opendir( path ))) { if (isBox && (errno == ENOENT || errno == ENOTDIR)) return 0; sys_error( "Maildir error: cannot list %s", path ); return -1; } if (isBox > 1 && style == SUB_UNSET) { error( "Maildir error: found subfolder '%.*s', but store '%s' does not specify SubFolders style\n", nameLen - 1, name, ctx->conf->name ); closedir( dir ); return -1; } if (isBox > 10) { // We do the other checks first to avoid confusing error messages for files. error( "Maildir error: path %s is too deeply nested. Symlink loop?\n", path ); closedir( dir ); return -1; } while ((de = readdir( dir ))) { const char *ent = de->d_name; if (ent[0] == '.' && (!ent[1] || (ent[1] == '.' && !ent[2]))) continue; pl = nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s", ent ); if (pl == 3 && (!memcmp( ent, "cur", 3 ) || !memcmp( ent, "new", 3 ) || !memcmp( ent, "tmp", 3 ))) continue; if (suffixLen) { if (!starts_with( ent, pl, suffix, suffixLen )) continue; if (pl == suffixLen) { error( "Maildir error: empty mailbox name under %s - did you forget the trailing slash?\n", path ); closedir( dir ); return -1; } ent += suffixLen; } pl += pathLen; if (inbox && equals( path, pl, inbox, inboxLen )) { // Inbox nested into Path. if (maildir_list_inbox( ctx ) < 0) { closedir( dir ); return -1; } } else { if (style == SUB_LEGACY) { if (*ent == '.') { if (!isBox) continue; ent++; } else { if (isBox) continue; } } // A folder named "INBOX" would be indistinguishable from the // actual INBOX after prefix stripping, so drop it. This applies // only to the fully uppercased spelling, as our canonical box // names are case-sensitive (unlike IMAP's INBOX). if (!nameLen && equals( ent, -1, "INBOX", 5 )) { path[pathLen] = 0; warn( "Maildir warning: ignoring INBOX in %s\n", path ); continue; } nl = nameLen + nfsnprintf( name + nameLen, _POSIX_PATH_MAX - nameLen, "%s", ent ); path[pl++] = '/'; nfsnprintf( path + pl, _POSIX_PATH_MAX - pl, "cur" ); if (!stat( path, &st ) && S_ISDIR(st.st_mode)) add_string_list( &ctx->boxes, name ); path[pl] = 0; name[nl++] = '/'; if (maildir_list_recurse( ctx, isBox + 1, inbox, inboxLen, NULL, 0, path, pl, name, nl ) < 0) { closedir( dir ); return -1; } } } closedir (dir); return 0; } static int maildir_list_inbox( maildir_store_t *ctx ) { char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX]; if (ctx->listed & LIST_INBOX) return 0; ctx->listed |= LIST_INBOX; add_string_list( &ctx->boxes, "INBOX" ); return maildir_list_recurse( ctx, 1, NULL, 0, NULL, 0, path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->inbox ), name, nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/" ) ); } static int maildir_list_path( maildir_store_t *ctx ) { char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX]; if (ctx->listed & LIST_PATH) return 0; ctx->listed |= LIST_PATH; if (maildir_ensure_path( ctx->conf ) < 0) return -1; const char *inbox = ctx->conf->inbox; return maildir_list_recurse( ctx, 0, inbox, strlen( inbox ), ctx->conf->path_sfx, strlen( ctx->conf->path_sfx ), path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->path ), name, 0 ); } static void maildir_list_store( store_t *gctx, int flags, void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_conf_t *conf = ctx->conf; if (conf->sub_style == SUB_MAILDIRPP ? maildir_list_maildirpp( ctx, flags, conf->inbox ) < 0 : ((((flags & LIST_PATH) || ((flags & LIST_PATH_MAYBE) && conf->path)) && maildir_list_path( ctx ) < 0) || ((flags & LIST_INBOX) && maildir_list_inbox( ctx ) < 0))) { maildir_invoke_bad_callback( ctx ); cb( DRV_CANCELED, NULL, aux ); } else { cb( DRV_OK, ctx->boxes, aux ); } } static const char *subdirs[] = { "cur", "new", "tmp" }; typedef struct { char *base; char *msgid; uint size; uint uid; uchar recent; char tuid[TUIDL]; } msg_t; DEFINE_ARRAY_TYPE(msg_t) static void maildir_free_scan( msg_t_array_alloc_t *msglist ) { uint i; if (msglist->array.data) { for (i = 0; i < msglist->array.size; i++) { free( msglist->array.data[i].base ); free( msglist->array.data[i].msgid ); } free( msglist->array.data ); } } #define _24_HOURS (3600 * 24) static int maildir_clear_tmp( char *buf, int bufsz, int bl ) { DIR *dirp; struct dirent *entry; time_t now; struct stat st; memcpy( buf + bl, "tmp/", 5 ); bl += 4; if (!(dirp = opendir( buf ))) { sys_error( "Maildir error: cannot list %s", buf ); return DRV_BOX_BAD; } time( &now ); while ((entry = readdir( dirp ))) { nfsnprintf( buf + bl, bufsz - bl, "%s", entry->d_name ); if (stat( buf, &st )) { if (errno != ENOENT) sys_error( "Maildir error: cannot access %s", buf ); } else if (S_ISREG(st.st_mode) && now - st.st_ctime >= _24_HOURS) { /* This should happen infrequently enough that it won't be * bothersome to the user to display when it occurs. */ notice( "Maildir notice: removing stale file %s\n", buf ); if (unlink( buf ) && errno != ENOENT) sys_error( "Maildir error: cannot remove %s", buf ); } } closedir( dirp ); return DRV_OK; } static int maildir_validate( const char *box, int create, maildir_store_t *ctx ) { int i, bl, ret; struct stat st; char buf[_POSIX_PATH_MAX]; bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box ); if (stat( buf, &st )) { if (errno != ENOENT) { sys_error( "Maildir error: cannot access mailbox '%s'", buf ); return DRV_BOX_BAD; } if (!create) return DRV_BOX_BAD; if (mkdir_p( buf, bl - 1 )) { sys_error( "Maildir error: cannot create mailbox '%s'", buf ); ctx->conf->failed = FAIL_FINAL; maildir_invoke_bad_callback( ctx ); return DRV_CANCELED; } } else if (!S_ISDIR(st.st_mode)) { notdir: error( "Maildir error: '%s' is no valid mailbox\n", buf ); return DRV_BOX_BAD; } for (i = 0; i < 3; i++) { memcpy( buf + bl, subdirs[i], 4 ); if (stat( buf, &st )) { /* We always create new/ and tmp/ if they are missing. cur/ is the presence indicator. */ if (!i && !create) return DRV_BOX_BAD; if (mkdir( buf, 0700 )) { sys_error( "Maildir error: cannot create directory %s", buf ); return DRV_BOX_BAD; } ctx->fresh[i] = 1; } else if (!S_ISDIR(st.st_mode)) { goto notdir; } else { if (i == 2) { if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) != DRV_OK) return ret; } } } return DRV_OK; } #ifdef USE_DB static void make_key( const char *info_stop, DBT *tkey, const char *name ) { char *u = strpbrk( name, info_stop ); DIAG_PUSH DIAG_DISABLE("-Wcast-qual") // C has no const_cast<> ... tkey->data = (char *)name; DIAG_POP tkey->size = u ? (size_t)(u - name) : strlen( name ); } #endif /* USE_DB */ static int maildir_store_uidval( maildir_store_t *ctx ) { int n; #ifdef USE_DB int ret; uint uv[2]; #endif char buf[128]; #ifdef USE_DB if (ctx->db) { key.data = (void *)"UIDVALIDITY"; key.size = 11; uv[0] = ctx->uidvalidity; uv[1] = ctx->nuid; value.data = uv; value.size = sizeof(uv); if ((ret = ctx->db->put( ctx->db, NULL, &key, &value, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->put()" ); return DRV_BOX_BAD; } if ((ret = ctx->db->sync( ctx->db, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->sync()" ); return DRV_BOX_BAD; } } else #endif /* USE_DB */ { n = sprintf( buf, "%u\n%u\n", ctx->uidvalidity, ctx->nuid ); lseek( ctx->uvfd, 0, SEEK_SET ); if (write( ctx->uvfd, buf, (uint)n ) != n || ftruncate( ctx->uvfd, n ) || (UseFSync && fdatasync( ctx->uvfd ))) { error( "Maildir error: cannot write UIDVALIDITY in %s\n", ctx->path ); return DRV_BOX_BAD; } } conf_wakeup( &ctx->lcktmr, 2000 ); return DRV_OK; } static int maildir_init_uidval( maildir_store_t *ctx ) { ctx->uidvalidity = (uint)time( NULL ); ctx->nuid = 0; ctx->uvok = 0; #ifdef USE_DB if (ctx->db) { u_int32_t count; ctx->db->truncate( ctx->db, NULL, &count, 0 ); } #endif /* USE_DB */ return maildir_store_uidval( ctx ); } static int maildir_init_uidval_new( maildir_store_t *ctx ) { notice( "Maildir notice: no UIDVALIDITY in %s, creating new.\n", ctx->path ); return maildir_init_uidval( ctx ); } static int maildir_uidval_lock( maildir_store_t *ctx ) { int n; #ifdef USE_DB int ret; struct stat st; #endif char buf[128]; if (pending_wakeup( &ctx->lcktmr )) { /* The unlock timer is active, so we are obviously already locked. */ return DRV_OK; } /* This (theoretically) works over NFS. Let's hope nobody else did the same in the opposite order, as we'd deadlock then. */ #if SEEK_SET != 0 lck.l_whence = SEEK_SET; #endif lck.l_type = F_WRLCK; if (fcntl( ctx->uvfd, F_SETLKW, &lck )) { error( "Maildir error: cannot fcntl lock UIDVALIDITY in %s.\n", ctx->path ); return DRV_BOX_BAD; } #ifdef USE_DB if (ctx->usedb) { if (fstat( ctx->uvfd, &st )) { sys_error( "Maildir error: cannot fstat UID database in %s", ctx->path ); return DRV_BOX_BAD; } if (db_create( &ctx->db, NULL, 0 )) { fputs( "Maildir error: db_create() failed\n", stderr ); return DRV_BOX_BAD; } if ((ret = (ctx->db->open)( ctx->db, NULL, ctx->usedb, NULL, DB_HASH, st.st_size ? 0 : DB_CREATE | DB_TRUNCATE, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->open(%s)", ctx->usedb ); return DRV_BOX_BAD; } key.data = (void *)"UIDVALIDITY"; key.size = 11; if ((ret = ctx->db->get( ctx->db, NULL, &key, &value, 0 ))) { if (ret != DB_NOTFOUND) { ctx->db->err( ctx->db, ret, "Maildir error: db->get()" ); return DRV_BOX_BAD; } return maildir_init_uidval_new( ctx ); } ctx->uidvalidity = ((uint *)value.data)[0]; ctx->nuid = ((uint *)value.data)[1]; } else #endif { lseek( ctx->uvfd, 0, SEEK_SET ); if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 || (buf[n] = 0, sscanf( buf, "%u\n%u", &ctx->uidvalidity, &ctx->nuid ) != 2)) { #if 1 /* In a generic driver, resetting the UID validity would be the right thing. * But this would mess up the sync state completely. So better bail out and * give the user a chance to fix the mailbox. */ if (n) { error( "Maildir error: cannot read UIDVALIDITY in %s.\n", ctx->path ); return DRV_BOX_BAD; } #endif return maildir_init_uidval_new( ctx ); } } ctx->uvok = 1; conf_wakeup( &ctx->lcktmr, 2000 ); return DRV_OK; } static void maildir_uidval_unlock( maildir_store_t *ctx ) { #ifdef USE_DB if (ctx->db) { ctx->db->close( ctx->db, 0 ); ctx->db = NULL; } #endif /* USE_DB */ lck.l_type = F_UNLCK; fcntl( ctx->uvfd, F_SETLK, &lck ); } static void lcktmr_timeout( void *aux ) { maildir_uidval_unlock( (maildir_store_t *)aux ); } static int maildir_obtain_uid( maildir_store_t *ctx, uint *uid ) { int ret; if ((ret = maildir_uidval_lock( ctx )) != DRV_OK) return ret; *uid = ++ctx->nuid; return maildir_store_uidval( ctx ); } #ifdef USE_DB static int maildir_set_uid( maildir_store_t *ctx, const char *name, uint *uid ) { int ret; if ((ret = maildir_uidval_lock( ctx )) != DRV_OK) return ret; *uid = ++ctx->nuid; make_key( ctx->conf->info_stop, &key, name ); value.data = uid; value.size = sizeof(*uid); if ((ret = ctx->db->put( ctx->db, NULL, &key, &value, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->put()" ); return DRV_BOX_BAD; } return maildir_store_uidval( ctx ); } #endif static int maildir_compare( const void *l, const void *r ) { const msg_t *lm = (const msg_t *)l, *rm = (const msg_t *)r; char *ldot, *rdot, *ldot2, *rdot2, *lseq, *rseq; uint llen, rlen; int ret; if (lm->uid != rm->uid) // Can't subtract, the result might not fit into signed int. return lm->uid > rm->uid ? 1 : -1; /* No UID, so sort by arrival date. We should not do this, but we rely on the suggested unique file name scheme - we have no choice. */ /* The first field are always the seconds. Alphabetical sort should be faster than numeric. */ if (!(ldot = strchr( lm->base, '.' )) || !(rdot = strchr( rm->base, '.' ))) goto stronly; /* Should never happen ... */ llen = (uint)(ldot - lm->base), rlen = (uint)(rdot - rm->base); /* The shorter number is smaller. Really. This won't trigger with any mail created after Sep 9 2001 anyway. */ if ((ret = (int)llen - (int)rlen)) return ret; if ((ret = memcmp( lm->base, rm->base, llen ))) return ret; ldot++, rdot++; if ((llen = strtoul( ldot, &ldot2, 10 ))) { if (!(rlen = strtoul( rdot, &rdot2, 10 ))) goto stronly; /* Comparing apples to oranges ... */ /* Classical PID specs */ if ((ret = (int)llen - (int)rlen)) { retpid: /* Handle PID wraparound. This works only on systems where PIDs are not reused too fast */ if (ret > 20000 || ret < -20000) ret = -ret; return ret; } return (*ldot2 != '_' ? 0 : atoi( ldot2 + 1 )) - (*rdot2 != '_' ? 0 : atoi( rdot2 + 1 )); } if (!(ldot2 = strchr( ldot, '.' )) || !(rdot2 = strchr( rdot, '.' ))) goto stronly; /* Should never happen ... */ llen = (uint)(ldot2 - ldot), rlen = (uint)(rdot2 - rdot); if (((lseq = memchr( ldot, '#', llen )) && (rseq = memchr( rdot, '#', rlen ))) || ((lseq = memchr( ldot, 'M', llen )) && (rseq = memchr( rdot, 'M', rlen )))) return atoi( lseq + 1 ) - atoi( rseq + 1 ); if ((lseq = memchr( ldot, 'P', llen )) && (rseq = memchr( rdot, 'P', rlen ))) { if ((ret = atoi( lseq + 1 ) - atoi( rseq + 1 ))) goto retpid; if ((lseq = memchr( ldot, 'Q', llen )) && (rseq = memchr( rdot, 'Q', rlen ))) return atoi( lseq + 1 ) - atoi( rseq + 1 ); } stronly: /* Fall-back, so the sort order is defined at all */ return strcmp( lm->base, rm->base ); } static int maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist ) { maildir_store_conf_t *conf = ctx->conf; DIR *d; FILE *f; struct dirent *e; const char *u, *ru; #ifdef USE_DB DB *tdb; DBC *dbc; #endif /* USE_DB */ msg_t *entry; uint i; int bl, fnl, ret; uint uid; time_t now, stamps[2]; struct stat st; char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX]; again: ARRAY_INIT( msglist ); ctx->total_msgs = ctx->recent_msgs = 0; if (ctx->uvok || ctx->maxuid == UINT_MAX) { #ifdef USE_DB if (ctx->usedb) { if (db_create( &tdb, NULL, 0 )) { fputs( "Maildir error: db_create() failed\n", stderr ); return DRV_BOX_BAD; } if ((tdb->open)( tdb, NULL, NULL, NULL, DB_HASH, DB_CREATE, 0 )) { fputs( "Maildir error: tdb->open() failed\n", stderr ); bork: tdb->close( tdb, 0 ); return DRV_BOX_BAD; } } #endif /* USE_DB */ bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path ); restat: now = time( NULL ); for (i = 0; i < 2; i++) { memcpy( buf + bl, subdirs[i], 4 ); if (stat( buf, &st )) { sys_error( "Maildir error: cannot stat %s", buf ); goto dfail; } if (st.st_mtime == now && !(DFlags & ZERODELAY) && !ctx->fresh[i]) { /* If the modification happened during this second, we wouldn't be able to * tell if there were further modifications during this second. So wait. * This has the nice side effect that we wait for "batches" of changes to * complete. On the downside, it can potentially block indefinitely. */ notice( "Maildir notice: sleeping due to recent directory modification.\n" ); sleep( 1 ); /* FIXME: should make this async */ goto restat; } stamps[i] = st.st_mtime; } for (i = 0; i < 2; i++) { memcpy( buf + bl, subdirs[i], 4 ); if (!(d = opendir( buf ))) { sys_error( "Maildir error: cannot list %s", buf ); rfail: maildir_free_scan( msglist ); dfail: #ifdef USE_DB if (ctx->usedb) tdb->close( tdb, 0 ); #endif /* USE_DB */ return DRV_BOX_BAD; } while ((e = readdir( d ))) { if (*e->d_name == '.') continue; ctx->total_msgs++; ctx->recent_msgs += i; #ifdef USE_DB if (ctx->usedb) { if (maildir_uidval_lock( ctx ) != DRV_OK) goto mbork; make_key( conf->info_stop, &key, e->d_name ); if ((ret = ctx->db->get( ctx->db, NULL, &key, &value, 0 ))) { if (ret != DB_NOTFOUND) { ctx->db->err( ctx->db, ret, "Maildir error: db->get()" ); mbork: maildir_free_scan( msglist ); closedir( d ); goto bork; } uid = UINT_MAX; } else { value.size = 0; if ((ret = tdb->put( tdb, NULL, &key, &value, 0 ))) { tdb->err( tdb, ret, "Maildir error: tdb->put()" ); goto mbork; } uid = *(uint *)value.data; } } else #endif /* USE_DB */ { uid = (ctx->uvok && (u = strstr( e->d_name, ",U=" ))) ? strtoul( u + 3, NULL, 10 ) : 0; if (!uid) uid = UINT_MAX; } if (uid <= ctx->maxuid) { if (uid < ctx->minuid && !find_uint_array( ctx->excs, uid )) continue; entry = msg_t_array_append( msglist ); entry->base = nfstrdup( e->d_name ); entry->msgid = NULL; entry->uid = uid; entry->recent = (uchar)i; entry->size = 0; entry->tuid[0] = 0; } } closedir( d ); } for (i = 0; i < 2; i++) { memcpy( buf + bl, subdirs[i], 4 ); if (stat( buf, &st )) { sys_error( "Maildir error: cannot re-stat %s", buf ); goto rfail; } if (st.st_mtime != stamps[i]) { /* Somebody messed with the mailbox since we started listing it. */ #ifdef USE_DB if (ctx->usedb) tdb->close( tdb, 0 ); #endif /* USE_DB */ maildir_free_scan( msglist ); goto again; } } #ifdef USE_DB if (ctx->usedb) { if (maildir_uidval_lock( ctx ) != DRV_OK) { } else if ((ret = ctx->db->cursor( ctx->db, NULL, &dbc, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->cursor()" ); } else { for (;;) { if ((ret = dbc->c_get( dbc, &key, &value, DB_NEXT ))) { if (ret != DB_NOTFOUND) ctx->db->err( ctx->db, ret, "Maildir error: db->c_get()" ); break; } if (!equals( key.data, (int)key.size, "UIDVALIDITY", 11 ) && (ret = tdb->get( tdb, NULL, &key, &value, 0 ))) { if (ret != DB_NOTFOUND) { tdb->err( tdb, ret, "Maildir error: tdb->get()" ); break; } if ((ret = dbc->c_del( dbc, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->c_del()" ); break; } } } dbc->c_close( dbc ); } tdb->close( tdb, 0 ); } #endif /* USE_DB */ qsort( msglist->array.data, msglist->array.size, sizeof(msg_t), maildir_compare ); for (uid = i = 0; i < msglist->array.size; i++) { entry = &msglist->array.data[i]; if (entry->uid != UINT_MAX) { if (uid == entry->uid) { #if 1 /* See comment in maildir_uidval_lock() why this is fatal. */ error( "Maildir error: duplicate UID %u in %s.\n", uid, ctx->path ); maildir_free_scan( msglist ); return DRV_BOX_BAD; #else notice( "Maildir notice: duplicate UID in %s; changing UIDVALIDITY.\n", ctx->path ); if ((ret = maildir_init_uid( ctx )) != DRV_OK) { maildir_free_scan( msglist ); return ret; } maildir_free_scan( msglist ); goto again; #endif } uid = entry->uid; if (uid > ctx->nuid) { /* In principle, we could just warn and top up nuid. However, getting into this * situation might indicate some serious trouble, so let's not make it worse. */ error( "Maildir error: UID %u is beyond highest assigned UID %u in %s.\n", uid, ctx->nuid, ctx->path ); maildir_free_scan( msglist ); return DRV_BOX_BAD; } fnl = 0; #ifdef USE_DB } else if (ctx->usedb) { if ((ret = maildir_set_uid( ctx, entry->base, &uid )) != DRV_OK) { maildir_free_scan( msglist ); return ret; } entry->uid = uid; fnl = 0; #endif /* USE_DB */ } else { if ((ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) { maildir_free_scan( msglist ); return ret; } entry->uid = uid; if ((u = strstr( entry->base, ",U=" ))) for (ru = u + 3; isdigit( (uchar)*ru ); ru++); else u = ru = strchr( entry->base, conf->info_delimiter ); fnl = (u ? nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%.*s,U=%u%s", subdirs[entry->recent], (int)(u - entry->base), entry->base, uid, ru ) : nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%s,U=%u", subdirs[entry->recent], entry->base, uid )) - 4; memcpy( nbuf, buf, (size_t)(bl + 4) ); nfsnprintf( nbuf + bl + 4, _POSIX_PATH_MAX - bl - 4, "%s", entry->base ); if (rename( nbuf, buf )) { if (errno != ENOENT) { sys_error( "Maildir error: cannot rename %s to %s", nbuf, buf ); fail: maildir_free_scan( msglist ); return DRV_BOX_BAD; } retry: maildir_free_scan( msglist ); goto again; } free( entry->base ); entry->base = nfstrndup( buf + bl + 4, (size_t)fnl ); } int want_size = ((ctx->opts & OPEN_OLD_SIZE) && uid <= ctx->newuid) || ((ctx->opts & OPEN_NEW_SIZE) && uid > ctx->newuid); int want_tuid = ((ctx->opts & OPEN_FIND) && uid >= ctx->finduid); int want_msgid = ((ctx->opts & OPEN_PAIRED_IDS) && uid <= ctx->pairuid); if (!want_size && !want_tuid && !want_msgid) continue; if (!fnl) nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%s", subdirs[entry->recent], entry->base ); if (want_size) { if (stat( buf, &st )) { if (errno != ENOENT) { sys_error( "Maildir error: cannot stat %s", buf ); goto fail; } goto retry; } // The clipped value is good enough for MaxSize comparisons. entry->size = st.st_size > UINT_MAX ? UINT_MAX : (uint)st.st_size; } if (want_tuid || want_msgid) { if (!(f = fopen( buf, "r" ))) { if (errno != ENOENT) { sys_error( "Maildir error: cannot open %s", buf ); goto fail; } goto retry; } int off, in_msgid = 0; char lnbuf[1000]; // Says RFC2822 while ((want_tuid || want_msgid) && fgets( lnbuf, sizeof(lnbuf), f )) { int bufl = strlen( lnbuf ); if (bufl && lnbuf[bufl - 1] == '\n') --bufl; if (bufl && lnbuf[bufl - 1] == '\r') --bufl; if (!bufl) break; if (want_tuid && starts_with( lnbuf, bufl, "X-TUID: ", 8 )) { if (bufl < 8 + TUIDL) { error( "Maildir error: malformed X-TUID header in %s\n", buf ); continue; } memcpy( entry->tuid, lnbuf + 8, TUIDL ); want_tuid = 0; in_msgid = 0; continue; } if (want_msgid && starts_with_upper( lnbuf, bufl, "MESSAGE-ID:", 11 )) { off = 11; } else if (in_msgid) { if (!isspace( lnbuf[0] )) { in_msgid = 0; continue; } off = 1; } else { continue; } while (off < bufl && isspace( lnbuf[off] )) off++; if (off == bufl) { in_msgid = 1; continue; } entry->msgid = nfstrndup( lnbuf + off, (size_t)(bufl - off) ); want_msgid = 0; in_msgid = 0; } fclose( f ); } } ctx->uvok = 1; } return DRV_OK; } static void maildir_init_msg( maildir_store_t *ctx, maildir_message_t *msg, msg_t *entry ) { msg->base = entry->base; entry->base = NULL; /* prevent deletion */ msg->msgid = entry->msgid; entry->msgid = NULL; /* prevent deletion */ msg->size = entry->size; memcpy( msg->tuid, entry->tuid, TUIDL ); if (entry->recent) msg->status |= M_RECENT; if (ctx->opts & OPEN_FLAGS) { msg->status |= M_FLAGS; msg->flags = maildir_parse_flags( ctx->conf->info_prefix, msg->base ); } else { msg->flags = 0; } } static void maildir_app_msg( maildir_store_t *ctx, maildir_message_t ***msgapp, msg_t *entry ) { maildir_message_t *msg = nfmalloc( sizeof(*msg) ); msg->next = **msgapp; **msgapp = msg; *msgapp = &msg->next; msg->uid = entry->uid; msg->status = 0; msg->srec = NULL; maildir_init_msg( ctx, msg, entry ); } static int maildir_select_box( store_t *gctx, const char *name ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_conf_t *conf = ctx->conf; maildir_cleanup( gctx ); ctx->msgs = NULL; ctx->app_msgs = &ctx->msgs; ctx->excs.data = NULL; ctx->uvfd = -1; #ifdef USE_DB ctx->db = NULL; ctx->usedb = NULL; #endif /* USE_DB */ ctx->fresh[0] = ctx->fresh[1] = 0; if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { if (!name[5]) { ctx->path = nfstrdup( conf->inbox ); ctx->is_inbox = 1; } else { ctx->path = maildir_join_path( conf, 1, name + 5 ); ctx->is_inbox = 0; } } else { if (!(ctx->path = maildir_join_path( conf, 0, name ))) return DRV_STORE_BAD; ctx->is_inbox = 0; } return ctx->path ? DRV_OK : DRV_BOX_BAD; } static const char * maildir_get_box_path( store_t *gctx ) { return ((maildir_store_t *)gctx)->path; } static void maildir_open_box( store_t *gctx, void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; int ret; char uvpath[_POSIX_PATH_MAX]; if ((ret = maildir_validate( ctx->path, ctx->is_inbox, ctx )) != DRV_OK) goto bail; nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", ctx->path ); #ifndef USE_DB if ((ctx->uvfd = open( uvpath, O_RDWR | O_CREAT, 0600 )) < 0) { sys_error( "Maildir error: cannot write %s", uvpath ); cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } #else ctx->usedb = NULL; if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) { nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", ctx->path ); if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) { if (ctx->conf->alt_map) { if ((ctx->uvfd = open( uvpath, O_RDWR | O_CREAT, 0600 )) >= 0) goto dbok; } else { nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", ctx->path ); if ((ctx->uvfd = open( uvpath, O_RDWR | O_CREAT, 0600 )) >= 0) goto fnok; } sys_error( "Maildir error: cannot write %s", uvpath ); cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } else { dbok: ctx->usedb = nfstrdup( uvpath ); } } fnok: #endif /* USE_DB */ ret = maildir_uidval_lock( ctx ); bail: cb( ret, ctx->uidvalidity, aux ); } static uint maildir_get_uidnext( store_t *gctx ) { maildir_store_t *ctx = (maildir_store_t *)gctx; return ctx->nuid; } static xint maildir_get_supported_flags( store_t *gctx ATTR_UNUSED ) { return 255; } static void maildir_create_box( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; cb( maildir_validate( ctx->path, 1, ctx ), aux ); } static int maildir_confirm_box_empty( store_t *gctx ) { maildir_store_t *ctx = (maildir_store_t *)gctx; msg_t_array_alloc_t msglist; ctx->excs.size = ctx->minuid = ctx->maxuid = ctx->finduid = 0; if (maildir_scan( ctx, &msglist ) != DRV_OK) return DRV_BOX_BAD; maildir_free_scan( &msglist ); return ctx->total_msgs ? DRV_BOX_BAD : DRV_OK; } static void maildir_delete_box( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; int i, bl, ret = DRV_OK; struct stat st; char buf[_POSIX_PATH_MAX]; bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path ); if (stat( buf, &st )) { if (errno != ENOENT) { sys_error( "Maildir error: cannot access mailbox '%s'", ctx->path ); ret = DRV_BOX_BAD; } } else if (!S_ISDIR(st.st_mode)) { error( "Maildir error: '%s' is no valid mailbox\n", ctx->path ); ret = DRV_BOX_BAD; } else if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) == DRV_OK) { nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, ".uidvalidity" ); if (unlink( buf ) && errno != ENOENT) goto badrm; #ifdef USE_DB nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, ".isyncuidmap.db" ); if (unlink( buf ) && errno != ENOENT) goto badrm; #endif /* We delete cur/ last, as it is the indicator for a present mailbox. * That way an interrupted operation can be resumed. */ for (i = 3; --i >= 0; ) { memcpy( buf + bl, subdirs[i], 4 ); if (rmdir( buf ) && errno != ENOENT) { badrm: sys_error( "Maildir error: cannot remove '%s'", buf ); ret = DRV_BOX_BAD; break; } } } cb( ret, aux ); } static int maildir_finish_delete_box( store_t *gctx ) { maildir_store_t *ctx = (maildir_store_t *)gctx; /* Subfolders are not deleted; the deleted folder is only "stripped of its mailboxness". * Consequently, the rmdir may legitimately fail. This behavior follows the IMAP spec. */ if (rmdir( ctx->path ) && errno != ENOENT && errno != ENOTEMPTY) { sys_error( "Maildir warning: cannot remove '%s'", ctx->path ); return DRV_BOX_BAD; } return DRV_OK; } static uint maildir_prepare_load_box( store_t *gctx, uint opts ) { maildir_store_t *ctx = (maildir_store_t *)gctx; if (opts & OPEN_SETFLAGS) opts |= OPEN_PAIRED; if (opts & OPEN_EXPUNGE) opts |= OPEN_PAIRED | OPEN_OLD | OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE; ctx->opts = opts; return opts; } static void maildir_load_box( store_t *gctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs, void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t **msgapp; msg_t_array_alloc_t msglist; uint i; ctx->minuid = minuid; ctx->maxuid = maxuid; ctx->finduid = finduid; ctx->pairuid = pairuid; ctx->newuid = newuid; ARRAY_SQUEEZE( &excs ); ctx->excs = excs; assert( !ctx->msgs ); if (maildir_scan( ctx, &msglist ) != DRV_OK) { cb( DRV_BOX_BAD, NULL, 0, 0, aux ); return; } msgapp = &ctx->msgs; for (i = 0; i < msglist.array.size; i++) maildir_app_msg( ctx, &msgapp, msglist.array.data + i ); ctx->app_msgs = msgapp; maildir_free_scan( &msglist ); cb( DRV_OK, &ctx->msgs->gen, ctx->total_msgs, ctx->recent_msgs, aux ); } static int maildir_rescan( maildir_store_t *ctx ) { maildir_message_t **msgapp, *msg; msg_t_array_alloc_t msglist; uint i; ctx->fresh[0] = ctx->fresh[1] = 0; if (maildir_scan( ctx, &msglist ) != DRV_OK) return DRV_BOX_BAD; debug( "Maildir processing rescan of %s:\n", ctx->path ); for (msgapp = &ctx->msgs, i = 0; (msg = *msgapp) || i < msglist.array.size; ) { if (!msg) { debug( " adding new message %u\n", msglist.array.data[i].uid ); maildir_app_msg( ctx, &msgapp, msglist.array.data + i ); i++; } else if (i >= msglist.array.size) { debug( " purging deleted message %u\n", msg->uid ); msg->status = M_DEAD; ctx->expunge_callback( &msg->gen, ctx->callback_aux ); msgapp = &msg->next; } else if (msglist.array.data[i].uid < msg->uid) { /* this should not happen, actually */ debug( " adding new message %u\n", msglist.array.data[i].uid ); maildir_app_msg( ctx, &msgapp, msglist.array.data + i ); i++; } else if (msglist.array.data[i].uid > msg->uid) { debug( " purging deleted message %u\n", msg->uid ); msg->status = M_DEAD; ctx->expunge_callback( &msg->gen, ctx->callback_aux ); msgapp = &msg->next; } else { debug( " updating message %u\n", msg->uid ); msg->status &= ~(M_FLAGS | M_RECENT); free( msg->base ); free( msg->msgid ); maildir_init_msg( ctx, msg, msglist.array.data + i ); i++, msgapp = &msg->next; } } maildir_free_scan( &msglist ); return DRV_OK; } static int ATTR_PRINTFLIKE(3, 0) maildir_again( maildir_store_t *ctx, maildir_message_t *msg, const char *err, ... ) { int ret; if (errno != ENOENT) { va_list va; va_start( va, err ); vsys_error( err, va ); va_end( va ); return DRV_BOX_BAD; } if ((ret = maildir_rescan( ctx )) != DRV_OK) return ret; return (msg->status & M_DEAD) ? DRV_MSG_BAD : DRV_OK; } static void maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, int minimal ATTR_UNUSED, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg = (maildir_message_t *)gmsg; int fd, ret; struct stat st; char buf[_POSIX_PATH_MAX]; for (;;) { nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->path, subdirs[gmsg->status & M_RECENT], msg->base ); if ((fd = open( buf, O_RDONLY )) >= 0) break; if ((ret = maildir_again( ctx, msg, "Cannot open %s", buf )) != DRV_OK) { cb( ret, aux ); return; } } fstat( fd, &st ); if (st.st_size > INT_MAX) { error( "Maildir error: %s is too big\n", buf ); goto mbad; } data->len = st.st_size; if (data->date == -1) data->date = st.st_mtime; data->data = nfmalloc( data->len ); if (read( fd, data->data, data->len ) != data->len) { sys_error( "Maildir error: cannot read %s", buf ); mbad: close( fd ); cb( DRV_MSG_BAD, aux ); return; } close( fd ); if (!(gmsg->status & M_FLAGS)) data->flags = maildir_parse_flags( ctx->conf->info_prefix, msg->base ); cb( DRV_OK, aux ); } static int maildir_make_flags( char info_delimiter, uchar flags, char *buf ) { int i, d; buf[0] = info_delimiter; buf[1] = '2'; buf[2] = ','; for (d = 3, i = 0; i < (int)as(MsgFlags); i++) if (flags & (1 << i)) buf[d++] = MsgFlags[i]; buf[d] = 0; return d; } static void maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, void (*cb)( int sts, uint uid, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; const char *box; int ret, fd, bl; uint uid; char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX], fbuf[as(MsgFlags) + 3], base[128]; bl = nfsnprintf( base, sizeof(base), "%lld.%d_%d.%s", (long long)time( NULL ), Pid, ++MaildirCount, Hostname ); if (!to_trash) { #ifdef USE_DB if (ctx->usedb) { if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) { free( data->data ); cb( ret, 0, aux ); return; } } else #endif /* USE_DB */ { if ((ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) { free( data->data ); cb( ret, 0, aux ); return; } nfsnprintf( base + bl, (int)sizeof(base) - bl, ",U=%u", uid ); } box = ctx->path; } else { uid = 0; box = ctx->trash; } maildir_make_flags( ctx->conf->info_delimiter, data->flags, fbuf ); nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", box, base, fbuf ); if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0) { if (errno != ENOENT || !to_trash) { sys_error( "Maildir error: cannot create %s", buf ); free( data->data ); cb( DRV_BOX_BAD, 0, aux ); return; } if ((ret = maildir_validate( box, 1, ctx )) != DRV_OK) { free( data->data ); cb( ret, 0, aux ); return; } if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0) { sys_error( "Maildir error: cannot create %s", buf ); free( data->data ); cb( DRV_BOX_BAD, 0, aux ); return; } } ret = write( fd, data->data, data->len ); free( data->data ); if (ret != (int)data->len || (UseFSync && (ret = fsync( fd )))) { if (ret < 0) sys_error( "Maildir error: cannot write %s", buf ); else error( "Maildir error: cannot write %s. Disk full?\n", buf ); close( fd ); cb( DRV_BOX_BAD, 0, aux ); return; } if (close( fd ) < 0) { /* Quota exceeded may cause this. */ sys_error( "Maildir error: cannot write %s", buf ); cb( DRV_BOX_BAD, 0, aux ); return; } if (data->date) { /* Set atime and mtime according to INTERNALDATE or mtime of source message */ struct utimbuf utimebuf; utimebuf.actime = utimebuf.modtime = data->date; if (utime( buf, &utimebuf ) < 0) { sys_error( "Maildir error: cannot set times for %s", buf ); cb( DRV_BOX_BAD, 0, aux ); return; } } /* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */ nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%s%s", box, subdirs[!(data->flags & F_SEEN)], base, fbuf ); if (rename( buf, nbuf )) { sys_error( "Maildir error: cannot rename %s to %s", buf, nbuf ); cb( DRV_BOX_BAD, 0, aux ); return; } if (DFlags & FAKEDUMBSTORE) uid = 0; cb( DRV_OK, uid, aux ); } static void maildir_find_new_msgs( store_t *gctx, uint newuid, void (*cb)( int sts, message_t *msgs, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; assert( DFlags & FAKEDUMBSTORE ); ctx->opts |= OPEN_FIND; ctx->finduid = newuid; int ret = maildir_rescan( ctx ); cb( ret, &(*ctx->app_msgs)->gen, aux ); } static void maildir_set_msg_flags( store_t *gctx, message_t *gmsg, uint uid ATTR_UNUSED, int add, int del, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_conf_t *conf = ctx->conf; maildir_message_t *msg = (maildir_message_t *)gmsg; char *s, *p; uint i; int j, ret, ol, fl, bbl, bl, tl; char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX]; bbl = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path ); memcpy( nbuf, ctx->path, (size_t)bbl - 1 ); memcpy( nbuf + bbl - 1, "/cur/", 5 ); for (;;) { bl = bbl + nfsnprintf( buf + bbl, _POSIX_PATH_MAX - bbl, "%s/", subdirs[gmsg->status & M_RECENT] ); ol = strlen( msg->base ); if (_POSIX_PATH_MAX - bl < ol + 3 + (int)as(MsgFlags)) oob(); memcpy( buf + bl, msg->base, (size_t)ol + 1 ); memcpy( nbuf + bl, msg->base, (size_t)ol + 1 ); if ((s = strstr( nbuf + bl, conf->info_prefix ))) { s += 3; fl = ol - (s - (nbuf + bl)); for (i = 0; i < as(MsgFlags); i++) { if ((p = strchr( s, MsgFlags[i] ))) { if (del & (1 << i)) { memmove( p, p + 1, (size_t)fl - (size_t)(p - s) ); fl--; } } else if (add & (1 << i)) { for (j = 0; j < fl && MsgFlags[i] > s[j]; j++); fl++; memmove( s + j + 1, s + j, (size_t)(fl - j) ); s[j] = MsgFlags[i]; } } tl = ol + 3 + fl; } else { tl = ol + maildir_make_flags( conf->info_delimiter, (uchar)add, nbuf + bl + ol ); } if (!rename( buf, nbuf )) break; if ((ret = maildir_again( ctx, msg, "Maildir error: cannot rename %s to %s", buf, nbuf )) != DRV_OK) { cb( ret, aux ); return; } } free( msg->base ); msg->base = nfstrndup( nbuf + bl, (size_t)tl ); msg->flags |= add; msg->flags &= ~del; gmsg->status &= ~M_RECENT; cb( DRV_OK, aux ); } #ifdef USE_DB static int maildir_purge_msg( maildir_store_t *ctx, const char *name ) { int ret; if ((ret = maildir_uidval_lock( ctx )) != DRV_OK) return ret; make_key( ctx->conf->info_stop, &key, name ); if ((ret = ctx->db->del( ctx->db, NULL, &key, 0 ))) { ctx->db->err( ctx->db, ret, "Maildir error: db->del()" ); return DRV_BOX_BAD; } return DRV_OK; } #endif /* USE_DB */ static void maildir_trash_msg( store_t *gctx, message_t *gmsg, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg = (maildir_message_t *)gmsg; char *s; int ret; struct stat st; char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX]; for (;;) { nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->path, subdirs[gmsg->status & M_RECENT], msg->base ); s = strstr( msg->base, ctx->conf->info_prefix ); nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%lld.%d_%d.%s%s", ctx->trash, subdirs[gmsg->status & M_RECENT], (long long)time( NULL ), Pid, ++MaildirCount, Hostname, s ? s : "" ); if (!rename( buf, nbuf )) break; if (!stat( buf, &st )) { if ((ret = maildir_validate( ctx->trash, 1, ctx )) != DRV_OK) { cb( ret, aux ); return; } if (!rename( buf, nbuf )) break; if (errno != ENOENT) { sys_error( "Maildir error: cannot move %s to %s", buf, nbuf ); cb( DRV_BOX_BAD, aux ); return; } } if ((ret = maildir_again( ctx, msg, "Maildir error: cannot move %s to %s", buf, nbuf )) != DRV_OK) { cb( ret, aux ); return; } } gmsg->status |= M_DEAD; ctx->total_msgs--; ctx->expunge_callback( gmsg, ctx->callback_aux ); #ifdef USE_DB if (ctx->usedb) { cb( maildir_purge_msg( ctx, msg->base ), aux ); return; } #endif /* USE_DB */ cb( DRV_OK, aux ); } static void maildir_close_box( store_t *gctx, void (*cb)( int sts, int reported, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg; int basel, retry, ret; char buf[_POSIX_PATH_MAX]; for (;;) { retry = 0; basel = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path ); for (msg = ctx->msgs; msg; msg = msg->next) { if (!(msg->status & M_DEAD) && (msg->status & M_EXPUNGE)) { nfsnprintf( buf + basel, _POSIX_PATH_MAX - basel, "%s/%s", subdirs[msg->status & M_RECENT], msg->base ); if (unlink( buf )) { if (errno == ENOENT) retry = 1; else sys_error( "Maildir error: cannot remove %s", buf ); } else { msg->status |= M_DEAD; ctx->total_msgs--; ctx->expunge_callback( &msg->gen, ctx->callback_aux ); #ifdef USE_DB if (ctx->db && (ret = maildir_purge_msg( ctx, msg->base )) != DRV_OK) { cb( ret, 1, aux ); return; } #endif /* USE_DB */ } } } if (!retry) { cb( DRV_OK, 1, aux ); return; } if ((ret = maildir_rescan( ctx )) != DRV_OK) { cb( ret, 1, aux ); return; } } } static void maildir_cancel_cmds( store_t *gctx ATTR_UNUSED, void (*cb)( void *aux ), void *aux ) { cb( aux ); } static uint maildir_get_memory_usage( store_t *gctx ATTR_UNUSED ) { return 0; } static int maildir_get_fail_state( store_conf_t *gconf ) { return ((maildir_store_conf_t *)gconf)->failed; } static int maildir_parse_store( conffile_t *cfg, store_conf_t **storep ) { maildir_store_conf_t *store; if (strcasecmp( "MaildirStore", cfg->cmd )) return 0; store = nfzalloc( sizeof(*store) ); store->info_delimiter = FieldDelimiter; store->driver = &maildir_driver; store->name = nfstrdup( cfg->val ); while (getcline( cfg ) && cfg->cmd) { if (!strcasecmp( "Inbox", cfg->cmd )) { store->inbox = expand_strdup( cfg->val, cfg ); } else if (!strcasecmp( "Path", cfg->cmd )) { store->path = expand_strdup( cfg->val, cfg ); } else if (!strcasecmp( "AltMap", cfg->cmd )) { #ifdef USE_DB store->alt_map = parse_bool( cfg ); #else if (parse_bool( cfg )) { error( "Error: AltMap=true is not supported by this build.\n" ); cfg->err = 1; } #endif /* USE_DB */ } else if (!strcasecmp( "InfoDelimiter", cfg->cmd )) { if (strlen( cfg->val ) != 1) { error( "%s:%d: Info delimiter must be exactly one character long\n", cfg->file, cfg->line ); cfg->err = 1; continue; } store->info_delimiter = cfg->val[0]; if (!ispunct( store->info_delimiter )) { error( "%s:%d: Info delimiter must be a punctuation character\n", cfg->file, cfg->line ); cfg->err = 1; continue; } } else if (!strcasecmp( "SubFolders", cfg->cmd )) { if (!strcasecmp( "Verbatim", cfg->val )) { store->sub_style = SUB_VERBATIM; } else if (!strcasecmp( "Maildir++", cfg->val )) { store->sub_style = SUB_MAILDIRPP; } else if (!strcasecmp( "Legacy", cfg->val )) { store->sub_style = SUB_LEGACY; } else { error( "%s:%d: Unrecognized SubFolders style\n", cfg->file, cfg->line ); cfg->err = 1; } } else { parse_generic_store( &store->gen, cfg, "MaildirStore" ); } } if (!store->inbox) store->inbox = expand_strdup( "~/Maildir", NULL ); if (store->path) { if (store->sub_style == SUB_MAILDIRPP) { error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name ); cfg->err = 1; } else { uint inboxLen = strlen( store->inbox ); if (starts_with( store->path, -1, store->inbox, inboxLen ) && store->path[inboxLen] == '/') { error( "Maildir store '%s': Path cannot be nested under Inbox\n", store->name ); cfg->err = 1; } else { char *s = strrchr( store->path, '/' ); assert( s ); // due to expand_strdup() store->path_sfx = s + 1; *s = 0; } } } nfasprintf( &store->info_prefix, "%c2,", store->info_delimiter ); nfasprintf( &store->info_stop, "%c,", store->info_delimiter ); *storep = &store->gen; return 1; } static uint maildir_get_caps( store_t *gctx ATTR_UNUSED ) { return 0; /* XXX DRV_CRLF? */ } struct driver maildir_driver = { maildir_get_caps, maildir_parse_store, maildir_cleanup_drv, maildir_alloc_store, maildir_set_callbacks, maildir_connect_store, maildir_free_store, maildir_free_store, /* _cancel_, but it's the same */ maildir_list_store, maildir_select_box, maildir_get_box_path, maildir_create_box, maildir_open_box, maildir_get_uidnext, maildir_get_supported_flags, maildir_confirm_box_empty, maildir_delete_box, maildir_finish_delete_box, maildir_prepare_load_box, maildir_load_box, maildir_fetch_msg, maildir_store_msg, maildir_find_new_msgs, maildir_set_msg_flags, maildir_trash_msg, maildir_close_box, maildir_cancel_cmds, maildir_get_memory_usage, maildir_get_fail_state, }; isync-1.5.1/src/imap_msgs.c0000644000175000001440000000616514405565305011251 // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #include "imap_p.h" #ifdef DEBUG_IMAP_MSGS # define dbg(...) print(__VA_ARGS__) #else # define dbg(...) do { } while (0) #endif imap_message_t * imap_new_msg( imap_messages_t *msgs ) { imap_message_t *msg = nfzalloc( sizeof(*msg) ); *msgs->tail = msg; msgs->tail = &msg->next; msgs->count++; return msg; } void reset_imap_messages( imap_messages_t *msgs ) { free_generic_messages( &msgs->head->gen ); msgs->head = NULL; msgs->tail = &msgs->head; msgs->count = 0; msgs->cursor_ptr = NULL; msgs->cursor_seq = 0; } static int imap_compare_msgs( const void *a_, const void *b_ ) { const imap_message_t *a = *(const imap_message_t * const *)a_; const imap_message_t *b = *(const imap_message_t * const *)b_; if (a->uid < b->uid) return -1; if (a->uid > b->uid) return 1; return 0; } void imap_ensure_relative( imap_messages_t *msgs ) { if (msgs->cursor_ptr) return; uint count = msgs->count; if (!count) return; if (count > 1) { imap_message_t **t = nfmalloc( sizeof(*t) * count ); imap_message_t *m = msgs->head; for (uint i = 0; i < count; i++) { t[i] = m; m = m->next; } qsort( t, count, sizeof(*t), imap_compare_msgs ); imap_message_t *nm = t[0]; msgs->head = nm; nm->prev = NULL; uint seq, nseq = nm->seq; for (uint j = 0; m = nm, seq = nseq, j < count - 1; j++) { nm = t[j + 1]; m->next = nm; m->next->prev = m; nseq = nm->seq; nm->seq = nseq - seq; } msgs->tail = &m->next; *msgs->tail = NULL; free( t ); } msgs->cursor_ptr = msgs->head; msgs->cursor_seq = msgs->head->seq; } void imap_ensure_absolute( imap_messages_t *msgs ) { if (!msgs->cursor_ptr) return; uint seq = 0; for (imap_message_t *msg = msgs->head; msg; msg = msg->next) { seq += msg->seq; msg->seq = seq; } msgs->cursor_ptr = NULL; msgs->cursor_seq = 0; } imap_message_t * imap_expunge_msg( imap_messages_t *msgs, uint fseq ) { dbg( "expunge %u\n", fseq ); imap_ensure_relative( msgs ); imap_message_t *ret = NULL, *msg = msgs->cursor_ptr; if (msg) { uint seq = msgs->cursor_seq; for (;;) { dbg( " now on message %u (uid %u), %sdead\n", seq, msg->uid, (msg->status & M_DEAD) ? "" : "not " ); if (seq == fseq && !(msg->status & M_DEAD)) { dbg( " => expunging\n" ); msg->status = M_DEAD; ret = msg; break; } if (seq < fseq) { dbg( " is below\n" ); if (!msg->next) { dbg( " no next\n" ); goto done; } msg = msg->next; seq += msg->seq; } else { dbg( " is not below\n" ); if (!msg->prev) { dbg( " no prev\n" ); break; } uint pseq = seq - msg->seq; if (pseq < fseq) { dbg( " prev too low\n" ); break; } seq = pseq; msg = msg->prev; } } dbg( " => lowering\n" ); assert( msg->seq ); msg->seq--; seq--; done: dbg( " saving cursor on %u (uid %u)\n", seq, msg->uid ); msgs->cursor_ptr = msg; msgs->cursor_seq = seq; } else { dbg( " => no messages\n" ); } return ret; } isync-1.5.1/src/imap_utf7.c0000644000175000001440000001553314405565305011164 // SPDX-FileCopyrightText: 2018-2021 Georgy Kibardin // SPDX-FileCopyrightText: 2022 Oswald Buddenhagen // SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception // // mbsync - mailbox synchronizer // #include "imap_p.h" #ifdef DEBUG_IMAP_UTF7 # define dbg(...) print(__VA_ARGS__) #else # define dbg(...) do { } while (0) #endif struct bit_fifo { unsigned long long value; uint bits; }; static void add_bits( struct bit_fifo *fifo, uint bits, uint size ) { fifo->value = (fifo->value << size) | bits; fifo->bits += size; assert( fifo->bits <= sizeof(fifo->value) * 8 ); } static uint eat_bits( struct bit_fifo *fifo, uint size ) { fifo->bits -= size; return (fifo->value >> fifo->bits) & ((1LL << size) - 1); } static uint peek_bits( struct bit_fifo *fifo, uint size ) { return (fifo->value >> (fifo->bits - size)) & ((1LL << size) - 1); } static void add_char( char **p, uint chr ) { *((*p)++) = (char)chr; } static uchar eat_char( const char **p ) { return (uchar)*((*p)++); } static uint read_as_utf8( const char **utf8_buf_p ) { uchar chr = eat_char( utf8_buf_p ); if (chr < 0x80) return chr; if ((chr & 0xf8) == 0xf0) { uchar chr2 = eat_char( utf8_buf_p ); if ((chr2 & 0xc0) != 0x80) return ~0; uchar chr3 = eat_char( utf8_buf_p ); if ((chr3 & 0xc0) != 0x80) return ~0; uchar chr4 = eat_char( utf8_buf_p ); if ((chr4 & 0xc0) != 0x80) return ~0; return ((chr & 0x7) << 18) | ((chr2 & 0x3f) << 12) | ((chr3 & 0x3f) << 6) | (chr4 & 0x3f); } if ((chr & 0xf0) == 0xe0) { uchar chr2 = eat_char( utf8_buf_p ); if ((chr2 & 0xc0) != 0x80) return ~0; uchar chr3 = eat_char( utf8_buf_p ); if ((chr3 & 0xc0) != 0x80) return ~0; return ((chr & 0xf) << 12) | ((chr2 & 0x3f) << 6) | (chr3 & 0x3f); } if ((chr & 0xe0) == 0xc0) { uchar chr2 = eat_char( utf8_buf_p ); if ((chr2 & 0xc0) != 0x80) return ~0; return (chr & 0x1f) << 6 | (chr2 & 0x3f); } return ~0; } static int needs_encoding( uint chr ) { return chr && (chr <= 0x1f || chr >= 0x7f); } static uint utf16_encode( uint chr ) { chr -= 0x10000; return (((chr >> 10) + 0xd800) << 16) | ((chr & 0x3ff) + 0xdc00); } static uchar b64_encode( uint chr ) { assert( chr <= 0x3f ); return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"[chr]; } char * imap_utf8_to_utf7( const char *buf ) { // Size requirements: // - pass-through: l, 1 => 1 // - all "&": l * 2, 1 => 2 // - 7-bit: (l * 2 * 4 + 2) / 3 + 2, ~ l * 2.7, 1 => 5 // - 3-octet: (l / 3 * 2 * 4 + 2) / 3 + 2, ~ l * 0.9, 3 => 5 // - 4-octet: (l / 4 * 2 * 2 * 4 + 2) / 3 + 2, ~ l * 1.3, 4 => 8 // => worst case: "&" and 7-bit alternating: l * 3.5, 2 => 7 int outsz = strlen( buf ) * 7 / 2 + 3; char *result = nfmalloc( outsz ); char *outp = result; struct bit_fifo fifo = { 0, 0 }; int encoding = 0; uint chr; do { chr = read_as_utf8( &buf ); if (chr == ~0U) { dbg( "Error: invalid UTF-8 string\n" ); free( result ); return NULL; } if (needs_encoding( chr )) { if (!encoding) { add_char( &outp, '&' ); encoding = 1; } if (chr <= 0xffff) add_bits( &fifo, chr, 16 ); else add_bits( &fifo, utf16_encode( chr ), 32 ); while (fifo.bits >= 6) add_char( &outp, b64_encode( eat_bits( &fifo, 6 ) ) ); } else { if (encoding) { if (fifo.bits) { uint trailing_bits = 6 - fifo.bits; uchar trail = b64_encode( eat_bits( &fifo, fifo.bits ) << trailing_bits ); add_char( &outp, trail ); } add_char( &outp, '-' ); encoding = 0; } add_char( &outp, chr ); if (chr == '&') add_char( &outp, '-' ); } } while (chr); assert( (int)(outp - result) <= outsz ); return result; } static void write_as_utf8( char **outp, uint chr ) { if (chr <= 0x7f) { add_char( outp, chr ); } else if (chr <= 0x7ff) { add_char( outp, (chr >> 6) | 0xc0 ); add_char( outp, (chr & 0x3f) | 0x80 ); } else if (chr <= 0xffff) { add_char( outp, (chr >> 12) | 0xe0 ); add_char( outp, ((chr >> 6) & 0x3f) | 0x80 ); add_char( outp, (chr & 0x3f) | 0x80 ); } else { assert( chr <= 0xfffff ); add_char( outp, (chr >> 18) | 0xf0 ); add_char( outp, ((chr >> 12) & 0x3f) | 0x80 ); add_char( outp, ((chr >> 6) & 0x3f) | 0x80 ); add_char( outp, (chr & 0x3f) | 0x80 ); } } static int need_another_16bit( uint bits ) { return (bits & 0xfc00) == 0xd800; } static uint utf16_decode( uint subject ) { return 0x10000 + (((subject >> 16) - 0xd800) << 10) + ((subject & 0xffff) - 0xdc00); } static uint b64_decode( uchar chr ) { static uint lu[128] = { ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 62, 63, ~0, ~0, ~0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 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, ~0, ~0, ~0, ~0, ~0, ~0, 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, ~0, ~0, ~0, ~0, ~0, }; return lu[chr]; } int imap_utf7_to_utf8( const char *buf, int bufl, char *outbuf ) { // Size requirements: // - pass-through: l (shortest worst case) // - all "&": l / 2, 2 => 1, * .5 // - 7-bit: ((l - 2) * 3 + 1) / 4 / 2, ~ l * .38, 5 => 1, * .2 // - 3-octet: ((l - 2) * 3 + 1) / 4 / 2 * 3, ~ l * 1.13, 5 => 3, * .6 (generic worst case) // - 4-octet: ((l - 2) * 3 + 1) / 4 / 2 / 2 * 4, ~ l * .75, 8 => 4, * .5 // => reserve bufl * 9 / 8 char *outp = outbuf; struct bit_fifo fifo = { 0, 0 }; const char *bufe = buf + bufl; while (buf != bufe) { uchar chr = *buf++; if (chr != '&') { if (chr & 0x80) { dbg( "Error: 8-bit char %x\n", chr ); return -1; } add_char( &outp, chr ); continue; } if (buf == bufe) { dbg( "Error: unterminated shift sequence\n" ); return -1; } chr = *buf++; if (chr == '-') { add_char( &outp, '&' ); continue; } fifo.bits = 0; do { if (chr & 0x80) { dbg( "Error: 8-bit char %x\n", chr ); return -1; } uint bits = b64_decode( chr ); if (bits == ~0U) { dbg( "Error: char %x outside alphabet\n", chr ); return -1; } add_bits( &fifo, bits, 6 ); if (fifo.bits >= 16) { if (need_another_16bit( peek_bits( &fifo, 16 ) )) { if (fifo.bits >= 32) { uint utf16 = eat_bits( &fifo, 32 ); if ((utf16 & 0xfc00) != 0xdc00) { dbg( "Error: unpaired UTF-16 surrogate\n" ); return -1; } write_as_utf8( &outp, utf16_decode( utf16 ) ); } } else { write_as_utf8( &outp, eat_bits( &fifo, 16 ) ); } } if (buf == bufe) { dbg( "Error: unterminated shift sequence\n" ); return -1; } chr = *buf++; } while (chr != '-'); if (fifo.bits > 6) { dbg( "Error: incomplete code point\n" ); return -1; } } return (int)(outp - outbuf); } isync-1.5.1/LICENSES/0000755000175000001440000000000014652510342007607 5isync-1.5.1/LICENSES/LicenseRef-isync-GPL-exception.txt0000644000175000001440000000111314652510342016042 SPDX-Exception-Identifier: LicenseRef-isync-GPL-exception SPDX-Licenses: GPL-2.0-or-later Usage-Guide: This exception is used together with the above SPDX-License to allow linking the compiled version of code to non GPL compliant code. To use this exception add it with the keyword WITH to one of the identifiers in the SPDX-Licenses tag: SPDX-License-Identifier: WITH LicenseRef-isync-GPL-exception License-Text: As a special exception, mbsync may be linked with libraries with a more restrictive license if the only incompatibility are advertising clauses. isync-1.5.1/LICENSES/GPL-2.0-or-later.txt0000644000175000001440000004445114405565305012747 Valid-License-Identifier: GPL-2.0 Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 or SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ or SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. isync-1.5.1/INSTALL0000644000175000001440000003777114764113020007366 Installation Instructions ************************* Basic Installation ================== The following shell commands: test -f configure || ./bootstrap ./configure make make install should configure, build, and install this package. The first line, which bootstraps, is intended for developers; when building from distribution tarballs it does nothing and can be skipped. The following more-detailed instructions are generic; see the ‘README’ file for instructions specific to this package. Some packages provide this ‘INSTALL’ file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in the GNU Coding Standards. Many packages have scripts meant for developers instead of ordinary builders, as they may use developer tools that are less commonly installed, or they may access the network, which has privacy implications. If the ‘bootstrap’ shell script exists, it attempts to build the ‘configure’ shell script and related files, possibly using developer tools or the network. Because the output of ‘bootstrap’ is system-independent, it is normally run by a package developer so that its output can be put into the distribution tarball and ordinary builders and users need not run ‘bootstrap’. Some packages have commands like ‘./autopull.sh’ and ‘./autogen.sh’ that you can run instead of ‘./bootstrap’, for more fine-grained control over bootstrapping. The ‘configure’ shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a ‘Makefile’ in each directory of the package. It may also create one or more ‘.h’ files containing system-dependent definitions. Finally, it creates a shell script ‘config.status’ that you can run in the future to recreate the current configuration, and a file ‘config.log’ containing output useful for debugging ‘configure’. It can also use an optional file (typically called ‘config.cache’ and enabled with ‘--cache-file=config.cache’ or simply ‘-C’) that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how ‘configure’ could check whether to do them, and mail diffs or instructions to the address given in the ‘README’ so they can be considered for the next release. If you are using the cache, and at some point ‘config.cache’ contains results you don’t want to keep, you may remove or edit it. The ‘autoconf’ program generates ‘configure’ from the file ‘configure.ac’. Normally you should edit ‘configure.ac’ instead of editing ‘configure’ directly. The simplest way to compile this package is: 1. ‘cd’ to the directory containing the package’s source code. 2. If this is a developer checkout and file ‘configure’ does not yet exist, type ‘./bootstrap’ to create it. You may need special developer tools and network access to bootstrap, and the network access may have privacy implications. 3. Type ‘./configure’ to configure the package for your system. This might take a while. While running, ‘configure’ prints messages telling which features it is checking for. 4. Type ‘make’ to compile the package. 5. Optionally, type ‘make check’ to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 6. Type ‘make install’ to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the ‘make install’ phase executed with root privileges. 7. Optionally, type ‘make installcheck’ to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior ‘make install’ required root privileges, verifies that the installation completed correctly. 8. You can remove the program binaries and object files from the source code directory by typing ‘make clean’. To also remove the files that ‘configure’ created (so you can compile the package for a different kind of computer), type ‘make distclean’. There is also a ‘make maintainer-clean’ target, but that is intended mainly for the package’s developers. If you use it, you may have to bootstrap again. 9. If the package follows the GNU Coding Standards, you can type ‘make uninstall’ to remove the installed files. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the ‘configure’ script does not know about. Run ‘./configure --help’ for details on some of the pertinent environment variables. You can give ‘configure’ initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=gcc CFLAGS=-g LIBS=-lposix See “Defining Variables” for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each system in their own directory. To do this, you can use GNU ‘make’. ‘cd’ to the directory where you want the object files and executables to go and run the ‘configure’ script. ‘configure’ automatically checks for the source code in the directory that ‘configure’ is in and in ‘..’. This is known as a “VPATH” build. With a non-GNU ‘make’, it is safer to compile the package for one system at a time in the source code directory. After you have installed the package for one system, use ‘make distclean’ before reconfiguring for another system. Some platforms, notably macOS, support “fat” or “universal” binaries, where a single binary can execute on different architectures. On these platforms you can configure and compile just once, with options specific to that platform. Installation Names ================== By default, ‘make install’ installs the package’s commands under ‘/usr/local/bin’, include files under ‘/usr/local/include’, etc. You can specify an installation prefix other than ‘/usr/local’ by giving ‘configure’ the option ‘--prefix=PREFIX’, where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option ‘--exec-prefix=PREFIX’ to ‘configure’, the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like ‘--bindir=DIR’ to specify different values for particular kinds of files. Run ‘configure --help’ for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of ‘${prefix}’, so that specifying just ‘--prefix’ will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to ‘configure’; however, many packages provide one or both of the following shortcuts of passing variable assignments to the ‘make install’ command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, ‘make install prefix=/alternate/directory’ will choose an alternate location for all directory configuration variables that were expressed in terms of ‘${prefix}’. Any directories that were specified during ‘configure’, but not in terms of ‘${prefix}’, must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the ‘DESTDIR’ variable. For example, ‘make install DESTDIR=/alternate/directory’ will prepend ‘/alternate/directory’ before all installation names. The approach of ‘DESTDIR’ overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of ‘${prefix}’ at ‘configure’ time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving ‘configure’ the option ‘--program-prefix=PREFIX’ or ‘--program-suffix=SUFFIX’. Some packages pay attention to ‘--enable-FEATURE’ and ‘--disable-FEATURE’ options to ‘configure’, where FEATURE indicates an optional part of the package. They may also pay attention to ‘--with-PACKAGE’ and ‘--without-PACKAGE’ options, where PACKAGE is something like ‘gnu-ld’. ‘./configure --help’ should mention the ‘--enable-...’ and ‘--with-...’ options that the package recognizes. Some packages offer the ability to configure how verbose the execution of ‘make’ will be. For these packages, running ‘./configure --enable-silent-rules’ sets the default to minimal output, which can be overridden with ‘make V=1’; while running ‘./configure --disable-silent-rules’ sets the default to verbose, which can be overridden with ‘make V=0’. Specifying a System Type ======================== By default ‘configure’ builds for the current system. To create binaries that can run on a different system type, specify a ‘--host=TYPE’ option along with compiler variables that specify how to generate object code for TYPE. For example, to create binaries intended to run on a 64-bit ARM processor: ./configure --host=aarch64-linux-gnu \ CC=aarch64-linux-gnu-gcc \ CXX=aarch64-linux-gnu-g++ If done on a machine that can execute these binaries (e.g., via ‘qemu-aarch64’, ‘$QEMU_LD_PREFIX’, and Linux’s ‘binfmt_misc’ capability), the build behaves like a native build. Otherwise it is a cross-build: ‘configure’ will make cross-compilation guesses instead of running test programs, and ‘make check’ will not work. A system type can either be a short name like ‘mingw64’, or a canonical name like ‘x86_64-pc-linux-gnu’. Canonical names have the form CPU-COMPANY-SYSTEM where SYSTEM is either OS or KERNEL-OS. To canonicalize and validate a system type, you can run the command ‘config.sub’, which is often squirreled away in a subdirectory like ‘build-aux’. For example: $ build-aux/config.sub arm64-linux aarch64-unknown-linux-gnu $ build-aux/config.sub riscv-lnx Invalid configuration 'riscv-lnx': OS 'lnx' not recognized You can look at the ‘config.sub’ file to see which types are recognized. If the file is absent, this package does not need the system type. If ‘configure’ fails with the diagnostic “cannot guess build type”. ‘config.sub’ did not recognize your system’s type. In this case, first fetch the newest versions of these files from the GNU config package (https://savannah.gnu.org/projects/config). If that fixes things, please report it to the maintainers of the package containing ‘configure’. Otherwise, you can try the configure option ‘--build=TYPE’ where TYPE comes close to your system type; also, please report the problem to . For more details about configuring system types, see the Autoconf documentation. Sharing Defaults ================ If you want to set default values for ‘configure’ scripts to share, you can create a site shell script called ‘config.site’ that gives default values for variables like ‘CC’, ‘cache_file’, and ‘prefix’. ‘configure’ looks for ‘PREFIX/share/config.site’ if it exists, then ‘PREFIX/etc/config.site’ if it exists. Or, you can set the ‘CONFIG_SITE’ environment variable to the location of the site script. A warning: not all ‘configure’ scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to ‘configure’. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the ‘configure’ command line, using ‘VAR=value’. For example: ./configure CC=/usr/local2/bin/gcc causes the specified ‘gcc’ to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for ‘CONFIG_SHELL’ due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash ‘configure’ Invocation ====================== ‘configure’ recognizes the following options to control how it operates. ‘--help’ ‘-h’ Print a summary of all of the options to ‘configure’, and exit. ‘--help=short’ ‘--help=recursive’ Print a summary of the options unique to this package’s ‘configure’, and exit. The ‘short’ variant lists options used only in the top level, while the ‘recursive’ variant lists options also present in any nested packages. ‘--version’ ‘-V’ Print the version of Autoconf used to generate the ‘configure’ script, and exit. ‘--cache-file=FILE’ Enable the cache: use and save the results of the tests in FILE, traditionally ‘config.cache’. FILE defaults to ‘/dev/null’ to disable caching. ‘--config-cache’ ‘-C’ Alias for ‘--cache-file=config.cache’. ‘--srcdir=DIR’ Look for the package’s source code in directory DIR. Usually ‘configure’ can determine that directory automatically. ‘--prefix=DIR’ Use DIR as the installation prefix. See “Installation Names” for more details, including other options available for fine-tuning the installation locations. ‘--host=TYPE’ Build binaries for system TYPE. See “Specifying a System Type”. ‘--enable-FEATURE’ ‘--disable-FEATURE’ Enable or disable the optional FEATURE. See “Optional Features”. ‘--with-PACKAGE’ ‘--without-PACKAGE’ Use or omit PACKAGE when building. See “Optional Features”. ‘--quiet’ ‘--silent’ ‘-q’ Do not print messages saying which checks are being made. To suppress all normal output, redirect it to ‘/dev/null’ (any error messages will still be shown). ‘--no-create’ ‘-n’ Run the configure checks, but stop before creating any output files. ‘configure’ also recognizes several environment variables, and accepts some other, less widely useful, options. Run ‘configure --help’ for more details. Copyright notice ================ Copyright © 1994–1996, 1999–2002, 2004–2017, 2020–2024 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. isync-1.5.1/README0000644000175000001440000000470413701322432007202 _ (_)___ _ _ _ __ ___ | / __| | | | '_ \ / __| | \__ \ |_| | | | | (__ |_|___/\__, |_| |_|\___| |___/ isync/mbsync - free (GPL) mailbox synchronization program http://isync.sf.net/ See AUTHORS for contact information. ``mbsync'' is a command line application which synchronizes mailboxes; currently Maildir and IMAP4 mailboxes are supported. New messages, message deletions and flag changes can be propagated both ways. ``mbsync'' is suitable for use in IMAP-disconnected mode. Synchronization is based on unique message identifiers (UIDs), so no identification conflicts can occur (unlike with some other mail synchronizers). Synchronization state is kept in one local text file per mailbox pair; these files are protected against concurrent ``mbsync'' processes. Mailboxes can be safely modified while ``mbsync'' operates. Multiple replicas of each mailbox can be maintained. isync is the project name, while mbsync is the current executable name; this change was necessary because of massive changes in the user interface. * Features * Fine-grained selection of synchronization operations to perform * Synchronizes single mailboxes or entire mailbox collections * Partial mirrors possible: keep only the latest messages locally * Trash functionality: backup messages before removing them * IMAP features: * Supports TLS/SSL via imaps: (port 993) and STARTTLS * Supports SASL for authentication * Pipelining for maximum speed * Compatibility isync should work fairly well with any IMAP4 compliant server; servers that support the UIDPLUS and LITERAL+ extensions are most efficient. Courier 1.4.3 is known to be buggy, version 1.7.3 works fine. M$ Exchange (2013 at least) needs DisableExtension MOVE to be compatible with the Trash functionality. * Platforms At some point, ``isync'' has successfully run on: Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3. * Requirements perl v5.14+ Berkeley DB 4.1+ (optional) OpenSSL for TLS/SSL support (optional) Cyrus SASL (optional) zlib (optional) The build from git also requires: GNU autotools (autoconf & automake) perl module Date::Parse (libtimedate-perl on Debian, perl-TimeDate on Fedora and Suse) * Installation ./autogen.sh (only when building from git) ./configure make sudo make install * Help Please see the man page for complete documentation. isync-1.5.1/acinclude.m40000644000175000001440000000275212452527622010526 # Add --enable-maintainer-mode option to configure. # From Jim Meyering # Change it to enable maintainer mode by default by Nicolas Boullis. # Copyright 1996, 1998, 2000, 2001, 2002 Free Software Foundation, Inc. # Copyright 2004 Nicolas Boullis. # 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, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA AC_DEFUN([AM_MAINTAINER_MODE], [AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode is enabled by default AC_ARG_ENABLE(maintainer-mode, [ --disable-maintainer-mode disable make rules and dependencies not useful (and sometimes confusing) to the casual installer], USE_MAINTAINER_MODE=$enableval, USE_MAINTAINER_MODE=yes) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST(MAINT)dnl ] ) isync-1.5.1/COPYING0000644000175000001440000004445114405565305007372 Valid-License-Identifier: GPL-2.0 Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 or SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ or SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. isync-1.5.1/Makefile.in0000644000175000001440000007401414764113020010371 # Makefile.in generated by automake 1.17 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2024 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@ # SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen # SPDX-License-Identifier: GPL-2.0-or-later 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)) am__rm_f = rm -f $(am__rm_f_notfound) am__rm_rf = rm -rf $(am__rm_f_notfound) 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@ target_triplet = @target@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 $(top_srcdir)/VERSION \ $(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) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = autodefs.h CONFIG_CLEAN_FILES = isync.spec 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 ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \ } am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(docdir)" SCRIPTS = $(bin_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 = 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 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 \ cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ autodefs.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)` DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/autodefs.h.in \ $(srcdir)/isync.spec.in AUTHORS COPYING ChangeLog INSTALL NEWS \ README TODO compile config.guess config.sub depcomp install-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 -700 -exec chmod u+rwx {} ';' \ ; 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 = -9 DIST_TARGETS = dist-gzip # Exists only to be overridden by the user if desired. AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = \ find . \( -type f -a \! \ \( -name .nfs* -o -name .smb* -o -name .__afs* \) \) -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DB_LIBS = @DB_LIBS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KEYCHAIN_LIBS = @KEYCHAIN_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RELEASE_DATE = @RELEASE_DATE@ SASL_LIBS = @SASL_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOCK_LIBS = @SOCK_LIBS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VERSION = @VERSION@ Z_LIBS = @Z_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__rm_f_notfound = @am__rm_f_notfound@ am__tar = @am__tar@ am__untar = @am__untar@ am__xargs_n = @am__xargs_n@ 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@ 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@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = src bin_SCRIPTS = mbsync-get-cert EXTRA_DIST = LICENSES VERSION debian isync.spec $(bin_SCRIPTS) doc_DATA = README TODO NEWS ChangeLog AUTHORS all: autodefs.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) --gnu'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu 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__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ 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): autodefs.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/autodefs.h.in $(top_builddir)/config.status $(AM_V_at)rm -f stamp-h1 $(AM_V_GEN)cd $(top_builddir) && $(SHELL) ./config.status autodefs.h $(srcdir)/autodefs.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(AM_V_GEN)($(am__cd) $(top_srcdir) && $(AUTOHEADER)) $(AM_V_at)rm -f stamp-h1 $(AM_V_at)touch $@ distclean-hdr: -rm -f autodefs.h stamp-h1 isync.spec: $(top_builddir)/config.status $(srcdir)/isync.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(bin_SCRIPTS)'; 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 \ 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)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) 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" 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: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) $(AM_V_at)$(MKDIR_P) "$(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 $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -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-zstd: distdir tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst $(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 -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 -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ *.tar.zst*) \ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ 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-$$$$/" \ && 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) $(AM_DISTCHECK_DVI_TARGET) \ && $(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 $(SCRIPTS) $(DATA) autodefs.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)" "$(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: -$(am__rm_f) $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || $(am__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 mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr 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-binSCRIPTS 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 pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binSCRIPTS uninstall-docDATA .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-hook dist-lzip dist-shar dist-tarZ dist-xz \ dist-zip dist-zstd distcheck distclean distclean-generic \ distclean-hdr distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binSCRIPTS 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 pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binSCRIPTS uninstall-docDATA .PRECIOUS: Makefile cov-scan: clean /opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE) tar cavf isync-cov.tar.xz cov-int deb: CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign dist-hook: find $(distdir)/debian \( -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf -cd $(distdir)/debian && test -f .gitignore && rm -rf `cut -c2- .gitignore` .gitignore dist-sign: dist gpg -b -a $(PACKAGE)-$(VERSION).tar.gz rpm: dist CFLAGS="-O2 -mtune=core2" rpmbuild --clean -ta $(PACKAGE)-$(VERSION).tar.gz rpm-ia32: dist CFLAGS="-O2 -m32 -march=i686" rpmbuild --target i686-unknown-linux --clean -ta $(PACKAGE)-$(VERSION).tar.gz # 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: # Tell GNU make to disable its built-in pattern rules. %:: %,v %:: RCS/%,v %:: RCS/% %:: s.% %:: SCCS/s.% isync-1.5.1/AUTHORS0000644000175000001440000000553314764104220007376 Contact ======= Send questions and bug reports to the isync-devel@lists.sourceforge.net mailing list. Do _NOT_ report bugs to Michael, not even in a CC: - he is not actively involved in isync development any more. Lead Developers =============== Oswald Buddenhagen - Current maintainer Michael Elkins - Original author Contributors ============ (Some of these people also contributed bugfixes and optimizations.) (In chronological order.) Jeremy Katz - UseNamespace & UseSSL* options Noa (ex. Daniel) Resare - Numerous SSL handling improvements Eivind Eklund - MaxMessages option Theodore Ts'o - get-cert script - Maildir UID mapping improvements - Initial version of partial async IMAP support Marc Hoersken - CopyArrivalDate option Jack Stone Jan Synacek - SASL support Jesse Weaver - IMAP stream compression support Anton Khirnov - ClientKey & ClientCertificate options Michael J Gruber - Support for the $Forwarded/Passed flag Patrick Steinhardt - UserCmd option Oliver Runge - UseKeychain option Georgy Kibardin - Support for UTF-7 IMAP mailbox names Honorary Contributors ===================== (These people contributed patches that were too small or obvious to claim copyright, or were rewritten from scratch.) (In alphabetical order.) Alessandro Ghedini Andreas Grapentin Aurélien Francillon Behnam Lal Ben Kibbey Caspar Schutijser Cedric Ware Dmitrij D. Czarkoff Dmitry Torokhov Felipe Contreras Felix Janda Gergely Risko Sung Pae "guns" Helmut Grohne Hugo Haas Husain Alshehhi Jaroslav Suchanek Jeremie Courreges-Anglas Klemens Nanni Lorenzo Martignoni Ludovico Gerardi Magnus Jonsson Marcin Niestroj Martin Stenberg Mike Delaney Nicolas Boullis Nihal Jere Paymon MARANDI Reimar Döffinger Remko Tronçon sbfnk@users.sf.net Thomas Roessler Todd T. Fries Vincent Bernat Yuri D'Elia isync-1.5.1/missing0000755000175000001440000001706014764113020007721 #! /bin/sh # Common wrapper for a few potentially missing GNU and other programs. scriptversion=2024-06-07.14; # UTC # shellcheck disable=SC2006,SC2268 # we must support pre-POSIX shells # Copyright (C) 1996-2024 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 autogen autoheader autom4te automake autoreconf bison flex help2man lex makeinfo perl yacc Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Report bugs to . GNU Automake home page: . General help using GNU software: ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing (GNU Automake) $scriptversion" 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=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake|autoreconf) 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'" autoheader_deps="'acconfig.h'" automake_deps="'Makefile.am'" aclocal_deps="'acinclude.m4'" case $normalized_program in aclocal*) echo "You should only need it if you modified $aclocal_deps or" echo "$configure_deps." ;; autoconf*) echo "You should only need it if you modified $configure_deps." ;; autogen*) echo "You should only need it if you modified a '.def' or '.tpl' file." echo "You may want to install the GNU AutoGen package:" echo "<$gnu_software_URL/autogen/>" ;; autoheader*) echo "You should only need it if you modified $autoheader_deps or" echo "$configure_deps." ;; automake*) echo "You should only need it if you modified $automake_deps or" echo "$configure_deps." ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." ;; autoreconf*) echo "You should only need it if you modified $aclocal_deps or" echo "$automake_deps or $autoheader_deps or $automake_deps or" echo "$configure_deps." ;; 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/>" ;; 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/>" ;; 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>" ;; 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/>" ;; perl*) echo "You should only need it to run GNU Autoconf, GNU Automake, " echo " assorted other tools, or if you modified a Perl source file." echo "You may want to install the Perl 5 language interpreter:" echo "<$perl_URL>" ;; *) 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 program_details "$normalized_program" } 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 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: isync-1.5.1/VERSION0000644000175000001440000000000614764112765007401 1.5.1 isync-1.5.1/mbsync-get-cert0000755000175000001440000000300514720577152011261 #!/bin/sh # # SPDX-FileCopyrightText: 2003 Theodore Ts'o # SPDX-License-Identifier: GPL-2.0-or-later # # This script will extract the necessary certificate from the IMAP server # It assumes that an attacker isn't trying to spoof you when you connect # to the IMAP server! You're better off downloading the certificate # from a trusted source. # usage() { echo "Usage: $0 [-s] " >&2 echo " -s Use IMAP+STARTTLS (port 143) instead of IMAPS (port 993)" >&2 exit 1 } STARTTLS=false while getopts "s" opt; do case $opt in s) STARTTLS=true ;; *) usage ;; esac done shift `expr $OPTIND - 1` if [ $# -ne 1 ]; then usage fi HOST=$1 seed=`date '+%s'` try=0 while :; do TMPDIR=/tmp/get-cert.$$.$seed mkdir $TMPDIR 2> /dev/null && break if [ $try = 1000 ]; then echo "Cannot create temporary directory." >&2 exit 1 fi try=`expr $try + 1` seed=`expr \( \( $seed \* 1103515245 \) + 12345 \) % 2147483648` done TMPFILE=$TMPDIR/get-cert ERRFILE=$TMPDIR/get-cert-err CERTFILE=$TMPDIR/cert if $STARTTLS; then FLAGS="-starttls imap" PORT=143 else FLAGS= PORT=993 fi echo QUIT | openssl s_client $FLAGS -connect $HOST:$PORT -showcerts \ > $TMPFILE 2> $ERRFILE sed -e '1,/^-----BEGIN CERTIFICATE-----/d' \ -e '/^-----END CERTIFICATE-----/,$d' < $TMPFILE > $CERTFILE if test -s $CERTFILE ; then echo -----BEGIN CERTIFICATE----- cat $CERTFILE echo -----END CERTIFICATE----- else echo "Couldn't retrieve certificate. openssl reported the following errors:" cat $ERRFILE fi rm -r $TMPDIR isync-1.5.1/config.guess0000755000175000001440000014051214764113020010641 #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-09' # 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: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -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-2022 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 # Just in case it came from the environment. GUESS= # 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. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { 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" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver 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 } # 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 ; 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/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; 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". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; 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) 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. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 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. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) 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 test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; 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. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; 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'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; 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) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # 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:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) 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; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then 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 GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *: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 test -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 GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 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 test -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 test "$HP_ARCH" = ""; then 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 test "$HP_ARCH" = hppa2.0w then 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 GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) 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; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; 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*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; 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:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; 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/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 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/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` 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 GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI=${LIBC}x32 fi fi GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; 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. GUESS=i386-sequent-sysv4 ;; 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. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; 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 GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; 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 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; 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. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; 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*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; x86_64:Haiku:*:*) GUESS=x86_64-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$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 # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *: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 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&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 fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: