Index: build_tools/menuselect-deps.in =================================================================== --- build_tools/menuselect-deps.in (revision 454) +++ build_tools/menuselect-deps.in (working copy) @@ -1,3 +1,4 @@ BLUETOOTH=@PBX_BLUETOOTH@ MYSQLCLIENT=@PBX_MYSQLCLIENT@ ASTERISK=@PBX_ASTERISK@ +SPANDSP=@PBX_SPANDSP@ Index: configure =================================================================== --- configure (revision 454) +++ configure (working copy) @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac Revision: 383 . +# From configure.ac Revision: 384 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61. # @@ -713,6 +713,9 @@ ASTERISK_LIB ASTERISK_INCLUDE PBX_ASTERISK +SPANDSP_LIB +SPANDSP_INCLUDE +PBX_SPANDSP CPP EGREP MYSQL_CONFIG @@ -1309,6 +1312,7 @@ --with-ncurses=PATH use ncurses files in PATH --with-mysqlclient=PATH use mysqlclient files in PATH --with-asterisk=PATH use asterisk files in PATH + --with-spandsp=PATH use spandsp Library files in PATH Some influential environment variables: CC C compiler command @@ -4594,7 +4598,34 @@ +SPANDSP_DESCRIP="spandsp Library" +SPANDSP_OPTION="spandsp" +# Check whether --with-spandsp was given. +if test "${with_spandsp+set}" = set; then + withval=$with_spandsp; +case ${withval} in + n|no) + USE_SPANDSP=no + ;; + y|ye|yes) + SPANDSP_MANDATORY="yes" + ;; + *) + SPANDSP_DIR="${withval}" + SPANDSP_MANDATORY="yes" + ;; +esac + +fi + +PBX_SPANDSP=0 + + + + + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -6417,6 +6448,402 @@ fi +if test "${USE_SPANDSP}" != "no"; then + pbxlibdir="" + if test "x${SPANDSP_DIR}" != "x"; then + if test -d ${SPANDSP_DIR}/lib; then + pbxlibdir="-L${SPANDSP_DIR}/lib" + else + pbxlibdir="-L${SPANDSP_DIR}" + fi + fi + { echo "$as_me:$LINENO: checking for fax_init in -lspandsp" >&5 +echo $ECHO_N "checking for fax_init in -lspandsp... $ECHO_C" >&6; } +if test "${ac_cv_lib_spandsp_fax_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lspandsp ${pbxlibdir} -ltiff $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char fax_init (); +int +main () +{ +return fax_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_spandsp_fax_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_spandsp_fax_init=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_spandsp_fax_init" >&5 +echo "${ECHO_T}$ac_cv_lib_spandsp_fax_init" >&6; } +if test $ac_cv_lib_spandsp_fax_init = yes; then + AST_SPANDSP_FOUND=yes +else + AST_SPANDSP_FOUND=no +fi + + + if test "${AST_SPANDSP_FOUND}" = "yes"; then + SPANDSP_LIB="-lspandsp -ltiff" + SPANDSP_HEADER_FOUND="1" + if test "x${SPANDSP_DIR}" != "x"; then + SPANDSP_LIB="${pbxlibdir} ${SPANDSP_LIB}" + SPANDSP_INCLUDE="-I${SPANDSP_DIR}/include" + if test "xspandsp.h" != "x" ; then + as_ac_Header=`echo "ac_cv_header_${SPANDSP_DIR}/include/spandsp.h" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 +echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h usability" >&5 +echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <${SPANDSP_DIR}/include/spandsp.h> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h presence" >&5 +echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <${SPANDSP_DIR}/include/spandsp.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 +echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + SPANDSP_HEADER_FOUND=1 +else + SPANDSP_HEADER_FOUND=0 +fi + + + fi + else + if test "xspandsp.h" != "x" ; then + if test "${ac_cv_header_spandsp_h+set}" = set; then + { echo "$as_me:$LINENO: checking for spandsp.h" >&5 +echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } +if test "${ac_cv_header_spandsp_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 +echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking spandsp.h usability" >&5 +echo $ECHO_N "checking spandsp.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking spandsp.h presence" >&5 +echo $ECHO_N "checking spandsp.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: spandsp.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: spandsp.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: spandsp.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: spandsp.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: spandsp.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: spandsp.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: spandsp.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: spandsp.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for spandsp.h" >&5 +echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } +if test "${ac_cv_header_spandsp_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_spandsp_h=$ac_header_preproc +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 +echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } + +fi +if test $ac_cv_header_spandsp_h = yes; then + SPANDSP_HEADER_FOUND=1 +else + SPANDSP_HEADER_FOUND=0 +fi + + + fi + fi + if test "x${SPANDSP_HEADER_FOUND}" = "x0" ; then + if test ! -z "${SPANDSP_MANDATORY}" ; + then + { echo "$as_me:$LINENO: ***" >&5 +echo "$as_me: ***" >&6;} + { echo "$as_me:$LINENO: *** It appears that you do not have the spandsp development package installed." >&5 +echo "$as_me: *** It appears that you do not have the spandsp development package installed." >&6;} + { echo "$as_me:$LINENO: *** Please install it to include ${SPANDSP_DESCRIP} support" >&5 +echo "$as_me: *** Please install it to include ${SPANDSP_DESCRIP} support" >&or re-run configure;} + { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 +echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} + exit 1 + fi + SPANDSP_LIB="" + SPANDSP_INCLUDE="" + PBX_SPANDSP=0 + else + PBX_SPANDSP=1 + +cat >>confdefs.h <<_ACEOF +#define HAVE_SPANDSP 1 +_ACEOF + + fi + elif test ! -z "${SPANDSP_MANDATORY}"; + then + { echo "$as_me:$LINENO: ***" >&5 +echo "$as_me: ***" >&6;} + { echo "$as_me:$LINENO: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&5 +echo "$as_me: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&6;} + { echo "$as_me:$LINENO: *** Either correct the installation" >&5 +echo "$as_me: *** Either correct the installation" >&or run configure;} + { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 +echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} + exit 1 + fi +fi + + + MYSQL_CONFIG=No PBX_MYSQLCLIENT=0 if test "${USE_MYSQLCLIENT}" != "no"; then @@ -7554,7 +7981,7 @@ ASTERISK_LIB!$ASTERISK_LIB$ac_delim ASTERISK_INCLUDE!$ASTERISK_INCLUDE$ac_delim PBX_ASTERISK!$PBX_ASTERISK$ac_delim -CPP!$CPP$ac_delim +SPANDSP_LIB!$SPANDSP_LIB$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -7596,13 +8023,16 @@ ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +SPANDSP_INCLUDE!$SPANDSP_INCLUDE$ac_delim +PBX_SPANDSP!$PBX_SPANDSP$ac_delim +CPP!$CPP$ac_delim EGREP!$EGREP$ac_delim MYSQL_CONFIG!$MYSQL_CONFIG$ac_delim LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 4; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 7; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 Index: makeopts.in =================================================================== --- makeopts.in (revision 454) +++ makeopts.in (working copy) @@ -46,3 +46,6 @@ NCURSES_LIB=@NCURSES_LIB@ NCURSES_INCLUDE=@NCURSES_INCLUDE@ + +SPANDSP_INCLUDE=@SPANDSP_INCLUDE@ +SPANDSP_LIB=@SPANDSP_LIB@ Index: app_fax.c =================================================================== --- app_fax.c (revision 0) +++ app_fax.c (revision 0) @@ -0,0 +1,492 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Simple fax applications + * + * 2007, Dmitry Andrianov + * + * Code based on original implementation by Steve Underwood + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + */ + +/*** MODULEINFO + spandsp +***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "asterisk/lock.h" +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/app.h" +#include "asterisk/module.h" +#include "asterisk/manager.h" + +#ifndef AST_MODULE +#define AST_MODULE "app_fax" +#endif + +static char *app_sndfax_name = "SendFAX"; +static char *app_sndfax_synopsis = "Send a FAX"; +static char *app_sndfax_desc = +" SendFAX(filename[|options]):\n" +"Send a given TIFF file to the channel as a FAX.\n" +"The option string may contain zero or more of the following characters:\n" +" 'a' -- makes the application behave as an answering machine\n" +" The default behaviour is to behave as a calling machine.\n" +" 'd' -- turn on debug output. This option may be specified multiple\n" +" times to increase verbosity.\n" +"Uses LOCALSTATIONID to identify itself to the remote end.\n" +" LOCALHEADERINFO to generate a header line on each page.\n" +"Sets REMOTESTATIONID to the receiver CSID.\n" +" FAXPAGES to the number of pages sent.\n" +" FAXBITRATE to the transmition rate.\n" +" FAXRESOLUTION to the resolution.\n" +"Returns -1 in case of eny error or user hang up.\n" +"Returns 0 on success.\n"; + +static char *app_rcvfax_name = "ReceiveFAX"; +static char *app_rcvfax_synopsis = "Receive a FAX"; +static char *app_rcvfax_desc = +" ReceiveFAX(filename[|options]):\n" +"Receives a fax from the channel into the given filename overwriting\n" +"the file if it already exists. File created will have TIFF format.\n" +"The option string may contain zero or more of the following characters:\n" +" 'c' -- makes the application behave as a calling machine\n" +" The default behaviour is to behave as an answering machine.\n" +" 'd' -- turn on debug output. This option may be specified multiple\n" +" times to increase verbosity.\n" +"Uses LOCALSTATIONID to identify itself to the remote end.\n" +" LOCALHEADERINFO to generate a header line on each page.\n" +"Sets REMOTESTATIONID to the receiver CSID.\n" +" FAXPAGES to the number of pages received.\n" +" FAXBITRATE to the transmition rate.\n" +" FAXRESOLUTION to the resolution.\n" +"Returns -1 in case of eny error or user hang up.\n" +"Returns 0 on success.\n"; + +#define MAX_BLOCK_SIZE 240 + +typedef struct { + struct ast_channel *chan; + fax_state_t fax; + volatile int finished; +} fax_session; + +static void span_message(int level, const char *msg) +{ + if (level == SPAN_LOG_ERROR) + ast_log(LOG_ERROR, msg); + else if (level == SPAN_LOG_WARNING) + ast_log(LOG_WARNING, msg); + else + ast_log(LOG_WARNING, msg); +} + +static void phase_e_handler(t30_state_t *f, void *user_data, int result) +{ + char local_ident[T30_MAX_IDENT_LEN]; + char far_ident[T30_MAX_IDENT_LEN]; + char buf[20]; + fax_session *s = (fax_session *) user_data; + t30_stats_t stat; + + t30_get_transfer_statistics(f, &stat); + + s = (fax_session *) user_data; + + if (result == T30_ERR_OK) { + s->finished = 1; + + t30_get_local_ident(f, local_ident); + t30_get_far_ident(f, far_ident); + pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident); + snprintf(buf, sizeof(buf), "%i", stat.pages_transferred); + pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf); + snprintf(buf, sizeof(buf), "%i", stat.y_resolution); + pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf); + snprintf(buf, sizeof(buf), "%i", stat.bit_rate); + pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); + + ast_log(LOG_DEBUG, "Fax transmitted successfully.\n"); + ast_log(LOG_DEBUG, " Remote station ID: %s\n", far_ident); + ast_log(LOG_DEBUG, " Pages transferred: %i\n", stat.pages_transferred); + ast_log(LOG_DEBUG, " Image resolution: %i x %i\n", stat.x_resolution, stat.y_resolution); + ast_log(LOG_DEBUG, " Transfer Rate: %i\n", stat.bit_rate); + + manager_event(EVENT_FLAG_CALL, + f->tx_file ? "FaxSent" : "FaxReceived", + "Channel: %s\nExten: %s\nCallerID: %s\n" + "RemoteStationID: %s\nLocalStationID: %s\n" + "PagesTransferred: %i\nResolution: %i\n" + "TransferRate: %i\nFileName: %s\n", + s->chan->name, + s->chan->exten, + S_OR(s->chan->cid.cid_num, ""), + far_ident, + local_ident, + stat.pages_transferred, + stat.y_resolution, + stat.bit_rate, + f->tx_file ? f->tx_file : f->rx_file); + } else { + s->finished = -1; + ast_log(LOG_WARNING, "Error sending fax - result (%d) %s.\n", result, t30_completion_code_to_str(result)); + } +} + +static int transmit(fax_session *s) +{ + int res = 0, res2; + + int original_read_fmt; + int original_write_fmt; + + const char *x; + int len; + int samples; + + struct ast_frame *inf = NULL; + struct ast_frame outf; + + uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_BLOCK_SIZE * sizeof(uint16_t)]; + int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET); + + int last_state = 0; + struct timeval now, start, state_change; + + if (s->chan->_state != AST_STATE_UP) { + /* Shouldn't need this, but checking to see if channel is already answered + * Theoretically asterisk should already have answered before running the app */ + res = ast_answer(s->chan); + if (res) { + ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name); + goto done; + } + } + + original_read_fmt = s->chan->readformat; + if (original_read_fmt != AST_FORMAT_SLINEAR) { + res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); + res = -1; + goto done; + } + } + + original_write_fmt = s->chan->writeformat; + if (original_write_fmt != AST_FORMAT_SLINEAR) { + res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); + res = -1; + goto done1; + } + } + + + x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID"); + if (!ast_strlen_zero(x)) + t30_set_local_ident(&s->fax.t30_state, x); + + x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO"); + if (!ast_strlen_zero(x)) + t30_set_header_info(&s->fax.t30_state, x); + + t30_set_phase_e_handler(&s->fax.t30_state, phase_e_handler, s); + + t30_set_ecm_capability(&s->fax.t30_state, TRUE); + t30_set_supported_compressions(&s->fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); + + start = state_change = ast_tvnow(); + while (!s->finished && (res = ast_waitfor(s->chan, -1)) > -1) { + inf = ast_read(s->chan); + if (inf == NULL) + break; + + /* Check the frame type. Format also must be checked because there is a chance + that a frame in old format was already queued before we set chanel format + to slinear so it will still be received by ast_read */ + if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) { + + if (fax_rx(&s->fax, inf->data, inf->samples) < 0) { + /* I know fax_rx never returns errors. The check here is for good style only */ + ast_log(LOG_WARNING, "fax_rx returned error\n"); + res = -1; + break; + } + + samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; + len = fax_tx(&s->fax, buf, samples); + + if (len < 0) { + /* fax_tx also never returns errors. The check here is for good style only */ + ast_log(LOG_WARNING, "fax_tx returned error\n"); + res = -1; + break; + } else if (len) { + memset(&outf, 0, sizeof(outf)); + outf.frametype = AST_FRAME_VOICE; + outf.subclass = AST_FORMAT_SLINEAR; + outf.samples = len; + AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t)); + if (ast_write(s->chan, &outf) < 0) { + ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); + res = -1; + break; + } + } + + if (last_state != s->fax.t30_state.state) { + state_change = ast_tvnow(); + last_state = s->fax.t30_state.state; + } + } + + ast_frfree(inf); + inf = NULL; + + /* Watchdog. I have seen situations when remote fax disconnects (because of poor line + quality) while SpanDSP continues staying in T30_STATE_IV_CTC state forever. + To avoid this, we terminate when we see that T30 state does not change for 5 minutes. + We also terminate application when more than 30 minutes passed regardless of + state changes. This is just a precaution measure - no fax should take that long */ + + now = ast_tvnow(); + if (ast_tvdiff_ms(now, start) > 30 * 60 * 1000 || ast_tvdiff_ms(now, state_change) > 5 * 60 * 1000) { + ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); + res = -1; + break; + } + } + + if (inf) + ast_frfree(inf); + + if (res) { + ast_log(LOG_WARNING, "Transmission loop error\n"); + res = -1; + } else if (s->finished < 0) { + ast_log(LOG_WARNING, "Transmission error\n"); + res = -1; + } else if (s->finished > 0) { + ast_log(LOG_DEBUG, "Transmission finished Ok\n"); + } else if (inf == NULL) { + ast_log(LOG_DEBUG, "Channel hangup\n"); + res = -1; + } + + + if (original_write_fmt != AST_FORMAT_SLINEAR) { + res2 = ast_set_write_format(s->chan, original_write_fmt); + if (res2) + ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name); + } + +done1: + if (original_read_fmt != AST_FORMAT_SLINEAR) { + res2 = ast_set_read_format(s->chan, original_read_fmt); + if (res2) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name); + } + +done: + + return res; +} + +static int sndfax_exec(struct ast_channel *chan, void *data) +{ + int res = 0; + char *parse; + char *pos; + int calling_party; + int verbose; + + fax_session session; + + struct ast_module_user *u; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(file_name); + AST_APP_ARG(options); + ); + + if (chan == NULL) { + ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); + return -1; + } + + span_set_message_handler(span_message); + + /* The next few lines of code parse out the filename and header from the input string */ + if (ast_strlen_zero(data)) { + /* No data implies no filename or anything is present */ + ast_log(LOG_ERROR, "Txfax requires an argument (filename)\n"); + return -1; + } + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + calling_party = TRUE; + verbose = 0; + + if (args.options) { + if (strchr(args.options, 'a')) + calling_party = FALSE; + + pos = args.options; + while ((pos = strchr(pos, 'd')) != NULL) { + verbose++; + pos++; + } + } + + /* Done parsing */ + + u = ast_module_user_add(chan); + + session.chan = chan; + session.finished = 0; + + /* Initialize SpanDSP fax for transmission */ + fax_init(&session.fax, calling_party); + t30_set_tx_file(&session.fax.t30_state, args.file_name, -1, -1); + + if (verbose) { + span_log_set_level(&session.fax.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + if (verbose > 1) + span_log_set_level(&session.fax.t30_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + } + + res = transmit(&session); + + t30_terminate(&session.fax.t30_state); + + ast_module_user_remove(u); + + return res; +} + +static int rcvfax_exec(struct ast_channel *chan, void *data) +{ + int res = 0; + char *parse; + char *pos; + int calling_party; + int verbose; + + fax_session session; + + struct ast_module_user *u; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(file_name); + AST_APP_ARG(options); + ); + + if (chan == NULL) { + ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); + return -1; + } + + span_set_message_handler(span_message); + + /* The next few lines of code parse out the filename and header from the input string */ + if (ast_strlen_zero(data)) { + /* No data implies no filename or anything is present */ + ast_log(LOG_ERROR, "Txfax requires an argument (filename)\n"); + return -1; + } + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + calling_party = FALSE; + verbose = 0; + + if (args.options) { + if (strchr(args.options, 'c')) + calling_party = TRUE; + + pos = args.options; + while ((pos = strchr(pos, 'd')) != NULL) { + verbose++; + pos++; + } + } + + /* Done parsing */ + + u = ast_module_user_add(chan); + + session.chan = chan; + session.finished = 0; + + /* Initialize SpanDSP fax for receiving */ + fax_init(&session.fax, calling_party); + t30_set_rx_file(&session.fax.t30_state, args.file_name, -1); + + if (verbose) { + span_log_set_level(&session.fax.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + if (verbose > 1) + span_log_set_level(&session.fax.t30_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + } + + res = transmit(&session); + + t30_terminate(&session.fax.t30_state); + + ast_module_user_remove(u); + + return res; +} + +static int unload_module(void) +{ + int res; + + ast_module_user_hangup_all(); + + res = ast_unregister_application(app_sndfax_name); + res |= ast_unregister_application(app_rcvfax_name); + + return res; +} + +static int load_module(void) +{ + int res ; + + res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc); + res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc); + + return res; +} + + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application", + .load = load_module, + .unload = unload_module, + ); + + Index: menuselect-tree =================================================================== --- menuselect-tree (revision 454) +++ menuselect-tree (working copy) @@ -9,6 +9,10 @@ asterisk + + spandsp + asterisk + mysqlclient asterisk Index: configure.ac =================================================================== --- configure.ac (revision 454) +++ configure.ac (working copy) @@ -164,11 +164,14 @@ AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses]) AST_EXT_LIB_SETUP([MYSQLCLIENT], [mysqlclient], [mysqlclient]) AST_EXT_LIB_SETUP([ASTERISK], [asterisk], [asterisk]) +AST_EXT_LIB_SETUP([SPANDSP], [spandsp Library], [spandsp]) AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h]) AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h]) +AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [fax_init], [spandsp.h], [-ltiff]) + MYSQL_CONFIG=No PBX_MYSQLCLIENT=0 if test "${USE_MYSQLCLIENT}" != "no"; then Index: Makefile =================================================================== --- Makefile (revision 454) +++ Makefile (working copy) @@ -49,7 +49,7 @@ endif MODULES_DIR=$(ASTLIBDIR)/modules -MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql chan_mobile +MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql chan_mobile app_fax SELECTED_MODS:=$(patsubst %,%.so,$(filter-out $(MENUSELECT_ADDONS),$(MODS))) @@ -134,6 +134,9 @@ CFLAGS+=$(ASTERISK_INCLUDE) +app_fax.so: app_fax.o + $(CC) $(SOLINK) -o $@ $< $(SPANDSP_LIB) + cdr_addon_mysql.so: cdr_addon_mysql.o $(CC) $(SOLINK) -o $@ $< $(MYSQLCLIENT_LIB)