From e6b84eca75dd2db4063bd2f6db8b2a925c677b34 Mon Sep 17 00:00:00 2001
From: Naveen Albert <asterisk@phreaknet.org>
Date: Fri, 28 Apr 2023 18:57:37 +0000
Subject: [PATCH] chan_dahdi: Add dialmode option for FXS lines.

Currently, both pulse and tone dialing are always enabled
on all FXS lines, with no way of disabling one or the other.

In some circumstances, it is desirable or necessary to
disable one of these, and this behavior can be problematic.

A new "dialmode" option is added which allows setting the
methods to support on a per channel basis for FXS (FXO
signalled lines). The four options are "both", "pulse",
"dtmf"/"tone", and "none".

Additionally, integration with the CHANNEL function is
added so that this setting can be updated for a channel
during a call.

Resolves: #35
ASTERISK-29992

UserNote: A "dialmode" option has been added which allows
specifying, on a per-channel basis, what methods of
subscriber dialing (pulse and/or tone) are permitted.

Additionally, this can be changed on a channel
at any point during a call using the CHANNEL
function.

(cherry picked from commit 82d7bb49ddc15cff5ea2e50f5ee6934102fe13d1)
---
 channels/chan_dahdi.c                  | 81 ++++++++++++++++++++++++++
 channels/chan_dahdi.h                  |  1 +
 channels/sig_analog.c                  | 10 +++-
 channels/sig_analog.h                  |  8 +++
 configs/samples/chan_dahdi.conf.sample | 11 +++-
 5 files changed, 107 insertions(+), 4 deletions(-)

diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 5607eb01b7..e49a7996a9 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -261,6 +261,17 @@
 					completely disabled)</para>
 				<para>	<literal>voice</literal>	Voice mode (returns from FAX mode, reverting the changes that were made)</para>
 			</enum>
+			<enum name="dialmode">
+				<para>R/W Pulse and tone dialing mode of the channel.</para>
+				<para>If set, overrides the setting in <literal>chan_dahdi.conf</literal> for that channel.</para>
+				<enumlist>
+					<enum name="both" />
+					<enum name="pulse" />
+					<enum name="dtmf" />
+					<enum name="tone" />
+					<enum name="none" />
+				</enumlist>
+			</enum>
 		</enumlist>
 	</info>
 	<info name="Dial_Resource" language="en_US" tech="DAHDI">
@@ -1034,6 +1045,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
 			.mohsuggest = "",
 			.parkinglot = "",
 			.transfertobusy = 1,
+			.dialmode = 0,
 
 			.ani_info_digits = 2,
 			.ani_wink_time = 1000,
@@ -7009,6 +7021,32 @@ static int dahdi_func_read(struct ast_channel *chan, const char *function, char
 		}
 		ast_mutex_unlock(&p->lock);
 #endif	/* defined(HAVE_PRI) */
+	} else if (!strcasecmp(data, "dialmode")) {
+		struct analog_pvt *analog_p;
+		ast_mutex_lock(&p->lock);
+		analog_p = p->sig_pvt;
+		/* Hardcode p->radio and p->oprmode as 0 since we're using this to check for analogness, not the handler */
+		if (dahdi_analog_lib_handles(p->sig, 0, 0) && analog_p) {
+			switch (analog_p->dialmode) {
+			case ANALOG_DIALMODE_BOTH:
+				ast_copy_string(buf, "both", len);
+				break;
+			case ANALOG_DIALMODE_PULSE:
+				ast_copy_string(buf, "pulse", len);
+				break;
+			case ANALOG_DIALMODE_DTMF:
+				ast_copy_string(buf, "dtmf", len);
+				break;
+			case ANALOG_DIALMODE_NONE:
+				ast_copy_string(buf, "none", len);
+				break;
+			}
+		} else {
+			ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
+			*buf = '\0';
+			res = -1;
+		}
+		ast_mutex_unlock(&p->lock);
 	} else {
 		*buf = '\0';
 		res = -1;
@@ -7114,6 +7152,30 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
 			ast_log(LOG_WARNING, "Unsupported value '%s' provided for '%s' item.\n", value, data);
 			res = -1;
 		}
+	} else if (!strcasecmp(data, "dialmode")) {
+		struct analog_pvt *analog_p;
+
+		ast_mutex_lock(&p->lock);
+		analog_p = p->sig_pvt;
+		if (!dahdi_analog_lib_handles(p->sig, 0, 0) || !analog_p) {
+			ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
+			ast_mutex_unlock(&p->lock);
+			return -1;
+		}
+		/* analog pvt is used for pulse dialing, so update both */
+		if (!strcasecmp(value, "pulse")) {
+			p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_PULSE;
+		} else if (!strcasecmp(value, "dtmf") || !strcasecmp(value, "tone")) {
+			p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_DTMF;
+		} else if (!strcasecmp(value, "none")) {
+			p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_NONE;
+		} else if (!strcasecmp(value, "both")) {
+			p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_BOTH;
+		} else {
+			ast_log(LOG_WARNING, "'%s' is an invalid setting for %s\n", value, data);
+			res = -1;
+		}
+		ast_mutex_unlock(&p->lock);
 	} else {
 		res = -1;
 	}
@@ -8997,6 +9059,13 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
 			} else {
 				dahdi_handle_dtmf(ast, idx, &f);
 			}
+			if (!(p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_DTMF)) {
+				if (f->frametype == AST_FRAME_DTMF_END) { /* only show this message when the key is let go of */
+					ast_debug(1, "Dropping DTMF digit '%c' because tone dialing is disabled\n", f->subclass.integer);
+				}
+				f->frametype = AST_FRAME_NULL;
+				f->subclass.integer = 0;
+			}
 			break;
 		case AST_FRAME_VOICE:
 			if (p->cidspill || p->cid_suppress_expire) {
@@ -12780,6 +12849,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 #endif
 		tmp->immediate = conf->chan.immediate;
 		tmp->transfertobusy = conf->chan.transfertobusy;
+		tmp->dialmode = conf->chan.dialmode;
 		if (chan_sig & __DAHDI_SIG_FXS) {
 			tmp->mwimonitor_fsk = conf->chan.mwimonitor_fsk;
 			tmp->mwimonitor_neon = conf->chan.mwimonitor_neon;
@@ -13113,6 +13183,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 				analog_p->threewaycalling = conf->chan.threewaycalling;
 				analog_p->transfer = conf->chan.transfer;
 				analog_p->transfertobusy = conf->chan.transfertobusy;
+				analog_p->dialmode = conf->chan.dialmode;
 				analog_p->use_callerid = tmp->use_callerid;
 				analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection;
 				analog_p->use_smdi = tmp->use_smdi;
@@ -18314,6 +18385,16 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 			confp->chan.immediate = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "transfertobusy")) {
 			confp->chan.transfertobusy = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "dialmode")) {
+			if (!strcasecmp(v->value, "pulse")) {
+				confp->chan.dialmode = ANALOG_DIALMODE_PULSE;
+			} else if (!strcasecmp(v->value, "dtmf") || !strcasecmp(v->value, "tone")) {
+				confp->chan.dialmode = ANALOG_DIALMODE_DTMF;
+			} else if (!strcasecmp(v->value, "none")) {
+				confp->chan.dialmode = ANALOG_DIALMODE_NONE;
+			} else {
+				confp->chan.dialmode = ANALOG_DIALMODE_BOTH;
+			}
 		} else if (!strcasecmp(v->name, "mwimonitor")) {
 			confp->chan.mwimonitor_neon = 0;
 			confp->chan.mwimonitor_fsk = 0;
diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h
index de813f21bf..8f16687528 100644
--- a/channels/chan_dahdi.h
+++ b/channels/chan_dahdi.h
@@ -146,6 +146,7 @@ struct dahdi_pvt {
 	 * \note Set to a couple of nonzero values but it is only tested like a boolean.
 	 */
 	int radio;
+	int dialmode;					/*!< Dialing Modes Allowed (Pulse/Tone) */
 	int outsigmod;					/*!< Outbound Signalling style (modifier) */
 	int oprmode;					/*!< "Operator Services" mode */
 	struct dahdi_pvt *oprpeer;				/*!< "Operator Services" peer tech_pvt ptr */
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index b694a96082..5ae96d9418 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -2775,9 +2775,13 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
 		analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT) ? 1 : 0);
 		ast_debug(1, "Detected %sdigit '%c'\n", (res & ANALOG_EVENT_PULSEDIGIT) ? "pulse ": "", res & 0xff);
 		analog_confmute(p, 0);
-		p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
-		p->subs[idx].f.subclass.integer = res & 0xff;
-		analog_handle_dtmf(p, ast, idx, &f);
+		if (p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_PULSE) {
+			p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
+			p->subs[idx].f.subclass.integer = res & 0xff;
+			analog_handle_dtmf(p, ast, idx, &f);
+		} else {
+			ast_debug(1, "Dropping pulse digit '%c' because pulse dialing disabled on channel %d\n", res & 0xff, p->channel);
+		}
 		return f;
 	}
 
diff --git a/channels/sig_analog.h b/channels/sig_analog.h
index 7e9acda55c..07e1cdd2aa 100644
--- a/channels/sig_analog.h
+++ b/channels/sig_analog.h
@@ -116,6 +116,13 @@ enum analog_dsp_digitmode {
 	ANALOG_DIGITMODE_MF,
 };
 
+enum analog_dialmode {
+	ANALOG_DIALMODE_BOTH = 0,
+	ANALOG_DIALMODE_PULSE,
+	ANALOG_DIALMODE_DTMF,
+	ANALOG_DIALMODE_NONE,
+};
+
 enum analog_cid_start {
 	ANALOG_CID_START_POLARITY = 1,
 	ANALOG_CID_START_POLARITY_IN,
@@ -308,6 +315,7 @@ struct analog_pvt {
 	int channel;					/*!< Channel Number */
 
 	enum analog_sigtype outsigmod;
+	enum analog_dialmode dialmode;	/*!< Which of pulse and/or tone dialing to support */
 	int echotraining;
 	int cid_signalling;				/*!< Asterisk callerid type we're using */
 	int polarityonanswerdelay;
diff --git a/configs/samples/chan_dahdi.conf.sample b/configs/samples/chan_dahdi.conf.sample
index 6b29549750..c54f482566 100644
--- a/configs/samples/chan_dahdi.conf.sample
+++ b/configs/samples/chan_dahdi.conf.sample
@@ -1131,10 +1131,19 @@ pickupgroup=1
 ;
 ; For FXO (FXS signalled) devices, whether to use pulse dial instead of DTMF
 ; Pulse digits from phones (FXS devices, FXO signalling) are always
-; detected.
+; detected, unless the dialmode setting has been changed from the default.
 ;
 ;pulsedial=yes
 ;
+; For FXS (FXO signalled) devices, the dialing modes to support for the channel.
+; By default, both pulse and tone (DTMF) dialing are always detected.
+; May be set to "pulse" if you only want to allow pulse dialing on a line.
+; May be set to "dtmf" or "tone" to only allow tone dialing on a line.
+; May be set to "none" to prevent dialing entirely.
+; You can also change this during a call using the CHANNEL function in the dialplan.
+;
+;dialmode=both
+;
 ; For fax detection, uncomment one of the following lines.  The default is *OFF*
 ;
 ;faxdetect=both
-- 
GitLab