res_fax.c: Add chan locked precondition comments.
[asterisk/asterisk.git] / res / res_fax.c
index 33c3f11..94c512d 100644 (file)
@@ -468,8 +468,6 @@ struct fax_gateway {
 struct fax_detect {
        /*! \brief the start of our timeout counter */
        struct timeval timeout_start;
-       /*! \brief faxdetect timeout */
-       int timeout;
        /*! \brief DSP Processor */
        struct ast_dsp *dsp;
        /*! \brief original audio formats */
@@ -626,6 +624,8 @@ static const struct ast_datastore_info fax_datastore = {
 static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details);
 static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags);
 static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan);
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan);
+
 
 /*! \brief Copies fax detection and gateway framehooks during masquerades
  *
@@ -2835,6 +2835,20 @@ static void destroy_gateway(void *data)
        ao2_cleanup(gateway->peer_write_format);
 }
 
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) {
+       struct ast_fax_session_details *v21_details;
+       struct ast_fax_session *v21_session;
+
+       if (!chan || !(v21_details = session_details_new())) {
+               return NULL;
+       }
+
+       v21_details->caps = AST_FAX_TECH_V21_DETECT;
+       v21_session = fax_session_new(v21_details, chan, NULL, NULL);
+       ao2_ref(v21_details, -1);
+       return v21_session;
+}
+
 /*! \brief Create a new fax gateway object.
  * \param chan the channel the gateway object will be attached to
  * \param details the fax session details
@@ -2843,29 +2857,15 @@ static void destroy_gateway(void *data)
 static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
        struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
-       struct ast_fax_session_details *v21_details;
        if (!gateway) {
                return NULL;
        }
 
-       if (!(v21_details = session_details_new())) {
-               ao2_ref(gateway, -1);
-               return NULL;
-       }
-
-       v21_details->caps = AST_FAX_TECH_V21_DETECT;
-       if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
-               ao2_ref(v21_details, -1);
-               ao2_ref(gateway, -1);
-               return NULL;
-       }
-
-       if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
-               ao2_ref(v21_details, -1);
+       if (!(gateway->chan_v21_session = fax_v21_session_new(chan))) {
+               ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(chan));
                ao2_ref(gateway, -1);
                return NULL;
        }
-       ao2_ref(v21_details, -1);
 
        gateway->framehook = -1;
 
@@ -2880,11 +2880,17 @@ static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_
        return gateway;
 }
 
-/*! \brief Create a fax session and start T.30<->T.38 gateway mode
+/*!
+ * \brief Create a fax session and start T.30<->T.38 gateway mode
+ *
  * \param gateway a fax gateway object
  * \param details fax session details
  * \param chan active channel
- * \return 0 on error 1 on success*/
+ *
+ * \pre chan is locked on entry
+ *
+ * \return 0 on error 1 on success
+ */
 static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan)
 {
        struct ast_fax_session *s;
@@ -2928,6 +2934,7 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
        return 0;
 }
 
+/*! \pre chan is locked on entry */
 static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_frame *f)
 {
        struct ast_frame *fp;
@@ -2966,6 +2973,7 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
        return fp;
 }
 
+/*! \pre chan is locked on entry */
 static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
 {
        struct ast_channel *other = (active == chan) ? peer : chan;
@@ -3002,12 +3010,17 @@ static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel
        }
 }
 
-/*! \brief T38 Gateway Negotiate t38 parameters
+/*!
+ * \brief T38 Gateway Negotiate t38 parameters
+ *
  * \param gateway gateway object
  * \param chan channel running the gateway
  * \param peer channel im bridged too
  * \param active channel the frame originated on
  * \param f the control frame to process
+ *
+ * \pre chan is locked on entry
+ *
  * \return processed control frame or null frame
  */
 static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
@@ -3250,7 +3263,8 @@ static void fax_gateway_framehook_destroy(void *data)
        ao2_ref(gateway, -1);
 }
 
-/*! \brief T.30<->T.38 gateway framehook.
+/*!
+ * \brief T.30<->T.38 gateway framehook.
  *
  * Intercept packets on bridged channels and determine if a T.38 gateway is
  * required. If a gateway is required, start a gateway and handle T.38
@@ -3261,6 +3275,8 @@ static void fax_gateway_framehook_destroy(void *data)
  * \param event framehook event
  * \param data framehook data (struct fax_gateway *)
  *
+ * \pre chan is locked on entry
+ *
  * \return processed frame or NULL when f is NULL or a null frame
  */
 static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
@@ -3360,6 +3376,11 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                ast_channel_unlock(peer);
 
                gateway->bridged = 1;
+               if (!(gateway->peer_v21_session = fax_v21_session_new(peer))) {
+                       ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(peer));
+                       ast_framehook_detach(chan, gateway->framehook);
+                       return f;
+               }
        }
 
        if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) {
@@ -3486,6 +3507,10 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
                .disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */
        };
 
+       if (global_fax_debug) {
+               details->option.debug = AST_FAX_OPTFLAG_TRUE;
+       }
+
        ast_string_field_set(details, result, "SUCCESS");
        ast_string_field_set(details, resultstr, "gateway operation started successfully");
        ast_string_field_set(details, error, "NO_ERROR");
@@ -3528,13 +3553,13 @@ static void destroy_faxdetect(void *data)
                ast_dsp_free(faxdetect->dsp);
                faxdetect->dsp = NULL;
        }
-       ao2_ref(faxdetect->details, -1);
+       ao2_cleanup(faxdetect->details);
        ao2_cleanup(faxdetect->orig_format);
 }
 
 /*! \brief Create a new fax detect object.
  * \param chan the channel attaching to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
  * \param flags required options
  * \return NULL or a fax gateway object
  */
@@ -3641,8 +3666,9 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
                return f;
        }
 
-       if ((!ast_tvzero(faxdetect->timeout_start) &&
-           (ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > faxdetect->timeout))) {
+       if (!ast_tvzero(faxdetect->timeout_start)
+               && ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > details->faxdetect_timeout) {
+               ast_debug(1, "FAXOPT(faxdetect) timeout on %s\n", ast_channel_name(chan));
                ast_framehook_detach(chan, details->faxdetect_id);
                details->faxdetect_id = -1;
                return f;
@@ -3690,30 +3716,36 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
        }
 
        if (result) {
-               const char *target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+               const char *target_context;
+
                switch (result) {
                case 'f':
                case 't':
+                       target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+
                        ast_channel_unlock(chan);
+                       ast_frfree(f);
+                       f = &ast_null_frame;
                        if (ast_exists_extension(chan, target_context, "fax", 1,
                            S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-                               ast_channel_lock(chan);
                                ast_verb(2, "Redirecting '%s' to fax extension due to %s detection\n",
                                        ast_channel_name(chan), (result == 'f') ? "CNG" : "T38");
                                pbx_builtin_setvar_helper(chan, "FAXEXTEN", ast_channel_exten(chan));
                                if (ast_async_goto(chan, target_context, "fax", 1)) {
                                        ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(chan), target_context);
                                }
-                               ast_frfree(f);
-                               f = &ast_null_frame;
                        } else {
-                               ast_channel_lock(chan);
                                ast_log(LOG_NOTICE, "FAX %s detected but no fax extension in context (%s)\n",
                                        (result == 'f') ? "CNG" : "T38", target_context);
                        }
+                       ast_channel_lock(chan);
+
+                       ast_framehook_detach(chan, details->faxdetect_id);
+                       details->faxdetect_id = -1;
+                       break;
+               default:
+                       break;
                }
-               ast_framehook_detach(chan, details->faxdetect_id);
-               details->faxdetect_id = -1;
        }
 
        return f;
@@ -3721,7 +3753,7 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
 
 /*! \brief Attach a faxdetect framehook object to a channel.
  * \param chan the channel to attach to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
  * \return the faxdetect structure or NULL on error
  * \param flags required options
  * \retval -1 error
@@ -4469,8 +4501,14 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                details->gateway_timeout = 0;
                                if (timeout) {
                                        unsigned int gwtimeout;
-                                       if (sscanf(timeout, "%u", &gwtimeout) == 1) {
-                                               details->gateway_timeout = gwtimeout * 1000;
+
+                                       if (sscanf(timeout, "%30u", &gwtimeout) == 1) {
+                                               if (gwtimeout >= 0) {
+                                                       details->gateway_timeout = gwtimeout * 1000;
+                                               } else {
+                                                       ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative.  Ignoring timeout\n",
+                                                               cmd, data);
+                                               }
                                        } else {
                                                ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n", timeout, data);
                                        }
@@ -4487,7 +4525,9 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", ast_channel_name(chan));
                        }
                } else if (ast_false(val)) {
+                       ast_channel_lock(chan);
                        ast_framehook_detach(chan, details->gateway_id);
+                       ast_channel_unlock(chan);
                        details->gateway_id = -1;
                } else {
                        ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
@@ -4505,11 +4545,18 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
 
                if (ast_true(val) || !strcasecmp(val, "t38") || !strcasecmp(val, "cng")) {
                        if (details->faxdetect_id < 0) {
-                               if (timeout && (sscanf(timeout, "%u", &fdtimeout) == 1)) {
-                                       if (fdtimeout > 0) {
-                                               fdtimeout = fdtimeout * 1000;
+                               if (timeout) {
+                                       if (sscanf(timeout, "%30u", &fdtimeout) == 1) {
+                                               if (fdtimeout >= 0) {
+                                                       fdtimeout *= 1000;
+                                               } else {
+                                                       ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative.  Ignoring timeout\n",
+                                                               cmd, data);
+                                                       fdtimeout = 0;
+                                               }
                                        } else {
-                                               ast_log(LOG_WARNING, "Timeout cannot be negative ignoring timeout\n");
+                                               ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n",
+                                                       timeout, data);
                                        }
                                }
 
@@ -4532,7 +4579,9 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                ast_log(LOG_WARNING, "Attempt to attach a FAX detect on channel (%s) with FAX detect already running.\n", ast_channel_name(chan));
                        }
                } else if (ast_false(val)) {
+                       ast_channel_lock(chan);
                        ast_framehook_detach(chan, details->faxdetect_id);
+                       ast_channel_unlock(chan);
                        details->faxdetect_id = -1;
                } else {
                        ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);