Skip to content
Snippets Groups Projects
chan_sip.c 1.06 MiB
Newer Older
  • Learn to ignore specific revisions
  • }
    
    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_ref(event_state_compositors[i].compositor, -1);
    	}
    }
    
    
     * Here we implement the container for dialogs which are in the
     * dialog_needdestroy state to iterate only through the dialogs
     * unlink them instead of iterate through all dialogs
     */
    struct ao2_container *dialogs_needdestroy;
    
    
     * Here we implement the container for dialogs which have rtp
     * traffic and rtptimeout, rtpholdtimeout or rtpkeepalive
     * set. We use this container instead the whole dialog list.
     */
    struct ao2_container *dialogs_rtpcheck;
    
    
    David Vossel's avatar
    David Vossel committed
     * Here we implement the container for dialogs (sip_pvt), defining
     * generic wrapper functions to ease the transition from the current
     * implementation (a single linked list) to a different container.
     * In addition to a reference to the container, we need functions to lock/unlock
     * the container and individual items, and functions to add/remove
     * references to the individual items.
     */
    static struct ao2_container *dialogs;
    #define sip_pvt_lock(x) ao2_lock(x)
    #define sip_pvt_trylock(x) ao2_trylock(x)
    #define sip_pvt_unlock(x) ao2_unlock(x)
    
    
    /*! \brief  The table of TCP threads */
    static struct ao2_container *threadt;
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  The peer list: Users, Peers and Friends */
    
    static struct ao2_container *peers;
    static struct ao2_container *peers_by_ip;
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
    
    static struct ast_register_list {
    
    	ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
    
    /*! \brief  The MWI subscription list */
    static struct ast_subscription_mwi_list {
    	ASTOBJ_CONTAINER_COMPONENTS(struct sip_subscription_mwi);
    } submwil;
    
    /*! \brief A per-thread temporary pvt structure */
    
    AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
    
    /*! \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);
    
    /* --- 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 */
    
    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)
    
    
    /*---------------------------- 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_channel *requestor, void *data, int *cause);
    
    static int sip_devicestate(void *data);
    static int sip_sendtext(struct ast_channel *ast, const char *text);
    static int sip_call(struct ast_channel *ast, 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 sip_transport type, int port);
    
    static int sip_prepare_socket(struct sip_pvt *p);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int get_address_family_filter(const struct ast_sockaddr *addr);
    
    /*--- 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, int 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, int sipmethod, struct sip_request *req, enum xmittype reliable);
    
    static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
    
    static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int 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_with_text(struct sip_pvt *p, const char *text, int init, int auth);
    static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg);
    
    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, int seqno);
    static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int 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 void *registry_unref(struct sip_registry *reg, char *tag);
    
    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 free_old_route(struct sip_route *route);
    static void list_route(struct sip_route *route);
    static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards);
    
    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 struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
    static void check_pendings(struct sip_pvt *p);
    static void *sip_park_thread(void *stuff);
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, const char *park_exten, const char *park_context);
    
    
    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_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_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);
    
    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,
    
    					 const char *uri, enum xmittype reliable, int ignore);
    
    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);
    
    static void add_diversion_header(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 */
    
    static int sip_refer_allocate(struct sip_pvt *p);
    
    static int sip_notify_allocate(struct sip_pvt *p);
    
    static void ast_quiet_chan(struct ast_channel *chan);
    static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
    
    static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
    
    /*--- Device monitoring and Device/extension state/event handling */
    
    static int cb_extensionstate(const char *context, const char *exten, enum ast_extension_states state, void *data);
    
    static int sip_devicestate(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(const struct ast_event *, void *);
    
    static void network_change_event_cb(const struct ast_event *, 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 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 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 void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
    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);
    
    static char *complete_sipnotify(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);
    
    	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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    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);
    
    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, 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(struct sip_registry *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 void append_date(struct sip_request *req);	/* Append date to SIP packet */
    
    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 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, 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);
    
    static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
    
    static int get_msg_text(char *buf, int len, struct sip_request *req);
    
    static int transmit_state_notify(struct sip_pvt *p, int state, 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);
    
    /*-- TCP connection handling ---*/
    
    static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, 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, int 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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_sockaddr *addr, int newdialog, struct ast_sockaddr *remote_address);
    
    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 make_our_tag(char *tagbuf, size_t len);
    static int add_header(struct sip_request *req, const char *var, const char *value);
    
    static int add_header_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 int add_text(struct sip_request *req, const char *text);
    
    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);
    
    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, char *uri);
    static void append_date(struct sip_request *req);
    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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct ast_sockaddr *addr, int *recount, const char *e, int *nounlock);
    
    static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int 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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int 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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct ast_sockaddr *addr, int *nounlock);
    static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int seqno, const char *e);
    
    static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock);
    
    
    /*------Response handling functions */
    
    static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    
    static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    
    /*------ SRTP Support -------- */
    static int setup_srtp(struct sip_srtp **srtp);
    static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_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 struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan);
    static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl);
    
    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 *strefresher2str(enum st_refresher r);
    static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher *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(struct sip_subscription_mwi *mwi);
    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 */
    
    	.bridge = ast_rtp_instance_bridge,			/* XXX 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,
    
    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 = chan->tech_pvt;
    
    	if (!agent_pvt) {
    		return -1;
    
    	ast_assert(!strcmp(chan->tech->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);
    }
    
    struct ao2_container *sip_monitor_instances;
    
    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);
    	return monitor_instance;
    
    static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags)
    
    	struct sip_monitor_instance *monitor_instance = obj;
    	return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0;
    }
    
    static int find_sip_monitor_instance_by_suspension_entry(void *obj, void *arg, int flags)
    {
    	struct sip_monitor_instance *monitor_instance = obj;
    	return monitor_instance->suspension_entry == arg ? CMP_MATCH | CMP_STOP : 0;
    }
    
    static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
    static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor);
    static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor);
    static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
    static void sip_cc_monitor_destructor(void *private_data);
    
    static struct ast_cc_monitor_callbacks sip_cc_monitor_callbacks = {
    	.type = "SIP",
    	.request_cc = sip_cc_monitor_request_cc,
    	.suspend = sip_cc_monitor_suspend,
    	.unsuspend = sip_cc_monitor_unsuspend,
    	.cancel_available_timer = sip_cc_monitor_cancel_available_timer,
    	.destructor = sip_cc_monitor_destructor,
    };
    
    static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
    {
    	struct sip_monitor_instance *monitor_instance = monitor->private_data;
    	enum ast_cc_service_type service = monitor->service_offered;
    	int when;
    
    	if (!monitor_instance) {
    		return -1;
    
    	if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL))) {
    		return -1;
    
    	when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
    		ast_get_ccnr_available_timer(monitor->interface->config_params);
    
    	sip_pvt_lock(monitor_instance->subscription_pvt);
    
    	ast_set_flag(&monitor_instance->subscription_pvt->flags[0], SIP_OUTGOING);
    
    	create_addr(monitor_instance->subscription_pvt, monitor_instance->peername, 0, 1, NULL);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sip_ouraddrfor(&monitor_instance->subscription_pvt->sa, &monitor_instance->subscription_pvt->ourip, monitor_instance->subscription_pvt);
    
    	monitor_instance->subscription_pvt->subscribed = CALL_COMPLETION;
    	monitor_instance->subscription_pvt->expiry = when;
    
    	transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 2, monitor_instance->subscribe_uri);
    	sip_pvt_unlock(monitor_instance->subscription_pvt);
    
    	ao2_t_ref(monitor, +1, "Adding a ref to the monitor for the scheduler");
    	*available_timer_id = ast_sched_add(sched, when * 1000, ast_cc_available_timer_expire, monitor);
    	return 0;
    
    static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
    
    	struct ast_str *body = ast_str_alloca(size);
    	char tuple_id[32];
    
    	generate_random_string(tuple_id, sizeof(tuple_id));
    
    	/* We'll make this a bare-bones pidf body. In state_notify_build_xml, the PIDF
    	 * body gets a lot more extra junk that isn't necessary, so we'll leave it out here.
    	 */
    	ast_str_append(&body, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    	/* XXX The entity attribute is currently set to the peer name associated with the
    	 * dialog. This is because we currently only call this function for call-completion
    	 * PUBLISH bodies. In such cases, the entity is completely disregarded. For other
    	 * event packages, it may be crucial to have a proper URI as the presentity so this
    	 * should be revisited as support is expanded.
    	 */
    	ast_str_append(&body, 0, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"%s\">\n", presentity);
    	ast_str_append(&body, 0, "<tuple id=\"%s\">\n", tuple_id);
    	ast_str_append(&body, 0, "<status><basic>%s</basic></status>\n", state == CC_OPEN ? "open" : "closed");
    	ast_str_append(&body, 0, "</tuple>\n");
    	ast_str_append(&body, 0, "</presence>\n");
    	ast_copy_string(pidf_body, ast_str_buffer(body), size);
    	return 0;
    
    static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor)
    
    	struct sip_monitor_instance *monitor_instance = monitor->private_data;
    	enum sip_publish_type publish_type;
    	struct cc_epa_entry *cc_entry;
    
    	if (!monitor_instance) {
    		return -1;
    	}
    
    	if (!monitor_instance->suspension_entry) {
    		/* We haven't yet allocated the suspension entry, so let's give it a shot */
    		if (!(monitor_instance->suspension_entry = create_epa_entry("call-completion", monitor_instance->peername))) {
    			ast_log(LOG_WARNING, "Unable to allocate sip EPA entry for call-completion\n");
    			ao2_ref(monitor_instance, -1);
    			return -1;
    
    		if (!(cc_entry = ast_calloc(1, sizeof(*cc_entry)))) {
    			ast_log(LOG_WARNING, "Unable to allocate space for instance data of EPA entry for call-completion\n");
    			ao2_ref(monitor_instance, -1);
    			return -1;