apps/early_announce/EarlyAnnounce.cpp
b3e1c37c
 /*
6d8f8d96
  * $Id$
b3e1c37c
  *
  * Copyright (C) 2002-2003 Fhg Fokus
a15202f0
  * Copyright (C) 2008 Juha Heinanen (USE_MYSQL parts)
b3e1c37c
  *
  * This file is part of sems, a free SIP media server.
  *
  * sems 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.
  *
  * For a license to use the sems software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * sems 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
1f5be2af
 #include "EarlyAnnounce.h"
b3e1c37c
 #include "AmConfig.h"
 #include "AmUtils.h"
 
 #include "sems.h"
 #include "log.h"
 
 #define MOD_NAME "early_announce"
 
a15202f0
 #ifdef USE_MYSQL
 #include <mysql++/mysql++.h>
 #include <stdio.h>
 #define DEFAULT_AUDIO_TABLE "default_audio"
 #define DOMAIN_AUDIO_TABLE "domain_audio"
 #define USER_AUDIO_TABLE "user_audio"
 #endif
 
1f5be2af
 EXPORT_SESSION_FACTORY(EarlyAnnounceFactory,MOD_NAME);
b3e1c37c
 
a15202f0
 #ifdef USE_MYSQL
 string EarlyAnnounceFactory::AnnounceApplication;
 string EarlyAnnounceFactory::AnnounceMessage;
 string EarlyAnnounceFactory::DefaultLanguage;
 #else
1f5be2af
 string EarlyAnnounceFactory::AnnouncePath;
 string EarlyAnnounceFactory::AnnounceFile;
a15202f0
 #endif
b3e1c37c
 
94a0733f
 EarlyAnnounceFactory::ContB2B EarlyAnnounceFactory::ContinueB2B = 
   EarlyAnnounceFactory::Never;
7dac246b
 
1f5be2af
 EarlyAnnounceFactory::EarlyAnnounceFactory(const string& _app_name)
b3e1c37c
   : AmSessionFactory(_app_name)
 {
 }
 
a15202f0
 #ifdef USE_MYSQL
 mysqlpp::Connection EarlyAnnounceFactory::Connection(mysqlpp::use_exceptions);
 
 int get_announce_msg(string application, string message, string user,
 		     string domain, string language, string *audio_file)
 {
     string query_string;
 
     if (!user.empty()) {
 	*audio_file = string("/tmp/") +  application + "_" + 
 	    message + "_" + domain + "_" + user + ".wav";
 	query_string = "select audio from " + string(USER_AUDIO_TABLE) +
 	    " where application='" + application + "' and message='" +
 	    message + "' and userid='" + user + "' and domain='" +
 	    domain + "'";
     } else if (!domain.empty()) {
 	*audio_file = string("/tmp/") +  application + "_" +
 	    message + "_" + domain + "_" + language + ".wav";
 	query_string = "select audio from " + string(DOMAIN_AUDIO_TABLE) +
 	    " where application='" + application + "' and message='" +
 	    message + "' and domain='" + domain + "' and language='" +
 	    language + "'";
     } else {
 	*audio_file = string("/tmp/") +  application  + "_" +
 	    message + "_" + language + ".wav";
 	query_string = "select audio from " + string(DEFAULT_AUDIO_TABLE) +
 	    " where application='" + application + "' and message='" +
 	    message + "' and language='" + language + "'";
     }
 
     try {
 
 	mysqlpp::Query query = EarlyAnnounceFactory::Connection.query();
 	    
 	DBG("Query string <%s>\n", query_string.c_str());
 
 	query << query_string;
ffe99681
 
 #ifdef VERSION2
a15202f0
 	mysqlpp::Result res = query.store();
ffe99681
 #else
     	mysqlpp::StoreQueryResult res = query.store();
 #endif
a15202f0
 
 	mysqlpp::Row row;
 
 	if (res) {
 	    if ((res.num_rows() > 0) && (row = res.at(0))) {
 		FILE *file;
 		file = fopen((*audio_file).c_str(), "wb");
ffe99681
 #ifdef VERSION2
 		unsigned long length = row.raw_string(0).size();
a15202f0
 		fwrite(row.at(0).data(), 1, length, file);
ffe99681
 #else
 		mysqlpp::String s = row[0];
 		fwrite(s.data(), 1, s.length(), file);
 #endif
a15202f0
 		fclose(file);
 		return 1;
 	    } else {
 		*audio_file = "";
 		return 1;
 	    }
 	} else {
 	    ERROR("Database query error\n");
 	    *audio_file = "";
 	    return 0;
 	}
     }
 
     catch (const mysqlpp::Exception& er) {
 	// Catch-all for any MySQL++ exceptions
 	ERROR("MySQL++ error: %s\n", er.what());
 	*audio_file = "";
 	return 0;
     }
 }
 
 #endif
 
1f5be2af
 int EarlyAnnounceFactory::onLoad()
b3e1c37c
 {
8d76c8bb
   AmConfigReader cfg;
   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
     return -1;
b3e1c37c
 
8d76c8bb
   // get application specific global parameters
   configureModule(cfg);
b3e1c37c
 
94a0733f
   if (cfg.hasParameter("continue_b2b")) { 
     if (cfg.getParameter("continue_b2b") == "yes") {
       ContinueB2B = Always;
       DBG("early_announce in b2bua mode.\n");
     }
     else if (cfg.getParameter("continue_b2b") == "app-param") {
       ContinueB2B = AppParam;
       DBG("early_announce in b2bua/final reply mode "
 	  "(depends on app-param).\n");
     } else {
       DBG("early_announce sends final reply.\n");
     }
7dac246b
   }
 
a15202f0
 #ifdef USE_MYSQL
 
   /* Get default audio from MySQL */
 
   string mysql_server, mysql_user, mysql_passwd, mysql_db;
 
   mysql_server = cfg.getParameter("mysql_server");
   if (mysql_server.empty()) {
     mysql_server = "localhost";
   }
 
   mysql_user = cfg.getParameter("mysql_user");
   if (mysql_user.empty()) {
c30c0217
     ERROR("conference.conf parameter 'mysql_user' is missing.\n");
a15202f0
     return -1;
   }
 
   mysql_passwd = cfg.getParameter("mysql_passwd");
   if (mysql_passwd.empty()) {
c30c0217
     ERROR("conference.conf parameter 'mysql_passwd' is missing.\n");
a15202f0
     return -1;
   }
 
   mysql_db = cfg.getParameter("mysql_db");
   if (mysql_db.empty()) {
     mysql_db = "sems";
   }
 
   AnnounceApplication = cfg.getParameter("application");
   if (AnnounceApplication.empty()) {
     AnnounceApplication = MOD_NAME;
   }
 
   AnnounceMessage = cfg.getParameter("message");
   if (AnnounceMessage.empty()) {
     AnnounceMessage = "greeting_msg";
   }
 
   DefaultLanguage = cfg.getParameter("default_language");
   if (DefaultLanguage.empty()) {
     DefaultLanguage = "en";
   }
 
   try {
 
ffe99681
 #ifdef VERSION2
18d97431
     Connection.set_option(Connection.opt_reconnect, true);
ffe99681
 #else
     Connection.set_option(new mysqlpp::ReconnectOption(true));
 #endif
a15202f0
     Connection.connect(mysql_db.c_str(), mysql_server.c_str(),
 		       mysql_user.c_str(), mysql_passwd.c_str());
     if (!Connection) {
       ERROR("Database connection failed: %s\n", Connection.error());
       return -1;
     }
   }
18d97431
 
   catch (const mysqlpp::BadOption& er) {
     ERROR("MySQL++ set_option error: %s\n", er.what());
     return -1;
   }
  	
a15202f0
   catch (const mysqlpp::Exception& er) {
     // Catch-all for any MySQL++ exceptions
     ERROR("MySQL++ error: %s\n", er.what());
     return -1;
   }
 
   string announce_file;
   if (!get_announce_msg(AnnounceApplication, AnnounceMessage, "", "",
 			DefaultLanguage, &announce_file)) {
     return -1;
   }
   if (announce_file.empty()) {
     ERROR("default announce for " MOD_NAME " module does not exist.\n");
     return -1;
   }
 
 #else 
 
   /* Get default audio from file system */
 
8d76c8bb
   AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH);
   if( !AnnouncePath.empty() 
       && AnnouncePath[AnnouncePath.length()-1] != '/' )
     AnnouncePath += "/";
b3e1c37c
 
8d76c8bb
   AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE);
b3e1c37c
 
8d76c8bb
   string announce_file = AnnouncePath + AnnounceFile;
   if(!file_exists(announce_file)){
     ERROR("default file for " MOD_NAME " module does not exist ('%s').\n",
 	  announce_file.c_str());
     return -1;
   }
b3e1c37c
 
a15202f0
 #endif
 
8d76c8bb
   return 0;
b3e1c37c
 }
 
 
1f5be2af
 void EarlyAnnounceDialog::onInvite(const AmSipRequest& req) 
b3e1c37c
 {
8d76c8bb
   try {
b3e1c37c
 
8d76c8bb
     string sdp_reply;
     acceptAudio(req.body,req.hdrs,&sdp_reply);
b3e1c37c
 
8d76c8bb
     if(dlg.reply(req,183,"Session Progress",
 		 "application/sdp",sdp_reply) != 0){
b3e1c37c
 
8d76c8bb
       throw AmSession::Exception(500,"could not reply");
     }
2a0c6142
     else {	    
       invite_req = req;
8d76c8bb
     }
b3e1c37c
 
8d76c8bb
   } catch(const AmSession::Exception& e) {
c75dd1ba
 
8d76c8bb
     ERROR("%i %s\n",e.code,e.reason.c_str());
     setStopped();
     AmSipDialog::reply_error(req,e.code,e.reason);
   }
b3e1c37c
 }
 
 
1f5be2af
 AmSession* EarlyAnnounceFactory::onInvite(const AmSipRequest& req)
b3e1c37c
 {
a15202f0
 
 #ifdef USE_MYSQL
 
     string iptel_app_param = getHeader(req.hdrs, PARAM_HDR);
     string language = get_header_keyvalue(iptel_app_param,"Language");
     string announce_file = "";
 
     if (language.empty()) language = DefaultLanguage;
 
     get_announce_msg(AnnounceApplication, AnnounceMessage, req.user,
 		     req.domain, "", &announce_file);
     if (!announce_file.empty()) goto end;
     get_announce_msg(AnnounceApplication, AnnounceMessage, "", req.domain,
 		     language, &announce_file);
     if (!announce_file.empty()) goto end;
     get_announce_msg(AnnounceApplication, AnnounceMessage, "", "", language,
 		     &announce_file);
 
 #else
 
8d76c8bb
   string announce_path = AnnouncePath;
   string announce_file = announce_path + req.domain 
7059248b
     + "/" + req.user + ".wav";
b3e1c37c
 
8d76c8bb
   DBG("trying '%s'\n",announce_file.c_str());
   if(file_exists(announce_file))
     goto end;
b3e1c37c
 
8d76c8bb
   announce_file = announce_path + req.user + ".wav";
   DBG("trying '%s'\n",announce_file.c_str());
   if(file_exists(announce_file))
     goto end;
b3e1c37c
 
8d76c8bb
   announce_file = AnnouncePath + AnnounceFile;
a15202f0
 
 #endif
 
8d76c8bb
  end:
   return new EarlyAnnounceDialog(announce_file);
b3e1c37c
 }
 
1f5be2af
 EarlyAnnounceDialog::EarlyAnnounceDialog(const string& filename)
8d76c8bb
   : filename(filename)
b3e1c37c
 {
7dac246b
   set_sip_relay_only(false);
b3e1c37c
 }
 
1f5be2af
 EarlyAnnounceDialog::~EarlyAnnounceDialog()
b3e1c37c
 {
 }
 
1f5be2af
 void EarlyAnnounceDialog::onSessionStart(const AmSipRequest& req)
b3e1c37c
 {
8d76c8bb
   // we can drop all received packets
   // this disables DTMF detection as well
   setReceiving(false);
ede1f718
 
8d76c8bb
   DBG("EarlyAnnounceDialog::onSessionStart\n");
   if(wav_file.open(filename,AmAudioFile::Read))
     throw string("EarlyAnnounceDialog::onSessionStart: Cannot open file\n");
b3e1c37c
     
8d76c8bb
   setOutput(&wav_file);
b3e1c37c
 }
 
1f5be2af
 void EarlyAnnounceDialog::onBye(const AmSipRequest& req)
b3e1c37c
 {
8d76c8bb
   DBG("onBye: stopSession\n");
   setStopped();
b3e1c37c
 }
 
1f5be2af
 void EarlyAnnounceDialog::onCancel()
b3e1c37c
 {
2a0c6142
   dlg.reply(invite_req,487,"Call terminated");
8d76c8bb
   setStopped();
b3e1c37c
 }
 
1f5be2af
 void EarlyAnnounceDialog::process(AmEvent* event)
b3e1c37c
 {
 
8d76c8bb
   AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);
   if(audio_event && 
94a0733f
      (audio_event->event_id == AmAudioEvent::cleared)) {
8d76c8bb
       DBG("AmAudioEvent::cleared\n");
7dac246b
 
94a0733f
       bool continue_b2b = false;
       if (EarlyAnnounceFactory::ContinueB2B == 
 	  EarlyAnnounceFactory::Always) {
 	continue_b2b = true;
       } else if (EarlyAnnounceFactory::ContinueB2B == 
 		 EarlyAnnounceFactory::AppParam) {
2a0c6142
 	string iptel_app_param = getHeader(invite_req.hdrs, PARAM_HDR);
94a0733f
 	if (iptel_app_param.length()) {
 	  continue_b2b = get_header_keyvalue(iptel_app_param,"B2B")=="yes";
 	} else {
2a0c6142
 	  continue_b2b = getHeader(invite_req.hdrs,"P-B2B")=="yes";
94a0733f
 	}
       }
       DBG("determined: continue_b2b = %s\n", continue_b2b?"true":"false");
 
       if (!continue_b2b) {
7dac246b
 	unsigned int code_i = 404;
 	string reason = "Not Found";
 	
2a0c6142
 	string iptel_app_param = getHeader(invite_req.hdrs, PARAM_HDR);
7dac246b
 	if (iptel_app_param.length()) {
 	  string code = get_header_keyvalue(iptel_app_param,"Final-Reply-Code");
 	  if (code.length() && str2i(code, code_i)) {
 	    ERROR("while parsing Final-Reply-Code parameter\n");
 	  }
 	  reason = get_header_keyvalue(iptel_app_param,"Final-Reply-Reason");
94a0733f
 	  if (!reason.length())
 	    reason = "Not Found";
7dac246b
 	} else {
2a0c6142
 	  string code = getHeader(invite_req.hdrs,"P-Final-Reply-Code");
7dac246b
 	  if (code.length() && str2i(code, code_i)) {
 	    ERROR("while parsing P-Final-Reply-Code\n");
 	  }
2a0c6142
 	  string h_reason =  getHeader(invite_req.hdrs,"P-Final-Reply-Reason");
7dac246b
 	  if (h_reason.length()) {
 	    INFO("Use of P-Final-Reply-Code/P-Final-Reply-Reason is deprecated. ");
 	    INFO("Use '%s: Final-Reply-Code=<code>;"
 		 "Final-Reply-Reason=<rs>' instead.\n",PARAM_HDR);
 	    reason = h_reason;
 	  }
73b06d8b
 	}
 
7dac246b
 	DBG("Replying with code %d %s\n", code_i, reason.c_str());
2a0c6142
 	dlg.reply(invite_req, code_i, reason);
fa35cb1c
 	
7dac246b
 	setStopped();
       } else {
 	set_sip_relay_only(true);
2a0c6142
 	recvd_req.insert(std::make_pair(invite_req.cseq,invite_req));
7dac246b
 	
2a0c6142
 	relayEvent(new B2BSipRequestEvent(invite_req,true));
7dac246b
       }
fa35cb1c
 	
8d76c8bb
       return;
b3e1c37c
     }
 
7dac246b
   AmB2BCallerSession::process(event);
b3e1c37c
 }