Skip to content
Snippets Groups Projects
chan_sip.c 1.16 MiB
Newer Older
  • Learn to ignore specific revisions
  • /*! \brief  A bogus peer, to be used when authentication should fail */
    static struct sip_peer *bogus_peer;
    /*! \brief  We can recognise the bogus peer by this invalid MD5 hash */
    #define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
    
    static struct ao2_container *registry_list;
    
    /*! \brief  The MWI subscription list */
    
    static struct ao2_container *subscription_mwi_list;
    
    
    /*! \brief A per-thread temporary pvt structure */
    
    AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
    
    /*! \brief A per-thread buffer for transport to string conversion */
    AST_THREADSTORAGE(sip_transport_str_buf);
    
    /*! \brief Size of the SIP transport buffer */
    #define SIP_TRANSPORT_STR_BUFSIZE 128
    
    
    /*! \brief Authentication container for realm authentication */
    static struct sip_auth_container *authl = NULL;
    /*! \brief Global authentication container protection while adjusting the references. */
    AST_MUTEX_DEFINE_STATIC(authl_lock);
    
    static struct ast_manager_event_blob *session_timeout_to_ami(struct stasis_message *msg);
    STASIS_MESSAGE_TYPE_DEFN_LOCAL(session_timeout_type,
    	.to_ami = session_timeout_to_ami,
    	);
    
    
    /* --- Sockets and networking --------------*/
    
    /*! \brief Main socket for UDP SIP communication.
    
     *
     * sipsock is shared between the SIP manager thread (which handles reload
    
     * requests), the udp io handler (sipsock_read()) and the user routines that
     * issue udp writes (using __sip_xmit()).
    
     * The socket is -1 only when opening fails (this is a permanent condition),
     * or when we are handling a reload() that changes its address (this is
     * a transient situation during which we might have a harmless race, see
     * below). Because the conditions for the race to be possible are extremely
     * rare, we don't want to pay the cost of locking on every I/O.
     * Rather, we remember that when the race may occur, communication is
     * bound to fail anyways, so we just live with this event and let
     * the protocol handle this above us.
     */
    static int sipsock  = -1;
    
    
    Mark Michelson's avatar
    Mark Michelson committed
    struct ast_sockaddr bindaddr;	/*!< UDP: The address we bind to */
    
    
    /*! \brief our (internal) default address/port to put in SIP/SDP messages
     *  internip is initialized picking a suitable address from one of the
     * interfaces, and the same port number we bind to. It is used as the
     * default address/port in SIP messages, and as the default address
     * (but not port) in SDP messages.
     */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static struct ast_sockaddr internip;
    
    
    /*! \brief our external IP address/port for SIP sessions.
    
     * externaddr.sin_addr is only set when we know we might be behind
    
     * a NAT, and this is done using a variety of (mutually exclusive)
     * ways from the config file:
     *
    
     * + with "externaddr = host[:port]" we specify the address/port explicitly.
    
     *   The address is looked up only once when (re)loading the config file;
    
     * + with "externhost = host[:port]" we do a similar thing, but the
     *   hostname is stored in externhost, and the hostname->IP mapping
     *   is refreshed every 'externrefresh' seconds;
    
     * Other variables (externhost, externexpire, externrefresh) are used
     * to support the above functions.
     */
    
    static struct ast_sockaddr externaddr;      /*!< External IP address if we are behind NAT */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static struct ast_sockaddr media_address; /*!< External RTP IP address if we are behind NAT */
    
    static struct ast_sockaddr rtpbindaddr;   /*!< RTP: The address we bind to */
    
    David Vossel's avatar
    David Vossel committed
    static char externhost[MAXHOSTNAMELEN];   /*!< External host name */
    static time_t externexpire;             /*!< Expiration counter for re-resolving external host name in dynamic DNS */
    static int externrefresh = 10;          /*!< Refresh timer for DNS-based external address (dyndns) */
    
    static uint16_t externtcpport;          /*!< external tcp port */
    
    David Vossel's avatar
    David Vossel committed
    static uint16_t externtlsport;          /*!< external tls port */
    
    
    /*! \brief  List of local networks
     * We store "localnet" addresses from the config file into an access list,
     * marked as 'DENY', so the call to ast_apply_ha() will return
     * AST_SENSE_DENY for 'local' addresses, and AST_SENSE_ALLOW for 'non local'
     * (i.e. presumably public) addresses.
     */
    
    David Vossel's avatar
    David Vossel committed
    static struct ast_ha *localaddr;    /*!< List of local networks, on the same side of NAT as this Asterisk */
    
    David Vossel's avatar
    David Vossel committed
    static int ourport_tcp;             /*!< The port used for TCP connections */
    static int ourport_tls;             /*!< The port used for TCP/TLS connections */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static struct ast_sockaddr debugaddr;
    
    David Vossel's avatar
    David Vossel committed
    static struct ast_config *notify_types = NULL;    /*!< The list of manual NOTIFY types we know how to send */
    
    /*! some list management macros. */
    
    #define UNLINK(element, head, prev) do {	\
    	if (prev)				\
    		(prev)->next = (element)->next;	\
    	else					\
    		(head) = (element)->next;	\
    	} while (0)
    
    
    struct ao2_container *sip_monitor_instances;
    
    
    /*---------------------------- Forward declarations of functions in chan_sip.c */
    
    /* Note: This is added to help splitting up chan_sip.c into several files
    	in coming releases. */
    
    static struct ast_channel *sip_request_call(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause);
    
    static int sip_devicestate(const char *data);
    
    static int sip_sendtext(struct ast_channel *ast, const char *text);
    
    static int sip_call(struct ast_channel *ast, const char *dest, int timeout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen);
    
    static int sip_hangup(struct ast_channel *ast);
    static int sip_answer(struct ast_channel *ast);
    static struct ast_frame *sip_read(struct ast_channel *ast);
    static int sip_write(struct ast_channel *ast, struct ast_frame *frame);
    static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
    static int sip_transfer(struct ast_channel *ast, const char *dest);
    static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
    
    static int sip_senddigit_begin(struct ast_channel *ast, char digit);
    
    static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
    
    static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen);
    
    static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
    
    static const char *sip_get_callid(struct ast_channel *chan);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_request_do(struct sip_request *req, struct ast_sockaddr *addr);
    
    static int sip_standard_port(enum ast_transport type, int port);
    
    static int sip_prepare_socket(struct sip_pvt *p);
    
    static int get_address_family_filter(unsigned int transport);
    
    /*--- Transmitting responses and requests */
    
    static int sipsock_read(int *id, int fd, short events, void *ignore);
    
    static int __sip_xmit(struct sip_pvt *p, struct ast_str *data);
    
    static int __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, int resp, struct ast_str *data, int fatal, int sipmethod);
    
    static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp);
    
    static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
    
    static int retrans_pkt(const void *data);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int transmit_response_using_temp(ast_string_field callid, struct ast_sockaddr *addr, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    
    static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
    static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
    
    static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
    
    static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable);
    
    static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
    static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
    
    static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri);
    static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri);
    
    static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
    
    static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded);
    
    static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_info_with_vidupdate(struct sip_pvt *p);
    
    static int transmit_message(struct sip_pvt *p, int init, int auth);
    
    static int transmit_refer(struct sip_pvt *p, const char *dest);
    
    static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
    
    static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
    
    static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
    
    static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
    static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
    
    static void copy_request(struct sip_request *dst, const struct sip_request *src);
    
    static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
    
    static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward);
    
    static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only);
    
    static int __sip_autodestruct(const void *data);
    
    static int update_call_counter(struct sip_pvt *fup, int event);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method);
    
    static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp);
    
    static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const char *pathbuf);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
    
    					      struct sip_request *req, const char *uri);
    
    static int get_sip_pvt_from_replaces(const char *callid, const char *totag, const char *fromtag,
    		struct sip_pvt **out_pvt, struct ast_channel **out_chan);
    
    static void check_pendings(struct sip_pvt *p);
    
    static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan);
    
    
    static void *sip_pickup_thread(void *stuff);
    static int sip_pickup(struct ast_channel *chan);
    
    
    static int sip_sipredirect(struct sip_pvt *p, const char *dest);
    
    static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method);
    
    
    /*--- Codec handling / SDP */
    static void try_suggested_sip_codec(struct sip_pvt *p);
    
    static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name);
    static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value);
    
    static int find_sdp(struct sip_request *req);
    
    static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action);
    
    static int process_sdp_o(const char *o, struct sip_pvt *p);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
    
    static int process_sdp_a_sendonly(const char *a, int *sendonly);
    
    static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
    
    static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
    
    static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
    static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
    static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
    static int process_sdp_a_image(const char *a, struct sip_pvt *p);
    
    static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
    
    static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
    
    static void start_ice(struct ast_rtp_instance *instance, int offer);
    
    static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
    
    			     struct ast_str **m_buf, struct ast_str **a_buf,
    
    			     int debug, int *min_packet_size, int *max_packet_size);
    
    static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
    
    				struct ast_str **m_buf, struct ast_str **a_buf,
    
    static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
    
    static void do_setnat(struct sip_pvt *p);
    
    static void stop_media_flows(struct sip_pvt *p);
    
    Olle Johansson's avatar
    Olle Johansson committed
    static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
    
    static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len);
    
    static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
    					 const char *secret, const char *md5secret, int sipmethod,
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    					 const char *uri, enum xmittype reliable);
    
    static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
    
    					      int sipmethod, const char *uri, enum xmittype reliable,
    
    Mark Michelson's avatar
    Mark Michelson committed
    					      struct ast_sockaddr *addr, struct sip_peer **authpeer);
    static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct ast_sockaddr *addr);
    
    static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
    
    static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context);
    static void clear_sip_domains(void);
    
    static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno);
    static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm);
    
    static int check_rtp_timeout(struct sip_pvt *dialog, time_t t);
    
    static int reload_config(enum channelreloadreason reason);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static void add_diversion(struct sip_request *req, struct sip_pvt *pvt);
    
    static int expire_register(const void *data);
    
    static void *do_monitor(void *data);
    
    static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
    
    static struct ast_variable *copy_vars(struct ast_variable *src);
    
    static int dialog_find_multiple(void *obj, void *arg, int flags);
    
    static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt);
    
    /* static int sip_addrcmp(char *name, struct sockaddr_in *sin);	Support for peer matching */
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static int sip_refer_alloc(struct sip_pvt *p);
    static int sip_notify_alloc(struct sip_pvt *p);
    
    static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
    
    static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer);
    static void check_for_nat(const struct ast_sockaddr *them, struct sip_pvt *p);
    
    /*--- Device monitoring and Device/extension state/event handling */
    
    static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force);
    
    static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data);
    
    static int sip_poke_noanswer(const void *data);
    
    static int sip_poke_peer(struct sip_peer *peer, int force);
    
    static void sip_poke_all_peers(void);
    
    static void sip_peer_hold(struct sip_pvt *p, int hold);
    
    static void mwi_event_cb(void *, struct stasis_subscription *, struct stasis_message *);
    static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
    static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
    
    static void sip_keepalive_all_peers(void);
    
    
    /*--- Applications, functions, CLI and manager command helpers */
    
    static const char *sip_nat_mode(const struct sip_pvt *p);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *transfermode2str(enum transfermodes mode) attribute_const;
    
    static int peer_status(struct sip_peer *peer, char *status, int statuslen);
    
    static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    
    static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct show_peers_context *cont, struct sip_peer *peer);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static void  print_group(int fd, ast_group_t group, int crlf);
    
    static void  print_named_groups(int fd, struct ast_namedgroups *groups, int crlf);
    
    static const char *dtmfmode2str(int mode) attribute_const;
    
    static int str2dtmfmode(const char *str) attribute_unused;
    
    static const char *insecure2str(int mode) attribute_const;
    
    static const char *allowoverlap2str(int mode) attribute_const;
    
    static void cleanup_stale_contexts(char *new, char *old);
    static const char *domain_mode_to_text(const enum domain_mode mode);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *_sip_qualify_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    static char *sip_qualify_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_show_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static const char *subscription_type2str(enum subscriptiontype subtype) attribute_pure;
    
    static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
    static char *complete_sip_peer(const char *word, int state, int flags2);
    
    static char *complete_sip_registered_peer(const char *word, int state, int flags2);
    
    static char *complete_sip_show_history(const char *line, const char *word, int pos, int state);
    
    static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state);
    
    static char *complete_sip_unregister(const char *line, const char *word, int pos, int state);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static char *complete_sip_notify(const char *line, const char *word, int pos, int state);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_do_debug_ip(int fd, const char *arg);
    static char *sip_do_debug_peer(int fd, const char *arg);
    
    static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static int sip_dtmfmode(struct ast_channel *chan, const char *data);
    static int sip_addheader(struct ast_channel *chan, const char *data);
    
    static int sip_do_reload(enum channelreloadreason reason);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr,
    				      const char *name, int flag, int family);
    static int ast_sockaddr_resolve_first(struct ast_sockaddr *addr,
    				      const char *name, int flag);
    
    static int ast_sockaddr_resolve_first_transport(struct ast_sockaddr *addr,
    						const char *name, int flag, unsigned int transport);
    
    	Functions for enabling debug per IP or fully, or enabling history logging for
    	a SIP dialog
    */
    
    static void sip_dump_history(struct sip_pvt *dialog);	/* Dump history to debuglog at end of dialog, before destroying data */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static inline int sip_debug_test_addr(const struct ast_sockaddr *addr);
    
    static inline int sip_debug_test_pvt(struct sip_pvt *p);
    
    static void append_history_full(struct sip_pvt *p, const char *fmt, ...);
    
    static void sip_dump_history(struct sip_pvt *dialog);
    
    static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only);
    
    static int update_call_counter(struct sip_pvt *fup, int event);
    static void sip_destroy_peer(struct sip_peer *peer);
    
    static void sip_destroy_peer_fn(void *peer);
    
    static void set_peer_defaults(struct sip_peer *peer);
    static struct sip_peer *temp_peer(const char *name);
    static void register_peer_exten(struct sip_peer *peer, int onoff);
    
    static int sip_poke_peer_s(const void *data);
    
    static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req);
    static void reg_source_db(struct sip_peer *peer);
    static void destroy_association(struct sip_peer *peer);
    
    static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno);
    
    static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v);
    
    David Vossel's avatar
    David Vossel committed
    static void set_socket_transport(struct sip_socket *socket, int transport);
    
    static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags);
    
    static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path);
    
    static void update_peer(struct sip_peer *p, int expire);
    
    static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
    
    static const char *get_name_from_variable(const struct ast_variable *var);
    
    static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, char *callbackexten, int devstate_only, int which_objects);
    
    static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    
    /*--- Internal UA client handling (outbound registrations) */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p);
    
    static void sip_registry_destroy(void *reg);
    
    static int sip_register(const char *value, int lineno);
    
    static const char *regstate2str(enum sipregistrystate regstate) attribute_const;
    
    static int sip_reregister(const void *data);
    
    static int __sip_do_register(struct sip_registry *r);
    
    static int sip_reg_timeout(const void *data);
    
    static void sip_send_all_registers(void);
    
    static int sip_reinvite_retry(const void *data);
    
    static int determine_firstline_parts(struct sip_request *req);
    
    static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
    
    static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize);
    
    static int find_sip_method(const char *msg);
    
    static unsigned int parse_allowed_methods(struct sip_request *req);
    static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req);
    
    static int parse_request(struct sip_request *req);
    
    static const char *referstatus2str(enum referstatus rstatus) attribute_pure;
    
    static int method_match(enum sipmethod id, const char *name);
    
    static void parse_copy(struct sip_request *dst, const struct sip_request *src);
    
    static void parse_oli(struct sip_request *req, struct ast_channel *chan);
    
    static const char *find_alias(const char *name, const char *_default);
    static const char *__get_header(const struct sip_request *req, const char *name, int *start);
    
    static void lws2sws(struct ast_str *msgbuf);
    
    static void extract_uri(struct sip_pvt *p, struct sip_request *req);
    
    static char *remove_uri_parameters(char *uri);
    
    static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
    static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
    static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
    static int set_address_from_contact(struct sip_pvt *pvt);
    
    static void check_via(struct sip_pvt *p, const struct sip_request *req);
    
    static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
    
    static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason, char **reason_str);
    
    static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
    
    static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout);
    
    static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
    static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
    
    static int get_domain(const char *str, char *domain, int len);
    
    static void get_realm(struct sip_pvt *p, const struct sip_request *req);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static char *get_content(struct sip_request *req);
    
    /*-- TCP connection handling ---*/
    
    static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session);
    
    static void *sip_tcp_worker_fn(void *);
    
    
    /*--- Constructing requests and responses */
    static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
    static int init_req(struct sip_request *req, int sipmethod, const char *recip);
    
    static void deinit_req(struct sip_request *req);
    
    static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, uint32_t seqno, int newbranch);
    
    static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri);
    
    static int init_resp(struct sip_request *resp, const char *msg);
    
    static inline int resp_needs_contact(const char *msg, enum sipmethod method);
    
    static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static const struct ast_sockaddr *sip_real_dst(const struct sip_pvt *p);
    
    static void build_via(struct sip_pvt *p);
    static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer);
    
    static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_sockaddr *addr, int newdialog);
    
    static char *generate_random_string(char *buf, size_t size);
    static void build_callid_pvt(struct sip_pvt *pvt);
    
    static void change_callid_pvt(struct sip_pvt *pvt, const char *callid);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static void build_callid_registry(struct sip_registry *reg, const struct ast_sockaddr *ourip, const char *fromdomain);
    
    static void build_localtag_registry(struct sip_registry *reg);
    
    static void make_our_tag(struct sip_pvt *pvt);
    
    static int add_header(struct sip_request *req, const char *var, const char *value);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static int add_max_forwards(struct sip_pvt *dialog, struct sip_request *req);
    
    static int add_content(struct sip_request *req, const char *line);
    static int finalize_content(struct sip_request *req);
    
    static void destroy_msg_headers(struct sip_pvt *pvt);
    static int add_text(struct sip_request *req, struct sip_pvt *p);
    
    static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode);
    
    static int add_rpid(struct sip_request *req, struct sip_pvt *p);
    
    static int add_vidupdate(struct sip_request *req);
    
    static void add_route(struct sip_request *req, struct sip_route *route, int skip);
    
    static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
    
    static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field);
    static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field);
    
    static void set_destination(struct sip_pvt *p, const char *uri);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static void add_date(struct sip_request *req);
    static void add_expires(struct sip_request *req, int expires);
    
    static void build_contact(struct sip_pvt *p);
    
    /*------Request handling functions */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int *recount, int *nounlock);
    
    static int handle_request_update(struct sip_pvt *p, struct sip_request *req);
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *recount, const char *e, int *nounlock);
    static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock);
    
    static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *sin, const char *e);
    
    static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
    
    static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
    
    static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
    
    static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
    		int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan);
    
    static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
    
    static int local_attended_transfer(struct sip_pvt *transferer, struct ast_channel *transferer_chan, uint32_t seqno, int *nounlock);
    
    
    /*------Response handling functions */
    
    static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
    
    /*------ SRTP Support -------- */
    
    static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp, const char *a);
    
    /*------ T38 Support --------- */
    static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
    
    static void change_t38_state(struct sip_pvt *p, int state);
    
    /*------ Session-Timers functions --------- */
    static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp);
    static int  proc_session_timer(const void *vp);
    static void stop_session_timer(struct sip_pvt *p);
    static void start_session_timer(struct sip_pvt *p);
    static void restart_session_timer(struct sip_pvt *p);
    
    static const char *strefresherparam2str(enum st_refresher r);
    static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher_param *const p_ref);
    
    static int parse_minse(const char *p_hdrval, int *const p_interval);
    static int st_get_se(struct sip_pvt *, int max);
    static enum st_refresher st_get_refresher(struct sip_pvt *);
    
    static enum st_mode st_get_mode(struct sip_pvt *, int no_cached);
    
    static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p);
    
    
    /*------- RTP Glue functions -------- */
    
    static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active);
    
    /*!--- SIP MWI Subscription support */
    static int sip_subscribe_mwi(const char *value, int lineno);
    
    static void sip_subscribe_mwi_destroy(void *data);
    
    static void sip_send_all_mwi_subscriptions(void);
    static int sip_subscribe_mwi_do(const void *data);
    static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi);
    
    /*! \brief Definition of this channel for PBX channel registration */
    
    struct ast_channel_tech sip_tech = {
    
    	.description = "Session Initiation Protocol (SIP)",
    
    	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
    
    	.requester = sip_request_call,			/* called with chan unlocked */
    	.devicestate = sip_devicestate,			/* called with chan unlocked (not chan-specific) */
    	.call = sip_call,			/* called with chan locked */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	.send_html = sip_sendhtml,
    
    	.hangup = sip_hangup,			/* called with chan locked */
    	.answer = sip_answer,			/* called with chan locked */
    	.read = sip_read,			/* called with chan locked */
    	.write = sip_write,			/* called with chan locked */
    	.write_video = sip_write,		/* called with chan locked */
    
    	.write_text = sip_write,
    
    	.indicate = sip_indicate,		/* called with chan locked */
    	.transfer = sip_transfer,		/* called with chan locked */
    	.fixup = sip_fixup,			/* called with chan locked */
    	.send_digit_begin = sip_senddigit_begin,	/* called with chan unlocked */
    
    	.early_bridge = ast_rtp_instance_early_bridge,
    
    	.send_text = sip_sendtext,		/* called with chan locked */
    
    	.get_pvt_uniqueid = sip_get_callid,
    
    /*! \brief This version of the sip channel tech has no send_digit_begin
    
     * callback so that the core knows that the channel does not want
     * DTMF BEGIN frames.
     * The struct is initialized just before registering the channel driver,
     * and is for use with channels using SIP INFO DTMF.
     */
    
    /*------- CC Support -------- */
    
    static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
    static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
    static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
    
    static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
    
    static int sip_cc_agent_status_request(struct ast_cc_agent *agent);
    static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent);
    static int sip_cc_agent_recall(struct ast_cc_agent *agent);
    static void sip_cc_agent_destructor(struct ast_cc_agent *agent);
    
    static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = {
    	.type = "SIP",
    	.init = sip_cc_agent_init,
    	.start_offer_timer = sip_cc_agent_start_offer_timer,
    	.stop_offer_timer = sip_cc_agent_stop_offer_timer,
    
    	.respond = sip_cc_agent_respond,
    
    	.status_request = sip_cc_agent_status_request,
    	.start_monitoring = sip_cc_agent_start_monitoring,
    	.callee_available = sip_cc_agent_recall,
    	.destructor = sip_cc_agent_destructor,
    
    /* -------- End of declarations of structures, constants and forward declarations of functions
    
       ------------------------
    */
    
    static int sip_epa_register(const struct epa_static_data *static_data)
    {
    	struct epa_backend *backend = ast_calloc(1, sizeof(*backend));
    
    	if (!backend) {
    		return -1;
    	}
    
    	backend->static_data = static_data;
    
    	AST_LIST_LOCK(&epa_static_data_list);
    	AST_LIST_INSERT_TAIL(&epa_static_data_list, backend, next);
    	AST_LIST_UNLOCK(&epa_static_data_list);
    	return 0;
    }
    
    static void sip_epa_unregister_all(void)
    {
    	struct epa_backend *backend;
    
    	AST_LIST_LOCK(&epa_static_data_list);
    	while ((backend = AST_LIST_REMOVE_HEAD(&epa_static_data_list, next))) {
    		ast_free(backend);
    	}
    	AST_LIST_UNLOCK(&epa_static_data_list);
    }
    
    static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry);
    
    static void cc_epa_destructor(void *data)
    {
    	struct sip_epa_entry *epa_entry = data;
    	struct cc_epa_entry *cc_entry = epa_entry->instance_data;
    	ast_free(cc_entry);
    }
    
    static const struct epa_static_data cc_epa_static_data  = {
    	.event = CALL_COMPLETION,
    	.name = "call-completion",
    	.handle_error = cc_handle_publish_error,
    	.destructor = cc_epa_destructor,
    };
    
    static const struct epa_static_data *find_static_data(const char * const event_package)
    {
    	const struct epa_backend *backend = NULL;
    
    	AST_LIST_LOCK(&epa_static_data_list);
    	AST_LIST_TRAVERSE(&epa_static_data_list, backend, next) {
    		if (!strcmp(backend->static_data->name, event_package)) {
    			break;
    		}
    	}
    	AST_LIST_UNLOCK(&epa_static_data_list);
    	return backend ? backend->static_data : NULL;
    }
    
    static struct sip_epa_entry *create_epa_entry (const char * const event_package, const char * const destination)
    {
    	struct sip_epa_entry *epa_entry;
    	const struct epa_static_data *static_data;
    
    	if (!(static_data = find_static_data(event_package))) {
    		return NULL;
    	}
    
    	if (!(epa_entry = ao2_t_alloc(sizeof(*epa_entry), static_data->destructor, "Allocate new EPA entry"))) {
    		return NULL;
    	}
    
    	epa_entry->static_data = static_data;
    	ast_copy_string(epa_entry->destination, destination, sizeof(epa_entry->destination));
    	return epa_entry;
    }
    static enum ast_cc_service_type service_string_to_service_type(const char * const service_string)
    {
    	enum ast_cc_service_type service;
    	for (service = AST_CC_CCBS; service <= AST_CC_CCNL; ++service) {
    		if (!strcasecmp(service_string, sip_cc_service_map[service].service_string)) {
    			return service;
    		}
    	}
    	return AST_CC_NONE;
    }
    
    /* Even state compositors code */
    static void esc_entry_destructor(void *obj)
    {
    	struct sip_esc_entry *esc_entry = obj;
    	if (esc_entry->sched_id > -1) {
    		AST_SCHED_DEL(sched, esc_entry->sched_id);
    	}
    }
    
    static int esc_hash_fn(const void *obj, const int flags)
    {
    	const struct sip_esc_entry *entry = obj;
    	return ast_str_hash(entry->entity_tag);
    }
    
    static int esc_cmp_fn(void *obj, void *arg, int flags)
    {
    	struct sip_esc_entry *entry1 = obj;
    	struct sip_esc_entry *entry2 = arg;
    
    	return (!strcmp(entry1->entity_tag, entry2->entity_tag)) ? (CMP_MATCH | CMP_STOP) : 0;
    }
    
    static struct event_state_compositor *get_esc(const char * const event_package) {
    	int i;
    	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
    		if (!strcasecmp(event_package, event_state_compositors[i].name)) {
    			return &event_state_compositors[i];
    		}
    	}
    	return NULL;
    }
    
    static struct sip_esc_entry *get_esc_entry(const char * entity_tag, struct event_state_compositor *esc) {
    	struct sip_esc_entry *entry;
    	struct sip_esc_entry finder;
    
    	ast_copy_string(finder.entity_tag, entity_tag, sizeof(finder.entity_tag));
    
    	entry = ao2_find(esc->compositor, &finder, OBJ_POINTER);
    
    	return entry;
    }
    
    static int publish_expire(const void *data)
    {
    	struct sip_esc_entry *esc_entry = (struct sip_esc_entry *) data;
    	struct event_state_compositor *esc = get_esc(esc_entry->event);
    
    	ast_assert(esc != NULL);
    
    	ao2_unlink(esc->compositor, esc_entry);
    	ao2_ref(esc_entry, -1);
    	return 0;
    }
    
    static void create_new_sip_etag(struct sip_esc_entry *esc_entry, int is_linked)
    {
    	int new_etag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
    	struct event_state_compositor *esc = get_esc(esc_entry->event);
    
    	ast_assert(esc != NULL);
    	if (is_linked) {
    		ao2_unlink(esc->compositor, esc_entry);
    	}
    	snprintf(esc_entry->entity_tag, sizeof(esc_entry->entity_tag), "%d", new_etag);
    	ao2_link(esc->compositor, esc_entry);
    }
    
    static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc, struct sip_request *req, const int expires)
    {
    	struct sip_esc_entry *esc_entry;
    	int expires_ms;
    
    	if (!(esc_entry = ao2_alloc(sizeof(*esc_entry), esc_entry_destructor))) {
    		return NULL;
    	}
    
    	esc_entry->event = esc->name;
    
    	expires_ms = expires * 1000;
    	/* Bump refcount for scheduler */
    	ao2_ref(esc_entry, +1);
    	esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
    
    	/* Note: This links the esc_entry into the ESC properly */
    	create_new_sip_etag(esc_entry, 0);
    
    	return esc_entry;
    }
    
    static int initialize_escs(void)
    {
    	int i, res = 0;
    	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
    		if (!((event_state_compositors[i].compositor) =
    					ao2_container_alloc(ESC_MAX_BUCKETS, esc_hash_fn, esc_cmp_fn))) {
    			res = -1;
    		}
    	}
    	return res;
    }
    
    static void destroy_escs(void)
    {
    	int i;
    	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
    
    		ao2_cleanup(event_state_compositors[i].compositor);
    
    static int find_by_notify_uri_helper(void *obj, void *arg, int flags)
    {
    	struct ast_cc_agent *agent = obj;
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	const char *uri = arg;
    
    	return !sip_uri_cmp(agent_pvt->notify_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
    
    static struct ast_cc_agent *find_sip_cc_agent_by_notify_uri(const char * const uri)
    
    David Vossel's avatar
    David Vossel committed
    {
    
    	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_notify_uri_helper, (char *)uri, "SIP");
    	return agent;
    
    static int find_by_subscribe_uri_helper(void *obj, void *arg, int flags)
    
    David Vossel's avatar
    David Vossel committed
    {
    
    	struct ast_cc_agent *agent = obj;
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	const char *uri = arg;
    
    
    	return !sip_uri_cmp(agent_pvt->subscribe_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
    
    static struct ast_cc_agent *find_sip_cc_agent_by_subscribe_uri(const char * const uri)
    
    	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_subscribe_uri_helper, (char *)uri, "SIP");
    	return agent;
    
    static int find_by_callid_helper(void *obj, void *arg, int flags)
    
    	struct ast_cc_agent *agent = obj;
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	struct sip_pvt *call_pvt = arg;
    
    	return !strcmp(agent_pvt->original_callid, call_pvt->callid) ? CMP_MATCH | CMP_STOP : 0;
    
    static struct ast_cc_agent *find_sip_cc_agent_by_original_callid(struct sip_pvt *pvt)
    
    David Vossel's avatar
    David Vossel committed
    {
    
    	struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_callid_helper, pvt, "SIP");
    	return agent;
    }
    
    David Vossel's avatar
    David Vossel committed
    
    
    static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
    {
    	struct sip_cc_agent_pvt *agent_pvt = ast_calloc(1, sizeof(*agent_pvt));
    
    	struct sip_pvt *call_pvt = ast_channel_tech_pvt(chan);
    
    
    	if (!agent_pvt) {
    		return -1;
    
    	ast_assert(!strcmp(ast_channel_tech(chan)->type, "SIP"));
    
    
    	ast_copy_string(agent_pvt->original_callid, call_pvt->callid, sizeof(agent_pvt->original_callid));
    	ast_copy_string(agent_pvt->original_exten, call_pvt->exten, sizeof(agent_pvt->original_exten));
    	agent_pvt->offer_timer_id = -1;
    	agent->private_data = agent_pvt;
    	sip_pvt_lock(call_pvt);
    	ast_set_flag(&call_pvt->flags[0], SIP_OFFER_CC);
    	sip_pvt_unlock(call_pvt);
    	return 0;
    
    static int sip_offer_timer_expire(const void *data)
    
    David Vossel's avatar
    David Vossel committed
    {
    
    	struct ast_cc_agent *agent = (struct ast_cc_agent *) data;
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    
    David Vossel's avatar
    David Vossel committed
    
    
    	agent_pvt->offer_timer_id = -1;
    
    	return ast_cc_failed(agent->core_id, "SIP agent %s's offer timer expired", agent->device_name);
    
    static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent)
    {
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	int when;
    
    	when = ast_get_cc_offer_timer(agent->cc_params) * 1000;
    	agent_pvt->offer_timer_id = ast_sched_add(sched, when, sip_offer_timer_expire, agent);
    	return 0;
    }
    
    static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
    
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    
    	AST_SCHED_DEL(sched, agent_pvt->offer_timer_id);
    	return 0;
    
    static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
    
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    
    	sip_pvt_lock(agent_pvt->subscribe_pvt);
    	ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
    
    	if (reason == AST_CC_AGENT_RESPONSE_SUCCESS || !ast_strlen_zero(agent_pvt->notify_uri)) {
    		/* The second half of this if statement may be a bit hard to grasp,
    		 * so here's an explanation. When a subscription comes into
    		 * chan_sip, as long as it is not malformed, it will be passed
    		 * to the CC core. If the core senses an out-of-order state transition,
    		 * then the core will call this callback with the "reason" set to a
    		 * failure condition.
    		 * However, an out-of-order state transition will occur during a resubscription
    		 * for CC. In such a case, we can see that we have already generated a notify_uri
    		 * and so we can detect that this isn't a *real* failure. Rather, it is just
    		 * something the core doesn't recognize as a legitimate SIP state transition.
    		 * Thus we respond with happiness and flowers.
    		 */
    		transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq);
    		transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED);
    	} else {
    		transmit_response(agent_pvt->subscribe_pvt, "500 Internal Error", &agent_pvt->subscribe_pvt->initreq);
    	}
    
    	sip_pvt_unlock(agent_pvt->subscribe_pvt);
    	agent_pvt->is_available = TRUE;
    
    static int sip_cc_agent_status_request(struct ast_cc_agent *agent)
    
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	enum ast_device_state state = agent_pvt->is_available ? AST_DEVICE_NOT_INUSE : AST_DEVICE_INUSE;
    	return ast_cc_agent_status_response(agent->core_id, state);
    
    static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent)
    
    	/* To start monitoring just means to wait for an incoming PUBLISH
    	 * to tell us that the caller has become available again. No special
    	 * action is needed
    	 */
    	return 0;
    }
    
    static int sip_cc_agent_recall(struct ast_cc_agent *agent)
    {
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    	/* If we have received a PUBLISH beforehand stating that the caller in question
    	 * is not available, we can save ourself a bit of effort here and just report
    	 * the caller as busy
    	 */
    	if (!agent_pvt->is_available) {
    		return ast_cc_agent_caller_busy(agent->core_id, "Caller %s is busy, reporting to the core",
    				agent->device_name);
    
    	/* Otherwise, we transmit a NOTIFY to the caller and await either
    	 * a PUBLISH or an INVITE
    	 */
    	sip_pvt_lock(agent_pvt->subscribe_pvt);
    	transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_READY);
    	sip_pvt_unlock(agent_pvt->subscribe_pvt);
    	return 0;
    }
    
    static void sip_cc_agent_destructor(struct ast_cc_agent *agent)
    {
    	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
    
    	if (!agent_pvt) {
    		/* The agent constructor probably failed. */
    		return;
    
    	sip_cc_agent_stop_offer_timer(agent);
    	if (agent_pvt->subscribe_pvt) {
    		sip_pvt_lock(agent_pvt->subscribe_pvt);
    		if (!ast_test_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
    			/* If we haven't sent a 200 OK for the SUBSCRIBE dialog yet, then we need to send a response letting
    			 * the subscriber know something went wrong
    			 */
    			transmit_response(agent_pvt->subscribe_pvt, "500 Internal Server Error", &agent_pvt->subscribe_pvt->initreq);
    		}
    		sip_pvt_unlock(agent_pvt->subscribe_pvt);
    		agent_pvt->subscribe_pvt = dialog_unref(agent_pvt->subscribe_pvt, "SIP CC agent destructor: Remove ref to subscription");
    
    	ast_free(agent_pvt);
    }
    
    
    static int sip_monitor_instance_hash_fn(const void *obj, const int flags)
    {
    	const struct sip_monitor_instance *monitor_instance = obj;
    	return monitor_instance->core_id;
    }
    
    static int sip_monitor_instance_cmp_fn(void *obj, void *arg, int flags)
    {
    	struct sip_monitor_instance *monitor_instance1 = obj;
    	struct sip_monitor_instance *monitor_instance2 = arg;
    
    	return monitor_instance1->core_id == monitor_instance2->core_id ? CMP_MATCH | CMP_STOP : 0;
    }
    
    static void sip_monitor_instance_destructor(void *data)
    {
    	struct sip_monitor_instance *monitor_instance = data;
    	if (monitor_instance->subscription_pvt) {
    		sip_pvt_lock(monitor_instance->subscription_pvt);
    		monitor_instance->subscription_pvt->expiry = 0;
    		transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 0, monitor_instance->subscribe_uri);
    		sip_pvt_unlock(monitor_instance->subscription_pvt);
    		dialog_unref(monitor_instance->subscription_pvt, "Unref monitor instance ref of subscription pvt");
    	}
    	if (monitor_instance->suspension_entry) {
    		monitor_instance->suspension_entry->body[0] = '\0';
    		transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_REMOVE ,monitor_instance->notify_uri);
    		ao2_t_ref(monitor_instance->suspension_entry, -1, "Decrementing suspension entry refcount in sip_monitor_instance_destructor");
    
    	ast_string_field_free_memory(monitor_instance);
    
    static struct sip_monitor_instance *sip_monitor_instance_init(int core_id, const char * const subscribe_uri, const char * const peername, const char * const device_name)
    
    	struct sip_monitor_instance *monitor_instance = ao2_alloc(sizeof(*monitor_instance), sip_monitor_instance_destructor);
    
    	if (!monitor_instance) {
    
    	if (ast_string_field_init(monitor_instance, 256)) {
    		ao2_ref(monitor_instance, -1);
    
    
    	ast_string_field_set(monitor_instance, subscribe_uri, subscribe_uri);
    	ast_string_field_set(monitor_instance, peername, peername);
    	ast_string_field_set(monitor_instance, device_name, device_name);
    	monitor_instance->core_id = core_id;
    	ao2_link(sip_monitor_instances, monitor_instance);