--
-- This DSM app 
--   o plays early media from a file in the DB
--   o sends a HTTP request to a web server
--   o SUBSCRIBEs to registration state 
--   o if registration becomes active (at least one active contact), it sends back "300 Multiple Choices"
--   o otherwise (timeout) it sends back "480 Not found"

-- Parameters (P-App-Param):
--    audio_id   - indicating audio file in DB
--    expiration - subscription expiration/waiting time (default: 60)
--    caller     - caller passed to notification web app
--    callee     - callee passed to notification web app
--    domain     - domain passed to notification web app
--
--  Example: P-App-Param: audio_id=rbt;expiration=30;caller=+43111111111;callee=+432222222222;domain=sip.sipwise.com
-- 
-- Example DB: 
--  CREATE TABLE `audio_files` (
--    `id` varchar(20) DEFAULT NULL,
--    `data` mediumblob
--  ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

import(mod_utils);
import(mod_dlg);
import(mod_subscription);
import(mod_xml);
import(mod_mysql);
import(mod_curl);

initial state START;

transition "SIP dialog error" START - exception; test(#type=="dlg") / {
  log(1, "Error in accepting call:");
  logParams(1);
  set($connect_session=0);
  stop(false);
} -> END;

transition "DB exception" START - exception / {
  log(1, "Error in initializing mobile push:");
  logAll(1);
  dlg.reply(500, "Server Internal Error");
  stop(false);
} -> END;

transition "invite" START - invite / {
  if test($config.enable_rbt=="yes") {
    if test($audio_id != "") {
      mysql.connect();
      throwOnError();
      -- play waiting file from DB
      if test($config.play_looped=="yes") {
        mysql.playDBAudioLooped(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
      } else {
        mysql.playDBAudio(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
      }
      throwOnError();
      mysql.disconnect();
    }
  }

  -- send 183 with early media
  dlg.acceptInvite(183, "Progress");
  throwOnError();
  -- no default 200 OK
  set($connect_session=0);

  -- start RTP processing 
  connectMedia();

  -- no need to process incoming RTP (also no RTP timeout needed)
  disableReceiving();

  -- create subscription to reg event
  set($r.domain=@domain);
  set($r.user=@user);
  set($r.from_user=$config.subscription_user);
  set($r.pwd=$config.subscription_pwd);
  set($r.event="reg");
  if test($config.proxy != "") {
    set($r.proxy=$config.proxy);
  }
  if test($config.use_subscription_id == "yes") {
    utils.getNewId(r.id);
  }
  if test($expiration != "") {
    set($r.expires=$expiration);
  } else {
    set($r.expires=60);
  }

  subscription.create(r);

  if test($r.handle == "") {
    log(1, "Subscription creation failed!");
    logVars(1);
    dlg.reply(500, "Server Internal Error");
    stop(false);
    throw(subscription);
  }

  -- send HTTP request
  set($curl.timeout=15);
  if test($config.use_post=="yes") {
    curl.postDiscardResult($config.push_server_url, $caller;$callee;$domain)
  } else {
    curl.getForm($config.push_server_url, $caller;$callee;$domain)
  }

  -- set Timer on our side, too (server subscription timing could be broken...)
  setTimer(1, $r.expires);

} -> WAITING;

state WAITING;

transition "RTP timeout - ignore" WAITING - rtpTimeout / set(#processed=true) -> WAITING;

transition "subscription failed" WAITING - subscription(#status=="failed") / {
  log(1, "Subscription failed:");
  logParams(1);
  logVars(1); 
  dlg.reply(500, "Server Internal Error");
  stop(false);
} -> END;

function evaluateRegNotifyBody() {
  xml.parseSIPMsgBody("SipSubscriptionBody", "substatus");
  -- namespaces to be used:
  set($substatus.ns="a=urn:ietf:params:xml:ns:reginfo")
  -- look for an active contact
  xml.evalXPath("/a:reginfo/a:registration/a:contact[@state='active']", "substatus");
  xml.XPathResultCount($active_contacts="substatus.xpath");
};

function releaseRegEvalObjects() {
  freeObject("substatus");
  freeObject("substatus.xpath");
};

function replyWith300() {
  if test($has_replied!="yes") {
    set($dlg.reply.hdrs="Contact: ");
    append($dlg.reply.hdrs, @local_uri);
    append($dlg.reply.hdrs, $config.extra_3xx_uri_append);
    dlg.reply(300, "Multiple Choices");
    set($has_replied="yes");
  }
  stop(false);
};

function replyWith480() {
  if test($has_replied!="yes") {
    dlg.reply(480, "Not found");
    set($has_replied="yes");
  }
  stop(false);
};

transition "NOTIFY received, with body" WAITING - subscription(#status!="terminated"); test(#has_body=="true") / {
  evaluateRegNotifyBody();

  if test($active_contacts != 0) {
    replyWith300();
    subscription.remove($r.handle);
  }

  releaseRegEvalObjects();
} -> WAITING;

transition "NOTIFY received, no body" WAITING - subscription(#status!="terminated")  / {
  log(3, "subscription active");
  logParams(3);
} -> WAITING;

transition "NOTIFY received, terminated (but with body)" WAITING - subscription(#status=="terminated"); test(#has_body=="true") / {
  evaluateRegNotifyBody();

  if test($active_contacts != 0) {
    replyWith300();
  } else {
    replyWith480();
  }
  releaseRegEvalObjects();
} -> END;

transition "NOTIFY received, subscription terminated" WAITING - subscription(#status=="terminated") / {
  replyWith480();
} -> END;

transition "timer hit" WAITING - timer(#id==1) / {
  subscription.remove($r.handle);
  replyWith480();
} -> END;

transition "CANCEL received" WAITING - hangup / {
  subscription.remove($r.handle);
  dlg.reply(487, "Request Terminated");
  stop(false);
} -> END;

state END;