You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/mohqueue/mohq_funcs.c

2928 lines
64 KiB

/*
* Copyright (C) 2013-16 Robert Boisvert
*
* This file is part of the mohqueue module for Kamailio, a free SIP server.
*
* The mohqueue module is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* The mohqueue module is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <stdarg.h>
#include "mohq.h"
#include "mohq_db.h"
#include "mohq_funcs.h"
/**********
* definitions
**********/
#define ALLOWHDR "Allow: INVITE, ACK, BYE, CANCEL, NOTIFY, PRACK"
#define CLENHDR "Content-Length"
#define SIPEOL "\r\n"
#define USRAGNT "Kamailio MOH Queue v1.3"
/**********
* local constants
**********/
str p100rel [1] = {STR_STATIC_INIT ("100rel")};
str pallq [1] = {STR_STATIC_INIT ("*")};
str paudio [1] = {STR_STATIC_INIT ("audio")};
str pbye [1] = {STR_STATIC_INIT ("BYE")};
str pinvite [1] = {STR_STATIC_INIT ("INVITE")};
str pmi_nolock [1] = {STR_STATIC_INIT ("Unable to lock queue")};
str pmi_noqueue [1] = {STR_STATIC_INIT ("No matching queue name found")};
str prefer [1] = {STR_STATIC_INIT ("REFER")};
str presp_busy [1] = {STR_STATIC_INIT ("Busy Here")};
str presp_noaccept [1] = {STR_STATIC_INIT ("Not Acceptable Here")};
str presp_noallow [1] = {STR_STATIC_INIT ("Method Not Allowed")};
str presp_nocall [1] = {STR_STATIC_INIT ("Call/Transaction Does Not Exist")};
str presp_ok [1] = {STR_STATIC_INIT ("OK")};
str presp_reqpend [1] = {STR_STATIC_INIT ("Request Pending")};
str presp_reqterm [1] = {STR_STATIC_INIT ("Request Terminated")};
str presp_ring [1] = {STR_STATIC_INIT ("Ringing")};
str psipfrag [1] = {STR_STATIC_INIT ("message/sipfrag")};
str presp_srverr [1] = {STR_STATIC_INIT ("Server Internal Error")};
str presp_trying [1] = {STR_STATIC_INIT ("Trying to enter MOH queue")};
str presp_unsupp [1] = {STR_STATIC_INIT ("Unsupported Media Type")};
rtpmap prtpmap [] =
{
{9, "G722/8000"},
{0, "PCMU/8000"},
{8, "PCMA/8000"},
{18, "G729/8000"},
{3, "GSM/8000"},
{4, "G723/8000"},
{15, "G728/8000"},
{5, "DVI4/8000"},
{7, "LPC/8000"},
{12, "QCELP/8000"},
{13, "CN/8000"},
{16, "DVI4/11025"},
{6, "DVI4/16000"},
{17, "DVI4/22050"},
{10, "L16/44100"},
{11, "L16/44100"},
{14, "MPA/90000"},
{0, 0}
};
rtpmap *pmohfiles [30]; // element count should be equal or greater than prtpmap
str pallowhdr [1] = { STR_STATIC_INIT (ALLOWHDR SIPEOL) };
char pbyemsg [] =
{
"%s"
"%s"
"Max-Forwards: 70" SIPEOL
"Contact: <%s>" SIPEOL
};
str pextrahdr [1] =
{
STR_STATIC_INIT (
ALLOWHDR SIPEOL
"Supported: 100rel" SIPEOL
"Accept-Language: en" SIPEOL
"Content-Type: application/sdp" SIPEOL
)
};
char pinvitesdp [] =
{
"v=0" SIPEOL
"o=- %d %d IN %s" SIPEOL
"s=" USRAGNT SIPEOL
"c=IN %s" SIPEOL
"t=0 0" SIPEOL
"a=send%s" SIPEOL
"m=audio %d RTP/AVP "
};
char prefermsg [] =
{
"%s"
"%s"
"Contact: <%s>" SIPEOL
"Max-Forwards: 70" SIPEOL
"Refer-To: <%s>" SIPEOL
"Referred-By: <%s>" SIPEOL
};
char preinvitemsg [] =
{
"%s"
"Max-Forwards: 70" SIPEOL
"Contact: <%s>" SIPEOL
ALLOWHDR SIPEOL
"Supported: 100rel" SIPEOL
"Accept-Language: en" SIPEOL
"Content-Type: application/sdp" SIPEOL
};
char prtpsdp [] =
{
"v=0" SIPEOL
// IP address and audio port faked since they will be replaced
"o=- 1 1 IN IP4 1.1.1.1" SIPEOL
"s=" USRAGNT SIPEOL
"c=IN IP4 1.1.1.1" SIPEOL
"t=0 0" SIPEOL
"a=sendrecv" SIPEOL
"m=audio 1 RTP/AVP"
};
/**********
* local function declarations
**********/
void delete_call (call_lst *);
void end_RTP (sip_msg_t *, call_lst *);
dlg_t *form_dialog (call_lst *, struct to_body *);
int form_rtp_SDP (str *, call_lst *, char *);
static void invite_cb (struct cell *, int, struct tmcb_params *);
int refer_call (call_lst *, mohq_lock *);
static void refer_cb (struct cell *, int, struct tmcb_params *);
int send_prov_rsp (sip_msg_t *, call_lst *);
int send_rtp_answer (sip_msg_t *, call_lst *);
int search_hdr_ext (struct hdr_field *, str *);
int start_stream (sip_msg_t *, call_lst *, int);
int stop_stream (sip_msg_t *, call_lst *, int);
/**********
* local functions
**********/
/**********
* Process ACK Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void ack_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* part of INVITE?
**********/
char *pfncname = "ack_msg: ";
struct cell *ptrans;
tm_api_t *ptm = pmod_data->ptm;
if (pcall->call_state != CLSTA_INVITED)
{
/**********
* ignore if from rejected re-INVITE
**********/
if (pcall->call_state != CLSTA_INQUEUE)
{ LM_ERR ("%sUnexpected ACK (%s)!\n", pfncname, pcall->call_from); }
else
{
mohq_debug (pcall->pmohq, "%sACK from refused re-INVITE (%s)!\n",
pfncname, pcall->call_from);
}
return;
}
/**********
* o release INVITE transaction
* o save SDP address info
* o put in queue
**********/
if (ptm->t_lookup_ident (&ptrans, pcall->call_hash, pcall->call_label) < 0)
{
LM_ERR ("%sINVITE transaction missing for call (%s)!\n",
pfncname, pcall->call_from);
return;
}
else
{
if (ptm->t_release (pcall->call_pmsg) < 0)
{
LM_ERR ("%sRelease transaction failed for call (%s)!\n",
pfncname, pcall->call_from);
return;
}
}
pcall->call_hash = pcall->call_label = 0;
sprintf (pcall->call_addr, "%s %s",
pmsg->rcv.dst_ip.af == AF_INET ? "IP4" : "IP6",
ip_addr2a (&pmsg->rcv.dst_ip));
pcall->call_state = CLSTA_INQUEUE;
update_call_rec (pcall);
pcall->call_cseq = 1;
mohq_debug (pcall->pmohq,
"%sACK received for call (%s); placed in queue (%s)",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
return;
}
/**********
* Add String to Buffer
*
* INPUT:
* Arg (1) = string pointer
* Arg (2) = string length
* Arg (3) = pointer to buffer pointer
* Arg (4) = pointer to buffer size
* Arg (5) = add NUL flag
* OUTPUT: =0 if insufficent space
* bufpointer incremented, size decremented
**********/
int addstrbfr (char *pstr, size_t nlen, char **pbuf, size_t *nmax, int bnull)
{
/**********
* o enough space?
* o copy string
* o adjust position/size
**********/
size_t nsize = nlen;
if (bnull)
{ nsize++; }
if (nsize > *nmax)
{ return 0; }
if (nlen)
{
strncpy (*pbuf, pstr, nlen);
*pbuf += nlen;
}
if (bnull)
{
**pbuf = '\0';
(*pbuf)++;
}
*nmax -= nsize;
return 1;
}
/**********
* BYE Callback
*
* INPUT:
* Arg (1) = cell pointer
* Arg (2) = callback type
* Arg (3) = callback parms
* OUTPUT: none
**********/
static void bye_cb
(struct cell *ptrans, int ntype, struct tmcb_params *pcbp)
{
/**********
* o error means must have hung after REFER
* o delete the call
**********/
char *pfncname = "bye_cb: ";
call_lst *pcall = (call_lst *)*pcbp->param;
if (ntype == TMCB_ON_FAILURE)
{
LM_ERR ("%sCall (%s) did not respond to BYE!\n", pfncname,
pcall->call_from);
}
else
{
int nreply = pcbp->code;
if ((nreply / 100) != 2)
{
LM_ERR ("%sCall (%s) BYE error (%d)!\n", pfncname,
pcall->call_from, nreply);
}
else
{
mohq_debug (pcall->pmohq, "%sCall (%s) BYE reply=%d", pfncname,
pcall->call_from, nreply);
}
}
delete_call (pcall);
return;
}
/**********
* Process BYE Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void bye_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* o responded?
* o teardown RTP
**********/
char *pfncname = "bye_msg: ";
if (pcall->call_state == CLSTA_BYEOK)
{ return; }
if (pcall->call_state >= CLSTA_INQUEUE)
{
pcall->call_state = CLSTA_BYEOK;
end_RTP (pmsg, pcall);
}
else
{
LM_ERR ("%sEnding call (%s) before placed in queue!\n",
pfncname, pcall->call_from);
}
/**********
* send OK and delete from queue
**********/
if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
{
LM_ERR ("%sUnable to create reply to call (%s)!\n", pfncname,
pcall->call_from);
return;
}
delete_call (pcall);
return;
}
/**********
* Process CANCEL Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void cancel_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* RFC 3261 section 9.2
* o still in INVITE dialog?
**********/
char *pfncname = "cancel_msg: ";
if (pcall->call_state < CLSTA_INQUEUE)
{
pcall->call_state = CLSTA_CANCEL;
mohq_debug (pcall->pmohq, "%sCANCELed call (%s)",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 487, presp_reqterm) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
}
else
{
LM_ERR ("%sUnable to CANCEL because accepted INVITE for call (%s)!\n",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
}
return;
}
/**********
* Check if RTP Still Active
*
* INPUT:
* Arg (1) = SIP message pointer
* OUTPUT: =0 if inactive
**********/
int chk_rtpstat (sip_msg_t *pmsg)
{
pv_value_t pval [1];
memset (pval, 0, sizeof (pv_value_t));
if (pv_get_spec_value (pmsg, prtp_pv, pval))
{ return 0; }
if (pval->flags & PV_VAL_NULL)
{ return 0; }
return 1;
}
/**********
* Close the Call
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void close_call (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* o destroy RTP connection
* o create dialog
**********/
char *pfncname = "close_call: ";
int bsent = 0;
char *phdr = 0;
end_RTP (pmsg, pcall);
struct to_body ptob [2];
dlg_t *pdlg = form_dialog (pcall, ptob);
if (!pdlg)
{ goto bye_err; }
pdlg->state = DLG_CONFIRMED;
/**********
* form BYE header
* o calculate size
* o create buffer
**********/
tm_api_t *ptm = pmod_data->ptm;
char *pquri = pcall->pmohq->mohq_uri;
int npos1 = sizeof (pbyemsg) // BYE template
+ strlen (pcall->call_via) // Via
+ strlen (pcall->call_route) // Route
+ strlen (pquri); // Contact
phdr = pkg_malloc (npos1);
if (!phdr)
{
LM_ERR ("%sNo more memory!\n", pfncname);
goto bye_err;
}
sprintf (phdr, pbyemsg,
pcall->call_via, // Via
pcall->call_route, // Route
pquri); // Contact
str phdrs [1];
phdrs->s = phdr;
phdrs->len = strlen (phdr);
/**********
* send BYE request
**********/
uac_req_t puac [1];
set_uac_req (puac, pbye, phdrs, 0, pdlg,
TMCB_LOCAL_COMPLETED | TMCB_ON_FAILURE, bye_cb, pcall);
pcall->call_state = CLSTA_BYE;
if (ptm->t_request_within (puac) < 0)
{
LM_ERR ("%sUnable to create BYE request for call (%s)!\n",
pfncname, pcall->call_from);
goto bye_err;
}
mohq_debug (pcall->pmohq, "%sSent BYE request for call (%s)",
pfncname, pcall->call_from);
bsent = 1;
/**********
* o free memory
* o delete call
**********/
bye_err:
if (pdlg)
{ pkg_free (pdlg); }
if (phdr)
{ pkg_free (phdr); }
if (!bsent)
{ delete_call (pcall); }
return;
}
/**********
* Create New Call Record
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* Arg (3) = call index
* Arg (4) = queue index
* OUTPUT: initializes call record; =0 if failed
**********/
int
create_call (sip_msg_t *pmsg, call_lst *pcall, int ncall_idx, int mohq_idx)
{
/**********
* add values to new entry
**********/
char *pfncname = "create_call: ";
pcall->pmohq = &pmod_data->pmohq_lst [mohq_idx];
str *pstr = &pmsg->callid->body;
char *pbuf = pcall->call_buffer;
pcall->call_buflen = sizeof (pcall->call_buffer);
pcall->call_id = pbuf;
if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
pstr = &pmsg->from->body;
pcall->call_from = pbuf;
if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
pcall->call_contact = pbuf;
if (pmsg->contact)
{
pstr = &pmsg->contact->body;
if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
}
if (!addstrbfr (0, 0, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
/**********
* extract Via headers
**********/
pcall->call_via = pbuf;
hdr_field_t *phdr;
for (phdr = pmsg->h_via1; phdr; phdr = next_sibling_hdr (phdr))
{
struct via_body *pvia;
char *pviabuf;
int npos;
for (pvia = (struct via_body *)phdr->parsed; pvia; pvia = pvia->next)
{
/**********
* skip trailing whitespace
**********/
npos = pvia->bsize;
pviabuf = pvia->name.s;
while (npos)
{
--npos;
if (pviabuf [npos] == ' ' || pviabuf [npos] == '\r'
|| pviabuf [npos] == '\n' || pviabuf [npos] == '\t'
|| pviabuf [npos] == ',')
{ continue; }
break;
}
/**********
* copy via
**********/
if (!addstrbfr ("Via: ", 5, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
if (!addstrbfr (pviabuf, npos + 1, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
if (!addstrbfr (SIPEOL, 2, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
}
}
if (!addstrbfr (0, 0, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
/**********
* extract Route headers
**********/
pcall->call_route = pbuf;
struct hdr_field *proute;
for (proute = pmsg->record_route; proute; proute = next_sibling_hdr (proute))
{
if (parse_rr (proute) < 0)
{ return 0; }
rr_t *prouterr;
for (prouterr = proute->parsed; prouterr; prouterr = prouterr->next)
{
if (!addstrbfr ("Route: ", 7, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
if (!addstrbfr (prouterr->nameaddr.name.s, prouterr->len,
&pbuf, &pcall->call_buflen, 0))
{ return 0; }
if (!addstrbfr (SIPEOL, 2, &pbuf, &pcall->call_buflen, 0))
{ return 0; }
}
}
if (!addstrbfr (0, 0, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
/**********
* o place tag at the end
* o update DB
**********/
pcall->call_tag = pbuf;
if (!addstrbfr (0, 0, &pbuf, &pcall->call_buflen, 1))
{ return 0; }
pcall->call_state = CLSTA_ENTER;
add_call_rec (ncall_idx);
mohq_debug (pcall->pmohq, "%sAdded call (%s) to queue (%s)",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
return 1;
}
/**********
* Delete Call
*
* INPUT:
* Arg (1) = call pointer
* OUTPUT: none
**********/
void delete_call (call_lst *pcall)
{
/**********
* release transaction
**********/
char *pfncname = "delete_call: ";
struct cell *ptrans;
tm_api_t *ptm = pmod_data->ptm;
if (pcall->call_hash || pcall->call_label)
{
if (ptm->t_lookup_ident (&ptrans, pcall->call_hash, pcall->call_label) < 0)
{
LM_ERR ("%sLookup transaction failed for call (%s) from queue (%s)!\n",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
}
else
{
if (ptm->t_release (pcall->call_pmsg) < 0)
{
LM_ERR ("%sRelease transaction failed for call (%s) from queue (%s)!\n",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
}
}
pcall->call_hash = pcall->call_label = 0;
}
/**********
* o update DB
* o inactivate slot
**********/
if (!mohq_lock_set (pmod_data->pcall_lock, 1, 5000))
{
LM_ERR ("%sUnable to set call lock for call (%s) from queue (%s)!\n",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
}
else
{
mohq_debug (pcall->pmohq, "%sDeleting call (%s) from queue (%s)",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
delete_call_rec (pcall);
mohq_lock_release (pmod_data->pcall_lock);
}
pcall->call_state = 0;
return;
}
/**********
* Deny Method
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void deny_method (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* RFC 3261 section 8.2.1
* o get transaction
* o respond with 405 and Allow header
**********/
char *pfncname = "deny_method: ";
tm_api_t *ptm = pmod_data->ptm;
if (ptm->t_newtran (pmsg) < 0)
{
LM_ERR ("%sUnable to create new transaction!\n", pfncname);
if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
{
LM_ERR ("%sUnable to create reply to %.*s!\n", pfncname,
STR_FMT (&REQ_LINE (pmsg).method));
}
return;
}
if (!add_lump_rpl2 (pmsg, pallowhdr->s, pallowhdr->len, LUMP_RPL_HDR))
{ LM_ERR ("%sUnable to add Allow header!\n", pfncname); }
LM_ERR ("%sRefused %.*s for call (%s)!\n", pfncname,
STR_FMT (&REQ_LINE (pmsg).method), pcall->call_from);
if (ptm->t_reply (pmsg, 405, presp_noallow->s) < 0)
{
LM_ERR ("%sUnable to create reply to %.*s!\n", pfncname,
STR_FMT (&REQ_LINE (pmsg).method));
}
return;
}
/**********
* End RTP
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void end_RTP (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* destroy RTP connection
**********/
char *pfncname = "end_RTP: ";
if ((pmsg != FAKED_REPLY) && (pcall->call_state != CLSTA_ENTER))
{
mohq_debug (pcall->pmohq, "%sDestroying RTP link for call (%s)",
pfncname, pcall->call_from);
if (pmod_data->fn_rtp_destroy (pmsg, 0, 0) != 1)
{
LM_ERR ("%srtpproxy_destroy refused for call (%s)!\n",
pfncname, pcall->call_from);
}
}
return;
}
/**********
* Find Call
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = queue index
* OUTPUT: call pointer; =0 if unable to find/create
**********/
call_lst *find_call (sip_msg_t *pmsg, int mohq_idx)
{
/**********
* o get to tag
* o get callID
* o search calls
**********/
char *pfncname = "find_call: ";
str *ptotag = &(get_to (pmsg)->tag_value);
if (!ptotag->len)
{ ptotag = 0; }
if (!pmsg->callid)
{
LM_ERR ("%sNo call ID!\n", pfncname);
return 0;
}
str *pcallid = &pmsg->callid->body;
if (!pcallid)
{
LM_ERR ("%sNo call ID!\n", pfncname);
return 0;
}
int nopen = -1;
int nidx;
call_lst *pcall;
for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
{
/**********
* o call active?
* o call timed out on ACK?
* o callID matches?
* o to tag matches?
**********/
pcall = &pmod_data->pcall_lst [nidx];
if (!pcall->call_state)
{
nopen = nidx;
continue;
}
#if 0 /* ??? need to handle */
if (pcall->call_time && (pcall->call_state < CLSTA_INQUEUE))
{
if ((pcall->call_time + 32) < time (0))
{
LM_ERR ("%sNo ACK response for call (%s)!\n", pfncname, pcall->call_from);
delete_call (pcall);
continue;
}
}
#endif /* ??? */
str tmpstr [1];
tmpstr->s = pcall->call_id;
tmpstr->len = strlen (tmpstr->s);
if (!STR_EQ (*tmpstr, *pcallid))
{ continue; }
if (ptotag)
{
tmpstr->s = pcall->call_tag;
tmpstr->len = strlen (tmpstr->s);
if (!STR_EQ (*tmpstr, *ptotag))
{ continue; }
}
else
{
/**********
* match not allowed for INVITE
**********/
if (pmsg->REQ_METHOD == METHOD_INVITE)
{ return 0; }
}
return pcall;
}
/**********
* o first INVITE?
* o create a new call record
**********/
if (pmsg->REQ_METHOD != METHOD_INVITE)
{ return 0; }
if (ptotag)
{ return 0; }
if (nopen < 0)
{
LM_ERR ("%sNo call slots available!\n", pfncname);
return 0;
}
pcall = &pmod_data->pcall_lst [nopen];
if (!create_call (pmsg, pcall, nopen, mohq_idx))
{ return 0; }
return pcall;
}
/**********
* Find Queue
*
* INPUT:
* Arg (1) = SIP message pointer
* OUTPUT: queue index; -1 if unable to find
**********/
int find_queue (sip_msg_t *pmsg)
{
/**********
* o find current RURI
* o strip off parms or headers
* o search queues
**********/
str *pruri =
pmsg->new_uri.s ? &pmsg->new_uri : &pmsg->first_line.u.request.uri;
int nidx;
str pstr [1];
pstr->s = pruri->s;
pstr->len = pruri->len;
for (nidx = 0; nidx < pruri->len; nidx++)
{
if (pstr->s [nidx] == ';' || pstr->s [nidx] == '?')
{
pstr->len = nidx;
break;
}
}
mohq_lst *pqlst = pmod_data->pmohq_lst;
int nqidx;
for (nqidx = 0; nqidx < pmod_data->mohq_cnt; nqidx++)
{
str pmohstr [1];
pmohstr->s = pqlst [nqidx].mohq_uri;
pmohstr->len = strlen (pmohstr->s);
if (STR_EQ (*pmohstr, *pstr))
{ break; }
}
if (nqidx == pmod_data->mohq_cnt)
{ return -1;}
return nqidx;
}
/**********
* Find Queue Name
*
* INPUT:
* Arg (1) = queue name str pointer
* OUTPUT: queue index; -1 if unable to find
**********/
int find_qname (str *pqname)
{
char *pfncname = "find_qname: ";
int nidx;
str tmpstr;
if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 500))
{
LM_ERR ("%sUnable to lock queues!\n", pfncname);
return -1;
}
for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
{
tmpstr.s = pmod_data->pmohq_lst [nidx].mohq_name;
tmpstr.len = strlen (tmpstr.s);
if (STR_EQ (tmpstr, *pqname))
{ break; }
}
if (nidx == pmod_data->mohq_cnt)
{
LM_ERR ("%sUnable to find queue (%.*s)!\n", pfncname, STR_FMT (pqname));
nidx = -1;
}
mohq_lock_release (pmod_data->pmohq_lock);
return nidx;
}
/**********
* Find Referred Call
*
* INPUT:
* Arg (1) = referred-by value
* OUTPUT: call index; -1 if unable to find
**********/
int find_referred_call (str *pvalue)
{
/**********
* get URI
**********/
char *pfncname = "find_referred_call: ";
struct to_body pref [1];
parse_to (pvalue->s, &pvalue->s [pvalue->len + 1], pref);
if (pref->error != PARSE_OK)
{
// should never happen
LM_ERR ("%sInvalid Referred-By URI (%.*s)!\n", pfncname, STR_FMT (pvalue));
return -1;
}
if (pref->param_lst)
{ free_to_params (pref); }
/**********
* search calls for matching
**********/
int nidx;
str tmpstr;
struct to_body pfrom [1];
for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
{
if (!pmod_data->pcall_lst [nidx].call_state)
{ continue; }
tmpstr.s = pmod_data->pcall_lst [nidx].call_from;
tmpstr.len = strlen (tmpstr.s);
parse_to (tmpstr.s, &tmpstr.s [tmpstr.len + 1], pfrom);
if (pfrom->error != PARSE_OK)
{
// should never happen
LM_ERR ("%sInvalid From URI (%.*s)!\n", pfncname, STR_FMT (&tmpstr));
continue;
}
if (pfrom->param_lst)
{ free_to_params (pfrom); }
if (STR_EQ (pfrom->uri, pref->uri))
{ return nidx; }
}
return -1;
}
/**********
* Process First INVITE Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void first_invite_msg (sip_msg_t *pmsg, call_lst *pcall)
{
char *pfncname = "first_invite_msg: ";
/**********
* o SDP exists?
* o accepts REFER?
* o send RTP offer
**********/
if (!(pmsg->msg_flags & FL_SDP_BODY))
{
if (parse_sdp (pmsg))
{
if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
LM_ERR ("%sINVITE lacks SDP (%s) from queue (%s)!\n",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
delete_call (pcall);
return;
}
}
if (pmsg->allow)
{
if (!search_hdr_ext (pmsg->allow, prefer))
{
if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
LM_ERR ("%sMissing REFER support (%s) from queue (%s)!\n",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
delete_call (pcall);
return;
}
}
mohq_debug (pcall->pmohq,
"%sMaking offer for RTP link for call (%s) from queue (%s)",
pfncname, pcall->call_from, pcall->pmohq->mohq_name);
if (pmod_data->fn_rtp_offer (pmsg, 0, 0) != 1)
{
if (pmod_data->psl->freply (pmsg, 486, presp_busy) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
LM_ERR ("%srtpproxy_offer refused for call (%s)!\n",
pfncname, pcall->call_from);
delete_call (pcall);
return;
}
/**********
* o create new transaction
* o save To tag
* o catch failures
* o save transaction data
**********/
tm_api_t *ptm = pmod_data->ptm;
if (ptm->t_newtran (pmsg) < 0)
{
LM_ERR ("%sUnable to create new transaction for call (%s)!\n",
pfncname, pcall->call_from);
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
struct cell *ptrans = ptm->t_gett ();
pcall->call_hash = ptrans->hash_index;
pcall->call_label = ptrans->label;
str ptotag [1];
if (ptm->t_get_reply_totag (pmsg, ptotag) != 1)
{
LM_ERR ("%sUnable to create totag for call (%s)!\n",
pfncname, pcall->call_from);
if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
{ LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
char *pbuf = pcall->call_tag;
if (!addstrbfr (ptotag->s, ptotag->len, &pbuf, &pcall->call_buflen, 1))
{
LM_ERR ("%sInsufficient buffer space for call (%s)!\n",
pfncname, pcall->call_from);
if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
{ LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
pcall->call_cseq = 1;
if (ptm->register_tmcb (pmsg, 0, TMCB_DESTROY | TMCB_ON_FAILURE,
invite_cb, pcall, 0) < 0)
{
LM_ERR ("%sUnable to set callback for call (%s)!\n",
pfncname, pcall->call_from);
if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
{ LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
/**********
* reply with trying
**********/
if (ptm->t_reply (pmsg, 100, presp_trying->s) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
pcall->call_state = CLSTA_TRYING;
/**********
* o add contact to reply
* o supports/requires PRACK? (RFC 3262 section 3)
* o exit if not ringing
**********/
str pcontact [1];
char *pcontacthdr = "Contact: <%s>" SIPEOL;
pcontact->s
= pkg_malloc (strlen (pcall->pmohq->mohq_uri) + strlen (pcontacthdr));
if (!pcontact->s)
{
LM_ERR ("%sNo more memory!\n", pfncname);
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
sprintf (pcontact->s, pcontacthdr, pcall->pmohq->mohq_uri);
pcontact->len = strlen (pcontact->s);
if (!add_lump_rpl2 (pmsg, pcontact->s, pcontact->len, LUMP_RPL_HDR))
{
LM_ERR ("%sUnable to add contact (%s) to call (%s)!\n",
pfncname, pcontact->s, pcall->call_from);
}
pkg_free (pcontact->s);
pcall->call_pmsg = pmsg;
if (search_hdr_ext (pmsg->require, p100rel))
{
if (!send_prov_rsp (pmsg, pcall))
{
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
if (pcall->call_state == CLSTA_CANCEL)
{
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
}
/**********
* accept call with RTP
**********/
if (!send_rtp_answer (pmsg, pcall))
{
if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
end_RTP (pmsg, pcall);
delete_call (pcall);
}
return;
}
/**********
* Form Dialog
*
* INPUT:
* Arg (1) = call pointer
* Arg (2) = to_body [2] pointer
* OUTPUT: dlg_t * if successful; 0=if not
**********/
dlg_t *form_dialog (call_lst *pcall, struct to_body *pto_body)
{
/**********
* get from/to values
**********/
char *pfncname = "form_dialog: ";
str pdsturi [1], ptarget [1];
int index;
name_addr_t pname [1];
struct to_body *ptob = &pto_body [0];
struct to_body *pcontact = &pto_body [1];
parse_to (pcall->call_from,
&pcall->call_from [strlen (pcall->call_from) + 1], ptob);
if (ptob->error != PARSE_OK)
{
// should never happen
LM_ERR ("%sInvalid from URI (%s)!\n", pfncname, pcall->call_from);
return 0;
}
if (ptob->param_lst)
{ free_to_params (ptob); }
/**********
* form dest URI from record route
**********/
if (!*pcall->call_route)
{ pdsturi->s = 0; }
else
{
/**********
* o find first route URI
* o strip off parameter
**********/
pdsturi->s = pcall->call_route;
pdsturi->len = strlen (pcall->call_route);
if (parse_nameaddr (pdsturi, pname) < 0)
{
// should never happen
LM_ERR ("%sUnable to parse route (%s)!\n", pfncname, pcall->call_from);
return 0;
}
pdsturi->s = pname->uri.s;
pdsturi->len = pname->uri.len;
for (index = 1; index < pdsturi->len; index++)
{
if (pdsturi->s [index] == ';')
{
pdsturi->len = index;
break;
}
}
}
/**********
* form target URI
**********/
if (!*pcall->call_contact)
{
ptarget->s = ptob->uri.s;
ptarget->len = ptob->uri.len;
}
else
{
parse_to (pcall->call_contact,
&pcall->call_contact [strlen (pcall->call_contact) + 1], pcontact);
if (pcontact->error != PARSE_OK)
{
// should never happen
LM_ERR ("%sInvalid contact (%s) for call (%s)!\n", pfncname,
pcall->call_contact, pcall->call_from);
return 0;
}
if (pcontact->param_lst)
{ free_to_params (pcontact); }
ptarget->s = pcontact->uri.s;
ptarget->len = pcontact->uri.len;
}
/**********
* create dialog
**********/
dlg_t *pdlg = (dlg_t *)pkg_malloc (sizeof (dlg_t));
if (!pdlg)
{
LM_ERR ("%sNo more memory!\n", pfncname);
return 0;
}
memset (pdlg, 0, sizeof (dlg_t));
pdlg->loc_seq.value = pcall->call_cseq++;
pdlg->loc_seq.is_set = 1;
pdlg->id.call_id.s = pcall->call_id;
pdlg->id.call_id.len = strlen (pcall->call_id);
pdlg->id.loc_tag.s = pcall->call_tag;
pdlg->id.loc_tag.len = strlen (pcall->call_tag);
pdlg->id.rem_tag.s = ptob->tag_value.s;
pdlg->id.rem_tag.len = ptob->tag_value.len;
pdlg->rem_target.s = ptarget->s;
pdlg->rem_target.len = ptarget->len;
pdlg->loc_uri.s = pcall->pmohq->mohq_uri;
pdlg->loc_uri.len = strlen (pdlg->loc_uri.s);
pdlg->rem_uri.s = ptob->uri.s;
pdlg->rem_uri.len = ptob->uri.len;
if (pdsturi->s)
{
pdlg->dst_uri.s = pdsturi->s;
pdlg->dst_uri.len = pdsturi->len;
}
return pdlg;
}
/**********
* Form RTP SDP String
*
* INPUT:
* Arg (1) = string pointer
* Arg (2) = call pointer
* Arg (3) = SDP body pointer
* OUTPUT: 0 if failed
**********/
int form_rtp_SDP (str *pstr, call_lst *pcall, char *pSDP)
{
/**********
* o find available files
* o calculate size of SDP
**********/
char *pfncname = "form_rtp_SDP: ";
rtpmap **pmohfiles = find_MOH (pcall->pmohq->mohq_mohdir,
pcall->pmohq->mohq_mohfile);
if (!pmohfiles [0])
{
LM_ERR ("%sUnable to find any MOH files for queue (%s)!\n", pfncname,
pcall->pmohq->mohq_name);
return 0;
}
int nsize = strlen (pSDP) + 2;
int nidx;
for (nidx = 0; pmohfiles [nidx]; nidx++)
{
nsize += strlen (pmohfiles [nidx]->pencode) // encode length
+ 19; // space, type number, "a=rtpmap:%d ", EOL
}
/**********
* o allocate memory
* o form SDP
**********/
pstr->s = pkg_malloc (nsize + 1);
if (!pstr->s)
{
LM_ERR ("%sNo more memory!\n", pfncname);
return 0;
}
strcpy (pstr->s, pSDP);
nsize = strlen (pstr->s);
for (nidx = 0; pmohfiles [nidx]; nidx++)
{
/**********
* add payload types to media description
**********/
sprintf (&pstr->s [nsize], " %d", pmohfiles [nidx]->ntype);
nsize += strlen (&pstr->s [nsize]);
}
strcpy (&pstr->s [nsize], SIPEOL);
nsize += 2;
for (nidx = 0; pmohfiles [nidx]; nidx++)
{
/**********
* add rtpmap attributes
**********/
sprintf (&pstr->s [nsize], "a=rtpmap:%d %s %s",
pmohfiles [nidx]->ntype, pmohfiles [nidx]->pencode, SIPEOL);
nsize += strlen (&pstr->s [nsize]);
}
pstr->len = nsize;
return 1;
}
/**********
* Invite Callback
*
* INPUT:
* Arg (1) = cell pointer
* Arg (2) = callback type
* Arg (3) = callback parms
* OUTPUT: none
**********/
static void
invite_cb (struct cell *ptrans, int ntype, struct tmcb_params *pcbp)
{
call_lst *pcall = (call_lst *)*pcbp->param;
if (ntype == TMCB_DESTROY)
{ pcall->call_hash = pcall->call_label = 0; }
LM_ERR ("invite_cb: INVITE failed for call (%s)!\n", pcall->call_from);
delete_call (pcall);
return;
}
/**********
* Process NOTIFY Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void notify_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* waiting on REFER?
**********/
char *pfncname = "notify_msg: ";
if (pcall->call_state != CLSTA_RFRWAIT)
{
LM_ERR ("%sNot waiting on a REFER for call (%s)!\n", pfncname,
pcall->call_from);
if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
/**********
* o sipfrag?
* o get status from body
* o add CRLF so parser can go beyond first line
**********/
if (!search_hdr_ext (pmsg->content_type, psipfrag))
{
LM_ERR ("%sNot a %s type for call (%s)!\n", pfncname,
psipfrag->s, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
char *pfrag = get_body (pmsg);
if (!pfrag)
{
LM_ERR ("%s%s body missing for call (%s)!\n", pfncname,
psipfrag->s, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
str pbody [1];
pbody->len = pmsg->len - (int)(pfrag - pmsg->buf);
pbody->s = pkg_malloc (pbody->len + 2);
if (!pbody->s)
{
LM_ERR ("%sNo more memory!\n", pfncname);
return;
}
strncpy (pbody->s, pfrag, pbody->len);
if (pbody->s [pbody->len - 1] != '\n')
{
strncpy (&pbody->s [pbody->len], SIPEOL, 2);
pbody->len += 2;
}
struct msg_start pstart [1];
parse_first_line (pbody->s, pbody->len + 1, pstart);
pkg_free (pbody->s);
if (pstart->type != SIP_REPLY)
{
LM_ERR ("%sReply missing for call (%s)!\n", pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
/**********
* o send OK
* o REFER done?
**********/
int nreply = pstart->u.reply.statuscode;
if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
{
LM_ERR ("%sUnable to create reply for call (%s)!\n",
pfncname, pcall->call_from);
return;
}
mohq_debug (pcall->pmohq, "%sNOTIFY received reply (%d) for call (%s)",
pfncname, nreply, pcall->call_from);
switch (nreply / 100)
{
case 1:
pcall->refer_time = time (0);
break;
case 2:
close_call (pmsg, pcall);
break;
default:
LM_WARN ("%sUnable to redirect call (%s)\n", pfncname, pcall->call_from);
if (nreply == 487)
{
/**********
* call was cancelled
**********/
end_RTP (pmsg, pcall);
delete_call (pcall);
return;
}
/**********
* return call to queue
**********/
pcall->call_state = CLSTA_INQUEUE;
update_call_rec (pcall);
break;
}
return;
}
/**********
* Process PRACK Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void prack_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* waiting on PRACK?
**********/
char *pfncname = "prack_msg: ";
tm_api_t *ptm = pmod_data->ptm;
if (pcall->call_state != CLSTA_PRACKSTRT)
{
LM_ERR ("%sUnexpected PRACK (%s)!\n", pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
/**********
* o check RAck ??? need to check
* o accept PRACK
**********/
if (ptm->t_newtran (pmsg) < 0)
{
LM_ERR ("%sUnable to create new transaction for call (%s)!\n",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
if (ptm->t_reply (pmsg, 200, presp_ok->s) < 0)
{
LM_ERR ("%sUnable to reply to PRACK for call (%s)!\n",
pfncname, pcall->call_from);
return;
}
pcall->call_state = CLSTA_PRACKRPLY;
return;
}
/**********
* Refer Call
*
* INPUT:
* Arg (1) = call pointer
* Arg (2) = lock pointer
* OUTPUT: 0 if failed
**********/
int refer_call (call_lst *pcall, mohq_lock *plock)
{
/**********
* create dialog
**********/
char *pfncname = "refer_call: ";
int nret = 0;
struct to_body ptob [2];
dlg_t *pdlg = form_dialog (pcall, ptob);
if (!pdlg)
{
mohq_lock_release (plock);
return 0;
}
pdlg->state = DLG_CONFIRMED;
/**********
* form REFER message
* o calculate basic size
* o create buffer
**********/
str puri [1];
puri->s = pcall->call_referto;
puri->len = strlen (puri->s);
int npos1 = sizeof (prefermsg) // REFER template
+ strlen (pcall->call_via) // Via
+ strlen (pcall->call_route) // Route
+ strlen (pcall->pmohq->mohq_uri) // Contact
+ puri->len // Refer-To
+ strlen (pcall->pmohq->mohq_uri); // Referred-By
char *pbuf = pkg_malloc (npos1);
if (!pbuf)
{
LM_ERR ("%sNo more memory!\n", pfncname);
goto refererr;
}
sprintf (pbuf, prefermsg,
pcall->call_via, // Via
pcall->call_route, // Route
pcall->pmohq->mohq_uri, // Contact
puri->s, // Refer-To
pcall->pmohq->mohq_uri); // Referred-By
/**********
* send REFER request
**********/
tm_api_t *ptm = pmod_data->ptm;
uac_req_t puac [1];
str phdrs [1];
phdrs->s = pbuf;
phdrs->len = strlen (pbuf);
set_uac_req (puac, prefer, phdrs, 0, pdlg,
TMCB_LOCAL_COMPLETED | TMCB_ON_FAILURE, refer_cb, pcall);
pcall->refer_time = time (0);
pcall->call_state = CLSTA_REFER;
update_call_rec (pcall);
mohq_lock_release (plock);
if (ptm->t_request_within (puac) < 0)
{
pcall->call_state = CLSTA_INQUEUE;
LM_ERR ("%sUnable to create REFER request for call (%s)!\n",
pfncname, pcall->call_from);
update_call_rec (pcall);
goto refererr;
}
mohq_debug (pcall->pmohq, "%sSent REFER request for call (%s) to %s",
pfncname, pcall->call_from, pcall->call_referto);
nret = -1;
refererr:
if (pdlg)
{ pkg_free (pdlg); }
pkg_free (pbuf);
return nret;
}
/**********
* REFER Callback
*
* INPUT:
* Arg (1) = cell pointer
* Arg (2) = callback type
* Arg (3) = callback parms
* OUTPUT: none
**********/
static void refer_cb
(struct cell *ptrans, int ntype, struct tmcb_params *pcbp)
{
char *pfncname = "refer_cb: ";
call_lst *pcall = (call_lst *)*pcbp->param;
if (pcall->call_state != CLSTA_REFER)
{
if (!pcall->call_state)
{
LM_ERR
("%sREFER response ignored because call not in queue!\n", pfncname);
}
else
{
LM_ERR ("%sCall (%s) ignored because not in REFER state!\n", pfncname,
pcall->call_from);
}
return;
}
if ((ntype == TMCB_ON_FAILURE) || (pcbp->req == FAKED_REPLY))
{
LM_ERR ("%sCall (%s) did not respond to REFER!\n", pfncname,
pcall->call_from);
end_RTP (pcbp->req, pcall);
delete_call (pcall);
return;
}
/**********
* check reply
**********/
int nreply = pcbp->code;
if ((nreply / 100) == 2)
{
pcall->refer_time = time (0);
pcall->call_state = CLSTA_RFRWAIT;
mohq_debug (pcall->pmohq, "%sCall (%s) REFER reply=%d",
pfncname, pcall->call_from, nreply);
}
else
{
LM_ERR ("%sCall (%s) REFER error (%d)!\n", pfncname,
pcall->call_from, nreply);
if (nreply == 481)
{ delete_call (pcall); }
else
{
if (!chk_rtpstat (pcbp->req))
{
LM_ERR ("%sRTP for call (%s) no longer active!\n",
pfncname, pcall->call_from);
delete_call (pcall);
}
else
{
pcall->call_state = CLSTA_INQUEUE;
update_call_rec (pcall);
}
}
}
return;
}
/**********
* Process re-INVITE Message
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: none
**********/
void reinvite_msg (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* RFC 3261 section 14.2
* o dialog pending?
* o get SDP
**********/
char *pfncname = "reinvite_msg: ";
if ((pcall->call_state / 100) < 2)
{
mohq_debug (pcall->pmohq, "%sINVITE still pending for call (%s)",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 491, presp_reqpend) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
if (!(pmsg->msg_flags & FL_SDP_BODY))
{
if (parse_sdp (pmsg))
{
LM_ERR ("%sre-INVITE lacks SDP (%s)!\n", pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return;
}
}
/**********
* o find available MOH files
* o look for hold condition and matching payload type
**********/
rtpmap **pmohfiles = find_MOH (pcall->pmohq->mohq_mohdir,
pcall->pmohq->mohq_mohfile);
int bhold = 0;
int bmatch = 0;
int nsession;
sdp_session_cell_t *psession;
for (nsession = 0; (psession = get_sdp_session (pmsg, nsession)); nsession++)
{
int nstream;
sdp_stream_cell_t *pstream;
for (nstream = 0; (pstream = get_sdp_stream (pmsg, nsession, nstream));
nstream++)
{
/**********
* o RTP?
* o audio?
* o hold?
* o at least one payload matches?
**********/
if (!pstream->is_rtp)
{ continue; }
if (!STR_EQ (*paudio, pstream->media))
{ continue; }
if (pstream->is_on_hold)
{
bhold = 1;
break;
}
if (bmatch)
{ continue; }
/**********
* check payload types for a match
**********/
sdp_payload_attr_t *ppayload;
for (ppayload = pstream->payload_attr; ppayload; ppayload = ppayload->next)
{
int ntype = atoi (ppayload->rtp_payload.s);
int nidx;
for (nidx = 0; pmohfiles [nidx]; nidx++)
{
if (pmohfiles [nidx]->ntype == ntype)
{
bmatch = 1;
break;
}
}
}
}
}
/**********
* if no hold, allow re-INVITE if matching file
**********/
if (!bhold)
{
if (!bmatch)
{
LM_ERR ("%sre-INVITE refused because no matching payload for call (%s)!\n",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
}
else
{
mohq_debug (pcall->pmohq, "%sAccepted re-INVITE for call (%s)",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
}
return;
}
/**********
* hold not allowed, say good-bye
**********/
LM_ERR ("%sTerminating call (%s) because hold not allowed!\n",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
{
LM_ERR ("%sUnable to create reply!\n", pfncname);
return;
}
close_call (pmsg, pcall);
return;
}
/**********
* Search Header for Extension
*
* INPUT:
* Arg (1) = header field pointer
* Arg (2) = extension str pointer
* OUTPUT: 0=not found
**********/
int search_hdr_ext (struct hdr_field *phdr, str *pext)
{
if (!phdr)
{ return 0; }
str *pstr = &phdr->body;
int npos1, npos2;
for (npos1 = 0; npos1 < pstr->len; npos1++)
{
/**********
* o find non-space
* o search to end, space or comma
* o same size?
* o same name?
**********/
if (pstr->s [npos1] == ' ')
{ continue; }
for (npos2 = npos1++; npos1 < pstr->len; npos1++)
{
if (pstr->s [npos1] == ' ' || pstr->s [npos1] == ',')
{ break; }
}
if (npos1 - npos2 != pext->len)
{ continue; }
if (!strncasecmp (&pstr->s [npos2], pext->s, pext->len))
{ return 1; }
}
return 0;
}
/**********
* Send Provisional Response
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: 0=unable to process; 1=processed
**********/
int send_prov_rsp (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* o send ringing response with require
* o update record
**********/
char *pfncname = "send_prov_rsp: ";
tm_api_t *ptm = pmod_data->ptm;
pcall->call_cseq = rand ();
char phdrtmp [200];
char *phdrtmplt =
"Accept-Language: en" SIPEOL
"Require: 100rel" SIPEOL
"RSeq: %d" SIPEOL
;
sprintf (phdrtmp, phdrtmplt, pcall->call_cseq);
struct lump_rpl **phdrlump = add_lump_rpl2 (pmsg, phdrtmp,
strlen (phdrtmp), LUMP_RPL_HDR);
if (!phdrlump)
{
LM_ERR ("%sUnable to create new header for call (%s)!\n",
pfncname, pcall->call_from);
if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
{ LM_ERR ("%sUnable to create reply!\n", pfncname); }
return 0;
}
if (ptm->t_reply (pmsg, 180, presp_ring->s) < 0)
{
LM_ERR ("%sUnable to reply to INVITE for call (%s)!\n",
pfncname, pcall->call_from);
return 0;
}
pcall->call_state = CLSTA_PRACKSTRT;
mohq_debug (pcall->pmohq, "%sSent PRACK RINGING for call (%s)",
pfncname, pcall->call_from);
/**********
* o wait until PRACK (64*T1 RFC 3261 section 7.1.1)
* o remove header lump
**********/
time_t nstart = time (0) + 32;
while (1)
{
usleep (USLEEP_LEN);
if (pcall->call_state != CLSTA_PRACKSTRT)
{ break; }
if (nstart < time (0))
{
LM_ERR ("%sNo PRACK response for call (%s)!\n",
pfncname, pcall->call_from);
break;
}
}
unlink_lump_rpl (pmsg, *phdrlump);
if (pcall->call_state != CLSTA_PRACKRPLY)
{ return 0; }
return 1;
}
/**********
* Send RTPProxy Answer
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* OUTPUT: 0=unable to process; 1=processed
**********/
int send_rtp_answer (sip_msg_t *pmsg, call_lst *pcall)
{
/**********
* build response from request
**********/
char *pfncname = "send_rtp_answer: ";
int nret = 0;
tm_api_t *ptm = pmod_data->ptm;
struct cell *ptrans = ptm->t_gett ();
str ptotag [1];
ptotag->s = pcall->call_tag;
ptotag->len = strlen (pcall->call_tag);
str pbuf [1];
struct bookmark pBM [1];
pbuf->s = build_res_buf_from_sip_req (200, presp_ok, ptotag, ptrans->uas.request,
(unsigned int *)&pbuf->len, pBM);
if (!pbuf->s || !pbuf->len)
{
LM_ERR ("%sUnable to create SDP response for call (%s)!\n",
pfncname, pcall->call_from);
return 0;
}
/**********
* parse out first line and headers
**********/
char *pclenhdr = CLENHDR;
str pparse [20];
int npos1, npos2;
int nhdrcnt = 0;
for (npos1 = 0; npos1 < pbuf->len; npos1++)
{
/**********
* find EOL
**********/
for (npos2 = npos1++; npos1 < pbuf->len; npos1++)
{
/**********
* o not EOL? (CRLF assumed)
* o next line a continuation? (RFC 3261 section 7.3.1)
**********/
if (pbuf->s [npos1] != '\n')
{ continue; }
if (npos1 + 1 == pbuf->len)
{ break; }
if (pbuf->s [npos1 + 1] == ' '
|| pbuf->s [npos1 + 1] == '\t')
{ continue; }
break;
}
/**********
* o blank is end of header (RFC 3261 section 7)
* o ignore Content-Length (assume followed by colon)
* o save header
**********/
if (npos1 - npos2 == 1)
{ break; }
if (npos1 - npos2 > 14)
{
if (!strncasecmp (&pbuf->s [npos2], pclenhdr, 14))
{ continue; }
}
pparse [nhdrcnt].s = &pbuf->s [npos2];
pparse [nhdrcnt++].len = npos1 - npos2 + 1;
}
/**********
* recreate buffer with extra headers and SDP
* o form SDP
* o count hdrs, extra hdrs, content-length hdr, SDP
* o alloc new buffer
* o form new buffer
* o replace orig buffer
**********/
str pSDP [1] = {STR_NULL};
if (!form_rtp_SDP (pSDP, pcall, prtpsdp))
{ goto answer_done; }
for (npos1 = npos2 = 0; npos2 < nhdrcnt; npos2++)
{ npos1 += pparse [npos2].len; }
char pbodylen [30];
sprintf (pbodylen, "%s: %d\r\n\r\n", pclenhdr, pSDP->len);
npos1 += pextrahdr->len + strlen (pbodylen) + pSDP->len + 1;
char *pnewbuf = pkg_malloc (npos1);
if (!pnewbuf)
{
LM_ERR ("%sNo more memory!\n", pfncname);
goto answer_done;
}
for (npos1 = npos2 = 0; npos2 < nhdrcnt; npos2++)
{
memcpy (&pnewbuf [npos1], pparse [npos2].s, pparse [npos2].len);
npos1 += pparse [npos2].len;
}
npos2 = pextrahdr->len;
memcpy (&pnewbuf [npos1], pextrahdr->s, npos2);
npos1 += npos2;
npos2 = strlen (pbodylen);
memcpy (&pnewbuf [npos1], pbodylen, npos2);
npos1 += npos2;
npos2 = pSDP->len;
memcpy (&pnewbuf [npos1], pSDP->s, npos2);
npos1 += npos2;
pkg_free (pbuf->s);
pbuf->s = pnewbuf;
pbuf->len = npos1;
/**********
* build SIP msg
**********/
struct sip_msg pnmsg [1];
build_sip_msg_from_buf (pnmsg, pbuf->s, pbuf->len, 0);
memcpy (&pnmsg->rcv, &pmsg->rcv, sizeof (struct receive_info));
/**********
* o send RTP answer
* o form stream file
* o send stream
**********/
mohq_debug (pcall->pmohq, "%sAnswering RTP link for call (%s)",
pfncname, pcall->call_from);
if (pmod_data->fn_rtp_answer (pnmsg, 0, 0) != 1)
{
LM_ERR ("%srtpproxy_answer refused for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
if (!start_stream (pnmsg, pcall, 0))
{ goto answer_done; }
/**********
* o create buffer from response
* o find SDP
**********/
pbuf->s = build_res_buf_from_sip_res (pnmsg, (unsigned int *)&pbuf->len);
pkg_free (pnewbuf);
free_sip_msg (pnmsg);
if (!pbuf->s || !pbuf->len)
{
LM_ERR ("%sUnable to create SDP response for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
str pnewSDP [1];
for (npos1 = 0; npos1 < pbuf->len; npos1++)
{
if (pbuf->s [npos1] != '\n')
{ continue; }
if (pbuf->s [npos1 - 3] == '\r')
{ break; }
}
pnewSDP->s = &pbuf->s [npos1 + 1];
pnewSDP->len = pbuf->len - npos1 - 1;
/**********
* o save media port number
* o send adjusted reply
**********/
char *pfnd = strstr (pnewSDP->s, "m=audio ");
if (!pfnd)
{
// should not happen
LM_ERR ("%sUnable to find audio port for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
pcall->call_aport = strtol (pfnd + 8, NULL, 10);
if (!add_lump_rpl2 (pmsg, pextrahdr->s, pextrahdr->len, LUMP_RPL_HDR))
{
LM_ERR ("%sUnable to add header for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
if (!add_lump_rpl2 (pmsg, pnewSDP->s, pnewSDP->len, LUMP_RPL_BODY))
{
LM_ERR ("%sUnable to add SDP body for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
if (ptm->t_reply (pmsg, 200, presp_ok->s) < 0)
{
LM_ERR ("%sUnable to reply to INVITE for call (%s)!\n",
pfncname, pcall->call_from);
goto answer_done;
}
pcall->call_state = CLSTA_INVITED;
mohq_debug (pcall->pmohq, "%sResponded to INVITE with RTP for call (%s)",
pfncname, pcall->call_from);
nret = 1;
/**********
* free buffer and return
**********/
answer_done:
if (pSDP->s)
{ pkg_free (pSDP->s); }
pkg_free (pbuf->s);
return nret;
}
/**********
* Start Streaming
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* Arg (3) = server flag
* OUTPUT: 0 if failed
**********/
int start_stream (sip_msg_t *pmsg, call_lst *pcall, int bserver)
{
char *pfncname = "start_stream: ";
char pfile [MOHDIRLEN + MOHFILELEN + 2];
strcpy (pfile, pcall->pmohq->mohq_mohdir);
int npos = strlen (pfile);
pfile [npos++] = '/';
strcpy (&pfile [npos], pcall->pmohq->mohq_mohfile);
npos += strlen (&pfile [npos]);
str pMOH [1] = {{pfile, npos}};
pv_elem_t *pmodel;
pv_parse_format (pMOH, &pmodel);
cmd_function fn_stream = bserver ? pmod_data->fn_rtp_stream_s
: pmod_data->fn_rtp_stream_c;
mohq_debug (pcall->pmohq, "%sStarting RTP link for call (%s)",
pfncname, pcall->call_from);
if (fn_stream (pmsg, (char *)pmodel, (char *)-1) != 1)
{
LM_ERR ("%srtpproxy_stream refused for call (%s)!\n",
pfncname, pcall->call_from);
return 0;
}
return 1;
}
/**********
* Stop Streaming
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = call pointer
* Arg (3) = server flag
* OUTPUT: 0 if failed
**********/
int stop_stream (sip_msg_t *pmsg, call_lst *pcall, int bserver)
{
char *pfncname = "stop_stream: ";
cmd_function fn_stop = bserver ? pmod_data->fn_rtp_stop_s
: pmod_data->fn_rtp_stop_c;
mohq_debug (pcall->pmohq, "%sStopping RTP link for call (%s)",
pfncname, pcall->call_from);
if (fn_stop (pmsg, (char *)-1, (char *)-1) != 1)
{
LM_ERR ("%srtpproxy_stop refused for call (%s)!\n",
pfncname, pcall->call_from);
return 0;
}
return 1;
}
/**********
* Form Char Array from STR
*
* INPUT:
* Arg (1) = str pointer
* OUTPUT: char pointer; NULL if unable to allocate
**********/
char *form_tmpstr (str *pstr)
{
char *pcstr = malloc (pstr->len + 1);
if (!pcstr)
{
LM_ERR ("No more memory!\n");
return NULL;
}
memcpy (pcstr, pstr->s, pstr->len);
pcstr [pstr->len] = 0;
return pcstr;
}
/**********
* Release Char Array
*
* INPUT:
* Arg (1) = char pointer
* OUTPUT: none
**********/
void free_tmpstr (char *pcstr)
{
if (pcstr)
{ free (pcstr); }
return;
}
/**********
* external functions
**********/
/**********
* Find MOH Files
*
* INPUT:
* Arg (1) = mohdir pointer
* Arg (2) = mohfile pointer
* OUTPUT: array of pointers for matching files; last element=0
**********/
rtpmap **find_MOH (char *pmohdir, char *pmohfile)
{
/**********
* form base file name
**********/
char pfile [MOHDIRLEN + MOHFILELEN + 6];
strcpy (pfile, pmohdir);
int nflen = strlen (pfile);
pfile [nflen++] = '/';
strcpy (&pfile [nflen], pmohfile);
nflen += strlen (&pfile [nflen]);
pfile [nflen++] = '.';
/**********
* find available files based on RTP payload type
**********/
int nidx;
int nfound = 0;
for (nidx = 0; prtpmap [nidx].pencode; nidx++)
{
/**********
* o form file name based on payload type
* o exists?
**********/
sprintf (&pfile [nflen], "%d", prtpmap [nidx].ntype);
struct stat psb [1];
if (lstat (pfile, psb))
{ continue; }
pmohfiles [nfound++] = &prtpmap [nidx];
}
pmohfiles [nfound] = 0;
return pmohfiles;
}
/**********
* MI Debug
*
* PARAMETERS:
* queue name = queue to use
* state = 0=off, <>0=on
*
* INPUT:
* Arg (1) = command tree pointer
* Arg (2) = parms pointer
* OUTPUT: root pointer
**********/
struct mi_root *mi_debug (struct mi_root *pcmd_tree, void *parms)
{
/**********
* o parm count correct?
* o find queue
* o lock queue
**********/
struct mi_node *pnode = pcmd_tree->node.kids;
if (!pnode || !pnode->next || pnode->next->next)
{ return init_mi_tree (400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); }
int nq_idx = find_qname (&pnode->value);
if (nq_idx == -1)
{ return init_mi_tree (400, pmi_noqueue->s, pmi_noqueue->len); }
char pint [20];
int nsize = (pnode->next->value.len >= sizeof (pint))
? sizeof (pint) - 1 : pnode->next->value.len;
strncpy (pint, pnode->next->value.s, nsize);
pint [nsize] = '\0';
int bdebug = atoi (pint) ? 1 : 0;
if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 5000))
{ return init_mi_tree (400, pmi_nolock->s, pmi_nolock->len); }
/**********
* o set flag
* o update queue table
* o release lock
**********/
mohq_lst *pqueue = &pmod_data->pmohq_lst [nq_idx];
if (bdebug)
{ pqueue->mohq_flags |= MOHQF_DBG; }
else
{ pqueue->mohq_flags &= ~MOHQF_DBG; }
update_debug (pqueue, bdebug);
mohq_lock_release (pmod_data->pmohq_lock);
return init_mi_tree (200, MI_OK_S, MI_OK_LEN);
}
/**********
* MI Drop Call
*
* PARAMETERS:
* queue name = queue to use
* callID = *=all, otherwise callID
*
* INPUT:
* Arg (1) = command tree pointer
* Arg (2) = parms pointer
* OUTPUT: root pointer
**********/
struct mi_root *mi_drop_call (struct mi_root *pcmd_tree, void *parms)
{
/**********
* o parm count correct?
* o find queue
* o lock calls
**********/
struct mi_node *pnode = pcmd_tree->node.kids;
if (!pnode || !pnode->next || pnode->next->next)
{ return init_mi_tree (400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); }
int nq_idx = find_qname (&pnode->value);
if (nq_idx == -1)
{ return init_mi_tree (400, pmi_noqueue->s, pmi_noqueue->len); }
if (!mohq_lock_set (pmod_data->pcall_lock, 0, 5000))
{ return init_mi_tree (400, pmi_nolock->s, pmi_nolock->len); }
/**********
* o find matching calls
* o release lock
**********/
mohq_lst *pqueue = &pmod_data->pmohq_lst [nq_idx];
int nidx;
str *pcallid = &pnode->next->value;
for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
{
/**********
* o call active?
* o callID matches?
* o close call
**********/
call_lst *pcall = &pmod_data->pcall_lst [nidx];
if (!pcall->call_state)
{ continue; }
if (pqueue->mohq_id != pcall->pmohq->mohq_id)
{ continue; }
str tmpstr [1];
if (!STR_EQ (*pcallid, *pallq))
{
tmpstr->s = pcall->call_id;
tmpstr->len = strlen (tmpstr->s);
if (!STR_EQ (*tmpstr, *pcallid))
{ continue; }
}
close_call (FAKED_REPLY, pcall);
}
mohq_lock_release (pmod_data->pcall_lock);
return init_mi_tree (200, MI_OK_S, MI_OK_LEN);
}
/**********
* Count Messages
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = queue name
* Arg (3) = pv result name
* OUTPUT: -1 if no items in queue; else result = count
**********/
int mohq_count (sip_msg_t *pmsg, char *pqueue, pv_spec_t *presult)
{
/**********
* get queue and pv names
**********/
char *pfncname = "mohq_count: ";
str pqname [1];
if (!pqueue || !presult)
{
LM_ERR ("%sParameters missing!\n", pfncname);
return -1;
}
if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
{
LM_ERR ("%sInvalid queue name!\n", pfncname);
return -1;
}
/**********
* o find queue
* o lock calls
* o count items in queue
**********/
int nq_idx = find_qname (pqname);
int ncount = 0;
call_lst *pcalls = pmod_data->pcall_lst;
int ncall_idx, mohq_id;
if (!mohq_lock_set (pmod_data->pcall_lock, 0, 200))
{ LM_ERR ("%sUnable to lock calls!\n", pfncname); }
else
{
if (nq_idx != -1)
{
mohq_id = pmod_data->pmohq_lst [nq_idx].mohq_id;
for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
{
if (!pcalls [ncall_idx].call_state)
{ continue; }
if (pcalls [ncall_idx].pmohq->mohq_id == mohq_id
&& pcalls [ncall_idx].call_state == CLSTA_INQUEUE)
{ ncount++; }
}
}
mohq_lock_release (pmod_data->pcall_lock);
}
/**********
* o set pv result
* o exit with result
**********/
pv_value_t pavp_val [1];
memset (pavp_val, 0, sizeof (pv_value_t));
pavp_val->ri = ncount;
pavp_val->flags = PV_TYPE_INT | PV_VAL_INT;
if (presult->setf (pmsg, &presult->pvp, (int)EQ_T, pavp_val) < 0)
{
LM_ERR ("%sUnable to set pv value for mohq_count ()!\n", pfncname);
return -1;
}
return 1;
}
/**********
* Log Debug Statement
*
* INPUT:
* Arg (1) = MOH queue pointer
* Arg (2) = format pointer
* Arg (...) = optional format values
* OUTPUT: outputs debugging values
**********/
void mohq_debug (mohq_lst *pmohq, char *pfmt, ...)
{
/**********
* o get system and MOHQ log level
* o exit if no debug printing
* o force local debug
* o form message and log
* o reset log level
**********/
int nsys_log = get_debug_level (LOG_MNAME, LOG_MNAME_LEN);
int nmohq_log = (pmohq->mohq_flags & MOHQF_DBG) ? L_DBG : L_INFO;
if (nmohq_log < L_DBG && nsys_log < L_DBG)
{ return; }
if (nsys_log < nmohq_log)
{ set_local_debug_level (nmohq_log); }
char ptext [1024];
va_list ap;
va_start (ap, pfmt);
vsnprintf (ptext, sizeof (ptext), pfmt, ap);
va_end (ap);
LM_DBG ("%s\n", ptext);
if (nsys_log < nmohq_log)
{ reset_local_debug_level (); }
return;
}
/**********
* Process Message
*
* INPUT:
* Arg (1) = SIP message pointer
* OUTPUT: -1=not directed to queue or other error; 1=processed
**********/
int mohq_process (sip_msg_t *pmsg)
{
/**********
* read lock queue and check for updates
**********/
char *pfncname = "mohq_process: ";
if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 500))
{
LM_ERR ("%sUnable to read lock queue!\n", pfncname);
return -1;
}
db1_con_t *pconn = mohq_dbconnect ();
if (pconn)
{
/**********
* o last update older than 1 minute?
* o update write locked queue
**********/
if (pmod_data->mohq_update + 60 < time (0))
{
if (mohq_lock_change (pmod_data->pmohq_lock, 1))
{
update_mohq_lst (pconn);
mohq_lock_change (pmod_data->pmohq_lock, 0);
pmod_data->mohq_update = time (0);
}
}
mohq_dbdisconnect (pconn);
}
/**********
* o parse headers
* o directed to message queue?
* o write lock calls
* o search for call
* o release call lock
**********/
if (parse_headers (pmsg, HDR_EOH_F, 0) < 0)
{
mohq_lock_release (pmod_data->pmohq_lock);
LM_ERR ("%sUnable to parse header!\n", pfncname);
return -1;
}
int mohq_idx = find_queue (pmsg);
if (mohq_idx < 0)
{
mohq_lock_release (pmod_data->pmohq_lock);
return -1;
}
if (!mohq_lock_set (pmod_data->pcall_lock, 1, 500))
{
mohq_lock_release (pmod_data->pmohq_lock);
LM_ERR ("%sUnable to write lock calls!\n", pfncname);
return 1;
}
call_lst *pcall = find_call (pmsg, mohq_idx);
mohq_lock_release (pmod_data->pcall_lock);
if (!pcall)
{
mohq_lock_release (pmod_data->pmohq_lock);
return 1;
}
/**********
* o process message
* o release queue lock
**********/
mohq_debug (&pmod_data->pmohq_lst [mohq_idx],
"%sProcessing %.*s, queue (%s)", pfncname,
STR_FMT (&REQ_LINE (pmsg).method),
pmod_data->pmohq_lst [mohq_idx].mohq_name);
str *ptotag;
switch (pmsg->REQ_METHOD)
{
case METHOD_INVITE:
/**********
* initial INVITE?
**********/
ptotag = &(get_to (pmsg)->tag_value);
if (!ptotag->len)
{ ptotag = 0; }
if (!ptotag)
{ first_invite_msg (pmsg, pcall); }
else
{ reinvite_msg (pmsg, pcall); }
break;
case METHOD_NOTIFY:
notify_msg (pmsg, pcall);
break;
case METHOD_PRACK:
prack_msg (pmsg, pcall);
break;
case METHOD_ACK:
ack_msg (pmsg, pcall);
break;
case METHOD_BYE:
bye_msg (pmsg, pcall);
break;
case METHOD_CANCEL:
cancel_msg (pmsg, pcall);
break;
default:
deny_method (pmsg, pcall);
break;
}
mohq_lock_release (pmod_data->pmohq_lock);
return 1;
}
/**********
* Retrieve Oldest Queued Call
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = queue name
* Arg (3) = redirect URI
* OUTPUT: -1 if no items in queue or error; 1 redirects oldest call
**********/
int mohq_retrieve (sip_msg_t *pmsg, char *pqueue, char *pURI)
{
/**********
* o get queue name and URI
* o check URI
**********/
char *pfncname = "mohq_retrieve: ";
str puri [1], pqname [1];
if (!pqueue || !pURI)
{
LM_ERR ("%sParameters missing!\n", pfncname);
return -1;
}
if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
{
LM_ERR ("%sInvalid queue name!\n", pfncname);
return -1;
}
if (fixup_get_svalue (pmsg, (gparam_p)pURI, puri))
{
LM_ERR ("%sInvalid URI!\n", pfncname);
return -1;
}
if (puri->len > URI_LEN)
{
LM_ERR ("%sURI too long!\n", pfncname);
return -1;
}
struct sip_uri puri_parsed [1];
if (parse_uri (puri->s, puri->len, puri_parsed))
{
LM_ERR ("%sInvalid URI (%.*s)!\n", pfncname, STR_FMT (puri));
return -1;
}
/**********
* o find queue
* o lock calls
* o find oldest call
**********/
int nq_idx = find_qname (pqname);
if (nq_idx == -1)
{ return -1; }
if (!mohq_lock_set (pmod_data->pcall_lock, 0, 200))
{
LM_ERR ("%sUnable to lock calls!\n", pfncname);
return -1;
}
call_lst *pcall = 0;
int ncall_idx;
time_t ntime = 0;
int nfound = -1;
int mohq_id = pmod_data->pmohq_lst [nq_idx].mohq_id;
for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
{
/**********
* o active call?
* o matching queue?
* o refer stuck?
* o in queue?
* o check age
**********/
pcall = &pmod_data->pcall_lst [ncall_idx];
if (!pcall->call_state)
{ continue; }
if (pcall->pmohq->mohq_id != mohq_id)
{ continue; }
if ((pcall->call_state == CLSTA_REFER)
|| (pcall->call_state == CLSTA_RFRWAIT))
{
if ((pcall->refer_time + 32) < time (0))
{
LM_ERR
("%sDropping call because no response to REFER for call (%s)!\n",
pfncname, pcall->call_from);
close_call (FAKED_REPLY, pcall);
}
}
if (pcall->call_state != CLSTA_INQUEUE)
{ continue; }
if (!ntime)
{
nfound = ncall_idx;
ntime = pcall->call_time;
}
else
{
if (pcall->call_time < ntime)
{
nfound = ncall_idx;
ntime = pcall->call_time;
}
}
}
if (nfound == -1)
{
LM_WARN ("%sNo calls in queue (%.*s)\n", pfncname, STR_FMT (pqname));
mohq_lock_release (pmod_data->pcall_lock);
return -1;
}
pcall = &pmod_data->pcall_lst [nfound];
/**********
* o save refer-to URI
* o send refer
**********/
strncpy (pcall->call_referto, puri->s, puri->len);
pcall->call_referto [puri->len] = '\0';
if (refer_call (pcall, pmod_data->pcall_lock))
{ return 1; }
LM_ERR ("%sUnable to refer call (%s)!\n", pfncname, pcall->call_from);
return -1;
}
/**********
* Send Message to Queue
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = queue name
* OUTPUT: -1 if no items in queue; 1 if successfull
**********/
int mohq_send (sip_msg_t *pmsg, char *pqueue)
{
/**********
* o first INVITE?
* o get queue name
**********/
char *pfncname = "mohq_send: ";
if (pmsg->REQ_METHOD != METHOD_INVITE)
{
LM_ERR ("%sNot an INVITE message!\n", pfncname);
return -1;
}
to_body_t *pto_body = get_to (pmsg);
if (pto_body->tag_value.len)
{
LM_ERR ("%sNot a first INVITE message!\n", pfncname);
return -1;
}
str pqname [1];
if (!pqueue)
{
LM_ERR ("%sParameters missing!\n", pfncname);
return -1;
}
if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
{
LM_ERR ("%sInvalid queue name!\n", pfncname);
return -1;
}
/**********
* o find queue
* o change RURI
* o relay message
**********/
int nq_idx = find_qname (pqname);
if (nq_idx == -1)
{ return -1; }
str pruri [1] = {{0, strlen (pmod_data->pmohq_lst [nq_idx].mohq_uri)}};
pruri->s = pkg_malloc (pruri->len + 1);
if (!pruri->s)
{
LM_ERR ("%sNo more memory!\n", pfncname);
return -1;
}
strcpy (pruri->s, pmod_data->pmohq_lst [nq_idx].mohq_uri);
if (pmsg->new_uri.s)
{ pkg_free (pmsg->new_uri.s); }
pmsg->new_uri.s = pruri->s;
pmsg->new_uri.len = pruri->len;
pmsg->parsed_uri_ok = 0;
pmsg->parsed_orig_ruri_ok = 0;
if (pmod_data->ptm->t_relay (pmsg, 0, 0) < 0)
{
LM_ERR ("%sUnable to relay INVITE!\n", pfncname);
return -1;
}
return 1;
}