freetdm: ss7 - added support for sending sub-address values
freetdm: ss7 - switch back to sending RSC at startup rather then GRS
This commit is contained in:
parent
38a64b696d
commit
89351e0252
|
@ -1332,7 +1332,7 @@ static ftdm_status_t ftdm_sangoma_ss7_start(ftdm_span_t * span)
|
||||||
sngss7_clear_flag(sngss7_info, FLAG_INFID_PAUSED);
|
sngss7_clear_flag(sngss7_info, FLAG_INFID_PAUSED);
|
||||||
sngss7_set_flag(sngss7_info, FLAG_INFID_RESUME);
|
sngss7_set_flag(sngss7_info, FLAG_INFID_RESUME);
|
||||||
}
|
}
|
||||||
#if 1
|
#if 0
|
||||||
/* throw the grp reset flag */
|
/* throw the grp reset flag */
|
||||||
sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_TX);
|
sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_TX);
|
||||||
if (x == 1) {
|
if (x == 1) {
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
|
|
||||||
#define SNGSS7_EVENT_QUEUE_SIZE 100
|
#define SNGSS7_EVENT_QUEUE_SIZE 100
|
||||||
|
|
||||||
|
#define MAX_SIZEOF_SUBADDR_IE 24 /* as per Q931 4.5.9 */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SNGSS7_CON_IND_EVENT = 0,
|
SNGSS7_CON_IND_EVENT = 0,
|
||||||
SNGSS7_CON_CFM_EVENT,
|
SNGSS7_CON_CFM_EVENT,
|
||||||
|
@ -83,6 +85,11 @@ typedef enum {
|
||||||
SNGSS7_PAUSED = (1 << 7)
|
SNGSS7_PAUSED = (1 << 7)
|
||||||
} sng_flag_t;
|
} sng_flag_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SNG_CALLED = 1,
|
||||||
|
SNG_CALLING = 2
|
||||||
|
} sng_addr_type_t;
|
||||||
|
|
||||||
typedef struct sng_mtp_link {
|
typedef struct sng_mtp_link {
|
||||||
char name[MAX_NAME_LEN];
|
char name[MAX_NAME_LEN];
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
@ -579,6 +586,8 @@ ftdm_status_t clear_rx_grs_data(sngss7_chan_data_t *sngss7_info);
|
||||||
ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info);
|
ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info);
|
||||||
ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info);
|
ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info);
|
||||||
|
|
||||||
|
ftdm_status_t encode_subAddrIE_nsap(const char *subAddr, char *subAddrIE, int type);
|
||||||
|
ftdm_status_t encode_subAddrIE_nat(const char *subAddr, char *subAddrIE, int type);
|
||||||
|
|
||||||
/* in ftmod_sangoma_ss7_timers.c */
|
/* in ftmod_sangoma_ss7_timers.c */
|
||||||
void handle_isup_t35(void *userdata);
|
void handle_isup_t35(void *userdata);
|
||||||
|
|
|
@ -76,6 +76,9 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan)
|
||||||
sngss7_chan_data_t *sngss7_info = ftdmchan->call_data;;
|
sngss7_chan_data_t *sngss7_info = ftdmchan->call_data;;
|
||||||
const char *clg_nadi = NULL;
|
const char *clg_nadi = NULL;
|
||||||
const char *cld_nadi = NULL;
|
const char *cld_nadi = NULL;
|
||||||
|
const char *clg_subAddr = NULL;
|
||||||
|
const char *cld_subAddr = NULL;
|
||||||
|
char subAddrIE[MAX_SIZEOF_SUBADDR_IE];
|
||||||
SiConEvnt iam;
|
SiConEvnt iam;
|
||||||
|
|
||||||
sngss7_info->suInstId = get_unique_id ();
|
sngss7_info->suInstId = get_unique_id ();
|
||||||
|
@ -186,7 +189,7 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan)
|
||||||
/* check if the user would like a custom NADI value for the calling Pty Num */
|
/* check if the user would like a custom NADI value for the calling Pty Num */
|
||||||
clg_nadi = ftdm_channel_get_var(ftdmchan, "ss7_clg_nadi");
|
clg_nadi = ftdm_channel_get_var(ftdmchan, "ss7_clg_nadi");
|
||||||
if ((clg_nadi != NULL) && (*clg_nadi)) {
|
if ((clg_nadi != NULL) && (*clg_nadi)) {
|
||||||
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied NADI value \"%s\"\n", clg_nadi);
|
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied Calling NADI value \"%s\"\n", clg_nadi);
|
||||||
iam.cgPtyNum.natAddrInd.val = atoi(clg_nadi);
|
iam.cgPtyNum.natAddrInd.val = atoi(clg_nadi);
|
||||||
} else {
|
} else {
|
||||||
iam.cgPtyNum.natAddrInd.val = g_ftdm_sngss7_data.cfg.isupIntf[sngss7_info->circuit->infId].clg_nadi;
|
iam.cgPtyNum.natAddrInd.val = g_ftdm_sngss7_data.cfg.isupIntf[sngss7_info->circuit->infId].clg_nadi;
|
||||||
|
@ -195,14 +198,93 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan)
|
||||||
|
|
||||||
cld_nadi = ftdm_channel_get_var(ftdmchan, "ss7_cld_nadi");
|
cld_nadi = ftdm_channel_get_var(ftdmchan, "ss7_cld_nadi");
|
||||||
if ((cld_nadi != NULL) && (*cld_nadi)) {
|
if ((cld_nadi != NULL) && (*cld_nadi)) {
|
||||||
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied NADI value \"%s\"\n", cld_nadi);
|
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied Called NADI value \"%s\"\n", cld_nadi);
|
||||||
iam.cdPtyNum.natAddrInd.val = atoi(cld_nadi);
|
iam.cdPtyNum.natAddrInd.val = atoi(cld_nadi);
|
||||||
} else {
|
} else {
|
||||||
iam.cdPtyNum.natAddrInd.val = g_ftdm_sngss7_data.cfg.isupIntf[sngss7_info->circuit->infId].cld_nadi;
|
iam.cdPtyNum.natAddrInd.val = g_ftdm_sngss7_data.cfg.isupIntf[sngss7_info->circuit->infId].cld_nadi;
|
||||||
SS7_DEBUG_CHAN(ftdmchan,"No user supplied NADI value found for CLD, using \"%d\"\n", iam.cdPtyNum.natAddrInd.val);
|
SS7_DEBUG_CHAN(ftdmchan,"No user supplied NADI value found for CLD, using \"%d\"\n", iam.cdPtyNum.natAddrInd.val);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check if the user would like us to send a clg_sub-address */
|
||||||
|
clg_subAddr = ftdm_channel_get_var(ftdmchan, "ss7_clg_subaddr");
|
||||||
|
if ((clg_subAddr != NULL) && (*clg_subAddr)) {
|
||||||
|
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied Calling Sub-Address value \"%s\"\n", clg_subAddr);
|
||||||
|
|
||||||
|
/* clean out the subAddrIE */
|
||||||
|
memset(subAddrIE, 0x0, sizeof(subAddrIE));
|
||||||
|
|
||||||
|
/* check the first character in the sub-address to see what type of encoding to use */
|
||||||
|
switch (clg_subAddr[0]) {
|
||||||
|
case '0': /* NSAP */
|
||||||
|
encode_subAddrIE_nsap(&clg_subAddr[1], subAddrIE, SNG_CALLING);
|
||||||
|
break;
|
||||||
|
case '1': /* national variant */
|
||||||
|
encode_subAddrIE_nat(&clg_subAddr[1], subAddrIE, SNG_CALLING);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SS7_ERROR_CHAN(ftdmchan,"Invalid Calling Sub-Address encoding requested: %c\n", clg_subAddr[0]);
|
||||||
|
break;
|
||||||
|
} /* switch (cld_subAddr[0]) */
|
||||||
|
|
||||||
|
|
||||||
|
/* if subaddIE is still empty don't copy it in */
|
||||||
|
if (subAddrIE[0] != '0') {
|
||||||
|
/* check if the clg_subAddr has already been added */
|
||||||
|
if (iam.accTrnspt.eh.pres == PRSNT_NODEF) {
|
||||||
|
/* append the subAddrIE */
|
||||||
|
memcpy(&iam.accTrnspt.infoElmts.val[iam.accTrnspt.infoElmts.len], subAddrIE, (subAddrIE[1] + 2));
|
||||||
|
iam.accTrnspt.infoElmts.len = iam.accTrnspt.infoElmts.len +subAddrIE[1] + 2;
|
||||||
|
} else {
|
||||||
|
/* fill in from the beginning */
|
||||||
|
iam.accTrnspt.eh.pres = PRSNT_NODEF;
|
||||||
|
iam.accTrnspt.infoElmts.pres = PRSNT_NODEF;
|
||||||
|
memcpy(iam.accTrnspt.infoElmts.val, subAddrIE, (subAddrIE[1] + 2));
|
||||||
|
iam.accTrnspt.infoElmts.len = subAddrIE[1] + 2;
|
||||||
|
} /* if (iam.accTrnspt.eh.pres */
|
||||||
|
} /* if (subAddrIE[0] != '0') */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if the user would like us to send a cld_sub-address */
|
||||||
|
cld_subAddr = ftdm_channel_get_var(ftdmchan, "ss7_cld_subaddr");
|
||||||
|
if ((cld_subAddr != NULL) && (*cld_subAddr)) {
|
||||||
|
SS7_DEBUG_CHAN(ftdmchan,"Found user supplied Called Sub-Address value \"%s\"\n", cld_subAddr);
|
||||||
|
|
||||||
|
/* clean out the subAddrIE */
|
||||||
|
memset(subAddrIE, 0x0, sizeof(subAddrIE));
|
||||||
|
|
||||||
|
/* check the first character in the sub-address to see what type of encoding to use */
|
||||||
|
switch (cld_subAddr[0]) {
|
||||||
|
case '0': /* NSAP */
|
||||||
|
encode_subAddrIE_nsap(&cld_subAddr[1], subAddrIE, SNG_CALLED);
|
||||||
|
break;
|
||||||
|
case '1': /* national variant */
|
||||||
|
encode_subAddrIE_nat(&cld_subAddr[1], subAddrIE, SNG_CALLED);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SS7_ERROR_CHAN(ftdmchan,"Invalid Called Sub-Address encoding requested: %c\n", cld_subAddr[0]);
|
||||||
|
break;
|
||||||
|
} /* switch (cld_subAddr[0]) */
|
||||||
|
|
||||||
|
/* if subaddIE is still empty don't copy it in */
|
||||||
|
if (subAddrIE[0] != '0') {
|
||||||
|
/* check if the cld_subAddr has already been added */
|
||||||
|
if (iam.accTrnspt.eh.pres == PRSNT_NODEF) {
|
||||||
|
/* append the subAddrIE */
|
||||||
|
memcpy(&iam.accTrnspt.infoElmts.val[iam.accTrnspt.infoElmts.len], subAddrIE, (subAddrIE[1] + 2));
|
||||||
|
iam.accTrnspt.infoElmts.len = iam.accTrnspt.infoElmts.len +subAddrIE[1] + 2;
|
||||||
|
} else {
|
||||||
|
/* fill in from the beginning */
|
||||||
|
iam.accTrnspt.eh.pres = PRSNT_NODEF;
|
||||||
|
iam.accTrnspt.infoElmts.pres = PRSNT_NODEF;
|
||||||
|
memcpy(iam.accTrnspt.infoElmts.val, subAddrIE, (subAddrIE[1] + 2));
|
||||||
|
iam.accTrnspt.infoElmts.len = subAddrIE[1] + 2;
|
||||||
|
} /* if (iam.accTrnspt.eh.pres */
|
||||||
|
} /* if (subAddrIE[0] != '0') */
|
||||||
|
} /* if ((cld_subAddr != NULL) && (*cld_subAddr)) */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sng_cc_con_request (sngss7_info->spId,
|
sng_cc_con_request (sngss7_info->spId,
|
||||||
sngss7_info->suInstId,
|
sngss7_info->suInstId,
|
||||||
sngss7_info->spInstId,
|
sngss7_info->spInstId,
|
||||||
|
|
|
@ -71,6 +71,9 @@ ftdm_status_t clear_tx_rsc_flags(sngss7_chan_data_t *sngss7_info);
|
||||||
ftdm_status_t clear_rx_grs_data(sngss7_chan_data_t *sngss7_info);
|
ftdm_status_t clear_rx_grs_data(sngss7_chan_data_t *sngss7_info);
|
||||||
ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info);
|
ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info);
|
||||||
ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info);
|
ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info);
|
||||||
|
|
||||||
|
ftdm_status_t encode_subAddrIE_nsap(const char *subAddr, char *subAddrIE, int type);
|
||||||
|
ftdm_status_t encode_subAddrIE_nat(const char *subAddr, char *subAddrIE, int type);
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
/* FUNCTIONS ******************************************************************/
|
/* FUNCTIONS ******************************************************************/
|
||||||
|
@ -884,6 +887,214 @@ ftdm_status_t clear_tx_rsc_flags(sngss7_chan_data_t *sngss7_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
ftdm_status_t encode_subAddrIE_nsap(const char *subAddr, char *subAddrIE, int type)
|
||||||
|
{
|
||||||
|
/* Q931 4.5.9
|
||||||
|
* 8 7 6 5 4 3 2 1 (octet)
|
||||||
|
*
|
||||||
|
* 0 1 1 1 0 0 0 1 (spare 8) ( IE id 1-7)
|
||||||
|
* X X X X X X X X (length of IE contents)
|
||||||
|
* 1 0 0 0 Z 0 0 0 (ext 8) (NSAP type 5-7) (odd/even 4) (spare 1-3)
|
||||||
|
* X X X X X X X X (sub address encoded in ia5)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int p = 0;
|
||||||
|
int len = 0;
|
||||||
|
char tmp[2];
|
||||||
|
|
||||||
|
/* initalize the second element of tmp to \0 so that atoi doesn't go to far */
|
||||||
|
tmp[1]='\0';
|
||||||
|
|
||||||
|
/* set octet 1 aka IE id */
|
||||||
|
p = 0;
|
||||||
|
switch(type) {
|
||||||
|
/**************************************************************************/
|
||||||
|
case SNG_CALLED: /* called party sub address */
|
||||||
|
subAddrIE[p] = 0x71;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
case SNG_CALLING: /* calling party sub address */
|
||||||
|
subAddrIE[p] = 0x6d;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
default: /* not good */
|
||||||
|
SS7_ERROR("Sub-Address type is invalid: %d\n", type);
|
||||||
|
return FTDM_FAIL;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
} /* switch(type) */
|
||||||
|
|
||||||
|
/* set octet 3 aka type and o/e */
|
||||||
|
p = 2;
|
||||||
|
subAddrIE[p] = 0x80;
|
||||||
|
|
||||||
|
/* set the subAddrIE pointer octet 4 */
|
||||||
|
p = 3;
|
||||||
|
|
||||||
|
/* loop through all digits in subAddr and insert them into subAddrIE */
|
||||||
|
while (subAddr[x] != '\0') {
|
||||||
|
|
||||||
|
/* grab a character */
|
||||||
|
tmp[0] = subAddr[x];
|
||||||
|
|
||||||
|
/* confirm it is a digit */
|
||||||
|
if (!isdigit(tmp[0])) {
|
||||||
|
/* move to the next character in subAddr */
|
||||||
|
x++;
|
||||||
|
|
||||||
|
/* restart the loop */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert the character to IA5 encoding and write into subAddrIE */
|
||||||
|
subAddrIE[p] = atoi(&tmp[0]); /* lower nibble is the digit */
|
||||||
|
subAddrIE[p] |= 0x3 << 4; /* upper nibble is 0x3 */
|
||||||
|
|
||||||
|
/* increment address length counter */
|
||||||
|
len++;
|
||||||
|
|
||||||
|
/* increment the subAddrIE pointer */
|
||||||
|
p++;
|
||||||
|
|
||||||
|
/* move to the next character in subAddr */
|
||||||
|
x++;
|
||||||
|
|
||||||
|
} /* while (subAddr[x] != '\0') */
|
||||||
|
|
||||||
|
/* set octet 2 aka length of subaddr */
|
||||||
|
p = 1;
|
||||||
|
subAddrIE[p] = len + 1;
|
||||||
|
|
||||||
|
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
ftdm_status_t encode_subAddrIE_nat(const char *subAddr, char *subAddrIE, int type)
|
||||||
|
{
|
||||||
|
/* Q931 4.5.9
|
||||||
|
* 8 7 6 5 4 3 2 1 (octet)
|
||||||
|
*
|
||||||
|
* 0 1 1 1 0 0 0 1 (spare 8) ( IE id 1-7)
|
||||||
|
* X X X X X X X X (length of IE contents)
|
||||||
|
* 1 0 0 0 Z 0 0 0 (ext 8) (NSAP type 5-7) (odd/even 4) (spare 1-3)
|
||||||
|
* X X X X X X X X (sub address encoded in ia5)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int p = 0;
|
||||||
|
int len = 0;
|
||||||
|
char tmp[2];
|
||||||
|
int flag = 0;
|
||||||
|
int odd = 0;
|
||||||
|
uint8_t lower = 0x0;
|
||||||
|
uint8_t upper = 0x0;
|
||||||
|
|
||||||
|
/* initalize the second element of tmp to \0 so that atoi doesn't go to far */
|
||||||
|
tmp[1]='\0';
|
||||||
|
|
||||||
|
/* set octet 1 aka IE id */
|
||||||
|
p = 0;
|
||||||
|
switch(type) {
|
||||||
|
/**************************************************************************/
|
||||||
|
case SNG_CALLED: /* called party sub address */
|
||||||
|
subAddrIE[p] = 0x71;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
case SNG_CALLING: /* calling party sub address */
|
||||||
|
subAddrIE[p] = 0x6d;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
default: /* not good */
|
||||||
|
SS7_ERROR("Sub-Address type is invalid: %d\n", type);
|
||||||
|
return FTDM_FAIL;
|
||||||
|
break;
|
||||||
|
/**************************************************************************/
|
||||||
|
} /* switch(type) */
|
||||||
|
|
||||||
|
/* set the subAddrIE pointer octet 4 */
|
||||||
|
p = 3;
|
||||||
|
|
||||||
|
/* loop through all digits in subAddr and insert them into subAddrIE */
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* grab a character */
|
||||||
|
tmp[0] = subAddr[x];
|
||||||
|
|
||||||
|
/* confirm it is a hex digit */
|
||||||
|
while ((!isxdigit(tmp[0])) && (tmp[0] != '\0')) {
|
||||||
|
/* move to the next character in subAddr */
|
||||||
|
x++;
|
||||||
|
tmp[0] = subAddr[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if tmp is null or a digit */
|
||||||
|
if (tmp[0] != '\0') {
|
||||||
|
/* push it into the lower nibble using strtol to allow a-f chars */
|
||||||
|
lower = strtol(&tmp[0], (char **)NULL, 16);
|
||||||
|
/* move to the next digit */
|
||||||
|
x++;
|
||||||
|
/* grab a digit from the ftdm digits */
|
||||||
|
tmp[0] = subAddr[x];
|
||||||
|
|
||||||
|
/* check if the digit is a hex digit and that is not null */
|
||||||
|
while (!(isxdigit(tmp[0])) && (tmp[0] != '\0')) {
|
||||||
|
x++;
|
||||||
|
tmp[0] = subAddr[x];
|
||||||
|
} /* while(!(isdigit(tmp))) */
|
||||||
|
|
||||||
|
/* check if tmp is null or a digit */
|
||||||
|
if (tmp[0] != '\0') {
|
||||||
|
/* push the digit into the upper nibble using strtol to allow a-f chars */
|
||||||
|
upper = (strtol(&tmp[0], (char **)NULL, 16)) << 4;
|
||||||
|
} else {
|
||||||
|
/* there is no upper ... fill in spare */
|
||||||
|
upper = 0x00;
|
||||||
|
/* throw the odd flag since we need to buffer */
|
||||||
|
odd = 1;
|
||||||
|
/* throw the end flag */
|
||||||
|
flag = 1;
|
||||||
|
} /* if (tmp != '\0') */
|
||||||
|
} else {
|
||||||
|
/* keep the odd flag down */
|
||||||
|
odd = 0;
|
||||||
|
|
||||||
|
/* throw the flag */
|
||||||
|
flag = 1;
|
||||||
|
|
||||||
|
/* bounce out right away */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill in the octet */
|
||||||
|
subAddrIE[p] = upper | lower;
|
||||||
|
|
||||||
|
/* increment address length counter */
|
||||||
|
len++;
|
||||||
|
|
||||||
|
/* if the flag is we're through all the digits */
|
||||||
|
if (flag) break;
|
||||||
|
|
||||||
|
/* increment the subAddrIE pointer */
|
||||||
|
p++;
|
||||||
|
|
||||||
|
/* move to the next character in subAddr */
|
||||||
|
x++;
|
||||||
|
|
||||||
|
} /* while (subAddr[x] != '\0') */
|
||||||
|
|
||||||
|
/* set octet 2 aka length of subaddr */
|
||||||
|
p = 1;
|
||||||
|
subAddrIE[p] = len + 1;
|
||||||
|
|
||||||
|
/* set octet 3 aka type and o/e */
|
||||||
|
p = 2;
|
||||||
|
subAddrIE[p] = 0xa0 | (odd << 3);
|
||||||
|
|
||||||
|
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* For Emacs:
|
/* For Emacs:
|
||||||
|
|
Loading…
Reference in New Issue