mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-09 14:36:48 +00:00
Compare commits
5 Commits
bce37d3b2f
...
daab5c330b
Author | SHA1 | Date | |
---|---|---|---|
|
daab5c330b | ||
|
137baf0838 | ||
|
dc2d057f16 | ||
|
33af0193c0 | ||
|
bbb9f5ead5 |
@@ -78,6 +78,20 @@
|
||||
<option name="m">
|
||||
<para>Mute conference.</para>
|
||||
</option>
|
||||
<option name="t">
|
||||
<para>Post-digit timeout, in seconds. If more than this amount of time
|
||||
elapses after a digit, the number will be deemed finalized and
|
||||
the application will return This is useful when reading a variable
|
||||
number of digits that is unknown in advance.</para>
|
||||
<para>This can be almost any positive decimal number of seconds, but must
|
||||
be no less than 0.6 seconds, since this is the SF inter-digit timeout.
|
||||
The default is 0.8, which should be compatible with <literal>SendSF</literal>.
|
||||
Values lower than 0.75 will generally produce unreliable results.</para>
|
||||
<para>Note this timer only kicks in after at least one digit has been received,
|
||||
to account for sender/receiver synchronization.</para>
|
||||
<para>Note that <literal>RECEIVESFSTATUS</literal> will still be
|
||||
set to <literal>TIMEOUT</literal> if the post-digit timer expires.</para>
|
||||
</option>
|
||||
<option name="q">
|
||||
<para>Quelch SF from in-band.</para>
|
||||
</option>
|
||||
@@ -146,18 +160,35 @@
|
||||
</application>
|
||||
***/
|
||||
|
||||
/* Default post-digit timer */
|
||||
#define DEFAULT_POST_DIGIT_TIMER 0.8
|
||||
|
||||
/* Bell System Technical Journal 39 (Nov. 1960) */
|
||||
#define SF_MIN_OFF 25
|
||||
#define SF_ON 67
|
||||
#define SF_BETWEEN 600
|
||||
#define SF_MIN_DETECT 50
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_DELAY = (1 << 0),
|
||||
OPT_MUTE = (1 << 1),
|
||||
OPT_QUELCH = (1 << 2),
|
||||
OPT_RELAXED = (1 << 3),
|
||||
OPT_EXTRAPULSES = (1 << 4),
|
||||
OPT_DIGIT_TIMEOUT = (1 << 5),
|
||||
};
|
||||
|
||||
enum {
|
||||
OPT_ARG_DIGIT_TIMEOUT,
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('d', OPT_DELAY),
|
||||
AST_APP_OPTION('e', OPT_EXTRAPULSES),
|
||||
AST_APP_OPTION('m', OPT_MUTE),
|
||||
AST_APP_OPTION_ARG('t', OPT_DIGIT_TIMEOUT, OPT_ARG_DIGIT_TIMEOUT),
|
||||
AST_APP_OPTION('q', OPT_QUELCH),
|
||||
AST_APP_OPTION('r', OPT_RELAXED),
|
||||
});
|
||||
@@ -172,6 +203,7 @@ static const char sendsf_name[] = "SendSF";
|
||||
* \param buf Buffer in which to store digits
|
||||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param digit_timeout ms to wait for the next digit before giving up
|
||||
* \param maxdigits Maximum number of digits
|
||||
* \param freq Frequency to use
|
||||
* \param features DSP features
|
||||
@@ -180,13 +212,8 @@ static const char sendsf_name[] = "SendSF";
|
||||
* \retval 0 if successful
|
||||
* \retval -1 if unsuccessful (including hangup).
|
||||
*/
|
||||
static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses) {
|
||||
/* Bell System Technical Journal 39 (Nov. 1960) */
|
||||
#define SF_MIN_OFF 25
|
||||
#define SF_ON 67
|
||||
#define SF_BETWEEN 600
|
||||
#define SF_MIN_DETECT 50
|
||||
|
||||
static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int digit_timeout, int maxdigits, int freq, int features, int extrapulses)
|
||||
{
|
||||
struct ast_dsp *dsp = NULL;
|
||||
struct ast_frame *frame = NULL;
|
||||
struct timeval start, pulsetimer, digittimer;
|
||||
@@ -212,6 +239,23 @@ static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int t
|
||||
if (timeout > 0) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
if (remaining_time <= 0) {
|
||||
ast_debug(1, "SF all-digit timer expired\n");
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If we haven't received a digit yet, don't apply the post-digit timer just yet,
|
||||
* since the sender may not have started sending any digits.
|
||||
*
|
||||
* Note that we use the digit timer, which is reset for each SF pulse,
|
||||
* as opposed to simply an entire digit being received.
|
||||
* This is done because we only want to expire the timer if there has been no activity
|
||||
* since the last digit. If we're in the middle of receiving a digit (e.g. 0)
|
||||
* we may not have a full digit yet but that should not cause an expiration. */
|
||||
if (digits_read > 0 && digit_timeout > 0) {
|
||||
int remaining_time_for_next_digit = ast_remaining_ms(digittimer, digit_timeout);
|
||||
if (remaining_time_for_next_digit <= 0) {
|
||||
ast_debug(1, "SF post-digit timer expired (>= %d ms since last SF pulse)\n", digit_timeout);
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT");
|
||||
break;
|
||||
}
|
||||
@@ -261,7 +305,7 @@ static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int t
|
||||
}
|
||||
} else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) {
|
||||
/* has the digit finished? */
|
||||
ast_debug(2, "Received SF digit: %d\n", hits);
|
||||
ast_debug(2, "Received SF digit: %d\n", hits == 10 ? 0 : hits); /* Edge case for 10, since this is the digit '0' */
|
||||
digits_read++;
|
||||
if (hits > 10) {
|
||||
if (extrapulses) {
|
||||
@@ -321,10 +365,11 @@ static int read_sf_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
#define BUFFER_SIZE 256
|
||||
char tmp[BUFFER_SIZE] = "";
|
||||
double tosec;
|
||||
double tosec, digitsec;
|
||||
struct ast_flags flags = {0};
|
||||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
char *argcopy = NULL;
|
||||
int res, features = 0, digits = 0, to = 0, freq = 2600;
|
||||
int res, features = 0, digits = 0, to = 0, digit_timeout = 0, freq = 2600;
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
@@ -344,7 +389,7 @@ static int read_sf_exec(struct ast_channel *chan, const char *data)
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
|
||||
ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.timeout)) {
|
||||
@@ -355,6 +400,18 @@ static int read_sf_exec(struct ast_channel *chan, const char *data)
|
||||
to = tosec * 1000.0;
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_DIGIT_TIMEOUT)) {
|
||||
digitsec = (!ast_strlen_zero(opt_args[OPT_ARG_DIGIT_TIMEOUT]) ? atof(opt_args[OPT_ARG_DIGIT_TIMEOUT]) : DEFAULT_POST_DIGIT_TIMER);
|
||||
if (digitsec <= 0) {
|
||||
digit_timeout = 0;
|
||||
} else {
|
||||
digit_timeout = digitsec * 1000.0;
|
||||
if (digit_timeout < SF_BETWEEN) {
|
||||
ast_log(LOG_WARNING, "SF post-digit timer (%d) cannot be less than the SF inter-digit timeout (%d ms)\n", digit_timeout, SF_BETWEEN);
|
||||
digit_timeout = SF_BETWEEN; /* If we have a shorter timeout, it won't work at all */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) {
|
||||
ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits);
|
||||
@@ -387,7 +444,7 @@ static int read_sf_exec(struct ast_channel *chan, const char *data)
|
||||
features |= DSP_DIGITMODE_RELAXDTMF;
|
||||
}
|
||||
|
||||
res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));
|
||||
res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digit_timeout, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (!ast_strlen_zero(tmp)) {
|
||||
ast_verb(3, "SF digits received: '%s'\n", tmp);
|
||||
|
@@ -177,6 +177,28 @@
|
||||
</example>
|
||||
</description>
|
||||
</function>
|
||||
<function name="DIGIT_SUM" language="en_US">
|
||||
<since>
|
||||
<version>23.1.0</version>
|
||||
<version>22.7.0</version>
|
||||
<version>20.17.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Returns the sum of all the digits in a number.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="num" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns the numeric sum of all the individual digits in a number, summed up.</para>
|
||||
<para>This can be useful for computing checksums based on the number,
|
||||
where errors are typically digits being off by one.</para>
|
||||
<example title="Get the sum of digits in 859">
|
||||
same => n,Set(digitsum=${DIGIT_SUM(859)}) ; assigns digitsum=22
|
||||
same => n,Set(checksum=$[${digitsum} % 10]) ; assigns checksum=2
|
||||
</example>
|
||||
</description>
|
||||
</function>
|
||||
***/
|
||||
|
||||
enum TypeOfFunctions {
|
||||
@@ -634,6 +656,27 @@ static int acf_abs_exec(struct ast_channel *chan, const char *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acf_digit_sum_exec(struct ast_channel *chan, const char *cmd,
|
||||
char *parse, char *buffer, size_t buflen)
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
if (ast_strlen_zero(parse)) {
|
||||
ast_log(LOG_WARNING, "Missing argument for number\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (; *parse; parse++) {
|
||||
if (*parse < '0' || *parse > '9') {
|
||||
continue;
|
||||
}
|
||||
sum += (*parse - '0');
|
||||
}
|
||||
|
||||
snprintf(buffer, buflen, "%d", sum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_custom_function math_function = {
|
||||
.name = "MATH",
|
||||
.read = math
|
||||
@@ -667,6 +710,11 @@ static struct ast_custom_function acf_abs = {
|
||||
.read_max = 12,
|
||||
};
|
||||
|
||||
static struct ast_custom_function acf_digit_sum = {
|
||||
.name = "DIGIT_SUM",
|
||||
.read = acf_digit_sum_exec,
|
||||
};
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_DEFINE(test_MATH_function)
|
||||
{
|
||||
@@ -728,6 +776,7 @@ static int unload_module(void)
|
||||
res |= ast_custom_function_unregister(&acf_min);
|
||||
res |= ast_custom_function_unregister(&acf_max);
|
||||
res |= ast_custom_function_unregister(&acf_abs);
|
||||
res |= ast_custom_function_unregister(&acf_digit_sum);
|
||||
AST_TEST_UNREGISTER(test_MATH_function);
|
||||
|
||||
return res;
|
||||
@@ -743,6 +792,7 @@ static int load_module(void)
|
||||
res |= ast_custom_function_register(&acf_min);
|
||||
res |= ast_custom_function_register(&acf_max);
|
||||
res |= ast_custom_function_register(&acf_abs);
|
||||
res |= ast_custom_function_register(&acf_digit_sum);
|
||||
AST_TEST_REGISTER(test_MATH_function);
|
||||
|
||||
return res;
|
||||
|
@@ -141,7 +141,17 @@ static int delete_channel(struct ast_channelstorage_instance *driver,
|
||||
/*! \brief returns number of active/allocated channels */
|
||||
static int active_channels(struct ast_channelstorage_instance *driver)
|
||||
{
|
||||
return driver ? getdb(driver).size() : 0;
|
||||
int count = 0;
|
||||
|
||||
if (!driver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rdlock(driver);
|
||||
count = getdb(driver).size();
|
||||
unlock(driver);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct ast_channel *callback(struct ast_channelstorage_instance *driver,
|
||||
@@ -454,14 +464,17 @@ static struct ast_channel *get_by_uniqueid(struct ast_channelstorage_instance *d
|
||||
{
|
||||
struct ast_channel *chan = NULL;
|
||||
char *search = uniqueid ? ast_str_to_lower(ast_strdupa(uniqueid)) : NULL;
|
||||
|
||||
if (ast_strlen_zero(uniqueid)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rdlock(driver);
|
||||
auto rtn = map_by_id(driver).find(search);
|
||||
if (rtn != map_by_id(driver).end()) {
|
||||
chan = ao2_bump((struct ast_channel *)rtn->second);
|
||||
}
|
||||
unlock(driver);
|
||||
|
||||
return chan;
|
||||
}
|
||||
@@ -469,16 +482,21 @@ static struct ast_channel *get_by_uniqueid(struct ast_channelstorage_instance *d
|
||||
static struct ast_channel *get_by_name_exact(struct ast_channelstorage_instance *driver,
|
||||
const char *name)
|
||||
{
|
||||
struct ast_channel *chan = NULL;
|
||||
char *search = name ? ast_str_to_lower(ast_strdupa(name)) : NULL;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
return NULL;
|
||||
}
|
||||
auto chan = getdb(driver).find(search);
|
||||
if (chan != getdb(driver).end()) {
|
||||
return ao2_bump((struct ast_channel *)chan->second);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
rdlock(driver);
|
||||
auto rtn = getdb(driver).find(search);
|
||||
if (rtn != getdb(driver).end()) {
|
||||
chan = ao2_bump((struct ast_channel *)rtn->second);
|
||||
}
|
||||
unlock(driver);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static struct ast_channel *get_by_name_prefix(struct ast_channelstorage_instance *driver,
|
||||
@@ -493,10 +511,14 @@ static struct ast_channel *get_by_name_prefix(struct ast_channelstorage_instance
|
||||
}
|
||||
|
||||
l_name = ast_str_to_lower(ast_strdupa(name));
|
||||
|
||||
rdlock(driver);
|
||||
auto rtn = getdb(driver).lower_bound(l_name);
|
||||
if (rtn != getdb(driver).end()) {
|
||||
chan = ao2_bump((struct ast_channel *)rtn->second);
|
||||
}
|
||||
unlock(driver);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
|
@@ -120,8 +120,44 @@
|
||||
<para>This application is provided by res_fax, which is a FAX technology agnostic module
|
||||
that utilizes FAX technology resource modules to complete a FAX transmission.</para>
|
||||
<para>Session arguments can be set by the FAXOPT function and to check results of the ReceiveFAX() application.</para>
|
||||
<variablelist>
|
||||
<variable name="FAXSTATUS">
|
||||
<para>Whether the fax transmission was successful</para>
|
||||
<value name="SUCCESS"/>
|
||||
<value name="FAILURE"/>
|
||||
</variable>
|
||||
<variable name="FAXERROR">
|
||||
<para>Description of what caused the fax to fail</para>
|
||||
<value name="MEMORY_ERROR"/>
|
||||
<value name="Channel Problems"/>
|
||||
</variable>
|
||||
<variable name="FAXSTATUSSTRING">
|
||||
<para>Detailed description of the status of the fax transmission</para>
|
||||
</variable>
|
||||
<variable name="LOCALSTATIONID">
|
||||
<para>Local Station ID</para>
|
||||
</variable>
|
||||
<variable name="REMOTESTATIONID">
|
||||
<para>Remote Station ID</para>
|
||||
</variable>
|
||||
<variable name="FAXPAGES">
|
||||
<para>Number of pages in the fax</para>
|
||||
</variable>
|
||||
<variable name="FAXBITRATE">
|
||||
<para>Bit rate of the fax transmission</para>
|
||||
</variable>
|
||||
<variable name="FAXRESOLUTION">
|
||||
<para>Resolution of the fax document</para>
|
||||
</variable>
|
||||
<variable name="FAXMODE">
|
||||
<para>Fax transmission mode</para>
|
||||
<value name="audio"/>
|
||||
<value name="T38"/>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendFAX</ref>
|
||||
<ref type="function">FAXOPT</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
@@ -162,8 +198,44 @@
|
||||
<para>This application is provided by res_fax, which is a FAX technology agnostic module
|
||||
that utilizes FAX technology resource modules to complete a FAX transmission.</para>
|
||||
<para>Session arguments can be set by the FAXOPT function and to check results of the SendFAX() application.</para>
|
||||
<variablelist>
|
||||
<variable name="FAXSTATUS">
|
||||
<para>Whether the fax transmission was successful</para>
|
||||
<value name="SUCCESS"/>
|
||||
<value name="FAILURE"/>
|
||||
</variable>
|
||||
<variable name="FAXERROR">
|
||||
<para>Description of what caused the fax to fail</para>
|
||||
<value name="MEMORY_ERROR"/>
|
||||
<value name="Channel Problems"/>
|
||||
</variable>
|
||||
<variable name="FAXSTATUSSTRING">
|
||||
<para>Detailed description of the status of the fax transmission</para>
|
||||
</variable>
|
||||
<variable name="LOCALSTATIONID">
|
||||
<para>Local Station ID</para>
|
||||
</variable>
|
||||
<variable name="REMOTESTATIONID">
|
||||
<para>Remote Station ID</para>
|
||||
</variable>
|
||||
<variable name="FAXPAGES">
|
||||
<para>Number of pages in the fax</para>
|
||||
</variable>
|
||||
<variable name="FAXBITRATE">
|
||||
<para>Bit rate of the fax transmission</para>
|
||||
</variable>
|
||||
<variable name="FAXRESOLUTION">
|
||||
<para>Resolution of the fax document</para>
|
||||
</variable>
|
||||
<variable name="FAXMODE">
|
||||
<para>Fax transmission mode</para>
|
||||
<value name="audio"/>
|
||||
<value name="T38"/>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ReceiveFAX</ref>
|
||||
<ref type="function">FAXOPT</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
@@ -84,14 +84,14 @@
|
||||
<description>
|
||||
<para>Waits for a single-frequency tone to be detected before dialplan execution continues.</para>
|
||||
<variablelist>
|
||||
<variable name="WAITFORTONESTATUS">
|
||||
<para>This indicates the result of the wait.</para>
|
||||
<value name="SUCCESS"/>
|
||||
<value name="ERROR"/>
|
||||
<value name="TIMEOUT"/>
|
||||
<value name="HANGUP"/>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<variable name="WAITFORTONESTATUS">
|
||||
<para>This indicates the result of the wait.</para>
|
||||
<value name="SUCCESS"/>
|
||||
<value name="ERROR"/>
|
||||
<value name="TIMEOUT"/>
|
||||
<value name="HANGUP"/>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">PlayTones</ref>
|
||||
@@ -135,43 +135,43 @@
|
||||
Unlike a conventional scanner, this is not currently capable of
|
||||
scanning for modem carriers.</para>
|
||||
<variablelist>
|
||||
<variable name="TONESCANSTATUS">
|
||||
This indicates the result of the scan.
|
||||
<value name="RINGING">
|
||||
Audible ringback tone
|
||||
</value>
|
||||
<value name="BUSY">
|
||||
Busy tone
|
||||
</value>
|
||||
<value name="SIT">
|
||||
Special Information Tones
|
||||
</value>
|
||||
<value name="VOICE">
|
||||
Human voice detected
|
||||
</value>
|
||||
<value name="DTMF">
|
||||
DTMF digit
|
||||
</value>
|
||||
<value name="FAX">
|
||||
Fax (answering)
|
||||
</value>
|
||||
<value name="MODEM">
|
||||
Modem (answering)
|
||||
</value>
|
||||
<value name="DIALTONE">
|
||||
Dial tone
|
||||
</value>
|
||||
<value name="NUT">
|
||||
UK Number Unobtainable tone
|
||||
</value>
|
||||
<value name="TIMEOUT">
|
||||
Timeout reached before any positive detection
|
||||
</value>
|
||||
<value name="HANGUP">
|
||||
Caller hung up before any positive detection
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<variable name="TONESCANSTATUS">
|
||||
This indicates the result of the scan.
|
||||
<value name="RINGING">
|
||||
Audible ringback tone
|
||||
</value>
|
||||
<value name="BUSY">
|
||||
Busy tone
|
||||
</value>
|
||||
<value name="SIT">
|
||||
Special Information Tones
|
||||
</value>
|
||||
<value name="VOICE">
|
||||
Human voice detected
|
||||
</value>
|
||||
<value name="DTMF">
|
||||
DTMF digit
|
||||
</value>
|
||||
<value name="FAX">
|
||||
Fax (answering)
|
||||
</value>
|
||||
<value name="MODEM">
|
||||
Modem (answering)
|
||||
</value>
|
||||
<value name="DIALTONE">
|
||||
Dial tone
|
||||
</value>
|
||||
<value name="NUT">
|
||||
UK Number Unobtainable tone
|
||||
</value>
|
||||
<value name="TIMEOUT">
|
||||
Timeout reached before any positive detection
|
||||
</value>
|
||||
<value name="HANGUP">
|
||||
Caller hung up before any positive detection
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">WaitForTone</ref>
|
||||
|
Reference in New Issue
Block a user