|
@@ -0,0 +1,334 @@
|
|
|
+/*
|
|
|
+ * pua_rpc module - RPC pua module
|
|
|
+ *
|
|
|
+ * Copyright (C) 2014-2016 Juha Heinanen
|
|
|
+ *
|
|
|
+ * This file is part of Kamailio, a free SIP server.
|
|
|
+ *
|
|
|
+ * Kamailio 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
|
|
|
+ *
|
|
|
+ * Kamailio 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
|
|
|
+ */
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \file
|
|
|
+ * \brief Kamailio pua_rpc :: rpc API interface for pua implementing pua.publish rpc command
|
|
|
+ * Module: \ref pua_rpc
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <time.h>
|
|
|
+
|
|
|
+#include "../../core/sr_module.h"
|
|
|
+#include "../../core/parser/parse_expires.h"
|
|
|
+#include "../../core/dprint.h"
|
|
|
+#include "../../core/mem/shm_mem.h"
|
|
|
+#include "../../core/parser/msg_parser.h"
|
|
|
+#include "../../core/str.h"
|
|
|
+#include "../../core/mem/mem.h"
|
|
|
+#include "../../core/pt.h"
|
|
|
+#include "../../core/rpc_lookup.h"
|
|
|
+#include "../../core/strutils.h"
|
|
|
+/* #include "../tm/tm_load.h" */
|
|
|
+#include "../pua/pua_bind.h"
|
|
|
+
|
|
|
+MODULE_VERSION
|
|
|
+
|
|
|
+static int mod_init(void);
|
|
|
+
|
|
|
+send_publish_t pua_send_publish;
|
|
|
+
|
|
|
+static const char* publish_doc[2] = {
|
|
|
+ "sends publish request and waits for the final reply, using a list "
|
|
|
+ "of string parameters: presentity uri, expires, event package, "
|
|
|
+ "content type, id, etag, outbound proxy, extra headers, "
|
|
|
+ " and body (optional)",
|
|
|
+ 0
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static int publish_callback(ua_pres_t* hentity, struct sip_msg* reply)
|
|
|
+{
|
|
|
+ rpc_delayed_ctx_t* dctx;
|
|
|
+ void* c;
|
|
|
+ rpc_t* rpc;
|
|
|
+ struct hdr_field* hdr= NULL;
|
|
|
+ int statuscode;
|
|
|
+ int expires;
|
|
|
+ int found;
|
|
|
+ str etag;
|
|
|
+ str reason = {0, 0};
|
|
|
+
|
|
|
+ LM_DBG("running callback\n");
|
|
|
+
|
|
|
+ if (reply == NULL || hentity == NULL || hentity->cb_param == NULL) {
|
|
|
+ LM_ERR("NULL reply or hentity parameter\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ dctx = (rpc_delayed_ctx_t *)(hentity->cb_param);
|
|
|
+ hentity->cb_param = NULL;
|
|
|
+ if (dctx == 0){
|
|
|
+ BUG("null delayed reply ctx\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ rpc = &dctx->rpc;
|
|
|
+ c = dctx->reply_ctx;
|
|
|
+
|
|
|
+ if (reply == FAKED_REPLY) {
|
|
|
+ statuscode = 408;
|
|
|
+ reason.s = "Request Timeout";
|
|
|
+ reason.len = strlen(reason.s);
|
|
|
+ } else {
|
|
|
+ statuscode = reply->first_line.u.reply.statuscode;
|
|
|
+ reason = reply->first_line.u.reply.reason;
|
|
|
+ }
|
|
|
+ rpc->add(c, "dS", statuscode, &reason);
|
|
|
+
|
|
|
+ if (statuscode == 200) {
|
|
|
+ expires = ((exp_body_t*)reply->expires->parsed)->val;
|
|
|
+ LM_DBG("expires = %d\n", expires);
|
|
|
+ hdr = reply->headers;
|
|
|
+ found = 0;
|
|
|
+ while (hdr != NULL) {
|
|
|
+ if(cmp_hdrname_strzn(&hdr->name, "SIP-ETag",8) == 0) {
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ hdr = hdr->next;
|
|
|
+ }
|
|
|
+ if (found == 0) {
|
|
|
+ LM_ERR("SIP-ETag header field not found\n");
|
|
|
+ rpc->delayed_ctx_close(dctx);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ etag = hdr->body;
|
|
|
+ LM_DBG("SIP-Etag = %.*s\n", etag.len, etag.s);
|
|
|
+ rpc->add(c, "Sd", &etag, expires);
|
|
|
+ }
|
|
|
+
|
|
|
+ rpc->delayed_ctx_close(dctx);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void publish(rpc_t* rpc, void* c)
|
|
|
+{
|
|
|
+ str pres_uri, expires, event, content_type, id, etag,
|
|
|
+ outbound_proxy, extra_headers, body;
|
|
|
+ rpc_delayed_ctx_t* dctx;
|
|
|
+ int exp, sign, ret, err_ret, sip_error;
|
|
|
+ char err_buf[MAX_REASON_LEN];
|
|
|
+ struct sip_uri uri;
|
|
|
+ publ_info_t publ;
|
|
|
+
|
|
|
+ body.s = 0;
|
|
|
+ body.len = 0;
|
|
|
+ dctx = 0;
|
|
|
+
|
|
|
+ LM_DBG("rpc publishing ...\n");
|
|
|
+
|
|
|
+ if ((rpc->capabilities == 0) ||
|
|
|
+ !(rpc->capabilities(c) & RPC_DELAYED_REPLY)) {
|
|
|
+ rpc->fault(c, 600, "Reply wait/async mode not supported"
|
|
|
+ " by this rpc transport");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = rpc->scan(c, "SSSSSSSS*S", &pres_uri, &expires, &event,
|
|
|
+ &content_type, &id, &etag, &outbound_proxy,
|
|
|
+ &extra_headers, &body);
|
|
|
+ if (ret < 8) {
|
|
|
+ rpc->fault(c, 400, "too few parameters (%d)", ret);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parse_uri(pres_uri.s, pres_uri.len, &uri) <0) {
|
|
|
+ LM_ERR("bad resentity uri\n");
|
|
|
+ rpc->fault(c, 400, "Invalid presentity uri '%s'", pres_uri.s);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LM_DBG("presentity uri '%.*s'\n", pres_uri.len, pres_uri.s);
|
|
|
+
|
|
|
+ if (expires.s[0]== '-') {
|
|
|
+ sign= -1;
|
|
|
+ expires.s++;
|
|
|
+ expires.len--;
|
|
|
+ } else {
|
|
|
+ sign = 1;
|
|
|
+ }
|
|
|
+ if (str2int(&expires, (unsigned int*)&exp) < 0) {
|
|
|
+ LM_ERR("invalid expires parameter\n" );
|
|
|
+ rpc->fault(c, 400, "Invalid expires '%s'", expires.s);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ exp = exp * sign;
|
|
|
+ LM_DBG("expires '%d'\n", exp);
|
|
|
+
|
|
|
+ LM_DBG("event '%.*s'\n", event.len, event.s);
|
|
|
+
|
|
|
+ LM_DBG("content type '%.*s'\n", content_type.len, content_type.s);
|
|
|
+
|
|
|
+ LM_DBG("id '%.*s'\n", id.len, id.s);
|
|
|
+
|
|
|
+ LM_DBG("ETag '%.*s'\n", etag.len, etag.s);
|
|
|
+
|
|
|
+ LM_DBG("outbound_proxy '%.*s'\n", outbound_proxy.len, outbound_proxy.s);
|
|
|
+
|
|
|
+ LM_DBG("extra headers '%.*s'\n", extra_headers.len, extra_headers.s);
|
|
|
+
|
|
|
+ if (body.len > 0) LM_DBG("body '%.*s'\n", body.len, body.s);
|
|
|
+
|
|
|
+ if ((body.s == 0) &&
|
|
|
+ (content_type.len != 1 || content_type.s[0] != '.')) {
|
|
|
+ LM_ERR("body is missing, but content type is not .\n");
|
|
|
+ rpc->fault(c, 400, "Body is missing");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(&publ, 0, sizeof(publ_info_t));
|
|
|
+
|
|
|
+ publ.pres_uri= &pres_uri;
|
|
|
+
|
|
|
+ publ.expires= exp;
|
|
|
+
|
|
|
+ publ.event= get_event_flag(&event);
|
|
|
+ if (publ.event < 0) {
|
|
|
+ LM_ERR("unknown event '%.*s'\n", event.len, event.s);
|
|
|
+ rpc->fault(c, 400, "Unknown event");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (content_type.len != 1) {
|
|
|
+ publ.content_type= content_type;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!((id.len == 1) && (id.s[0]== '.'))) {
|
|
|
+ publ.id= id;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!((etag.len== 1) && (etag.s[0]== '.'))) {
|
|
|
+ publ.etag= &etag;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!((outbound_proxy.len == 1) && (outbound_proxy.s[0] == '.'))) {
|
|
|
+ publ.outbound_proxy = &outbound_proxy;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!((extra_headers.len == 1) && (extra_headers.s[0] == '.'))) {
|
|
|
+ publ.extra_headers = &extra_headers;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (body.s != 0) {
|
|
|
+ publ.body= &body;
|
|
|
+ }
|
|
|
+
|
|
|
+ dctx = rpc->delayed_ctx_new(c);
|
|
|
+ if (dctx == 0) {
|
|
|
+ LM_ERR("internal error: failed to create context\n");
|
|
|
+ rpc->fault(c, 500, "internal error: failed to create context");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ publ.cb_param = dctx;
|
|
|
+ publ.source_flag = MI_ASYN_PUBLISH;
|
|
|
+
|
|
|
+ ret = pua_send_publish(&publ);
|
|
|
+ LM_DBG("pua_send_publish returned %d\n", ret);
|
|
|
+
|
|
|
+ if (dctx->reply_ctx != 0) {
|
|
|
+ /* callback was not executed or its execution failed */
|
|
|
+ rpc = &dctx->rpc;
|
|
|
+ c = dctx->reply_ctx;
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret < 0) {
|
|
|
+ LM_ERR("pua_send_publish failed\n");
|
|
|
+ err_ret = err2reason_phrase(ret, &sip_error, err_buf,
|
|
|
+ sizeof(err_buf), "RPC/PUBLISH") ;
|
|
|
+ if (err_ret > 0 ) {
|
|
|
+ rpc->fault(c, sip_error, "%s", err_buf);
|
|
|
+ } else {
|
|
|
+ rpc->fault(c, 500, "RPC/PUBLISH error");
|
|
|
+ }
|
|
|
+ rpc->delayed_ctx_close(dctx);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret == 418) {
|
|
|
+ rpc->fault(c, 500, "Wrong ETag");
|
|
|
+ rpc->delayed_ctx_close(dctx);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+rpc_export_t pua_rpc[] = {
|
|
|
+ {"pua.publish", publish, publish_doc, 0},
|
|
|
+ {0, 0, 0, 0}
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/** module exports */
|
|
|
+struct module_exports exports= {
|
|
|
+ "pua_rpc",
|
|
|
+ 0,
|
|
|
+ pua_rpc,
|
|
|
+ 0,
|
|
|
+ mod_init,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * init module function
|
|
|
+ */
|
|
|
+static int mod_init(void)
|
|
|
+{
|
|
|
+ bind_pua_t bind_pua;
|
|
|
+ pua_api_t pua;
|
|
|
+
|
|
|
+ LM_DBG("initializing\n");
|
|
|
+
|
|
|
+ bind_pua= (bind_pua_t)find_export("bind_pua", 1,0);
|
|
|
+
|
|
|
+ if (!bind_pua) {
|
|
|
+ LM_ERR("can't find pua\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bind_pua(&pua) < 0) {
|
|
|
+ LM_ERR("can't bind pua\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pua.send_publish == NULL) {
|
|
|
+ LM_ERR("could not import send_publish\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ pua_send_publish = pua.send_publish;
|
|
|
+
|
|
|
+ if (pua.register_puacb(MI_ASYN_PUBLISH, publish_callback, NULL) < 0) {
|
|
|
+ LM_ERR("could not register callback\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|