Mon Mar 12 2012 21:44:24

Asterisk developer's documentation


res_fax.c File Reference

Generic FAX Resource for FAX technology resource modules. More...

#include "asterisk.h"
#include "asterisk/io.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/lock.h"
#include "asterisk/options.h"
#include "asterisk/strings.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/config.h"
#include "asterisk/astobj2.h"
#include "asterisk/res_fax.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/manager.h"
#include "asterisk/dsp.h"
#include "asterisk/indications.h"
#include "asterisk/ast_version.h"
Include dependency graph for res_fax.c:

Go to the source code of this file.

Data Structures

struct  ast_fax_debug_info
struct  debug_info_history
struct  fax_module
 registered FAX technology modules are put into this list More...
struct  faxmodules
struct  manager_event_info

Defines

#define FAX_MAXBUCKETS   10
 maximum buckets for res_fax ao2 containers
#define GENERIC_FAX_EXEC_ERROR(fax, chan, errorstr, reason)
#define GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason)
#define GENERIC_FAX_EXEC_SET_VARS(fax, chan, errorstr, reason)
#define RES_FAX_MAXRATE   14400
#define RES_FAX_MINRATE   2400
#define RES_FAX_MODEM   (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29)
#define RES_FAX_STATUSEVENTS   0
#define RES_FAX_TIMEOUT   10000

Enumerations

enum  {
  OPT_CALLEDMODE = (1 << 0), OPT_CALLERMODE = (1 << 1), OPT_DEBUG = (1 << 2), OPT_STATUS = (1 << 3),
  OPT_ALLOWAUDIO = (1 << 5), OPT_REQUEST_T38 = (1 << 6)
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int acf_faxopt_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 FAXOPT read function returns the contents of a FAX option.
static int acf_faxopt_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 FAXOPT write function modifies the contents of a FAX option.
static char * ast_fax_caps_to_str (enum ast_fax_capabilities caps, char *buf, size_t bufsize)
void ast_fax_log (int level, const char *file, const int line, const char *function, const char *msg)
 Log message at FAX or recommended level.
unsigned int ast_fax_maxrate (void)
 get the maxiumum supported fax rate
unsigned int ast_fax_minrate (void)
 get the minimum supported fax rate
static int ast_fax_modem_to_str (enum ast_fax_modems bits, char *tbuf, size_t bufsize)
const char * ast_fax_state_to_str (enum ast_fax_state state)
 convert a ast_fax_state to a string
int ast_fax_tech_register (struct ast_fax_tech *tech)
 register a FAX technology module
void ast_fax_tech_unregister (struct ast_fax_tech *tech)
 unregister a FAX technology module
static int check_modem_rate (enum ast_fax_modems modems, unsigned int rate)
static char * cli_fax_set_debug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 enable FAX debugging
static char * cli_fax_show_capabilities (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 display registered FAX capabilities
static char * cli_fax_show_session (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 display details of a specified fax session
static char * cli_fax_show_sessions (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 display fax sessions
static char * cli_fax_show_settings (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 display global defaults and settings
static char * cli_fax_show_stats (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 display fax stats
static char * cli_fax_show_version (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void debug_check_frame_for_silence (struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame)
static void destroy_callback (void *data)
static void destroy_session (void *session)
 destroy a FAX session structure
static void destroy_session_details (void *details)
 destroy a FAX session details structure
static int disable_t38 (struct ast_channel *chan)
static unsigned int fax_rate_str_to_int (const char *ratestr)
 convert a rate string to a rate
static struct ast_fax_sessionfax_session_new (struct ast_fax_session_details *details, struct ast_channel *chan, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
 create a FAX session
static void fax_session_release (struct ast_fax_session *s, struct ast_fax_tech_token *token)
static struct ast_fax_sessionfax_session_reserve (struct ast_fax_session_details *details, struct ast_fax_tech_token **token)
static char * fax_session_tab_complete (struct ast_cli_args *a)
 fax session tab completion
static struct
ast_fax_session_details
find_details (struct ast_channel *chan)
 returns a reference counted pointer to a fax datastore, if it exists
static struct
ast_fax_session_details
find_or_create_details (struct ast_channel *chan)
 returns a reference counted details structure from the channel's fax datastore. If the datastore does not exist it will be created
static char * generate_filenames_string (struct ast_fax_session_details *details, char *prefix, char *separator)
static int generic_fax_exec (struct ast_channel *chan, struct ast_fax_session_details *details, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
 this is the generic FAX session handling function
static void get_manager_event_info (struct ast_channel *chan, struct manager_event_info *info)
static int load_module (void)
 load res_fax
static int receivefax_exec (struct ast_channel *chan, const char *data)
 initiate a receive FAX session
static int receivefax_t38_init (struct ast_channel *chan, struct ast_fax_session_details *details)
static int report_fax_status (struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 send a FAX status manager event
static int sendfax_exec (struct ast_channel *chan, const char *data)
 initiate a send FAX session
static int sendfax_t38_init (struct ast_channel *chan, struct ast_fax_session_details *details)
static int session_cmp_cb (void *obj, void *arg, int flags)
 compare callback for ao2
static struct
ast_fax_session_details
session_details_new (void)
 create a FAX session details structure
static int session_hash_cb (const void *obj, const int flags)
 hash callback for ao2
static void set_channel_variables (struct ast_channel *chan, struct ast_fax_session_details *details)
 Set fax related channel variables.
static int set_config (const char *config_file)
 configure res_fax
static int set_fax_t38_caps (struct ast_channel *chan, struct ast_fax_session_details *details)
static void t38_parameters_ast_to_fax (struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src)
static void t38_parameters_fax_to_ast (struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src)
static int unload_module (void)
 unload res_fax
static int update_modem_bits (enum ast_fax_modems *bits, const char *value)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "Generic FAX Applications" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, }
struct ast_custom_function acf_faxopt
 FAXOPT dialplan function.
static const char app_receivefax [] = "ReceiveFAX"
static const char app_sendfax [] = "SendFAX"
static struct ast_module_infoast_module_info = &__mod_info
static const char * config = "res_fax.conf"
static struct ast_cli_entry fax_cli []
static struct ast_datastore_info fax_datastore
static struct ast_app_option fax_exec_options [128] = { [ 'a' ] = { .flag = OPT_CALLEDMODE }, [ 'c' ] = { .flag = OPT_CALLERMODE }, [ 'd' ] = { .flag = OPT_DEBUG }, [ 'f' ] = { .flag = OPT_ALLOWAUDIO }, [ 's' ] = { .flag = OPT_STATUS }, [ 'z' ] = { .flag = OPT_REQUEST_T38 }, }
static int fax_logger_level = -1
static struct faxmodules faxmodules
struct {
   int   active_sessions
   struct ao2_container *   container
   int   fax_complete
   int   fax_failures
   int   fax_rx_attempts
   int   fax_tx_attempts
   int   nextsessionname
   int   reserved_sessions
faxregistry
 The faxregistry is used to manage information and statistics for all FAX sessions.
struct {
   uint32_t   ecm:1
   unsigned int   maxrate
   unsigned int   minrate
   enum ast_fax_modems   modems
   uint32_t   statusevents:1
general_options
static int global_fax_debug = 0
static struct
ast_control_t38_parameters 
our_t38_parameters

Detailed Description

Generic FAX Resource for FAX technology resource modules.

Author:
Dwayne M. Hubbard <dhubbard@digium.com>
Kevin P. Fleming <kpfleming@digium.com>

A generic FAX resource module that provides SendFAX and ReceiveFAX applications. This module requires FAX technology modules, like res_fax_spandsp, to register with it so it can use the technology modules to perform the actual FAX transmissions.

Definition in file res_fax.c.


Define Documentation

#define FAX_MAXBUCKETS   10

maximum buckets for res_fax ao2 containers

Definition at line 216 of file res_fax.c.

Referenced by load_module().

#define GENERIC_FAX_EXEC_ERROR (   fax,
  chan,
  errorstr,
  reason 
)
Value:
do {  \
      ast_log(LOG_ERROR, "channel '%s' FAX session '%d' failure, reason: '%s' (%s)\n", chan->name, fax->id, reason, errorstr); \
      GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \
   } while (0)

Definition at line 1000 of file res_fax.c.

Referenced by generic_fax_exec().

#define GENERIC_FAX_EXEC_ERROR_QUIET (   fax,
  chan,
  errorstr,
  reason 
)
Value:
do {  \
      GENERIC_FAX_EXEC_SET_VARS(fax, chan, errorstr, reason); \
      res = ms = -1; \
   } while (0)

Definition at line 994 of file res_fax.c.

#define GENERIC_FAX_EXEC_SET_VARS (   fax,
  chan,
  errorstr,
  reason 
)

Definition at line 983 of file res_fax.c.

Referenced by generic_fax_exec().

#define RES_FAX_MAXRATE   14400

Definition at line 248 of file res_fax.c.

Referenced by set_config().

#define RES_FAX_MINRATE   2400

Definition at line 247 of file res_fax.c.

Referenced by set_config().

#define RES_FAX_MODEM   (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29)

Definition at line 250 of file res_fax.c.

Referenced by set_config().

#define RES_FAX_STATUSEVENTS   0

Definition at line 249 of file res_fax.c.

Referenced by set_config().

#define RES_FAX_TIMEOUT   10000

Definition at line 218 of file res_fax.c.

Referenced by generic_fax_exec().


Enumeration Type Documentation

anonymous enum
Enumerator:
OPT_CALLEDMODE 
OPT_CALLERMODE 
OPT_DEBUG 
OPT_STATUS 
OPT_ALLOWAUDIO 
OPT_REQUEST_T38 

Definition at line 264 of file res_fax.c.

     {
   OPT_CALLEDMODE  = (1 << 0),
   OPT_CALLERMODE  = (1 << 1),
   OPT_DEBUG       = (1 << 2),
   OPT_STATUS      = (1 << 3),
   OPT_ALLOWAUDIO  = (1 << 5),
   OPT_REQUEST_T38 = (1 << 6),
};

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2862 of file res_fax.c.

static void __unreg_module ( void  ) [static]

Definition at line 2862 of file res_fax.c.

static int acf_faxopt_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

FAXOPT read function returns the contents of a FAX option.

Definition at line 2675 of file res_fax.c.

References ao2_ref, ast_copy_string(), ast_fax_modem_to_str(), ast_free, AST_LIST_EMPTY, AST_LIST_FIRST, ast_log(), ast_fax_session_details::documents, ast_fax_session_details::ecm, ast_fax_session_details::error, find_details(), generate_filenames_string(), ast_fax_session_details::headerinfo, ast_fax_session_details::id, ast_fax_session_details::localstationid, LOG_ERROR, LOG_WARNING, ast_fax_session_details::maxrate, ast_fax_session_details::minrate, ast_fax_session_details::modems, ast_channel::name, ast_fax_session_details::option, ast_fax_session_details::pages_transferred, ast_fax_session_details::remotestationid, ast_fax_session_details::resolution, ast_fax_session_details::result, ast_fax_session_details::resultstr, and ast_fax_session_details::transfer_rate.

{
   struct ast_fax_session_details *details = find_details(chan);
   int res = 0;
   char *filenames;

   if (!details) {
      ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
      return -1;
   }
   if (!strcasecmp(data, "ecm")) {
      ast_copy_string(buf, details->option.ecm ? "yes" : "no", len);
   } else if (!strcasecmp(data, "error")) {
      ast_copy_string(buf, details->error, len);
   } else if (!strcasecmp(data, "filename")) {
      if (AST_LIST_EMPTY(&details->documents)) {
         ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
         res = -1;
      } else {
         ast_copy_string(buf, AST_LIST_FIRST(&details->documents)->filename, len);
      }
   } else if (!strcasecmp(data, "filenames")) {
      if (AST_LIST_EMPTY(&details->documents)) {
         ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
         res = -1;
      } else if ((filenames = generate_filenames_string(details, "", ","))) {
         ast_copy_string(buf, filenames, len);
         ast_free(filenames);
      } else {
         ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s), there was an error generating the filenames list.\n", chan->name, data);
         res = -1;
      }
   } else if (!strcasecmp(data, "headerinfo")) {
      ast_copy_string(buf, details->headerinfo, len);
   } else if (!strcasecmp(data, "localstationid")) {
      ast_copy_string(buf, details->localstationid, len);
   } else if (!strcasecmp(data, "maxrate")) {
      snprintf(buf, len, "%d", details->maxrate);
   } else if (!strcasecmp(data, "minrate")) {
      snprintf(buf, len, "%d", details->minrate);
   } else if (!strcasecmp(data, "pages")) {
      snprintf(buf, len, "%d", details->pages_transferred);
   } else if (!strcasecmp(data, "rate")) {
      ast_copy_string(buf, details->transfer_rate, len);
   } else if (!strcasecmp(data, "remotestationid")) {
      ast_copy_string(buf, details->remotestationid, len);
   } else if (!strcasecmp(data, "resolution")) {
      ast_copy_string(buf, details->resolution, len);
   } else if (!strcasecmp(data, "sessionid")) {
      snprintf(buf, len, "%d", details->id);
   } else if (!strcasecmp(data, "status")) {
      ast_copy_string(buf, details->result, len);
   } else if (!strcasecmp(data, "statusstr")) {
      ast_copy_string(buf, details->resultstr, len);
   } else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
      ast_fax_modem_to_str(details->modems, buf, len);
   } else {
      ast_log(LOG_WARNING, "channel '%s' can't read FAXOPT(%s) because it is unhandled!\n", chan->name, data);
      res = -1;
   }
   ao2_ref(details, -1);

   return res;
}
static int acf_faxopt_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

FAXOPT write function modifies the contents of a FAX option.

Definition at line 2741 of file res_fax.c.

References ao2_ref, ast_debug, ast_false(), ast_fax_maxrate(), ast_fax_minrate(), AST_FAX_OPTFLAG_FALSE, AST_FAX_OPTFLAG_TRUE, ast_log(), ast_skip_blanks(), ast_string_field_set, ast_true(), ast_fax_session_details::ecm, fax_rate_str_to_int(), find_or_create_details(), ast_fax_session_details::headerinfo, ast_fax_session_details::localstationid, LOG_WARNING, ast_fax_session_details::maxrate, ast_fax_session_details::minrate, ast_fax_session_details::modems, ast_channel::name, ast_fax_session_details::option, and update_modem_bits().

{
   int res = 0;
   struct ast_fax_session_details *details;

   if (!(details = find_or_create_details(chan))) {
      ast_log(LOG_WARNING, "channel '%s' can't set FAXOPT(%s) to '%s' because it failed to create a datastore.\n", chan->name, data, value);
      return -1;
   }
   ast_debug(3, "channel '%s' setting FAXOPT(%s) to '%s'\n", chan->name, data, value);

   if (!strcasecmp(data, "ecm")) {
      const char *val = ast_skip_blanks(value);
      if (ast_true(val)) {
         details->option.ecm = AST_FAX_OPTFLAG_TRUE;
      } else if (ast_false(val)) {
         details->option.ecm = AST_FAX_OPTFLAG_FALSE;
      } else {
         ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value);
      }
   } else if (!strcasecmp(data, "headerinfo")) {
      ast_string_field_set(details, headerinfo, value);
   } else if (!strcasecmp(data, "localstationid")) {
      ast_string_field_set(details, localstationid, value);
   } else if (!strcasecmp(data, "maxrate")) {
      details->maxrate = fax_rate_str_to_int(value);
      if (!details->maxrate) {
         details->maxrate = ast_fax_maxrate();
      }
   } else if (!strcasecmp(data, "minrate")) {
      details->minrate = fax_rate_str_to_int(value);
      if (!details->minrate) {
         details->minrate = ast_fax_minrate();
      }
   } else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
      update_modem_bits(&details->modems, value);
   } else {
      ast_log(LOG_WARNING, "channel '%s' set FAXOPT(%s) to '%s' is unhandled!\n", chan->name, data, value);
      res = -1;
   }

   ao2_ref(details, -1);

   return res;
}
static char* ast_fax_caps_to_str ( enum ast_fax_capabilities  caps,
char *  buf,
size_t  bufsize 
) [static]

Definition at line 470 of file res_fax.c.

References ast_build_string(), AST_FAX_TECH_AUDIO, AST_FAX_TECH_MULTI_DOC, AST_FAX_TECH_RECEIVE, AST_FAX_TECH_SEND, AST_FAX_TECH_T38, and first.

Referenced by fax_session_new(), and fax_session_reserve().

{
   char *out = buf;
   size_t size = bufsize;
   int first = 1;

   if (caps & AST_FAX_TECH_SEND) {
      if (!first) {
         ast_build_string(&buf, &size, ",");
      }
      ast_build_string(&buf, &size, "SEND");
      first = 0;
   }
   if (caps & AST_FAX_TECH_RECEIVE) {
      if (!first) {
         ast_build_string(&buf, &size, ",");
      }
      ast_build_string(&buf, &size, "RECEIVE");
      first = 0;
   }
   if (caps & AST_FAX_TECH_AUDIO) {
      if (!first) {
         ast_build_string(&buf, &size, ",");
      }
      ast_build_string(&buf, &size, "AUDIO");
      first = 0;
   }
   if (caps & AST_FAX_TECH_T38) {
      if (!first) {
         ast_build_string(&buf, &size, ",");
      }
      ast_build_string(&buf, &size, "T38");
      first = 0;
   }
   if (caps & AST_FAX_TECH_MULTI_DOC) {
      if (!first) {
         ast_build_string(&buf, &size, ",");
      }
      ast_build_string(&buf, &size, "MULTI_DOC");
      first = 0;
   }

   return out;
}
void ast_fax_log ( int  level,
const char *  file,
const int  line,
const char *  function,
const char *  msg 
)

Log message at FAX or recommended level.

The first four parameters can be represented with Asterisk's LOG_* levels. In other words, this function may be called like

ast_fax_log(LOG_DEBUG, msg);

Definition at line 652 of file res_fax.c.

References ast_log(), and ast_log_dynamic_level.

Referenced by spandsp_log().

{
   if (fax_logger_level != -1) {
      ast_log_dynamic_level(fax_logger_level, "%s", msg);
   } else {
      ast_log(level, file, line, function, "%s", msg);
   }
}
unsigned int ast_fax_maxrate ( void  )

get the maxiumum supported fax rate

Definition at line 426 of file res_fax.c.

References general_options.

Referenced by acf_faxopt_write().

{
   return general_options.maxrate;
}
unsigned int ast_fax_minrate ( void  )

get the minimum supported fax rate

Definition at line 431 of file res_fax.c.

References general_options.

Referenced by acf_faxopt_write().

{
   return general_options.minrate;
}
static int ast_fax_modem_to_str ( enum ast_fax_modems  bits,
char *  tbuf,
size_t  bufsize 
) [static]

Definition at line 515 of file res_fax.c.

References AST_FAX_MODEM_V17, AST_FAX_MODEM_V27, AST_FAX_MODEM_V29, and AST_FAX_MODEM_V34.

Referenced by acf_faxopt_read(), cli_fax_show_settings(), receivefax_exec(), sendfax_exec(), and set_config().

{
   int count = 0;

   if (bits & AST_FAX_MODEM_V17) {
      strcat(tbuf, "V17");
      count++;
   }
   if (bits & AST_FAX_MODEM_V27) {
      if (count) {
         strcat(tbuf, ",");
      }
      strcat(tbuf, "V27");
      count++;
   }
   if (bits & AST_FAX_MODEM_V29) {
      if (count) {
         strcat(tbuf, ",");
      }
      strcat(tbuf, "V29");
      count++;
   }
   if (bits & AST_FAX_MODEM_V34) {
      if (count) {
         strcat(tbuf, ",");
      }
      strcat(tbuf, "V34");
      count++;
   }

   return 0;
}
const char* ast_fax_state_to_str ( enum ast_fax_state  state)

convert a ast_fax_state to a string

convert an ast_fax_state to a string

Definition at line 629 of file res_fax.c.

References AST_FAX_STATE_ACTIVE, AST_FAX_STATE_COMPLETE, AST_FAX_STATE_INACTIVE, AST_FAX_STATE_INITIALIZED, AST_FAX_STATE_OPEN, AST_FAX_STATE_RESERVED, AST_FAX_STATE_UNINITIALIZED, ast_log(), and LOG_WARNING.

Referenced by cli_fax_show_sessions(), spandsp_fax_cli_show_session(), and spandsp_fax_write().

{
   switch (state) {
   case AST_FAX_STATE_UNINITIALIZED:
      return "Uninitialized";
   case AST_FAX_STATE_INITIALIZED:
      return "Initialized";
   case AST_FAX_STATE_OPEN:
      return "Open";
   case AST_FAX_STATE_ACTIVE:
      return "Active";
   case AST_FAX_STATE_COMPLETE:
      return "Complete";
   case AST_FAX_STATE_RESERVED:
      return "Reserved";
   case AST_FAX_STATE_INACTIVE:
      return "Inactive";
   default:
      ast_log(LOG_WARNING, "unhandled FAX state: %d\n", state);
      return "Unknown";
   }
}
int ast_fax_tech_register ( struct ast_fax_tech tech)

register a FAX technology module

register a fax technology

Definition at line 588 of file res_fax.c.

References ast_calloc, ast_module_ref(), AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_verb, ast_fax_tech::description, ast_module_info::self, fax_module::tech, and ast_fax_tech::type.

Referenced by load_module().

{
   struct fax_module *fax;

   if (!(fax = ast_calloc(1, sizeof(*fax)))) {
      return -1;
   }
   fax->tech = tech;
   AST_RWLIST_WRLOCK(&faxmodules);
   AST_RWLIST_INSERT_TAIL(&faxmodules, fax, list);
   AST_RWLIST_UNLOCK(&faxmodules);
   ast_module_ref(ast_module_info->self);

   ast_verb(3, "Registered handler for '%s' (%s)\n", fax->tech->type, fax->tech->description);

   return 0;
}
void ast_fax_tech_unregister ( struct ast_fax_tech tech)

unregister a FAX technology module

unregister a fax technology

Definition at line 607 of file res_fax.c.

References ast_free, ast_module_unref(), AST_RWLIST_REMOVE_CURRENT, AST_RWLIST_TRAVERSE_SAFE_BEGIN, AST_RWLIST_TRAVERSE_SAFE_END, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_verb, ast_module_info::self, fax_module::tech, and ast_fax_tech::type.

Referenced by unload_module().

{
   struct fax_module *fax;

   ast_verb(3, "Unregistering FAX module type '%s'\n", tech->type);

   AST_RWLIST_WRLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE_SAFE_BEGIN(&faxmodules, fax, list) {
      if (fax->tech != tech) {
         continue;
      }
      AST_RWLIST_REMOVE_CURRENT(list);
      ast_module_unref(ast_module_info->self);
      ast_free(fax);
      ast_verb(4, "Unregistered FAX module type '%s'\n", tech->type);
      break;   
   }
   AST_RWLIST_TRAVERSE_SAFE_END;
   AST_RWLIST_UNLOCK(&faxmodules);
}
static int check_modem_rate ( enum ast_fax_modems  modems,
unsigned int  rate 
) [static]

Definition at line 548 of file res_fax.c.

References AST_FAX_MODEM_V17, AST_FAX_MODEM_V27, AST_FAX_MODEM_V29, and AST_FAX_MODEM_V34.

Referenced by receivefax_exec(), sendfax_exec(), and set_config().

{
   switch (rate) {
   case 2400:
      if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
         return 1;
      }
      break;
   case 4800:
      if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
         return 1;
      }
      break;
   case 7200:
   case 9600:
      if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
         return 1;
      }
      break;
   case 12000:
   case 14400:
      if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V34))) {
         return 1;
      }
      break;
   case 28800:
   case 33600:
      if (!(modems & AST_FAX_MODEM_V34)) {
         return 1;
      }
      break;
   default:
      /* this should never happen */
      return 1;
   }

   return 0;
}
static char* cli_fax_set_debug ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

enable FAX debugging

Definition at line 2362 of file res_fax.c.

References ast_cli_entry::args, ast_cli_args::argv, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, global_fax_debug, and ast_cli_entry::usage.

{
   int flag;
   const char *what;

   switch (cmd) {
   case CLI_INIT:
      e->command = "fax set debug {on|off}";
      e->usage = 
         "Usage: fax set debug { on | off }\n"
         "       Enable/Disable FAX debugging on new FAX sessions.  The basic FAX debugging will result in\n"
         "       additional events sent to manager sessions with 'call' class permissions.  When\n"
         "       verbosity is greater than '5' events will be displayed to the console and audio versus\n"
         "       energy analysis will be performed and displayed to the console.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   what = a->argv[e->args-1];      /* guaranteed to exist */
   if (!strcasecmp(what, "on")) {
      flag = 1;
   } else if (!strcasecmp(what, "off")) {
      flag = 0;
   } else {
      return CLI_SHOWUSAGE;
   }

   global_fax_debug = flag;
   ast_cli(a->fd, "\n\nFAX Debug %s\n\n", (flag) ? "Enabled" : "Disabled");

   return CLI_SUCCESS;
}
static char* cli_fax_show_capabilities ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

display registered FAX capabilities

Definition at line 2397 of file res_fax.c.

References ast_cli(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, CLI_GENERATE, CLI_INIT, ast_fax_tech::cli_show_capabilities, CLI_SUCCESS, ast_cli_entry::command, ast_fax_tech::description, ast_cli_args::fd, fax_module::tech, ast_fax_tech::type, and ast_cli_entry::usage.

{
   struct fax_module *fax;
   unsigned int num_modules = 0;
   
   switch (cmd) {
   case CLI_INIT:
      e->command = "fax show capabilities";
      e->usage = 
         "Usage: fax show capabilities\n"
         "       Shows the capabilities of the registered FAX technology modules\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "\n\nRegistered FAX Technology Modules:\n\n");
   AST_RWLIST_RDLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
      ast_cli(a->fd, "%-15s : %s\n%-15s : %s\n%-15s : ", "Type", fax->tech->type, "Description", fax->tech->description, "Capabilities");
      fax->tech->cli_show_capabilities(a->fd);
      num_modules++;
   }
   AST_RWLIST_UNLOCK(&faxmodules);
   ast_cli(a->fd, "%d registered modules\n\n", num_modules);

   return CLI_SUCCESS;
}
static char* cli_fax_show_session ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

display details of a specified fax session

Definition at line 2462 of file res_fax.c.

References ao2_find, ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_log(), CLI_GENERATE, CLI_INIT, ast_fax_tech::cli_show_session, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, fax_session_tab_complete(), faxregistry, ast_cli_args::fd, ast_fax_session::id, LOG_ERROR, OBJ_POINTER, RESULT_SUCCESS, ast_fax_session::tech, and ast_cli_entry::usage.

{
   struct ast_fax_session *s, tmp;

   switch (cmd) {
   case CLI_INIT:
      e->command = "fax show session";
      e->usage =
         "Usage: fax show session <session number>\n"
         "       Shows status of the named FAX session\n";
      return NULL;
   case CLI_GENERATE:
      return fax_session_tab_complete(a);
   }

   if (a->argc != 4) {
      return CLI_SHOWUSAGE;
   }

   if (sscanf(a->argv[3], "%d", &tmp.id) != 1) {
      ast_log(LOG_ERROR, "invalid session id: '%s'\n", a->argv[3]);
      return RESULT_SUCCESS;
   }

   ast_cli(a->fd, "\nFAX Session Details:\n--------------------\n\n");
   s = ao2_find(faxregistry.container, &tmp, OBJ_POINTER);
   if (s) {
      s->tech->cli_show_session(s, a->fd);
      ao2_ref(s, -1);
   }
   ast_cli(a->fd, "\n\n");

   return CLI_SUCCESS;
}
static char* cli_fax_show_sessions ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

display fax sessions

Definition at line 2531 of file res_fax.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli(), ast_fax_state_to_str(), AST_FAX_TECH_AUDIO, AST_FAX_TECH_SEND, ast_free, ast_log(), ast_fax_session_details::caps, ast_fax_session::channame, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_fax_session::details, faxregistry, ast_cli_args::fd, generate_filenames_string(), ast_fax_session::id, LOG_ERROR, session_count, ast_fax_session::state, ast_fax_session::tech, ast_fax_tech::type, and ast_cli_entry::usage.

{
   struct ast_fax_session *s;
   struct ao2_iterator i;
   int session_count;
   char *filenames;

   switch (cmd) {
   case CLI_INIT:
      e->command = "fax show sessions";
      e->usage =
         "Usage: fax show sessions\n"
         "       Shows the current FAX sessions\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "\nCurrent FAX Sessions:\n\n");
   ast_cli(a->fd, "%-20.20s %-10.10s %-10.10s %-5.5s %-10.10s %-15.15s %-30.30s\n",
      "Channel", "Tech", "FAXID", "Type", "Operation", "State", "File(s)");
   i = ao2_iterator_init(faxregistry.container, 0);
   while ((s = ao2_iterator_next(&i))) {
      ao2_lock(s);

      if (!(filenames = generate_filenames_string(s->details, "", ", "))) {
         ast_log(LOG_ERROR, "error printing filenames for 'fax show sessions' command");
         ao2_unlock(s);
         ao2_ref(s, -1);
         ao2_iterator_destroy(&i);
         return CLI_FAILURE;
      }

      ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n",
         s->channame, s->tech->type, s->id,
         (s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38",
         (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
         ast_fax_state_to_str(s->state), filenames);

      ast_free(filenames);
      ao2_unlock(s);
      ao2_ref(s, -1);
   }
   ao2_iterator_destroy(&i);
   session_count = ao2_container_count(faxregistry.container);
   ast_cli(a->fd, "\n%d FAX sessions\n\n", session_count);

   return CLI_SUCCESS;
}
static char* cli_fax_show_settings ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

display global defaults and settings

Definition at line 2427 of file res_fax.c.

References ast_cli(), ast_fax_modem_to_str(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, CLI_GENERATE, CLI_INIT, ast_fax_tech::cli_show_settings, CLI_SUCCESS, ast_cli_entry::command, ast_fax_tech::description, ast_cli_args::fd, general_options, modems, fax_module::tech, ast_fax_tech::type, and ast_cli_entry::usage.

{
   struct fax_module *fax;
   char modems[128] = "";

   switch (cmd) {
   case CLI_INIT:
      e->command = "fax show settings";
      e->usage =
         "Usage: fax show settings\n"
         "       Show the global settings and defaults of both the FAX core and technology modules\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "FAX For Asterisk Settings:\n");
   ast_cli(a->fd, "\tECM: %s\n", general_options.ecm ? "Enabled" : "Disabled");
   ast_cli(a->fd, "\tStatus Events: %s\n",  general_options.statusevents ? "On" : "Off");
   ast_cli(a->fd, "\tMinimum Bit Rate: %d\n", general_options.minrate);
   ast_cli(a->fd, "\tMaximum Bit Rate: %d\n", general_options.maxrate);
   ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
   ast_cli(a->fd, "\tModem Modulations Allowed: %s\n", modems);
   ast_cli(a->fd, "\n\nFAX Technology Modules:\n\n");
   AST_RWLIST_RDLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
      ast_cli(a->fd, "%s (%s) Settings:\n", fax->tech->type, fax->tech->description);
      fax->tech->cli_show_settings(a->fd);
   }
   AST_RWLIST_UNLOCK(&faxmodules);

   return CLI_SUCCESS;
}
static char* cli_fax_show_stats ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

display fax stats

Definition at line 2498 of file res_fax.c.

References ast_cli(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, CLI_GENERATE, CLI_INIT, ast_fax_tech::cli_show_stats, CLI_SUCCESS, ast_cli_entry::command, faxregistry, ast_cli_args::fd, fax_module::tech, and ast_cli_entry::usage.

{
   struct fax_module *fax;
   
   switch (cmd) {
   case CLI_INIT:
      e->command = "fax show stats";
      e->usage =
         "Usage: fax show stats\n"
         "       Shows a statistical summary of FAX transmissions\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "\nFAX Statistics:\n---------------\n\n");
   ast_cli(a->fd, "%-20.20s : %d\n", "Current Sessions", faxregistry.active_sessions);
   ast_cli(a->fd, "%-20.20s : %d\n", "Reserved Sessions", faxregistry.reserved_sessions);
   ast_cli(a->fd, "%-20.20s : %d\n", "Transmit Attempts", faxregistry.fax_tx_attempts);
   ast_cli(a->fd, "%-20.20s : %d\n", "Receive Attempts", faxregistry.fax_rx_attempts);
   ast_cli(a->fd, "%-20.20s : %d\n", "Completed FAXes", faxregistry.fax_complete);
   ast_cli(a->fd, "%-20.20s : %d\n", "Failed FAXes", faxregistry.fax_failures);
   AST_RWLIST_RDLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
      fax->tech->cli_show_stats(a->fd);
   }
   AST_RWLIST_UNLOCK(&faxmodules);
   ast_cli(a->fd, "\n\n");

   return CLI_SUCCESS;
}
static char* cli_fax_show_version ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 2330 of file res_fax.c.

References ast_cli_args::argc, ast_cli(), ast_get_version(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_fax_tech::description, ast_cli_args::fd, fax_module::tech, ast_cli_entry::usage, and ast_fax_tech::version.

{
   struct fax_module *fax;

   switch(cmd) {
   case CLI_INIT:
      e->command = "fax show version";
      e->usage =
         "Usage: fax show version\n"
         "       Show versions of FAX For Asterisk components.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != 3) {
      return CLI_SHOWUSAGE;
   }

   ast_cli(a->fd, "FAX For Asterisk Components:\n");
   ast_cli(a->fd, "\tApplications: %s\n", ast_get_version());
   AST_RWLIST_RDLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
      ast_cli(a->fd, "\t%s: %s\n", fax->tech->description, fax->tech->version);
   }
   AST_RWLIST_UNLOCK(&faxmodules);
   ast_cli(a->fd, "\n");

   return CLI_SUCCESS;
}
static void debug_check_frame_for_silence ( struct ast_fax_session s,
unsigned int  c2s,
struct ast_frame frame 
) [static]

Definition at line 288 of file res_fax.c.

References ast_dsp_reset(), ast_dsp_silence(), ast_tvnow(), ast_tvsub(), ast_verb, ast_fax_debug_info::base_tv, ast_fax_session::channame, debug_info_history::consec_frames, debug_info_history::consec_ms, ast_fax_session::debug_info, ast_fax_debug_info::dsp, ast_fax_session::id, ast_fax_debug_info::s2c, ast_frame::samples, and debug_info_history::silence.

Referenced by generic_fax_exec().

{  
   struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c;
   int dspsilence;
   unsigned int last_consec_frames, last_consec_ms;
   unsigned char wassil;
   struct timeval diff;

   diff = ast_tvsub(ast_tvnow(), s->debug_info->base_tv);

   ast_dsp_reset(s->debug_info->dsp);
   ast_dsp_silence(s->debug_info->dsp, frame, &dspsilence);

   wassil = history->silence;
   history->silence = (dspsilence != 0) ? 1 : 0;
   if (history->silence != wassil) {
      last_consec_frames = history->consec_frames;
      last_consec_ms = history->consec_ms;
      history->consec_frames = 0;
      history->consec_ms = 0;

      if ((last_consec_frames != 0)) {
         ast_verb(6, "Channel '%s' fax session '%d', [ %.3ld.%.6ld ], %s sent %d frames (%d ms) of %s.\n",
             s->channame, s->id, (long) diff.tv_sec, (long int) diff.tv_usec,
             (c2s) ? "channel" : "stack", last_consec_frames, last_consec_ms,
             (wassil) ? "silence" : "energy");
      }
   }

   history->consec_frames++;
   history->consec_ms += (frame->samples / 8);
}
static void destroy_callback ( void *  data) [static]

Definition at line 321 of file res_fax.c.

References ao2_ref.

{
   if (data) {
      ao2_ref(data, -1);
   }
}
static void destroy_session_details ( void *  details) [static]

destroy a FAX session details structure

Definition at line 356 of file res_fax.c.

References ast_free, AST_LIST_REMOVE_HEAD, ast_string_field_free_memory, ast_fax_session_details::documents, and ast_fax_document::next.

Referenced by session_details_new().

{
   struct ast_fax_session_details *d = details;
   struct ast_fax_document *doc;
   
   while ((doc = AST_LIST_REMOVE_HEAD(&d->documents, next))) {
      ast_free(doc);
   }
   ast_string_field_free_memory(d); 
}
static int disable_t38 ( struct ast_channel chan) [static]

Definition at line 1061 of file res_fax.c.

References AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FRAME_CONTROL, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), AST_T38_REFUSED, AST_T38_REQUEST_TERMINATE, AST_T38_TERMINATED, ast_waitfor(), ast_frame::data, ast_frame::datalen, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_channel::name, ast_frame::ptr, ast_control_t38_parameters::request_response, and ast_frame::subclass.

Referenced by receivefax_exec(), sendfax_exec(), and transmit_t38().

{
   int ms;
   struct ast_frame *frame = NULL;
   struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };

   ast_debug(1, "Shutting down T.38 on %s\n", chan->name);
   if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) {
      ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name);
      return -1;
   }

   /* wait up to five seconds for negotiation to complete */
   ms = 5000;

   while (ms > 0) {
      ms = ast_waitfor(chan, ms);
      if (ms < 0) {
         ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name);
         return -1;
      }

      if (ms == 0) { /* all done, nothing happened */
         ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", chan->name);
         break;
      }

      if (!(frame = ast_read(chan))) {
         return -1;
      }
      if ((frame->frametype == AST_FRAME_CONTROL) &&
          (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
          (frame->datalen == sizeof(t38_parameters))) {
         struct ast_control_t38_parameters *parameters = frame->data.ptr;

         switch (parameters->request_response) {
         case AST_T38_TERMINATED:
            ast_debug(1, "Shut down T.38 on %s\n", chan->name);
            break;
         case AST_T38_REFUSED:
            ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", chan->name);
            ast_frfree(frame);
            return -1;
         default:
            ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", chan->name);
            ast_frfree(frame);
            return -1;
         }
         ast_frfree(frame);
         break;
      }
      ast_frfree(frame);
   }

   return 0;
}
static unsigned int fax_rate_str_to_int ( const char *  ratestr) [static]

convert a rate string to a rate

Definition at line 662 of file res_fax.c.

References ast_log(), LOG_ERROR, and LOG_WARNING.

Referenced by acf_faxopt_write(), and set_config().

{
   int rate;

   if (sscanf(ratestr, "%d", &rate) != 1) {
      ast_log(LOG_ERROR, "failed to sscanf '%s' to rate\n", ratestr);
      return 0;
   }
   switch (rate) {
   case 2400:
   case 4800:
   case 7200:
   case 9600:
   case 12000:
   case 14400:
   case 28800:
   case 33600:
      return rate;
   default:
      ast_log(LOG_WARNING, "ignoring invalid rate '%s'.  Valid options are {2400 | 4800 | 7200 | 9600 | 12000 | 14400 | 28800 | 33600}\n", ratestr);
      return 0;
   }
}
static struct ast_fax_session* fax_session_new ( struct ast_fax_session_details details,
struct ast_channel chan,
struct ast_fax_session reserved,
struct ast_fax_tech_token *  token 
) [static, read]

create a FAX session

Definition at line 782 of file res_fax.c.

References ao2_alloc, ao2_link, ao2_ref, ast_atomic_fetchadd_int(), ast_calloc, ast_debug, ast_dsp_new(), ast_dsp_set_threshold(), ast_fax_caps_to_str(), AST_FAX_STATE_RESERVED, AST_FAX_STATE_UNINITIALIZED, AST_FAX_TECH_AUDIO, ast_free, ast_log(), ast_module_ref(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_strdup, ast_fax_session_details::caps, ast_fax_tech::caps, ast_fax_session::chan, ast_fax_session::chan_uniqueid, ast_fax_session::channame, ast_fax_session_details::debug, ast_fax_session::debug_info, ast_fax_tech::description, destroy_session(), ast_fax_session::details, ast_fax_debug_info::dsp, fax_session_release(), faxregistry, ast_fax_session_details::id, ast_fax_session::id, LOG_ERROR, ast_fax_tech::module, ast_channel::name, ast_fax_tech::new_session, ast_fax_session_details::option, ast_fax_session::state, ast_fax_session::tech, fax_module::tech, ast_fax_session::tech_pvt, and ast_channel::uniqueid.

Referenced by generic_fax_exec().

{
   struct ast_fax_session *s = NULL;
   struct fax_module *faxmod;
   char caps[128] = "";

   if (reserved) {
      s = reserved;
      ao2_ref(reserved, +1);

      if (s->state == AST_FAX_STATE_RESERVED) {
         ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1);
         s->state = AST_FAX_STATE_UNINITIALIZED;
      }
   }

   if (!s && !(s = ao2_alloc(sizeof(*s), destroy_session))) {
      return NULL;
   }

   ast_atomic_fetchadd_int(&faxregistry.active_sessions, 1);
   s->state = AST_FAX_STATE_UNINITIALIZED;

   if (details->option.debug && (details->caps & AST_FAX_TECH_AUDIO)) {
      if (!(s->debug_info = ast_calloc(1, sizeof(*(s->debug_info))))) {
         fax_session_release(s, token);
         ao2_ref(s, -1);
         return NULL;
      }
      if (!(s->debug_info->dsp = ast_dsp_new())) {
         ast_free(s->debug_info);
         s->debug_info = NULL;
         fax_session_release(s, token);
         ao2_ref(s, -1);
         return NULL;
      }
      ast_dsp_set_threshold(s->debug_info->dsp, 128);
   }  

   if (!(s->channame = ast_strdup(chan->name))) {
      fax_session_release(s, token);
      ao2_ref(s, -1);
      return NULL;
   }

   if (!(s->chan_uniqueid = ast_strdup(chan->uniqueid))) {
      fax_session_release(s, token);
      ao2_ref(s, -1);
      return NULL;
   }

   s->chan = chan;
   s->details = details;
   ao2_ref(s->details, 1);

   details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1);

   if (!token) {
      /* locate a FAX technology module that can handle said requirements */
      AST_RWLIST_RDLOCK(&faxmodules);
      AST_RWLIST_TRAVERSE(&faxmodules, faxmod, list) {
         if ((faxmod->tech->caps & details->caps) != details->caps) {
            continue;
         }
         ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description);
         ast_module_ref(faxmod->tech->module);
         s->tech = faxmod->tech;
         break;
      }
      AST_RWLIST_UNLOCK(&faxmodules);

      if (!faxmod) {
         ast_log(LOG_ERROR, "Could not locate a FAX technology module with capabilities (%s)\n", ast_fax_caps_to_str(details->caps, caps, sizeof(caps)));
         ao2_ref(s, -1);
         return NULL;
      }
   }

   if (!(s->tech_pvt = s->tech->new_session(s, token))) {
      ast_log(LOG_ERROR, "FAX session failed to initialize.\n");
      ao2_ref(s, -1);
      return NULL;
   }
   /* link the session to the session container */
   if (!(ao2_link(faxregistry.container, s))) {
      ast_log(LOG_ERROR, "failed to add FAX session '%d' to container.\n", s->id);
      ao2_ref(s, -1);
      return NULL;
   }
   ast_debug(4, "channel '%s' using FAX session '%d'\n", s->channame, s->id);

   return s;
}
static void fax_session_release ( struct ast_fax_session s,
struct ast_fax_tech_token *  token 
) [static]
static struct ast_fax_session* fax_session_reserve ( struct ast_fax_session_details details,
struct ast_fax_tech_token **  token 
) [static, read]

Definition at line 732 of file res_fax.c.

References ao2_alloc, ao2_ref, ast_atomic_fetchadd_int(), ast_debug, ast_fax_caps_to_str(), AST_FAX_STATE_INACTIVE, AST_FAX_STATE_RESERVED, ast_log(), ast_module_ref(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_fax_session_details::caps, ast_fax_tech::caps, ast_fax_tech::description, destroy_session(), faxregistry, LOG_ERROR, ast_fax_tech::module, ast_fax_tech::reserve_session, ast_fax_session::state, ast_fax_session::tech, and fax_module::tech.

Referenced by receivefax_exec(), and sendfax_exec().

{
   struct ast_fax_session *s;
   struct fax_module *faxmod;
   char caps[128] = "";

   if (!(s = ao2_alloc(sizeof(*s), destroy_session))) {
      return NULL;
   }

   s->state = AST_FAX_STATE_INACTIVE;

   /* locate a FAX technology module that can handle said requirements
    * Note: the requirements have not yet been finalized as T.38
    * negotiation has not yet occured. */
   AST_RWLIST_RDLOCK(&faxmodules);
   AST_RWLIST_TRAVERSE(&faxmodules, faxmod, list) {
      if ((faxmod->tech->caps & details->caps) != details->caps) {
         continue;
      }
      ast_debug(4, "Reserving a FAX session from '%s'.\n", faxmod->tech->description);
      ast_module_ref(faxmod->tech->module);
      s->tech = faxmod->tech;
      break;
   }
   AST_RWLIST_UNLOCK(&faxmodules);

   if (!faxmod) {
      ast_log(LOG_ERROR, "Could not locate a FAX technology module with capabilities (%s)\n", ast_fax_caps_to_str(details->caps, caps, sizeof(caps)));
      ao2_ref(s, -1);
      return NULL;
   }

   if (!s->tech->reserve_session) {
      ast_debug(1, "Selected FAX technology module (%s) does not support reserving sessions.\n", s->tech->description);
      return s;
   }

   if (!(*token = s->tech->reserve_session(s))) {
      ao2_ref(s, -1);
      return NULL;
   }

   s->state = AST_FAX_STATE_RESERVED;
   ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, 1);

   return s;
}
static char* fax_session_tab_complete ( struct ast_cli_args a) [static]

fax session tab completion

Definition at line 2302 of file res_fax.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_strdup, faxregistry, ast_fax_session::id, ast_cli_args::n, name, ast_cli_args::pos, and ast_cli_args::word.

Referenced by cli_fax_show_session().

{
   int tklen;
   int wordnum = 0;
   char *name = NULL;
   struct ao2_iterator i;
   struct ast_fax_session *s;
   char tbuf[5];

   if (a->pos != 3) {
      return NULL;
   }

   tklen = strlen(a->word);
   i = ao2_iterator_init(faxregistry.container, 0);
   while ((s = ao2_iterator_next(&i))) {
      snprintf(tbuf, sizeof(tbuf), "%d", s->id);
      if (!strncasecmp(a->word, tbuf, tklen) && ++wordnum > a->n) {
         name = ast_strdup(tbuf);
         ao2_ref(s, -1);
         break;
      }
      ao2_ref(s, -1);
   }
   ao2_iterator_destroy(&i);
   return name;
}
static struct ast_fax_session_details* find_details ( struct ast_channel chan) [static, read]

returns a reference counted pointer to a fax datastore, if it exists

Definition at line 334 of file res_fax.c.

References ao2_ref, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_log(), ast_datastore::data, fax_datastore, LOG_WARNING, and ast_channel::name.

Referenced by acf_faxopt_read(), and find_or_create_details().

{
   struct ast_fax_session_details *details;
   struct ast_datastore *datastore;

   ast_channel_lock(chan); 
   if (!(datastore = ast_channel_datastore_find(chan, &fax_datastore, NULL))) {
      ast_channel_unlock(chan);  
      return NULL;
   }
   if (!(details = datastore->data)) {
      ast_log(LOG_WARNING, "Huh?  channel '%s' has a FAX datastore without data!\n", chan->name);
      ast_channel_unlock(chan);
      return NULL;
   }
   ao2_ref(details, 1); 
   ast_channel_unlock(chan);  

   return details;
}
static struct ast_fax_session_details* find_or_create_details ( struct ast_channel chan) [static, read]

returns a reference counted details structure from the channel's fax datastore. If the datastore does not exist it will be created

Definition at line 399 of file res_fax.c.

References ao2_ref, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc(), ast_log(), ast_datastore::data, fax_datastore, find_details(), LOG_WARNING, ast_channel::name, and session_details_new().

Referenced by acf_faxopt_write(), receivefax_exec(), and sendfax_exec().

{
   struct ast_fax_session_details *details;
   struct ast_datastore *datastore;

   if ((details = find_details(chan))) {
      return details;
   }
   /* channel does not have one so we must create one */
   if (!(details = session_details_new())) {
      ast_log(LOG_WARNING, "channel '%s' can't get a FAX details structure for the datastore!\n", chan->name);
      return NULL;
   }
   if (!(datastore = ast_datastore_alloc(&fax_datastore, NULL))) {
      ao2_ref(details, -1);
      ast_log(LOG_WARNING, "channel '%s' can't get a datastore!\n", chan->name);
      return NULL;
   }
   /* add the datastore to the channel and increment the refcount */
   datastore->data = details;
   ao2_ref(details, 1);
   ast_channel_lock(chan);
   ast_channel_datastore_add(chan, datastore);
   ast_channel_unlock(chan);
   return details;
}
static char* generate_filenames_string ( struct ast_fax_session_details details,
char *  prefix,
char *  separator 
) [static]

Definition at line 895 of file res_fax.c.

References ast_build_string(), AST_LIST_EMPTY, AST_LIST_FIRST, AST_LIST_TRAVERSE, ast_malloc, ast_fax_session_details::documents, ast_fax_document::filename, first, and ast_fax_document::next.

Referenced by acf_faxopt_read(), cli_fax_show_sessions(), report_fax_status(), and sendfax_exec().

{
   char *filenames, *c;
   size_t size = 0;
   int first = 1;
   struct ast_fax_document *doc;

   /* don't process empty lists */
   if (AST_LIST_EMPTY(&details->documents)) {
      return NULL;
   }

   /* Calculate the total length of all of the file names */
   AST_LIST_TRAVERSE(&details->documents, doc, next) {
      size += strlen(separator) + strlen(prefix) + strlen(doc->filename);
   }
   size += 1; /* add space for the terminating null */

   if (!(filenames = ast_malloc(size))) {
      return NULL;
   }
   c = filenames;

   ast_build_string(&c, &size, "%s%s", prefix, AST_LIST_FIRST(&details->documents)->filename);
   AST_LIST_TRAVERSE(&details->documents, doc, next) {
      if (first) {
         first = 0;
         continue;
      }

      ast_build_string(&c, &size, "%s%s%s", separator, prefix, doc->filename);
   }

   return filenames;
}
static int generic_fax_exec ( struct ast_channel chan,
struct ast_fax_session_details details,
struct ast_fax_session reserved,
struct ast_fax_tech_token *  token 
) [static]

this is the generic FAX session handling function

Definition at line 1126 of file res_fax.c.

References ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_atomic_fetchadd_int(), ast_channel_get_t38_state(), ast_channel_lock, ast_channel_unlock, AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FAX_TECH_AUDIO, AST_FAX_TECH_T38, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_MODEM, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), AST_MODEM_T38, ast_read(), ast_set_read_format(), ast_set_write_format(), ast_smoother_feed, ast_smoother_free(), ast_smoother_new(), ast_smoother_read(), ast_string_field_set, ast_strlen_zero(), AST_T38_NEGOTIATED, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_tvnow(), ast_verb, ast_waitfor_nandfds(), ast_write(), ast_fax_debug_info::base_tv, ast_fax_tech::cancel_session, ast_fax_session_details::caps, ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, debug_check_frame_for_silence(), ast_fax_session::debug_info, errno, f, fax_session_new(), faxregistry, ast_fax_session::fd, ast_fax_session::frames_received, ast_fax_session::frames_sent, ast_frame::frametype, ast_fax_tech::generate_silence, GENERIC_FAX_EXEC_ERROR, GENERIC_FAX_EXEC_SET_VARS, ast_fax_session_details::headerinfo, ast_fax_session::id, ast_frame_subclass::integer, ast_fax_session_details::localstationid, LOG_ERROR, LOG_WARNING, ast_channel::name, ast_fax_session_details::our_t38_parameters, pbx_builtin_getvar_helper(), ast_frame::ptr, ast_fax_tech::read, ast_channel::readformat, report_fax_status(), ast_control_t38_parameters::request_response, RES_FAX_TIMEOUT, ast_fax_session_details::result, set_channel_variables(), ast_fax_session::smoother, ast_fax_tech::start_session, ast_frame::subclass, ast_fax_tech::switch_to_t38, t38_parameters_ast_to_fax(), t38_parameters_fax_to_ast(), T38_STATE_NEGOTIATED, ast_fax_session::tech, ast_fax_session_details::their_t38_parameters, ast_fax_tech::write, and ast_channel::writeformat.

Referenced by receivefax_exec(), and sendfax_exec().

{
   int ms;
   int timeout = RES_FAX_TIMEOUT;
   int res = 0, chancount;
   unsigned int expected_frametype = -1;
   union ast_frame_subclass expected_framesubclass = { .integer = -1 };
   unsigned int t38negotiated = (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED);
   struct ast_control_t38_parameters t38_parameters;
   const char *tempvar;
   struct ast_fax_session *fax = NULL;
   struct ast_frame *frame = NULL;
   struct ast_channel *c = chan;
   unsigned int orig_write_format = 0, orig_read_format = 0;

   chancount = 1;

   /* create the FAX session */
   if (!(fax = fax_session_new(details, chan, reserved, token))) {
      ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n");
      report_fax_status(chan, details, "No Available Resource");
      return -1;
   }

   ast_channel_lock(chan);
   /* update session details */  
   if (ast_strlen_zero(details->headerinfo) && (tempvar = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"))) {
      ast_string_field_set(details, headerinfo, tempvar);
   }
   if (ast_strlen_zero(details->localstationid)) {
      tempvar = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID");
      ast_string_field_set(details, localstationid, tempvar ? tempvar : "unknown");
   }
   ast_channel_unlock(chan);

   report_fax_status(chan, details, "Allocating Resources");

   if (details->caps & AST_FAX_TECH_AUDIO) {
      expected_frametype = AST_FRAME_VOICE;;
      expected_framesubclass.codec = AST_FORMAT_SLINEAR;
      orig_write_format = chan->writeformat;
      if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
         ast_log(LOG_ERROR, "channel '%s' failed to set write format to signed linear'.\n", chan->name);
         ao2_lock(faxregistry.container);
         ao2_unlink(faxregistry.container, fax);
         ao2_unlock(faxregistry.container);
         ao2_ref(fax, -1);
         ast_channel_unlock(chan);
         return -1;
      }
      orig_read_format = chan->readformat;
      if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
         ast_log(LOG_ERROR, "channel '%s' failed to set read format to signed linear.\n", chan->name);
         ao2_lock(faxregistry.container);
         ao2_unlink(faxregistry.container, fax);
         ao2_unlock(faxregistry.container);
         ao2_ref(fax, -1);
         ast_channel_unlock(chan);
         return -1;
      }
      if (fax->smoother) {
         ast_smoother_free(fax->smoother);
         fax->smoother = NULL;
      }
      if (!(fax->smoother = ast_smoother_new(320))) {
         ast_log(LOG_WARNING, "Channel '%s' FAX session '%d' failed to obtain a smoother.\n", chan->name, fax->id);
      }
   } else {
      expected_frametype = AST_FRAME_MODEM;
      expected_framesubclass.codec = AST_MODEM_T38;
   }

   if (fax->debug_info) {
      fax->debug_info->base_tv = ast_tvnow();
   }

   /* reset our result fields just in case the fax tech driver wants to
    * set custom error messages */
   ast_string_field_set(details, result, "");
   ast_string_field_set(details, resultstr, "");
   ast_string_field_set(details, error, "");
   set_channel_variables(chan, details);

   if (fax->tech->start_session(fax) < 0) {
      GENERIC_FAX_EXEC_ERROR(fax, chan, "INIT_ERROR", "failed to start FAX session");
   }

   report_fax_status(chan, details, "FAX Transmission In Progress");

   ast_debug(5, "channel %s will wait on FAX fd %d\n", chan->name, fax->fd);

   /* handle frames for the session */
   ms = 1000;
   while ((res > -1) && (ms > -1) && (timeout > 0)) {
      struct ast_channel *ready_chan;
      int ofd, exception;

      ms = 1000;
      errno = 0;
      ready_chan = ast_waitfor_nandfds(&c, chancount, &fax->fd, 1, &exception, &ofd, &ms);
      if (ready_chan) {
         if (!(frame = ast_read(chan))) {
            /* the channel is probably gone, so lets stop polling on it and let the
             * FAX session complete before we exit the application.  if needed,
             * send the FAX stack silence so the modems can finish their session without
             * any problems */
            ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", chan->name);
            GENERIC_FAX_EXEC_SET_VARS(fax, chan, "HANGUP", "remote channel hungup");
            c = NULL;
            chancount = 0;
            timeout -= (1000 - ms);
            fax->tech->cancel_session(fax);
            if (fax->tech->generate_silence) {
               fax->tech->generate_silence(fax);
            }
            continue;
         }

         if ((frame->frametype == AST_FRAME_CONTROL) &&
             (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
             (frame->datalen == sizeof(t38_parameters))) {
            unsigned int was_t38 = t38negotiated;
            struct ast_control_t38_parameters *parameters = frame->data.ptr;
            
            switch (parameters->request_response) {
            case AST_T38_REQUEST_NEGOTIATE:
               /* the other end has requested a switch to T.38, so reply that we are willing, if we can
                * do T.38 as well
                */
               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
               t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
               break;
            case AST_T38_NEGOTIATED:
               t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
               t38negotiated = 1;
               break;
            default:
               break;
            }
            if (t38negotiated && !was_t38) {
               fax->tech->switch_to_t38(fax);
               details->caps &= ~AST_FAX_TECH_AUDIO;
               expected_frametype = AST_FRAME_MODEM;
               expected_framesubclass.codec = AST_MODEM_T38;
               if (fax->smoother) {
                  ast_smoother_free(fax->smoother);
                  fax->smoother = NULL;
               }
               
               report_fax_status(chan, details, "T.38 Negotiated");
               
               ast_verb(3, "Channel '%s' switched to T.38 FAX session '%d'.\n", chan->name, fax->id);
            }
         } else if ((frame->frametype == expected_frametype) &&
               (!memcmp(&frame->subclass, &expected_framesubclass, sizeof(frame->subclass)))) {
            struct ast_frame *f;
            
            if (fax->smoother) {
               /* push the frame into a smoother */
               if (ast_smoother_feed(fax->smoother, frame) < 0) {
                  GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "Failed to feed the smoother");
               }
               while ((f = ast_smoother_read(fax->smoother)) && (f->data.ptr)) {
                  if (fax->debug_info) {
                     debug_check_frame_for_silence(fax, 1, f);
                  }
                  /* write the frame to the FAX stack */
                  fax->tech->write(fax, f);
                  fax->frames_received++;
                  if (f != frame) {
                     ast_frfree(f);
                  }
               }
            } else {
               /* write the frame to the FAX stack */
               fax->tech->write(fax, frame);
               fax->frames_received++;
            }
            timeout = RES_FAX_TIMEOUT;
         }
         ast_frfree(frame);
      } else if (ofd == fax->fd) {
         /* read a frame from the FAX stack and send it out the channel.
          * the FAX stack will return a NULL if the FAX session has already completed */
         if (!(frame = fax->tech->read(fax))) {
            break;
         }

         if (fax->debug_info && (frame->frametype == AST_FRAME_VOICE)) {
            debug_check_frame_for_silence(fax, 0, frame);
         }

         ast_write(chan, frame);
         fax->frames_sent++;
         ast_frfree(frame);
         timeout = RES_FAX_TIMEOUT;
      } else {
         if (ms && (ofd < 0)) {
            if ((errno == 0) || (errno == EINTR)) {
               timeout -= (1000 - ms);
               if (timeout <= 0)
                  GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
               continue;
            } else {
               ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", chan->name);
               GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "error polling data");
               res = ms;
               break;
            }
         } else {
            /* nothing happened */
            if (timeout > 0) {
               timeout -= 1000;
               if (timeout <= 0)
                  GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
               continue;
            } else {
               ast_log(LOG_WARNING, "channel '%s' timed-out during the FAX transmission.\n", chan->name);
               GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
               break;
            }
         }
      }
   }
   ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, ms: %d, res: %d }\n", chan->name, timeout, ms, res);

   set_channel_variables(chan, details);

   if (!strcasecmp(details->result, "FAILED")) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
   } else {
      ast_atomic_fetchadd_int(&faxregistry.fax_complete, 1);
   }

   if (fax) {
      ao2_lock(faxregistry.container);
      ao2_unlink(faxregistry.container, fax);
      ao2_unlock(faxregistry.container);
      ao2_ref(fax, -1);
   }

   /* if the channel is still alive, and we changed its read/write formats,
    * restore them now
    */
   if (chancount) {
      if (orig_read_format) {
         ast_set_read_format(chan, orig_read_format);
      }
      if (orig_write_format) {
         ast_set_write_format(chan, orig_write_format);
      }
   }

   /* return the chancount so the calling function can determine if the channel hungup during this FAX session or not */
   return chancount;
}
static void get_manager_event_info ( struct ast_channel chan,
struct manager_event_info info 
) [static]

Definition at line 876 of file res_fax.c.

References manager_event_info::cid, manager_event_info::context, manager_event_info::exten, and pbx_substitute_variables_helper().

Referenced by receivefax_exec(), report_fax_status(), and sendfax_exec().

{
   pbx_substitute_variables_helper(chan, "${CONTEXT}", info->context, sizeof(info->context));
   pbx_substitute_variables_helper(chan, "${EXTEN}", info->exten, sizeof(info->exten));
   pbx_substitute_variables_helper(chan, "${CALLERID(num)}", info->cid, sizeof(info->cid));
}
static int load_module ( void  ) [static]

load res_fax

Definition at line 2821 of file res_fax.c.

References acf_faxopt, ao2_container_alloc, ao2_ref, ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_log(), ast_logger_register_level(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, ast_unregister_application(), config, fax_cli, FAX_MAXBUCKETS, faxregistry, LOG_ERROR, LOG_WARNING, receivefax_exec(), sendfax_exec(), session_cmp_cb(), session_hash_cb(), and set_config().

{
   int res;

   /* initialize the registry */
   faxregistry.active_sessions = 0;
   faxregistry.reserved_sessions = 0;
   if (!(faxregistry.container = ao2_container_alloc(FAX_MAXBUCKETS, session_hash_cb, session_cmp_cb))) {
      return AST_MODULE_LOAD_DECLINE;
   }
   
   if (set_config(config) < 0) {
      ast_log(LOG_ERROR, "failed to load configuration file '%s'\n", config);
      ao2_ref(faxregistry.container, -1);
      return AST_MODULE_LOAD_DECLINE;
   }

   /* register CLI operations and applications */
   if (ast_register_application_xml(app_sendfax, sendfax_exec) < 0) {
      ast_log(LOG_WARNING, "failed to register '%s'.\n", app_sendfax);
      ao2_ref(faxregistry.container, -1);
      return AST_MODULE_LOAD_DECLINE;
   }
   if (ast_register_application_xml(app_receivefax, receivefax_exec) < 0) {
      ast_log(LOG_WARNING, "failed to register '%s'.\n", app_receivefax);
      ast_unregister_application(app_sendfax);
      ao2_ref(faxregistry.container, -1);
      return AST_MODULE_LOAD_DECLINE;
   }
   ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli));
   res = ast_custom_function_register(&acf_faxopt);   
   fax_logger_level = ast_logger_register_level("FAX");

   return res;
}
static int receivefax_exec ( struct ast_channel chan,
const char *  data 
) [static]

initiate a receive FAX session

Definition at line 1537 of file res_fax.c.

References ast_channel::_state, ast_fax_session_details::allow_audio, ao2_ref, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_atomic_fetchadd_int(), ast_calloc, ast_channel_get_t38_state(), ast_channel_lock, ast_channel_unlock, ast_debug, AST_DECLARE_APP_ARGS, ast_fax_modem_to_str(), AST_FAX_OPTFLAG_TRUE, AST_FAX_TECH_RECEIVE, AST_FAX_TECH_T38, AST_LIST_INSERT_TAIL, ast_log(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_test_flag, ast_verb, ast_fax_session_details::caps, check_modem_rate(), manager_event_info::cid, manager_event_info::context, ast_fax_session_details::debug, disable_t38(), ast_fax_session_details::documents, EVENT_FLAG_CALL, manager_event_info::exten, fax_exec_options, fax_session_release(), fax_session_reserve(), faxregistry, ast_fax_document::filename, find_or_create_details(), generic_fax_exec(), get_manager_event_info(), global_fax_debug, LOG_ERROR, LOG_WARNING, manager_event, ast_fax_session_details::maxrate, ast_fax_session_details::minrate, ast_fax_session_details::modems, modems, ast_channel::name, OPT_ALLOWAUDIO, OPT_CALLEDMODE, OPT_CALLERMODE, OPT_DEBUG, OPT_STATUS, ast_fax_session_details::option, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), receivefax_t38_init(), S_OR, ast_fax_session_details::send_ced, set_channel_variables(), set_fax_t38_caps(), ast_fax_session_details::statusevents, T38_STATE_NEGOTIATED, and T38_STATE_UNAVAILABLE.

Referenced by load_module().

{
   char *parse, modems[128] = "";
   int channel_alive;
   struct ast_fax_session_details *details;
   struct ast_fax_session *s;
   struct ast_fax_tech_token *token = NULL;
   struct ast_fax_document *doc;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(filename);
      AST_APP_ARG(options);
   );
   struct ast_flags opts = { 0, };
   struct manager_event_info info;

   /* initialize output channel variables */
   pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
   pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", NULL);
   pbx_builtin_setvar_helper(chan, "FAXPAGES", "0");
   pbx_builtin_setvar_helper(chan, "FAXBITRATE", NULL);
   pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", NULL);

   /* if we ran receivefax then we attempted to receive a fax, even if we
    * never start a fax session */
   ast_atomic_fetchadd_int(&faxregistry.fax_rx_attempts, 1);

   /* Get a FAX session details structure from the channel's FAX datastore and create one if
    * it does not already exist. */
   if (!(details = find_or_create_details(chan))) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      pbx_builtin_setvar_helper(chan, "FAXERROR", "MEMORY_ERROR");
      pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "error allocating memory");
      ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
      return -1;
   }

   ast_string_field_set(details, result, "FAILED");
   ast_string_field_set(details, resultstr, "error starting fax session");
   ast_string_field_set(details, error, "INIT_ERROR");
   set_channel_variables(chan, details);

   if (details->maxrate < details->minrate) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "maxrate is less than minrate");
      set_channel_variables(chan, details);
      ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
      ao2_ref(details, -1);
      return -1;
   }

   if (check_modem_rate(details->modems, details->minrate)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }

   if (check_modem_rate(details->modems, details->maxrate)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }

   if (ast_strlen_zero(data)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
      ao2_ref(details, -1);
      return -1;
   }
   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);

   if (!ast_strlen_zero(args.options) &&
       ast_app_parse_options(fax_exec_options, &opts, NULL, args.options)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }
   if (ast_strlen_zero(args.filename)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
      ao2_ref(details, -1);
      return -1;
   }

   /* check for unsupported FAX application options */
   if (ast_test_flag(&opts, OPT_CALLERMODE) || ast_test_flag(&opts, OPT_CALLEDMODE)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s does not support polling\n", app_receivefax);
      ao2_ref(details, -1);
      return -1;
   }

   pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel Problems");
   pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "Error before FAX transmission started.");

   if (!(doc = ast_calloc(1, sizeof(*doc) + strlen(args.filename) + 1))) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "MEMORY_ERROR");
      ast_string_field_set(details, resultstr, "error allocating memory");
      set_channel_variables(chan, details);
      ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
      ao2_ref(details, -1);
      return -1;
   }

   strcpy(doc->filename, args.filename);
   AST_LIST_INSERT_TAIL(&details->documents, doc, next);

   ast_verb(3, "Channel '%s' receiving FAX '%s'\n", chan->name, args.filename);

   details->caps = AST_FAX_TECH_RECEIVE;

   /* check for debug */
   if (ast_test_flag(&opts, OPT_DEBUG) || global_fax_debug) {
      details->option.debug = AST_FAX_OPTFLAG_TRUE;
   }

   /* check for request for status events */
   if (ast_test_flag(&opts, OPT_STATUS)) {
      details->option.statusevents = AST_FAX_OPTFLAG_TRUE;
   }

   if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) ||
       ast_test_flag(&opts, OPT_ALLOWAUDIO)) {
      details->option.allow_audio = AST_FAX_OPTFLAG_TRUE;
   }

   if (!(s = fax_session_reserve(details, &token))) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, resultstr, "error reserving fax session");
      set_channel_variables(chan, details);
      ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
      ao2_ref(details, -1);
      return -1;
   }

   /* make sure the channel is up */
   if (chan->_state != AST_STATE_UP) {
      if (ast_answer(chan)) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, resultstr, "error answering channel");
         set_channel_variables(chan, details);
         ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name);
         fax_session_release(s, token);
         ao2_ref(s, -1);
         ao2_ref(details, -1);
         return -1;
      }
   }

   if (set_fax_t38_caps(chan, details)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "T38_NEG_ERROR");
      ast_string_field_set(details, resultstr, "error negotiating T.38");
      set_channel_variables(chan, details);
      fax_session_release(s, token);
      ao2_ref(s, -1);
      ao2_ref(details, -1);
      return -1;
   }

   if (details->caps & AST_FAX_TECH_T38) {
      if (receivefax_t38_init(chan, details)) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, error, "T38_NEG_ERROR");
         ast_string_field_set(details, resultstr, "error negotiating T.38");
         set_channel_variables(chan, details);
         fax_session_release(s, token);
         ao2_ref(s, -1);
         ao2_ref(details, -1);
         ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
         return -1;
      }
   } else {
      details->option.send_ced = 1;
   }

   if ((channel_alive = generic_fax_exec(chan, details, s, token)) < 0) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
   }

   if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
      if (disable_t38(chan)) {
         ast_debug(1, "error disabling T.38 mode on %s\n", chan->name);
      }
   }

   /* send out the AMI completion event */
   ast_channel_lock(chan);

   get_manager_event_info(chan, &info);
   manager_event(EVENT_FLAG_CALL,
            "ReceiveFAX", 
            "Channel: %s\r\n"
            "Context: %s\r\n"
            "Exten: %s\r\n"
            "CallerID: %s\r\n"
            "RemoteStationID: %s\r\n"
            "LocalStationID: %s\r\n"
            "PagesTransferred: %s\r\n"
            "Resolution: %s\r\n"
            "TransferRate: %s\r\n"
            "FileName: %s\r\n",
            chan->name,
            info.context,
            info.exten,
            info.cid,
            S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
            args.filename);
   ast_channel_unlock(chan);

   ao2_ref(s, -1);
   ao2_ref(details, -1);

   /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
   return (!channel_alive) ? -1 : 0;
}
static int receivefax_t38_init ( struct ast_channel chan,
struct ast_fax_session_details details 
) [static]

Definition at line 1384 of file res_fax.c.

References ast_fax_session_details::allow_audio, ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FAX_OPTFLAG_TRUE, AST_FAX_TECH_AUDIO, AST_FAX_TECH_T38, AST_FRAME_CONTROL, ast_frfree, ast_indicate_data(), ast_log(), ast_playtones_start(), ast_playtones_stop(), ast_read(), AST_T38_NEGOTIATED, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_waitfor(), ast_fax_session_details::caps, ast_frame::data, ast_frame::datalen, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_channel::name, ast_fax_session_details::option, ast_fax_session_details::our_t38_parameters, our_t38_parameters, ast_frame::ptr, report_fax_status(), ast_control_t38_parameters::request_response, ast_frame::subclass, t38_parameters_ast_to_fax(), t38_parameters_fax_to_ast(), T38_STATE_NEGOTIATED, T38_STATE_NEGOTIATING, and ast_fax_session_details::their_t38_parameters.

Referenced by receivefax_exec().

{
   int ms;
   struct ast_frame *frame = NULL;
   struct ast_control_t38_parameters t38_parameters;

   t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);

   /* don't send any audio if we've already received a T.38 reinvite */
   if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
      /* generate 3 seconds of CED */
      if (ast_playtones_start(chan, 1024, "!2100/3000", 1)) {
         ast_log(LOG_ERROR, "error generating CED tone on %s\n", chan->name);
         return -1;
      }

      ms = 3000;
      while (ms > 0) {
         ms = ast_waitfor(chan, ms);
         if (ms < 0) {
            ast_log(LOG_ERROR, "error while generating CED tone on %s\n", chan->name);
            ast_playtones_stop(chan);
            return -1;
         }

         if (ms == 0) { /* all done, nothing happened */
            break;
         }

         if (!(frame = ast_read(chan))) {
            ast_log(LOG_ERROR, "error reading frame while generating CED tone on %s\n", chan->name);
            ast_playtones_stop(chan);
            return -1;
         }

         if ((frame->frametype == AST_FRAME_CONTROL) &&
             (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
             (frame->datalen == sizeof(t38_parameters))) {
            struct ast_control_t38_parameters *parameters = frame->data.ptr;

            switch (parameters->request_response) {
            case AST_T38_REQUEST_NEGOTIATE:
               /* the other end has requested a switch to T.38, so reply that we are willing, if we can
                * do T.38 as well
                */
               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
               t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
               ast_playtones_stop(chan);
               break;
            case AST_T38_NEGOTIATED:
               ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
               t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
               details->caps &= ~AST_FAX_TECH_AUDIO;
               report_fax_status(chan, details, "T.38 Negotiated");
               break;
            default:
               break;
            }
         }
         ast_frfree(frame);
      }

      ast_playtones_stop(chan);
   }

   /* if T.38 was negotiated, we are done initializing */
   if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
      return 0;
   }

   /* request T.38 */
   ast_debug(1, "Negotiating T.38 for receive on %s\n", chan->name);

   /* wait up to five seconds for negotiation to complete */
   ms = 5000;

   /* set parameters based on the session's parameters */
   t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
   t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
   if ((ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0)) {
      return -1;
   }

   while (ms > 0) {
      ms = ast_waitfor(chan, ms);
      if (ms < 0) {
         ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
         return -1;
      }

      if (ms == 0) { /* all done, nothing happened */
         ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
         details->caps &= ~AST_FAX_TECH_T38;
         break;
      }

      if (!(frame = ast_read(chan))) {
         ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
         return -1;
      }

      if ((frame->frametype == AST_FRAME_CONTROL) &&
            (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
            (frame->datalen == sizeof(t38_parameters))) {
         struct ast_control_t38_parameters *parameters = frame->data.ptr;

         switch (parameters->request_response) {
         case AST_T38_REQUEST_NEGOTIATE:
            t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
            t38_parameters.request_response = AST_T38_NEGOTIATED;
            ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
            break;
         case AST_T38_NEGOTIATED:
            ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
            t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
            details->caps &= ~AST_FAX_TECH_AUDIO;
            report_fax_status(chan, details, "T.38 Negotiated");
            ms = 0;
            break;
         case AST_T38_REFUSED:
            ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
            details->caps &= ~AST_FAX_TECH_T38;
            ms = 0;
            break;
         default:
            ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
            details->caps &= ~AST_FAX_TECH_T38;
            ms = 0;
            break;
         }
      }
      ast_frfree(frame);
   }

   /* if T.38 was negotiated, we are done initializing */
   if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
      return 0;
   }

   /* if we made it here, then T.38 failed, check the 'f' flag */
   if (details->option.allow_audio != AST_FAX_OPTFLAG_TRUE) {
      ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", chan->name);
      return -1;
   }

   /* ok, audio fallback is allowed */
   details->caps |= AST_FAX_TECH_AUDIO;

   return 0;
}
static int report_fax_status ( struct ast_channel chan,
struct ast_fax_session_details details,
const char *  status 
) [static]

send a FAX status manager event

Definition at line 932 of file res_fax.c.

References ast_channel_lock, ast_channel_unlock, AST_FAX_TECH_RECEIVE, ast_free, ast_fax_session_details::caps, manager_event_info::cid, manager_event_info::context, EVENT_FLAG_CALL, manager_event_info::exten, generate_filenames_string(), get_manager_event_info(), ast_fax_session_details::localstationid, manager_event, ast_channel::name, ast_fax_session_details::option, and ast_fax_session_details::statusevents.

Referenced by generic_fax_exec(), receivefax_t38_init(), and sendfax_t38_init().

{
   char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
   if (!filenames) {
      return 1;
   }

   ast_channel_lock(chan);
   if (details->option.statusevents) {
      struct manager_event_info info;

      get_manager_event_info(chan, &info);
      manager_event(EVENT_FLAG_CALL,
               (details->caps & AST_FAX_TECH_RECEIVE) ? "ReceiveFAXStatus" : "SendFAXStatus",
               "Status: %s\r\n"
               "Channel: %s\r\n"
               "Context: %s\r\n"
               "Exten: %s\r\n"
               "CallerID: %s\r\n"
               "LocalStationID: %s\r\n"
               "%s\r\n",
               status,
               chan->name,
               info.context,
               info.exten,
               info.cid,
               details->localstationid,
               filenames);
   }
   ast_channel_unlock(chan);
   ast_free(filenames);

   return 0;
}
static int sendfax_exec ( struct ast_channel chan,
const char *  data 
) [static]

initiate a send FAX session

Definition at line 2008 of file res_fax.c.

References ast_channel::_state, ast_fax_session_details::allow_audio, ao2_ref, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_atomic_fetchadd_int(), ast_calloc, ast_channel_get_t38_state(), ast_channel_lock, ast_channel_unlock, ast_debug, AST_DECLARE_APP_ARGS, ast_fax_modem_to_str(), AST_FAX_OPTFLAG_TRUE, AST_FAX_TECH_MULTI_DOC, AST_FAX_TECH_SEND, AST_FAX_TECH_T38, ast_free, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_test_flag, ast_verb, ast_fax_session_details::caps, check_modem_rate(), manager_event_info::cid, manager_event_info::context, ast_fax_session_details::debug, disable_t38(), ast_fax_session_details::documents, EVENT_FLAG_CALL, manager_event_info::exten, F_OK, fax_exec_options, fax_session_release(), fax_session_reserve(), faxregistry, ast_fax_document::filename, find_or_create_details(), generate_filenames_string(), generic_fax_exec(), get_manager_event_info(), global_fax_debug, LOG_ERROR, LOG_WARNING, manager_event, ast_fax_session_details::maxrate, ast_fax_session_details::minrate, ast_fax_session_details::modems, modems, ast_channel::name, OPT_ALLOWAUDIO, OPT_CALLEDMODE, OPT_CALLERMODE, OPT_DEBUG, OPT_REQUEST_T38, OPT_STATUS, ast_fax_session_details::option, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), R_OK, ast_fax_session_details::request_t38, S_OR, ast_fax_session_details::send_cng, sendfax_t38_init(), set_channel_variables(), set_fax_t38_caps(), ast_fax_session_details::statusevents, strsep(), T38_STATE_NEGOTIATED, and T38_STATE_UNAVAILABLE.

Referenced by load_module().

{
   char *parse, *filenames, *c, modems[128] = "";
   int channel_alive, file_count;
   struct ast_fax_session_details *details;
   struct ast_fax_session *s;
   struct ast_fax_tech_token *token = NULL;
   struct ast_fax_document *doc;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(filenames);
      AST_APP_ARG(options);
   );
   struct ast_flags opts = { 0, };
   struct manager_event_info info;

   /* initialize output channel variables */
   pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
   pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", NULL);
   pbx_builtin_setvar_helper(chan, "FAXPAGES", "0");
   pbx_builtin_setvar_helper(chan, "FAXBITRATE", NULL);
   pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", NULL);

   /* if we ran sendfax then we attempted to send a fax, even if we never
    * start a fax session */
   ast_atomic_fetchadd_int(&faxregistry.fax_tx_attempts, 1);

   /* Get a requirement structure and set it.  This structure is used
    * to tell the FAX technology module about the higher level FAX session */
   if (!(details = find_or_create_details(chan))) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      pbx_builtin_setvar_helper(chan, "FAXERROR", "MEMORY_ERROR");
      pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "error allocating memory");
      ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
      return -1;
   }

   ast_string_field_set(details, result, "FAILED");
   ast_string_field_set(details, resultstr, "error starting fax session");
   ast_string_field_set(details, error, "INIT_ERROR");
   set_channel_variables(chan, details);

   if (details->maxrate < details->minrate) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "maxrate is less than minrate");
      set_channel_variables(chan, details);
      ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
      ao2_ref(details, -1);
      return -1;
   }

   if (check_modem_rate(details->modems, details->minrate)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }

   if (check_modem_rate(details->modems, details->maxrate)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }

   if (ast_strlen_zero(data)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]][,options])\n", app_sendfax);
      ao2_ref(details, -1);
      return -1;
   }
   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);


   if (!ast_strlen_zero(args.options) &&
       ast_app_parse_options(fax_exec_options, &opts, NULL, args.options)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ao2_ref(details, -1);
      return -1;
   }
   if (ast_strlen_zero(args.filenames)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]],options])\n", app_sendfax);
      ao2_ref(details, -1);
      return -1;
   }
   
   /* check for unsupported FAX application options */
   if (ast_test_flag(&opts, OPT_CALLERMODE) || ast_test_flag(&opts, OPT_CALLEDMODE)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "INVALID_ARGUMENTS");
      ast_string_field_set(details, resultstr, "invalid arguments");
      set_channel_variables(chan, details);
      ast_log(LOG_WARNING, "%s does not support polling\n", app_sendfax);
      ao2_ref(details, -1);
      return -1;
   }

   file_count = 0;
   filenames = args.filenames;
   while ((c = strsep(&filenames, "&"))) {
      if (access(c, (F_OK | R_OK)) < 0) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, error, "FILE_ERROR");
         ast_string_field_set(details, resultstr, "error reading file");
         set_channel_variables(chan, details);
         ast_log(LOG_ERROR, "access failure.  Verify '%s' exists and check permissions.\n", args.filenames);
         ao2_ref(details, -1);
         return -1;
      }

      if (!(doc = ast_calloc(1, sizeof(*doc) + strlen(c) + 1))) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, error, "MEMORY_ERROR");
         ast_string_field_set(details, resultstr, "error allocating memory");
         set_channel_variables(chan, details);
         ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
         ao2_ref(details, -1);
         return -1;
      }

      strcpy(doc->filename, c);
      AST_LIST_INSERT_TAIL(&details->documents, doc, next);
      file_count++;
   }

   if (file_count > 1) {
      details->caps |= AST_FAX_TECH_MULTI_DOC;
   }

   ast_verb(3, "Channel '%s' sending FAX:\n", chan->name);
   AST_LIST_TRAVERSE(&details->documents, doc, next) {
      ast_verb(3, "   %s\n", doc->filename);
   }

   details->caps = AST_FAX_TECH_SEND;

   /* check for debug */
   if (ast_test_flag(&opts, OPT_DEBUG) || global_fax_debug) {
      details->option.debug = AST_FAX_OPTFLAG_TRUE;
   }

   /* check for request for status events */
   if (ast_test_flag(&opts, OPT_STATUS)) {
      details->option.statusevents = AST_FAX_OPTFLAG_TRUE;
   }

   if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) ||
       ast_test_flag(&opts, OPT_ALLOWAUDIO)) {
      details->option.allow_audio = AST_FAX_OPTFLAG_TRUE;
   }

   if (ast_test_flag(&opts, OPT_REQUEST_T38)) {
      details->option.request_t38 = AST_FAX_OPTFLAG_TRUE;
   }

   if (!(s = fax_session_reserve(details, &token))) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, resultstr, "error reserving fax session");
      set_channel_variables(chan, details);
      ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
      ao2_ref(details, -1);
      return -1;
   }

   /* make sure the channel is up */
   if (chan->_state != AST_STATE_UP) {
      if (ast_answer(chan)) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, resultstr, "error answering channel");
         set_channel_variables(chan, details);
         ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name);
         fax_session_release(s, token);
         ao2_ref(s, -1);
         ao2_ref(details, -1);
         return -1;
      }
   }

   if (set_fax_t38_caps(chan, details)) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
      ast_string_field_set(details, error, "T38_NEG_ERROR");
      ast_string_field_set(details, resultstr, "error negotiating T.38");
      set_channel_variables(chan, details);
      fax_session_release(s, token);
      ao2_ref(s, -1);
      ao2_ref(details, -1);
      return -1;
   }

   if (details->caps & AST_FAX_TECH_T38) {
      if (sendfax_t38_init(chan, details)) {
         ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
         ast_string_field_set(details, error, "T38_NEG_ERROR");
         ast_string_field_set(details, resultstr, "error negotiating T.38");
         set_channel_variables(chan, details);
         fax_session_release(s, token);
         ao2_ref(s, -1);
         ao2_ref(details, -1);
         ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
         return -1;
      }
   } else {
      details->option.send_cng = 1;
   }

   if ((channel_alive = generic_fax_exec(chan, details, s, token)) < 0) {
      ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
   }

   if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
      if (disable_t38(chan)) {
         ast_debug(1, "error disabling T.38 mode on %s\n", chan->name);
      }
   }

   if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) {
      ast_log(LOG_ERROR, "Error generating SendFAX manager event\n");
      ao2_ref(s, -1);
      ao2_ref(details, -1);
      return (!channel_alive) ? -1 : 0;
   }

   /* send out the AMI completion event */
   ast_channel_lock(chan);
   get_manager_event_info(chan, &info);
   manager_event(EVENT_FLAG_CALL,
            "SendFAX", 
            "Channel: %s\r\n"
            "Context: %s\r\n"
            "Exten: %s\r\n"
            "CallerID: %s\r\n"
            "RemoteStationID: %s\r\n"
            "LocalStationID: %s\r\n"
            "PagesTransferred: %s\r\n"
            "Resolution: %s\r\n"
            "TransferRate: %s\r\n"
            "%s\r\n",
            chan->name,
            info.context,
            info.exten,
            info.cid,
            S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
            S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
            filenames);
   ast_channel_unlock(chan);

   ast_free(filenames);

   ao2_ref(s, -1);
   ao2_ref(details, -1);

   /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
   return (!channel_alive) ? -1 : 0;
}
static int sendfax_t38_init ( struct ast_channel chan,
struct ast_fax_session_details details 
) [static]

Definition at line 1782 of file res_fax.c.

References ast_fax_session_details::allow_audio, ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FAX_OPTFLAG_FALSE, AST_FAX_OPTFLAG_TRUE, AST_FAX_TECH_AUDIO, AST_FAX_TECH_T38, AST_FRAME_CONTROL, ast_frfree, ast_indicate_data(), ast_log(), ast_playtones_start(), ast_playtones_stop(), ast_read(), AST_T38_NEGOTIATED, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_waitfor(), ast_fax_session_details::caps, ast_frame::data, ast_frame::datalen, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_channel::name, ast_fax_session_details::option, ast_fax_session_details::our_t38_parameters, our_t38_parameters, ast_frame::ptr, report_fax_status(), ast_control_t38_parameters::request_response, ast_fax_session_details::request_t38, ast_frame::subclass, t38_parameters_ast_to_fax(), t38_parameters_fax_to_ast(), T38_STATE_NEGOTIATED, T38_STATE_NEGOTIATING, and ast_fax_session_details::their_t38_parameters.

Referenced by sendfax_exec().

{
   int ms;
   struct ast_frame *frame = NULL;
   struct ast_control_t38_parameters t38_parameters;

   t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);

   /* send CNG tone while listening for the receiver to initiate a switch
    * to T.38 mode; if they do, stop sending the CNG tone and proceed with
    * the switch.
    *
    * 10500 is enough time for 3 CNG tones
    */
   ms = 10500;

   /* don't send any audio if we've already received a T.38 reinvite */
   if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
      if (ast_playtones_start(chan, 1024, "!1100/500,!0/3000,!1100/500,!0/3000,!1100/500,!0/3000", 1)) {
         ast_log(LOG_ERROR, "error generating CNG tone on %s\n", chan->name);
         return -1;
      }
   }

   while (ms > 0) {
      ms = ast_waitfor(chan, ms);
      if (ms < 0) {
         ast_log(LOG_ERROR, "error while generating CNG tone on %s\n", chan->name);
         ast_playtones_stop(chan);
         return -1;
      }

      if (ms == 0) { /* all done, nothing happened */
         break;
      }

      if (!(frame = ast_read(chan))) {
         ast_log(LOG_ERROR, "error reading frame while generating CNG tone on %s\n", chan->name);
         ast_playtones_stop(chan);
         return -1;
      }

      if ((frame->frametype == AST_FRAME_CONTROL) &&
            (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
            (frame->datalen == sizeof(t38_parameters))) {
         struct ast_control_t38_parameters *parameters = frame->data.ptr;

         switch (parameters->request_response) {
         case AST_T38_REQUEST_NEGOTIATE:
            /* the other end has requested a switch to T.38, so reply that we are willing, if we can
             * do T.38 as well
             */
            t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
            t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
            ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
            ast_playtones_stop(chan);
            break;
         case AST_T38_NEGOTIATED:
            ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name);
            t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
            details->caps &= ~AST_FAX_TECH_AUDIO;
            report_fax_status(chan, details, "T.38 Negotiated");
            ms = 0;
            break;
         default:
            break;
         }
      }
      ast_frfree(frame);
   }

   ast_playtones_stop(chan);

   if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
      return 0;
   }

   /* T.38 negotiation did not happen, initiate a switch if requested */
   if (details->option.request_t38 == AST_FAX_OPTFLAG_TRUE) {
      ast_debug(1, "Negotiating T.38 for send on %s\n", chan->name);

      /* wait up to five seconds for negotiation to complete */
      ms = 5000;

      /* set parameters based on the session's parameters */
      t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
      t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
      if ((ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0)) {
         return -1;
      }

      while (ms > 0) {
         ms = ast_waitfor(chan, ms);
         if (ms < 0) {
            ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
            return -1;
         }

         if (ms == 0) { /* all done, nothing happened */
            ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
            details->caps &= ~AST_FAX_TECH_T38;
            break;
         }

         if (!(frame = ast_read(chan))) {
            ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
            return -1;
         }

         if ((frame->frametype == AST_FRAME_CONTROL) &&
               (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
               (frame->datalen == sizeof(t38_parameters))) {
            struct ast_control_t38_parameters *parameters = frame->data.ptr;

            switch (parameters->request_response) {
            case AST_T38_REQUEST_NEGOTIATE:
               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
               t38_parameters.request_response = AST_T38_NEGOTIATED;
               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
               break;
            case AST_T38_NEGOTIATED:
               ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
               t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
               details->caps &= ~AST_FAX_TECH_AUDIO;
               report_fax_status(chan, details, "T.38 Negotiated");
               ms = 0;
               break;
            case AST_T38_REFUSED:
               ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
               details->caps &= ~AST_FAX_TECH_T38;
               ms = 0;
               break;
            default:
               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
               details->caps &= ~AST_FAX_TECH_T38;
               ms = 0;
               break;
            }
         }
         ast_frfree(frame);
      }

      /* if T.38 was negotiated, we are done initializing */
      if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
         return 0;
      }

      /* send one more CNG tone to get audio going again for some
       * carriers if we are going to fall back to audio mode */
      if (details->option.allow_audio == AST_FAX_OPTFLAG_TRUE) {
         if (ast_playtones_start(chan, 1024, "!1100/500,!0/3000", 1)) {
            ast_log(LOG_ERROR, "error generating second CNG tone on %s\n", chan->name);
            return -1;
         }

         ms = 3500;
         while (ms > 0) {
            ms = ast_waitfor(chan, ms);
            if (ms < 0) {
               ast_log(LOG_ERROR, "error while generating second CNG tone on %s\n", chan->name);
               ast_playtones_stop(chan);
               return -1;
            }

            if (ms == 0) { /* all done, nothing happened */
               break;
            }

            if (!(frame = ast_read(chan))) {
               ast_log(LOG_ERROR, "error reading frame while generating second CNG tone on %s\n", chan->name);
               ast_playtones_stop(chan);
               return -1;
            }

            if ((frame->frametype == AST_FRAME_CONTROL) &&
                  (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
                  (frame->datalen == sizeof(t38_parameters))) {
               struct ast_control_t38_parameters *parameters = frame->data.ptr;

               switch (parameters->request_response) {
               case AST_T38_REQUEST_NEGOTIATE:
                  /* the other end has requested a switch to T.38, so reply that we are willing, if we can
                   * do T.38 as well
                   */
                  t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
                  t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
                  ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
                  ast_playtones_stop(chan);
                  break;
               case AST_T38_NEGOTIATED:
                  ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name);
                  t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                  details->caps &= ~AST_FAX_TECH_AUDIO;
                  report_fax_status(chan, details, "T.38 Negotiated");
                  ms = 0;
                  break;
               default:
                  break;
               }
            }
            ast_frfree(frame);
         }

         ast_playtones_stop(chan);

         /* if T.38 was negotiated, we are done initializing */
         if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
            return 0;
         }
      }
   }

   /* if we made it here, then T.38 failed, check the 'f' flag */
   if (details->option.allow_audio == AST_FAX_OPTFLAG_FALSE) {
      ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", chan->name);
      return -1;
   }

   /* ok, audio fallback is allowed */
   details->caps |= AST_FAX_TECH_AUDIO;

   return 0;
}
static int session_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

compare callback for ao2

Definition at line 2294 of file res_fax.c.

References CMP_MATCH, CMP_STOP, and ast_fax_session::id.

Referenced by load_module().

{
   struct ast_fax_session *lhs = obj, *rhs = arg;

   return (lhs->id == rhs->id) ? CMP_MATCH | CMP_STOP : 0;
}
static int session_hash_cb ( const void *  obj,
const int  flags 
) [static]

hash callback for ao2

Definition at line 2286 of file res_fax.c.

References ast_fax_session::id.

Referenced by load_module().

{
   const struct ast_fax_session *s = obj;

   return s->id;
}
static void set_channel_variables ( struct ast_channel chan,
struct ast_fax_session_details details 
) [static]

Set fax related channel variables.

Definition at line 968 of file res_fax.c.

References ast_fax_session_details::error, ast_fax_session_details::localstationid, ast_fax_session_details::pages_transferred, pbx_builtin_setvar_helper(), ast_fax_session_details::remotestationid, ast_fax_session_details::resolution, ast_fax_session_details::result, ast_fax_session_details::resultstr, S_OR, and ast_fax_session_details::transfer_rate.

Referenced by generic_fax_exec(), receivefax_exec(), and sendfax_exec().

{
   char buf[10];
   pbx_builtin_setvar_helper(chan, "FAXSTATUS", S_OR(details->result, NULL));
   pbx_builtin_setvar_helper(chan, "FAXERROR", S_OR(details->error, NULL));
   pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", S_OR(details->resultstr, NULL));
   pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", S_OR(details->remotestationid, NULL));
   pbx_builtin_setvar_helper(chan, "LOCALSTATIONID", S_OR(details->localstationid, NULL));
   pbx_builtin_setvar_helper(chan, "FAXBITRATE", S_OR(details->transfer_rate, NULL));
   pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", S_OR(details->resolution, NULL));

   snprintf(buf, sizeof(buf), "%d", details->pages_transferred);
   pbx_builtin_setvar_helper(chan, "FAXPAGES", buf);
}
static int set_config ( const char *  config_file) [static]

configure res_fax

Definition at line 2592 of file res_fax.c.

References ast_clear_flag, ast_config_destroy(), ast_config_load2(), ast_debug, ast_fax_modem_to_str(), AST_FAX_OPTFLAG_TRUE, ast_log(), ast_true(), ast_variable_browse(), check_modem_rate(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, fax_rate_str_to_int(), general_options, LOG_ERROR, LOG_NOTICE, modems, ast_variable::name, ast_variable::next, RES_FAX_MAXRATE, RES_FAX_MINRATE, RES_FAX_MODEM, RES_FAX_STATUSEVENTS, update_modem_bits(), and ast_variable::value.

Referenced by load_module().

{
   struct ast_config *cfg;
   struct ast_variable *v;
   struct ast_flags config_flags = { 0 };
   char modems[128] = "";

   /* set defaults */   
   general_options.minrate = RES_FAX_MINRATE;
   general_options.maxrate = RES_FAX_MAXRATE;   
   general_options.statusevents = RES_FAX_STATUSEVENTS;
   general_options.modems = RES_FAX_MODEM;
   general_options.ecm = AST_FAX_OPTFLAG_TRUE;

   /* read configuration */
   if (!(cfg = ast_config_load2(config_file, "res_fax", config_flags))) {
      ast_log(LOG_NOTICE, "Configuration file '%s' not found, using default options.\n", config_file);
      return 0;
   }

   if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_NOTICE, "Configuration file '%s' is invalid, using default options.\n", config_file);
      return 0;
   }

   if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
      cfg = ast_config_load2(config_file, "res_fax", config_flags);
   }

   /* create configuration */
   for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
      int rate;

      if (!strcasecmp(v->name, "minrate")) {
         ast_debug(3, "reading minrate '%s' from configuration file\n", v->value);
         if ((rate = fax_rate_str_to_int(v->value)) == 0) {
            ast_config_destroy(cfg);
            return -1;
         }
         general_options.minrate = rate;
      } else if (!strcasecmp(v->name, "maxrate")) {
         ast_debug(3, "reading maxrate '%s' from configuration file\n", v->value);
         if ((rate = fax_rate_str_to_int(v->value)) == 0) {
            ast_config_destroy(cfg);
            return -1;
         }
         general_options.maxrate = rate;
      } else if (!strcasecmp(v->name, "statusevents")) {
         ast_debug(3, "reading statusevents '%s' from configuration file\n", v->value);
         general_options.statusevents = ast_true(v->value);
      } else if (!strcasecmp(v->name, "ecm")) {
         ast_debug(3, "reading ecm '%s' from configuration file\n", v->value);
         general_options.ecm = ast_true(v->value);
      } else if ((!strcasecmp(v->name, "modem")) || (!strcasecmp(v->name, "modems"))) {
         general_options.modems = 0;
         update_modem_bits(&general_options.modems, v->value);
      }
   }

   ast_config_destroy(cfg);

   if (general_options.maxrate < general_options.minrate) {
      ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", general_options.maxrate, general_options.minrate);
      return -1;
   }

   if (check_modem_rate(general_options.modems, general_options.minrate)) {
      ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, general_options.minrate);
      return -1;
   }

   if (check_modem_rate(general_options.modems, general_options.maxrate)) {
      ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
      ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, general_options.maxrate);
      return -1;
   }

   return 0;
}
static int set_fax_t38_caps ( struct ast_channel chan,
struct ast_fax_session_details details 
) [static]

Definition at line 1028 of file res_fax.c.

References ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, AST_FAX_TECH_AUDIO, AST_FAX_TECH_T38, ast_indicate_data(), ast_log(), AST_T38_REQUEST_PARMS, ast_fax_session_details::caps, LOG_ERROR, ast_channel::name, ast_control_t38_parameters::request_response, T38_STATE_NEGOTIATING, T38_STATE_UNAVAILABLE, and T38_STATE_UNKNOWN.

Referenced by receivefax_exec(), and sendfax_exec().

{
   switch (ast_channel_get_t38_state(chan)) {
   case T38_STATE_UNKNOWN:
      details->caps |= AST_FAX_TECH_T38;
      break;
   case T38_STATE_UNAVAILABLE:
      details->caps |= AST_FAX_TECH_AUDIO;
      break;
   case T38_STATE_NEGOTIATING: {
      /* the other end already sent us a T.38 reinvite, so we need to prod the channel
       * driver into resending their parameters to us if it supports doing so... if
       * not, we can't proceed, because we can't create a proper reply without them.
       * if it does work, the channel driver will send an AST_CONTROL_T38_PARAMETERS
       * with a request of AST_T38_REQUEST_NEGOTIATE, which will be read by the function
       * that gets called after this one completes
       */
      struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_PARMS, };
      if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters)) != AST_T38_REQUEST_PARMS) {
         ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
         return -1;
      }
      details->caps |= AST_FAX_TECH_T38;
      break;
   }
   default:
      ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
      return -1;
   }

   return 0;
}
static int unload_module ( void  ) [static]
static int update_modem_bits ( enum ast_fax_modems bits,
const char *  value 
) [static]

Definition at line 436 of file res_fax.c.

References AST_FAX_MODEM_V17, AST_FAX_MODEM_V27, AST_FAX_MODEM_V29, AST_FAX_MODEM_V34, ast_log(), and LOG_WARNING.

Referenced by acf_faxopt_write(), and set_config().

{     
   char *m[5], *tok, *v = (char *)value;
   int i = 0, j;

   if (!(tok = strchr(v, ','))) {
      m[i++] = v;
      m[i] = NULL;
   } else {
      tok = strtok(v, ", ");
      while (tok && (i < 5)) {
         m[i++] = tok;
         tok = strtok(NULL, ", ");
      }
      m[i] = NULL;
   }

   *bits = 0;
   for (j = 0; j < i; j++) {
      if (!strcasecmp(m[j], "v17")) {
         *bits |= AST_FAX_MODEM_V17;
      } else if (!strcasecmp(m[j], "v27")) {
         *bits |= AST_FAX_MODEM_V27;
      } else if (!strcasecmp(m[j], "v29")) {
         *bits |= AST_FAX_MODEM_V29;
      } else if (!strcasecmp(m[j], "v34")) {
         *bits |= AST_FAX_MODEM_V34;
      } else {
         ast_log(LOG_WARNING, "ignoring invalid modem setting: '%s', valid options {v17 | v27 | v29 | v34}\n", m[j]);
      }
   }
   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "Generic FAX Applications" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, } [static]

Definition at line 2862 of file res_fax.c.

Initial value:
 {
   .name = "FAXOPT",
   .read = acf_faxopt_read,
   .write = acf_faxopt_write,
}

FAXOPT dialplan function.

Definition at line 2788 of file res_fax.c.

Referenced by load_module(), and unload_module().

The number of active FAX sessions

Definition at line 223 of file res_fax.c.

const char app_receivefax[] = "ReceiveFAX" [static]

Definition at line 198 of file res_fax.c.

const char app_sendfax[] = "SendFAX" [static]

Definition at line 199 of file res_fax.c.

Definition at line 2862 of file res_fax.c.

const char* config = "res_fax.conf" [static]

Definition at line 260 of file res_fax.c.

Referenced by load_module().

active sessions are astobj2 objects

Definition at line 227 of file res_fax.c.

uint32_t ecm

Definition at line 255 of file res_fax.c.

struct ast_cli_entry fax_cli[] [static]

Definition at line 2581 of file res_fax.c.

Referenced by load_module(), and unload_module().

Number of successful FAX transmissions

Definition at line 233 of file res_fax.c.

Initial value:
 {
   .type = "res_fax",
   .destroy = destroy_callback,
}

Definition at line 328 of file res_fax.c.

Referenced by find_details(), and find_or_create_details().

struct ast_app_option fax_exec_options[128] = { [ 'a' ] = { .flag = OPT_CALLEDMODE }, [ 'c' ] = { .flag = OPT_CALLERMODE }, [ 'd' ] = { .flag = OPT_DEBUG }, [ 'f' ] = { .flag = OPT_ALLOWAUDIO }, [ 's' ] = { .flag = OPT_STATUS }, [ 'z' ] = { .flag = OPT_REQUEST_T38 }, } [static]

Definition at line 280 of file res_fax.c.

Referenced by receivefax_exec(), and sendfax_exec().

Number of failed FAX transmissions

Definition at line 235 of file res_fax.c.

int fax_logger_level = -1 [static]

Definition at line 213 of file res_fax.c.

Total number of Rx FAX attempts

Definition at line 231 of file res_fax.c.

Total number of Tx FAX attempts

Definition at line 229 of file res_fax.c.

struct faxmodules faxmodules [static]
int global_fax_debug = 0 [static]

Definition at line 262 of file res_fax.c.

Referenced by cli_fax_set_debug(), receivefax_exec(), and sendfax_exec().

unsigned int maxrate

Definition at line 257 of file res_fax.c.

unsigned int minrate

Definition at line 256 of file res_fax.c.

the next unique session name

Definition at line 237 of file res_fax.c.

Definition at line 1118 of file res_fax.c.

Referenced by receivefax_t38_init(), and sendfax_t38_init().

The number of reserved FAX sessions

Definition at line 225 of file res_fax.c.

uint32_t statusevents

Definition at line 254 of file res_fax.c.