- Added config param to restrict response from mms-service (VAS GW)
- Added param for headers that should be sent to MMC are received from mms-service response (VAS GW) - Fixed bug where omit-empty param for VAS GW was actually ignored
This commit is contained in:
parent
ca05acedc9
commit
ea8bce278a
|
@ -61,7 +61,10 @@ file = /tmp/start.smil
|
||||||
catch-all = true
|
catch-all = true
|
||||||
# http-post-parameters = fx=true&images.=%i&text.=%t
|
# http-post-parameters = fx=true&images.=%i&text.=%t
|
||||||
accept-x-mbuni-headers = true
|
accept-x-mbuni-headers = true
|
||||||
|
pass-thro-headers = X-NOKIA-MMSC-Charging,X-NOKIA-MMSC-Charged-Party
|
||||||
keyword = test
|
keyword = test
|
||||||
|
omit-empty = no
|
||||||
|
suppress-reply = true
|
||||||
|
|
||||||
group = mms-service
|
group = mms-service
|
||||||
name = fullmessage
|
name = fullmessage
|
||||||
|
|
|
@ -2222,7 +2222,7 @@ faked-sender = 100<br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The Send MMS service can be invoked using HTTP GET requests to the
|
The Send MMS service can be invoked using HTTP GET or POST requests to the
|
||||||
Send MMS port on the VAS Gateway hosts. The interface expects and
|
Send MMS port on the VAS Gateway hosts. The interface expects and
|
||||||
processes the following CGI paramerters:
|
processes the following CGI paramerters:
|
||||||
</p>
|
</p>
|
||||||
|
@ -2352,6 +2352,18 @@ faked-sender = 100<br>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td valign=top >
|
||||||
|
<tt>base-url</tt>
|
||||||
|
</td>
|
||||||
|
<td valign=top >
|
||||||
|
If the <tt>smil</tt> URL parameter is provided, then this
|
||||||
|
parameter may be supplied to be used as the base URL for resolving
|
||||||
|
all relative URLs specified within the SMIL file while building the
|
||||||
|
MM. Default: <tt><i>http://localhost/</i></tt>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td valign=top >
|
<td valign=top >
|
||||||
<tt>vasid</tt>
|
<tt>vasid</tt>
|
||||||
|
@ -2378,6 +2390,19 @@ method), and information about the report sent to it via <a
|
||||||
headers</a>.
|
headers</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The SendMMS interface can also be used to send binary content
|
||||||
|
(e.g. binary-coded MMS, image or audio)
|
||||||
|
directly to Mbuni. This is done as follows:
|
||||||
|
<ul>
|
||||||
|
<li>Send the binary content as the body of an HTTP POST request to the
|
||||||
|
SendMMS port. Be sure to specify the correct body content type as part of
|
||||||
|
the HTTP request headers. This is interpreted according to the rules
|
||||||
|
set out <a href="#how_content">below</a>.
|
||||||
|
<li>Supply to the request URL all but the <tt>text</tt> and <tt>smil</tt> URL
|
||||||
|
parameters (as described above) as part of the request URL.
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
<a name="mms_service"></a>
|
<a name="mms_service"></a>
|
||||||
<h5>MMS Service Configuration</h5>
|
<h5>MMS Service Configuration</h5>
|
||||||
|
|
||||||
|
@ -2628,6 +2653,21 @@ A detailed list of configuration parameters for MMS Services is given below.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td valign=top >
|
||||||
|
<tt>pass-thro-headers</tt>
|
||||||
|
</td>
|
||||||
|
<td valign=top >
|
||||||
|
List
|
||||||
|
</td>
|
||||||
|
<td valign=top >
|
||||||
|
Set this to a comma-separated list of HTTP response headers that, if received from
|
||||||
|
the service response, should be passed to the MMC (with their
|
||||||
|
corresponding values) unchanged. Note
|
||||||
|
that these headers are merged with other headers generated by Mbuni.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td valign=top >
|
<td valign=top >
|
||||||
<tt>omit-empty</tt>
|
<tt>omit-empty</tt>
|
||||||
|
@ -2641,6 +2681,19 @@ A detailed list of configuration parameters for MMS Services is given below.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td valign=top >
|
||||||
|
<tt>suppress-reply</tt>
|
||||||
|
</td>
|
||||||
|
<td valign=top >
|
||||||
|
boolean
|
||||||
|
</td>
|
||||||
|
<td valign=top >
|
||||||
|
Set this to true if Mbuni not send a reply back to the user of
|
||||||
|
this service. Note that the request URL will still be invoked.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td valign=top >
|
<td valign=top >
|
||||||
<tt>assume-plain-text</tt>
|
<tt>assume-plain-text</tt>
|
||||||
|
|
|
@ -138,7 +138,9 @@ MULTI_GROUP(mms-service,
|
||||||
OCTSTR(faked-sender)
|
OCTSTR(faked-sender)
|
||||||
OCTSTR(catch-all)
|
OCTSTR(catch-all)
|
||||||
OCTSTR(omit-empty)
|
OCTSTR(omit-empty)
|
||||||
|
OCTSTR(suppress-reply)
|
||||||
OCTSTR(accept-x-mbuni-headers)
|
OCTSTR(accept-x-mbuni-headers)
|
||||||
|
OCTSTR(pass-thro-headers)
|
||||||
OCTSTR(assume-plain-text)
|
OCTSTR(assume-plain-text)
|
||||||
OCTSTR(accepted-mmscs)
|
OCTSTR(accepted-mmscs)
|
||||||
OCTSTR(denied-mmscs)
|
OCTSTR(denied-mmscs)
|
||||||
|
|
|
@ -91,6 +91,7 @@ static int free_envelope(MmsEnvelope *e, int removefromqueue);
|
||||||
* v - vasid -- from VASP
|
* v - vasid -- from VASP
|
||||||
* U - url1 -- e.g. for delivery report
|
* U - url1 -- e.g. for delivery report
|
||||||
* u - url2 -- e.g. for read report
|
* u - url2 -- e.g. for read report
|
||||||
|
* H - generic headers associated with message (e.g. for passing to MMC)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,7 +189,8 @@ MmsEnvelope *mms_queue_readenvelope(char *qf, char *mms_queuedir, int shouldbloc
|
||||||
char *line = octstr_get_cstr(s);
|
char *line = octstr_get_cstr(s);
|
||||||
int ch = line[0];
|
int ch = line[0];
|
||||||
char *res = line + 1;
|
char *res = line + 1;
|
||||||
|
char *ptmp;
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
Octstr *t;
|
Octstr *t;
|
||||||
MmsEnvelopeTo *to;
|
MmsEnvelopeTo *to;
|
||||||
|
@ -283,7 +285,21 @@ MmsEnvelope *mms_queue_readenvelope(char *qf, char *mms_queuedir, int shouldbloc
|
||||||
case 'u':
|
case 'u':
|
||||||
e->url2 = octstr_create(res);
|
e->url2 = octstr_create(res);
|
||||||
break;
|
break;
|
||||||
|
case 'H':
|
||||||
|
if (e->hdrs == NULL)
|
||||||
|
e->hdrs = http_create_empty_headers();
|
||||||
|
if ((ptmp = index(res, ':')) == NULL)
|
||||||
|
error(0, "Incorrectly formatted line %s in queue file %s!",
|
||||||
|
line, xqf);
|
||||||
|
else {
|
||||||
|
char *value = ptmp + 1;
|
||||||
|
char hname[512];
|
||||||
|
int xlen = (ptmp - res < sizeof hname) ? ptmp - res : -1 + sizeof hname;
|
||||||
|
strncpy(hname, res, xlen);
|
||||||
|
hname[xlen] = 0; /* terminate it. */
|
||||||
|
http_header_add(e->hdrs, hname, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
okfile = 1;
|
okfile = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -319,7 +335,7 @@ static int writeenvelope(MmsEnvelope *e, int newenv)
|
||||||
{
|
{
|
||||||
Octstr *tfname = NULL;
|
Octstr *tfname = NULL;
|
||||||
char *s;
|
char *s;
|
||||||
char buf[64];
|
char buf[512];
|
||||||
int fd;
|
int fd;
|
||||||
int i, n;
|
int i, n;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
@ -374,6 +390,23 @@ static int writeenvelope(MmsEnvelope *e, int newenv)
|
||||||
_putline(fd, "R", octstr_get_cstr(to->rcpt));
|
_putline(fd, "R", octstr_get_cstr(to->rcpt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Output headers if any. */
|
||||||
|
n = (e->hdrs) ? list_len(e->hdrs) : 0;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
Octstr *h = NULL, *v = NULL;
|
||||||
|
|
||||||
|
http_header_get(e->hdrs, i, &h, &v);
|
||||||
|
if (h && v) {
|
||||||
|
Octstr *x = octstr_format("%s:%s", octstr_get_cstr(h),
|
||||||
|
octstr_get_cstr(v));
|
||||||
|
_putline(fd, "H", octstr_get_cstr(x));
|
||||||
|
octstr_destroy(x);
|
||||||
|
}
|
||||||
|
if (h) octstr_destroy(h);
|
||||||
|
if (v) octstr_destroy(v);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
sprintf(buf, "%ld", e->created);
|
sprintf(buf, "%ld", e->created);
|
||||||
_putline(fd, "C", buf);
|
_putline(fd, "C", buf);
|
||||||
|
|
||||||
|
@ -626,6 +659,7 @@ Octstr *mms_queue_add(Octstr *from, List *to,
|
||||||
time_t senddate, time_t expirydate, MmsMsg *m, Octstr *token,
|
time_t senddate, time_t expirydate, MmsMsg *m, Octstr *token,
|
||||||
Octstr *vaspid, Octstr *vasid,
|
Octstr *vaspid, Octstr *vasid,
|
||||||
Octstr *url1, Octstr *url2,
|
Octstr *url1, Octstr *url2,
|
||||||
|
List *hdrs,
|
||||||
int dlr,
|
int dlr,
|
||||||
char *directory, Octstr *mmscname)
|
char *directory, Octstr *mmscname)
|
||||||
{
|
{
|
||||||
|
@ -684,6 +718,7 @@ Octstr *mms_queue_add(Octstr *from, List *to,
|
||||||
e->vasid = vasid;
|
e->vasid = vasid;
|
||||||
e->url1 = url1;
|
e->url1 = url1;
|
||||||
e->url2 = url2;
|
e->url2 = url2;
|
||||||
|
e->hdrs = hdrs ? http_header_duplicate(hdrs) : NULL;
|
||||||
|
|
||||||
e->dlr = dlr;
|
e->dlr = dlr;
|
||||||
e->bill.billed = 0;
|
e->bill.billed = 0;
|
||||||
|
@ -787,6 +822,9 @@ static int free_envelope(MmsEnvelope *e, int removefromqueue)
|
||||||
|
|
||||||
if (e->url2)
|
if (e->url2)
|
||||||
octstr_destroy(e->url2);
|
octstr_destroy(e->url2);
|
||||||
|
|
||||||
|
if (e->hdrs)
|
||||||
|
http_destroy_headers(e->hdrs);
|
||||||
|
|
||||||
if (removefromqueue) {
|
if (removefromqueue) {
|
||||||
char fname[2*QFNAMEMAX];
|
char fname[2*QFNAMEMAX];
|
||||||
|
|
|
@ -38,9 +38,10 @@ typedef struct MmsEnvelope {
|
||||||
|
|
||||||
Octstr *url1; /* Generic URLs (2) associated with message. */
|
Octstr *url1; /* Generic URLs (2) associated with message. */
|
||||||
Octstr *url2;
|
Octstr *url2;
|
||||||
|
List *hdrs; /* Generic list of headers associated with message. */
|
||||||
|
|
||||||
List *to; /* List of recipients: MmsEnvelopeTo */
|
List *to; /* List of recipients: MmsEnvelopeTo */
|
||||||
|
|
||||||
Octstr *subject; /* Message subject (if any). */
|
Octstr *subject; /* Message subject (if any). */
|
||||||
|
|
||||||
time_t created; /* date/time this queue entry was made. */
|
time_t created; /* date/time this queue entry was made. */
|
||||||
|
@ -90,6 +91,7 @@ extern Octstr *mms_queue_add(Octstr *from, List *to,
|
||||||
time_t senddate, time_t expirydate, MmsMsg *m, Octstr *token,
|
time_t senddate, time_t expirydate, MmsMsg *m, Octstr *token,
|
||||||
Octstr *vaspid, Octstr *vasid,
|
Octstr *vaspid, Octstr *vasid,
|
||||||
Octstr *url1, Octstr *url2,
|
Octstr *url1, Octstr *url2,
|
||||||
|
List *hdrs,
|
||||||
int dlr,
|
int dlr,
|
||||||
char *directory, Octstr *mmscname);
|
char *directory, Octstr *mmscname);
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,7 @@ static void mm7soap_receive(MmsHTTPClientInfo *h)
|
||||||
delivert, expiryt, m, linkedid,
|
delivert, expiryt, m, linkedid,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(incoming_qdir),
|
octstr_get_cstr(incoming_qdir),
|
||||||
octstr_imm(MM_NAME));
|
octstr_imm(MM_NAME));
|
||||||
|
@ -363,6 +364,7 @@ static void mm7eaif_receive(MmsHTTPClientInfo *h)
|
||||||
h->m->id, NULL, deliveryt, expiryt, m, NULL,
|
h->m->id, NULL, deliveryt, expiryt, m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(incoming_qdir),
|
octstr_get_cstr(incoming_qdir),
|
||||||
octstr_imm(MM_NAME));
|
octstr_imm(MM_NAME));
|
||||||
|
@ -512,6 +514,7 @@ static Octstr *mm7soap_send(MmscGrp *mmc, Octstr *from, Octstr *to,
|
||||||
Octstr *transid,
|
Octstr *transid,
|
||||||
Octstr *linkedid,
|
Octstr *linkedid,
|
||||||
char *vasid,
|
char *vasid,
|
||||||
|
List *hdrs,
|
||||||
MmsMsg *m, Octstr **error)
|
MmsMsg *m, Octstr **error)
|
||||||
{
|
{
|
||||||
Octstr *ret = NULL;
|
Octstr *ret = NULL;
|
||||||
|
@ -543,7 +546,10 @@ static Octstr *mm7soap_send(MmscGrp *mmc, Octstr *from, Octstr *to,
|
||||||
*error = octstr_format("Failed to convert SOAP message 2 HTTP Msg!");
|
*error = octstr_format("Failed to convert SOAP message 2 HTTP Msg!");
|
||||||
goto done1;
|
goto done1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hdrs)
|
||||||
|
http_header_combine(rh, hdrs); /* If specified, then update and pass on. */
|
||||||
|
|
||||||
hstatus = mmsbox_url_fetch_content(HTTP_METHOD_POST, mmc->mmsc_url, rh, body, &ph,&rbody);
|
hstatus = mmsbox_url_fetch_content(HTTP_METHOD_POST, mmc->mmsc_url, rh, body, &ph,&rbody);
|
||||||
if (hstatus != HTTP_OK) {
|
if (hstatus != HTTP_OK) {
|
||||||
*error = octstr_format("Failed to contact MMC[url=%s] => HTTP returned status = %d!",
|
*error = octstr_format("Failed to contact MMC[url=%s] => HTTP returned status = %d!",
|
||||||
|
@ -619,6 +625,7 @@ done1:
|
||||||
static Octstr *mm7eaif_send(MmscGrp *mmc, Octstr *from, Octstr *to,
|
static Octstr *mm7eaif_send(MmscGrp *mmc, Octstr *from, Octstr *to,
|
||||||
Octstr *transid,
|
Octstr *transid,
|
||||||
char *vasid,
|
char *vasid,
|
||||||
|
List *hdrs,
|
||||||
MmsMsg *m, Octstr **error)
|
MmsMsg *m, Octstr **error)
|
||||||
{
|
{
|
||||||
Octstr *ret = NULL, *resp = NULL;
|
Octstr *ret = NULL, *resp = NULL;
|
||||||
|
@ -645,6 +652,10 @@ static Octstr *mm7eaif_send(MmscGrp *mmc, Octstr *from, Octstr *to,
|
||||||
else
|
else
|
||||||
msgtype = "ReadReply";
|
msgtype = "ReadReply";
|
||||||
http_header_add(rh, "X-NOKIA-MMSC-Message-Type", msgtype);
|
http_header_add(rh, "X-NOKIA-MMSC-Message-Type", msgtype);
|
||||||
|
|
||||||
|
if (hdrs)
|
||||||
|
http_header_combine(rh, hdrs); /* If specified, then update and pass on. */
|
||||||
|
|
||||||
http_header_add(rh, "Content-Type", "application/vnd.wap.mms-message");
|
http_header_add(rh, "Content-Type", "application/vnd.wap.mms-message");
|
||||||
|
|
||||||
/* Patch the message FROM and TO fields. */
|
/* Patch the message FROM and TO fields. */
|
||||||
|
@ -713,6 +724,7 @@ static int mms_sendtommsc(MmscGrp *mmc, Octstr *from, Octstr *to, Octstr *transi
|
||||||
MmsMsg *m,
|
MmsMsg *m,
|
||||||
Octstr *dlr_url,
|
Octstr *dlr_url,
|
||||||
Octstr *rr_url,
|
Octstr *rr_url,
|
||||||
|
List *hdrs,
|
||||||
Octstr **err)
|
Octstr **err)
|
||||||
{
|
{
|
||||||
Octstr *id = NULL;
|
Octstr *id = NULL;
|
||||||
|
@ -720,9 +732,9 @@ static int mms_sendtommsc(MmscGrp *mmc, Octstr *from, Octstr *to, Octstr *transi
|
||||||
|
|
||||||
mutex_lock(mmc->mutex); { /* Grab a lock on it. */
|
mutex_lock(mmc->mutex); { /* Grab a lock on it. */
|
||||||
if (mmc->type == SOAP_MMSC)
|
if (mmc->type == SOAP_MMSC)
|
||||||
id = mm7soap_send(mmc, from, to, transid, linkedid, vasid, m, err);
|
id = mm7soap_send(mmc, from, to, transid, linkedid, vasid, hdrs, m, err);
|
||||||
else if (mmc->type == EAIF_MMSC)
|
else if (mmc->type == EAIF_MMSC)
|
||||||
id = mm7eaif_send(mmc, from, to, transid, vasid, m, err);
|
id = mm7eaif_send(mmc, from, to, transid, vasid, hdrs, m, err);
|
||||||
else
|
else
|
||||||
error(0, "MMC[%s] of unknown type, can't send!",
|
error(0, "MMC[%s] of unknown type, can't send!",
|
||||||
mmc->id ? octstr_get_cstr(mmc->id) : "");
|
mmc->id ? octstr_get_cstr(mmc->id) : "");
|
||||||
|
@ -829,6 +841,7 @@ static int sendMsg(MmsEnvelope *e)
|
||||||
e->vasid ? octstr_get_cstr(e->vasid) : NULL,
|
e->vasid ? octstr_get_cstr(e->vasid) : NULL,
|
||||||
msg,
|
msg,
|
||||||
e->url1, e->url2,
|
e->url1, e->url2,
|
||||||
|
e->hdrs,
|
||||||
&err);
|
&err);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
|
@ -256,7 +256,9 @@ static Octstr *filename2content_type(char *fname);
|
||||||
static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
||||||
Octstr *base_url, int type, MmsEnvelope *e,
|
Octstr *base_url, int type, MmsEnvelope *e,
|
||||||
Octstr *svc_name, Octstr *faked_sender,
|
Octstr *svc_name, Octstr *faked_sender,
|
||||||
int accept_x_headers, Octstr **err);
|
int accept_x_headers,
|
||||||
|
List *passthro_headers,
|
||||||
|
Octstr **err);
|
||||||
static int fetch_serviceurl(MmsEnvelope *e,
|
static int fetch_serviceurl(MmsEnvelope *e,
|
||||||
MmsService *ms, MmsMsg *m,
|
MmsService *ms, MmsMsg *m,
|
||||||
MIMEEntity *msg,
|
MIMEEntity *msg,
|
||||||
|
@ -351,14 +353,25 @@ static int fetch_serviceurl(MmsEnvelope *e,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have the content, make the message and write it to out-going. */
|
/* If we have the content, make the message and write it to out-going.
|
||||||
if (!ctype || !rb)
|
* if we have no content, or we have been told to suppress the reply, then we suppress
|
||||||
|
*/
|
||||||
|
if (!ctype || !rb ||
|
||||||
|
(octstr_len(rb) == 0 && ms->omitempty) ||
|
||||||
|
ms->noreply) {
|
||||||
|
info(0, "MMSBox.service_request: Suppressed reply for service %s, "
|
||||||
|
"suppress-reply=%s, omit-empty=%s, reply-len=%ld, content-type=%s",
|
||||||
|
octstr_get_cstr(ms->name), ms->noreply ? "true" : "false",
|
||||||
|
ms->omitempty ? "true" : "false",
|
||||||
|
rb ? octstr_len(rb) : 0,
|
||||||
|
ctype ? octstr_get_cstr(ctype) : "");
|
||||||
goto done;
|
goto done;
|
||||||
else {
|
} else {
|
||||||
Octstr *base_url = url_path_prefix(ms->url, typ);
|
Octstr *base_url = url_path_prefix(ms->url, typ);
|
||||||
res = make_and_queue_msg(rb, ctype, rph, base_url,
|
res = make_and_queue_msg(rb, ctype, rph, base_url,
|
||||||
typ, e, ms->name, ms->faked_sender,
|
typ, e, ms->name, ms->faked_sender,
|
||||||
ms->accept_x_headers, err);
|
ms->accept_x_headers, ms->passthro_headers,
|
||||||
|
err);
|
||||||
|
|
||||||
if (base_url)
|
if (base_url)
|
||||||
octstr_destroy(base_url);
|
octstr_destroy(base_url);
|
||||||
|
@ -816,13 +829,15 @@ static void add_msg_parts(MIMEEntity *res, xmlNodePtr node, Octstr *base_url,
|
||||||
static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
||||||
Octstr *base_url, int type, MmsEnvelope *e,
|
Octstr *base_url, int type, MmsEnvelope *e,
|
||||||
Octstr *svc_name, Octstr *faked_sender,
|
Octstr *svc_name, Octstr *faked_sender,
|
||||||
int accept_x_headers, Octstr **err)
|
int accept_x_headers, List *passthro_headers,
|
||||||
|
Octstr **err)
|
||||||
{
|
{
|
||||||
Octstr *from = NULL, *subject = NULL, *turl = get_toplevel_url(base_url);
|
Octstr *from = NULL, *subject = NULL, *turl = get_toplevel_url(base_url);
|
||||||
Octstr *dlr_url = NULL, *rr_url = NULL, *mmc = NULL;
|
Octstr *dlr_url = NULL, *rr_url = NULL, *mmc = NULL;
|
||||||
MmsMsg *m = NULL;
|
MmsMsg *m = NULL;
|
||||||
MIMEEntity *me = mime_entity_create();
|
MIMEEntity *me = mime_entity_create();
|
||||||
|
List *hdrs = NULL;
|
||||||
|
|
||||||
time_t expiryt = 0;
|
time_t expiryt = 0;
|
||||||
Octstr *x;
|
Octstr *x;
|
||||||
List *xto = list_create();;
|
List *xto = list_create();;
|
||||||
|
@ -973,6 +988,35 @@ static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
||||||
octstr_get_cstr(svc_name));
|
octstr_get_cstr(svc_name));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (passthro_headers && reply_headers) { /* if user wants passthro headers, get them and add them. */
|
||||||
|
int n = list_len(reply_headers);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hdrs = http_create_empty_headers();
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
Octstr *h = NULL, *v = NULL;
|
||||||
|
int j;
|
||||||
|
http_header_get(reply_headers, i, &h, &v);
|
||||||
|
if (h == NULL || v == NULL)
|
||||||
|
goto loop;
|
||||||
|
|
||||||
|
/* Check for this header in
|
||||||
|
* pass thro list.
|
||||||
|
*/
|
||||||
|
for (j = 0; j < list_len(passthro_headers); j++)
|
||||||
|
if (octstr_case_compare(h, list_get(passthro_headers, j)) == 0) {
|
||||||
|
http_header_add(hdrs, octstr_get_cstr(h), octstr_get_cstr(v));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loop:
|
||||||
|
if (h)
|
||||||
|
octstr_destroy(h);
|
||||||
|
if (v)
|
||||||
|
octstr_destroy(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Write to queue. */
|
/* Write to queue. */
|
||||||
x = mms_queue_add(from, xto, subject,
|
x = mms_queue_add(from, xto, subject,
|
||||||
e ? e->fromproxy : NULL,
|
e ? e->fromproxy : NULL,
|
||||||
|
@ -980,6 +1024,7 @@ static int make_and_queue_msg(Octstr *data, Octstr *ctype, List *reply_headers,
|
||||||
time(NULL), expiryt, m, NULL,
|
time(NULL), expiryt, m, NULL,
|
||||||
NULL, svc_name,
|
NULL, svc_name,
|
||||||
dlr_url, rr_url,
|
dlr_url, rr_url,
|
||||||
|
hdrs,
|
||||||
(dlr_url != NULL),
|
(dlr_url != NULL),
|
||||||
octstr_get_cstr(outgoing_qdir),
|
octstr_get_cstr(outgoing_qdir),
|
||||||
octstr_imm(MM_NAME));
|
octstr_imm(MM_NAME));
|
||||||
|
@ -1005,7 +1050,8 @@ done:
|
||||||
mms_destroy(m);
|
mms_destroy(m);
|
||||||
if (xto)
|
if (xto)
|
||||||
list_destroy(xto, (list_item_destructor_t *)octstr_destroy);
|
list_destroy(xto, (list_item_destructor_t *)octstr_destroy);
|
||||||
|
if (hdrs)
|
||||||
|
http_destroy_headers(hdrs);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,10 +1092,13 @@ static void sendmms_func(void *unused)
|
||||||
Octstr *data, *ctype = NULL, *mmc, *to, *from, *dlr_url;
|
Octstr *data, *ctype = NULL, *mmc, *to, *from, *dlr_url;
|
||||||
Octstr *rr_url, *subject = NULL;
|
Octstr *rr_url, *subject = NULL;
|
||||||
List *lto = NULL, *rh = http_create_empty_headers();
|
List *lto = NULL, *rh = http_create_empty_headers();
|
||||||
Octstr *rb = NULL, *base_url = octstr_imm("http://localhost");
|
Octstr *rb = NULL, *base_url;
|
||||||
int i, n, res = 0;
|
int i, n, res = 0;
|
||||||
Octstr *vasid = http_cgi_variable(cgivars, "vasid");
|
Octstr *vasid = http_cgi_variable(cgivars, "vasid");
|
||||||
|
|
||||||
|
if ((base_url = http_cgi_variable(cgivars, "base-url")) == NULL)
|
||||||
|
base_url = octstr_imm("http://localhost");
|
||||||
|
|
||||||
if ((data = http_cgi_variable(cgivars, "text")) != NULL) { /* text. */
|
if ((data = http_cgi_variable(cgivars, "text")) != NULL) { /* text. */
|
||||||
Octstr *charset = http_cgi_variable(cgivars, "charset");
|
Octstr *charset = http_cgi_variable(cgivars, "charset");
|
||||||
|
|
||||||
|
@ -1058,7 +1107,10 @@ static void sendmms_func(void *unused)
|
||||||
octstr_format_append(ctype, "; charset=%S", charset);
|
octstr_format_append(ctype, "; charset=%S", charset);
|
||||||
} else if ((data = http_cgi_variable(cgivars, "smil")) != NULL) /* smil. */
|
} else if ((data = http_cgi_variable(cgivars, "smil")) != NULL) /* smil. */
|
||||||
ctype = octstr_imm("application/smil");
|
ctype = octstr_imm("application/smil");
|
||||||
else
|
else if (body) { /* if all above fails, use send-mms msg body if any. */
|
||||||
|
data = octstr_duplicate(body);
|
||||||
|
ctype = http_header_value(h, octstr_imm("Content-Type"));
|
||||||
|
} else
|
||||||
rb = octstr_imm("Missing content");
|
rb = octstr_imm("Missing content");
|
||||||
|
|
||||||
dlr_url = http_cgi_variable(cgivars, "dlr-url");
|
dlr_url = http_cgi_variable(cgivars, "dlr-url");
|
||||||
|
@ -1086,12 +1138,10 @@ static void sendmms_func(void *unused)
|
||||||
if (lto) {
|
if (lto) {
|
||||||
for (i = 0, n = list_len(lto); i < n; i++) {
|
for (i = 0, n = list_len(lto); i < n; i++) {
|
||||||
Octstr *x = list_get(lto, i);
|
Octstr *x = list_get(lto, i);
|
||||||
|
|
||||||
http_header_add(rh, "X-Mbuni-To", octstr_get_cstr(x));
|
http_header_add(rh, "X-Mbuni-To", octstr_get_cstr(x));
|
||||||
}
|
}
|
||||||
list_destroy(lto, (list_item_destructor_t *)octstr_destroy);
|
list_destroy(lto, (list_item_destructor_t *)octstr_destroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dlr_url)
|
if (dlr_url)
|
||||||
http_header_add(rh, "X-Mbuni-DLR-Url", octstr_get_cstr(dlr_url));
|
http_header_add(rh, "X-Mbuni-DLR-Url", octstr_get_cstr(dlr_url));
|
||||||
|
|
||||||
|
@ -1109,12 +1159,11 @@ static void sendmms_func(void *unused)
|
||||||
res = make_and_queue_msg(data, ctype, rh, base_url, URL_TYPE, NULL,
|
res = make_and_queue_msg(data, ctype, rh, base_url, URL_TYPE, NULL,
|
||||||
vasid ? vasid : octstr_imm("sendmms-user"),
|
vasid ? vasid : octstr_imm("sendmms-user"),
|
||||||
u->faked_sender,
|
u->faked_sender,
|
||||||
1, &err);
|
1, NULL, &err);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
rb = octstr_imm("Error in message conversion");
|
rb = octstr_imm("Error in message conversion");
|
||||||
} else if (!rb)
|
} else if (!rb)
|
||||||
rb = octstr_imm("Failed to send message");
|
rb = octstr_imm("Failed to send message");
|
||||||
|
|
||||||
http_send_reply(client, HTTP_BAD_REQUEST, hh,
|
http_send_reply(client, HTTP_BAD_REQUEST, hh,
|
||||||
rb ? rb : octstr_imm("Sent"));
|
rb ? rb : octstr_imm("Sent"));
|
||||||
info(0, "MMSBox.mmssend: u=%s, %s",
|
info(0, "MMSBox.mmssend: u=%s, %s",
|
||||||
|
@ -1123,7 +1172,6 @@ static void sendmms_func(void *unused)
|
||||||
|
|
||||||
if (rh)
|
if (rh)
|
||||||
http_destroy_headers(rh);
|
http_destroy_headers(rh);
|
||||||
octstr_destroy(base_url);
|
|
||||||
if (ctype)
|
if (ctype)
|
||||||
octstr_destroy(ctype);
|
octstr_destroy(ctype);
|
||||||
if (rb)
|
if (rb)
|
||||||
|
|
|
@ -223,8 +223,18 @@ int mms_load_mmsbox_settings(mCfg *cfg, gwthread_func_t *mmsc_handler_func)
|
||||||
catchall = m;
|
catchall = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
mms_cfg_get_bool(x, octstr_imm("omit-empty"), &m->omitempty);
|
if (mms_cfg_get_bool(x, octstr_imm("omit-empty"), &m->omitempty) < 0)
|
||||||
|
m->omitempty = 0;
|
||||||
|
if (mms_cfg_get_bool(x, octstr_imm("suppress-reply"), &m->noreply) < 0)
|
||||||
|
m->noreply = 0;
|
||||||
mms_cfg_get_bool(x, octstr_imm("accept-x-mbuni-headers"), &m->accept_x_headers);
|
mms_cfg_get_bool(x, octstr_imm("accept-x-mbuni-headers"), &m->accept_x_headers);
|
||||||
|
|
||||||
|
if ((s = mms_cfg_get(x, octstr_imm("pass-thro-headers"))) != NULL) {
|
||||||
|
m->passthro_headers = octstr_split(s, octstr_imm(","));
|
||||||
|
octstr_destroy(s);
|
||||||
|
} else
|
||||||
|
m->passthro_headers = NULL;
|
||||||
|
|
||||||
mms_cfg_get_bool(x, octstr_imm("assume-plain-text"), &m->assume_plain_text);
|
mms_cfg_get_bool(x, octstr_imm("assume-plain-text"), &m->assume_plain_text);
|
||||||
|
|
||||||
if ((s = mms_cfg_get(x, octstr_imm("accepted-mmscs"))) != NULL) {
|
if ((s = mms_cfg_get(x, octstr_imm("accepted-mmscs"))) != NULL) {
|
||||||
|
|
|
@ -44,7 +44,10 @@ typedef struct MmsService {
|
||||||
Octstr *name; /* name of service. */
|
Octstr *name; /* name of service. */
|
||||||
int isdefault;
|
int isdefault;
|
||||||
int omitempty;
|
int omitempty;
|
||||||
|
int noreply;
|
||||||
int accept_x_headers;
|
int accept_x_headers;
|
||||||
|
List *passthro_headers;
|
||||||
|
|
||||||
int assume_plain_text;
|
int assume_plain_text;
|
||||||
List *keywords; /* List of keywords matched. */
|
List *keywords; /* List of keywords matched. */
|
||||||
enum {TRANS_TYPE_GET_URL, TRANS_TYPE_POST_URL, TRANS_TYPE_FILE, TRANS_TYPE_EXEC,
|
enum {TRANS_TYPE_GET_URL, TRANS_TYPE_POST_URL, TRANS_TYPE_FILE, TRANS_TYPE_EXEC,
|
||||||
|
|
|
@ -157,6 +157,7 @@ int main(int argc, char *argv[])
|
||||||
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -188,6 +189,7 @@ int main(int argc, char *argv[])
|
||||||
mresp, NULL,
|
mresp, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -257,6 +259,7 @@ int main(int argc, char *argv[])
|
||||||
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -292,6 +295,7 @@ int main(int argc, char *argv[])
|
||||||
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
0, time(NULL) + settings->default_msgexpiry, msg, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
|
|
@ -278,6 +278,7 @@ static int sendMsg(MmsEnvelope *e)
|
||||||
tnow, tnow+settings->default_msgexpiry, m, NULL,
|
tnow, tnow+settings->default_msgexpiry, m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
qdir,
|
qdir,
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -418,6 +419,7 @@ int mms_sendtomobile(Octstr *from, Octstr *to,
|
||||||
ret = mms_queue_add(from, l, subject, fromproxy, NULL, 0, expires, m,
|
ret = mms_queue_add(from, l, subject, fromproxy, NULL, 0, expires, m,
|
||||||
x, NULL, NULL,
|
x, NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr, mobile_qdir,
|
dlr, mobile_qdir,
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
octstr_destroy(x);
|
octstr_destroy(x);
|
||||||
|
@ -459,6 +461,7 @@ static int mms_sendtoproxy(Octstr *from, Octstr *to,
|
||||||
ret = mms_queue_add(from, l, subject, NULL, proxy, 0, expires, msg,NULL,
|
ret = mms_queue_add(from, l, subject, NULL, proxy, 0, expires, msg,NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr, mm4_qdir,
|
dlr, mm4_qdir,
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
list_destroy(l, NULL);
|
list_destroy(l, NULL);
|
||||||
|
|
|
@ -411,6 +411,7 @@ static int sendNotify(MmsEnvelope *e)
|
||||||
tnow, tnow+settings->default_msgexpiry, m, NULL,
|
tnow, tnow+settings->default_msgexpiry, m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->mm1_queuedir),
|
octstr_get_cstr(settings->mm1_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
|
|
@ -543,7 +543,8 @@ static void sendmms_proxy(MmsHTTPClientInfo *h)
|
||||||
qf = mms_queue_add(from, to, subject,
|
qf = mms_queue_add(from, to, subject,
|
||||||
NULL, NULL, deliveryt, expiryt, m, NULL,
|
NULL, NULL, deliveryt, expiryt, m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -737,6 +738,7 @@ static void sendmms_proxy(MmsHTTPClientInfo *h)
|
||||||
NULL, NULL, deliveryt, expiryt, mfwd, NULL,
|
NULL, NULL, deliveryt, expiryt, mfwd, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -825,6 +827,7 @@ static void sendmms_proxy(MmsHTTPClientInfo *h)
|
||||||
mrep, NULL,
|
mrep, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -948,6 +951,7 @@ static void sendmms_proxy(MmsHTTPClientInfo *h)
|
||||||
time(NULL) + settings->default_msgexpiry, mrpt, NULL,
|
time(NULL) + settings->default_msgexpiry, mrpt, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -1002,6 +1006,7 @@ static void sendmms_proxy(MmsHTTPClientInfo *h)
|
||||||
m, NULL,
|
m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -1614,6 +1619,7 @@ static void mm7soap_dispatch(MmsHTTPClientInfo *h)
|
||||||
delivert, expiryt, m, NULL,
|
delivert, expiryt, m, NULL,
|
||||||
h->vasp->id, vasid,
|
h->vasp->id, vasid,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -1865,6 +1871,7 @@ static void mm7eaif_dispatch(MmsHTTPClientInfo *h)
|
||||||
NULL, NULL, deliveryt, expiryt, m, NULL,
|
NULL, NULL, deliveryt, expiryt, m, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
NULL,
|
||||||
dlr,
|
dlr,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
|
|
@ -76,7 +76,9 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
int cfidx;
|
int cfidx;
|
||||||
int msize;
|
int msize;
|
||||||
|
|
||||||
|
List *h = NULL;
|
||||||
|
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -140,11 +142,17 @@ int main(int argc, char *argv[])
|
||||||
if (!m)
|
if (!m)
|
||||||
panic(0, "No Message supplied, or failed to decode binary data!");
|
panic(0, "No Message supplied, or failed to decode binary data!");
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
h = http_create_empty_headers();
|
||||||
|
http_header_add(h, "X-Mms-Tool", "mmssend");
|
||||||
|
http_header_add(h, "X-Mms-CalledFrom", "Terminal");
|
||||||
|
#endif
|
||||||
s = mms_queue_add(from, to, NULL, NULL, NULL, time(NULL),
|
s = mms_queue_add(from, to, NULL, NULL, NULL, time(NULL),
|
||||||
time(NULL) + settings->default_msgexpiry, m,
|
time(NULL) + settings->default_msgexpiry, m,
|
||||||
NULL,
|
NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
h,
|
||||||
0,
|
0,
|
||||||
octstr_get_cstr(settings->global_queuedir),
|
octstr_get_cstr(settings->global_queuedir),
|
||||||
settings->host_alias);
|
settings->host_alias);
|
||||||
|
@ -158,6 +166,10 @@ int main(int argc, char *argv[])
|
||||||
printf("Queued: %s, mmbox=%s\n",
|
printf("Queued: %s, mmbox=%s\n",
|
||||||
octstr_get_cstr(s), mmbox ? octstr_get_cstr(mmbox) : "");
|
octstr_get_cstr(s), mmbox ? octstr_get_cstr(mmbox) : "");
|
||||||
octstr_destroy(s);
|
octstr_destroy(s);
|
||||||
|
|
||||||
|
if (h)
|
||||||
|
http_destroy_headers(h);
|
||||||
|
|
||||||
mms_lib_shutdown();
|
mms_lib_shutdown();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue