diff --git a/apps/early_announce/Announcement.cpp b/apps/early_announce/Announcement.cpp new file mode 100644 index 00000000..c7f64c9e --- /dev/null +++ b/apps/early_announce/Announcement.cpp @@ -0,0 +1,186 @@ +/* + * $Id: Announcement.cpp,v 1.7.8.4 2005/08/31 13:54:29 rco Exp $ + * + * Copyright (C) 2002-2003 Fhg Fokus + * + * 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 + */ + +#include "Announcement.h" +#include "AmConfig.h" +#include "AmUtils.h" +#include "AmSessionScheduler.h" + +#include "sems.h" +#include "log.h" + +#define MOD_NAME "early_announce" + +EXPORT_SESSION_FACTORY(AnnouncementFactory,MOD_NAME); + +string AnnouncementFactory::AnnouncePath; +string AnnouncementFactory::AnnounceFile; + +AnnouncementFactory::AnnouncementFactory(const string& _app_name) + : AmSessionFactory(_app_name) +{ +} + +int AnnouncementFactory::onLoad() +{ + AmConfigReader cfg; + if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) + return -1; + + // get application specific global parameters + configureModule(cfg); + + AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH); + if( !AnnouncePath.empty() + && AnnouncePath[AnnouncePath.length()-1] != '/' ) + AnnouncePath += "/"; + + AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE); + + string announce_file = AnnouncePath + AnnounceFile; + if(!file_exists(announce_file)){ + ERROR("default file for ann_b2b module does not exist ('%s').\n", + announce_file.c_str()); + return -1; + } + + return 0; +} + + +void AnnouncementDialog::onInvite(const AmSipRequest& req) +{ + string sdp_reply; + if(acceptAudio(req,sdp_reply)!=0) + return; + + if(dlg.reply(req,183,"Session Progress", + "application/sdp",sdp_reply) != 0){ + + //throw AmSession::Exception(500,"error while sending response"); + setStopped(); + } + else { + + localreq = req; + } + +// reply_code = 183; // early dialog +// reply_reason = "Session Progress"; + +} + + +AmSession* AnnouncementFactory::onInvite(const AmSipRequest& req) +{ + string announce_path = AnnouncePath; + string announce_file = announce_path + req.domain + + "/" + req.user + ".wav"; + + DBG("trying '%s'\n",announce_file.c_str()); + if(file_exists(announce_file)) + goto end; + + announce_file = announce_path + req.user + ".wav"; + DBG("trying '%s'\n",announce_file.c_str()); + if(file_exists(announce_file)) + goto end; + + announce_file = AnnouncePath + AnnounceFile; + +end: + return new AnnouncementDialog(announce_file); +} + +AnnouncementDialog::AnnouncementDialog(const string& filename) + : filename(filename) +{ +} + +AnnouncementDialog::~AnnouncementDialog() +{ +} + +void AnnouncementDialog::onSessionStart(const AmSipRequest& req) +{ + DBG("AnnouncementDialog::onSessionStart\n"); + if(wav_file.open(filename,AmAudioFile::Read)) + throw string("AnnouncementDialog::onSessionStart: Cannot open file\n"); + + setOutput(&wav_file); +} + +void AnnouncementDialog::onBye(const AmSipRequest& req) +{ + DBG("onBye: stopSession\n"); + setStopped(); +} + +void AnnouncementDialog::onCancel() +{ + dlg.reply(localreq,487,"Call terminated"); + setStopped(); +} + +void AnnouncementDialog::process(AmEvent* event) +{ + + AmAudioEvent* audio_event = dynamic_cast(event); + if(audio_event) + { + switch(audio_event->event_id) + { + + case AmAudioEvent::cleared: + DBG("AmAudioEvent::cleared\n"); + //dlg.bye(); + setStopped(); + return; + break; + + case AmAudioEvent::noAudio: + DBG("AmAudioEvent::noAudio\n"); + unsigned int code_i; + string code = getHeader(localreq.hdrs,"P-Final-Reply-Code"); + string reason = getHeader(localreq.hdrs,"P-Final-Reply-Reason"); + + if (code.length() && reason.length() && !str2i(code, code_i) ) { + DBG("Replying with code %d %s\n", code_i, reason.c_str()); + dlg.reply(localreq, code_i, reason); + } else { + DBG("Replying with std code 404 Not found\n"); + dlg.reply(localreq, 404, "Not Found"); + } + setStopped(); + return; + break; + } + } + + AmSession::process(event); +} + diff --git a/apps/early_announce/Announcement.h b/apps/early_announce/Announcement.h new file mode 100644 index 00000000..687c9786 --- /dev/null +++ b/apps/early_announce/Announcement.h @@ -0,0 +1,72 @@ +/* + * $Id: Announcement.h,v 1.6.8.1 2005/06/01 12:00:24 rco Exp $ + * + * Copyright (C) 2002-2003 Fhg Fokus + * + * 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 + */ + +#ifndef _ANNOUNCEMENT_H_ +#define _ANNOUNCEMENT_H_ + +#include "AmSession.h" +#include "AmConfigReader.h" + +#include +using std::string; + +class AnnouncementFactory: public AmSessionFactory +{ +public: + static string AnnouncePath; + static string AnnounceFile; + + AnnouncementFactory(const string& _app_name); + + int onLoad(); + AmSession* onInvite(const AmSipRequest& req); +}; + +class AnnouncementDialog : public AmSession +{ + AmAudioFile wav_file; + string filename; + AmSipRequest localreq; + + public: + AnnouncementDialog(const string& filename); + ~AnnouncementDialog(); + + void onInvite(const AmSipRequest& req); + void onSessionStart(const AmSipRequest& req); + void onBye(const AmSipRequest& req); + void onCancel(); + void onDtmf(int event, int duration_msec) {} + + void process(AmEvent* event); +}; + +#endif +// Local Variables: +// mode:C++ +// End: + diff --git a/apps/early_announce/Makefile b/apps/early_announce/Makefile new file mode 100644 index 00000000..3249aacd --- /dev/null +++ b/apps/early_announce/Makefile @@ -0,0 +1,7 @@ +plug_in_name = early_announce + +module_ldflags = +module_cflags = + +COREPATH ?=../../core +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/early_announce/Readme b/apps/early_announce/Readme new file mode 100644 index 00000000..3d7b1a06 --- /dev/null +++ b/apps/early_announce/Readme @@ -0,0 +1,141 @@ +************************************ +* Early announcement Server Module * +************************************ + +Description: +------------ + +The annoucement server plays a wav file to the caller +in an early session: it replies to an invite with +183 Session Progress (with SDP), then sends the announcement +and finally replies the request with a response (by default +404 Not found ). + +Additional Headers: + P-Final-Reply-Code integer (ex. P-Final-Reply-Code:405 ) + P-Final-Reply-Reason string (e.g. P-Final-Reply-Reason:Prohibited) + + +It searches for files to play following these rules: + + 1) [AnnouncePath]/[Domain]/[User].wav + 2) [AnnouncePath]/[User].wav + +example sems (early_announce.conf) configuration: + +announce_path=wav/ +default_announce=default_en.wav + + +example ser configuration: + +# +# simple single instance Ser config script with early media +# ----------- global configuration parameters ------------------------ + +debug=5 # debug level (cmd line: -dddddddddd) +fork=yes +log_stderror=yes # (cmd line: -E) + +check_via=no # (cmd. line: -v) +dns=no # (cmd. line: -r) +rev_dns=no # (cmd. line: -R) +port=5060 +#children=4 +sip_warning=no + +# Configure Ser to create a FIFO server +#fifo="/tmp/ser_fifo" + +# Configure Ser to create an unix socket server +unix_sock="/tmp/ser_socket" + +loadmodule "modules/sl/sl.so" +loadmodule "modules/tm/tm.so" +loadmodule "modules/rr/rr.so" +loadmodule "modules/maxfwd/maxfwd.so" +loadmodule "modules/avp/avp.so" +loadmodule "modules/avpops/avpops.so" + +# -- rr params -- +# add value to ;lr param to make some broken UAs happy +modparam("rr", "enable_full_lr", 1) + +modparam( "avpops", "avp_aliases", "early_code=i:66 ; early_reason=i:67" ) + +# this is to be appended to the invite +modparam("tm", "tw_append","early_headers:P-Final-Reply-Code=avp[$early_code];P-Final-Reply-Reason=avp[$early_reason]") + +# ------------------------- request routing logic ------------------- +# main routing logic +route{ + + # initial sanity checks -- messages with + # max_forwards==0, or excessively long requests + if (!mf_process_maxfwd_header("10")) { + sl_send_reply("483","Too Many Hops"); + break; + }; + if ( msg:len > max_len ) { + sl_send_reply("513", "Message too big"); + break; + }; + # we record-route all messages -- to make sure that + # subsequent messages will go through our proxy; that's + # particularly good if upstream and downstream entities + # use different transport protocol + record_route(); + # loose-route processing + if (loose_route()) { + t_relay(); + break; + }; + + if (uri==myself) { + # filter unsupported requests + if (!(method=="REGISTER" || method=="ACK" || method=="INVITE" || + method=="BYE" || method=="CANCEL" )) { + sl_send_reply("501", "method not understood here"); + break; + } + + if (method=="REGISTER") { + # make UAs which want to register unhappy + sl_send_reply("501","registration couldn't be accepted"); + break; + }; + + if (!t_newtran()){ + sl_send_reply("500","could not create transaction"); + break; + }; + + if (method == "INVITE") { + + avp_write("405", "$early_code"); + avp_write("Prohiboted", "$early_reason"); + + if(!t_write_unix("/tmp/sems_sock","early_announce/early_headers")){ + log("could not contact early_announce\n"); + t_reply("404","not found"); + }; + break; + + } else if (method=="BYE" || method=="CANCEL") { + if(!t_write_unix("/tmp/sems_sock","bye")) { + t_reply("500","error contacting sems"); + }; + break; + } else if (method=="ACK"){ + # absorb ACKs + t_relay(); + break; + }; + break; + } + + if (!t_relay()) { + sl_reply_error(); + }; +} + diff --git a/apps/early_announce/etc/early_announce.conf b/apps/early_announce/etc/early_announce.conf new file mode 100644 index 00000000..0606f982 --- /dev/null +++ b/apps/early_announce/etc/early_announce.conf @@ -0,0 +1,7 @@ +#CFGOPTION_SEMS_ANNOUNCEPATH +announce_path=wav/ +#ENDCFGOPTION + +#CFGOPTION_SEMS_ANNOUNCEMENT +default_announce=default_en.wav +#ENDCFGOPTION