/* * 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. This program is released under * the GPL with the additional exemption that compiling, linking, * and/or using OpenSSL is allowed. * * 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 */ /** @file AmB2BSession.h */ #ifndef AmB2BSession_h #define AmB2BSession_h #include "AmSession.h" #include "AmSipDialog.h" #include "sip/hash.h" #define MAX_RELAY_STREAMS 3 // voice, video, rtt enum { B2BTerminateLeg, B2BConnectLeg, B2BSipRequest, B2BSipReply, B2BMsgBody }; /** \brief base class for event in B2B session */ struct B2BEvent: public AmEvent { B2BEvent(int ev_id) : AmEvent(ev_id) {} }; /** \brief base class for SIP event in B2B session */ struct B2BSipEvent: public B2BEvent { bool forward; B2BSipEvent(int ev_id, bool forward) : B2BEvent(ev_id), forward(forward) {} }; /** \brief SIP request in B2B session */ struct B2BSipRequestEvent: public B2BSipEvent { AmSipRequest req; B2BSipRequestEvent(const AmSipRequest& req, bool forward) : B2BSipEvent(B2BSipRequest,forward), req(req) { } }; /** \brief SIP reply in B2B session */ struct B2BSipReplyEvent: public B2BSipEvent { AmSipReply reply; string trans_method; B2BSipReplyEvent(const AmSipReply& reply, bool forward, string trans_method) : B2BSipEvent(B2BSipReply,forward), reply(reply), trans_method(trans_method) { } }; /** \brief trigger connecting the callee leg in B2B session */ struct B2BConnectEvent: public B2BEvent { string remote_party; string remote_uri; string content_type; string body; string hdrs; bool relayed_invite; unsigned int r_cseq; B2BConnectEvent(const string& remote_party, const string& remote_uri) : B2BEvent(B2BConnectLeg), remote_party(remote_party), remote_uri(remote_uri), relayed_invite(false), r_cseq(0) {} }; /** * \brief Base class for Sessions in B2BUA mode. * * It has two legs as independent sessions: * Callee- and caller-leg. */ class AmB2BSession: public AmSession { public: enum B2BMode { B2BMode_Transparent, // relay message bodies B2BMode_SDPFilter // reconstruct SDP }; protected: /** local tag of the other leg */ string other_id; /** Tell if the session should * process SIP request itself * or only relay them (B2B mode). */ bool sip_relay_only; B2BMode b2b_mode; bool a_leg; /** * Requests which have been relayed * from the other leg and sent as SIP */ TransMap relayed_req; /** Requests received for relaying */ std::map<int,AmSipRequest> recvd_req; /** CSeq of the INVITE that established this call */ unsigned int est_invite_cseq; auto_ptr<AmSdp> invite_sdp; /** content-type of established session */ string established_content_type; /** body of established session */ string established_body; /** hash of body (from o-line) */ uint32_t body_hash; /** save current session description (SDP) */ virtual void saveSessionDescription(const string& content_type, const string& body); /** @return whether session has changed */ virtual bool updateSessionDescription(const string& content_type, const string& body); /** reset relation with other leg */ virtual void clear_other(); /** Relay one event to the other side. @return 0 on success */ virtual int relayEvent(AmEvent* ev); /** send a relayed SIP Request */ void relaySip(const AmSipRequest& req); /** send a relayed SIP Reply */ void relaySip(const AmSipRequest& orig, const AmSipReply& reply); /** Terminate our leg and forget the other. */ virtual void terminateLeg(); /** Terminate the other leg and forget it.*/ virtual void terminateOtherLeg(); /** @see AmSession */ virtual void updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq); void onSipRequest(const AmSipRequest& req); void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method); void onInvite2xx(const AmSipReply& reply); void onSessionTimeout(); /** send re-INVITE with established session description * @return 0 on success */ int sendEstablishedReInvite(); /** do session refresh */ bool refresh(int flags = 0); /** @see AmEventQueue */ void process(AmEvent* event); /** B2BEvent handler */ virtual void onB2BEvent(B2BEvent* ev); /** handle BYE on other leg */ virtual void onOtherBye(const AmSipRequest& req); /** * Reply received from other leg has been replied * @return true if reply was processed (should be absorbed) * @return false if reply was not processed */ virtual bool onOtherReply(const AmSipReply& reply); /** filter body ( b2b_mode == SDPFilter */ virtual int filterBody(string& content_type, string& body, AmSdp& filter_sdp, bool is_a2b); /** filter SDP body ( b2b_mode == SDPFilter */ virtual int filterBody(AmSdp& sdp, bool is_a2b); AmB2BSession(); AmB2BSession(const string& other_local_tag); virtual ~AmB2BSession(); /** flag to enable RTP relay mode */ bool rtp_relay_enabled; /** force symmetric RTP */ bool rtp_relay_force_symmetric_rtp; /** RTP streams which receive from our side and are used for relaying RTP from the other side */ AmRtpStream** relay_rtp_streams; /** number of relay RTP streams */ unsigned int relay_rtp_streams_cnt; /** fd of the other streams' sockets (to remove from RtpReceiver at end of relaying) */ int other_stream_fds[MAX_RELAY_STREAMS]; /** clear our and the other side's RTP streams from RTPReceiver */ void clearRtpReceiverRelay(); /** update remote connection in relay_streams */ void updateRelayStreams(const string& content_type, const string& body, AmSdp& parser_sdp); /** replace connection with our address */ bool replaceConnectionAddress(const string& content_type, const string& body, string& r_body); public: void set_sip_relay_only(bool r); B2BMode getB2BMode() const; /** set RTP relay mode enabled for initial INVITE */ void enableRtpRelay(const AmSipRequest& initial_invite_req); /** set RTP relay mode enabled */ void enableRtpRelay(); /** set RTP relay mode disabled */ void disableRtpRelay(); /** link RTP streams of other_session to our streams */ void setupRelayStreams(AmB2BSession* from_session); bool getRtpRelayEnabled() const { return rtp_relay_enabled; } bool getRtpRelayForceSymmetricRtp() const { return rtp_relay_force_symmetric_rtp; } }; class AmB2BCalleeSession; /** \brief Caller leg of a B2B session */ class AmB2BCallerSession: public AmB2BSession { public: enum CalleeStatus { None=0, NoReply, Ringing, Connected }; private: // Callee Status CalleeStatus callee_status; int reinviteCaller(const AmSipReply& callee_reply); protected: AmSipRequest invite_req; virtual void createCalleeSession(); int relayEvent(AmEvent* ev); /** Tell if the session should * relay early media SDPs to * caller leg */ bool sip_relay_early_media_sdp; public: AmB2BCallerSession(); virtual ~AmB2BCallerSession(); CalleeStatus getCalleeStatus() { return callee_status; } void setCalleeStatus(CalleeStatus c) { callee_status = c; } virtual AmB2BCalleeSession* newCalleeSession(); void connectCallee(const string& remote_party, const string& remote_uri, bool relayed_invite = false); const AmSipRequest& getOriginalRequest() { return invite_req; } // @see AmSession void onSessionStart(const AmSipRequest& req); // @see AmB2BSession void terminateLeg(); void terminateOtherLeg(); void onB2BEvent(B2BEvent* ev); AmSipRequest* getInviteReq() { return &invite_req; } void set_sip_relay_early_media_sdp(bool r); /** initialize RTP relay mode, if rtp_relay_enabled must be called *before* callee_session is started */ void initializeRTPRelay(AmB2BCalleeSession* callee_session); }; /** \brief Callee leg of a B2B session */ class AmB2BCalleeSession: public AmB2BSession { public: AmB2BCalleeSession(const string& other_local_tag); AmB2BCalleeSession(const AmB2BCallerSession* caller); virtual ~AmB2BCalleeSession(); void onB2BEvent(B2BEvent* ev); }; #endif