mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-24 05:02:10 +00:00
Merge remote-tracking branch 'fsorig/master'
Conflicts: src/mod/endpoints/mod_sofia/mod_sofia.c src/mod/endpoints/mod_sofia/sofia_glue.c
This commit is contained in:
@@ -169,7 +169,7 @@ struct switch_core_session {
|
||||
uint32_t track_id;
|
||||
switch_log_level_t loglevel;
|
||||
uint32_t soft_lock;
|
||||
switch_ivr_dmachine_t *dmachine;
|
||||
switch_ivr_dmachine_t *dmachine[2];
|
||||
plc_state_t *plc;
|
||||
};
|
||||
|
||||
@@ -255,6 +255,7 @@ struct switch_runtime {
|
||||
uint32_t db_handle_timeout;
|
||||
int curl_count;
|
||||
int ssl_count;
|
||||
int cpu_count;
|
||||
};
|
||||
|
||||
extern struct switch_runtime runtime;
|
||||
|
||||
@@ -87,6 +87,10 @@ SWITCH_DECLARE(int) switch_channel_test_ready(switch_channel_t *channel, switch_
|
||||
|
||||
#define switch_channel_up(_channel) (switch_channel_check_signal(_channel, SWITCH_TRUE) || switch_channel_get_state(_channel) < CS_HANGUP)
|
||||
#define switch_channel_down(_channel) (switch_channel_check_signal(_channel, SWITCH_TRUE) || switch_channel_get_state(_channel) >= CS_HANGUP)
|
||||
|
||||
#define switch_channel_up_nosig(_channel) switch_channel_get_state(_channel) < CS_HANGUP
|
||||
#define switch_channel_down_nosig(_channel) switch_channel_get_state(_channel) >= CS_HANGUP
|
||||
|
||||
#define switch_channel_media_ack(_channel) (!switch_channel_test_cap(_channel, CC_MEDIA_ACK) || switch_channel_test_flag(_channel, CF_MEDIA_ACK))
|
||||
|
||||
SWITCH_DECLARE(void) switch_channel_wait_for_state(switch_channel_t *channel, switch_channel_t *other_channel, switch_channel_state_t want_state);
|
||||
@@ -625,7 +629,10 @@ SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_
|
||||
/** @} */
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_channel_execute_on(switch_channel_t *channel, const char *variable_prefix);
|
||||
SWITCH_DECLARE(switch_status_t) switch_channel_api_on(switch_channel_t *channel, const char *variable_prefix);
|
||||
|
||||
SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_queued_extension(switch_channel_t *channel);
|
||||
SWITCH_DECLARE(void) switch_channel_transfer_to_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension);
|
||||
|
||||
SWITCH_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
@@ -687,7 +687,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_thread_launch(_In_ switch_co
|
||||
/*!
|
||||
\brief Signal a session's state machine thread that a state change has occured
|
||||
*/
|
||||
SWITCH_DECLARE(void) switch_core_session_wake_session_thread(_In_ switch_core_session_t *session);
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_session_wake_session_thread(_In_ switch_core_session_t *session);
|
||||
SWITCH_DECLARE(void) switch_core_session_signal_state_change(_In_ switch_core_session_t *session);
|
||||
|
||||
/*!
|
||||
@@ -712,12 +712,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_set_loglevel(switch_core_ses
|
||||
\return the log level
|
||||
*/
|
||||
SWITCH_DECLARE(switch_log_level_t) switch_core_session_get_loglevel(switch_core_session_t *session);
|
||||
|
||||
|
||||
|
||||
SWITCH_DECLARE(void) switch_core_session_soft_lock(switch_core_session_t *session, uint32_t sec);
|
||||
SWITCH_DECLARE(void) switch_core_session_soft_unlock(switch_core_session_t *session);
|
||||
SWITCH_DECLARE(void) switch_core_session_set_dmachine(switch_core_session_t *session, switch_ivr_dmachine_t *dmachine);
|
||||
SWITCH_DECLARE(switch_ivr_dmachine_t *) switch_core_session_get_dmachine(switch_core_session_t *session);
|
||||
SWITCH_DECLARE(void) switch_core_session_set_dmachine(switch_core_session_t *session, switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target);
|
||||
SWITCH_DECLARE(switch_ivr_dmachine_t *) switch_core_session_get_dmachine(switch_core_session_t *session, switch_digit_action_target_t target);
|
||||
SWITCH_DECLARE(switch_digit_action_target_t) switch_ivr_dmachine_get_target(switch_ivr_dmachine_t *dmachine);
|
||||
SWITCH_DECLARE(void) switch_ivr_dmachine_set_target(switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_session_set_codec_slin(switch_core_session_t *session, switch_slin_data_t *data);
|
||||
|
||||
/*!
|
||||
@@ -1975,8 +1978,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid,
|
||||
\brief Set the maximum priority the process can obtain
|
||||
\return 0 on success
|
||||
*/
|
||||
SWITCH_DECLARE(int32_t) set_high_priority(void);
|
||||
|
||||
SWITCH_DECLARE(int32_t) set_normal_priority(void);
|
||||
SWITCH_DECLARE(int32_t) set_auto_priority(void);
|
||||
SWITCH_DECLARE(int32_t) set_realtime_priority(void);
|
||||
SWITCH_DECLARE(int32_t) set_low_priority(void);
|
||||
|
||||
/*!
|
||||
\brief Change user and/or group of the running process
|
||||
@@ -2101,8 +2107,10 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_alias(const char *string);
|
||||
SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait);
|
||||
SWITCH_DECLARE(void) switch_cond_yield(switch_interval_time_t t);
|
||||
SWITCH_DECLARE(void) switch_cond_next(void);
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_chat_send(const char *name, const char *proto, const char *from, const char *to,
|
||||
const char *subject, const char *body, const char *type, const char *hint);
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_chat_send_args(const char *dest_proto, const char *proto, const char *from, const char *to,
|
||||
const char *subject, const char *body, const char *type, const char *hint);
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_chat_send(const char *dest_proto, switch_event_t *message_event);
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_chat_deliver(const char *dest_proto, switch_event_t **message_event);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_preprocess_session(switch_core_session_t *session, const char *cmds);
|
||||
|
||||
|
||||
@@ -540,6 +540,8 @@ SWITCH_DECLARE(int) switch_core_db_changes(switch_core_db_t *db);
|
||||
* literal.
|
||||
*/
|
||||
|
||||
SWITCH_DECLARE(char*)switch_sql_concat(void);
|
||||
|
||||
SWITCH_END_EXTERN_C
|
||||
#endif
|
||||
/* For Emacs:
|
||||
|
||||
@@ -156,6 +156,8 @@ SWITCH_DECLARE(bool) email(char *to, char *from, char *headers = NULL, char *bod
|
||||
SWITCH_DECLARE_CONSTRUCTOR Event(const char *type, const char *subclass_name = NULL);
|
||||
SWITCH_DECLARE_CONSTRUCTOR Event(switch_event_t *wrap_me, int free_me = 0);
|
||||
virtual SWITCH_DECLARE_CONSTRUCTOR ~ Event();
|
||||
SWITCH_DECLARE(int) chat_execute(const char *app, const char *data = NULL);
|
||||
SWITCH_DECLARE(int) chat_send(const char *dest_proto = NULL);
|
||||
SWITCH_DECLARE(const char *) serialize(const char *format = NULL);
|
||||
SWITCH_DECLARE(bool) setPriority(switch_priority_t priority = SWITCH_PRIORITY_NORMAL);
|
||||
SWITCH_DECLARE(const char *) getHeader(const char *header_name);
|
||||
@@ -312,7 +314,8 @@ SWITCH_DECLARE(bool) email(char *to, char *from, char *headers = NULL, char *bod
|
||||
int max_digits,
|
||||
int max_tries,
|
||||
int timeout, char *terminators, char *audio_files, char *bad_input_audio_files,
|
||||
char *digits_regex, const char *var_name = NULL, int digit_timeout = 0);
|
||||
char *digits_regex, const char *var_name = NULL, int digit_timeout = 0,
|
||||
const char *transfer_on_failure = NULL);
|
||||
|
||||
/** \brief Play a file that resides on disk into the channel
|
||||
*
|
||||
|
||||
@@ -103,7 +103,8 @@ struct switch_event {
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
EF_UNIQ_HEADERS = (1 << 0)
|
||||
EF_UNIQ_HEADERS = (1 << 0),
|
||||
EF_NO_CHAT_EXEC = (1 << 1)
|
||||
} switch_event_flag_t;
|
||||
|
||||
|
||||
@@ -157,6 +158,8 @@ _Ret_opt_z_ SWITCH_DECLARE(char *) switch_event_get_header_idx(switch_event_t *e
|
||||
|
||||
#define switch_event_get_header_nil(e, h) switch_str_nil(switch_event_get_header(e,h))
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_rename_header(switch_event_t *event, const char *header_name, const char *new_header_name);
|
||||
|
||||
/*!
|
||||
\brief Retrieve the body value from an event
|
||||
\param event the event to read the body from
|
||||
@@ -207,6 +210,8 @@ SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event);
|
||||
*/
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_dup(switch_event_t **event, switch_event_t *todup);
|
||||
SWITCH_DECLARE(void) switch_event_merge(switch_event_t *event, switch_event_t *tomerge);
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_dup_reply(switch_event_t **event, switch_event_t *todup);
|
||||
|
||||
/*!
|
||||
\brief Fire an event with full arguement list
|
||||
\param file the calling file
|
||||
@@ -321,6 +326,9 @@ SWITCH_DECLARE(switch_status_t) switch_event_running(void);
|
||||
*/
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_add_body(switch_event_t *event, const char *fmt, ...) PRINTF_FUNCTION(2, 3);
|
||||
#endif
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_set_body(switch_event_t *event, const char *body);
|
||||
|
||||
SWITCH_DECLARE(char *) switch_event_expand_headers(switch_event_t *event, const char *in);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_event_create_pres_in_detailed(_In_z_ char *file, _In_z_ char *func, _In_ int line,
|
||||
|
||||
@@ -111,6 +111,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_events(switch_core_session_
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_event(switch_core_session_t *session);
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_session_t *session);
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_signal_data(switch_core_session_t *session);
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_process_indications(switch_core_session_t *session, switch_core_session_message_t *message);
|
||||
|
||||
/*!
|
||||
\brief Wait for time to pass for a specified number of milliseconds
|
||||
\param session the session to wait for.
|
||||
@@ -407,7 +409,8 @@ SWITCH_DECLARE(switch_status_t) switch_play_and_get_digits(switch_core_session_t
|
||||
const char *bad_input_audio_file,
|
||||
const char *var_name, char *digit_buffer, uint32_t digit_buffer_length,
|
||||
const char *digits_regex,
|
||||
uint32_t digit_timeout);
|
||||
uint32_t digit_timeout,
|
||||
const char *transfer_on_failure);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session_t *session,
|
||||
switch_speech_handle_t *sh,
|
||||
@@ -461,7 +464,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_enterprise_originate(switch_core_sess
|
||||
const char *cid_name_override,
|
||||
const char *cid_num_override,
|
||||
switch_caller_profile_t *caller_profile_override,
|
||||
switch_event_t *ovars, switch_originate_flag_t flags);
|
||||
switch_event_t *ovars, switch_originate_flag_t flags,
|
||||
switch_call_cause_t *cancel_cause);
|
||||
|
||||
SWITCH_DECLARE(void) switch_ivr_bridge_display(switch_core_session_t *session, switch_core_session_t *peer_session);
|
||||
|
||||
@@ -927,6 +931,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_release_file_handle(switch_core_sessi
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_process_fh(switch_core_session_t *session, const char *cmd, switch_file_handle_t *fhp);
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_insert_file(switch_core_session_t *session, const char *file, const char *insert_file, switch_size_t sample_point);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_ivr_create_message_reply(switch_event_t **reply, switch_event_t *message, const char *new_proto);
|
||||
|
||||
/** @} */
|
||||
|
||||
SWITCH_END_EXTERN_C
|
||||
|
||||
@@ -55,33 +55,35 @@ SWITCH_BEGIN_EXTERN_C
|
||||
struct switch_loadable_module_interface {
|
||||
/*! the name of the module */
|
||||
const char *module_name;
|
||||
/*! the table of endpoints the module has implmented */
|
||||
/*! the table of endpoints the module has implemented */
|
||||
switch_endpoint_interface_t *endpoint_interface;
|
||||
/*! the table of timers the module has implmented */
|
||||
/*! the table of timers the module has implemented */
|
||||
switch_timer_interface_t *timer_interface;
|
||||
/*! the table of dialplans the module has implmented */
|
||||
/*! the table of dialplans the module has implemented */
|
||||
switch_dialplan_interface_t *dialplan_interface;
|
||||
/*! the table of codecs the module has implmented */
|
||||
/*! the table of codecs the module has implemented */
|
||||
switch_codec_interface_t *codec_interface;
|
||||
/*! the table of applications the module has implmented */
|
||||
/*! the table of applications the module has implemented */
|
||||
switch_application_interface_t *application_interface;
|
||||
/*! the table of api functions the module has implmented */
|
||||
/*! the table of chat applications the module has implemented */
|
||||
switch_chat_application_interface_t *chat_application_interface;
|
||||
/*! the table of api functions the module has implemented */
|
||||
switch_api_interface_t *api_interface;
|
||||
/*! the table of file formats the module has implmented */
|
||||
/*! the table of file formats the module has implemented */
|
||||
switch_file_interface_t *file_interface;
|
||||
/*! the table of speech interfaces the module has implmented */
|
||||
/*! the table of speech interfaces the module has implemented */
|
||||
switch_speech_interface_t *speech_interface;
|
||||
/*! the table of directory interfaces the module has implmented */
|
||||
/*! the table of directory interfaces the module has implemented */
|
||||
switch_directory_interface_t *directory_interface;
|
||||
/*! the table of chat interfaces the module has implmented */
|
||||
/*! the table of chat interfaces the module has implemented */
|
||||
switch_chat_interface_t *chat_interface;
|
||||
/*! the table of say interfaces the module has implmented */
|
||||
/*! the table of say interfaces the module has implemented */
|
||||
switch_say_interface_t *say_interface;
|
||||
/*! the table of asr interfaces the module has implmented */
|
||||
/*! the table of asr interfaces the module has implemented */
|
||||
switch_asr_interface_t *asr_interface;
|
||||
/*! the table of management interfaces the module has implmented */
|
||||
/*! the table of management interfaces the module has implemented */
|
||||
switch_management_interface_t *management_interface;
|
||||
/*! the table of limit interfaces the module has implmented */
|
||||
/*! the table of limit interfaces the module has implemented */
|
||||
switch_limit_interface_t *limit_interface;
|
||||
switch_thread_rwlock_t *rwlock;
|
||||
int refs;
|
||||
@@ -169,6 +171,15 @@ SWITCH_DECLARE(switch_timer_interface_t *) switch_loadable_module_get_timer_inte
|
||||
*/
|
||||
SWITCH_DECLARE(switch_application_interface_t *) switch_loadable_module_get_application_interface(const char *name);
|
||||
|
||||
/*!
|
||||
\brief Retrieve the chat application interface by it's registered name
|
||||
\param name the name of the chat application
|
||||
\return the desired chat application interface
|
||||
*/
|
||||
SWITCH_DECLARE(switch_chat_application_interface_t *) switch_loadable_module_get_chat_application_interface(const char *name);
|
||||
|
||||
SWITCH_DECLARE(switch_status_t) switch_core_execute_chat_app(switch_event_t *message, const char *app, const char *data);
|
||||
|
||||
/*!
|
||||
\brief Retrieve the API interface by it's registered name
|
||||
\param name the name of the API
|
||||
@@ -335,6 +346,18 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void);
|
||||
break; \
|
||||
}
|
||||
|
||||
#define SWITCH_ADD_CHAT_APP(app_int, int_name, short_descript, long_descript, funcptr, syntax_string, app_flags) \
|
||||
for (;;) { \
|
||||
app_int = (switch_chat_application_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_CHAT_APPLICATION_INTERFACE); \
|
||||
app_int->interface_name = int_name; \
|
||||
app_int->chat_application_function = funcptr; \
|
||||
app_int->short_desc = short_descript; \
|
||||
app_int->long_desc = long_descript; \
|
||||
app_int->syntax = syntax_string; \
|
||||
app_int->flags = app_flags; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define SWITCH_ADD_DIALPLAN(dp_int, int_name, funcptr) \
|
||||
for (;;) { \
|
||||
dp_int = (switch_dialplan_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_DIALPLAN_INTERFACE); \
|
||||
|
||||
@@ -483,8 +483,8 @@ struct switch_chat_interface {
|
||||
/*! the name of the interface */
|
||||
const char *interface_name;
|
||||
/*! function to open the directory interface */
|
||||
switch_status_t (*chat_send) (const char *proto, const char *from, const char *to,
|
||||
const char *subject, const char *body, const char *type, const char *hint);
|
||||
switch_status_t (*chat_send) (switch_event_t *message_event);
|
||||
|
||||
switch_thread_rwlock_t *rwlock;
|
||||
int refs;
|
||||
switch_mutex_t *reflock;
|
||||
@@ -681,6 +681,27 @@ struct switch_application_interface {
|
||||
struct switch_application_interface *next;
|
||||
};
|
||||
|
||||
/*! \brief A module interface to implement a chat application */
|
||||
struct switch_chat_application_interface {
|
||||
/*! the name of the interface */
|
||||
const char *interface_name;
|
||||
/*! function the application implements */
|
||||
switch_chat_application_function_t chat_application_function;
|
||||
/*! the long winded description of the application */
|
||||
const char *long_desc;
|
||||
/*! the short and sweet description of the application */
|
||||
const char *short_desc;
|
||||
/*! an example of the application syntax */
|
||||
const char *syntax;
|
||||
/*! flags to control behaviour */
|
||||
uint32_t flags;
|
||||
switch_thread_rwlock_t *rwlock;
|
||||
int refs;
|
||||
switch_mutex_t *reflock;
|
||||
switch_loadable_module_interface_t *parent;
|
||||
struct switch_chat_application_interface *next;
|
||||
};
|
||||
|
||||
/*! \brief A module interface to implement an api function */
|
||||
struct switch_api_interface {
|
||||
/*! the name of the interface */
|
||||
|
||||
@@ -74,6 +74,7 @@ SWITCH_DECLARE(void) switch_capture_regex(switch_regex_t *re, int match_count, c
|
||||
int *ovector, const char *var, switch_cap_callback_t callback, void *user_data);
|
||||
|
||||
SWITCH_DECLARE_NONSTD(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data);
|
||||
SWITCH_DECLARE_NONSTD(void) switch_regex_set_event_header_callback(const char *var, const char *val, void *user_data);
|
||||
|
||||
#define switch_regex_safe_free(re) if (re) {\
|
||||
switch_regex_free(re);\
|
||||
|
||||
@@ -131,12 +131,21 @@ SWITCH_BEGIN_EXTERN_C
|
||||
#define SWITCH_COPY_XML_CDR_VARIABLE "copy_xml_cdr"
|
||||
#define SWITCH_CURRENT_APPLICATION_VARIABLE "current_application"
|
||||
#define SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE "proto_specific_hangup_cause"
|
||||
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_ANSWER_VARIABLE "execute_on_answer"
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_PRE_ANSWER_VARIABLE "execute_on_pre_answer"
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_MEDIA_VARIABLE "execute_on_media"
|
||||
#define SWITCH_CHANNEL_API_ON_ANSWER_VARIABLE "api_on_answer"
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_RING_VARIABLE "execute_on_ring"
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_TONE_DETECT_VARIABLE "execute_on_tone_detect"
|
||||
#define SWITCH_CHANNEL_EXECUTE_ON_ORIGINATE_VARIABLE "execute_on_originate"
|
||||
|
||||
#define SWITCH_CHANNEL_API_ON_ANSWER_VARIABLE "api_on_answer"
|
||||
#define SWITCH_CHANNEL_API_ON_PRE_ANSWER_VARIABLE "api_on_pre_answer"
|
||||
#define SWITCH_CHANNEL_API_ON_MEDIA_VARIABLE "api_on_media"
|
||||
#define SWITCH_CHANNEL_API_ON_RING_VARIABLE "api_on_ring"
|
||||
#define SWITCH_CHANNEL_API_ON_TONE_DETECT_VARIABLE "api_on_tone_detect"
|
||||
#define SWITCH_CHANNEL_API_ON_ORIGINATE_VARIABLE "api_on_originate"
|
||||
|
||||
#define SWITCH_CALL_TIMEOUT_VARIABLE "call_timeout"
|
||||
#define SWITCH_HOLDING_UUID_VARIABLE "holding_uuid"
|
||||
#define SWITCH_SOFT_HOLDING_UUID_VARIABLE "soft_holding_uuid"
|
||||
@@ -173,6 +182,7 @@ SWITCH_BEGIN_EXTERN_C
|
||||
#define SWITCH_ORIGINATOR_VIDEO_CODEC_VARIABLE "originator_video_codec"
|
||||
#define SWITCH_LOCAL_MEDIA_IP_VARIABLE "local_media_ip"
|
||||
#define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "local_media_port"
|
||||
#define SWITCH_ADVERTISED_MEDIA_IP_VARIABLE "advertised_media_ip"
|
||||
#define SWITCH_REMOTE_MEDIA_IP_VARIABLE "remote_media_ip"
|
||||
#define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "remote_media_port"
|
||||
#define SWITCH_REMOTE_VIDEO_IP_VARIABLE "remote_video_ip"
|
||||
@@ -193,6 +203,33 @@ SWITCH_BEGIN_EXTERN_C
|
||||
#define SWITCH_DTMF_LOG_LEN 1000
|
||||
typedef uint8_t switch_byte_t;
|
||||
|
||||
/*!
|
||||
\enum switch_dtmf_source_t
|
||||
\brief DTMF sources
|
||||
<pre>
|
||||
SWITCH_DTMF_UNKNOWN - Unknown source
|
||||
SWITCH_DTMF_INBAND_AUDIO - From audio
|
||||
SWITCH_DTMF_RTP - From RTP as a telephone event
|
||||
SWITCH_DTMF_ENDPOINT - From endpoint signaling
|
||||
SWITCH_DTMF_APP - From application
|
||||
</pre>
|
||||
*/
|
||||
typedef enum {
|
||||
SWITCH_DTMF_UNKNOWN,
|
||||
SWITCH_DTMF_INBAND_AUDIO,
|
||||
SWITCH_DTMF_RTP,
|
||||
SWITCH_DTMF_ENDPOINT,
|
||||
SWITCH_DTMF_APP
|
||||
} switch_dtmf_source_t;
|
||||
|
||||
typedef enum {
|
||||
DIGIT_TARGET_SELF,
|
||||
DIGIT_TARGET_PEER,
|
||||
DIGIT_TARGET_BOTH
|
||||
} switch_digit_action_target_t;
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
DTMF_FLAG_SKIP_PROCESS = (1 << 0)
|
||||
} dtmf_flag_t;
|
||||
@@ -201,6 +238,7 @@ typedef struct {
|
||||
char digit;
|
||||
uint32_t duration;
|
||||
int32_t flags;
|
||||
switch_dtmf_source_t source;
|
||||
} switch_dtmf_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -267,7 +305,9 @@ typedef enum {
|
||||
SCF_USE_WIN32_MONOTONIC = (1 << 12),
|
||||
SCF_AUTO_SCHEMAS = (1 << 13),
|
||||
SCF_MINIMAL = (1 << 14),
|
||||
SCF_USE_NAT_MAPPING = (1 << 15)
|
||||
SCF_USE_NAT_MAPPING = (1 << 15),
|
||||
SCF_CLEAR_SQL = (1 << 16),
|
||||
SCF_THREADED_SYSTEM_EXEC = (1 << 17)
|
||||
} switch_core_flag_enum_t;
|
||||
typedef uint32_t switch_core_flag_t;
|
||||
|
||||
@@ -285,7 +325,8 @@ typedef enum {
|
||||
SWITCH_SAY_INTERFACE,
|
||||
SWITCH_ASR_INTERFACE,
|
||||
SWITCH_MANAGEMENT_INTERFACE,
|
||||
SWITCH_LIMIT_INTERFACE
|
||||
SWITCH_LIMIT_INTERFACE,
|
||||
SWITCH_CHAT_APPLICATION_INTERFACE
|
||||
} switch_module_interface_name_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -455,6 +496,7 @@ typedef enum {
|
||||
SWITCH_XML_SECTION_DIRECTORY = (1 << 1),
|
||||
SWITCH_XML_SECTION_DIALPLAN = (1 << 2),
|
||||
SWITCH_XML_SECTION_PHRASES = (1 << 3),
|
||||
SWITCH_XML_SECTION_CHATPLAN = (1 << 4),
|
||||
|
||||
/* Nothing after this line */
|
||||
SWITCH_XML_SECTION_MAX = (1 << 4)
|
||||
@@ -634,7 +676,7 @@ typedef enum {
|
||||
This flag will never send any. Sheesh....
|
||||
*/
|
||||
|
||||
RTP_BUG_IGNORE_DTMF_DURATION = (1 << 6)
|
||||
RTP_BUG_IGNORE_DTMF_DURATION = (1 << 6),
|
||||
|
||||
/*
|
||||
Guess Who? ... Yep, Sonus (and who know's who else) likes to interweave DTMF with the audio stream making it take
|
||||
@@ -642,6 +684,16 @@ typedef enum {
|
||||
This flag will treat every dtmf as if it were 50ms and queue it on recipt of the leading packet rather than at the end.
|
||||
*/
|
||||
|
||||
|
||||
RTP_BUG_ACCEPT_ANY_PACKETS = (1 << 7)
|
||||
|
||||
/*
|
||||
Oracle's Contact Center Anywhere (CCA) likes to use a single RTP socket to send all its outbound audio.
|
||||
This messes up our ability to auto adjust to NATTED RTP and causes us to ignore its audio packets.
|
||||
This flag will allow compatibility with this dying product.
|
||||
*/
|
||||
|
||||
|
||||
} switch_rtp_bug_flag_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -818,6 +870,7 @@ typedef struct {
|
||||
uint16_t remote_port;
|
||||
const char *local_ip;
|
||||
uint16_t local_port;
|
||||
const char *sdp_o_line;
|
||||
} switch_t38_options_t;
|
||||
|
||||
/*!
|
||||
@@ -1048,6 +1101,7 @@ typedef enum {
|
||||
CC_PROXY_MEDIA,
|
||||
CC_JITTERBUFFER,
|
||||
CC_FS_RTP,
|
||||
CC_QUEUEABLE_DTMF_DELAY,
|
||||
/* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
|
||||
CC_FLAG_MAX
|
||||
} switch_channel_cap_t;
|
||||
@@ -1116,7 +1170,9 @@ typedef enum {
|
||||
CF_LAZY_ATTENDED_TRANSFER,
|
||||
CF_SIGNAL_DATA,
|
||||
CF_SIMPLIFY,
|
||||
CF_ZOMBIE_EXEC,
|
||||
/* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
|
||||
/* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */
|
||||
CF_FLAG_MAX
|
||||
} switch_channel_flag_t;
|
||||
|
||||
@@ -1125,7 +1181,8 @@ typedef enum {
|
||||
CF_APP_TAGGED = (1 << 0),
|
||||
CF_APP_T38 = (1 << 1),
|
||||
CF_APP_T38_REQ = (1 << 2),
|
||||
CF_APP_T38_FAIL = (1 << 3)
|
||||
CF_APP_T38_FAIL = (1 << 3),
|
||||
CF_APP_T38_NEGOTIATED = (1 << 4)
|
||||
} switch_channel_app_flag_t;
|
||||
|
||||
|
||||
@@ -1152,7 +1209,8 @@ typedef enum {
|
||||
SFF_PROXY_PACKET = (1 << 5),
|
||||
SFF_DYNAMIC = (1 << 6),
|
||||
SFF_ZRTP = (1 << 7),
|
||||
SFF_UDPTL_PACKET = (1 << 8)
|
||||
SFF_UDPTL_PACKET = (1 << 8),
|
||||
SFF_NOT_AUDIO = (1 << 9)
|
||||
} switch_frame_flag_enum_t;
|
||||
typedef uint32_t switch_frame_flag_t;
|
||||
|
||||
@@ -1161,10 +1219,17 @@ typedef enum {
|
||||
SAF_NONE = 0,
|
||||
SAF_SUPPORT_NOMEDIA = (1 << 0),
|
||||
SAF_ROUTING_EXEC = (1 << 1),
|
||||
SAF_MEDIA_TAP = (1 << 2)
|
||||
SAF_MEDIA_TAP = (1 << 2),
|
||||
SAF_ZOMBIE_EXEC = (1 << 3)
|
||||
} switch_application_flag_enum_t;
|
||||
typedef uint32_t switch_application_flag_t;
|
||||
|
||||
typedef enum {
|
||||
SCAF_NONE = 0
|
||||
} switch_chat_application_flag_enum_t;
|
||||
typedef uint32_t switch_chat_application_flag_t;
|
||||
|
||||
|
||||
/*!
|
||||
\enum switch_signal_t
|
||||
\brief Signals to send to channels
|
||||
@@ -1625,7 +1690,8 @@ typedef enum {
|
||||
SCSC_VERBOSE_EVENTS,
|
||||
SCSC_SHUTDOWN_CHECK,
|
||||
SCSC_PAUSE_CHECK,
|
||||
SCSC_READY_CHECK
|
||||
SCSC_READY_CHECK,
|
||||
SCSC_THREADED_SYSTEM_EXEC
|
||||
} switch_session_ctl_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -1672,6 +1738,7 @@ typedef struct switch_timer_interface switch_timer_interface_t;
|
||||
typedef struct switch_dialplan_interface switch_dialplan_interface_t;
|
||||
typedef struct switch_codec_interface switch_codec_interface_t;
|
||||
typedef struct switch_application_interface switch_application_interface_t;
|
||||
typedef struct switch_chat_application_interface switch_chat_application_interface_t;
|
||||
typedef struct switch_api_interface switch_api_interface_t;
|
||||
typedef struct switch_file_interface switch_file_interface_t;
|
||||
typedef struct switch_speech_interface switch_speech_interface_t;
|
||||
@@ -1723,7 +1790,8 @@ typedef switch_status_t (*switch_core_codec_fmtp_parse_func_t) (const char *fmtp
|
||||
typedef switch_status_t (*switch_core_codec_destroy_func_t) (switch_codec_t *);
|
||||
|
||||
|
||||
|
||||
typedef switch_status_t (*switch_chat_application_function_t) (switch_event_t *, const char *);
|
||||
#define SWITCH_STANDARD_CHAT_APP(name) static switch_status_t name (switch_event_t *message, const char *data)
|
||||
|
||||
typedef void (*switch_application_function_t) (switch_core_session_t *, const char *);
|
||||
#define SWITCH_STANDARD_APP(name) static void name (switch_core_session_t *session, const char *data)
|
||||
|
||||
@@ -426,6 +426,8 @@ static inline char *switch_sanitize_number(char *number)
|
||||
char warp[] = "/:";
|
||||
int i;
|
||||
|
||||
switch_assert(number);
|
||||
|
||||
if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@'))) {
|
||||
return number;
|
||||
}
|
||||
@@ -523,6 +525,18 @@ static inline char *switch_clean_name_string(char *s)
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief Turn a string into a number (default if NULL)
|
||||
\param nptr the string
|
||||
\param dft the default
|
||||
\return the number version of the string or the default
|
||||
*/
|
||||
static inline int switch_safe_atoi(const char *nptr, int dft)
|
||||
{
|
||||
return nptr ? atoi(nptr) : dft;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief Free a pointer and set it to NULL unless it already is NULL
|
||||
\param it the pointer
|
||||
|
||||
+15
-12
@@ -1,10 +1,6 @@
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/applications/mod_fax/Makefile
|
||||
/applications/mod_fax/Makefile.in
|
||||
/applications/mod_spandsp/Makefile
|
||||
/applications/mod_spandsp/Makefile.in
|
||||
/applications/mod_spandsp/mod_spandsp.log
|
||||
/applications/mod_blacklist/Makefile
|
||||
/applications/mod_commands/Makefile
|
||||
/applications/mod_conference/Makefile
|
||||
/applications/mod_db/Makefile
|
||||
@@ -13,25 +9,35 @@
|
||||
/applications/mod_expr/Makefile
|
||||
/applications/mod_expr/Makefile.in
|
||||
/applications/mod_expr/mod_expr.log
|
||||
/applications/mod_fax/Makefile
|
||||
/applications/mod_fax/Makefile.in
|
||||
/applications/mod_fax/mod_fax.log
|
||||
/applications/mod_fifo/Makefile
|
||||
/applications/mod_fsv/Makefile
|
||||
/applications/mod_limit/Makefile
|
||||
/applications/mod_sms/Makefile
|
||||
/applications/mod_spandsp/Makefile
|
||||
/applications/mod_spandsp/Makefile.in
|
||||
/applications/mod_spandsp/mod_spandsp.log
|
||||
/applications/mod_stress/Makefile
|
||||
/applications/mod_stress/Makefile.in
|
||||
/applications/mod_stress/mod_stress.log
|
||||
/applications/mod_valet_parking/Makefile
|
||||
/applications/mod_voicemail/Makefile
|
||||
/asr_tts/mod_unimrcp/Makefile
|
||||
/asr_tts/mod_unimrcp/Makefile.in
|
||||
/asr_tts/mod_unimrcp/mod_unimrcp.log
|
||||
/dialplans/mod_dialplan_asterisk/Makefile
|
||||
/dialplans/mod_dialplan_xml/Makefile
|
||||
/endpoints/mod_portaudio/Makefile
|
||||
/endpoints/mod_portaudio/Makefile.in
|
||||
/endpoints/mod_portaudio/mod_portaudio.log
|
||||
/endpoints/mod_skinny/Makefile
|
||||
/endpoints/mod_skinny/Makefile.in
|
||||
/endpoints/mod_skinny/mod_skinny.log
|
||||
/endpoints/mod_skypopen/Makefile
|
||||
/endpoints/mod_skypopen/Makefile.in
|
||||
/endpoints/mod_skypopen/mod_skypopen.log
|
||||
/endpoints/mod_sofia/Makefile
|
||||
/endpoints/mod_sofia/Makefile.in
|
||||
/endpoints/mod_sofia/mod_sofia.log
|
||||
@@ -40,11 +46,14 @@
|
||||
/formats/mod_native_file/Makefile
|
||||
/formats/mod_portaudio_stream/Makefile
|
||||
/formats/mod_portaudio_stream/Makefile.in
|
||||
/formats/mod_portaudio_stream/mod_portaudio_stream.log
|
||||
/formats/mod_tone_stream/Makefile
|
||||
/languages/mod_java/freeswitch.jar
|
||||
/languages/mod_java/Makefile
|
||||
/languages/mod_lua/Makefile
|
||||
/languages/mod_lua/Makefile.in
|
||||
/languages/mod_lua/mod_lua.log
|
||||
/languages/mod_managed/freeswitch_wrap.cpp
|
||||
/languages/mod_python/Makefile
|
||||
/languages/mod_spidermonkey/Makefile
|
||||
/languages/mod_spidermonkey/Makefile.in
|
||||
@@ -54,10 +63,4 @@
|
||||
/loggers/mod_syslog/Makefile
|
||||
/say/mod_say_en/Makefile
|
||||
/say/mod_say_ru/Makefile
|
||||
/applications/mod_stress/mod_stress.log
|
||||
/asr_tts/mod_unimrcp/mod_unimrcp.log
|
||||
/endpoints/mod_portaudio/mod_portaudio.log
|
||||
/endpoints/mod_skypopen/mod_skypopen.log
|
||||
/formats/mod_portaudio_stream/mod_portaudio_stream.log
|
||||
/languages/mod_java/freeswitch.jar
|
||||
/languages/mod_managed/freeswitch_wrap.cpp
|
||||
/timers/mod_timerfd/Makefile
|
||||
|
||||
+1
-1
@@ -39,6 +39,6 @@ mod_com_g729-activate:
|
||||
cd $(switch_builddir)/src/mod/codecs/mod_com_g729 && $(MAKE) $(AM_MAKEFLAGS) activate
|
||||
|
||||
.DEFAULT:
|
||||
@if test -z "`echo $@ | grep all`"; then $(MAKE) $(AM_MAKEFLAGS) $@-all ; else echo Unknown target `echo $@ | sed -e 's|-all||'`; exit 1; fi
|
||||
@if test -z "`echo $@ | grep '\-all$$'`"; then $(MAKE) $(AM_MAKEFLAGS) $@-all ; else echo Unknown target `echo $@ | sed -e 's|-all||'`; exit 1; fi
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Mathieu Rene <mrene@avgs.ca>
|
||||
* Raymond Chandler <intralanman@freeswitch.org>
|
||||
*
|
||||
* mod_blacklist.c -- Blacklist module
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_blacklist_shutdown);
|
||||
SWITCH_MODULE_RUNTIME_FUNCTION(mod_blacklist_runtime);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_blacklist_load);
|
||||
|
||||
|
||||
SWITCH_MODULE_DEFINITION(mod_blacklist, mod_blacklist_load, mod_blacklist_shutdown, NULL);
|
||||
|
||||
typedef struct {
|
||||
switch_hash_t *list;
|
||||
switch_mutex_t *list_mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
} blacklist_t;
|
||||
|
||||
static struct {
|
||||
switch_hash_t *files;
|
||||
switch_hash_t *lists;
|
||||
switch_mutex_t *files_mutex;
|
||||
switch_mutex_t *lists_mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
} globals;
|
||||
|
||||
blacklist_t *blacklist_create(const char *name)
|
||||
{
|
||||
switch_memory_pool_t *pool = NULL;
|
||||
blacklist_t *bl = NULL;
|
||||
|
||||
switch_core_new_memory_pool(&pool);
|
||||
bl = switch_core_alloc(pool, sizeof(*bl));
|
||||
switch_assert(bl);
|
||||
bl->pool = pool;
|
||||
|
||||
switch_core_hash_init(&bl->list, pool);
|
||||
switch_mutex_init(&bl->list_mutex, SWITCH_MUTEX_NESTED, pool);
|
||||
|
||||
return bl;
|
||||
}
|
||||
|
||||
void blacklist_free(blacklist_t *bl)
|
||||
{
|
||||
switch_core_destroy_memory_pool(&bl->pool);
|
||||
}
|
||||
|
||||
|
||||
void trim(char *string)
|
||||
{
|
||||
char *p;
|
||||
if ((p = strchr(string, '\n'))) {
|
||||
*p = '\0';
|
||||
}
|
||||
if ((p = strchr(string, '\r'))) {
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static switch_status_t load_list(const char *name, const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if ((f = fopen(filename, "r"))) {
|
||||
char buf[1024] = {0};
|
||||
blacklist_t *bl = blacklist_create(name); /* Create a hashtable + mutex for that list */
|
||||
|
||||
while (fgets(buf, 1024, f)) {
|
||||
trim(buf);
|
||||
switch_core_hash_insert(bl->list, buf, (void *)SWITCH_TRUE);
|
||||
}
|
||||
|
||||
switch_core_hash_insert(globals.files, name, filename);
|
||||
switch_core_hash_insert(globals.lists, name, bl);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Loaded list [%s]\n", name);
|
||||
|
||||
fclose(f);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open [%s] to load list [%s]\n", filename, name);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status_t do_config(switch_bool_t reload)
|
||||
{
|
||||
/* Load up blacklists */
|
||||
switch_xml_t xml, cfg, lists, list;
|
||||
switch_hash_index_t *hi;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg("mod_blacklist.conf", &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't load configuration section\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
|
||||
/* Destroy any active lists */
|
||||
while ((hi = switch_hash_first(NULL, globals.lists))) {
|
||||
const void *key;
|
||||
void *val;
|
||||
switch_hash_this(hi, &key, NULL, &val);
|
||||
blacklist_free((blacklist_t*)val);
|
||||
switch_core_hash_delete(globals.lists, (const char*)key);
|
||||
}
|
||||
|
||||
if ((lists = switch_xml_child(cfg, "lists"))) {
|
||||
for (list = switch_xml_child(lists, "list"); list; list = list->next) {
|
||||
const char *name = switch_xml_attr_soft(list, "name");
|
||||
const char *filename = switch_xml_attr_soft(list, "filename");
|
||||
|
||||
if (zstr(name)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "list has no name\n");
|
||||
continue;
|
||||
}
|
||||
if (zstr(filename)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "list [%s] has no filename\n", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
load_list(name, filename);
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
|
||||
if (xml) {
|
||||
switch_xml_free(xml);
|
||||
xml = NULL;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define BLACKLIST_API_SYNTAX \
|
||||
"blacklist check <listname> <item>\n" \
|
||||
"blacklist add <listname> <item>\n" \
|
||||
"blacklist del <listname> <item>\n" \
|
||||
"blacklist reload\n" \
|
||||
"blacklist help\n"
|
||||
|
||||
SWITCH_STANDARD_API(blacklist_api_function)
|
||||
{
|
||||
char *data;
|
||||
int argc;
|
||||
char *argv[3];
|
||||
|
||||
data = strdup(cmd);
|
||||
trim(data);
|
||||
if (!(argc = switch_separate_string(data, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid usage\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!strcasecmp(argv[0], "check")) {
|
||||
blacklist_t *bl = NULL;
|
||||
switch_bool_t result;
|
||||
|
||||
if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
bl = switch_core_hash_find(globals.lists, argv[1]);
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
|
||||
if (!bl) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]);
|
||||
stream->write_function(stream, "false");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(bl->list_mutex);
|
||||
result = (switch_bool_t)(intptr_t)switch_core_hash_find(bl->list, argv[2]);
|
||||
stream->write_function(stream, "%s", result ? "true" : "false");
|
||||
switch_mutex_unlock(bl->list_mutex);
|
||||
} else if (!strcasecmp(argv[0], "add")) {
|
||||
blacklist_t *bl = NULL;
|
||||
if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
bl = switch_core_hash_find(globals.lists, argv[1]);
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
|
||||
if (!bl) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]);
|
||||
stream->write_function(stream, "-ERR Unknown blacklist\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(bl->list_mutex);
|
||||
switch_core_hash_insert(bl->list, argv[2], (void*)SWITCH_TRUE);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Added [%s] to list [%s]\n", argv[2], argv[1]);
|
||||
switch_mutex_unlock(bl->list_mutex);
|
||||
stream->write_function(stream, "+OK\n");
|
||||
} else if (!strcasecmp(argv[0], "del")) {
|
||||
blacklist_t *bl = NULL;
|
||||
if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
bl = switch_core_hash_find(globals.lists, argv[1]);
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
|
||||
if (!bl) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]);
|
||||
stream->write_function(stream, "-ERR Unknown blacklist\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(bl->list_mutex);
|
||||
switch_core_hash_insert(bl->list, argv[2], NULL);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Removed [%s] from list [%s]\n", argv[2], argv[1]);
|
||||
switch_mutex_unlock(bl->list_mutex);
|
||||
stream->write_function(stream, "+OK\n");
|
||||
} else if (!strcasecmp(argv[0], "dump")) {
|
||||
switch_hash_index_t *hi;
|
||||
void *val;
|
||||
const void *var;
|
||||
blacklist_t *bl = NULL;
|
||||
char *filename = NULL;
|
||||
switch_file_t *fd;
|
||||
|
||||
if (argc < 1 || zstr(argv[1])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Missing blacklist name");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
bl = switch_core_hash_find(globals.lists, argv[1]);
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
|
||||
switch_mutex_lock(globals.files_mutex);
|
||||
filename = switch_core_hash_find(globals.files, argv[1]);
|
||||
switch_mutex_unlock(globals.files_mutex);
|
||||
|
||||
if (!bl) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]);
|
||||
stream->write_function(stream, "-ERR Unknown blacklist\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Dumping %s to %s\n", argv[1], filename);
|
||||
|
||||
switch_mutex_lock(globals.lists_mutex);
|
||||
if (switch_file_open(&fd, filename, SWITCH_FOPEN_WRITE, SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, globals.pool) == SWITCH_STATUS_SUCCESS) {
|
||||
for (hi = switch_hash_first(NULL, bl->list); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, &var, NULL, &val);
|
||||
switch_file_printf(fd, "%s\n", (char *)var);
|
||||
}
|
||||
stream->write_function(stream, "+OK\n");
|
||||
}
|
||||
switch_mutex_unlock(globals.lists_mutex);
|
||||
} else if (!strcasecmp(argv[0], "reload")) {
|
||||
do_config(SWITCH_TRUE);
|
||||
stream->write_function(stream, "+OK\n");
|
||||
} else if (!strcasecmp(argv[0], "help")) {
|
||||
stream->write_function(stream, BLACKLIST_API_SYNTAX "+OK\n");
|
||||
} else if (!zstr(argv[0])) {
|
||||
stream->write_function(stream, "-ERR: No such command: %s (see 'blacklist help')\n", argv[0]);
|
||||
}
|
||||
|
||||
done:
|
||||
switch_safe_free(data);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_blacklist_load)
|
||||
{
|
||||
switch_api_interface_t *api_interface;
|
||||
//switch_application_interface_t *app_interface;
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
globals.pool = pool;
|
||||
|
||||
switch_core_hash_init(&globals.files, globals.pool);
|
||||
switch_mutex_init(&globals.files_mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||
|
||||
switch_core_hash_init(&globals.lists, globals.pool);
|
||||
switch_mutex_init(&globals.lists_mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||
|
||||
do_config(SWITCH_FALSE);
|
||||
|
||||
SWITCH_ADD_API(api_interface, "blacklist", "Control blacklists", blacklist_api_function, "");
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_blacklist_shutdown)
|
||||
{
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
||||
@@ -850,7 +850,7 @@ cc_status_t cc_agent_get(const char *key, const char *agent, char *ret_result, s
|
||||
switch_event_t *event;
|
||||
char res[256];
|
||||
|
||||
/* Check to see if agent already exist */
|
||||
/* Check to see if agent already exists */
|
||||
sql = switch_mprintf("SELECT count(*) FROM agents WHERE name = '%q'", agent);
|
||||
cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res));
|
||||
switch_safe_free(sql);
|
||||
@@ -860,8 +860,8 @@ cc_status_t cc_agent_get(const char *key, const char *agent, char *ret_result, s
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!strcasecmp(key, "status") ) {
|
||||
/* Check to see if agent already exist */
|
||||
if (!strcasecmp(key, "status") || !strcasecmp(key, "state") || !strcasecmp(key, "uuid") ) {
|
||||
/* Check to see if agent already exists */
|
||||
sql = switch_mprintf("SELECT %q FROM agents WHERE name = '%q'", key, agent);
|
||||
cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res));
|
||||
switch_safe_free(sql);
|
||||
@@ -869,9 +869,15 @@ cc_status_t cc_agent_get(const char *key, const char *agent, char *ret_result, s
|
||||
result = CC_STATUS_SUCCESS;
|
||||
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
char tmpname[256];
|
||||
if (!strcasecmp(key, "uuid")) {
|
||||
switch_snprintf(tmpname, sizeof(tmpname), "CC-Agent-UUID");
|
||||
} else {
|
||||
switch_snprintf(tmpname, sizeof(tmpname), "CC-Agent-%c%s", (char) switch_toupper(key[0]), key+1);
|
||||
}
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-status-get");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-Status", res);
|
||||
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-%s-get", key);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, tmpname, res);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
|
||||
@@ -1400,6 +1406,10 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
|
||||
|
||||
/* Proceed contact the agent to offer the member */
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_channel_t *member_channel = switch_core_session_get_channel(member_session);
|
||||
switch_caller_profile_t *member_profile = switch_channel_get_caller_profile(member_channel);
|
||||
const char *member_dnis = member_profile->rdnis;
|
||||
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-offering");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
|
||||
@@ -1409,6 +1419,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-Session-UUID", h->member_session_uuid);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-CID-Name", h->member_cid_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-CID-Number", h->member_cid_number);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-DNIS", member_dnis);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
|
||||
@@ -1548,6 +1559,9 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
|
||||
t_agent_answered = local_epoch_time_now(NULL);
|
||||
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_caller_profile_t *member_profile = switch_channel_get_caller_profile(member_channel);
|
||||
const char *member_dnis = member_profile->rdnis;
|
||||
|
||||
switch_channel_event_set_data(agent_channel, event);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "bridge-agent-start");
|
||||
@@ -1561,6 +1575,7 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-Session-UUID", h->member_session_uuid);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-CID-Name", h->member_cid_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-CID-Number", h->member_cid_number);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Member-DNIS", member_dnis);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
/* for xml_cdr needs */
|
||||
@@ -2676,7 +2691,9 @@ static int list_result_callback(void *pArg, int argc, char **argv, char **column
|
||||
"\tcallcenter_config agent set busy_delay_time [agent_name] [wait second] | \n"\
|
||||
"\tcallcenter_config agent set no_answer_delay_time [agent_name] [wait second] | \n"\
|
||||
"\tcallcenter_config agent get status [agent_name] | \n" \
|
||||
"\tcallcenter_config agent list | \n" \
|
||||
"\tcallcenter_config agent get state [agent_name] | \n" \
|
||||
"\tcallcenter_config agent get uuid [agent_name] | \n" \
|
||||
"\tcallcenter_config agent list [[agent_name]] | \n" \
|
||||
"\tcallcenter_config tier add [queue_name] [agent_name] [level] [position] | \n" \
|
||||
"\tcallcenter_config tier set state [queue_name] [agent_name] [state] | \n" \
|
||||
"\tcallcenter_config tier set level [queue_name] [agent_name] [level] | \n" \
|
||||
@@ -2832,7 +2849,14 @@ SWITCH_STANDARD_API(cc_config_api_function)
|
||||
struct list_result cbt;
|
||||
cbt.row_process = 0;
|
||||
cbt.stream = stream;
|
||||
sql = switch_mprintf("SELECT * FROM agents");
|
||||
if ( argc-initial_argc > 1 ) {
|
||||
stream->write_function(stream, "%s", "-ERR Invalid!\n");
|
||||
goto done;
|
||||
} else if ( argc-initial_argc == 1 ) {
|
||||
sql = switch_mprintf("SELECT * FROM agents WHERE name='%q'", argv[0 + initial_argc]);
|
||||
} else {
|
||||
sql = switch_mprintf("SELECT * FROM agents");
|
||||
}
|
||||
cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, list_result_callback, &cbt /* Call back variables */);
|
||||
switch_safe_free(sql);
|
||||
stream->write_function(stream, "%s", "+OK\n");
|
||||
|
||||
@@ -1840,6 +1840,15 @@ SWITCH_STANDARD_API(ctl_function)
|
||||
switch_core_session_ctl(SCSC_VERBOSE_EVENTS, &arg);
|
||||
|
||||
stream->write_function(stream, "+OK verbose_events is %s \n", arg ? "on" : "off");
|
||||
} else if (!strcasecmp(argv[0], "threaded_system_exec")) {
|
||||
arg = -1;
|
||||
if (argv[1]) {
|
||||
arg = switch_true(argv[1]);
|
||||
}
|
||||
|
||||
switch_core_session_ctl(SCSC_THREADED_SYSTEM_EXEC, &arg);
|
||||
|
||||
stream->write_function(stream, "+OK threaded_system_exec is %s \n", arg ? "true" : "false");
|
||||
|
||||
} else if (!strcasecmp(argv[0], "save_history")) {
|
||||
switch_core_session_ctl(SCSC_SAVE_HISTORY, NULL);
|
||||
@@ -4020,12 +4029,12 @@ SWITCH_STANDARD_API(show_function)
|
||||
}
|
||||
if (strchr(argv[2], '%')) {
|
||||
sprintf(sql,
|
||||
"select * from channels where hostname='%s' and uuid like '%s' or name like '%s' or cid_name like '%s' or cid_num like '%s' order by created_epoch",
|
||||
hostname, argv[2], argv[2], argv[2], argv[2]);
|
||||
"select * from channels where hostname='%s' and uuid like '%s' or name like '%s' or cid_name like '%s' or cid_num like '%s' or presence_data like '%s' order by created_epoch",
|
||||
hostname, argv[2], argv[2], argv[2], argv[2], argv[2]);
|
||||
} else {
|
||||
sprintf(sql,
|
||||
"select * from channels where hostname='%s' and uuid like '%%%s%%' or name like '%%%s%%' or cid_name like '%%%s%%' or cid_num like '%%%s%%' order by created_epoch",
|
||||
hostname, argv[2], argv[2], argv[2], argv[2]);
|
||||
"select * from channels where hostname='%s' and uuid like '%%%s%%' or name like '%%%s%%' or cid_name like '%%%s%%' or cid_num like '%%%s%%' or presence_data like '%%%s%%' order by created_epoch",
|
||||
hostname, argv[2], argv[2], argv[2], argv[2], argv[2]);
|
||||
|
||||
}
|
||||
|
||||
@@ -4262,6 +4271,21 @@ SWITCH_STANDARD_API(uuid_flush_dtmf_function)
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_API(uuid_zombie_exec_function)
|
||||
{
|
||||
switch_core_session_t *fsession;
|
||||
|
||||
if (!zstr(cmd) && (fsession = switch_core_session_locate(cmd))) {
|
||||
switch_channel_set_flag(switch_core_session_get_channel(fsession), CF_ZOMBIE_EXEC);
|
||||
switch_core_session_rwunlock(fsession);
|
||||
stream->write_function(stream, "+OK MMM Brains...\n");
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR no such session\n");
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define SETVAR_SYNTAX "<uuid> <var> [value]"
|
||||
SWITCH_STANDARD_API(uuid_setvar_function)
|
||||
{
|
||||
@@ -5322,6 +5346,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
|
||||
SWITCH_ADD_API(commands_api_interface, "uuid_simplify", "Try to cut out of a call path / attended xfer", uuid_simplify_function, SIMPLIFY_SYNTAX);
|
||||
SWITCH_ADD_API(commands_api_interface, "uuid_jitterbuffer", "Try to cut out of a call path / attended xfer",
|
||||
uuid_jitterbuffer_function, JITTERBUFFER_SYNTAX);
|
||||
SWITCH_ADD_API(commands_api_interface, "uuid_zombie_exec", "Set zombie_exec flag on the specified uuid", uuid_zombie_exec_function, "<uuid>");
|
||||
SWITCH_ADD_API(commands_api_interface, "xml_flush_cache", "clear xml cache", xml_flush_function, "<id> <key> <val>");
|
||||
SWITCH_ADD_API(commands_api_interface, "xml_locate", "find some xml", xml_locate_function, "[root | <section> <tag> <tag_attr_name> <tag_attr_val>]");
|
||||
SWITCH_ADD_API(commands_api_interface, "xml_wrap", "Wrap another api command in xml", xml_wrap_api_function, "<command> <args>");
|
||||
@@ -5389,7 +5414,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
|
||||
switch_console_set_complete("add show codec");
|
||||
switch_console_set_complete("add show complete");
|
||||
switch_console_set_complete("add show dialplan");
|
||||
switch_console_set_complete("add show distinct_channels");
|
||||
switch_console_set_complete("add show detailed_calls");
|
||||
switch_console_set_complete("add show bridged_calls");
|
||||
switch_console_set_complete("add show detailed_bridged_calls");
|
||||
switch_console_set_complete("add show endpoint");
|
||||
switch_console_set_complete("add show file");
|
||||
switch_console_set_complete("add show interfaces");
|
||||
|
||||
@@ -144,7 +144,9 @@ typedef enum {
|
||||
MFLAG_MOD = (1 << 16),
|
||||
MFLAG_INDICATE_MUTE = (1 << 17),
|
||||
MFLAG_INDICATE_UNMUTE = (1 << 18),
|
||||
MFLAG_NOMOH = (1 << 19)
|
||||
MFLAG_NOMOH = (1 << 19),
|
||||
MFLAG_VIDEO_BRIDGE = (1 << 20),
|
||||
MFLAG_INDICATE_MUTE_DETECT = (1 << 21)
|
||||
} member_flag_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -161,7 +163,8 @@ typedef enum {
|
||||
CFLAG_OUTCALL = (1 << 10),
|
||||
CFLAG_INHASH = (1 << 11),
|
||||
CFLAG_EXIT_SOUND = (1 << 12),
|
||||
CFLAG_ENTER_SOUND = (1 << 13)
|
||||
CFLAG_ENTER_SOUND = (1 << 13),
|
||||
CFLAG_VIDEO_BRIDGE = (1 << 14)
|
||||
} conf_flag_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -433,10 +436,11 @@ static switch_status_t conference_member_play_file(conference_member_t *member,
|
||||
static switch_status_t conference_member_say(conference_member_t *member, char *text, uint32_t leadin);
|
||||
static uint32_t conference_member_stop_file(conference_member_t *member, file_stop_t stop);
|
||||
static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_core_session_t *session, switch_memory_pool_t *pool);
|
||||
static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint);
|
||||
static switch_status_t chat_send(switch_event_t *message_event);
|
||||
|
||||
|
||||
static void launch_conference_record_thread(conference_obj_t *conference, char *path);
|
||||
static void launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b);
|
||||
|
||||
typedef switch_status_t (*conf_api_args_cmd_t) (conference_obj_t *, switch_stream_handle_t *, int, char **);
|
||||
typedef switch_status_t (*conf_api_member_cmd_t) (conference_member_t *, switch_stream_handle_t *, void *);
|
||||
@@ -951,6 +955,56 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
|
||||
return status;
|
||||
}
|
||||
|
||||
struct vid_helper {
|
||||
conference_member_t *member_a;
|
||||
conference_member_t *member_b;
|
||||
int up;
|
||||
};
|
||||
|
||||
/* Thread bridging video between two members, there will be two threads if video briding is used */
|
||||
static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread_t *thread, void *obj)
|
||||
{
|
||||
struct vid_helper *vh = obj;
|
||||
switch_channel_t *channel_a = switch_core_session_get_channel(vh->member_a->session);
|
||||
switch_channel_t *channel_b = switch_core_session_get_channel(vh->member_b->session);
|
||||
switch_status_t status;
|
||||
switch_frame_t *read_frame;
|
||||
|
||||
/* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */
|
||||
if (switch_core_session_read_lock(vh->member_a->session) != SWITCH_STATUS_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (switch_core_session_read_lock(vh->member_b->session) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_core_session_rwunlock(vh->member_a->session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vh->up = 1;
|
||||
while (switch_test_flag(vh->member_a, MFLAG_RUNNING) && switch_test_flag(vh->member_b, MFLAG_RUNNING) &&
|
||||
switch_channel_ready(channel_a) && switch_channel_ready(channel_b)) {
|
||||
status = switch_core_session_read_video_frame(vh->member_a->session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
|
||||
if (!SWITCH_READ_ACCEPTABLE(status)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!switch_test_flag(read_frame, SFF_CNG)) {
|
||||
if (switch_core_session_write_video_frame(vh->member_b->session, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_core_session_rwunlock(vh->member_a->session);
|
||||
switch_core_session_rwunlock(vh->member_b->session);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s video thread ended.\n", switch_channel_get_name(channel_a));
|
||||
|
||||
vh->up = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Main video monitor thread (1 per distinct conference room) */
|
||||
static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thread, void *obj)
|
||||
{
|
||||
@@ -1103,6 +1157,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
|
||||
int has_file_data = 0, members_with_video = 0;
|
||||
uint32_t conf_energy = 0;
|
||||
int nomoh = 0;
|
||||
conference_member_t *video_bridge_members[2] = { 0 };
|
||||
|
||||
/* Sync the conference to a single timing source */
|
||||
if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) {
|
||||
@@ -1128,6 +1183,14 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
|
||||
if (switch_test_flag(imember, MFLAG_NOMOH)) {
|
||||
nomoh++;
|
||||
}
|
||||
|
||||
if (switch_test_flag(imember, MFLAG_VIDEO_BRIDGE)) {
|
||||
if (!video_bridge_members[0]) {
|
||||
video_bridge_members[0] = imember;
|
||||
} else {
|
||||
video_bridge_members[1] = imember;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_clear_flag_locked(imember, MFLAG_HAS_AUDIO);
|
||||
@@ -1187,7 +1250,11 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
|
||||
|
||||
|
||||
if (members_with_video && conference->video_running != 1) {
|
||||
launch_conference_video_thread(conference);
|
||||
if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
|
||||
launch_conference_video_thread(conference);
|
||||
} else if (video_bridge_members[0] && video_bridge_members[1]){
|
||||
launch_conference_video_bridge_thread(video_bridge_members[0], video_bridge_members[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* If a file or speech event is being played */
|
||||
@@ -1498,13 +1565,15 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
|
||||
if (!switch_test_flag(imember, MFLAG_NOCHANNEL)) {
|
||||
channel = switch_core_session_get_channel(imember->session);
|
||||
|
||||
/* add this little bit to preserve the bridge cause code in case of an early media call that */
|
||||
/* never answers */
|
||||
if (switch_test_flag(conference, CFLAG_ANSWERED)) {
|
||||
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
|
||||
} else {
|
||||
/* put actual cause code from outbound channel hangup here */
|
||||
switch_channel_hangup(channel, conference->bridge_hangup_cause);
|
||||
if (!switch_false(switch_channel_get_variable(channel, "hangup_after_conference"))) {
|
||||
/* add this little bit to preserve the bridge cause code in case of an early media call that */
|
||||
/* never answers */
|
||||
if (switch_test_flag(conference, CFLAG_ANSWERED)) {
|
||||
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
|
||||
} else {
|
||||
/* put actual cause code from outbound channel hangup here */
|
||||
switch_channel_hangup(channel, conference->bridge_hangup_cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2291,7 +2360,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v
|
||||
if (switch_test_flag(member, MFLAG_MUTE_DETECT) && !switch_test_flag(member, MFLAG_CAN_SPEAK)) {
|
||||
|
||||
if (!zstr(member->conference->mute_detect_sound)) {
|
||||
conference_member_play_file(member, member->conference->mute_detect_sound, 0);
|
||||
switch_set_flag(member, MFLAG_INDICATE_MUTE_DETECT);
|
||||
}
|
||||
|
||||
if (test_eflag(member->conference, EFLAG_MUTE_DETECT) &&
|
||||
@@ -2636,19 +2705,16 @@ static void conference_loop_output(conference_member_t *member)
|
||||
if (event->event_id == SWITCH_EVENT_MESSAGE) {
|
||||
char *from = switch_event_get_header(event, "from");
|
||||
char *to = switch_event_get_header(event, "to");
|
||||
char *proto = switch_event_get_header(event, "proto");
|
||||
char *subject = switch_event_get_header(event, "subject");
|
||||
char *hint = switch_event_get_header(event, "hint");
|
||||
char *body = switch_event_get_body(event);
|
||||
char *p, *freeme = NULL;
|
||||
char *p;
|
||||
|
||||
if (to && from && body) {
|
||||
if ((p = strchr(to, '+')) && strncmp(to, CONF_CHAT_PROTO, strlen(CONF_CHAT_PROTO))) {
|
||||
freeme = switch_mprintf("%s+%s@%s", CONF_CHAT_PROTO, member->conference->name, member->conference->domain);
|
||||
to = freeme;
|
||||
switch_event_del_header(event, "to");
|
||||
switch_event_add_header(event, SWITCH_STACK_BOTTOM,
|
||||
"to", "%s+%s@%s", CONF_CHAT_PROTO, member->conference->name, member->conference->domain);
|
||||
}
|
||||
chat_send(proto, from, to, subject, body, NULL, hint);
|
||||
switch_safe_free(freeme);
|
||||
chat_send(event);
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&event);
|
||||
@@ -2782,6 +2848,18 @@ static void conference_loop_output(conference_member_t *member)
|
||||
}
|
||||
switch_clear_flag(member, MFLAG_INDICATE_MUTE);
|
||||
}
|
||||
|
||||
if (switch_test_flag(member, MFLAG_INDICATE_MUTE_DETECT)) {
|
||||
if (!zstr(member->conference->mute_detect_sound)) {
|
||||
conference_member_play_file(member, member->conference->mute_detect_sound, 0);
|
||||
} else {
|
||||
char msg[512];
|
||||
|
||||
switch_snprintf(msg, sizeof(msg), "Currently Muted");
|
||||
conference_member_say(member, msg, 0);
|
||||
}
|
||||
switch_clear_flag(member, MFLAG_INDICATE_MUTE_DETECT);
|
||||
}
|
||||
|
||||
if (switch_test_flag(member, MFLAG_INDICATE_UNMUTE)) {
|
||||
if (!zstr(member->conference->unmuted_sound)) {
|
||||
@@ -5448,6 +5526,8 @@ static void set_mflags(const char *flags, member_flag_t *f)
|
||||
*f |= MFLAG_ENDCONF;
|
||||
} else if (!strcasecmp(argv[i], "mintwo")) {
|
||||
*f |= MFLAG_MINTWO;
|
||||
} else if (!strcasecmp(argv[i], "video-bridge")) {
|
||||
*f |= MFLAG_VIDEO_BRIDGE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5480,6 +5560,8 @@ static void set_cflags(const char *flags, uint32_t *f)
|
||||
*f |= CFLAG_VID_FLOOR;
|
||||
} else if (!strcasecmp(argv[i], "waste-bandwidth")) {
|
||||
*f |= CFLAG_WASTE_BANDWIDTH;
|
||||
} else if (!strcasecmp(argv[i], "video-bridge")) {
|
||||
*f |= CFLAG_VIDEO_BRIDGE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6227,20 +6309,44 @@ static void launch_conference_thread(conference_obj_t *conference)
|
||||
switch_thread_create(&thread, thd_attr, conference_thread_run, conference, conference->pool);
|
||||
}
|
||||
|
||||
|
||||
/* Create a video thread for the conference and launch it */
|
||||
static void launch_conference_video_thread(conference_obj_t *conference)
|
||||
static switch_thread_t *launch_thread_detached(switch_thread_start_t func, switch_memory_pool_t *pool, void *data)
|
||||
{
|
||||
switch_thread_t *thread;
|
||||
switch_threadattr_t *thd_attr = NULL;
|
||||
|
||||
switch_threadattr_create(&thd_attr, conference->pool);
|
||||
switch_threadattr_create(&thd_attr, pool);
|
||||
switch_threadattr_detach_set(thd_attr, 1);
|
||||
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||||
switch_thread_create(&thread, thd_attr, conference_video_thread_run, conference, conference->pool);
|
||||
switch_thread_create(&thread, thd_attr, func, data, pool);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
/* Create a video thread for the conference and launch it */
|
||||
static void launch_conference_video_thread(conference_obj_t *conference)
|
||||
{
|
||||
launch_thread_detached(conference_video_thread_run, conference->pool, conference);
|
||||
conference->video_running = 1;
|
||||
}
|
||||
|
||||
/* Create a video thread for the conference and launch it */
|
||||
static void launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b)
|
||||
{
|
||||
switch_memory_pool_t *pool = member_a->conference->pool;
|
||||
struct vid_helper *vh = switch_core_alloc(pool, 2 * sizeof *vh);
|
||||
|
||||
vh[0].member_a = member_a;
|
||||
vh[0].member_b = member_b;
|
||||
|
||||
vh[1].member_a = member_b;
|
||||
vh[1].member_b = member_a;
|
||||
|
||||
launch_thread_detached(conference_video_bridge_thread_run, pool, &vh[0]);
|
||||
launch_thread_detached(conference_video_bridge_thread_run, pool, &vh[1]);
|
||||
|
||||
member_a->conference->video_running = 1;
|
||||
}
|
||||
|
||||
static void launch_conference_record_thread(conference_obj_t *conference, char *path)
|
||||
{
|
||||
switch_thread_t *thread;
|
||||
@@ -6270,12 +6376,26 @@ static void launch_conference_record_thread(conference_obj_t *conference, char *
|
||||
switch_thread_create(&thread, thd_attr, conference_record_thread_run, rec, rec->pool);
|
||||
}
|
||||
|
||||
static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint)
|
||||
static switch_status_t chat_send(switch_event_t *message_event)
|
||||
{
|
||||
char name[512] = "", *p, *lbuf = NULL;
|
||||
conference_obj_t *conference = NULL;
|
||||
switch_stream_handle_t stream = { 0 };
|
||||
const char *proto;
|
||||
const char *from;
|
||||
const char *to;
|
||||
//const char *subject;
|
||||
const char *body;
|
||||
//const char *type;
|
||||
const char *hint;
|
||||
|
||||
proto = switch_event_get_header(message_event, "proto");
|
||||
from = switch_event_get_header(message_event, "from");
|
||||
to = switch_event_get_header(message_event, "to");
|
||||
//subject = switch_event_get_header(message_event, "subject");
|
||||
body = switch_event_get_body(message_event);
|
||||
//type = switch_event_get_header(message_event, "type");
|
||||
hint = switch_event_get_header(message_event, "hint");
|
||||
|
||||
if ((p = strchr(to, '+'))) {
|
||||
to = ++p;
|
||||
@@ -6292,7 +6412,7 @@ static switch_status_t chat_send(const char *proto, const char *from, const char
|
||||
}
|
||||
|
||||
if (!(conference = conference_find(name))) {
|
||||
switch_core_chat_send(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", "Conference not active.", NULL, NULL);
|
||||
switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", "Conference not active.", NULL, NULL);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
@@ -6310,7 +6430,7 @@ static switch_status_t chat_send(const char *proto, const char *from, const char
|
||||
|
||||
switch_safe_free(lbuf);
|
||||
|
||||
switch_core_chat_send(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL);
|
||||
switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL);
|
||||
switch_safe_free(stream.data);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
@@ -622,8 +622,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_db_load)
|
||||
/* register limit interfaces */
|
||||
SWITCH_ADD_LIMIT(limit_interface, "db", limit_incr_db, limit_release_db, limit_usage_db, limit_reset_db, limit_status_db, NULL);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
|
||||
switch_console_set_complete("add db insert");
|
||||
switch_console_set_complete("add db delete");
|
||||
|
||||
@@ -102,37 +102,50 @@ struct action_binding {
|
||||
char *input;
|
||||
char *string;
|
||||
char *value;
|
||||
switch_digit_action_target_t target;
|
||||
switch_core_session_t *session;
|
||||
};
|
||||
|
||||
static switch_status_t digit_nomatch_action_callback(switch_ivr_dmachine_match_t *match)
|
||||
{
|
||||
switch_core_session_t *session = (switch_core_session_t *) match->user_data;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
char str[DMACHINE_MAX_DIGIT_LEN + 2];
|
||||
switch_channel_t *channel;
|
||||
switch_event_t *event;
|
||||
switch_status_t status;
|
||||
switch_core_session_t *use_session = session;
|
||||
|
||||
if (switch_ivr_dmachine_get_target(match->dmachine) == DIGIT_TARGET_PEER) {
|
||||
if (switch_core_session_get_partner(session, &use_session) != SWITCH_STATUS_SUCCESS) {
|
||||
use_session = session;
|
||||
}
|
||||
}
|
||||
|
||||
channel = switch_core_session_get_channel(use_session);
|
||||
|
||||
|
||||
switch_channel_set_variable(channel, "last_non_matching_digits", match->match_digits);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Digit NOT match binding [%s]\n",
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_DEBUG, "%s Digit NOT match binding [%s]\n",
|
||||
switch_channel_get_name(channel), match->match_digits);
|
||||
|
||||
if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "digits", match->match_digits);
|
||||
|
||||
if ((status = switch_core_session_queue_event(session, &event)) != SWITCH_STATUS_SUCCESS) {
|
||||
if ((status = switch_core_session_queue_event(use_session, &event)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_destroy(&event);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s event queue faiure.\n",
|
||||
switch_core_session_get_name(session));
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_WARNING, "%s event queue failure.\n",
|
||||
switch_core_session_get_name(use_session));
|
||||
}
|
||||
}
|
||||
|
||||
/* send it back around flagged to skip the dmachine */
|
||||
switch_snprintf(str, sizeof(str), "!%s", match->match_digits);
|
||||
|
||||
switch_channel_queue_dtmf_string(channel, str);
|
||||
/* send it back around and skip the dmachine */
|
||||
switch_channel_queue_dtmf_string(channel, match->match_digits);
|
||||
|
||||
if (use_session != session) {
|
||||
switch_core_session_rwunlock(use_session);
|
||||
}
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -143,9 +156,25 @@ static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match)
|
||||
switch_status_t status;
|
||||
int exec = 0;
|
||||
char *string = act->string;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(act->session);
|
||||
switch_channel_t *channel;
|
||||
switch_core_session_t *use_session = act->session;
|
||||
int x = 0;
|
||||
if (switch_ivr_dmachine_get_target(match->dmachine) == DIGIT_TARGET_PEER || act->target == DIGIT_TARGET_PEER || act->target == DIGIT_TARGET_BOTH) {
|
||||
if (switch_core_session_get_partner(act->session, &use_session) != SWITCH_STATUS_SUCCESS) {
|
||||
use_session = act->session;
|
||||
}
|
||||
}
|
||||
|
||||
top:
|
||||
x++;
|
||||
|
||||
string = act->string;
|
||||
exec = 0;
|
||||
|
||||
channel = switch_core_session_get_channel(use_session);
|
||||
|
||||
switch_channel_set_variable(channel, "last_matching_digits", match->match_digits);
|
||||
|
||||
|
||||
if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(act->session), SWITCH_LOG_DEBUG, "%s Digit match binding [%s][%s]\n",
|
||||
@@ -163,31 +192,62 @@ static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute", exec == 2 ? "non-blocking" : "blocking");
|
||||
}
|
||||
|
||||
if ((status = switch_core_session_queue_event(act->session, &event)) != SWITCH_STATUS_SUCCESS) {
|
||||
if ((status = switch_core_session_queue_event(use_session, &event)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_destroy(&event);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(act->session), SWITCH_LOG_WARNING, "%s event queue faiure.\n",
|
||||
switch_core_session_get_name(act->session));
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_WARNING, "%s event queue faiure.\n",
|
||||
switch_core_session_get_name(use_session));
|
||||
}
|
||||
}
|
||||
|
||||
if (exec) {
|
||||
char *cmd = switch_core_session_sprintf(act->session, "%s::%s", string, act->value);
|
||||
switch_ivr_broadcast_in_thread(act->session, cmd, SMF_ECHO_ALEG|SMF_HOLD_BLEG);
|
||||
char *cmd = switch_core_session_sprintf(use_session, "%s::%s", string, act->value);
|
||||
switch_ivr_broadcast_in_thread(use_session, cmd, SMF_ECHO_ALEG | (act->target == DIGIT_TARGET_BOTH ? 0 : SMF_HOLD_BLEG));
|
||||
}
|
||||
|
||||
|
||||
if (use_session != act->session) {
|
||||
switch_core_session_rwunlock(use_session);
|
||||
|
||||
if (act->target == DIGIT_TARGET_BOTH) {
|
||||
use_session = act->session;
|
||||
goto top;
|
||||
}
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define CLEAR_DIGIT_ACTION_USAGE "<realm>|all"
|
||||
static switch_digit_action_target_t str2target(const char *target_str)
|
||||
{
|
||||
if (!strcasecmp(target_str, "peer")) {
|
||||
return DIGIT_TARGET_PEER;
|
||||
}
|
||||
|
||||
if (!strcasecmp(target_str, "both")) {
|
||||
return DIGIT_TARGET_BOTH;
|
||||
}
|
||||
|
||||
return DIGIT_TARGET_SELF;
|
||||
}
|
||||
|
||||
#define CLEAR_DIGIT_ACTION_USAGE "<realm>|all[,target]"
|
||||
SWITCH_STANDARD_APP(clear_digit_action_function)
|
||||
{
|
||||
//switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_ivr_dmachine_t *dmachine;
|
||||
char *realm = (char *) data;
|
||||
char *realm = switch_core_session_strdup(session, data);
|
||||
char *target_str;
|
||||
switch_digit_action_target_t target = DIGIT_TARGET_SELF;
|
||||
|
||||
if ((dmachine = switch_core_session_get_dmachine(session))) {
|
||||
if ((target_str = strchr(realm, ','))) {
|
||||
*target_str++ = '\0';
|
||||
target = str2target(target_str);
|
||||
}
|
||||
|
||||
|
||||
if ((dmachine = switch_core_session_get_dmachine(session, target))) {
|
||||
if (zstr(realm) || !strcasecmp(realm, "all")) {
|
||||
switch_core_session_set_dmachine(session, NULL);
|
||||
switch_core_session_set_dmachine(session, NULL, target);
|
||||
switch_ivr_dmachine_destroy(&dmachine);
|
||||
} else {
|
||||
switch_ivr_dmachine_clear_realm(dmachine, realm);
|
||||
@@ -195,49 +255,41 @@ SWITCH_STANDARD_APP(clear_digit_action_function)
|
||||
}
|
||||
}
|
||||
|
||||
#define DIGIT_ACTION_SET_REALM_USAGE "<realm>"
|
||||
#define DIGIT_ACTION_SET_REALM_USAGE "<realm>[,<target>]"
|
||||
SWITCH_STANDARD_APP(digit_action_set_realm_function)
|
||||
{
|
||||
switch_ivr_dmachine_t *dmachine;
|
||||
char *realm = (char *) data;
|
||||
char *realm = switch_core_session_strdup(session, data);
|
||||
char *target_str;
|
||||
switch_digit_action_target_t target = DIGIT_TARGET_SELF;
|
||||
|
||||
if ((target_str = strchr(realm, ','))) {
|
||||
*target_str++ = '\0';
|
||||
target = str2target(target_str);
|
||||
}
|
||||
|
||||
if (zstr(data)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", DIGIT_ACTION_SET_REALM_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ((dmachine = switch_core_session_get_dmachine(session))) {
|
||||
if ((dmachine = switch_core_session_get_dmachine(session, target))) {
|
||||
switch_ivr_dmachine_set_realm(dmachine, realm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define BIND_DIGIT_ACTION_USAGE "<realm>,<digits|~regex>,<string>,<value>"
|
||||
SWITCH_STANDARD_APP(bind_digit_action_function)
|
||||
|
||||
static void bind_to_session(switch_core_session_t *session,
|
||||
const char *arg0, const char *arg1, const char *arg2, const char *arg3,
|
||||
switch_digit_action_target_t target, switch_digit_action_target_t bind_target)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_ivr_dmachine_t *dmachine;
|
||||
char *mydata;
|
||||
int argc = 0;
|
||||
char *argv[4] = { 0 };
|
||||
struct action_binding *act;
|
||||
switch_ivr_dmachine_t *dmachine;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (zstr(data)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
mydata = switch_core_session_strdup(session, data);
|
||||
|
||||
argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0])));
|
||||
|
||||
if (argc < 4 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2]) || zstr(argv[3])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!(dmachine = switch_core_session_get_dmachine(session))) {
|
||||
if (!(dmachine = switch_core_session_get_dmachine(session, target))) {
|
||||
uint32_t digit_timeout = 1500;
|
||||
uint32_t input_timeout = 0;
|
||||
const char *var;
|
||||
@@ -256,20 +308,69 @@ SWITCH_STANDARD_APP(bind_digit_action_function)
|
||||
}
|
||||
|
||||
switch_ivr_dmachine_create(&dmachine, "DPTOOLS", NULL, digit_timeout, input_timeout, NULL, digit_nomatch_action_callback, session);
|
||||
switch_core_session_set_dmachine(session, dmachine);
|
||||
switch_core_session_set_dmachine(session, dmachine, target);
|
||||
}
|
||||
|
||||
|
||||
act = switch_core_session_alloc(session, sizeof(*act));
|
||||
act->realm = argv[0];
|
||||
act->input = argv[1];
|
||||
act->string = argv[2];
|
||||
act->value = argv[3];
|
||||
act->realm = switch_core_session_strdup(session, arg0);
|
||||
act->input = switch_core_session_strdup(session, arg1);
|
||||
act->string = switch_core_session_strdup(session, arg2);
|
||||
act->value = switch_core_session_strdup(session, arg3);
|
||||
act->target = bind_target;
|
||||
act->session = session;
|
||||
|
||||
switch_ivr_dmachine_bind(dmachine, act->realm, act->input, 0, digit_action_callback, act);
|
||||
}
|
||||
|
||||
#define BIND_DIGIT_ACTION_USAGE "<realm>,<digits|~regex>,<string>,<value>[,<dtmf target leg>][,<event target leg>]"
|
||||
SWITCH_STANDARD_APP(bind_digit_action_function)
|
||||
{
|
||||
|
||||
char *mydata;
|
||||
int argc = 0;
|
||||
char *argv[6] = { 0 };
|
||||
switch_digit_action_target_t target, bind_target;
|
||||
char *target_str = "self", *bind_target_str = "self";
|
||||
|
||||
if (zstr(data)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
mydata = switch_core_session_strdup(session, data);
|
||||
|
||||
argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0])));
|
||||
|
||||
if (argc < 4 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2]) || zstr(argv[3])) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[4]) {
|
||||
target_str = argv[4];
|
||||
}
|
||||
|
||||
if (argv[5]) {
|
||||
bind_target_str = argv[5];
|
||||
}
|
||||
|
||||
target = str2target(target_str);
|
||||
bind_target = str2target(bind_target_str);
|
||||
|
||||
|
||||
switch(target) {
|
||||
case DIGIT_TARGET_PEER:
|
||||
bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_PEER, bind_target);
|
||||
break;
|
||||
case DIGIT_TARGET_BOTH:
|
||||
bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_PEER, bind_target);
|
||||
bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_SELF, bind_target);
|
||||
break;
|
||||
default:
|
||||
bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_SELF, bind_target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define DETECT_SPEECH_SYNTAX "<mod_name> <gram_name> <gram_path> [<addr>] OR grammar <gram_name> [<path>] OR nogrammar <gram_name> OR grammaron/grammaroff <gram_name> OR grammarsalloff OR pause OR resume OR start_input_timers OR stop OR param <name> <value>"
|
||||
SWITCH_STANDARD_APP(detect_speech_function)
|
||||
@@ -904,6 +1005,18 @@ SWITCH_STANDARD_APP(eval_function)
|
||||
return;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_APP(zombie_function)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (switch_channel_up(channel)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s MMM Brains....\n", switch_channel_get_name(channel));
|
||||
switch_channel_set_flag(channel, CF_ZOMBIE_EXEC);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_STANDARD_APP(hangup_function)
|
||||
{
|
||||
@@ -1271,6 +1384,7 @@ SWITCH_STANDARD_APP(event_function)
|
||||
switch_assert(new);
|
||||
memcpy(new, val, len);
|
||||
event->subclass_name = new;
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, var, val);
|
||||
} else {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, var, val);
|
||||
}
|
||||
@@ -1447,7 +1561,7 @@ SWITCH_STANDARD_API(chat_api_function)
|
||||
if (!zstr(cmd) && (lbuf = strdup(cmd))
|
||||
&& (argc = switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0])))) >= 4) {
|
||||
|
||||
if (switch_core_chat_send(argv[0], "dp", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) {
|
||||
if (switch_core_chat_send_args(argv[0], "dp", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) {
|
||||
stream->write_function(stream, "Sent");
|
||||
} else {
|
||||
stream->write_function(stream, "Error! Message Not Sent");
|
||||
@@ -2022,7 +2136,7 @@ SWITCH_STANDARD_APP(read_function)
|
||||
SWITCH_STANDARD_APP(play_and_get_digits_function)
|
||||
{
|
||||
char *mydata;
|
||||
char *argv[10] = { 0 };
|
||||
char *argv[11] = { 0 };
|
||||
int argc;
|
||||
int32_t min_digits = 0;
|
||||
int32_t max_digits = 0;
|
||||
@@ -2035,6 +2149,7 @@ SWITCH_STANDARD_APP(play_and_get_digits_function)
|
||||
const char *var_name = NULL;
|
||||
const char *valid_terminators = NULL;
|
||||
const char *digits_regex = NULL;
|
||||
const char *transfer_on_failure = NULL;
|
||||
|
||||
if (!zstr(data) && (mydata = switch_core_session_strdup(session, data))) {
|
||||
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
||||
@@ -2084,6 +2199,10 @@ SWITCH_STANDARD_APP(play_and_get_digits_function)
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 10) {
|
||||
transfer_on_failure = argv[10];
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Transfer on failure = [%s].\n", transfer_on_failure);
|
||||
}
|
||||
|
||||
if (min_digits <= 1) {
|
||||
min_digits = 1;
|
||||
@@ -2102,7 +2221,8 @@ SWITCH_STANDARD_APP(play_and_get_digits_function)
|
||||
}
|
||||
|
||||
switch_play_and_get_digits(session, min_digits, max_digits, max_tries, timeout, valid_terminators,
|
||||
prompt_audio_file, bad_input_audio_file, var_name, digit_buffer, sizeof(digit_buffer), digits_regex, digit_timeout);
|
||||
prompt_audio_file, bad_input_audio_file, var_name, digit_buffer, sizeof(digit_buffer),
|
||||
digits_regex, digit_timeout, transfer_on_failure);
|
||||
}
|
||||
|
||||
#define SAY_SYNTAX "<module_name>[:<lang>] <say_type> <say_method> [<say_gender>] <text>"
|
||||
@@ -3084,8 +3204,10 @@ static switch_call_cause_t user_outgoing_channel(switch_core_session_t *session,
|
||||
}
|
||||
}
|
||||
|
||||
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "dialed_user", user);
|
||||
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "dialed_domain", domain);
|
||||
if (var_event) {
|
||||
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "dialed_user", user);
|
||||
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "dialed_domain", domain);
|
||||
}
|
||||
|
||||
if (!dest) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No dial-string available, please check your user directory.\n");
|
||||
@@ -3262,44 +3384,51 @@ SWITCH_STANDARD_APP(wait_for_silence_function)
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: %s\n", WAIT_FOR_SILENCE_SYNTAX);
|
||||
}
|
||||
|
||||
static switch_status_t event_chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint)
|
||||
static switch_status_t event_chat_send(switch_event_t *message_event)
|
||||
|
||||
{
|
||||
switch_event_t *event;
|
||||
const char *to;
|
||||
|
||||
if (switch_event_create(&event, SWITCH_EVENT_RECV_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
if (proto)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Proto", proto);
|
||||
if (from)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "From", from);
|
||||
if (subject)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Subject", subject);
|
||||
if (hint)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Hint", hint);
|
||||
if (body)
|
||||
switch_event_add_body(event, "%s", body);
|
||||
if (to) {
|
||||
char *v;
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "To", to);
|
||||
if ((v = switch_core_get_variable_dup(to))) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command", v);
|
||||
free(v);
|
||||
}
|
||||
switch_event_dup(&event, message_event);
|
||||
event->event_id = SWITCH_EVENT_RECV_MESSAGE;
|
||||
|
||||
if ((to = switch_event_get_header(event, "to"))) {
|
||||
char *v;
|
||||
if ((v = switch_core_get_variable_dup(to))) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command", v);
|
||||
free(v);
|
||||
}
|
||||
|
||||
if (switch_event_fire(&event) == SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
switch_event_destroy(&event);
|
||||
}
|
||||
|
||||
if (switch_event_fire(&event) == SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
switch_event_destroy(&event);
|
||||
|
||||
return SWITCH_STATUS_MEMERR;
|
||||
}
|
||||
|
||||
static switch_status_t api_chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint)
|
||||
static switch_status_t api_chat_send(switch_event_t *message_event)
|
||||
{
|
||||
const char *proto;
|
||||
const char *from;
|
||||
const char *to;
|
||||
//const char *subject;
|
||||
//const char *body;
|
||||
const char *type;
|
||||
const char *hint;
|
||||
|
||||
proto = switch_event_get_header(message_event, "proto");
|
||||
from = switch_event_get_header(message_event, "from");
|
||||
to = switch_event_get_header(message_event, "to");
|
||||
//subject = switch_event_get_header(message_event, "subject");
|
||||
//body = switch_event_get_body(message_event);
|
||||
type = switch_event_get_header(message_event, "type");
|
||||
hint = switch_event_get_header(message_event, "hint");
|
||||
|
||||
|
||||
if (to) {
|
||||
char *v = NULL;
|
||||
switch_stream_handle_t stream = { 0 };
|
||||
@@ -3322,7 +3451,7 @@ static switch_status_t api_chat_send(const char *proto, const char *from, const
|
||||
switch_api_execute(cmd, arg, NULL, &stream);
|
||||
|
||||
if (proto) {
|
||||
switch_core_chat_send(proto, "api", to, hint && strchr(hint, '/') ? hint : from, !zstr(type) ? type : NULL, (char *) stream.data, NULL, NULL);
|
||||
switch_core_chat_send_args(proto, "api", to, hint && strchr(hint, '/') ? hint : from, !zstr(type) ? type : NULL, (char *) stream.data, NULL, NULL);
|
||||
}
|
||||
|
||||
switch_safe_free(stream.data);
|
||||
@@ -3667,7 +3796,9 @@ static switch_status_t file_string_file_close(switch_file_handle_t *handle)
|
||||
{
|
||||
file_string_context_t *context = handle->private_info;
|
||||
|
||||
switch_core_file_close(&context->fh);
|
||||
if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
|
||||
switch_core_file_close(&context->fh);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -3778,7 +3909,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "digit_action_set_realm", "change binding realm", "",
|
||||
digit_action_set_realm_function, DIGIT_ACTION_SET_REALM_USAGE, SAF_SUPPORT_NOMEDIA);
|
||||
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "privacy", "Set privacy on calls", "Set caller privacy on calls.", privacy_function, "off|on|name|full|number",
|
||||
SAF_SUPPORT_NOMEDIA);
|
||||
@@ -3802,7 +3932,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
|
||||
SWITCH_ADD_APP(app_interface, "delay_echo", "echo audio at a specified delay", "Delay n ms", delay_function, "<delay ms>", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "strftime", "strftime", "strftime", strftime_function, "[<epoch>|]<format string>", SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "phrase", "Say a Phrase", "Say a Phrase", phrase_function, "<macro_name>,<data>", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "eval", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "eval", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "stop", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "set_zombie_exec", "Enable Zombie Execution", "Enable Zombie Execution",
|
||||
zombie_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "pre_answer", "Pre-Answer the call", "Pre-Answer the call for a channel.", pre_answer_function, "", SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "answer", "Answer the call", "Answer the call for a channel.", answer_function, "", SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "hangup", "Hangup the call", "Hangup the call for a channel.", hangup_function, "[<cause>]", SAF_SUPPORT_NOMEDIA);
|
||||
@@ -3810,29 +3943,29 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
|
||||
SWITCH_ADD_APP(app_interface, "presence", "Send Presence", "Send Presence.", presence_function, "<rpid> <status> [<id>]",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "log", "Logs to the logger", LOG_LONG_DESC, log_function, "<log_level> <log_string>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "event", "Fire an event", "Fire an event", event_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "event", "Fire an event", "Fire an event", event_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "sound_test", "Analyze Audio", "Analyze Audio", sound_test_function, "", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "export", "Export a channel variable across a bridge", EXPORT_LONG_DESC, export_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "bridge_export", "Export a channel variable across a bridge", EXPORT_LONG_DESC, bridge_export_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "set", "Set a channel variable", SET_LONG_DESC, set_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "push", "Set a channel variable", SET_LONG_DESC, push_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "unshift", "Set a channel variable", SET_LONG_DESC, unshift_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "set_global", "Set a global variable", SET_GLOBAL_LONG_DESC, set_global_function, "<varname>=<value>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "set_profile_var", "Set a caller profile variable", SET_PROFILE_VAR_LONG_DESC, set_profile_var_function,
|
||||
"<varname>=<value>", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
"<varname>=<value>", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "unset", "Unset a channel variable", UNSET_LONG_DESC, unset_function, "<varname>",
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
|
||||
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
|
||||
SWITCH_ADD_APP(app_interface, "ring_ready", "Indicate Ring_Ready", "Indicate Ring_Ready on a channel.", ring_ready_function, "", SAF_SUPPORT_NOMEDIA);
|
||||
SWITCH_ADD_APP(app_interface, "remove_bugs", "Remove media bugs", "Remove all media bugs from a channel.", remove_bugs_function, "", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA);
|
||||
@@ -3900,7 +4033,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
|
||||
"<min> <max> <file> <var_name> <timeout> <terminators> <digit_timeout>", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "play_and_get_digits", "Play and get Digits", "Play and get Digits",
|
||||
play_and_get_digits_function,
|
||||
"<min> <max> <tries> <timeout> <terminators> <file> <invalid_file> <var_name> <regexp> [<digit_timeout>]", SAF_NONE);
|
||||
"\n\t<min> <max> <tries> <timeout> <terminators> <file> <invalid_file> <var_name> <regexp> [<digit_timeout>] ['<failure_ext> [failure_dp [failure_context]]']", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "stop_record_session", "Stop Record Session", STOP_SESS_REC_DESC, stop_record_session_function, "<path>", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "record_session", "Record Session", SESS_REC_DESC, record_session_function, "<path> [+<timeout>]", SAF_MEDIA_TAP);
|
||||
SWITCH_ADD_APP(app_interface, "record", "Record File", "Record a file from the channels input", record_function,
|
||||
|
||||
@@ -303,12 +303,12 @@ SWITCH_STANDARD_APP(easyroute_app_function)
|
||||
|
||||
if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
|
||||
destnum = argv[0];
|
||||
if (argc == 2) {
|
||||
if (argc >= 2) {
|
||||
if (!strcasecmp(argv[1], "noat")) {
|
||||
noat = 1;
|
||||
} else if (!strcasecmp(argv[1], "separator")) {
|
||||
if (argc == 3) {
|
||||
switch_set_string(separator, argv[2]);
|
||||
separator = argv[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,13 +358,13 @@ SWITCH_STANDARD_API(easyroute_function)
|
||||
status = SWITCH_STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
if (argc == 2) {
|
||||
if (argc >= 2) {
|
||||
if (!strcasecmp(argv[1], "noat")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Entering noat.\n");
|
||||
noat = 1;
|
||||
} else if (!strcasecmp(argv[1], "separator")) {
|
||||
if (argc == 3) {
|
||||
switch_set_string(separator, argv[2]);
|
||||
separator = argv[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1329,7 +1329,7 @@ static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_da
|
||||
if (digit_str[0]) {
|
||||
char *p = digit_str;
|
||||
while (p && *p) {
|
||||
switch_dtmf_t dtmf;
|
||||
switch_dtmf_t dtmf = {0};
|
||||
dtmf.digit = *p;
|
||||
dtmf.duration = switch_core_default_dtmf_duration(0);
|
||||
switch_channel_queue_dtmf(channel, &dtmf);
|
||||
|
||||
@@ -1249,15 +1249,29 @@ static void *SWITCH_THREAD_FUNC ringall_thread_run(switch_thread_t *thread, void
|
||||
for (i = 0; i < cbh->rowcount; i++) {
|
||||
struct call_helper *h = cbh->rows[i];
|
||||
char *parsed = NULL;
|
||||
int use_ent = 0;
|
||||
|
||||
if (strstr(h->originate_string, "user/")) {
|
||||
switch_event_create_brackets(h->originate_string, '<', '>', ',', &ovars, &parsed, SWITCH_TRUE);
|
||||
use_ent = 1;
|
||||
} else {
|
||||
switch_event_create_brackets(h->originate_string, '{', '}', ',', &ovars, &parsed, SWITCH_TRUE);
|
||||
}
|
||||
|
||||
switch_event_create_brackets(h->originate_string, '{', '}', ',', &ovars, &parsed, SWITCH_TRUE);
|
||||
switch_event_del_header(ovars, "fifo_outbound_uuid");
|
||||
|
||||
if (!h->timeout) h->timeout = node->ring_timeout;
|
||||
if (timeout < h->timeout) timeout = h->timeout;
|
||||
|
||||
stream.write_function(&stream, "[leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s]%s,",
|
||||
h->timeout, h->uuid, node->name, parsed ? parsed : h->originate_string);
|
||||
if (use_ent) {
|
||||
stream.write_function(&stream, "{ignore_early_media=true,outbound_redirect_fatal=true,leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s}%s%s",
|
||||
h->timeout, h->uuid, node->name,
|
||||
parsed ? parsed : h->originate_string, (i == cbh->rowcount - 1) ? "" : SWITCH_ENT_ORIGINATE_DELIM);
|
||||
} else {
|
||||
stream.write_function(&stream, "[leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s]%s,",
|
||||
h->timeout, h->uuid, node->name, parsed ? parsed : h->originate_string);
|
||||
}
|
||||
|
||||
stream2.write_function(&stream2, "%s,", h->uuid);
|
||||
switch_safe_free(parsed);
|
||||
|
||||
@@ -1265,10 +1279,6 @@ static void *SWITCH_THREAD_FUNC ringall_thread_run(switch_thread_t *thread, void
|
||||
|
||||
originate_string = (char *) stream.data;
|
||||
|
||||
if (originate_string) {
|
||||
end_of(originate_string) = '\0';
|
||||
}
|
||||
|
||||
uuid_list = (char *) stream2.data;
|
||||
|
||||
if (uuid_list) {
|
||||
@@ -2958,6 +2968,9 @@ SWITCH_STANDARD_APP(fifo_function)
|
||||
switch_safe_free(sql);
|
||||
|
||||
|
||||
switch_channel_set_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(other_session));
|
||||
switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session));
|
||||
|
||||
switch_ivr_multi_threaded_bridge(session, other_session, on_dtmf, other_session, session);
|
||||
|
||||
if (outbound_id) {
|
||||
|
||||
@@ -945,7 +945,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load)
|
||||
switch_scheduler_add_task(switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_hash", 0, NULL,
|
||||
SSHF_NONE);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA)
|
||||
SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC)
|
||||
SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
|
||||
SWITCH_ADD_API(commands_api_interface, "hash_dump", "dump hash/limit_hash data (used for synchronization)", hash_dump_function, HASH_DUMP_SYNTAX);
|
||||
SWITCH_ADD_API(commands_api_interface, "hash_remote", "hash remote", hash_remote_function, HASH_REMOTE_SYNTAX);
|
||||
|
||||
@@ -127,6 +127,7 @@ struct callback_obj {
|
||||
int matches;
|
||||
switch_memory_pool_t *pool;
|
||||
char *lookup_number;
|
||||
char *lrn_number;
|
||||
char *cid;
|
||||
switch_bool_t intrastate;
|
||||
switch_bool_t intralata;
|
||||
@@ -622,6 +623,8 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa
|
||||
additional->carrier_name = switch_core_strdup(pool, switch_str_nil(argv[i]));
|
||||
} else if (CF("lcr_rate_field")) {
|
||||
if (!argv[i] || zstr(argv[i])) {
|
||||
/* maybe we want to consider saying which carriers have null rate fields... maybe they can run the query and find out */
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "rate field is null, skipping\n");
|
||||
goto end;
|
||||
}
|
||||
additional->rate = (float)atof(switch_str_nil(argv[i]));
|
||||
@@ -798,7 +801,7 @@ static switch_status_t is_intrastatelata(callback_t *cb_struct)
|
||||
cb_struct->lookup_number+1, cb_struct->lookup_number+4,
|
||||
cb_struct->cid+1, cb_struct->cid+4);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(cb_struct->session), SWITCH_LOG_DEBUG, "SQL: %s\n", sql);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(cb_struct->session), SWITCH_LOG_DEBUG, "SQL: %s\n", sql);
|
||||
|
||||
return(lcr_execute_sql_callback(sql, intrastatelata_callback, cb_struct));
|
||||
|
||||
@@ -808,8 +811,9 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct)
|
||||
{
|
||||
switch_stream_handle_t sql_stream = { 0 };
|
||||
char *digits = cb_struct->lookup_number;
|
||||
char *digits_copy;
|
||||
char *digits_expanded;
|
||||
char *digits_copy = NULL;
|
||||
char *digits_expanded = NULL;
|
||||
char *lrn_digits_expanded = NULL;
|
||||
profile_t *profile = cb_struct->profile;
|
||||
switch_bool_t lookup_status;
|
||||
switch_channel_t *channel;
|
||||
@@ -832,6 +836,11 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct)
|
||||
}
|
||||
|
||||
digits_expanded = expand_digits(cb_struct->pool, digits_copy, cb_struct->profile->quote_in_list);
|
||||
if (cb_struct->lrn_number) {
|
||||
lrn_digits_expanded = expand_digits(cb_struct->pool, cb_struct->lrn_number, cb_struct->profile->quote_in_list);
|
||||
} else {
|
||||
lrn_digits_expanded = switch_core_strdup(cb_struct->pool, digits_expanded);
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(cb_struct->session), SWITCH_LOG_DEBUG, "Has NPA NXX: [%u == %u]\n", profile->profile_has_npanxx, SWITCH_TRUE);
|
||||
if (profile->profile_has_npanxx == SWITCH_TRUE) {
|
||||
@@ -866,6 +875,10 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct)
|
||||
id_str = switch_core_sprintf(cb_struct->pool, "%d", cb_struct->profile->id);
|
||||
switch_channel_set_variable_var_check(channel, "lcr_query_profile", id_str, SWITCH_FALSE);
|
||||
switch_channel_set_variable_var_check(channel, "lcr_query_expanded_digits", digits_expanded, SWITCH_FALSE);
|
||||
switch_channel_set_variable_var_check(channel, "lcr_query_expanded_lrn_digits", lrn_digits_expanded, SWITCH_FALSE);
|
||||
if ( cb_struct->lrn_number ) {
|
||||
switch_channel_set_variable_var_check(channel, "lcr_lrn", cb_struct->lrn_number, SWITCH_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cb_struct->event) {
|
||||
@@ -876,6 +889,10 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct)
|
||||
id_str = switch_core_sprintf(cb_struct->pool, "%d", cb_struct->profile->id);
|
||||
switch_event_add_header_string(cb_struct->event, SWITCH_STACK_BOTTOM, "lcr_query_profile", id_str);
|
||||
switch_event_add_header_string(cb_struct->event, SWITCH_STACK_BOTTOM, "lcr_query_expanded_digits", digits_expanded);
|
||||
switch_event_add_header_string(cb_struct->event, SWITCH_STACK_BOTTOM, "lcr_query_expanded_lrn_digits", lrn_digits_expanded);
|
||||
if ( cb_struct->lrn_number ) {
|
||||
switch_event_add_header_string(cb_struct->event, SWITCH_STACK_BOTTOM, "lcr_lrn", cb_struct->lrn_number);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up the query to be executed */
|
||||
@@ -998,7 +1015,7 @@ static switch_status_t lcr_load_config()
|
||||
char *export_fields = NULL;
|
||||
char *limit_type = NULL;
|
||||
int argc, x = 0;
|
||||
char *argv[4] = { 0 };
|
||||
char *argv[32] = { 0 };
|
||||
|
||||
SWITCH_STANDARD_STREAM(order_by);
|
||||
|
||||
@@ -1078,29 +1095,48 @@ static switch_status_t lcr_load_config()
|
||||
SWITCH_STANDARD_STREAM(sql_stream);
|
||||
if (zstr(custom_sql)) {
|
||||
/* use default sql */
|
||||
|
||||
/* Checking for codec field, adding if needed */
|
||||
if (db_check("SELECT codec FROM carrier_gateway LIMIT 1") == SWITCH_TRUE) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "codec field defined.\n");
|
||||
} else {
|
||||
if (db_check("ALTER TABLE carrier_gateway add codec varchar(255);") == SWITCH_TRUE) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "adding codec field your lcr carrier_gateway database schema.\n");
|
||||
} else {
|
||||
return SWITCH_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checking for cid field, adding if needed */
|
||||
if (db_check("SELECT cid FROM lcr LIMIT 1") == SWITCH_TRUE) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cid field defined.\n");
|
||||
} else {
|
||||
if (db_check("ALTER TABLE lcr add cid varchar(32);") == SWITCH_TRUE) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "adding cid field to your lcr database schema.\n");
|
||||
} else {
|
||||
return SWITCH_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (db_check("SELECT lrn FROM lcr LIMIT 1") != SWITCH_TRUE) {
|
||||
if (db_check("ALTER TABLE lcr ADD lrn BOOLEAN NOT NULL DEFAULT false")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "adding lrn field to your lcr database schema.\n");
|
||||
} else {
|
||||
return SWITCH_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
sql_stream.write_function(&sql_stream,
|
||||
"SELECT l.digits AS lcr_digits, c.carrier_name AS lcr_carrier_name, l.${lcr_rate_field} AS lcr_rate_field, \
|
||||
cg.prefix AS lcr_gw_prefix, cg.suffix AS lcr_gw_suffix, l.lead_strip AS lcr_lead_strip, \
|
||||
l.trail_strip AS lcr_trail_strip, l.prefix AS lcr_prefix, l.suffix AS lcr_suffix "
|
||||
);
|
||||
if (db_check("SELECT codec from carrier_gateway limit 1") == SWITCH_TRUE) {
|
||||
sql_stream.write_function(&sql_stream, ", cg.codec AS lcr_codec ");
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "codec field defined.\n");
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
|
||||
"codec field not defined, please update your lcr carrier_gateway database schema.\n"
|
||||
);
|
||||
}
|
||||
if (db_check("SELECT cid from lcr limit 1") == SWITCH_TRUE) {
|
||||
sql_stream.write_function(&sql_stream, ", l.cid AS lcr_cid ");
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cid field defined.\n");
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "cid field not defined, please update your lcr database schema.\n");
|
||||
}
|
||||
l.trail_strip AS lcr_trail_strip, l.prefix AS lcr_prefix, l.suffix AS lcr_suffix, \
|
||||
cg.codec AS lcr_codec, l.cid AS lcr_cid ");
|
||||
sql_stream.write_function(&sql_stream, "FROM lcr l JOIN carriers c ON l.carrier_id=c.id \
|
||||
JOIN carrier_gateway cg ON c.id=cg.carrier_id WHERE \
|
||||
c.enabled = '1' AND cg.enabled = '1' AND l.enabled = '1' AND digits IN (");
|
||||
sql_stream.write_function(&sql_stream, "${lcr_query_expanded_digits}");
|
||||
JOIN carrier_gateway cg ON c.id=cg.carrier_id \
|
||||
WHERE c.enabled = '1' AND cg.enabled = '1' AND l.enabled = '1' AND (\
|
||||
(digits IN (${lcr_query_expanded_digits}) AND lrn = false) OR \
|
||||
(digits IN (${lcr_query_expanded_lrn_digits}) AND lrn = true)");
|
||||
|
||||
sql_stream.write_function(&sql_stream, ") AND CURRENT_TIMESTAMP BETWEEN date_start AND date_end ");
|
||||
if (profile->id > 0) {
|
||||
sql_stream.write_function(&sql_stream, "AND lcr_profile=%d ", profile->id);
|
||||
@@ -1430,6 +1466,7 @@ SWITCH_STANDARD_DIALPLAN(lcr_dialplan_hunt)
|
||||
switch_event_t *event = NULL;
|
||||
const char *intrastate = NULL;
|
||||
const char *intralata = NULL;
|
||||
const char *lrn = NULL;
|
||||
|
||||
if (session) {
|
||||
pool = switch_core_session_get_pool(session);
|
||||
@@ -1443,8 +1480,12 @@ SWITCH_STANDARD_DIALPLAN(lcr_dialplan_hunt)
|
||||
|
||||
intrastate = switch_channel_get_variable(channel, "intrastate");
|
||||
intralata = switch_channel_get_variable(channel, "intralata");
|
||||
lrn = switch_channel_get_variable(channel, "lrn");
|
||||
routes.lrn_number = (char *) lrn;
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "intrastate channel var is [%s]\n", intrastate);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "intralata channel var is [%s]\n", intralata);
|
||||
|
||||
if (!zstr(intralata) && !strcasecmp((char *)intralata, "true")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Select routes based on intralata rates\n");
|
||||
routes.intralata = SWITCH_FALSE;
|
||||
@@ -1531,7 +1572,7 @@ void str_repeat(size_t how_many, char *what, switch_stream_handle_t *str_stream)
|
||||
SWITCH_STANDARD_APP(lcr_app_function)
|
||||
{
|
||||
int argc = 0;
|
||||
char *argv[4] = { 0 };
|
||||
char *argv[32] = { 0 };
|
||||
char *mydata = NULL;
|
||||
char *dest = NULL;
|
||||
char vbuf[1024] = "";
|
||||
@@ -1544,6 +1585,7 @@ SWITCH_STANDARD_APP(lcr_app_function)
|
||||
switch_memory_pool_t *pool;
|
||||
switch_event_t *event;
|
||||
const char *intra = NULL;
|
||||
const char *lrn = NULL;
|
||||
|
||||
if (!(mydata = switch_core_session_strdup(session, data))) {
|
||||
return;
|
||||
@@ -1557,11 +1599,15 @@ SWITCH_STANDARD_APP(lcr_app_function)
|
||||
switch_event_create(&event, SWITCH_EVENT_MESSAGE);
|
||||
routes.event = event;
|
||||
}
|
||||
|
||||
routes.pool = pool;
|
||||
|
||||
|
||||
lrn = switch_channel_get_variable(channel, "lrn");
|
||||
routes.lrn_number = (char *) lrn;
|
||||
|
||||
intra = switch_channel_get_variable(channel, "intrastate");
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "intrastate channel var is [%s]\n",
|
||||
zstr(intra) ? "undef" : intra);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "intrastate channel var is [%s]\n", zstr(intra) ? "undef" : intra);
|
||||
if (zstr(intra) || strcasecmp((char *)intra, "true")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Select routes based on interstate rates\n");
|
||||
routes.intrastate = SWITCH_FALSE;
|
||||
@@ -1578,6 +1624,7 @@ SWITCH_STANDARD_APP(lcr_app_function)
|
||||
|
||||
if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
|
||||
dest = argv[0];
|
||||
|
||||
if (argc > 1) {
|
||||
lcr_profile = argv[1];
|
||||
}
|
||||
@@ -1664,21 +1711,20 @@ static void write_data(switch_stream_handle_t *stream, switch_bool_t as_xml, con
|
||||
|
||||
SWITCH_STANDARD_API(dialplan_lcr_function)
|
||||
{
|
||||
char *argv[9] = { 0 };
|
||||
char *argv[32] = { 0 };
|
||||
int argc;
|
||||
char *mydata = NULL;
|
||||
//char *dialstring = NULL;
|
||||
char *lcr_profile = NULL;
|
||||
lcr_route current = NULL;
|
||||
max_obj_t maximum_lengths = { 0 };
|
||||
callback_t cb_struct = { 0 };
|
||||
switch_memory_pool_t *pool = NULL;
|
||||
char *mydata = NULL;
|
||||
char *lcr_profile = NULL;
|
||||
lcr_route current = NULL;
|
||||
max_obj_t maximum_lengths = { 0 };
|
||||
callback_t cb_struct = { 0 };
|
||||
switch_memory_pool_t *pool = NULL;
|
||||
switch_event_t *event;
|
||||
switch_status_t lookup_status = SWITCH_STATUS_SUCCESS;
|
||||
switch_bool_t as_xml = SWITCH_FALSE;
|
||||
char *event_str = NULL;
|
||||
switch_xml_t event_xml = NULL;
|
||||
int rowcount=0;
|
||||
switch_bool_t as_xml = SWITCH_FALSE;
|
||||
char *event_str = NULL;
|
||||
switch_xml_t event_xml = NULL;
|
||||
int rowcount = 0;
|
||||
|
||||
if (zstr(cmd)) {
|
||||
goto usage;
|
||||
@@ -1708,10 +1754,18 @@ SWITCH_STANDARD_API(dialplan_lcr_function)
|
||||
}
|
||||
if (argc > 2) {
|
||||
int i;
|
||||
for (i=2; i<argc; i++) {
|
||||
for (i = 2; i < argc; i++) {
|
||||
if (!strcasecmp(argv[i], "intrastate")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Select routes based on intrastate rates\n");
|
||||
cb_struct.intrastate = SWITCH_TRUE;
|
||||
} else if (!strcasecmp(argv[i], "lrn")) {
|
||||
i++;
|
||||
if (argv[i]) {
|
||||
cb_struct.lrn_number = argv[i];
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "LRN number [%s]\n", cb_struct.lrn_number);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "you must pass a LRN number to use lrn\n");
|
||||
}
|
||||
} else if (!strcasecmp(argv[i], "as")) {
|
||||
i++;
|
||||
if (argv[i] && !strcasecmp(argv[i], "xml")) {
|
||||
@@ -1868,7 +1922,7 @@ usage:
|
||||
|
||||
SWITCH_STANDARD_API(dialplan_lcr_admin_function)
|
||||
{
|
||||
char *argv[4] = { 0 };
|
||||
char *argv[32] = { 0 };
|
||||
int argc;
|
||||
char *mydata = NULL;
|
||||
switch_hash_index_t *hi;
|
||||
|
||||
@@ -23,21 +23,22 @@ DROP TABLE IF EXISTS `lcr`;
|
||||
CREATE TABLE `lcr` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
`digits` varchar(15) default NULL,
|
||||
`rate` float(11,5) unsigned NOT NULL,
|
||||
`intrastate_rate` float(11, 5) unsigned NOT NULL,
|
||||
`intralata_rate` float(11, 5) unsigned NOT NULL,
|
||||
`rate` float(11,5) unsigned,
|
||||
`intrastate_rate` float(11, 5) unsigned,
|
||||
`intralata_rate` float(11, 5) unsigned,
|
||||
`carrier_id` int(11) NOT NULL,
|
||||
`lead_strip` int(11) NOT NULL,
|
||||
`trail_strip` int(11) NOT NULL,
|
||||
`prefix` varchar(16) NOT NULL,
|
||||
`suffix` varchar(16) NOT NULL,
|
||||
`lcr_profile` varchar(32) default NULL,
|
||||
`lcr_profile` int(11) NOT NULL default 0,
|
||||
`date_start` datetime NOT NULL DEFAULT '1970-01-01',
|
||||
`date_end` datetime NOT NULL DEFAULT '2030-12-31',
|
||||
`quality` float(10,6) NOT NULL,
|
||||
`reliability` float(10,6) NOT NULL,
|
||||
`cid` varchar(32) NOT NULL DEFAULT '',
|
||||
`enabled` boolean NOT NULL DEFAULT '1',
|
||||
`lrn` boolean NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `carrier_id` (`carrier_id`),
|
||||
KEY `digits` (`digits`),
|
||||
|
||||
@@ -42,9 +42,9 @@ CREATE TABLE lcr
|
||||
(
|
||||
id serial NOT NULL,
|
||||
digits NUMERIC(20, 0),
|
||||
rate numeric(11,5) NOT NULL,
|
||||
intrastate_rate numeric(11,5) NOT NULL,
|
||||
intralata_rate numeric(11,5) NOT NULL,
|
||||
rate numeric(11,5),
|
||||
intrastate_rate numeric(11,5),
|
||||
intralata_rate numeric(11,5),
|
||||
carrier_id integer NOT NULL REFERENCES carriers(id),
|
||||
lead_strip integer NOT NULL DEFAULT 0,
|
||||
trail_strip integer NOT NULL DEFAULT 0,
|
||||
@@ -57,6 +57,7 @@ CREATE TABLE lcr
|
||||
reliability numeric(10,6) NOT NULL DEFAULT 0,
|
||||
cid VARCHAR(32) NOT NULL DEFAULT '',
|
||||
enabled boolean NOT NULL DEFAULT 'true',
|
||||
lrn boolean NOT NULL DEFAULT 'false',
|
||||
CONSTRAINT lcr_pkey PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,33 +1,94 @@
|
||||
#include <switch.h>
|
||||
#include "mod_mongo.h"
|
||||
|
||||
#define DELIMITER ';'
|
||||
#define FIND_ONE_SYNTAX "mongo_find_one ns; query; fields"
|
||||
#define MAPREDUCE_SYNTAX "mongo_mapreduce ns; query"
|
||||
|
||||
static struct {
|
||||
mongo_connection_pool_t *conn_pool;
|
||||
char *map;
|
||||
char *reduce;
|
||||
char *finalize;
|
||||
} globals;
|
||||
|
||||
SWITCH_STANDARD_API(mongo_mapreduce_function)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
DBClientBase *conn = NULL;
|
||||
char *ns = NULL, *json_query = NULL;
|
||||
|
||||
ns = strdup(cmd);
|
||||
switch_assert(ns != NULL);
|
||||
|
||||
if ((json_query = strchr(ns, DELIMITER))) {
|
||||
*json_query++ = '\0';
|
||||
}
|
||||
|
||||
if (!zstr(ns) && !zstr(json_query)) {
|
||||
try {
|
||||
BSONObj query = fromjson(json_query);
|
||||
BSONObj out;
|
||||
BSONObjBuilder cmd;
|
||||
|
||||
cmd.append("mapreduce", conn->nsGetCollection(ns));
|
||||
if (!zstr(globals.map)) {
|
||||
cmd.appendCode("map", globals.map);
|
||||
}
|
||||
if (!zstr(globals.reduce)) {
|
||||
cmd.appendCode("reduce", globals.reduce);
|
||||
}
|
||||
if (!zstr(globals.finalize)) {
|
||||
cmd.appendCode("finalize", globals.finalize);
|
||||
}
|
||||
if(!query.isEmpty()) {
|
||||
cmd.append("query", query);
|
||||
}
|
||||
cmd.append("out", BSON("inline" << 1));
|
||||
|
||||
conn = mongo_connection_pool_get(globals.conn_pool);
|
||||
if (conn) {
|
||||
conn->runCommand(conn->nsGetDB(ns), cmd.done(), out);
|
||||
mongo_connection_pool_put(globals.conn_pool, conn);
|
||||
|
||||
stream->write_function(stream, "-OK\n%s\n", out.toString().c_str());
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR\nNo connection\n");
|
||||
}
|
||||
} catch (DBException &e) {
|
||||
if (conn) {
|
||||
mongo_connection_destroy(&conn);
|
||||
}
|
||||
stream->write_function(stream, "-ERR\n%s\n", e.toString().c_str());
|
||||
}
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR\n%s\n", MAPREDUCE_SYNTAX);
|
||||
}
|
||||
|
||||
switch_safe_free(ns);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const char *SYNTAX = "mongo_find_one ns; query; fields";
|
||||
|
||||
SWITCH_STANDARD_API(mongo_find_one_function)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
char *ns = NULL, *json_query = NULL, *json_fields = NULL;
|
||||
char delim = ';';
|
||||
|
||||
ns = strdup(cmd);
|
||||
switch_assert(ns != NULL);
|
||||
|
||||
if ((json_query = strchr(ns, delim))) {
|
||||
if ((json_query = strchr(ns, DELIMITER))) {
|
||||
*json_query++ = '\0';
|
||||
if ((json_fields = strchr(json_query, delim))) {
|
||||
if ((json_fields = strchr(json_query, DELIMITER))) {
|
||||
*json_fields++ = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (!zstr(ns) && !zstr(json_query) && !zstr(json_fields)) {
|
||||
|
||||
DBClientConnection *conn = NULL;
|
||||
DBClientBase *conn = NULL;
|
||||
|
||||
try {
|
||||
BSONObj query = fromjson(json_query);
|
||||
@@ -49,9 +110,8 @@ SWITCH_STANDARD_API(mongo_find_one_function)
|
||||
stream->write_function(stream, "-ERR\n%s\n", e.toString().c_str());
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR\n%s\n", SYNTAX);
|
||||
stream->write_function(stream, "-ERR\n%s\n", FIND_ONE_SYNTAX);
|
||||
}
|
||||
|
||||
switch_safe_free(ns);
|
||||
@@ -64,7 +124,7 @@ static switch_status_t config(void)
|
||||
const char *cf = "mongo.conf";
|
||||
switch_xml_t cfg, xml, settings, param;
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
const char *host = "127.0.0.1";
|
||||
const char *conn_str = "127.0.0.1";
|
||||
switch_size_t min_connections = 1, max_connections = 1;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
|
||||
@@ -79,7 +139,10 @@ static switch_status_t config(void)
|
||||
int tmp;
|
||||
|
||||
if (!strcmp(var, "host")) {
|
||||
host = val;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "'host' is deprecated. use 'connection-string'\n");
|
||||
conn_str = val;
|
||||
} else if (!strcmp(var, "connection-string")) {
|
||||
conn_str = val;
|
||||
} else if (!strcmp(var, "min-connections")) {
|
||||
if ((tmp = atoi(val)) > 0) {
|
||||
min_connections = tmp;
|
||||
@@ -88,15 +151,21 @@ static switch_status_t config(void)
|
||||
if ((tmp = atoi(val)) > 0) {
|
||||
max_connections = tmp;
|
||||
}
|
||||
} else if (!strcmp(var, "map")) {
|
||||
globals.map = strdup(val);
|
||||
} else if (!strcmp(var, "reduce")) {
|
||||
globals.reduce = strdup(val);
|
||||
} else if (!strcmp(var, "finalize")) {
|
||||
globals.finalize = strdup(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mongo_connection_pool_create(&globals.conn_pool, min_connections, max_connections, host) != SWITCH_STATUS_SUCCESS) {
|
||||
if (mongo_connection_pool_create(&globals.conn_pool, min_connections, max_connections, conn_str) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Can't create connection pool\n");
|
||||
status = SWITCH_STATUS_GENERR;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Mongo connection pool created [%s %d/%d]\n", host, (int)min_connections, (int)max_connections);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Mongo connection pool created [%s %d/%d]\n", conn_str, (int)min_connections, (int)max_connections);
|
||||
}
|
||||
|
||||
switch_xml_free(xml);
|
||||
@@ -125,7 +194,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_mongo_load)
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
SWITCH_ADD_API(api_interface, "mongo_find_one", "mongo", mongo_find_one_function, SYNTAX);
|
||||
SWITCH_ADD_API(api_interface, "mongo_find_one", "findOne", mongo_find_one_function, FIND_ONE_SYNTAX);
|
||||
SWITCH_ADD_API(api_interface, "mongo_mapreduce", "Map/Reduce", mongo_mapreduce_function, MAPREDUCE_SYNTAX);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -133,6 +203,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_mongo_load)
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_mongo_shutdown)
|
||||
{
|
||||
mongo_connection_pool_destroy(&globals.conn_pool);
|
||||
switch_safe_free(globals.map);
|
||||
switch_safe_free(globals.reduce);
|
||||
switch_safe_free(globals.finalize);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef MOD_MONGO_H
|
||||
#define MOD_MONGO_H
|
||||
|
||||
|
||||
#include <client/dbclient.h>
|
||||
#include <client/connpool.h>
|
||||
#include <db/json.h>
|
||||
@@ -10,7 +9,7 @@
|
||||
using namespace mongo;
|
||||
|
||||
typedef struct {
|
||||
char *host;
|
||||
char *conn_str;
|
||||
|
||||
switch_size_t min_connections;
|
||||
switch_size_t max_connections;
|
||||
@@ -22,16 +21,16 @@ typedef struct {
|
||||
} mongo_connection_pool_t;
|
||||
|
||||
|
||||
switch_status_t mongo_connection_create(DBClientConnection **connection, const char *host);
|
||||
void mongo_connection_destroy(DBClientConnection **conn);
|
||||
switch_status_t mongo_connection_create(DBClientBase **connection, const char *conn_str);
|
||||
void mongo_connection_destroy(DBClientBase **conn);
|
||||
|
||||
switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool, switch_size_t min_connections, switch_size_t max_connections,
|
||||
const char *host);
|
||||
const char *conn_str);
|
||||
void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool);
|
||||
|
||||
|
||||
DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool);
|
||||
switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientConnection *conn);
|
||||
DBClientBase *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool);
|
||||
switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientBase *conn);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,24 +10,31 @@
|
||||
scoped_conn.done();
|
||||
*/
|
||||
|
||||
switch_status_t mongo_connection_create(DBClientConnection **connection, const char *host)
|
||||
switch_status_t mongo_connection_create(DBClientBase **connection, const char *conn_str)
|
||||
{
|
||||
DBClientConnection *conn = new DBClientConnection();
|
||||
DBClientBase *conn = NULL;
|
||||
string conn_string(conn_str), err_msg;
|
||||
ConnectionString cs = ConnectionString::parse(conn_string, err_msg);
|
||||
|
||||
if (!cs.isValid()) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't parse url: %s\n", err_msg.c_str());
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
try {
|
||||
conn->connect(host);
|
||||
conn = cs.connect(err_msg);
|
||||
} catch (DBException &e) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't connect to mongo [%s]\n", host);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't connect to mongo [%s]: %s\n", conn_str, err_msg.c_str());
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
*connection = conn;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to mongo [%s]\n", host);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to mongo [%s]\n", conn_str);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void mongo_connection_destroy(DBClientConnection **conn)
|
||||
void mongo_connection_destroy(DBClientBase **conn)
|
||||
{
|
||||
switch_assert(*conn != NULL);
|
||||
delete *conn;
|
||||
@@ -36,12 +43,12 @@ void mongo_connection_destroy(DBClientConnection **conn)
|
||||
}
|
||||
|
||||
switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool, switch_size_t min_connections, switch_size_t max_connections,
|
||||
const char *host)
|
||||
const char *conn_str)
|
||||
{
|
||||
switch_memory_pool_t *pool = NULL;
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
mongo_connection_pool_t *cpool = NULL;
|
||||
DBClientConnection *conn = NULL;
|
||||
DBClientBase *conn = NULL;
|
||||
|
||||
if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
|
||||
return status;
|
||||
@@ -61,13 +68,13 @@ switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool
|
||||
|
||||
cpool->min_connections = min_connections;
|
||||
cpool->max_connections = max_connections;
|
||||
cpool->host = switch_core_strdup(pool, host);
|
||||
cpool->conn_str = switch_core_strdup(pool, conn_str);
|
||||
|
||||
cpool->pool = pool;
|
||||
|
||||
for (cpool->size = 0; cpool->size < min_connections; cpool->size++) {
|
||||
|
||||
if (mongo_connection_create(&conn, host) == SWITCH_STATUS_SUCCESS) {
|
||||
if (mongo_connection_create(&conn, conn_str) == SWITCH_STATUS_SUCCESS) {
|
||||
mongo_connection_pool_put(cpool, conn);
|
||||
} else {
|
||||
break;
|
||||
@@ -94,7 +101,7 @@ void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool)
|
||||
switch_assert(cpool != NULL);
|
||||
|
||||
while (switch_queue_trypop(cpool->connections, &data) == SWITCH_STATUS_SUCCESS) {
|
||||
mongo_connection_destroy((DBClientConnection **)&data);
|
||||
mongo_connection_destroy((DBClientBase **)&data);
|
||||
}
|
||||
|
||||
switch_mutex_destroy(cpool->mutex);
|
||||
@@ -104,9 +111,9 @@ void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool)
|
||||
}
|
||||
|
||||
|
||||
DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool)
|
||||
DBClientBase *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool)
|
||||
{
|
||||
DBClientConnection *conn = NULL;
|
||||
DBClientBase *conn = NULL;
|
||||
void *data = NULL;
|
||||
|
||||
switch_assert(conn_pool != NULL);
|
||||
@@ -114,8 +121,8 @@ DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool
|
||||
switch_mutex_lock(conn_pool->mutex);
|
||||
|
||||
if (switch_queue_trypop(conn_pool->connections, &data) == SWITCH_STATUS_SUCCESS) {
|
||||
conn = (DBClientConnection *) data;
|
||||
} else if (mongo_connection_create(&conn, conn_pool->host) == SWITCH_STATUS_SUCCESS) {
|
||||
conn = (DBClientBase *) data;
|
||||
} else if (mongo_connection_create(&conn, conn_pool->conn_str) == SWITCH_STATUS_SUCCESS) {
|
||||
if (++conn_pool->size > conn_pool->max_connections) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Connection pool is empty. You may want to increase 'max-connections'\n");
|
||||
}
|
||||
@@ -130,7 +137,7 @@ DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool
|
||||
return conn;
|
||||
}
|
||||
|
||||
switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientConnection *conn)
|
||||
switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientBase *conn)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
BASE=../../../..
|
||||
LOCAL_OBJS=ivr.o util.o config.o menu.o
|
||||
include $(BASE)/build/modmake.rules
|
||||
@@ -1,135 +0,0 @@
|
||||
/* Copy paste from FS mod_voicemail */
|
||||
#include <switch.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
const char *global_cf = "protovm.conf";
|
||||
|
||||
void populate_profile_menu_event(vmivr_profile_t *profile, vmivr_menu_profile_t *menu) {
|
||||
switch_xml_t cfg, xml, x_profiles, x_profile, x_keys, x_phrases, x_menus, x_menu;
|
||||
|
||||
free_profile_menu_event(menu);
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
goto end;
|
||||
}
|
||||
if (!(x_profiles = switch_xml_child(cfg, "profiles"))) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", profile->name))) {
|
||||
if ((x_menus = switch_xml_child(x_profile, "menus"))) {
|
||||
if ((x_menu = switch_xml_find_child(x_menus, "menu", "name", menu->name))) {
|
||||
if ((x_keys = switch_xml_child(x_menu, "keys"))) {
|
||||
switch_event_import_xml(switch_xml_child(x_keys, "key"), "dtmf", "action", &menu->event_keys_dtmf);
|
||||
switch_event_import_xml(switch_xml_child(x_keys, "key"), "action", "dtmf", &menu->event_keys_action);
|
||||
switch_event_import_xml(switch_xml_child(x_keys, "key"), "action", "variable", &menu->event_keys_varname);
|
||||
}
|
||||
if ((x_phrases = switch_xml_child(x_menu, "phrases"))) {
|
||||
switch_event_import_xml(switch_xml_child(x_phrases, "phrase"), "name", "value", &menu->event_phrases);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
if (xml)
|
||||
switch_xml_free(xml);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void free_profile_menu_event(vmivr_menu_profile_t *menu) {
|
||||
if (menu->event_keys_dtmf) {
|
||||
switch_event_destroy(&menu->event_keys_dtmf);
|
||||
}
|
||||
if (menu->event_keys_action) {
|
||||
switch_event_destroy(&menu->event_keys_action);
|
||||
}
|
||||
if (menu->event_keys_varname) {
|
||||
switch_event_destroy(&menu->event_keys_varname);
|
||||
}
|
||||
|
||||
if (menu->event_phrases) {
|
||||
switch_event_destroy(&menu->event_phrases);
|
||||
}
|
||||
}
|
||||
|
||||
vmivr_profile_t *get_profile(switch_core_session_t *session, const char *profile_name)
|
||||
{
|
||||
vmivr_profile_t *profile = NULL;
|
||||
switch_xml_t cfg, xml, x_profiles, x_profile, x_apis, param;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
return profile;
|
||||
}
|
||||
if (!(x_profiles = switch_xml_child(cfg, "profiles"))) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", profile_name))) {
|
||||
if (!(profile = switch_core_session_alloc(session, sizeof(vmivr_profile_t)))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
profile->name = profile_name;
|
||||
|
||||
profile->current_msg = 0;
|
||||
profile->current_msg_uuid = NULL;
|
||||
|
||||
/* TODO Make the following configurable */
|
||||
profile->api_profile = profile->name;
|
||||
profile->menu_check_auth = "std_authenticate";
|
||||
profile->menu_check_main = "std_navigator";
|
||||
profile->menu_check_terminate = "std_purge";
|
||||
|
||||
if ((x_apis = switch_xml_child(x_profile, "apis"))) {
|
||||
int total_options = 0;
|
||||
int total_invalid_options = 0;
|
||||
for (param = switch_xml_child(x_apis, "api"); param; param = param->next) {
|
||||
char *var, *val;
|
||||
if ((var = (char *) switch_xml_attr_soft(param, "name")) && (val = (char *) switch_xml_attr_soft(param, "value"))) {
|
||||
if (!strcasecmp(var, "msg_undelete") && !profile->api_msg_undelete)
|
||||
profile->api_msg_undelete = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_delete") && !profile->api_msg_delete)
|
||||
profile->api_msg_delete = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_list") && !profile->api_msg_list)
|
||||
profile->api_msg_list = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_count") && !profile->api_msg_count)
|
||||
profile->api_msg_count = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_save") && !profile->api_msg_save)
|
||||
profile->api_msg_save = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_purge") && !profile->api_msg_purge)
|
||||
profile->api_msg_purge = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_get") && !profile->api_msg_get)
|
||||
profile->api_msg_get = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "msg_forward") && !profile->api_msg_forward)
|
||||
profile->api_msg_forward = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "pref_greeting_set") && !profile->api_pref_greeting_set)
|
||||
profile->api_pref_greeting_set = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "pref_recname_set") && !profile->api_pref_recname_set)
|
||||
profile->api_pref_recname_set = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "pref_password_set") && !profile->api_pref_password_set)
|
||||
profile->api_pref_password_set = switch_core_session_strdup(session, val);
|
||||
else if (!strcasecmp(var, "auth_login") && !profile->api_auth_login)
|
||||
profile->api_auth_login = switch_core_session_strdup(session, val);
|
||||
else
|
||||
total_invalid_options++;
|
||||
total_options++;
|
||||
}
|
||||
}
|
||||
if (total_options - total_invalid_options != 12) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing api definition for profile '%s'\n", profile_name);
|
||||
profile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
end:
|
||||
switch_xml_free(xml);
|
||||
return profile;
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
extern const char *global_cf;
|
||||
|
||||
struct vmivr_profile {
|
||||
const char *name;
|
||||
|
||||
const char *domain;
|
||||
const char *id;
|
||||
|
||||
int current_msg;
|
||||
const char *current_msg_uuid;
|
||||
|
||||
const char *menu_check_auth;
|
||||
const char *menu_check_main;
|
||||
const char *menu_check_terminate;
|
||||
|
||||
switch_bool_t authorized;
|
||||
|
||||
const char *api_profile;
|
||||
const char *api_auth_login;
|
||||
const char *api_msg_delete;
|
||||
const char *api_msg_undelete;
|
||||
const char *api_msg_list;
|
||||
const char *api_msg_count;
|
||||
const char *api_msg_save;
|
||||
const char *api_msg_purge;
|
||||
const char *api_msg_get;
|
||||
const char *api_msg_forward;
|
||||
const char *api_pref_greeting_set;
|
||||
const char *api_pref_recname_set;
|
||||
const char *api_pref_password_set;
|
||||
|
||||
};
|
||||
typedef struct vmivr_profile vmivr_profile_t;
|
||||
|
||||
struct vmivr_menu_profile {
|
||||
const char *name;
|
||||
|
||||
switch_event_t *event_keys_action;
|
||||
switch_event_t *event_keys_dtmf;
|
||||
switch_event_t *event_keys_varname;
|
||||
switch_event_t *event_phrases;
|
||||
};
|
||||
typedef struct vmivr_menu_profile vmivr_menu_profile_t;
|
||||
|
||||
vmivr_profile_t *get_profile(switch_core_session_t *session, const char *profile_name);
|
||||
|
||||
void free_profile_menu_event(vmivr_menu_profile_t *menu);
|
||||
void populate_profile_menu_event(vmivr_profile_t *profile, vmivr_menu_profile_t *menu);
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Marc Olivier Chouinard <mochouinard@moctel.com>
|
||||
*
|
||||
*
|
||||
* ivr.c -- MT IVR System Interface
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include "ivr.h"
|
||||
|
||||
int match_dtmf(switch_core_session_t *session, dtmf_ss_t *loc) {
|
||||
switch_bool_t is_invalid[128] = { SWITCH_FALSE };
|
||||
int i;
|
||||
loc->potentialMatch = NULL;
|
||||
loc->completeMatch = NULL;
|
||||
loc->potentialMatchCount = 0;
|
||||
|
||||
for (i = 0; i < loc->dtmf_received; i++) {
|
||||
int j;
|
||||
loc->potentialMatchCount = 0;
|
||||
for (j = 0; !zstr(loc->dtmf_accepted[j]) && j < 128; j++) {
|
||||
switch_bool_t cMatch = SWITCH_FALSE;
|
||||
char test[2] = { 0 };
|
||||
|
||||
if (is_invalid[j])
|
||||
continue;
|
||||
|
||||
test[0] = loc->dtmf_stored[i];
|
||||
if (loc->dtmf_accepted[j][i] == 'N' && atoi(test) >= 2 && atoi(test) <= 9)
|
||||
cMatch = SWITCH_TRUE;
|
||||
if (loc->dtmf_accepted[j][i] == 'X' && atoi(test) >= 0 && atoi(test) <= 9) {
|
||||
cMatch = SWITCH_TRUE;
|
||||
}
|
||||
if (i >= strlen(loc->dtmf_accepted[j]) - 1 && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] == '.')
|
||||
cMatch = SWITCH_TRUE;
|
||||
if (loc->dtmf_accepted[j][i] == loc->dtmf_stored[i])
|
||||
cMatch = SWITCH_TRUE;
|
||||
|
||||
if (cMatch == SWITCH_FALSE) {
|
||||
is_invalid[j] = SWITCH_TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == strlen(loc->dtmf_accepted[j]) - 1 && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] == '.') {
|
||||
loc->completeMatch = loc->dtmf_accepted[j];
|
||||
}
|
||||
if (i == loc->dtmf_received - 1 && loc->dtmf_received == strlen(loc->dtmf_accepted[j]) && loc->dtmf_accepted[j][strlen(loc->dtmf_accepted[j])-1] != '.') {
|
||||
loc->completeMatch = loc->dtmf_accepted[j];
|
||||
continue;
|
||||
}
|
||||
loc->potentialMatchCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static switch_status_t cb_on_dtmf_ignore(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
||||
{
|
||||
switch (itype) {
|
||||
case SWITCH_INPUT_TYPE_DTMF:
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
|
||||
switch_channel_queue_dtmf(channel, dtmf);
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status_t cb_on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
||||
{
|
||||
dtmf_ss_t *loc = (dtmf_ss_t*) buf;
|
||||
|
||||
switch (itype) {
|
||||
case SWITCH_INPUT_TYPE_DTMF:
|
||||
{
|
||||
switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
|
||||
switch_bool_t audio_was_stopped = loc->audio_stopped;
|
||||
loc->audio_stopped = SWITCH_TRUE;
|
||||
|
||||
if (loc->dtmf_received >= sizeof(loc->dtmf_stored)) {
|
||||
loc->result = RES_BUFFER_OVERFLOW;
|
||||
break;
|
||||
}
|
||||
if (!loc->terminate_key || dtmf->digit != loc->terminate_key)
|
||||
loc->dtmf_stored[loc->dtmf_received++] = dtmf->digit;
|
||||
|
||||
match_dtmf(session, loc);
|
||||
|
||||
if (loc->terminate_key && dtmf->digit == loc->terminate_key && loc->result == RES_WAITFORMORE) {
|
||||
if (loc->potentialMatchCount == 1 && loc->completeMatch != NULL) {
|
||||
loc->result = RES_FOUND;
|
||||
} else {
|
||||
loc->result = RES_INVALID;
|
||||
}
|
||||
return SWITCH_STATUS_BREAK;
|
||||
} else {
|
||||
if (loc->potentialMatchCount == 0 && loc->completeMatch != NULL) {
|
||||
loc->result = RES_FOUND;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
} else if (loc->potentialMatchCount > 0) {
|
||||
loc->result = RES_WAITFORMORE;
|
||||
if (!audio_was_stopped)
|
||||
return SWITCH_STATUS_BREAK;
|
||||
} else {
|
||||
loc->result = RES_INVALID;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
switch_status_t captureMenuInitialize(dtmf_ss_t *loc, char **dtmf_accepted) {
|
||||
int i;
|
||||
|
||||
memset(loc, 0, sizeof(*loc));
|
||||
|
||||
for (i = 0; dtmf_accepted[i] && i < 16; i++) {
|
||||
strncpy(loc->dtmf_accepted[i], dtmf_accepted[i], 128);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
switch_status_t playbackBufferDTMF(switch_core_session_t *session, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (switch_channel_ready(channel)) {
|
||||
switch_input_args_t args = { 0 };
|
||||
|
||||
args.input_callback = cb_on_dtmf_ignore;
|
||||
|
||||
if (macro_name) {
|
||||
status = switch_ivr_phrase_macro_event(session, macro_name, data, event, lang, &args);
|
||||
}
|
||||
} else {
|
||||
status = SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
switch_status_t captureMenu(switch_core_session_t *session, dtmf_ss_t *loc, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (switch_channel_ready(channel)) {
|
||||
switch_input_args_t args = { 0 };
|
||||
|
||||
args.input_callback = cb_on_dtmf;
|
||||
args.buf = loc;
|
||||
|
||||
if (macro_name && loc->audio_stopped == SWITCH_FALSE && loc->result == RES_WAITFORMORE) {
|
||||
status = switch_ivr_phrase_macro_event(session, macro_name, data, event, lang, &args);
|
||||
}
|
||||
|
||||
if (switch_channel_ready(channel) && (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) && timeout && loc->result == RES_WAITFORMORE) {
|
||||
loc->audio_stopped = SWITCH_TRUE;
|
||||
switch_ivr_collect_digits_callback(session, &args, timeout, 0);
|
||||
if (loc->result == RES_WAITFORMORE) {
|
||||
if (loc->potentialMatchCount == 1 && loc->completeMatch != NULL) {
|
||||
loc->result = RES_FOUND;
|
||||
} else {
|
||||
loc->result = RES_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
switch_status_t captureMenuRecord(switch_core_session_t *session, dtmf_ss_t *loc, switch_event_t *event, const char *file_path, switch_file_handle_t *fh, int max_record_len) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (switch_channel_ready(channel)) {
|
||||
switch_input_args_t args = { 0 };
|
||||
|
||||
args.input_callback = cb_on_dtmf;
|
||||
args.buf = loc;
|
||||
|
||||
if (loc->audio_stopped == SWITCH_FALSE && loc->result == RES_WAITFORMORE) {
|
||||
loc->recorded_audio = SWITCH_TRUE;
|
||||
switch_ivr_gentones(session, "%(1000, 0, 640)", 0, NULL); /* TODO Make this optional and configurable */
|
||||
status = switch_ivr_record_file(session, fh, file_path, &args, max_record_len);
|
||||
|
||||
}
|
||||
if (loc->result == RES_WAITFORMORE) {
|
||||
loc->result = RES_TIMEOUT;
|
||||
}
|
||||
|
||||
} else {
|
||||
status = SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
||||
@@ -1,30 +0,0 @@
|
||||
struct dtmf_ss {
|
||||
char dtmf_stored[128];
|
||||
int dtmf_received;
|
||||
char dtmf_accepted[16][128];
|
||||
int result;
|
||||
switch_bool_t audio_stopped;
|
||||
switch_bool_t recorded_audio;
|
||||
const char *potentialMatch;
|
||||
int potentialMatchCount;
|
||||
const char *completeMatch;
|
||||
char terminate_key;
|
||||
};
|
||||
typedef struct dtmf_ss dtmf_ss_t;
|
||||
|
||||
#define RES_WAITFORMORE 0
|
||||
#define RES_FOUND 1
|
||||
#define RES_INVALID 3
|
||||
#define RES_TIMEOUT 4
|
||||
#define RES_BREAK 5
|
||||
#define RES_RECORD 6
|
||||
#define RES_BUFFER_OVERFLOW 99
|
||||
|
||||
#define MAX_DTMF_SIZE_OPTION 32
|
||||
|
||||
switch_status_t captureMenu(switch_core_session_t *session, dtmf_ss_t *loc, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout);
|
||||
switch_status_t captureMenuRecord(switch_core_session_t *session, dtmf_ss_t *loc, switch_event_t *event, const char *file_path, switch_file_handle_t *fh, int max_record_len);
|
||||
switch_status_t captureMenuInitialize(dtmf_ss_t *loc, char **dtmf_accepted);
|
||||
|
||||
switch_status_t playbackBufferDTMF(switch_core_session_t *session, const char *macro_name, const char *data, switch_event_t *event, const char *lang, int timeout);
|
||||
|
||||
@@ -1,743 +0,0 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Marc Olivier Chouinard <mochouinard@moctel.com>
|
||||
*
|
||||
*
|
||||
* menu.c -- VoiceMail Menu
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
#include "ivr.h"
|
||||
#include "menu.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
|
||||
/* List of available menu */
|
||||
vmivr_menu_function_t menu_list[] = {
|
||||
{"std_authenticate", mtvm_menu_authenticate},
|
||||
{"std_navigator", mtvm_menu_main},
|
||||
{"std_record_name", mtvm_menu_record_name},
|
||||
{"std_set_password", mtvm_menu_set_password},
|
||||
{"std_select_greeting_slot", mtvm_menu_select_greeting_slot},
|
||||
{"std_record_greeting_with_slot", mtvm_menu_record_greeting_with_slot},
|
||||
{"std_preference", mtvm_menu_preference},
|
||||
{"std_purge", mtvm_menu_purge},
|
||||
{"std_forward", mtvm_menu_forward},
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
#define MAX_ATTEMPT 3 /* TODO Make these fields configurable */
|
||||
#define DEFAULT_IVR_TIMEOUT 3000
|
||||
|
||||
void mtvm_menu_purge(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
if (profile->id && profile->authorized) {
|
||||
if (1==1 /* TODO make Purge email on exit optional ??? */) {
|
||||
const char *cmd = switch_core_session_sprintf(session, "%s %s %s", profile->api_profile, profile->domain, profile->id);
|
||||
mt_api_execute(session, profile->api_msg_purge, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
void mtvm_menu_main(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_event_t *msg_list_params = NULL;
|
||||
size_t msg_count = 0;
|
||||
size_t current_msg = 1;
|
||||
size_t next_msg = current_msg;
|
||||
size_t previous_msg = current_msg;
|
||||
char *cmd = NULL;
|
||||
int retry;
|
||||
|
||||
/* Different switch to control playback of phrases */
|
||||
switch_bool_t initial_count_played = SWITCH_FALSE;
|
||||
switch_bool_t skip_header = SWITCH_FALSE;
|
||||
switch_bool_t msg_deleted = SWITCH_FALSE;
|
||||
switch_bool_t msg_undeleted = SWITCH_FALSE;
|
||||
switch_bool_t msg_saved = SWITCH_FALSE;
|
||||
|
||||
vmivr_menu_profile_t menu = { "std_navigator" };
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
if (!menu.event_keys_dtmf || !menu.event_phrases) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases or Keys\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get VoiceMail List And update msg count */
|
||||
cmd = switch_core_session_sprintf(session, "json %s %s %s", profile->api_profile, profile->domain, profile->id);
|
||||
msg_list_params = jsonapi2event(session, NULL, profile->api_msg_list, cmd);
|
||||
msg_count = atol(switch_event_get_header(msg_list_params,"VM-List-Count"));
|
||||
|
||||
/* TODO Add Detection of new message and notify the user */
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
switch_core_session_message_t msg = { 0 };
|
||||
char cid_buf[1024] = "";
|
||||
dtmf_ss_t loc;
|
||||
char *dtmfa[16] = { 0 };
|
||||
switch_event_t *phrase_params = NULL;
|
||||
|
||||
switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
|
||||
append_event_profile(phrase_params, profile, menu);
|
||||
|
||||
populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa);
|
||||
|
||||
previous_msg = current_msg;
|
||||
|
||||
/* Simple Protection to not go out of msg list scope */
|
||||
/* TODO: Add Prompt to notify they reached the begining or the end */
|
||||
if (next_msg == 0) {
|
||||
next_msg = 1;
|
||||
} else if (next_msg > msg_count) {
|
||||
next_msg = msg_count;
|
||||
}
|
||||
|
||||
current_msg = next_msg;
|
||||
|
||||
captureMenuInitialize(&loc, dtmfa);
|
||||
|
||||
/* Prompt related to previous Message here */
|
||||
append_event_message(session, profile, phrase_params, msg_list_params, previous_msg);
|
||||
if (msg_deleted) {
|
||||
msg_deleted = SWITCH_FALSE;
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "deleted", phrase_params, NULL, 0);
|
||||
}
|
||||
if (msg_undeleted) {
|
||||
msg_undeleted = SWITCH_FALSE;
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "undeleted", phrase_params, NULL, 0);
|
||||
}
|
||||
if (msg_saved) {
|
||||
msg_saved = SWITCH_FALSE;
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "ack"), "saved", phrase_params, NULL, 0);
|
||||
}
|
||||
|
||||
/* Prompt related the current message */
|
||||
append_event_message(session, profile, phrase_params, msg_list_params, current_msg);
|
||||
|
||||
/* Save in profile the current msg info for other menu processing AND restoration of our current position */
|
||||
switch_snprintf(cid_buf, sizeof(cid_buf), "%s|%s", switch_str_nil(switch_event_get_header(phrase_params, "VM-Message-Caller-Number")), switch_str_nil(switch_event_get_header(phrase_params, "VM-Message-Caller-Name")));
|
||||
|
||||
/* Display MSG CID/Name to caller */
|
||||
msg.from = __FILE__;
|
||||
msg.string_arg = cid_buf;
|
||||
msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
|
||||
switch_core_session_receive_message(session, &msg);
|
||||
|
||||
profile->current_msg = current_msg;
|
||||
profile->current_msg_uuid = switch_core_session_strdup(session, switch_event_get_header(phrase_params, "VM-Message-UUID"));
|
||||
|
||||
/* TODO check if msg is gone (purged by another session, notify user and auto jump to next message or something) */
|
||||
if (!skip_header) {
|
||||
if (!initial_count_played) {
|
||||
cmd = switch_core_session_sprintf(session, "json %s %s %s", profile->api_profile, profile->domain, profile->id);
|
||||
jsonapi2event(session, phrase_params, profile->api_msg_count, cmd);
|
||||
initial_count_played = SWITCH_TRUE;
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "msg_count"), NULL, phrase_params, NULL, 0);
|
||||
}
|
||||
if (msg_count > 0) {
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "say_msg_number"), NULL, phrase_params, NULL, 0);
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "say_date"), NULL, phrase_params, NULL, 0);
|
||||
}
|
||||
}
|
||||
if (msg_count > 0) {
|
||||
/* TODO Update the Read date of a message (When msg start, or when it listen compleatly ??? To be determined */
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "play_message"), NULL, phrase_params, NULL, 0);
|
||||
}
|
||||
skip_header = SWITCH_FALSE;
|
||||
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT);
|
||||
|
||||
if (loc.result == RES_TIMEOUT) {
|
||||
/* TODO Ask for the prompt Again IF retry != 0 */
|
||||
} else if (loc.result == RES_INVALID) {
|
||||
/* TODO Say invalid option, and ask for the prompt again IF retry != 0 */
|
||||
} else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */
|
||||
const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored);
|
||||
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
if (action) {
|
||||
if (!strcasecmp(action, "skip_intro")) { /* Skip Header / Play the recording again */
|
||||
skip_header = SWITCH_TRUE;
|
||||
} else if (!strcasecmp(action, "next_msg")) { /* Next Message */
|
||||
next_msg++;
|
||||
} else if (!strcasecmp(action, "prev_msg")) { /* Previous Message */
|
||||
next_msg--;
|
||||
} else if (!strcasecmp(action, "delete_msg")) { /* Delete / Undelete Message */
|
||||
const char *msg_flags = switch_event_get_header(phrase_params, "VM-Message-Flags");
|
||||
if (!msg_flags || strncasecmp(msg_flags, "delete", 6)) {
|
||||
cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID"));
|
||||
mt_api_execute(session, profile->api_msg_delete, cmd);
|
||||
|
||||
msg_deleted = SWITCH_TRUE;
|
||||
/* TODO Option for auto going to next message or just return to the menu (So user used to do 76 to delete and next message wont be confused) */
|
||||
next_msg++;
|
||||
} else {
|
||||
cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID"));
|
||||
mt_api_execute(session, profile->api_msg_undelete, cmd);
|
||||
|
||||
msg_undeleted = SWITCH_TRUE;
|
||||
}
|
||||
} else if (!strcasecmp(action, "save_msg")) { /* Save Message */
|
||||
cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(phrase_params, "VM-Message-UUID"));
|
||||
mt_api_execute(session, profile->api_msg_save, cmd);
|
||||
|
||||
msg_saved = SWITCH_TRUE;
|
||||
} else if (!strcasecmp(action, "callback")) { /* CallBack caller */
|
||||
const char *cid_num = switch_event_get_header(phrase_params, "VM-Message-Caller-Number");
|
||||
if (cid_num) {
|
||||
/* TODO add detection for private number */
|
||||
switch_core_session_execute_exten(session, cid_num, "XML", profile->domain);
|
||||
} else {
|
||||
/* TODO Some error msg that the msg doesn't contain a caller number */
|
||||
}
|
||||
} else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
|
||||
void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5);
|
||||
if (fPtr) {
|
||||
fPtr(session, profile);
|
||||
}
|
||||
} else if (!strcasecmp(action, "return")) { /* Return */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* IF the API to get the message returned us a COPY of the file locally (temp file create from a DB or from a web server), delete it */
|
||||
if (switch_true(switch_event_get_header(phrase_params, "VM-Message-Private-Local-Copy"))) {
|
||||
const char *file_path = switch_event_get_header(phrase_params, "VM-Message-File-Path");
|
||||
if (file_path && unlink(file_path) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete temp file [%s]\n", file_path);
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrase_params);
|
||||
}
|
||||
|
||||
switch_event_destroy(&msg_list_params);
|
||||
|
||||
free_profile_menu_event(&menu);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void mtvm_menu_forward(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
|
||||
vmivr_menu_profile_t menu = { "std_forward_ask_prepend" };
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
const char *prepend_filepath = NULL;
|
||||
int retry;
|
||||
switch_bool_t forward_msg = SWITCH_FALSE;
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
if (!menu.event_keys_dtmf || !menu.event_phrases) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
dtmf_ss_t loc;
|
||||
char *dtmfa[16] = { 0 };
|
||||
switch_event_t *phrase_params = NULL;
|
||||
|
||||
switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
append_event_profile(phrase_params, profile, menu);
|
||||
|
||||
populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa);
|
||||
|
||||
captureMenuInitialize(&loc, dtmfa);
|
||||
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT);
|
||||
|
||||
if (loc.result == RES_TIMEOUT) {
|
||||
/* TODO Ask for the prompt Again IF retry != 0 */
|
||||
} else if (loc.result == RES_INVALID) {
|
||||
/* TODO Say invalid option, and ask for the prompt again IF retry != 0 */
|
||||
} else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */
|
||||
const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored);
|
||||
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
if (action) {
|
||||
if (!strcasecmp(action, "return")) { /* Return to the previous menu */
|
||||
retry = -1;
|
||||
forward_msg = SWITCH_FALSE;
|
||||
} else if (!strcasecmp(action, "prepend")) { /* Prepend record msg */
|
||||
vmivr_menu_profile_t sub_menu = { "std_record_message" };
|
||||
char *tmp_filepath = generate_random_file_name(session, "protovm", "wav" /* TODO make it configurable */);
|
||||
switch_status_t status;
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &sub_menu);
|
||||
|
||||
status = mtvm_menu_record(session, profile, sub_menu, tmp_filepath);
|
||||
|
||||
if (status == SWITCH_STATUS_SUCCESS) {
|
||||
//char *cmd = switch_core_session_sprintf(session, "%s %s %s %d %s", profile->api_profile, profile->domain, profile->id, gnum, tmp_filepath);
|
||||
//char *str_num = switch_core_session_sprintf(session, "%d", gnum);
|
||||
//mt_api_execute(session, profile->api_pref_greeting_set, cmd);
|
||||
//playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0);
|
||||
prepend_filepath = tmp_filepath;
|
||||
retry = -1;
|
||||
forward_msg = SWITCH_TRUE;
|
||||
} else {
|
||||
/* TODO Error Recording msg */
|
||||
}
|
||||
free_profile_menu_event(&sub_menu);
|
||||
|
||||
} else if (!strcasecmp(action, "forward")) { /* Forward without prepend msg */
|
||||
retry = -1;
|
||||
forward_msg = SWITCH_TRUE;
|
||||
} else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
|
||||
void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5);
|
||||
if (fPtr) {
|
||||
fPtr(session, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrase_params);
|
||||
|
||||
|
||||
}
|
||||
/* Ask Extension to Forward */
|
||||
if (forward_msg) {
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
const char *id = NULL;
|
||||
vmivr_menu_profile_t sub_menu = { "std_forward_ask_extension" };
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &sub_menu);
|
||||
|
||||
id = mtvm_menu_get_input_set(session, profile, sub_menu, "X.");
|
||||
if (id) {
|
||||
const char *cmd = switch_core_session_sprintf(session, "%s %s %s %s %s %s %s%s%s", profile->api_profile, profile->domain, profile->id, profile->current_msg_uuid, profile->domain, id, prepend_filepath?" ":"", prepend_filepath?prepend_filepath:"" );
|
||||
if (mt_api_execute(session, profile->api_msg_forward, cmd) == SWITCH_STATUS_SUCCESS) {
|
||||
playbackBufferDTMF(session, switch_event_get_header(sub_menu.event_phrases, "ack"), "saved", NULL, NULL, 0);
|
||||
retry = -1;
|
||||
} else {
|
||||
playbackBufferDTMF(session, switch_event_get_header(sub_menu.event_phrases, "invalid_extension"), NULL, NULL, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
/* TODO Prompt about input not valid */
|
||||
}
|
||||
free_profile_menu_event(&sub_menu);
|
||||
/* TODO add Confirmation of the transfered number */
|
||||
}
|
||||
/* TODO Ask if we want to transfer the msg to more person */
|
||||
|
||||
}
|
||||
|
||||
free_profile_menu_event(&menu);
|
||||
}
|
||||
|
||||
|
||||
void mtvm_menu_record_name(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
switch_status_t status;
|
||||
vmivr_menu_profile_t menu = { "std_record_name" };
|
||||
|
||||
char *tmp_filepath = generate_random_file_name(session, "protovm", "wav" /* TODO make it configurable */);
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
status = mtvm_menu_record(session, profile, menu, tmp_filepath);
|
||||
|
||||
if (status == SWITCH_STATUS_SUCCESS) {
|
||||
char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, tmp_filepath);
|
||||
mt_api_execute(session, profile->api_pref_recname_set, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void mtvm_menu_set_password(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
char *password;
|
||||
vmivr_menu_profile_t menu = { "std_set_password" };
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
password = mtvm_menu_get_input_set(session, profile, menu, "XXX." /* TODO Conf Min 3 Digit */);
|
||||
|
||||
/* TODO Add Prompts to tell if password was set and if it was not */
|
||||
if (password) {
|
||||
char *cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, profile->id, password);
|
||||
mt_api_execute(session, profile->api_pref_password_set, cmd);
|
||||
|
||||
}
|
||||
|
||||
free_profile_menu_event(&menu);
|
||||
}
|
||||
|
||||
void mtvm_menu_authenticate(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
vmivr_menu_profile_t menu = { "std_authenticate" };
|
||||
int retry;
|
||||
const char *auth_var = NULL;
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
if (profile->id && (auth_var = switch_channel_get_variable(channel, "voicemail_authorized")) && switch_true(auth_var)) {
|
||||
profile->authorized = SWITCH_TRUE;
|
||||
}
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0 && profile->authorized == SWITCH_FALSE; retry--) {
|
||||
const char *id = profile->id, *password = NULL;
|
||||
char *cmd = NULL;
|
||||
|
||||
if (!id) {
|
||||
vmivr_menu_profile_t sub_menu = { "std_authenticate_ask_user" };
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &sub_menu);
|
||||
|
||||
id = mtvm_menu_get_input_set(session, profile, sub_menu, "X." /* TODO Conf Min 3 Digit */);
|
||||
free_profile_menu_event(&sub_menu);
|
||||
}
|
||||
if (!password) {
|
||||
vmivr_menu_profile_t sub_menu = { "std_authenticate_ask_password" };
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &sub_menu);
|
||||
|
||||
password = mtvm_menu_get_input_set(session, profile, sub_menu, "X." /* TODO Conf Min 3 Digit */);
|
||||
free_profile_menu_event(&sub_menu);
|
||||
}
|
||||
cmd = switch_core_session_sprintf(session, "%s %s %s %s", profile->api_profile, profile->domain, id, password);
|
||||
|
||||
if (mt_api_execute(session, profile->api_auth_login, cmd) == SWITCH_STATUS_SUCCESS) {
|
||||
profile->id = id;
|
||||
profile->authorized = SWITCH_TRUE;
|
||||
} else {
|
||||
playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "fail_auth"), NULL, NULL, NULL, 0);
|
||||
}
|
||||
}
|
||||
free_profile_menu_event(&menu);
|
||||
}
|
||||
|
||||
void mtvm_menu_select_greeting_slot(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
vmivr_menu_profile_t menu = { "std_select_greeting_slot" };
|
||||
|
||||
const char *result;
|
||||
int gnum = -1;
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
result = mtvm_menu_get_input_set(session, profile, menu, "X");
|
||||
|
||||
if (result)
|
||||
gnum = atoi(result);
|
||||
if (gnum != -1) {
|
||||
char * cmd = switch_core_session_sprintf(session, "%s %s %s %d", profile->api_profile, profile->domain, profile->id, gnum);
|
||||
if (mt_api_execute(session, profile->api_pref_greeting_set, cmd) == SWITCH_STATUS_SUCCESS) {
|
||||
char *str_num = switch_core_session_sprintf(session, "%d", gnum);
|
||||
playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0);
|
||||
} else {
|
||||
playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "invalid_slot"), NULL, NULL, NULL, 0);
|
||||
}
|
||||
}
|
||||
free_profile_menu_event(&menu);
|
||||
}
|
||||
|
||||
void mtvm_menu_record_greeting_with_slot(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
|
||||
vmivr_menu_profile_t menu = { "std_record_greeting_with_slot" };
|
||||
|
||||
const char *result;
|
||||
int gnum = -1;
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
result = mtvm_menu_get_input_set(session, profile, menu, "X");
|
||||
|
||||
if (result)
|
||||
gnum = atoi(result);
|
||||
|
||||
/* If user entered 0, we don't accept it */
|
||||
if (gnum > 0) {
|
||||
vmivr_menu_profile_t sub_menu = { "std_record_greeting" };
|
||||
char *tmp_filepath = generate_random_file_name(session, "protovm", "wav" /* TODO make it configurable */);
|
||||
switch_status_t status;
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &sub_menu);
|
||||
|
||||
status = mtvm_menu_record(session, profile, sub_menu, tmp_filepath);
|
||||
|
||||
if (status == SWITCH_STATUS_SUCCESS) {
|
||||
char *cmd = switch_core_session_sprintf(session, "%s %s %s %d %s", profile->api_profile, profile->domain, profile->id, gnum, tmp_filepath);
|
||||
char *str_num = switch_core_session_sprintf(session, "%d", gnum);
|
||||
mt_api_execute(session, profile->api_pref_greeting_set, cmd);
|
||||
playbackBufferDTMF(session, switch_event_get_header(menu.event_phrases, "selected_slot"), str_num, NULL, NULL, 0);
|
||||
}
|
||||
free_profile_menu_event(&sub_menu);
|
||||
|
||||
}
|
||||
|
||||
free_profile_menu_event(&menu);
|
||||
|
||||
}
|
||||
|
||||
void mtvm_menu_preference(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
int retry;
|
||||
|
||||
vmivr_menu_profile_t menu = { "std_preference" };
|
||||
|
||||
/* Initialize Menu Configs */
|
||||
populate_profile_menu_event(profile, &menu);
|
||||
|
||||
if (!menu.event_keys_dtmf || !menu.event_phrases) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
dtmf_ss_t loc;
|
||||
char *dtmfa[16] = { 0 };
|
||||
switch_event_t *phrase_params = NULL;
|
||||
|
||||
switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
append_event_profile(phrase_params, profile, menu);
|
||||
|
||||
populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa);
|
||||
|
||||
captureMenuInitialize(&loc, dtmfa);
|
||||
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT);
|
||||
|
||||
if (loc.result == RES_TIMEOUT) {
|
||||
/* TODO Ask for the prompt Again IF retry != 0 */
|
||||
} else if (loc.result == RES_INVALID) {
|
||||
/* TODO Say invalid option, and ask for the prompt again IF retry != 0 */
|
||||
} else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */
|
||||
const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored);
|
||||
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
if (action) {
|
||||
if (!strcasecmp(action, "return")) { /* Return to the previous menu */
|
||||
retry = -1;
|
||||
} else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
|
||||
void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5);
|
||||
if (fPtr) {
|
||||
fPtr(session, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrase_params);
|
||||
}
|
||||
|
||||
free_profile_menu_event(&menu);
|
||||
}
|
||||
|
||||
char *mtvm_menu_get_input_set(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *input_mask) {
|
||||
char *result = NULL;
|
||||
int retry;
|
||||
const char *terminate_key = NULL;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (!menu.event_keys_dtmf || !menu.event_phrases) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys : %s\n", menu.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
terminate_key = switch_event_get_header(menu.event_keys_action, "ivrengine:terminate_entry");
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
dtmf_ss_t loc;
|
||||
char *dtmfa[16] = { 0 };
|
||||
int i;
|
||||
switch_event_t *phrase_params = NULL;
|
||||
|
||||
switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
append_event_profile(phrase_params, profile, menu);
|
||||
|
||||
populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa);
|
||||
|
||||
/* Find the last entry and append this one to it */
|
||||
for (i=0; dtmfa[i] && i < 16; i++){
|
||||
}
|
||||
dtmfa[i] = (char *) input_mask;
|
||||
|
||||
captureMenuInitialize(&loc, dtmfa);
|
||||
if (terminate_key) {
|
||||
loc.terminate_key = terminate_key[0];
|
||||
}
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "instructions"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT);
|
||||
|
||||
if (loc.result == RES_TIMEOUT) {
|
||||
/* TODO Ask for the prompt Again IF retry != 0 */
|
||||
} else if (loc.result == RES_INVALID) {
|
||||
/* TODO Say invalid option, and ask for the prompt again IF retry != 0 */
|
||||
} else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */
|
||||
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
if (!strncasecmp(loc.completeMatch, input_mask, 1)) {
|
||||
result = switch_core_session_strdup(session, loc.dtmf_stored);
|
||||
retry = -1;
|
||||
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrase_params);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
switch_status_t mtvm_menu_record(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *file_name) {
|
||||
switch_status_t status = SWITCH_STATUS_FALSE;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
int retry;
|
||||
|
||||
switch_bool_t record_prompt = SWITCH_TRUE;
|
||||
switch_bool_t listen_recording = SWITCH_FALSE;
|
||||
switch_bool_t play_instruction = SWITCH_TRUE;
|
||||
|
||||
if (!menu.event_keys_dtmf || !menu.event_phrases) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Menu Phrases and Keys\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
for (retry = MAX_ATTEMPT; switch_channel_ready(channel) && retry > 0; retry--) {
|
||||
dtmf_ss_t loc;
|
||||
|
||||
char *dtmfa[16] = { 0 };
|
||||
switch_event_t *phrase_params = NULL;
|
||||
switch_file_handle_t fh = { 0 };
|
||||
|
||||
/* TODO Make the following configurable */
|
||||
fh.thresh = 200;
|
||||
fh.silence_hits = 4;
|
||||
//fh.samplerate = 8000;
|
||||
|
||||
|
||||
switch_event_create(&phrase_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
append_event_profile(phrase_params, profile, menu);
|
||||
|
||||
populate_dtmfa_from_event(phrase_params, profile, menu, dtmfa);
|
||||
|
||||
captureMenuInitialize(&loc, dtmfa);
|
||||
if (record_prompt) {
|
||||
if (play_instruction) {
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "instructions"), NULL, phrase_params, NULL, 0);
|
||||
}
|
||||
play_instruction = SWITCH_TRUE;
|
||||
|
||||
captureMenuRecord(session, &loc, phrase_params, file_name, &fh, 30 /* TODO Make max recording configurable */);
|
||||
} else {
|
||||
if (listen_recording) {
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Record-File-Path", "%s", file_name);
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "play_recording"), NULL, phrase_params, NULL, 0);
|
||||
listen_recording = SWITCH_FALSE;
|
||||
|
||||
}
|
||||
captureMenu(session, &loc, switch_event_get_header(menu.event_phrases, "menu_options"), NULL, phrase_params, NULL, DEFAULT_IVR_TIMEOUT);
|
||||
}
|
||||
|
||||
if (loc.recorded_audio) {
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
/* TODO Check if message is too short */
|
||||
|
||||
record_prompt = SWITCH_FALSE;
|
||||
|
||||
} else if (loc.result == RES_TIMEOUT) {
|
||||
/* TODO Ask for the prompt Again IF retry != 0 */
|
||||
} else if (loc.result == RES_INVALID) {
|
||||
/* TODO Say invalid option, and ask for the prompt again IF retry != 0 */
|
||||
} else if (loc.result == RES_FOUND) { /* Matching DTMF Key Pressed */
|
||||
const char *action = switch_event_get_header(menu.event_keys_dtmf, loc.dtmf_stored);
|
||||
|
||||
/* Reset the try count */
|
||||
retry = MAX_ATTEMPT;
|
||||
|
||||
if (action) {
|
||||
if (!strcasecmp(action, "listen")) { /* Listen */
|
||||
listen_recording = SWITCH_TRUE;
|
||||
|
||||
} else if (!strcasecmp(action, "save")) {
|
||||
retry = -1;
|
||||
/* TODO ALLOW SAVE ONLY IF FILE IS RECORDED AND HIGHER THAN MIN SIZE */
|
||||
status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
} else if (!strcasecmp(action, "rerecord")) {
|
||||
record_prompt = SWITCH_TRUE;
|
||||
|
||||
} else if (!strcasecmp(action, "skip_instruction")) { /* Skip Recording Greeting */
|
||||
play_instruction = SWITCH_FALSE;
|
||||
|
||||
} else if (!strncasecmp(action, "menu:", 5)) { /* Sub Menu */
|
||||
void (*fPtr)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(action+5);
|
||||
if (fPtr) {
|
||||
fPtr(session, profile);
|
||||
}
|
||||
} else if (!strcasecmp(action, "return")) { /* Return */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrase_params);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void (*mtvm_get_menu_function(const char *menu_name))(switch_core_session_t *session, vmivr_profile_t *profile) {
|
||||
int i = 0;
|
||||
|
||||
if (menu_name) {
|
||||
for (i=0; menu_list[i].name ; i++) {
|
||||
if (!strcasecmp(menu_list[i].name, menu_name)) {
|
||||
return menu_list[i].pt2Func;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef _MENU_H_
|
||||
#define _MENU_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
void mtvm_menu_purge(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_authenticate(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_main(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_record_name(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_set_password(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_select_greeting_slot(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_record_greeting_with_slot(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_preference(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
void mtvm_menu_forward(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
|
||||
switch_status_t mtvm_menu_record(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *file_name);
|
||||
char *mtvm_menu_get_input_set(switch_core_session_t *session, vmivr_profile_t *profile, vmivr_menu_profile_t menu, const char *input_mask);
|
||||
|
||||
|
||||
struct vmivr_menu_function {
|
||||
const char *name;
|
||||
void (*pt2Func)(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
|
||||
};
|
||||
typedef struct vmivr_menu_function vmivr_menu_function_t;
|
||||
|
||||
extern vmivr_menu_function_t menu_list[];
|
||||
|
||||
void (*mtvm_get_menu_function(const char *menu_name))(switch_core_session_t *session, vmivr_profile_t *profile);
|
||||
|
||||
#endif /* _MENU_H_ */
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Marc Olivier Chouinard <mochouinard@moctel.com>
|
||||
*
|
||||
*
|
||||
* mod_protovm.c -- MT VoiceMail System
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "menu.h"
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_protovm_shutdown);
|
||||
SWITCH_MODULE_RUNTIME_FUNCTION(mod_protovm_runtime);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_protovm_load);
|
||||
|
||||
/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
|
||||
* Defines a switch_loadable_module_function_table_t and a static const char[] modname
|
||||
*/
|
||||
SWITCH_MODULE_DEFINITION(mod_protovm, mod_protovm_load, mod_protovm_shutdown, NULL);
|
||||
|
||||
|
||||
#define MTVM_DESC "protovm"
|
||||
#define MTVM_USAGE "<check> profile domain [id]"
|
||||
|
||||
SWITCH_STANDARD_APP(protovm_function)
|
||||
{
|
||||
const char *id = NULL;
|
||||
const char *domain = NULL;
|
||||
const char *profile_name = NULL;
|
||||
vmivr_profile_t *profile = NULL;
|
||||
int argc = 0;
|
||||
char *argv[6] = { 0 };
|
||||
char *mydata = NULL;
|
||||
|
||||
if (!zstr(data)) {
|
||||
mydata = switch_core_session_strdup(session, data);
|
||||
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
||||
}
|
||||
|
||||
if (argv[1])
|
||||
profile_name = argv[1];
|
||||
|
||||
if (argv[2])
|
||||
domain = argv[2];
|
||||
|
||||
if (!strcasecmp(argv[0], "check")) {
|
||||
if (argv[3])
|
||||
id = argv[3];
|
||||
|
||||
if (domain && profile_name) {
|
||||
profile = get_profile(session, profile_name);
|
||||
|
||||
if (profile) {
|
||||
void (*fPtrAuth)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_auth);
|
||||
void (*fPtrMain)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_main);
|
||||
void (*fPtrTerminate)(switch_core_session_t *session, vmivr_profile_t *profile) = mtvm_get_menu_function(profile->menu_check_terminate);
|
||||
|
||||
profile->domain = domain;
|
||||
profile->id = id;
|
||||
|
||||
if (fPtrAuth && !profile->authorized) {
|
||||
fPtrAuth(session, profile);
|
||||
}
|
||||
|
||||
if (fPtrMain && profile->authorized) {
|
||||
fPtrMain(session, profile);
|
||||
}
|
||||
if (fPtrTerminate) {
|
||||
fPtrTerminate(session, profile);
|
||||
}
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile '%s' not found\n", profile_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Macro expands to: switch_status_t mod_protovm_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_protovm_load)
|
||||
{
|
||||
switch_application_interface_t *app_interface;
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "protovm", "protovm", MTVM_DESC, protovm_function, MTVM_USAGE, SAF_NONE);
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the system shuts down
|
||||
Macro expands to: switch_status_t mod_protovm_shutdown() */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_protovm_shutdown)
|
||||
{
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
||||
@@ -1,176 +0,0 @@
|
||||
<configuration name="protovm.conf" description="ProtoVoicemailIVR">
|
||||
<profiles>
|
||||
<profile name="default">
|
||||
<apis>
|
||||
<api name="auth_login" value="vm_fsdb_auth_login" />
|
||||
<api name="msg_list" value="vm_fsdb_msg_list" />
|
||||
<api name="msg_count" value="vm_fsdb_msg_count" />
|
||||
<api name="msg_delete" value="vm_fsdb_msg_delete" />
|
||||
<api name="msg_undelete" value="vm_fsdb_msg_undelete" />
|
||||
<api name="msg_save" value="vm_fsdb_msg_save" />
|
||||
<api name="msg_purge" value="vm_fsdb_msg_purge" />
|
||||
<api name="msg_get" value="vm_fsdb_msg_get" />
|
||||
<api name="msg_forward" value="vm_fsdb_msg_forward" />
|
||||
<api name="pref_greeting_set" value="vm_fsdb_pref_greeting_set" />
|
||||
<api name="pref_recname_set" value="vm_fsdb_pref_recname_set" />
|
||||
<api name="pref_password_set" value="vm_fsdb_pref_password_set" />
|
||||
</apis>
|
||||
<menus>
|
||||
<menu name="std_authenticate">
|
||||
<phrases>
|
||||
<phrase name="fail_auth" value="fail_auth@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_authenticate_ask_user">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="enter_id@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="#" action="ivrengine:terminate_entry" variable="VM-Key-Terminator" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_authenticate_ask_password">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="enter_pass@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="#" action="ivrengine:terminate_entry" variable="VM-Key-Terminator" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_navigator">
|
||||
<phrases>
|
||||
<phrase name="msg_count" value="message_count@protovm" />
|
||||
<phrase name="say_date" value="say_date_event@protovm" />
|
||||
<phrase name="say_msg_number" value="say_message_number@protovm" />
|
||||
<phrase name="menu_options" value="listen_file_check@protovm" />
|
||||
<phrase name="ack" value="ack@protovm" />
|
||||
<phrase name="play_message" value="play_message@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="skip_intro" variable="VM-Key-Main-Listen-File" />
|
||||
<key dtmf="6" action="next_msg" variable="VM-Key-Main-Next-Msg" />
|
||||
<key dtmf="4" action="prev_msg" />
|
||||
<key dtmf="7" action="delete_msg" variable="VM-Key-Main-Delete-File" /> <!-- Same key for undelete if it already deleted -->
|
||||
<key dtmf="8" action="menu:std_forward" variable="VM-Key-Main-Forward" />
|
||||
<key dtmf="3" action="save_msg" variable="VM-Key-Main-Save-File" />
|
||||
<key dtmf="2" action="callback" variable="VM-Key-Main-Callback" />
|
||||
<key dtmf="5" action="menu:std_preference" />
|
||||
<key dtmf="#" action="return" /> <!-- TODO Might Conflict with future fast-forward -->
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_preference">
|
||||
<phrases>
|
||||
<phrase name="menu_options" value="config_menu@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="menu:std_record_greeting_with_slot" variable="VM-Key-Record-Greeting" />
|
||||
<key dtmf="2" action="menu:std_select_greeting_slot" variable="VM-Key-Choose-Greeting" />
|
||||
<key dtmf="3" action="menu:std_record_name" variable="VM-Key-Record-Name" />
|
||||
<key dtmf="6" action="menu:std_set_password" variable="VM-Key-Change-Password" />
|
||||
<key dtmf="#" action="return" variable="VM-Key-Main-Menu" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_record_greeting">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="record_greeting@protovm" />
|
||||
<phrase name="play_recording" value="play_recording@protovm" />
|
||||
<phrase name="menu_options" value="record_file_check@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="listen" variable="VM-Key-Listen-File" />
|
||||
<key dtmf="3" action="save" variable="VM-Key-Save-File" />
|
||||
<key dtmf="4" action="rerecord" variable="VM-Key-ReRecord-File" />
|
||||
<key dtmf="#" action="skip_instruction" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
|
||||
<menu name="std_record_name">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="record_name@protovm" />
|
||||
<phrase name="play_recording" value="play_recording@protovm" />
|
||||
<phrase name="menu_options" value="record_file_check@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="listen" variable="VM-Key-Listen-File" />
|
||||
<key dtmf="3" action="save" variable="VM-Key-Save-File" />
|
||||
<key dtmf="4" action="rerecord" variable="VM-Key-ReRecord-File" />
|
||||
<key dtmf="#" action="skip_instruction" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_record_message">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="record_message@protovm" />
|
||||
<phrase name="play_recording" value="play_recording@protovm" />
|
||||
<phrase name="menu_options" value="record_file_check@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="listen" variable="VM-Key-Listen-File" />
|
||||
<key dtmf="3" action="save" variable="VM-Key-Save-File" />
|
||||
<key dtmf="4" action="rerecord" variable="VM-Key-ReRecord-File" />
|
||||
<key dtmf="#" action="skip_instruction" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_forward_ask_prepend">
|
||||
<phrases>
|
||||
<phrase name="menu_options" value="forward_ask_prepend@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="1" action="prepend" variable="VM-Key-Prepend" />
|
||||
<key dtmf="8" action="forward" variable="VM-Key-Forward" />
|
||||
<key dtmf="#" action="return" variable="VM-Key-Return" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_forward_ask_extension">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="forward_ask_extension@protovm" />
|
||||
<phrase name="ack" value="ack@protovm" />
|
||||
<phrase name="invalid_extension" value="invalid_extension@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="#" action="ivrengine:terminate_entry" variable="VM-Key-Terminator" />
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_select_greeting_slot">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="choose_greeting@protovm" />
|
||||
<phrase name="invalid_slot" value="choose_greeting_fail@protovm" />
|
||||
<phrase name="selected_slot" value="greeting_selected@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_record_greeting_with_slot">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="choose_greeting@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
</keys>
|
||||
</menu>
|
||||
|
||||
<menu name="std_set_password">
|
||||
<phrases>
|
||||
<phrase name="instructions" value="enter_pass@protovm" />
|
||||
</phrases>
|
||||
<keys>
|
||||
<key dtmf="#" action="ivrengine:terminate_entry" variable="VM-Key-Terminator" />
|
||||
</keys>
|
||||
</menu>
|
||||
</menus>
|
||||
</profile>
|
||||
</profiles>
|
||||
</configuration>
|
||||
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
<include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
<macros name="protovm" sound-prefix="$${sounds_dir}/en/us/callie">
|
||||
<macro name="press_key">
|
||||
<input pattern="^(.*):(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="$2"/>
|
||||
<action function="play-file" data="voicemail/vm-press.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="plurial_msg">
|
||||
<input pattern="^[01]:(.*):(.*)$" break_on_match="true">
|
||||
<match>
|
||||
<action function="play-file" data="$1"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^.*:(.*):(.*)$" break_on_match="true">
|
||||
<match>
|
||||
<action function="play-file" data="$2"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="enter_id">
|
||||
<input pattern="(.+)">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-enter_id.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
<nomatch>
|
||||
<action function="play-file" data="voicemail/vm-enter_id.wav"/>
|
||||
<action function="say" data="${VM-Key-Terminator}" method="pronounced" type="name_spelled"/>
|
||||
</nomatch>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
|
||||
<macro name="enter_pass">
|
||||
<input pattern="(.+)">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-enter_pass.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
<nomatch>
|
||||
<action function="play-file" data="voicemail/vm-enter_pass.wav"/>
|
||||
<action function="say" data="${VM-Key-Terminator}" method="pronounced" type="name_spelled"/>
|
||||
</nomatch>
|
||||
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="fail_auth">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-fail_auth.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="hello">
|
||||
<input>
|
||||
<match>
|
||||
<!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="goodbye">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-goodbye.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="abort">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-abort.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="message_count">
|
||||
<input field="${VM-Total-New-Urgent-Messages}" pattern="^(0)$">
|
||||
<nomatch>
|
||||
<action function="play-file" data="voicemail/vm-you_have.wav"/>
|
||||
<action function="say" data="${VM-Total-New-Urgent-Messages}" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="voicemail/vm-urgent-new.wav"/>
|
||||
<action function="phrase" phrase="plurial_msg@protovm" data="${VM-Total-New-Urgent-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
|
||||
</nomatch>
|
||||
</input>
|
||||
<input field="${VM-Total-New-Messages}" pattern="^(\d+)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-you_have.wav"/>
|
||||
<action function="say" data="${VM-Total-New-Messages}" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="voicemail/vm-new.wav"/>
|
||||
<action function="phrase" phrase="plurial_msg@protovm" data="${VM-Total-New-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input field="${VM-Total-Saved-Messages}" pattern="^(0)$">
|
||||
<nomatch>
|
||||
<action function="play-file" data="currency/and.wav"/>
|
||||
<action function="say" data="${VM-Total-Saved-Messages}" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="voicemail/vm-saved.wav"/>
|
||||
<action function="phrase" phrase="plurial_msg@protovm" data="${VM-Total-Saved-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
|
||||
</nomatch>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="menu">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Play-New-Messages}:voicemail/vm-listen_new.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Play-Saved-Messages}:voicemail/vm-listen_saved.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Config-Menu}:voicemail/vm-advanced.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Terminator}:voicemail/vm-to_exit.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="config_menu">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Record-Greeting}:voicemail/vm-to_record_greeting.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Choose-Greeting}:voicemail/vm-choose_greeting.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Record-Name}:voicemail/vm-record_name2.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Change-Password}:voicemail/vm-change_password.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Menu}:voicemail/vm-main_menu.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="record_name">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-record_name1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="forward_ask_prepend">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Prepend}:voicemail/vm-forward_add_intro.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Forward}:voicemail/vm-send_message_now.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="forward_ask_extension">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
|
||||
<!-- <action function="phrase" phrase="play-file" data="voicemail/vm-followed_by.wav"/>
|
||||
<action function="say" data="${VM-Key-Terminate}" method="pronounced" type="name_spelled"/>-->
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="record_file_check">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Listen-File}:voicemail/vm-listen_to_recording.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Save-File}:voicemail/vm-save_recording.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Record-File}:voicemail/vm-rerecord.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
|
||||
<macro name="record_urgent_check">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Urgent}:voicemail/vm-mark-urgent.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Terminator}:voicemail/vm-continue.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="forward_prepend">
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Prepend}:voicemail/vm-forward_add_intro.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Forward}:voicemail/vm-send_message_now.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="forward_message_enter_extension">
|
||||
<input pattern="^([0-9#*])$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-followed_by.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="invalid_extension">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="listen_file_check">
|
||||
<input>
|
||||
<match>
|
||||
<!--<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Next-Msg}:voicemail/vm-for_next_msg.wav"/>--> <!-- Not existant in callie recordings -->
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Listen-File}:voicemail/vm-listen_to_recording.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Save-File}:voicemail/vm-save_recording.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Delete-File}:voicemail/vm-delete_recording.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Forward}:voicemail/vm-to_forward.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input field="${VM-Message-Email}" pattern="^$">
|
||||
<nomatch>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Email}:voicemail/vm-forward_to_email.wav"/>
|
||||
</nomatch>
|
||||
</input>
|
||||
<input>
|
||||
<match>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Callback}:voicemail/vm-return_call.wav"/>
|
||||
<action function="phrase" phrase="press_key@protovm" data="${VM-Key-Main-Forward}:voicemail/vm-to_forward.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="choose_greeting">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="choose_greeting_fail">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="record_greeting">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-record_greeting.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="record_message">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-record_message.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="greeting_selected">
|
||||
<input pattern="^(\d+)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-greeting.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="voicemail/vm-selected.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="play_greeting">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-person.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="voicemail/vm-not_available.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_number">
|
||||
<input pattern="^(\d+)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_message_number">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-${VM-Message-Type}.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-message_number.wav"/>
|
||||
<action function="say" data="${VM-Message-Number}" method="pronounced" type="items"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_phone_number">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_name">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
<!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
|
||||
<macro name="ack">
|
||||
<input pattern="^(too-small)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-too-small.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(undeleted)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-message.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-$1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(deleted)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-message.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-$1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(saved)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-message.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-$1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(emailed)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-message.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-$1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(marked-urgent)$">
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-message.wav"/>
|
||||
<action function="play-file" data="voicemail/vm-$1.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_date">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="short_date_time"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="say_date_event">
|
||||
<input>
|
||||
<match>
|
||||
<action function="say" data="${VM-Message-Received-Epoch}" method="pronounced" type="short_date_time"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="play_message">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="${VM-Message-File-Path}"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="play_recording">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="${VM-Record-File-Path}"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="disk_quota_exceeded">
|
||||
<input>
|
||||
<match>
|
||||
<action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
</macros>
|
||||
</include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Marc Olivier Chouinard <mochouinard@moctel.com>
|
||||
*
|
||||
*
|
||||
* utils.c -- MT VoiceMail / Different utility that might need to go into the core (after cleanup)
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
switch_status_t mt_merge_media_files(const char** inputs, const char *output) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_file_handle_t fh_output = { 0 };
|
||||
int channels = 1;
|
||||
int rate = 8000; /* TODO Make this configurable */
|
||||
int j = 0;
|
||||
|
||||
if (switch_core_file_open(&fh_output, output, channels, rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open %s\n", output);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (j = 0; inputs[j] != NULL && j < 128 && status == SWITCH_STATUS_SUCCESS; j++) {
|
||||
switch_file_handle_t fh_input = { 0 };
|
||||
char buf[2048];
|
||||
switch_size_t len = sizeof(buf) / 2;
|
||||
|
||||
if (switch_core_file_open(&fh_input, inputs[j], channels, rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open %s\n", inputs[j]);
|
||||
status = SWITCH_STATUS_GENERR;
|
||||
break;
|
||||
}
|
||||
|
||||
while (switch_core_file_read(&fh_input, buf, &len) == SWITCH_STATUS_SUCCESS) {
|
||||
if (switch_core_file_write(&fh_output, buf, &len) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write error\n");
|
||||
status = SWITCH_STATUS_GENERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fh_input.file_interface) {
|
||||
switch_core_file_close(&fh_input);
|
||||
}
|
||||
}
|
||||
|
||||
if (fh_output.file_interface) {
|
||||
switch_core_file_close(&fh_output);
|
||||
}
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
switch_event_t *jsonapi2event(switch_core_session_t *session, switch_event_t *apply_event, const char *api, const char *data) {
|
||||
switch_event_t *phrases_event = NULL;
|
||||
switch_stream_handle_t stream = { 0 };
|
||||
SWITCH_STANDARD_STREAM(stream);
|
||||
switch_api_execute(api, data, session, &stream);
|
||||
switch_event_create_json(&phrases_event, (char *) stream.data);
|
||||
switch_safe_free(stream.data);
|
||||
|
||||
if (apply_event) {
|
||||
switch_event_header_t *hp;
|
||||
for (hp = phrases_event->headers; hp; hp = hp->next) {
|
||||
if (!strncasecmp(hp->name, "VM-", 3)) {
|
||||
switch_event_add_header(apply_event, SWITCH_STACK_BOTTOM, hp->name, "%s", hp->value);
|
||||
}
|
||||
}
|
||||
switch_event_destroy(&phrases_event);
|
||||
phrases_event = apply_event;
|
||||
|
||||
}
|
||||
|
||||
return phrases_event;
|
||||
}
|
||||
|
||||
char *generate_random_file_name(switch_core_session_t *session, const char *mod_name, char *file_extension) {
|
||||
char rand_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = "";
|
||||
switch_uuid_t srand_uuid;
|
||||
|
||||
switch_uuid_get(&srand_uuid);
|
||||
switch_uuid_format(rand_uuid, &srand_uuid);
|
||||
|
||||
return switch_core_session_sprintf(session, "%s%s%s_%s.%s", SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, mod_name, rand_uuid, file_extension);
|
||||
|
||||
}
|
||||
|
||||
switch_status_t mt_api_execute(switch_core_session_t *session, const char *apiname, const char *arguments) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
switch_stream_handle_t stream = { 0 };
|
||||
|
||||
SWITCH_STANDARD_STREAM(stream);
|
||||
switch_api_execute(apiname, arguments, session, &stream);
|
||||
if (!strncasecmp(stream.data, "-ERR", 4)) {
|
||||
status = SWITCH_STATUS_GENERR;
|
||||
}
|
||||
switch_safe_free(stream.data);
|
||||
return status;
|
||||
}
|
||||
|
||||
void append_event_profile(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu) {
|
||||
/* Used for some appending function */
|
||||
if (profile->name && profile->id && profile->domain) {
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Profile", "%s", profile->name);
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Account-ID", "%s", profile->id);
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Account-Domain", "%s", profile->domain);
|
||||
}
|
||||
}
|
||||
|
||||
void populate_dtmfa_from_event(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu, char **dtmfa) {
|
||||
int i = 0;
|
||||
if (menu.event_keys_dtmf) {
|
||||
switch_event_header_t *hp;
|
||||
|
||||
for (hp = menu.event_keys_dtmf->headers; hp; hp = hp->next) {
|
||||
if (strlen(hp->name) < 3 && hp->value) { /* TODO This is a hack to discard default FS Events ! */
|
||||
const char *varphrasename = switch_event_get_header(menu.event_keys_varname, hp->value);
|
||||
dtmfa[i++] = hp->name;
|
||||
|
||||
if (varphrasename && !zstr(varphrasename)) {
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, varphrasename, "%s", hp->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void append_event_message(switch_core_session_t *session, vmivr_profile_t *profile, switch_event_t *phrase_params, switch_event_t *msg_list_event, size_t current_msg) {
|
||||
|
||||
char *varname;
|
||||
char *apicmd;
|
||||
|
||||
varname = switch_mprintf("VM-List-Message-%" SWITCH_SIZE_T_FMT "-UUID", current_msg);
|
||||
apicmd = switch_mprintf("json %s %s %s %s", profile->api_profile, profile->domain, profile->id, switch_event_get_header(msg_list_event, varname));
|
||||
|
||||
switch_safe_free(varname);
|
||||
|
||||
jsonapi2event(session, phrase_params, profile->api_msg_get, apicmd);
|
||||
|
||||
/* TODO Set these 2 header correctly */
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Type", "%s", "new");
|
||||
switch_event_add_header(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Number", "%"SWITCH_SIZE_T_FMT, current_msg);
|
||||
|
||||
switch_event_add_header_string(phrase_params, SWITCH_STACK_BOTTOM, "VM-Message-Private-Local-Copy", "False");
|
||||
|
||||
switch_safe_free(apicmd);
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef _UTIL_H_
|
||||
#define _UTIL_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
switch_status_t mt_merge_files(const char** inputs, const char *output);
|
||||
|
||||
void append_event_message(switch_core_session_t *session, vmivr_profile_t *profile, switch_event_t *phrase_params, switch_event_t *msg_list_event, size_t current_msg);
|
||||
void append_event_profile(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu);
|
||||
char *generate_random_file_name(switch_core_session_t *session, const char *mod_name, char *file_extension);
|
||||
switch_event_t *jsonapi2event(switch_core_session_t *session, switch_event_t *apply_event, const char *api, const char *data);
|
||||
switch_status_t mt_merge_media_files(const char** inputs, const char *output);
|
||||
switch_status_t mt_api_execute(switch_core_session_t *session, const char *apiname, const char *arguments);
|
||||
void populate_dtmfa_from_event(switch_event_t *phrase_params, vmivr_profile_t *profile, vmivr_menu_profile_t menu, char **dtmfa);
|
||||
#endif /* _UTIL_H_ */
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="mod_sms"
|
||||
ProjectGUID="{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}"
|
||||
RootNamespace="mod_sms"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
<Platform
|
||||
Name="x64"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<File
|
||||
RelativePath=".\mod_sms.c"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>mod_sms</ProjectName>
|
||||
<ProjectGuid>{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}</ProjectGuid>
|
||||
<RootNamespace>mod_sms</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\..\..\w32\module_release.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\..\..\w32\module_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\..\..\w32\module_release.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\..\..\w32\module_debug.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention>
|
||||
</DataExecutionPrevention>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention>
|
||||
</DataExecutionPrevention>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention>
|
||||
</DataExecutionPrevention>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention>
|
||||
</DataExecutionPrevention>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="mod_sms.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\w32\Library\FreeSwitchCore.2010.vcxproj">
|
||||
<Project>{202d7a4e-760d-4d0e-afa1-d7459ced30ff}</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* mod_sms.c -- Abstract SMS
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
#define SMS_CHAT_PROTO "GLOBAL_SMS"
|
||||
#define MY_EVENT_SEND_MESSAGE "SMS::SEND_MESSAGE"
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown);
|
||||
SWITCH_MODULE_RUNTIME_FUNCTION(mod_sms_runtime);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load);
|
||||
SWITCH_MODULE_DEFINITION(mod_sms, mod_sms_load, mod_sms_shutdown, NULL);
|
||||
|
||||
|
||||
static void event_handler(switch_event_t *event)
|
||||
{
|
||||
const char *dest_proto = switch_event_get_header(event, "dest_proto");
|
||||
switch_core_chat_send(dest_proto, event);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
BREAK_ON_TRUE,
|
||||
BREAK_ON_FALSE,
|
||||
BREAK_ALWAYS,
|
||||
BREAK_NEVER
|
||||
} break_t;
|
||||
|
||||
|
||||
|
||||
static int parse_exten(switch_event_t *event, switch_xml_t xexten, switch_event_t **extension)
|
||||
{
|
||||
switch_xml_t xcond, xaction, xexpression;
|
||||
char *exten_name = (char *) switch_xml_attr(xexten, "name");
|
||||
int proceed = 0;
|
||||
char *expression_expanded = NULL, *field_expanded = NULL;
|
||||
switch_regex_t *re = NULL;
|
||||
const char *to = switch_event_get_header(event, "to");
|
||||
|
||||
if (!to) {
|
||||
to = "nobody";
|
||||
}
|
||||
|
||||
if (!exten_name) {
|
||||
exten_name = "_anon_";
|
||||
}
|
||||
|
||||
for (xcond = switch_xml_child(xexten, "condition"); xcond; xcond = xcond->next) {
|
||||
char *field = NULL;
|
||||
char *do_break_a = NULL;
|
||||
char *expression = NULL;
|
||||
const char *field_data = NULL;
|
||||
int ovector[30];
|
||||
switch_bool_t anti_action = SWITCH_TRUE;
|
||||
break_t do_break_i = BREAK_ON_FALSE;
|
||||
|
||||
int time_match = switch_xml_std_datetime_check(xcond);
|
||||
|
||||
switch_safe_free(field_expanded);
|
||||
switch_safe_free(expression_expanded);
|
||||
|
||||
if (switch_xml_child(xcond, "condition")) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Nested conditions are not allowed!\n");
|
||||
proceed = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
field = (char *) switch_xml_attr(xcond, "field");
|
||||
|
||||
if ((xexpression = switch_xml_child(xcond, "expression"))) {
|
||||
expression = switch_str_nil(xexpression->txt);
|
||||
} else {
|
||||
expression = (char *) switch_xml_attr_soft(xcond, "expression");
|
||||
}
|
||||
|
||||
if ((expression_expanded = switch_event_expand_headers(event, expression)) == expression) {
|
||||
expression_expanded = NULL;
|
||||
} else {
|
||||
expression = expression_expanded;
|
||||
}
|
||||
|
||||
if ((do_break_a = (char *) switch_xml_attr(xcond, "break"))) {
|
||||
if (!strcasecmp(do_break_a, "on-true")) {
|
||||
do_break_i = BREAK_ON_TRUE;
|
||||
} else if (!strcasecmp(do_break_a, "on-false")) {
|
||||
do_break_i = BREAK_ON_FALSE;
|
||||
} else if (!strcasecmp(do_break_a, "always")) {
|
||||
do_break_i = BREAK_ALWAYS;
|
||||
} else if (!strcasecmp(do_break_a, "never")) {
|
||||
do_break_i = BREAK_NEVER;
|
||||
} else {
|
||||
do_break_a = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_match == 1) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Date/Time Match (PASS) [%s] break=%s\n",
|
||||
to, exten_name, do_break_a ? do_break_a : "on-false");
|
||||
anti_action = SWITCH_FALSE;
|
||||
} else if (time_match == 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Date/Time Match (FAIL) [%s] break=%s\n",
|
||||
to, exten_name, do_break_a ? do_break_a : "on-false");
|
||||
}
|
||||
|
||||
if (field) {
|
||||
if (strchr(field, '$')) {
|
||||
if ((field_expanded = switch_event_expand_headers(event, field)) == field) {
|
||||
field_expanded = NULL;
|
||||
field_data = field;
|
||||
} else {
|
||||
field_data = field_expanded;
|
||||
}
|
||||
} else {
|
||||
field_data = switch_event_get_header(event, field);
|
||||
}
|
||||
if (!field_data) {
|
||||
field_data = "";
|
||||
}
|
||||
|
||||
if ((proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Regex (PASS) [%s] %s(%s) =~ /%s/ break=%s\n",
|
||||
to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
|
||||
anti_action = SWITCH_FALSE;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Regex (FAIL) [%s] %s(%s) =~ /%s/ break=%s\n",
|
||||
to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
|
||||
}
|
||||
} else if (time_match == -1) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Absolute Condition [%s]\n", to, exten_name);
|
||||
anti_action = SWITCH_FALSE;
|
||||
}
|
||||
|
||||
if (anti_action) {
|
||||
for (xaction = switch_xml_child(xcond, "anti-action"); xaction; xaction = xaction->next) {
|
||||
const char *application = switch_xml_attr_soft(xaction, "application");
|
||||
const char *loop = switch_xml_attr(xaction, "loop");
|
||||
const char *data;
|
||||
const char *inline_ = switch_xml_attr_soft(xaction, "inline");
|
||||
int xinline = switch_true(inline_);
|
||||
int loop_count = 1;
|
||||
|
||||
if (!zstr(xaction->txt)) {
|
||||
data = xaction->txt;
|
||||
} else {
|
||||
data = (char *) switch_xml_attr_soft(xaction, "data");
|
||||
}
|
||||
|
||||
if (!*extension) {
|
||||
if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (loop) {
|
||||
loop_count = atoi(loop);
|
||||
}
|
||||
|
||||
for (;loop_count > 0; loop_count--) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s ANTI-Action %s(%s) %s\n", to, application, data, xinline ? "INLINE" : "");
|
||||
|
||||
if (xinline) {
|
||||
switch_core_execute_chat_app(event, application, data);
|
||||
} else {
|
||||
switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(data) ? "__undef" : data);
|
||||
}
|
||||
}
|
||||
proceed = 1;
|
||||
}
|
||||
} else {
|
||||
if (field && strchr(expression, '(')) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "DP_MATCH", NULL);
|
||||
switch_capture_regex(re, proceed, field_data, ovector, "DP_MATCH", switch_regex_set_event_header_callback, event);
|
||||
}
|
||||
|
||||
for (xaction = switch_xml_child(xcond, "action"); xaction; xaction = xaction->next) {
|
||||
char *application = (char *) switch_xml_attr_soft(xaction, "application");
|
||||
const char *loop = switch_xml_attr(xaction, "loop");
|
||||
char *data = NULL;
|
||||
char *substituted = NULL;
|
||||
uint32_t len = 0;
|
||||
char *app_data = NULL;
|
||||
const char *inline_ = switch_xml_attr_soft(xaction, "inline");
|
||||
int xinline = switch_true(inline_);
|
||||
int loop_count = 1;
|
||||
|
||||
if (!zstr(xaction->txt)) {
|
||||
data = xaction->txt;
|
||||
} else {
|
||||
data = (char *) switch_xml_attr_soft(xaction, "data");
|
||||
}
|
||||
|
||||
if (field && strchr(expression, '(')) {
|
||||
len = (uint32_t) (strlen(data) + strlen(field_data) + 10) * proceed;
|
||||
if (!(substituted = malloc(len))) {
|
||||
abort();
|
||||
}
|
||||
memset(substituted, 0, len);
|
||||
switch_perform_substitution(re, proceed, data, field_data, substituted, len, ovector);
|
||||
app_data = substituted;
|
||||
} else {
|
||||
app_data = data;
|
||||
}
|
||||
|
||||
if (!*extension) {
|
||||
if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (loop) {
|
||||
loop_count = atoi(loop);
|
||||
}
|
||||
for (;loop_count > 0; loop_count--) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s Action %s(%s) %s\n", to, application, app_data, xinline ? "INLINE" : "");
|
||||
|
||||
if (xinline) {
|
||||
switch_core_execute_chat_app(event, application, app_data);
|
||||
} else {
|
||||
switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(data) ? "__undef" : data);
|
||||
}
|
||||
}
|
||||
switch_safe_free(substituted);
|
||||
}
|
||||
}
|
||||
switch_regex_safe_free(re);
|
||||
|
||||
if (((anti_action == SWITCH_FALSE && do_break_i == BREAK_ON_TRUE) ||
|
||||
(anti_action == SWITCH_TRUE && do_break_i == BREAK_ON_FALSE)) || do_break_i == BREAK_ALWAYS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
switch_regex_safe_free(re);
|
||||
switch_safe_free(field_expanded);
|
||||
switch_safe_free(expression_expanded);
|
||||
return proceed;
|
||||
}
|
||||
|
||||
|
||||
static switch_event_t *chatplan_hunt(switch_event_t *event)
|
||||
{
|
||||
switch_event_t *extension = NULL;
|
||||
switch_xml_t alt_root = NULL, cfg, xml = NULL, xcontext, xexten = NULL;
|
||||
const char *alt_path;
|
||||
const char *context;
|
||||
const char *from;
|
||||
const char *to;
|
||||
|
||||
if (!(context = switch_event_get_header(event, "context"))) {
|
||||
context = "default";
|
||||
}
|
||||
|
||||
if (!(from = switch_event_get_header(event, "from_user"))) {
|
||||
from = switch_event_get_header(event, "from");
|
||||
}
|
||||
|
||||
if (!(to = switch_event_get_header(event, "to_user"))) {
|
||||
to = switch_event_get_header(event, "to");
|
||||
}
|
||||
|
||||
alt_path = switch_event_get_header(event, "alt_path");
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Processing text message %s->%s in context %s\n", from, to, context);
|
||||
|
||||
/* get our handle to the "chatplan" section of the config */
|
||||
|
||||
if (!zstr(alt_path)) {
|
||||
switch_xml_t conf = NULL, tag = NULL;
|
||||
if (!(alt_root = switch_xml_parse_file_simple(alt_path))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of [%s] failed\n", alt_path);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((conf = switch_xml_find_child(alt_root, "section", "name", "chatplan")) && (tag = switch_xml_find_child(conf, "chatplan", NULL, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Getting chatplan from alternate path: %s\n", alt_path);
|
||||
xml = alt_root;
|
||||
cfg = tag;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n");
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
if (switch_xml_locate("chatplan", NULL, NULL, NULL, &xml, &cfg, event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* get a handle to the context tag */
|
||||
if (!(xcontext = switch_xml_find_child(cfg, "context", "name", context))) {
|
||||
if (!(xcontext = switch_xml_find_child(cfg, "context", "name", "global"))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Context %s not found\n", context);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
xexten = switch_xml_child(xcontext, "extension");
|
||||
|
||||
while (xexten) {
|
||||
int proceed = 0;
|
||||
const char *cont = switch_xml_attr(xexten, "continue");
|
||||
const char *exten_name = switch_xml_attr(xexten, "name");
|
||||
|
||||
if (!exten_name) {
|
||||
exten_name = "UNKNOWN";
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
|
||||
"Chatplan: %s parsing [%s->%s] continue=%s\n",
|
||||
to, context, exten_name, cont ? cont : "false");
|
||||
|
||||
proceed = parse_exten(event, xexten, &extension);
|
||||
|
||||
if (proceed && !switch_true(cont)) {
|
||||
break;
|
||||
}
|
||||
|
||||
xexten = xexten->next;
|
||||
}
|
||||
|
||||
switch_xml_free(xml);
|
||||
xml = NULL;
|
||||
|
||||
done:
|
||||
switch_xml_free(xml);
|
||||
return extension;
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t chat_send(switch_event_t *message_event)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_event_t *exten;
|
||||
|
||||
if ((exten = chatplan_hunt(message_event))) {
|
||||
switch_event_header_t *hp;
|
||||
|
||||
for (hp = exten->headers; hp; hp = hp->next) {
|
||||
status = switch_core_execute_chat_app(message_event, hp->name, hp->value);
|
||||
if (!SWITCH_READ_ACCEPTABLE(status)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch_event_destroy(&exten);
|
||||
status = SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_CHAT_APP(stop_function)
|
||||
{
|
||||
switch_set_flag(message, EF_NO_CHAT_EXEC);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_CHAT_APP(send_function)
|
||||
{
|
||||
const char *dest_proto = data;
|
||||
|
||||
if (zstr(dest_proto)) {
|
||||
dest_proto = switch_event_get_header(message, "dest_proto");
|
||||
}
|
||||
|
||||
switch_core_chat_send(dest_proto, message);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_CHAT_APP(set_function)
|
||||
{
|
||||
char *var, *val;
|
||||
|
||||
if (data) {
|
||||
var = strdup(data);
|
||||
if ((val = strchr(var, '='))) {
|
||||
*val++ = '\0';
|
||||
}
|
||||
|
||||
if (zstr(val)) {
|
||||
switch_event_del_header(message, var);
|
||||
} else {
|
||||
switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, var, val);
|
||||
}
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_CHAT_APP(fire_function)
|
||||
{
|
||||
switch_event_t *fireme;
|
||||
|
||||
switch_event_dup(&fireme, message);
|
||||
switch_event_fire(&fireme);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_CHAT_APP(reply_function)
|
||||
{
|
||||
switch_event_t *reply;
|
||||
const char *proto = switch_event_get_header(message, "proto");
|
||||
|
||||
if (proto) {
|
||||
switch_ivr_create_message_reply(&reply, message, SMS_CHAT_PROTO);
|
||||
|
||||
if (!zstr(data)) {
|
||||
switch_event_set_body(reply, data);
|
||||
}
|
||||
|
||||
switch_core_chat_deliver(proto, &reply);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Macro expands to: switch_status_t mod_sms_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load)
|
||||
{
|
||||
switch_chat_interface_t *chat_interface;
|
||||
switch_chat_application_interface_t *chat_app_interface;
|
||||
|
||||
|
||||
if (switch_event_bind(modname, SWITCH_EVENT_CUSTOM, MY_EVENT_SEND_MESSAGE, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
SWITCH_ADD_CHAT(chat_interface, SMS_CHAT_PROTO, chat_send);
|
||||
|
||||
SWITCH_ADD_CHAT_APP(chat_app_interface, "reply", "reply to a message", "reply to a message", reply_function, "", SCAF_NONE);
|
||||
SWITCH_ADD_CHAT_APP(chat_app_interface, "stop", "stop execution", "stop execution", stop_function, "", SCAF_NONE);
|
||||
SWITCH_ADD_CHAT_APP(chat_app_interface, "set", "set a variable", "set a variable", set_function, "", SCAF_NONE);
|
||||
SWITCH_ADD_CHAT_APP(chat_app_interface, "send", "send the message as-is", "send the message as-is", send_function, "", SCAF_NONE);
|
||||
SWITCH_ADD_CHAT_APP(chat_app_interface, "fire", "fire the message", "fire the message", fire_function, "", SCAF_NONE);
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the system shuts down
|
||||
Macro expands to: switch_status_t mod_sms_shutdown() */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown)
|
||||
{
|
||||
switch_event_unbind_callback(event_handler);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
||||
@@ -252,7 +252,7 @@ SWITCH_STANDARD_APP(soundtouch_start_function)
|
||||
char *argv[6];
|
||||
int argc;
|
||||
char *lbuf = NULL;
|
||||
int x;
|
||||
int x, n;
|
||||
|
||||
if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_soundtouch_"))) {
|
||||
if (!zstr(data) && !strcasecmp(data, "stop")) {
|
||||
@@ -275,26 +275,38 @@ SWITCH_STANDARD_APP(soundtouch_start_function)
|
||||
sth->tempo = 1;
|
||||
sth->hook_dtmf = false;
|
||||
sth->send_not_recv = false;
|
||||
n = 0;
|
||||
for (x = 0; x < argc; x++) {
|
||||
if (!strncasecmp(argv[x], "send_leg", 8)) {
|
||||
sth->send_not_recv = true;
|
||||
} else if (!strncasecmp(argv[x], "hook_dtmf", 9)) {
|
||||
sth->hook_dtmf = true;
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'p')) {
|
||||
sth->pitch = normalize_soundtouch_value('p',atof(argv[x]));
|
||||
sth->pitch = normalize_soundtouch_value('p', atof(argv[x]));
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'r')) {
|
||||
sth->rate = normalize_soundtouch_value('r',atof(argv[x]));
|
||||
sth->rate = normalize_soundtouch_value('r', atof(argv[x]));
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'o')) {
|
||||
sth->pitch = normalize_soundtouch_value('p', compute_pitch_from_octaves(atof(argv[x])) );
|
||||
n++;
|
||||
} else if (strchr(argv[x], 's')) {
|
||||
/*12.0f taken from soundtouch conversion to octaves*/
|
||||
sth->pitch = normalize_soundtouch_value('p', compute_pitch_from_octaves(atof(argv[x]) / 12.0f) );
|
||||
n++;
|
||||
} else if (strchr(argv[x], 't')) {
|
||||
sth->tempo = normalize_soundtouch_value('t',atof(argv[x]));
|
||||
sth->tempo = normalize_soundtouch_value('t', atof(argv[x]));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run, no pitch set\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sth->session = session;
|
||||
|
||||
@@ -308,9 +320,134 @@ SWITCH_STANDARD_APP(soundtouch_start_function)
|
||||
|
||||
}
|
||||
|
||||
/* API Interface Function */
|
||||
#define SOUNDTOUCH_API_SYNTAX "<uuid> [start|stop] [send_leg] [hook_dtmf] [-]<X>s [-]<X>o <X>p <X>r <X>t"
|
||||
SWITCH_STANDARD_API(soundtouch_api_function)
|
||||
{
|
||||
switch_core_session_t *rsession = NULL;
|
||||
switch_channel_t *channel = NULL;
|
||||
switch_media_bug_t *bug;
|
||||
switch_status_t status;
|
||||
struct soundtouch_helper *sth;
|
||||
char *mycmd = NULL;
|
||||
int argc = 0;
|
||||
char *argv[10] = { 0 };
|
||||
char *uuid = NULL;
|
||||
char *action = NULL;
|
||||
char *lbuf = NULL;
|
||||
int x, n;
|
||||
|
||||
if (zstr(cmd)) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
if (!(mycmd = strdup(cmd))) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
uuid = argv[0];
|
||||
action = argv[1];
|
||||
|
||||
if (!(rsession = switch_core_session_locate(uuid))) {
|
||||
stream->write_function(stream, "-ERR Cannot locate session!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
channel = switch_core_session_get_channel(rsession);
|
||||
|
||||
if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_soundtouch_"))) {
|
||||
if (!zstr(action) && !strcasecmp(action, "stop")) {
|
||||
switch_channel_set_private(channel, "_soundtouch_", NULL);
|
||||
switch_core_media_bug_remove(rsession, &bug);
|
||||
stream->write_function(stream, "+OK Success\n");
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Cannot run 2 at once on the same channel!\n");
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!zstr(action) && strcasecmp(action, "start")) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
sth = (struct soundtouch_helper *) switch_core_session_alloc(rsession, sizeof(*sth));
|
||||
assert(sth != NULL);
|
||||
|
||||
|
||||
sth->pitch = 1;
|
||||
sth->rate = 1;
|
||||
sth->tempo = 1;
|
||||
sth->hook_dtmf = false;
|
||||
sth->send_not_recv = false;
|
||||
n = 0;
|
||||
for (x = 2; x < argc; x++) {
|
||||
if (!strncasecmp(argv[x], "send_leg", 8)) {
|
||||
sth->send_not_recv = true;
|
||||
} else if (!strncasecmp(argv[x], "hook_dtmf", 9)) {
|
||||
sth->hook_dtmf = true;
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'p')) {
|
||||
sth->pitch = normalize_soundtouch_value('p', atof(argv[x]));
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'r')) {
|
||||
sth->rate = normalize_soundtouch_value('r', atof(argv[x]));
|
||||
n++;
|
||||
} else if (strchr(argv[x], 'o')) {
|
||||
sth->pitch = normalize_soundtouch_value('p', compute_pitch_from_octaves(atof(argv[x])) );
|
||||
n++;
|
||||
} else if (strchr(argv[x], 's')) {
|
||||
/*12.0f taken from soundtouch conversion to octaves*/
|
||||
sth->pitch = normalize_soundtouch_value('p', compute_pitch_from_octaves(atof(argv[x]) / 12.0f) );
|
||||
n++;
|
||||
} else if (strchr(argv[x], 't')) {
|
||||
sth->tempo = normalize_soundtouch_value('t', atof(argv[x]));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rsession), SWITCH_LOG_WARNING, "Cannot run, no pitch set\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
sth->session = rsession;
|
||||
|
||||
if ((status = switch_core_media_bug_add(rsession, "soundtouch", NULL, soundtouch_callback, sth, 0,
|
||||
sth->send_not_recv ? SMBF_WRITE_REPLACE : SMBF_READ_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) {
|
||||
stream->write_function(stream, "-ERR Failure!\n");
|
||||
goto done;
|
||||
} else {
|
||||
switch_channel_set_private(channel, "_soundtouch_", bug);
|
||||
stream->write_function(stream, "+OK Success\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
usage:
|
||||
stream->write_function(stream, "-USAGE: %s\n", SOUNDTOUCH_API_SYNTAX);
|
||||
|
||||
done:
|
||||
if (rsession) {
|
||||
switch_core_session_rwunlock(rsession);
|
||||
}
|
||||
|
||||
switch_safe_free(mycmd);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_soundtouch_load)
|
||||
{
|
||||
switch_application_interface_t *app_interface;
|
||||
switch_api_interface_t *api_interface;
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
@@ -318,6 +455,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_soundtouch_load)
|
||||
SWITCH_ADD_APP(app_interface, "soundtouch", "Alter the audio stream", "Alter the audio stream pitch/rate/tempo",
|
||||
soundtouch_start_function, "[send_leg] [hook_dtmf] [-]<X>s [-]<X>o <X>p <X>r <X>t", SAF_NONE);
|
||||
|
||||
SWITCH_ADD_API(api_interface, "soundtouch", "soundtouch", soundtouch_api_function, SOUNDTOUCH_API_SYNTAX);
|
||||
|
||||
switch_console_set_complete("add soundtouch ::console::list_uuid ::[start:stop");
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la
|
||||
mod_LTLIBRARIES = mod_spandsp.la
|
||||
mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c mod_spandsp_fax.c mod_spandsp_dsp.c mod_spandsp_codecs.c
|
||||
mod_spandsp_la_CFLAGS = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I.
|
||||
mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA)
|
||||
mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared -ljpeg
|
||||
mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) -ljpeg -lz
|
||||
mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared
|
||||
|
||||
$(SPANDSP_LA): $(TIFF_LA) $(SPANDSP_DIR) $(SPANDSP_DIR)/.update
|
||||
cd $(SPANDSP_BUILDDIR) && $(MAKE) -j1
|
||||
|
||||
@@ -52,11 +52,12 @@ static void spandsp_dtmf_rx_realtime_callback(void *user_data, int code, int lev
|
||||
if (digit) {
|
||||
/* prevent duplicate DTMF */
|
||||
if (digit != pvt->last_digit || (pvt->samples - pvt->last_digit_end) > pvt->min_dup_digit_spacing) {
|
||||
switch_dtmf_t dtmf;
|
||||
switch_dtmf_t dtmf = {0};
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "DTMF BEGIN DETECTED: [%c]\n", digit);
|
||||
pvt->last_digit = digit;
|
||||
dtmf.digit = digit;
|
||||
dtmf.duration = switch_core_default_dtmf_duration(0);
|
||||
dtmf.source = SWITCH_DTMF_INBAND_AUDIO;
|
||||
switch_channel_queue_dtmf(switch_core_session_get_channel(pvt->session), &dtmf);
|
||||
pvt->digit_begin = pvt->samples;
|
||||
} else {
|
||||
|
||||
@@ -806,7 +806,9 @@ static t38_mode_t negotiate_t38(pvt_t *pvt)
|
||||
switch_channel_set_private(channel, "t38_options", NULL);
|
||||
} else {
|
||||
pvt->t38_mode = T38_MODE_NEGOTIATED;
|
||||
|
||||
switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38 SDP Origin = %s\n", t38_options->sdp_o_line);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxVersion = %d\n", t38_options->T38FaxVersion);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38MaxBitRate = %d\n", t38_options->T38MaxBitRate);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxFillBitRemoval = %d\n", t38_options->T38FaxFillBitRemoval);
|
||||
@@ -827,7 +829,12 @@ static t38_mode_t negotiate_t38(pvt_t *pvt)
|
||||
t38_options->T38FaxVersion = 3;
|
||||
}
|
||||
t38_options->T38MaxBitRate = (pvt->disable_v17) ? 9600 : 14400;
|
||||
t38_options->T38FaxFillBitRemoval = 1;
|
||||
|
||||
/* cisco gets mad when we set this to one in a response where they set it to 0, are we allowed to hardcode this to 1 on responses? */
|
||||
if (!zstr(t38_options->sdp_o_line) && !switch_stristr("cisco", t38_options->sdp_o_line)) {
|
||||
t38_options->T38FaxFillBitRemoval = 1;
|
||||
}
|
||||
|
||||
t38_options->T38FaxTranscodingMMR = 0;
|
||||
t38_options->T38FaxTranscodingJBIG = 0;
|
||||
t38_options->T38FaxRateManagement = "transferredTCF";
|
||||
@@ -997,13 +1004,25 @@ static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_applicati
|
||||
}
|
||||
|
||||
if ((tmp = switch_channel_get_variable(channel, "fax_ident"))) {
|
||||
pvt->ident = switch_core_session_strdup(session, tmp);
|
||||
char *data = NULL;
|
||||
|
||||
data = strdup(tmp);
|
||||
switch_url_decode(data);
|
||||
pvt->ident = switch_core_session_strdup(session, data);
|
||||
|
||||
switch_safe_free(data);
|
||||
} else {
|
||||
pvt->ident = switch_core_session_strdup(session, globals.ident);
|
||||
}
|
||||
|
||||
if ((tmp = switch_channel_get_variable(channel, "fax_header"))) {
|
||||
pvt->header = switch_core_session_strdup(session, tmp);
|
||||
char *data = NULL;
|
||||
|
||||
data = strdup(tmp);
|
||||
switch_url_decode(data);
|
||||
pvt->header = switch_core_session_strdup(session, data);
|
||||
|
||||
switch_safe_free(data);
|
||||
} else {
|
||||
pvt->header = switch_core_session_strdup(session, globals.header);
|
||||
}
|
||||
@@ -1192,6 +1211,7 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat
|
||||
} else if (switch_channel_test_app_flag_key("T38", channel, CF_APP_T38)) {
|
||||
switch_core_session_message_t msg = { 0 };
|
||||
pvt->t38_mode = T38_MODE_NEGOTIATED;
|
||||
switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED);
|
||||
spanfax_init(pvt, T38_MODE);
|
||||
configure_t38(pvt);
|
||||
|
||||
@@ -1474,13 +1494,14 @@ static switch_status_t t38_gateway_on_soft_execute(switch_core_session_t *sessio
|
||||
spanfax_init(pvt, T38_GATEWAY_MODE);
|
||||
configure_t38(pvt);
|
||||
pvt->t38_mode = T38_MODE_NEGOTIATED;
|
||||
switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED);
|
||||
} else {
|
||||
if (negotiate_t38(pvt) != T38_MODE_NEGOTIATED) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Could not negotiate T38\n", switch_channel_get_name(channel));
|
||||
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||
goto end_unlock;
|
||||
}
|
||||
|
||||
switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED);
|
||||
spanfax_init(pvt, T38_GATEWAY_MODE);
|
||||
}
|
||||
|
||||
@@ -1570,6 +1591,8 @@ static switch_status_t t38_gateway_on_consume_media(switch_core_session_t *sessi
|
||||
switch_core_session_receive_message(session, &msg);
|
||||
|
||||
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid);
|
||||
switch_channel_event_set_data(channel, event);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,17 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load);
|
||||
*/
|
||||
SWITCH_MODULE_DEFINITION(mod_valet_parking, mod_valet_parking_load, NULL, NULL);
|
||||
|
||||
typedef struct {
|
||||
char ext[256];
|
||||
char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
|
||||
time_t timeout;
|
||||
} valet_token_t;
|
||||
|
||||
typedef struct {
|
||||
switch_hash_t *hash;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
time_t last_timeout_check;
|
||||
} valet_lot_t;
|
||||
|
||||
static valet_lot_t globals = { 0 };
|
||||
@@ -80,29 +87,98 @@ static switch_status_t valet_on_dtmf(switch_core_session_t *session, void *input
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int next_id(valet_lot_t *lot, int min, int max, int in)
|
||||
static void check_timeouts(void)
|
||||
{
|
||||
int i, r = 0, m;
|
||||
char buf[128] = "";
|
||||
switch_hash_index_t *hi;
|
||||
const void *var;
|
||||
void *val;
|
||||
time_t now;
|
||||
valet_lot_t *lot;
|
||||
switch_console_callback_match_t *matches = NULL;
|
||||
switch_console_callback_match_node_t *m;
|
||||
switch_hash_index_t *i_hi;
|
||||
const void *i_var;
|
||||
void *i_val;
|
||||
char *i_ext;
|
||||
valet_token_t *token;
|
||||
|
||||
if (!min)
|
||||
now = switch_epoch_time_now(NULL);
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (now - globals.last_timeout_check < 30) {
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
globals.last_timeout_check = now;
|
||||
for (hi = switch_hash_first(NULL, globals.hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, &var, NULL, &val);
|
||||
switch_console_push_match(&matches, (const char *) var);
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
|
||||
if (matches) {
|
||||
for (m = matches->head; m; m = m->next) {
|
||||
|
||||
lot = valet_find_lot(m->val);
|
||||
switch_mutex_lock(lot->mutex);
|
||||
|
||||
top:
|
||||
|
||||
for (i_hi = switch_hash_first(NULL, lot->hash); i_hi; i_hi = switch_hash_next(i_hi)) {
|
||||
switch_hash_this(i_hi, &i_var, NULL, &i_val);
|
||||
i_ext = (char *) i_var;
|
||||
token = (valet_token_t *) i_val;
|
||||
if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) {
|
||||
switch_core_hash_delete(lot->hash, i_ext);
|
||||
switch_safe_free(token);
|
||||
goto top;
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
}
|
||||
|
||||
switch_console_free_matches(&matches);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static valet_token_t *next_id(switch_core_session_t *session, valet_lot_t *lot, int min, int max, int in)
|
||||
{
|
||||
int i, r = 0;
|
||||
char buf[256] = "";
|
||||
valet_token_t *token;
|
||||
|
||||
if (!min) {
|
||||
min = 1;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
for (i = min; (i < max || max == 0); i++) {
|
||||
switch_snprintf(buf, sizeof(buf), "%d", i);
|
||||
m = !!switch_core_hash_find(lot->hash, buf);
|
||||
|
||||
if ((in && !m) || (!in && m)) {
|
||||
token = (valet_token_t *) switch_core_hash_find(lot->hash, buf);
|
||||
|
||||
if ((in && !token) || (!in && token && !token->timeout)) {
|
||||
r = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
token = NULL;
|
||||
|
||||
if (r) {
|
||||
switch_snprintf(buf, sizeof(buf), "%d", r);
|
||||
switch_zmalloc(token, sizeof(*token));
|
||||
switch_set_string(token->uuid, switch_core_session_get_uuid(session));
|
||||
switch_set_string(token->ext, buf);
|
||||
switch_core_hash_insert(lot->hash, buf, token);
|
||||
}
|
||||
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
return r;
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +192,11 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
char dtmf_buf[128] = "";
|
||||
int is_auto = 0, play_announce = 1;
|
||||
const char *var;
|
||||
valet_token_t *token = NULL;
|
||||
|
||||
|
||||
check_timeouts();
|
||||
|
||||
if ((var = switch_channel_get_variable(channel, "valet_announce_slot"))) {
|
||||
play_announce = switch_true(var);
|
||||
}
|
||||
@@ -142,7 +221,7 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
const char *io = argv[2];
|
||||
const char *min = argv[3];
|
||||
const char *max = argv[4];
|
||||
int min_i, max_i, id, in = -1;
|
||||
int min_i, max_i, in = -1;
|
||||
|
||||
if (argc < 5) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
|
||||
@@ -167,13 +246,13 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
min_i = atoi(min);
|
||||
max_i = atoi(max);
|
||||
|
||||
if (!(id = next_id(lot, min_i, max_i, in))) {
|
||||
if (!(token = next_id(session, lot, min_i, max_i, in))) {
|
||||
switch_ivr_phrase_macro(session, in ? "valet_lot_full" : "valet_lot_empty", "", NULL, NULL);
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%d", id);
|
||||
switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%s", token->ext);
|
||||
ext = dtmf_buf;
|
||||
} else if (!strcasecmp(ext, "ask")) {
|
||||
const char *prompt = "ivr/ivr-enter_ext_pound.wav";
|
||||
@@ -219,37 +298,65 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_lock(lot->mutex);
|
||||
if ((uuid = switch_core_hash_find(lot->hash, ext))) {
|
||||
switch_core_session_t *b_session;
|
||||
if ((b_session = switch_core_session_locate(uuid))) {
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session));
|
||||
switch_channel_event_set_data(switch_core_session_get_channel(b_session), event);
|
||||
switch_event_fire(&event);
|
||||
switch_core_session_rwunlock(b_session);
|
||||
if (!token) {
|
||||
switch_mutex_lock(lot->mutex);
|
||||
if ((token = (valet_token_t *) switch_core_hash_find(lot->hash, ext))) {
|
||||
switch_core_session_t *b_session;
|
||||
|
||||
if (token->timeout) {
|
||||
const char *var = switch_channel_get_variable(channel, "valet_ticket");
|
||||
|
||||
if (!zstr(var)) {
|
||||
if (!strcmp(var, token->uuid)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Valet ticket %s accepted.\n", var);
|
||||
token->timeout = 0;
|
||||
switch_channel_set_variable(channel, "valet_ticket", NULL);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid token %s\n", token->uuid);
|
||||
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_ivr_uuid_bridge(switch_core_session_get_uuid(session), uuid);
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
return;
|
||||
if (!zstr(token->uuid) && (b_session = switch_core_session_locate(token->uuid))) {
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session));
|
||||
switch_channel_event_set_data(switch_core_session_get_channel(b_session), event);
|
||||
switch_event_fire(&event);
|
||||
switch_core_session_rwunlock(b_session);
|
||||
token->timeout = 0;
|
||||
switch_ivr_uuid_bridge(switch_core_session_get_uuid(session), token->uuid);
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
token = NULL;
|
||||
|
||||
switch_zmalloc(token, sizeof(*token));
|
||||
switch_set_string(token->uuid, switch_core_session_get_uuid(session));
|
||||
switch_core_hash_insert(lot->hash, ext, token);
|
||||
}
|
||||
|
||||
if (!(tmp = switch_channel_get_variable(channel, "valet_hold_music"))) {
|
||||
tmp = switch_channel_get_hold_music(channel);
|
||||
}
|
||||
if (tmp)
|
||||
|
||||
if (tmp) {
|
||||
music = tmp;
|
||||
}
|
||||
|
||||
if (!strcasecmp(music, "silence")) {
|
||||
music = "silence_stream://-1";
|
||||
}
|
||||
|
||||
dest = switch_core_session_sprintf(session, "set:valet_hold_music=%s,sleep:1000,valet_park:%s %s", music, lot_name, ext);
|
||||
|
||||
dest = switch_core_session_sprintf(session, "set:valet_ticket=%s,set:valet_hold_music=%s,sleep:1000,valet_park:%s %s",
|
||||
token->uuid, music, lot_name, ext);
|
||||
switch_channel_set_variable(channel, "inline_destination", dest);
|
||||
|
||||
if (is_auto) {
|
||||
@@ -260,18 +367,21 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
switch_core_session_t *b_session;
|
||||
|
||||
if ((b_session = switch_core_session_locate(uuid))) {
|
||||
token->timeout = switch_epoch_time_now(NULL) + 10;
|
||||
if (play_announce) {
|
||||
switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
|
||||
switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
|
||||
}
|
||||
switch_ivr_session_transfer(b_session, dest, "inline", NULL);
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
switch_core_session_rwunlock(b_session);
|
||||
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (play_announce) {
|
||||
switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
|
||||
switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
|
||||
}
|
||||
}
|
||||
@@ -286,7 +396,7 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
}
|
||||
|
||||
|
||||
switch_core_hash_insert(lot->hash, ext, switch_core_session_get_uuid(session));
|
||||
|
||||
|
||||
args.input_callback = valet_on_dtmf;
|
||||
args.buf = dbuf;
|
||||
@@ -302,10 +412,6 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_lock(lot->mutex);
|
||||
switch_core_hash_delete(lot->hash, ext);
|
||||
switch_mutex_unlock(lot->mutex);
|
||||
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
||||
@@ -316,6 +422,13 @@ SWITCH_STANDARD_APP(valet_parking_function)
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
if (token) {
|
||||
token->timeout = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_API(valet_info_function)
|
||||
@@ -373,6 +486,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load)
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
|
||||
globals.pool = pool;
|
||||
switch_core_hash_init(&globals.hash, NULL);
|
||||
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||
|
||||
@@ -45,6 +45,7 @@ SWITCH_MODULE_DEFINITION(mod_voicemail, mod_voicemail_load, mod_voicemail_shutdo
|
||||
#define VM_EVENT_MAINT "vm::maintenance"
|
||||
|
||||
#define VM_MAX_GREETINGS 9
|
||||
#define VM_EVENT_QUEUE_SIZE 50000
|
||||
|
||||
static switch_status_t voicemail_inject(const char *data, switch_core_session_t *session);
|
||||
|
||||
@@ -53,6 +54,9 @@ static struct {
|
||||
switch_hash_t *profile_hash;
|
||||
int debug;
|
||||
int message_query_exact_match;
|
||||
int32_t threads;
|
||||
int32_t running;
|
||||
switch_queue_t *event_queue;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
} globals;
|
||||
@@ -849,6 +853,7 @@ struct call_control {
|
||||
switch_file_handle_t *fh;
|
||||
char buf[4];
|
||||
int noexit;
|
||||
int playback_controls_active;
|
||||
};
|
||||
typedef struct call_control cc_t;
|
||||
|
||||
@@ -867,7 +872,13 @@ static switch_status_t control_playback(switch_core_session_t *session, void *in
|
||||
|| dtmf->digit == *cc->profile->prev_msg_key || dtmf->digit == *cc->profile->next_msg_key
|
||||
|| dtmf->digit == *cc->profile->repeat_msg_key
|
||||
|| dtmf->digit == *cc->profile->terminator_key || dtmf->digit == *cc->profile->skip_info_key
|
||||
|| dtmf->digit == *cc->profile->email_key || dtmf->digit == *cc->profile->forward_key)) {
|
||||
|| dtmf->digit == *cc->profile->forward_key)) {
|
||||
*cc->buf = dtmf->digit;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
if (!cc->playback_controls_active
|
||||
&& (dtmf->digit == *cc->profile->email_key)) {
|
||||
*cc->buf = dtmf->digit;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
@@ -903,10 +914,6 @@ static switch_status_t control_playback(switch_core_session_t *session, void *in
|
||||
switch_core_file_seek(fh, &pos, samps, SEEK_CUR);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
if (!cc->noexit && dtmf->digit == *cc->profile->terminator_key) {
|
||||
*cc->buf = dtmf->digit;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1160,6 +1167,8 @@ static switch_status_t create_file(switch_core_session_t *session, vm_profile_t
|
||||
args.buf = input;
|
||||
args.buflen = sizeof(input);
|
||||
|
||||
unlink(file_path);
|
||||
|
||||
switch_ivr_record_file(session, &fh, file_path, &args, profile->max_record_len);
|
||||
|
||||
if (switch_file_exists(file_path, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
|
||||
@@ -1529,6 +1538,9 @@ static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t
|
||||
msg.from = __FILE__;
|
||||
msg.string_arg = cid_buf;
|
||||
msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sending display update [%s] to %s\n",
|
||||
cid_buf, switch_channel_get_name(channel));
|
||||
switch_core_session_receive_message(session, &msg);
|
||||
|
||||
if (!zstr(cbt->cid_number) && (vm_announce_cid = switch_channel_get_variable(channel, "vm_announce_cid"))) {
|
||||
@@ -1562,9 +1574,11 @@ static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t
|
||||
*cc.buf = '\0';
|
||||
memset(&fh, 0, sizeof(fh));
|
||||
cc.fh = &fh;
|
||||
cc.playback_controls_active = 1;
|
||||
if (switch_file_exists(cbt->file_path, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
|
||||
TRY_CODE(switch_ivr_play_file(session, &fh, cbt->file_path, &args));
|
||||
}
|
||||
cc.playback_controls_active = 0;
|
||||
}
|
||||
|
||||
if (!*cc.buf && (profile->play_date_announcement == VM_DATE_LAST)) {
|
||||
@@ -1819,7 +1833,7 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p
|
||||
uint32_t timeout, attempts = 0, retries = 0;
|
||||
int failed = 0;
|
||||
msg_type_t play_msg_type = MSG_NONE;
|
||||
char *dir_path = NULL, *file_path = NULL;
|
||||
char *dir_path = NULL, *file_path = NULL, *tmp_file_path = NULL;
|
||||
int total_new_messages = 0;
|
||||
int total_saved_messages = 0;
|
||||
int total_new_urgent_messages = 0;
|
||||
@@ -2099,13 +2113,19 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p
|
||||
} else {
|
||||
switch_event_t *params;
|
||||
file_path = switch_mprintf("%s%sgreeting_%d.%s", dir_path, SWITCH_PATH_SEPARATOR, num, profile->file_ext);
|
||||
tmp_file_path = switch_mprintf("%s%sgreeting_%d_TMP.%s", dir_path, SWITCH_PATH_SEPARATOR, num, profile->file_ext);
|
||||
unlink(tmp_file_path);
|
||||
|
||||
TRY_CODE(create_file(session, profile, VM_RECORD_GREETING_MACRO, file_path, &message_len, SWITCH_TRUE, NULL, NULL));
|
||||
switch_file_rename(tmp_file_path, file_path, switch_core_session_get_pool(session));
|
||||
|
||||
sql =
|
||||
switch_mprintf("update voicemail_prefs set greeting_path='%s' where username='%s' and domain='%s'", file_path, myid,
|
||||
domain_name);
|
||||
vm_execute_sql(profile, sql, profile->mutex);
|
||||
switch_safe_free(sql);
|
||||
switch_safe_free(file_path);
|
||||
switch_safe_free(tmp_file_path);
|
||||
|
||||
switch_event_create_subclass(¶ms, SWITCH_EVENT_CUSTOM, VM_EVENT_MAINT);
|
||||
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "VM-Action", "record-greeting");
|
||||
@@ -2146,10 +2166,14 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p
|
||||
} else if (!strcmp(input, profile->record_name_key)) {
|
||||
switch_event_t *params;
|
||||
file_path = switch_mprintf("%s%srecorded_name.%s", dir_path, SWITCH_PATH_SEPARATOR, profile->file_ext);
|
||||
tmp_file_path = switch_mprintf("%s%srecorded_name_TMP.%s", dir_path, SWITCH_PATH_SEPARATOR, profile->file_ext);
|
||||
unlink(tmp_file_path);
|
||||
TRY_CODE(create_file(session, profile, VM_RECORD_NAME_MACRO, file_path, &message_len, SWITCH_FALSE, NULL, NULL));
|
||||
switch_file_rename(tmp_file_path, file_path, switch_core_session_get_pool(session));
|
||||
sql = switch_mprintf("update voicemail_prefs set name_path='%s' where username='%s' and domain='%s'", file_path, myid, domain_name);
|
||||
vm_execute_sql(profile, sql, profile->mutex);
|
||||
switch_safe_free(file_path);
|
||||
switch_safe_free(tmp_file_path);
|
||||
switch_safe_free(sql);
|
||||
|
||||
switch_event_create_subclass(¶ms, SWITCH_EVENT_CUSTOM, VM_EVENT_MAINT);
|
||||
@@ -2431,6 +2455,14 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p
|
||||
|
||||
end:
|
||||
|
||||
switch_safe_free(file_path);
|
||||
|
||||
if (tmp_file_path) {
|
||||
unlink(tmp_file_path);
|
||||
free(tmp_file_path);
|
||||
tmp_file_path = NULL;
|
||||
}
|
||||
|
||||
if (switch_channel_ready(channel)) {
|
||||
if (failed) {
|
||||
status = switch_ivr_phrase_macro(session, VM_ABORT_MACRO, NULL, NULL, NULL);
|
||||
@@ -2747,6 +2779,10 @@ static switch_status_t deliver_vm(vm_profile_t *profile,
|
||||
|
||||
|
||||
if (send_notify) {
|
||||
if (zstr(vm_notify_email)) {
|
||||
vm_notify_email = vm_email;
|
||||
}
|
||||
|
||||
if (zstr(profile->notify_email_headers)) {
|
||||
headers = switch_mprintf("From: FreeSWITCH mod_voicemail <%s@%s>\n"
|
||||
"Subject: Voicemail from %s %s\nX-Priority: %d", myid, domain_name, caller_id_name, caller_id_number, priority);
|
||||
@@ -2815,7 +2851,7 @@ static switch_status_t deliver_vm(vm_profile_t *profile,
|
||||
|
||||
failed:
|
||||
|
||||
if (del_file && file_path) {
|
||||
if (del_file && file_path && switch_file_exists(file_path, pool)) {
|
||||
if (unlink(file_path) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete file [%s]\n", file_path);
|
||||
}
|
||||
@@ -3629,16 +3665,14 @@ SWITCH_STANDARD_API(prefs_api_function)
|
||||
switch_event_add_header_string(new_event, SWITCH_STACK_BOTTOM, "MWI-Message-Account", account); \
|
||||
switch_event_add_header(new_event, SWITCH_STACK_BOTTOM, "MWI-Voice-Message", "%d/%d (%d/%d)", \
|
||||
+total_new_messages, total_saved_messages, total_new_urgent_messages, total_saved_urgent_messages); \
|
||||
created++; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
static void message_query_handler(switch_event_t *event)
|
||||
static void actual_message_query_handler(switch_event_t *event)
|
||||
{
|
||||
char *account = switch_event_get_header(event, "message-account");
|
||||
int created = 0;
|
||||
switch_event_t *new_event = NULL;
|
||||
char *dup = NULL;
|
||||
int total_new_messages = 0;
|
||||
@@ -3677,6 +3711,10 @@ static void message_query_handler(switch_event_t *event)
|
||||
switch_hash_this(hi, NULL, NULL, &val);
|
||||
profile = (vm_profile_t *) val;
|
||||
parse_profile();
|
||||
|
||||
if (new_event) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3685,7 +3723,7 @@ static void message_query_handler(switch_event_t *event)
|
||||
|
||||
}
|
||||
|
||||
if (!created) {
|
||||
if (!new_event) {
|
||||
if (switch_event_create(&new_event, SWITCH_EVENT_MESSAGE_WAITING) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(new_event, SWITCH_STACK_BOTTOM, "MWI-Messages-Waiting", "no");
|
||||
switch_event_add_header_string(new_event, SWITCH_STACK_BOTTOM, "MWI-Message-Account", account);
|
||||
@@ -3707,6 +3745,101 @@ static void message_query_handler(switch_event_t *event)
|
||||
|
||||
}
|
||||
|
||||
static int EVENT_THREAD_RUNNING = 0;
|
||||
static int EVENT_THREAD_STARTED = 0;
|
||||
|
||||
void *SWITCH_THREAD_FUNC vm_event_thread_run(switch_thread_t *thread, void *obj)
|
||||
{
|
||||
void *pop;
|
||||
int done = 0;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (!EVENT_THREAD_RUNNING) {
|
||||
EVENT_THREAD_RUNNING++;
|
||||
globals.threads++;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (done) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Event Thread Started\n");
|
||||
|
||||
while (globals.running == 1) {
|
||||
int count = 0;
|
||||
|
||||
if (switch_queue_trypop(globals.event_queue, &pop) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_t *event = (switch_event_t *) pop;
|
||||
|
||||
if (!pop) {
|
||||
break;
|
||||
}
|
||||
actual_message_query_handler(event);
|
||||
switch_event_destroy(&event);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
switch_yield(100000);
|
||||
}
|
||||
}
|
||||
|
||||
while (switch_queue_trypop(globals.event_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) {
|
||||
switch_event_t *event = (switch_event_t *) pop;
|
||||
switch_event_destroy(&event);
|
||||
}
|
||||
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Event Thread Ended\n");
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
globals.threads--;
|
||||
EVENT_THREAD_RUNNING = EVENT_THREAD_STARTED = 0;
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void vm_event_thread_start(void)
|
||||
{
|
||||
switch_thread_t *thread;
|
||||
switch_threadattr_t *thd_attr = NULL;
|
||||
int done = 0;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (!EVENT_THREAD_STARTED) {
|
||||
EVENT_THREAD_STARTED++;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch_threadattr_create(&thd_attr, globals.pool);
|
||||
switch_threadattr_detach_set(thd_attr, 1);
|
||||
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||||
switch_threadattr_priority_increase(thd_attr);
|
||||
switch_thread_create(&thread, thd_attr, vm_event_thread_run, NULL, globals.pool);
|
||||
}
|
||||
|
||||
void vm_event_handler(switch_event_t *event)
|
||||
{
|
||||
switch_event_t *cloned_event;
|
||||
|
||||
switch_event_dup(&cloned_event, event);
|
||||
switch_assert(cloned_event);
|
||||
switch_queue_push(globals.event_queue, cloned_event);
|
||||
|
||||
if (!EVENT_THREAD_STARTED) {
|
||||
vm_event_thread_start();
|
||||
}
|
||||
}
|
||||
|
||||
struct holder {
|
||||
vm_profile_t *profile;
|
||||
@@ -4318,7 +4451,7 @@ static int api_list_callback(void *pArg, int argc, char **argv, char **columnNam
|
||||
{
|
||||
switch_stream_handle_t *stream = (switch_stream_handle_t *) pArg;
|
||||
|
||||
if (!strcasecmp(argv[9], "xml")) {
|
||||
if (!strcasecmp(argv[10], "xml")) {
|
||||
stream->write_function(stream, " <message>\n");
|
||||
stream->write_function(stream, " <created_epoch>%s</created_epoch>\n", argv[0]);
|
||||
stream->write_function(stream, " <read_epoch>%s</read_epoch>\n", argv[1]);
|
||||
@@ -4329,9 +4462,10 @@ static int api_list_callback(void *pArg, int argc, char **argv, char **columnNam
|
||||
stream->write_function(stream, " <uuid>%s</uuid>\n", argv[6]);
|
||||
stream->write_function(stream, " <cid-name>%s</cid-name>\n", argv[7]);
|
||||
stream->write_function(stream, " <cid-number>%s</cid-number>\n", argv[8]);
|
||||
stream->write_function(stream, " <message-len>%s</message-len>\n", argv[9]);
|
||||
stream->write_function(stream, " </message>\n");
|
||||
} else {
|
||||
stream->write_function(stream, "%s:%s:%s:%s:%s:%s:%s:%s:%s\n", argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
|
||||
stream->write_function(stream, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n", argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -4375,11 +4509,11 @@ SWITCH_STANDARD_API(voicemail_list_api_function)
|
||||
|
||||
if (id && domain && profile_name && (profile = get_profile(profile_name))) {
|
||||
if (uuid) {
|
||||
sql = switch_mprintf("select created_epoch, read_epoch, username, domain, in_folder, file_path, uuid, cid_name, cid_number, "
|
||||
sql = switch_mprintf("select created_epoch, read_epoch, username, domain, in_folder, file_path, uuid, cid_name, cid_number, message_len, "
|
||||
"'%q' from voicemail_msgs where username='%q' and domain='%q' and uuid='%q'",
|
||||
format, id, domain, uuid);
|
||||
} else {
|
||||
sql = switch_mprintf("select created_epoch, read_epoch, username, domain, in_folder, file_path, uuid, cid_name, cid_number, "
|
||||
sql = switch_mprintf("select created_epoch, read_epoch, username, domain, in_folder, file_path, uuid, cid_name, cid_number, message_len, "
|
||||
"'%q' from voicemail_msgs where username='%q' and domain='%q'",
|
||||
format, id, domain);
|
||||
}
|
||||
@@ -4390,7 +4524,6 @@ SWITCH_STANDARD_API(voicemail_list_api_function)
|
||||
|
||||
vm_execute_sql_callback(profile, profile->mutex, sql, api_list_callback, stream);
|
||||
switch_safe_free(sql);
|
||||
update_mwi(profile, id, domain, "inbox");
|
||||
|
||||
if (!strcasecmp(format, "xml")) {
|
||||
stream->write_function(stream, "</voicemail>\n");
|
||||
@@ -5482,14 +5615,20 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_voicemail_load)
|
||||
switch_core_hash_init(&globals.profile_hash, globals.pool);
|
||||
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
globals.running = 1;
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_queue_create(&globals.event_queue, VM_EVENT_QUEUE_SIZE, globals.pool);
|
||||
|
||||
if ((status = load_config()) != SWITCH_STATUS_SUCCESS) {
|
||||
globals.running = 0;
|
||||
return status;
|
||||
}
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
if (switch_event_bind(modname, SWITCH_EVENT_MESSAGE_QUERY, SWITCH_EVENT_SUBCLASS_ANY, message_query_handler, NULL)
|
||||
if (switch_event_bind(modname, SWITCH_EVENT_MESSAGE_QUERY, SWITCH_EVENT_SUBCLASS_ANY, vm_event_handler, NULL)
|
||||
!= SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
@@ -5534,9 +5673,23 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_voicemail_shutdown)
|
||||
void *val = NULL;
|
||||
const void *key;
|
||||
switch_ssize_t keylen;
|
||||
int sanity = 0;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (globals.running == 1) {
|
||||
globals.running = 0;
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_event_free_subclass(VM_EVENT_MAINT);
|
||||
switch_event_unbind_callback(message_query_handler);
|
||||
switch_event_unbind_callback(vm_event_handler);
|
||||
|
||||
while (globals.threads) {
|
||||
switch_cond_next();
|
||||
if (++sanity >= 60000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
while ((hi = switch_hash_first(NULL, globals.profile_hash))) {
|
||||
|
||||
@@ -12,7 +12,7 @@ POCKETSPHINX_LA=$(POCKETSPHINX_BUILDDIR)/src/libpocketsphinx/libpocketsphinx.la
|
||||
SPHINXMODEL=communicator_semi_6000_20080321
|
||||
|
||||
LOCAL_CFLAGS=-I$(SPHINXBASE_DIR)/include -I$(POCKETSPHINX_DIR)/include
|
||||
LOCAL_LIBADD=$(SPHINXBASE_LA) $(SPHINXBASE_LA2) $(POCKETSPHINX_LA)
|
||||
LOCAL_LIBADD=$(POCKETSPHINX_LA) $(SPHINXBASE_LA) $(SPHINXBASE_LA2)
|
||||
BASE=../../../..
|
||||
include $(BASE)/build/modmake.rules
|
||||
|
||||
|
||||
@@ -192,7 +192,9 @@ static switch_status_t switch_silk_init(switch_codec_t *codec,
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
context->encoder_object.sampleRate = codec->implementation->actual_samples_per_second;
|
||||
|
||||
context->encoder_object.API_sampleRate = codec->implementation->actual_samples_per_second;
|
||||
context->encoder_object.maxInternalSampleRate = codec->implementation->actual_samples_per_second;
|
||||
context->encoder_object.packetSize = codec->implementation->samples_per_packet;
|
||||
context->encoder_object.useInBandFEC = silk_codec_settings.useinbandfec;
|
||||
context->encoder_object.complexity = 0;
|
||||
@@ -210,7 +212,7 @@ static switch_status_t switch_silk_init(switch_codec_t *codec,
|
||||
if (SKP_Silk_SDK_InitDecoder(context->dec_state)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
context->decoder_object.sampleRate = codec->implementation->actual_samples_per_second;
|
||||
context->decoder_object.API_sampleRate = codec->implementation->actual_samples_per_second;
|
||||
}
|
||||
|
||||
codec->private_info = context;
|
||||
@@ -241,22 +243,22 @@ void printSilkError(SKP_int16 ret){
|
||||
case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT:
|
||||
message = "Allocated payload buffer too short";
|
||||
break;
|
||||
case SKP_SILK_ENC_WRONG_LOSS_RATE:
|
||||
case SKP_SILK_ENC_INVALID_LOSS_RATE:
|
||||
message = " Loss rate not between 0 and 100 % ";
|
||||
break;
|
||||
case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING:
|
||||
case SKP_SILK_ENC_INVALID_COMPLEXITY_SETTING:
|
||||
message = "Complexity setting not valid, use 0 ,1 or 2";
|
||||
break;
|
||||
case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING:
|
||||
case SKP_SILK_ENC_INVALID_INBAND_FEC_SETTING:
|
||||
message = "Inband FEC setting not valid, use 0 or 1 ";
|
||||
break;
|
||||
case SKP_SILK_ENC_WRONG_DTX_SETTING:
|
||||
case SKP_SILK_ENC_INVALID_DTX_SETTING:
|
||||
message = "DTX setting not valid, use 0 or 1";
|
||||
break;
|
||||
case SKP_SILK_ENC_INTERNAL_ERROR:
|
||||
message = "Internal Encoder Error ";
|
||||
break;
|
||||
case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY:
|
||||
case SKP_SILK_DEC_INVALID_SAMPLING_FREQUENCY:
|
||||
message = "Output sampling frequency lower than internal decoded sampling frequency";
|
||||
break;
|
||||
case SKP_SILK_DEC_PAYLOAD_TOO_LARGE:
|
||||
|
||||
@@ -489,11 +489,21 @@ static void pres_event_handler(switch_event_t *event)
|
||||
switch_safe_free(sql);
|
||||
}
|
||||
|
||||
static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint)
|
||||
static switch_status_t chat_send(switch_event_t *message_event)
|
||||
{
|
||||
char *user, *host, *f_user = NULL, *ffrom = NULL, *f_host = NULL, *f_resource = NULL;
|
||||
mdl_profile_t *profile = NULL;
|
||||
const char *proto;
|
||||
const char *from;
|
||||
const char *to;
|
||||
const char *body;
|
||||
const char *hint;
|
||||
|
||||
proto = switch_event_get_header(message_event, "proto");
|
||||
from = switch_event_get_header(message_event, "from");
|
||||
to = switch_event_get_header(message_event, "to");
|
||||
body = switch_event_get_body(message_event);
|
||||
hint = switch_event_get_header(message_event, "hint");
|
||||
|
||||
switch_assert(proto != NULL);
|
||||
|
||||
@@ -2876,6 +2886,8 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
|
||||
char *proto = MDL_CHAT_PROTO;
|
||||
char *pproto = NULL, *ffrom = NULL;
|
||||
char *hint;
|
||||
switch_event_t *event;
|
||||
char *from_user, *from_host;
|
||||
#ifdef AUTO_REPLY
|
||||
if (profile->auto_reply) {
|
||||
ldl_handle_send_msg(handle,
|
||||
@@ -2902,10 +2914,41 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
|
||||
from = ffrom;
|
||||
}
|
||||
|
||||
if (strcasecmp(proto, MDL_CHAT_PROTO)) { /* yes no ! on purpose */
|
||||
switch_core_chat_send(proto, MDL_CHAT_PROTO, from, to, subject, switch_str_nil(msg), NULL, hint);
|
||||
from_user = strdup(from);
|
||||
if ((from_host = strchr(from_user, '@'))) {
|
||||
*from_host++ = '\0';
|
||||
}
|
||||
|
||||
|
||||
if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", from_user);
|
||||
if (from_host) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", from_host);
|
||||
}
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", subject);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", hint);
|
||||
|
||||
if (msg) {
|
||||
switch_event_add_body(event, "%s", msg);
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
switch_safe_free(from_user);
|
||||
|
||||
if (strcasecmp(proto, MDL_CHAT_PROTO)) { /* yes no ! on purpose */
|
||||
switch_core_chat_send(proto, event);
|
||||
}
|
||||
|
||||
switch_core_chat_send("GLOBAL", event);
|
||||
|
||||
switch_event_destroy(&event);
|
||||
|
||||
switch_safe_free(pproto);
|
||||
switch_safe_free(ffrom);
|
||||
}
|
||||
|
||||
@@ -928,7 +928,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
while (p && *p) {
|
||||
switch_dtmf_t dtmf;
|
||||
switch_dtmf_t dtmf = {0};
|
||||
dtmf.digit = *p;
|
||||
dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION;
|
||||
switch_channel_queue_dtmf(channel, &dtmf);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
moves the switch_rtp_request_port() call from the contructor to FSH323Connection::CreateRealTimeLogicalChannel() - fix
|
||||
rtp port leak. tnx to Peter Olsson.
|
||||
fix log printing
|
||||
fix small interoperability issues if remote endpoint send progress twice
|
||||
make sure dtmfinband gets initialized
|
||||
make gk-identifier and gk-interface settings optional (documentation sayed that about gk-identifier already)
|
||||
|
||||
@@ -413,7 +413,7 @@ bool FSH323EndPoint::Initialise(switch_loadable_module_interface_t *iface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_fax_old_asn) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "--->fax_old_asn\n");
|
||||
SetT38_IFP_PRE();
|
||||
@@ -714,6 +714,7 @@ FSH323Connection::FSH323Connection(FSH323EndPoint& endpoint, H323Transport* tran
|
||||
, m_rtp_resetting(0)
|
||||
, m_isRequst_fax(false)
|
||||
, m_channel_hangup(false)
|
||||
, m_RTPlocalPort(0)
|
||||
{
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"======>FSH323Connection::FSH323Connection [%p]\n",this);
|
||||
|
||||
@@ -739,8 +740,6 @@ FSH323Connection::FSH323Connection(FSH323EndPoint& endpoint, H323Transport* tran
|
||||
|
||||
switch_channel_set_state(m_fsChannel, CS_INIT);
|
||||
}
|
||||
|
||||
m_RTPlocalPort = switch_rtp_request_port((const char *)m_RTPlocalIP.AsString());
|
||||
}
|
||||
|
||||
FSH323Connection::~FSH323Connection()
|
||||
@@ -974,6 +973,9 @@ H323Channel* FSH323Connection::CreateRealTimeLogicalChannel(const H323Capability
|
||||
|
||||
H323TransportAddress m_h323transportadd = GetSignallingChannel()->GetLocalAddress();
|
||||
m_h323transportadd.GetIpAddress(m_RTPlocalIP);
|
||||
if (!m_RTPlocalPort) {
|
||||
m_RTPlocalPort = switch_rtp_request_port((const char *)m_RTPlocalIP.AsString());
|
||||
}
|
||||
|
||||
return new FSH323_ExternalRTPChannel(*this, capability, dir, sessionID,m_RTPlocalIP,m_RTPlocalPort);
|
||||
}
|
||||
@@ -2044,7 +2046,7 @@ PBoolean FSH323_ExternalRTPChannel::Start()
|
||||
, GetMainTypes[m_capability->GetMainType()],(const char*)(m_capability->GetFormatName()),this);
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"%s initialise %s codec %s for connection [%p]\n",switch_channel_get_name(m_fsChannel),((GetDirection() == IsReceiver)? " read" : " write")
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"%s Unsupported ptime of %u on %s %s codec %s for connection [%p]\n",switch_channel_get_name(m_fsChannel),((GetDirection() == IsReceiver)? " read" : " write")
|
||||
, GetMainTypes[m_capability->GetMainType()],(const char*)(m_capability->GetFormatName()),this);
|
||||
|
||||
if (GetDirection() == IsReceiver) {
|
||||
|
||||
@@ -802,15 +802,20 @@ static switch_status_t loopback_bowout_on_execute_state_handler(switch_core_sess
|
||||
switch_core_session_read_lock(tech_pvt->other_session);
|
||||
b_channel = switch_core_session_get_channel(tech_pvt->other_session);
|
||||
|
||||
/* Wait for b_channel to be fully bridged */
|
||||
switch_channel_wait_for_flag(b_channel, CF_BRIDGED, SWITCH_TRUE, 5000, NULL);
|
||||
|
||||
uuid = switch_channel_get_variable(b_channel, SWITCH_SIGNAL_BOND_VARIABLE);
|
||||
|
||||
if (uuid && (other_session = switch_core_session_locate(uuid))) {
|
||||
switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
|
||||
switch_caller_profile_t *cp, *clone;
|
||||
|
||||
|
||||
switch_channel_wait_for_state(other_channel, NULL, CS_EXCHANGE_MEDIA);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->other_session), SWITCH_LOG_INFO, "Replacing loopback channel: %s with real channel: %s\n",
|
||||
switch_channel_get_name(b_channel), switch_channel_get_name(other_channel));
|
||||
|
||||
|
||||
if ((cp = switch_channel_get_caller_profile(channel))) {
|
||||
clone = switch_caller_profile_clone(other_session, cp);
|
||||
clone->originator_caller_profile = NULL;
|
||||
@@ -831,7 +836,7 @@ static switch_status_t loopback_bowout_on_execute_state_handler(switch_core_sess
|
||||
switch_core_session_rwunlock(tech_pvt->other_session);
|
||||
|
||||
switch_core_event_hook_remove_state_change(session, loopback_bowout_on_execute_state_handler);
|
||||
|
||||
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1324,7 +1324,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load)
|
||||
globals.read_frame.buflen = sizeof(globals.databuf);
|
||||
globals.cng_frame.data = globals.cngbuf;
|
||||
globals.cng_frame.buflen = sizeof(globals.cngbuf);
|
||||
globals.cng_frame.datalen = switch_samples_per_packet(globals.sample_rate, globals.codec_ms) * 2;
|
||||
switch_set_flag((&globals.cng_frame), SFF_CNG);
|
||||
globals.flags = GFLAG_EAR | GFLAG_MOUTH;
|
||||
/* dual streams makes portaudio on solaris choke */
|
||||
@@ -1750,6 +1749,8 @@ static switch_status_t load_config(void)
|
||||
globals.codec_ms = 20;
|
||||
}
|
||||
|
||||
globals.cng_frame.datalen = switch_samples_per_packet(globals.sample_rate, globals.codec_ms) * 2;
|
||||
|
||||
if (!globals.ring_interval) {
|
||||
globals.ring_interval = 5;
|
||||
}
|
||||
|
||||
@@ -188,9 +188,14 @@ switch_status_t rtmp_check_auth(rtmp_session_t *rsession, const char *user, cons
|
||||
switch_xml_t xml = NULL, x_param, x_params;
|
||||
switch_bool_t allow_empty_password = SWITCH_FALSE;
|
||||
const char *passwd = NULL;
|
||||
switch_event_t *locate_params;
|
||||
|
||||
switch_event_create(&locate_params, SWITCH_EVENT_GENERAL);
|
||||
switch_assert(locate_params);
|
||||
switch_event_add_header_string(locate_params, SWITCH_STACK_BOTTOM, "source", "mod_rtmp");
|
||||
|
||||
/* Locate user */
|
||||
if (switch_xml_locate_user_merged("id", user, domain, NULL, &xml, NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
if (switch_xml_locate_user_merged("id", user, domain, NULL, &xml, locate_params) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_WARNING, "Authentication failed. No such user %s@%s\n", user, domain);
|
||||
goto done;
|
||||
}
|
||||
@@ -231,6 +236,9 @@ done:
|
||||
if (xml) {
|
||||
switch_xml_free(xml);
|
||||
}
|
||||
|
||||
switch_event_destroy(&locate_params);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ RTMP_INVOKE_FUNCTION(rtmp_i_makeCall)
|
||||
amf_object_to_event(argv[3], &event);
|
||||
}
|
||||
|
||||
if (rtmp_session_create_call(rsession, &newsession, 0, RTMP_DEFAULT_STREAM_AUDIO, number, user, domain, event) != SWITCH_CAUSE_NONE) {
|
||||
if (rtmp_session_create_call(rsession, &newsession, 0, RTMP_DEFAULT_STREAM_AUDIO, number, user, domain, event) != SWITCH_CAUSE_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_ERROR, "Couldn't create call.\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ static switch_status_t rtmp_tcp_write(rtmp_session_t *rsession, const unsigned c
|
||||
|
||||
status = switch_socket_send_nonblock(io_pvt->socket, (char*)buf, len);
|
||||
|
||||
if (*len < orig_len) {
|
||||
if (*len > 0 && *len < orig_len) {
|
||||
|
||||
if (rsession->state >= RS_DESTROY) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "skypopen.h"
|
||||
#define MDL_CHAT_PROTO "skype"
|
||||
#define SKYPE_CHAT_PROTO "skype"
|
||||
|
||||
#ifdef WIN32
|
||||
/***************/
|
||||
@@ -838,7 +838,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
|
||||
|
||||
if (switch_test_flag(tech_pvt, TFLAG_PROGRESS)) {
|
||||
//DEBUGA_SKYPE("CHANNEL READ FRAME in TFLAG_PROGRESS goto CNG\n", SKYPOPEN_P_LOG);
|
||||
switch_sleep(MS_SKYPOPEN * 1000);
|
||||
//switch_sleep(MS_SKYPOPEN * 1000);
|
||||
goto cng;
|
||||
}
|
||||
|
||||
@@ -879,7 +879,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
|
||||
DEBUGA_SKYPE("skypopen_audio_read going back to read\n", SKYPOPEN_P_LOG);
|
||||
goto read;
|
||||
}
|
||||
DEBUGA_SKYPE("skypopen_audio_read Silence\n", SKYPOPEN_P_LOG);
|
||||
DEBUGA_SKYPE("READ BUFFER EMPTY, skypopen_audio_read Silence\n", SKYPOPEN_P_LOG);
|
||||
memset(tech_pvt->read_frame.data, 255, BYTES_PER_FRAME);
|
||||
tech_pvt->read_frame.datalen = BYTES_PER_FRAME;
|
||||
|
||||
@@ -943,7 +943,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
|
||||
if(channel){
|
||||
|
||||
while (p && *p) {
|
||||
switch_dtmf_t dtmf;
|
||||
switch_dtmf_t dtmf = {0};
|
||||
dtmf.digit = *p;
|
||||
dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION;
|
||||
switch_channel_queue_dtmf(channel, &dtmf);
|
||||
@@ -1022,17 +1022,16 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
|
||||
|
||||
switch_mutex_lock(tech_pvt->mutex_audio_cli);
|
||||
if (switch_buffer_freespace(tech_pvt->write_buffer) < frame->datalen) {
|
||||
DEBUGA_SKYPE("NO SPACE WRITE: %d\n", SKYPOPEN_P_LOG, frame->datalen);
|
||||
switch_buffer_zero(tech_pvt->write_buffer);
|
||||
no_space = 1;
|
||||
}
|
||||
switch_buffer_write(tech_pvt->write_buffer, frame->data, frame->datalen);
|
||||
switch_mutex_unlock(tech_pvt->mutex_audio_cli);
|
||||
if (no_space) {
|
||||
switch_sleep(MS_SKYPOPEN * 1000);
|
||||
} else {
|
||||
tech_pvt->begin_to_write = 1;
|
||||
//switch_sleep(MS_SKYPOPEN * 1000);
|
||||
DEBUGA_SKYPE("NO SPACE in WRITE BUFFER: there was no space for %d\n", SKYPOPEN_P_LOG, frame->datalen);
|
||||
}
|
||||
tech_pvt->begin_to_write = 1;
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1859,17 +1858,35 @@ static switch_status_t load_config(int reload_type)
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type,
|
||||
const char *hint)
|
||||
static switch_status_t chat_send(switch_event_t *message_event)
|
||||
|
||||
{
|
||||
char *user = NULL, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL;
|
||||
private_t *tech_pvt = NULL;
|
||||
int i = 0, found = 0, tried = 0;
|
||||
char skype_msg[1024];
|
||||
|
||||
const char *proto;
|
||||
const char *from;
|
||||
const char *to;
|
||||
const char *subject;
|
||||
const char *body;
|
||||
//const char *type;
|
||||
const char *hint;
|
||||
|
||||
proto = switch_event_get_header(message_event, "proto");
|
||||
from = switch_event_get_header(message_event, "from");
|
||||
to = switch_event_get_header(message_event, "to");
|
||||
subject = switch_event_get_header(message_event, "subject");
|
||||
body = switch_event_get_body(message_event);
|
||||
//type = switch_event_get_header(message_event, "type");
|
||||
hint = switch_event_get_header(message_event, "hint");
|
||||
|
||||
switch_assert(proto != NULL);
|
||||
|
||||
DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type,
|
||||
//DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type,
|
||||
// hint ? hint : "NULL");
|
||||
DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body,
|
||||
hint ? hint : "NULL");
|
||||
|
||||
if (!to || !strlen(to)) {
|
||||
@@ -1896,7 +1913,9 @@ static switch_status_t chat_send(const char *proto, const char *from, const char
|
||||
*host++ = '\0';
|
||||
}
|
||||
|
||||
DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type,
|
||||
//DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type,
|
||||
// hint ? hint : "NULL");
|
||||
DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body,
|
||||
hint ? hint : "NULL");
|
||||
if (hint && strlen(hint)) {
|
||||
//in hint we receive the interface name to use
|
||||
@@ -1999,7 +2018,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skypopen_load)
|
||||
SWITCH_ADD_API(commands_api_interface, "skypopen", "Skypopen interface commands", skypopen_function, SKYPOPEN_SYNTAX);
|
||||
SWITCH_ADD_API(commands_api_interface, "skypopen_chat", "Skypopen_chat interface remote_skypename TEXT", skypopen_chat_function,
|
||||
SKYPOPEN_CHAT_SYNTAX);
|
||||
SWITCH_ADD_CHAT(chat_interface, MDL_CHAT_PROTO, chat_send);
|
||||
SWITCH_ADD_CHAT(chat_interface, SKYPE_CHAT_PROTO, chat_send);
|
||||
|
||||
if (switch_event_reserve_subclass(MY_EVENT_INCOMING_CHATMESSAGE) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
|
||||
@@ -2945,7 +2964,7 @@ int incoming_chatmessage(private_t *tech_pvt, int which)
|
||||
session = switch_core_session_locate(tech_pvt->session_uuid_str);
|
||||
}
|
||||
if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SKYPE_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle);
|
||||
@@ -2972,7 +2991,7 @@ int incoming_chatmessage(private_t *tech_pvt, int which)
|
||||
if (!event_sent_to_esl) {
|
||||
|
||||
if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SKYPE_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle);
|
||||
@@ -2998,6 +3017,33 @@ int incoming_chatmessage(private_t *tech_pvt, int which)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static switch_status_t compat_chat_send(const char *proto, const char *from, const char *to,
|
||||
const char *subject, const char *body, const char *type, const char *hint)
|
||||
{
|
||||
switch_event_t *message_event;
|
||||
switch_status_t status;
|
||||
|
||||
if (switch_event_create(&message_event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "proto", proto);
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "from", from);
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "to", to);
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "subject", subject);
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "type", type);
|
||||
switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "hint", hint);
|
||||
|
||||
if (body) {
|
||||
switch_event_add_body(message_event, "%s", body);
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
status = chat_send(message_event);
|
||||
switch_event_destroy(&message_event);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_API(skypopen_chat_function)
|
||||
{
|
||||
@@ -3038,11 +3084,11 @@ SWITCH_STANDARD_API(skypopen_chat_function)
|
||||
goto end;
|
||||
} else {
|
||||
|
||||
NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPOPEN_P_LOG, MDL_CHAT_PROTO, tech_pvt->skype_user,
|
||||
NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPOPEN_P_LOG, SKYPE_CHAT_PROTO, tech_pvt->skype_user,
|
||||
argv[1], "SIMPLE MESSAGE", switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), tech_pvt->name);
|
||||
|
||||
chat_send(MDL_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE",
|
||||
switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name);
|
||||
compat_chat_send(SKYPE_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE",
|
||||
switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name);
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -283,6 +283,7 @@ static ssize_t skypopen_write(struct file *filp, const char __user *buf, size_t
|
||||
* The ioctl() implementation
|
||||
*/
|
||||
|
||||
#ifndef HAVE_UNLOCKED_IOCTL
|
||||
static int skypopen_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@@ -302,13 +303,38 @@ static int skypopen_ioctl(struct inode *inode, struct file *filp,
|
||||
}
|
||||
|
||||
}
|
||||
#else// HAVE_UNLOCKED_IOCTL
|
||||
static long skypopen_unlocked_ioctl(struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
|
||||
switch (cmd) {
|
||||
case OSS_GETVERSION:
|
||||
return put_user(SOUND_VERSION, p);
|
||||
case SNDCTL_DSP_GETBLKSIZE:
|
||||
return put_user(SKYPOPEN_BLK, p);
|
||||
case SNDCTL_DSP_GETFMTS:
|
||||
return put_user(28731, p);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
#endif// HAVE_UNLOCKED_IOCTL
|
||||
|
||||
struct file_operations skypopen_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = skypopen_read,
|
||||
.write = skypopen_write,
|
||||
#ifndef HAVE_UNLOCKED_IOCTL
|
||||
.ioctl = skypopen_ioctl,
|
||||
#else// HAVE_UNLOCKED_IOCTL
|
||||
.unlocked_ioctl = skypopen_unlocked_ioctl,
|
||||
#endif// HAVE_UNLOCKED_IOCTL
|
||||
.open = skypopen_c_open,
|
||||
.release = skypopen_c_release,
|
||||
};
|
||||
|
||||
@@ -540,6 +540,7 @@ int skypopen_signaling_read(private_t *tech_pvt)
|
||||
DEBUGA_SKYPE("Skype FAILED on skype_call %s. Let's wait for the FAILED message.\n", SKYPOPEN_P_LOG, id);
|
||||
}
|
||||
#if 0
|
||||
#ifndef WIN32
|
||||
if (!strcasecmp(prop, "DURATION")) { /* each 20 seconds, we zero the buffers and sync the timers */
|
||||
if (!((atoi(value) % 20))) {
|
||||
if (tech_pvt->read_buffer) {
|
||||
@@ -565,6 +566,7 @@ int skypopen_signaling_read(private_t *tech_pvt)
|
||||
DEBUGA_SKYPE("Synching audio on skype_call: %s.\n", SKYPOPEN_P_LOG, id);
|
||||
}
|
||||
}
|
||||
#endif //WIN32
|
||||
#endif //0
|
||||
if (!strcasecmp(prop, "DURATION") && (!strcasecmp(value, "1"))) {
|
||||
if (strcasecmp(id, tech_pvt->skype_call_id)) {
|
||||
@@ -882,19 +884,21 @@ void *skypopen_do_tcp_srv_thread_func(void *obj)
|
||||
|| tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA
|
||||
|| tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) {
|
||||
|
||||
unsigned int fdselect;
|
||||
//unsigned int fdselect;
|
||||
int rt=1;
|
||||
fd_set fs;
|
||||
//fd_set fs;
|
||||
//struct timeval to;
|
||||
int nospace;
|
||||
|
||||
if (!(running && tech_pvt->running))
|
||||
break;
|
||||
#if 0
|
||||
fdselect = fd;
|
||||
FD_ZERO(&fs);
|
||||
FD_SET(fdselect, &fs);
|
||||
//to.tv_usec = MS_SKYPOPEN * 1000 * 3;
|
||||
//to.tv_sec = 0;
|
||||
to.tv_usec = MS_SKYPOPEN * 1000 * 3;
|
||||
to.tv_sec = 0;
|
||||
#endif //0
|
||||
|
||||
if (tech_pvt->timer_read_srv.timer_interface && tech_pvt->timer_read_srv.timer_interface->timer_next) {
|
||||
switch_core_timer_next(&tech_pvt->timer_read_srv);
|
||||
@@ -935,7 +939,7 @@ void *skypopen_do_tcp_srv_thread_func(void *obj)
|
||||
}
|
||||
switch_mutex_unlock(tech_pvt->mutex_audio_srv);
|
||||
if (nospace) {
|
||||
DEBUGA_SKYPE("NO SPACE READ: there was no space for: %d\n", SKYPOPEN_P_LOG, len);
|
||||
DEBUGA_SKYPE("NO SPACE in READ BUFFER: there was no space for: %d\n", SKYPOPEN_P_LOG, len);
|
||||
}
|
||||
} else if (len == 0) {
|
||||
DEBUGA_SKYPE("CLOSED\n", SKYPOPEN_P_LOG);
|
||||
@@ -1080,7 +1084,7 @@ void *skypopen_do_tcp_cli_thread_func(void *obj)
|
||||
}
|
||||
switch_mutex_lock(tech_pvt->mutex_audio_cli);
|
||||
if (tech_pvt->write_buffer && switch_buffer_inuse(tech_pvt->write_buffer)) {
|
||||
bytes_to_write = switch_buffer_read(tech_pvt->write_buffer, cli_out, BYTES_PER_FRAME * 2);
|
||||
bytes_to_write = switch_buffer_read(tech_pvt->write_buffer, cli_out, BYTES_PER_FRAME);
|
||||
}
|
||||
switch_mutex_unlock(tech_pvt->mutex_audio_cli);
|
||||
|
||||
|
||||
@@ -282,6 +282,8 @@ char *generate_pai_str(private_object_t *tech_pvt)
|
||||
header = (tech_pvt->cid_type == CID_TYPE_RPID && !switch_stristr("aastra", ua)) ? "Remote-Party-ID" : "P-Asserted-Identity";
|
||||
|
||||
if (!zstr(callee_name) && !zstr(callee_number)) {
|
||||
check_decode(callee_name, tech_pvt->session);
|
||||
|
||||
if (switch_stristr("update_display", tech_pvt->x_freeswitch_support_remote)) {
|
||||
pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n"
|
||||
"X-FS-Display-Name: %s\nX-FS-Display-Number: %s\n",
|
||||
@@ -741,13 +743,6 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
|
||||
}
|
||||
}
|
||||
|
||||
if ((val = switch_channel_get_variable(channel, SOFIA_SESSION_TIMEOUT))) {
|
||||
int v_session_timeout = atoi(val);
|
||||
if (v_session_timeout >= 0) {
|
||||
session_timeout = v_session_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (sofia_test_flag(tech_pvt, TFLAG_NAT) ||
|
||||
(val = switch_channel_get_variable(channel, "sip-force-contact")) ||
|
||||
((val = switch_channel_get_variable(channel, "sip_sticky_contact")) && switch_true(val))) {
|
||||
@@ -756,6 +751,13 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
|
||||
switch_channel_set_variable(channel, "sip_nat_detected", "true");
|
||||
}
|
||||
|
||||
if ((val = switch_channel_get_variable(channel, SOFIA_SESSION_TIMEOUT))) {
|
||||
int v_session_timeout = atoi(val);
|
||||
if (v_session_timeout >= 0) {
|
||||
session_timeout = v_session_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
|
||||
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX);
|
||||
char *cid = NULL;
|
||||
@@ -780,18 +782,27 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
|
||||
}
|
||||
}
|
||||
|
||||
if ((tech_pvt->session_timeout = session_timeout)) {
|
||||
tech_pvt->session_refresher = switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? nua_local_refresher : nua_remote_refresher;
|
||||
} else {
|
||||
tech_pvt->session_refresher = nua_no_refresher;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (sofia_use_soa(tech_pvt)) {
|
||||
nua_respond(tech_pvt->nh, SIP_200_OK,
|
||||
NUTAG_AUTOANSWER(0),
|
||||
TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)),
|
||||
TAG_IF(sticky, NUTAG_PROXY(tech_pvt->record_route)),
|
||||
TAG_IF(cid, SIPTAG_HEADER_STR(cid)),
|
||||
NUTAG_SESSION_TIMER(session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(session_timeout ? nua_local_refresher : nua_no_refresher),
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
|
||||
SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
|
||||
SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str),
|
||||
SOATAG_REUSE_REJECTED(1), SOATAG_ORDERED_USER(1), SOATAG_AUDIO_AUX("cn telephone-event"), NUTAG_INCLUDE_EXTRA_SDP(1),
|
||||
TAG_IF(is_proxy, SOATAG_RTP_SELECT(1)),
|
||||
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
|
||||
TAG_IF(switch_stristr("update_display", tech_pvt->x_freeswitch_support_remote),
|
||||
SIPTAG_HEADER_STR("X-FS-Support: " FREESWITCH_SUPPORT)), TAG_END());
|
||||
@@ -802,8 +813,8 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
|
||||
TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)),
|
||||
TAG_IF(sticky, NUTAG_PROXY(tech_pvt->record_route)),
|
||||
TAG_IF(cid, SIPTAG_HEADER_STR(cid)),
|
||||
NUTAG_SESSION_TIMER(session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(session_timeout ? nua_local_refresher : nua_no_refresher),
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
|
||||
SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
|
||||
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
|
||||
@@ -1364,11 +1375,17 @@ static switch_status_t sofia_send_dtmf(switch_core_session_t *session, const swi
|
||||
}
|
||||
case DTMF_INFO:
|
||||
{
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sending INFO DTMF %c of length %d\n", dtmf->digit, dtmf->duration);
|
||||
snprintf(message, sizeof(message), "Signal=%c\r\nDuration=%d\r\n", dtmf->digit, dtmf->duration / 8);
|
||||
switch_mutex_lock(tech_pvt->sofia_mutex);
|
||||
nua_info(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("application/dtmf-relay"), SIPTAG_PAYLOAD_STR(message), TAG_END());
|
||||
switch_mutex_unlock(tech_pvt->sofia_mutex);
|
||||
if (dtmf->digit == 'w') {
|
||||
switch_yield(500000);
|
||||
} else if (dtmf->digit == 'W') {
|
||||
switch_yield(1000000);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sending INFO DTMF %c of length %d\n", dtmf->digit, dtmf->duration / 8);
|
||||
snprintf(message, sizeof(message), "Signal=%c\r\nDuration=%d\r\n", dtmf->digit, dtmf->duration / 8);
|
||||
switch_mutex_lock(tech_pvt->sofia_mutex);
|
||||
nua_info(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("application/dtmf-relay"), SIPTAG_PAYLOAD_STR(message), TAG_END());
|
||||
switch_mutex_unlock(tech_pvt->sofia_mutex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DTMF_NONE:
|
||||
@@ -1393,6 +1410,9 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
if (msg->message_id == SWITCH_MESSAGE_INDICATE_SIGNAL_DATA) {
|
||||
sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) msg->pointer_arg;
|
||||
switch_mutex_lock(tech_pvt->sofia_mutex);
|
||||
if (switch_core_session_in_thread(session)) {
|
||||
de->session = session;
|
||||
}
|
||||
sofia_process_dispatch_event(&de);
|
||||
switch_mutex_unlock(tech_pvt->sofia_mutex);
|
||||
goto end;
|
||||
@@ -1883,14 +1903,21 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
case SWITCH_MESSAGE_INDICATE_PHONE_EVENT:
|
||||
{
|
||||
const char *event = "talk";
|
||||
|
||||
if (!zstr(msg->string_arg) && strcasecmp(msg->string_arg, event)) {
|
||||
if (!strcasecmp(msg->string_arg, "hold")) {
|
||||
event = "hold";
|
||||
} else {
|
||||
} else if (strncasecmp(msg->string_arg, "talk", 4)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid event.\n");
|
||||
}
|
||||
}
|
||||
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), NUTAG_SUBSTATE(nua_substate_active), SIPTAG_EVENT_STR(event), TAG_END());
|
||||
|
||||
if (!switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
|
||||
"Operation not permitted on an inbound non-answered call leg!\n");
|
||||
} else {
|
||||
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), NUTAG_SUBSTATE(nua_substate_active), SIPTAG_EVENT_STR(event), TAG_END());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SWITCH_MESSAGE_INDICATE_SIMPLIFY:
|
||||
@@ -1961,12 +1988,15 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
char message[256] = "";
|
||||
const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent");
|
||||
switch_event_t *event;
|
||||
|
||||
|
||||
if (zstr(number)) {
|
||||
number = tech_pvt->caller_profile->destination_number;
|
||||
}
|
||||
|
||||
if (!sofia_test_flag(tech_pvt, TFLAG_UPDATING_DISPLAY)) {
|
||||
if (sofia_test_flag(tech_pvt, TFLAG_UPDATING_DISPLAY)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR,
|
||||
"Cannot send display update to %s Did not receive reply to last update\n", switch_channel_get_name(tech_pvt->channel));
|
||||
} else {
|
||||
if (zstr(tech_pvt->last_sent_callee_id_name) || strcmp(tech_pvt->last_sent_callee_id_name, name) ||
|
||||
zstr(tech_pvt->last_sent_callee_id_number) || strcmp(tech_pvt->last_sent_callee_id_number, number)) {
|
||||
|
||||
@@ -1995,6 +2025,8 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <%s>", name, number);
|
||||
sofia_set_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
|
||||
nua_update(tech_pvt->nh,
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(!zstr(tech_pvt->route_uri), NUTAG_PROXY(tech_pvt->route_uri)),
|
||||
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
@@ -2003,28 +2035,40 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
|
||||
sofia_set_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
|
||||
nua_update(tech_pvt->nh,
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(!zstr(tech_pvt->route_uri), NUTAG_PROXY(tech_pvt->route_uri)),
|
||||
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
} else if ((ua && (switch_stristr("cisco/spa50", ua) || switch_stristr("cisco/spa525", ua)))) {
|
||||
snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <sip:%s@%s>", name, number, tech_pvt->profile->sipip);
|
||||
snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <sip:%s@%s>", name, number, tech_pvt->profile->sipip);
|
||||
|
||||
sofia_set_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
|
||||
nua_update(tech_pvt->nh,
|
||||
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
}
|
||||
sofia_set_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
|
||||
nua_update(tech_pvt->nh,
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
}
|
||||
|
||||
tech_pvt->last_sent_callee_id_name = switch_core_session_strdup(tech_pvt->session, name);
|
||||
tech_pvt->last_sent_callee_id_number = switch_core_session_strdup(tech_pvt->session, number);
|
||||
|
||||
switch_channel_set_variable(channel, "last_sent_callee_id_name", name);
|
||||
switch_channel_set_variable(channel, "last_sent_callee_id_number", number);
|
||||
|
||||
|
||||
if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) {
|
||||
const char *uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "SEND");
|
||||
|
||||
switch_channel_set_profile_var(channel, "callee_id_name", name);
|
||||
switch_channel_set_profile_var(channel, "callee_id_number", number);
|
||||
|
||||
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sent-Callee-ID-Name", name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sent-Callee-ID-Number", number);
|
||||
|
||||
//switch_channel_set_profile_var(channel, "callee_id_name", name);
|
||||
//switch_channel_set_profile_var(channel, "callee_id_number", number);
|
||||
|
||||
if (uuid) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridged-To", uuid);
|
||||
}
|
||||
@@ -2057,6 +2101,8 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
} else if (ua && switch_stristr("polycom", ua)) {
|
||||
snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <%s>", msg->string_arg, tech_pvt->caller_profile->destination_number);
|
||||
nua_update(tech_pvt->nh,
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(!zstr(tech_pvt->route_uri), NUTAG_PROXY(tech_pvt->route_uri)),
|
||||
TAG_IF(!zstr_buf(message), SIPTAG_HEADER_STR(message)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
@@ -3580,12 +3626,12 @@ SWITCH_STANDARD_API(sofia_count_reg_function)
|
||||
if (zstr(user)) {
|
||||
sql = switch_mprintf("select count(*) "
|
||||
"from sip_registrations where (sip_host='%q' or presence_hosts like '%%%q%%')",
|
||||
(concat != NULL) ? concat : "", domain, domain);
|
||||
domain, domain);
|
||||
|
||||
} else {
|
||||
sql = switch_mprintf("select count(*) "
|
||||
"from sip_registrations where (sip_user='%q' or dir_user='%q') and (sip_host='%q' or presence_hosts like '%%%q%%')",
|
||||
(concat != NULL) ? concat : "", user, user, domain, domain);
|
||||
"from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')",
|
||||
user, domain, domain);
|
||||
}
|
||||
switch_assert(sql);
|
||||
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sql2str_callback, &cb);
|
||||
@@ -4702,7 +4748,7 @@ static void general_event_handler(switch_event_t *event)
|
||||
|
||||
nua_notify(nh,
|
||||
NUTAG_NEWSUB(1),
|
||||
TAG_IF(dst->route_uri, NUTAG_PROXY(dst->contact)), TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
|
||||
TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)), TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
|
||||
SIPTAG_EVENT_STR(es), TAG_IF(ct, SIPTAG_CONTENT_TYPE_STR(ct)), TAG_IF(!zstr(body), SIPTAG_PAYLOAD_STR(body)), TAG_END());
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#define HAVE_APR
|
||||
#include <switch.h>
|
||||
#include <switch_version.h>
|
||||
#define SOFIA_NAT_SESSION_TIMEOUT 1800
|
||||
#define SOFIA_NAT_SESSION_TIMEOUT 90
|
||||
#define SOFIA_MAX_ACL 100
|
||||
#ifdef _MSC_VER
|
||||
#define HAVE_FUNCTION 1
|
||||
@@ -98,6 +98,7 @@ typedef struct private_object private_object_t;
|
||||
#define SOFIA_SIP_RESPONSE_HEADER_PREFIX "sip_rh_"
|
||||
#define SOFIA_SIP_RESPONSE_HEADER_PREFIX_T "~sip_rh_"
|
||||
#define SOFIA_SIP_BYE_HEADER_PREFIX "sip_bye_h_"
|
||||
#define SOFIA_SIP_BYE_HEADER_PREFIX_T "~sip_bye_h_"
|
||||
#define SOFIA_SIP_PROGRESS_HEADER_PREFIX "sip_ph_"
|
||||
#define SOFIA_SIP_PROGRESS_HEADER_PREFIX_T "~sip_ph_"
|
||||
#define SOFIA_SIP_HEADER_PREFIX_T "~sip_h_"
|
||||
@@ -143,6 +144,7 @@ typedef struct sofia_dispatch_event_s {
|
||||
nua_t *nua;
|
||||
sofia_profile_t *profile;
|
||||
int save;
|
||||
switch_core_session_t *session;
|
||||
} sofia_dispatch_event_t;
|
||||
|
||||
struct sofia_private {
|
||||
@@ -214,6 +216,7 @@ typedef enum {
|
||||
PFLAG_MANAGE_SHARED_APPEARANCE,
|
||||
PFLAG_MANAGE_SHARED_APPEARANCE_SYLANTRO,
|
||||
PFLAG_DISABLE_SRV,
|
||||
PFLAG_DISABLE_SRV503,
|
||||
PFLAG_DISABLE_NAPTR,
|
||||
PFLAG_AUTOFLUSH,
|
||||
PFLAG_NAT_OPTIONS_PING,
|
||||
@@ -249,6 +252,7 @@ typedef enum {
|
||||
PFLAG_LIBERAL_DTMF,
|
||||
PFLAG_AUTO_ASSIGN_PORT,
|
||||
PFLAG_AUTO_ASSIGN_TLS_PORT,
|
||||
PFLAG_SHUTDOWN,
|
||||
/* No new flags below this line */
|
||||
PFLAG_MAX
|
||||
} PFLAGS;
|
||||
@@ -320,7 +324,7 @@ typedef enum {
|
||||
TFLAG_MAX
|
||||
} TFLAGS;
|
||||
|
||||
#define SOFIA_MAX_MSG_QUEUE 51
|
||||
#define SOFIA_MAX_MSG_QUEUE 101
|
||||
#define SOFIA_MSG_QUEUE_SIZE 5000
|
||||
|
||||
struct mod_sofia_globals {
|
||||
@@ -353,6 +357,7 @@ struct mod_sofia_globals {
|
||||
int debug_presence;
|
||||
int debug_sla;
|
||||
int auto_restart;
|
||||
int reg_deny_binding_fetch_and_no_lookup; /* backwards compatibility */
|
||||
int auto_nat;
|
||||
int tracelevel;
|
||||
char *capture_server;
|
||||
@@ -764,6 +769,8 @@ struct private_object {
|
||||
sofia_cid_type_t cid_type;
|
||||
switch_payload_t payload_space;
|
||||
switch_payload_t ianacodes[SWITCH_MAX_CODECS];
|
||||
uint32_t session_timeout;
|
||||
enum nua_session_refresher session_refresher;
|
||||
};
|
||||
|
||||
struct callback_t {
|
||||
@@ -866,8 +873,8 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void
|
||||
|
||||
void launch_sofia_profile_thread(sofia_profile_t *profile);
|
||||
|
||||
switch_status_t sofia_presence_chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint);
|
||||
switch_status_t sofia_presence_chat_send(switch_event_t *message_event);
|
||||
|
||||
void sofia_glue_tech_absorb_sdp(private_object_t *tech_pvt);
|
||||
|
||||
/*
|
||||
@@ -1115,6 +1122,7 @@ switch_t38_options_t *sofia_glue_extract_t38_options(switch_core_session_t *sess
|
||||
char *sofia_glue_get_multipart(switch_core_session_t *session, const char *prefix, const char *sdp, char **mp_type);
|
||||
void sofia_glue_tech_simplify(private_object_t *tech_pvt);
|
||||
switch_console_callback_match_t *sofia_reg_find_reg_url_multi(sofia_profile_t *profile, const char *user, const char *host);
|
||||
switch_console_callback_match_t *sofia_reg_find_reg_url_with_positive_expires_multi(sofia_profile_t *profile, const char *user, const char *host);
|
||||
switch_bool_t sofia_glue_profile_exists(const char *key);
|
||||
void sofia_glue_global_siptrace(switch_bool_t on);
|
||||
void sofia_glue_global_capture(switch_bool_t on);
|
||||
|
||||
@@ -48,7 +48,9 @@ extern su_log_t nth_server_log[];
|
||||
extern su_log_t nua_log[];
|
||||
extern su_log_t soa_log[];
|
||||
extern su_log_t sresolv_log[];
|
||||
#ifdef HAVE_SOFIA_STUN
|
||||
extern su_log_t stun_log[];
|
||||
#endif
|
||||
extern su_log_t su_log_default[];
|
||||
|
||||
static void config_sofia_profile_urls(sofia_profile_t * profile);
|
||||
@@ -85,7 +87,7 @@ void sofia_handle_sip_r_notify(switch_core_session_t *session, int status,
|
||||
sofia_dispatch_event_t *de, tagi_t tags[])
|
||||
{
|
||||
|
||||
if (status >= 300 && sip && sip->sip_call_id) {
|
||||
if (status >= 300 && sip && sip->sip_call_id && (!sofia_private || !sofia_private->is_call)) {
|
||||
char *sql;
|
||||
sql = switch_mprintf("delete from sip_subscriptions where call_id='%q'", sip->sip_call_id->i_id);
|
||||
switch_assert(sql != NULL);
|
||||
@@ -504,6 +506,7 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status,
|
||||
private_object_t *tech_pvt;
|
||||
char *extra_headers;
|
||||
const char *call_info = NULL;
|
||||
const char *vval = NULL;
|
||||
#ifdef MANUAL_BYE
|
||||
int cause;
|
||||
char st[80] = "";
|
||||
@@ -555,6 +558,18 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status,
|
||||
extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_BYE_HEADER_PREFIX);
|
||||
sofia_glue_set_extra_headers(channel, sip, SOFIA_SIP_BYE_HEADER_PREFIX);
|
||||
|
||||
if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) {
|
||||
switch_core_session_t *nsession = NULL;
|
||||
|
||||
switch_core_session_get_partner(session, &nsession);
|
||||
|
||||
if (nsession) {
|
||||
switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_BYE_HEADER_PREFIX_T);
|
||||
switch_core_session_rwunlock(nsession);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch_channel_hangup(channel, cause);
|
||||
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
|
||||
TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_END());
|
||||
@@ -818,14 +833,14 @@ static void our_sofia_event_callback(nua_event_t event,
|
||||
return;
|
||||
}
|
||||
} else if (!zstr(sofia_private->uuid)) {
|
||||
if ((session = switch_core_session_locate(sofia_private->uuid))) {
|
||||
if ((session = de->session) || (session = switch_core_session_locate(sofia_private->uuid))) {
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
channel = switch_core_session_get_channel(session);
|
||||
if (tech_pvt) {
|
||||
switch_mutex_lock(tech_pvt->sofia_mutex);
|
||||
locked = 1;
|
||||
} else {
|
||||
switch_core_session_rwunlock(session);
|
||||
if (session != de->session) switch_core_session_rwunlock(session);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -957,6 +972,7 @@ static void our_sofia_event_callback(nua_event_t event,
|
||||
break;
|
||||
case nua_r_shutdown:
|
||||
if (status >= 200) {
|
||||
sofia_set_pflag(profile, PFLAG_SHUTDOWN);
|
||||
su_root_break(profile->s_root);
|
||||
}
|
||||
break;
|
||||
@@ -1110,7 +1126,7 @@ static void our_sofia_event_callback(nua_event_t event,
|
||||
switch_mutex_unlock(tech_pvt->sofia_mutex);
|
||||
}
|
||||
|
||||
if (session) {
|
||||
if (session && session != de->session) {
|
||||
switch_core_session_rwunlock(session);
|
||||
}
|
||||
}
|
||||
@@ -1145,6 +1161,7 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run(switch_thread_t *thread, void *obj
|
||||
while(switch_queue_pop(q, &pop) == SWITCH_STATUS_SUCCESS && pop) {
|
||||
sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) pop;
|
||||
sofia_process_dispatch_event(&de);
|
||||
switch_cond_next();
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "MSG Thread Ended\n");
|
||||
@@ -1175,7 +1192,7 @@ static void sofia_msg_thread_start(int idx)
|
||||
|
||||
switch_threadattr_create(&thd_attr, mod_sofia_globals.pool);
|
||||
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||||
switch_threadattr_priority_increase(thd_attr);
|
||||
//switch_threadattr_priority_increase(thd_attr);
|
||||
switch_thread_create(&mod_sofia_globals.msg_queue_thread[i],
|
||||
thd_attr,
|
||||
sofia_msg_thread_run,
|
||||
@@ -1254,7 +1271,6 @@ void sofia_event_callback(nua_event_t event,
|
||||
|
||||
if (!zstr(sofia_private->uuid)) {
|
||||
if ((session = switch_core_session_locate(sofia_private->uuid))) {
|
||||
|
||||
if (switch_core_session_running(session)) {
|
||||
switch_core_session_queue_signal_data(session, de);
|
||||
} else {
|
||||
@@ -1788,6 +1804,8 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void
|
||||
NTATAG_USE_SRV(0)),
|
||||
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_NAPTR),
|
||||
NTATAG_USE_NAPTR(0)),
|
||||
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV503),
|
||||
NTATAG_SRV_503(0)),
|
||||
NTATAG_DEFAULT_PROXY(profile->outbound_proxy),
|
||||
NTATAG_SERVER_RPORT(profile->server_rport_level),
|
||||
NTATAG_CLIENT_RPORT(profile->client_rport_level),
|
||||
@@ -1951,9 +1969,17 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void
|
||||
switch_thread_rwlock_wrlock(profile->rwlock);
|
||||
sofia_reg_unregister(profile);
|
||||
nua_shutdown(profile->nua);
|
||||
su_root_run(profile->s_root);
|
||||
|
||||
sanity = 10;
|
||||
while (!sofia_test_pflag(profile, PFLAG_SHUTDOWN)) {
|
||||
su_root_step(profile->s_root, 1000);
|
||||
if (!--sanity) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sofia_clear_pflag_locked(profile, PFLAG_RUNNING);
|
||||
sofia_clear_pflag_locked(profile, PFLAG_SHUTDOWN);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Waiting for worker thread\n");
|
||||
|
||||
switch_thread_join(&st, worker_thread);
|
||||
@@ -2076,8 +2102,10 @@ static su_log_t *sofia_get_logger(const char *name)
|
||||
return soa_log;
|
||||
} else if (!strcasecmp(name, "sresolv")) {
|
||||
return sresolv_log;
|
||||
#ifdef HAVE_SOFIA_STUN
|
||||
} else if (!strcasecmp(name, "stun")) {
|
||||
return stun_log;
|
||||
#endif
|
||||
} else if (!strcasecmp(name, "default")) {
|
||||
return su_log_default;
|
||||
} else {
|
||||
@@ -2104,7 +2132,9 @@ switch_status_t sofia_set_loglevel(const char *name, int level)
|
||||
su_log_set_level(nua_log, level);
|
||||
su_log_set_level(soa_log, level);
|
||||
su_log_set_level(sresolv_log, level);
|
||||
#ifdef HAVE_SOFIA_STUN
|
||||
su_log_set_level(stun_log, level);
|
||||
#endif
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -2189,7 +2219,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag)
|
||||
}
|
||||
|
||||
switch_mutex_lock(mod_sofia_globals.hash_mutex);
|
||||
if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name)) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey))) {
|
||||
if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name)) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) && !gp->deleted) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name);
|
||||
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
|
||||
free(pkey);
|
||||
@@ -2553,7 +2583,7 @@ static void parse_domain_tag(sofia_profile_t *profile, switch_xml_t x_domain_tag
|
||||
if (sofia_glue_add_profile(switch_core_strdup(profile->pool, dname), profile) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Alias [%s] for profile [%s]\n", dname, profile->name);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Alias [%s] for profile [%s] (already exists)\n", dname, profile->name);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Alias [%s] for profile [%s] (already exists)\n", dname, profile->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2701,6 +2731,12 @@ switch_status_t reconfig_sofia(sofia_profile_t *profile)
|
||||
mod_sofia_globals.debug_sla = atoi(val);
|
||||
} else if (!strcasecmp(var, "auto-restart")) {
|
||||
mod_sofia_globals.auto_restart = switch_true(val);
|
||||
} else if (!strcasecmp(var, "reg-deny-binding-fetch-and-no-lookup")) { /* backwards compatibility */
|
||||
mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = switch_true(val); /* remove when noone complains about the extra lookup */
|
||||
if (switch_true(val)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Enabling reg-deny-binding-fetch-and-no-lookup - this functionality is "
|
||||
"deprecated and will be removed - let FS devs know if you think it should stay\n");
|
||||
}
|
||||
} else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) {
|
||||
if( (!strcasecmp(val, "to_host")) || (!strcasecmp(val, "1")) ) {
|
||||
/* old behaviour */
|
||||
@@ -3355,7 +3391,9 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
su_log_redirect(nua_log, logger, NULL);
|
||||
su_log_redirect(soa_log, logger, NULL);
|
||||
su_log_redirect(sresolv_log, logger, NULL);
|
||||
#ifdef HAVE_SOFIA_STUN
|
||||
su_log_redirect(stun_log, logger, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!zstr(profile_name) && (profile = sofia_glue_find_profile(profile_name))) {
|
||||
@@ -3376,6 +3414,7 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
}
|
||||
|
||||
mod_sofia_globals.auto_restart = SWITCH_TRUE;
|
||||
mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = SWITCH_FALSE; /* handle backwards compatilibity - by default use new behavior */
|
||||
mod_sofia_globals.rewrite_multicasted_fs_path = SWITCH_FALSE;
|
||||
|
||||
if ((settings = switch_xml_child(cfg, "global_settings"))) {
|
||||
@@ -3392,6 +3431,12 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
mod_sofia_globals.debug_sla = atoi(val);
|
||||
} else if (!strcasecmp(var, "auto-restart")) {
|
||||
mod_sofia_globals.auto_restart = switch_true(val);
|
||||
} else if (!strcasecmp(var, "reg-deny-binding-fetch-and-no-lookup")) { /* backwards compatibility */
|
||||
mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = switch_true(val); /* remove when noone complains about the extra lookup */
|
||||
if (switch_true(val)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Enabling reg-deny-binding-fetch-and-no-lookup - this functionality is "
|
||||
"deprecated and will be removed - let FS devs know if you think it should stay\n");
|
||||
}
|
||||
} else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) {
|
||||
if( (!strcasecmp(val, "to_host")) || (!strcasecmp(val, "1")) ) {
|
||||
/* old behaviour */
|
||||
@@ -3486,6 +3531,7 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
sofia_set_flag(profile, TFLAG_ENABLE_SOA);
|
||||
sofia_set_pflag(profile, PFLAG_CID_IN_1XX);
|
||||
profile->ndlb |= PFLAG_NDLB_ALLOW_NONDUP_SDP;
|
||||
profile->te = 101;
|
||||
|
||||
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
|
||||
char *var = (char *) switch_xml_attr_soft(param, "name");
|
||||
@@ -3940,6 +3986,10 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
if (switch_true(val)) {
|
||||
sofia_set_pflag(profile, PFLAG_DISABLE_NAPTR);
|
||||
}
|
||||
} else if (!strcasecmp(var, "disable-srv503")) {
|
||||
if (switch_true(val)) {
|
||||
sofia_set_pflag(profile, PFLAG_DISABLE_SRV503);
|
||||
}
|
||||
} else if (!strcasecmp(var, "unregister-on-options-fail")) {
|
||||
if (switch_true(val)) {
|
||||
sofia_set_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL);
|
||||
@@ -4153,10 +4203,14 @@ switch_status_t config_sofia(int reload, char *profile_name)
|
||||
}
|
||||
} else if (!strcasecmp(var, "alias")) {
|
||||
sip_alias_node_t *node;
|
||||
if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) {
|
||||
if ((node->url = switch_core_strdup(profile->pool, val))) {
|
||||
node->next = profile->aliases;
|
||||
profile->aliases = node;
|
||||
if (zstr(val)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Alias Param has no data...\n");
|
||||
} else {
|
||||
if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) {
|
||||
if ((node->url = switch_core_strdup(profile->pool, val))) {
|
||||
node->next = profile->aliases;
|
||||
profile->aliases = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(var, "dialplan")) {
|
||||
@@ -5161,7 +5215,8 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
|
||||
if (channel && (status == 180 || status == 183) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
|
||||
const char *val;
|
||||
if ((val = switch_channel_get_variable(channel, "sip_auto_answer")) && switch_true(val)) {
|
||||
nua_notify(nh, NUTAG_NEWSUB(1), NUTAG_SUBSTATE(nua_substate_active), SIPTAG_EVENT_STR("talk"), TAG_END());
|
||||
nua_notify(nh, NUTAG_NEWSUB(1), NUTAG_WITH_THIS_MSG(de->data->e_msg),
|
||||
NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_EVENT_STR("talk"), TAG_END());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5243,6 +5298,9 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
|
||||
break;
|
||||
case nua_callstate_completing:
|
||||
{
|
||||
const char *invite_full_from = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_from");
|
||||
const char *invite_full_to = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_to");
|
||||
|
||||
if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_3PCC_INVITE) && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
|
||||
sofia_set_flag(tech_pvt, TFLAG_SDP);
|
||||
|
||||
@@ -5266,12 +5324,18 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
|
||||
const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri");
|
||||
|
||||
nua_ack(nh,
|
||||
TAG_IF(!zstr(invite_full_via), SIPTAG_VIA_STR(invite_full_via)),
|
||||
TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)),
|
||||
TAG_END());
|
||||
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
|
||||
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
|
||||
TAG_IF(!zstr(invite_full_via), SIPTAG_VIA_STR(invite_full_via)),
|
||||
TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)),
|
||||
TAG_END());
|
||||
|
||||
} else {
|
||||
nua_ack(nh, TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END());
|
||||
nua_ack(nh,
|
||||
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
|
||||
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
|
||||
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
|
||||
TAG_END());
|
||||
}
|
||||
}
|
||||
goto done;
|
||||
@@ -5558,6 +5622,12 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
|
||||
}
|
||||
goto done;
|
||||
} else {
|
||||
|
||||
if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_NEGOTIATED)) {
|
||||
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_END());
|
||||
goto done;
|
||||
}
|
||||
|
||||
sofia_set_flag_locked(tech_pvt, TFLAG_REINVITE);
|
||||
|
||||
if (tech_pvt->num_codecs) {
|
||||
@@ -5573,6 +5643,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
|
||||
if (sofia_glue_tech_choose_port(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0);
|
||||
|
||||
if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) {
|
||||
@@ -6596,7 +6667,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t
|
||||
const char *signal_ptr;
|
||||
const char *rec_header;
|
||||
const char *clientcode_header;
|
||||
switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) };
|
||||
switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0), 0, SWITCH_DTMF_ENDPOINT };
|
||||
switch_event_t *event;
|
||||
private_object_t *tech_pvt = NULL;
|
||||
|
||||
@@ -6735,35 +6806,37 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dtmf.digit && (tech_pvt->dtmf_type == DTMF_INFO ||
|
||||
sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || sofia_test_flag(tech_pvt, TFLAG_LIBERAL_DTMF))) {
|
||||
/* queue it up */
|
||||
switch_channel_queue_dtmf(channel, &dtmf);
|
||||
if (dtmf.digit) {
|
||||
if (tech_pvt->dtmf_type == DTMF_INFO ||
|
||||
sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || sofia_test_flag(tech_pvt, TFLAG_LIBERAL_DTMF)) {
|
||||
/* queue it up */
|
||||
switch_channel_queue_dtmf(channel, &dtmf);
|
||||
|
||||
/* print debug info */
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "INFO DTMF(%c)\n", dtmf.digit);
|
||||
/* print debug info */
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "INFO DTMF(%c)\n", dtmf.digit);
|
||||
|
||||
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
|
||||
const char *uuid;
|
||||
switch_core_session_t *session_b;
|
||||
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
|
||||
const char *uuid;
|
||||
switch_core_session_t *session_b;
|
||||
|
||||
if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (session_b = switch_core_session_locate(uuid))) {
|
||||
while (switch_channel_has_dtmf(channel)) {
|
||||
switch_dtmf_t idtmf = { 0, 0 };
|
||||
if (switch_channel_dequeue_dtmf(channel, &idtmf) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_core_session_send_dtmf(session_b, &idtmf);
|
||||
if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (session_b = switch_core_session_locate(uuid))) {
|
||||
while (switch_channel_has_dtmf(channel)) {
|
||||
switch_dtmf_t idtmf = { 0, 0 };
|
||||
if (switch_channel_dequeue_dtmf(channel, &idtmf) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_core_session_send_dtmf(session_b, &idtmf);
|
||||
}
|
||||
}
|
||||
|
||||
switch_core_session_rwunlock(session_b);
|
||||
}
|
||||
|
||||
switch_core_session_rwunlock(session_b);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send 200 OK response */
|
||||
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
|
||||
"IGNORE INFO DTMF(%c) (This channel was not configured to use INFO DTMF!)\n", dtmf.digit);
|
||||
/* Send 200 OK response */
|
||||
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
|
||||
"IGNORE INFO DTMF(%c) (This channel was not configured to use INFO DTMF!)\n", dtmf.digit);
|
||||
}
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
@@ -7423,6 +7496,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
|
||||
if (sofia_glue_check_nat(profile, tech_pvt->remote_ip)) {
|
||||
tech_pvt->user_via = sofia_glue_create_external_via(session, profile, tech_pvt->transport);
|
||||
nua_set_hparams(tech_pvt->nh, SIPTAG_VIA_STR(tech_pvt->user_via), TAG_END());
|
||||
}
|
||||
|
||||
if (sip->sip_contact && sip->sip_contact->m_url) {
|
||||
@@ -7659,6 +7733,12 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
}
|
||||
}
|
||||
|
||||
check_decode(displayname, session);
|
||||
tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
|
||||
from_user,
|
||||
dialplan,
|
||||
displayname, from_user, network_ip, from_user, aniii, NULL, MODNAME, context, destination_number);
|
||||
|
||||
if (!bnh && sip->sip_replaces) {
|
||||
if (!(bnh = nua_handle_by_replaces(nua, sip->sip_replaces))) {
|
||||
if (!(bnh = nua_handle_by_call_id(nua, sip->sip_replaces->rp_call_id))) {
|
||||
@@ -7679,10 +7759,32 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
private_object_t *b_tech_pvt = NULL;
|
||||
const char *app = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_VARIABLE);
|
||||
const char *data = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_DATA_VARIABLE);
|
||||
switch_caller_profile_t *orig_cp;
|
||||
const char *sent_name, *sent_number;
|
||||
orig_cp = switch_channel_get_caller_profile(b_channel);
|
||||
|
||||
sent_name = switch_channel_get_variable(b_channel, "last_sent_callee_id_name");
|
||||
sent_number = switch_channel_get_variable(b_channel, "last_sent_callee_id_number");
|
||||
|
||||
if (!zstr(sent_name) && !zstr(sent_number)) {
|
||||
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, sent_name);
|
||||
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, sent_number);
|
||||
} else {
|
||||
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
|
||||
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_name);
|
||||
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_number);
|
||||
} else {
|
||||
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_name);
|
||||
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_number);
|
||||
}
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Setting NAT mode based on %s\n", is_nat);
|
||||
|
||||
|
||||
if (app && data && !strcasecmp(app, "conference")) {
|
||||
destination_number = switch_core_session_sprintf(session, "answer,conference:%s", data);
|
||||
dialplan = "inline";
|
||||
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,conference:%s", data);
|
||||
tech_pvt->caller_profile->dialplan = "inline";
|
||||
} else {
|
||||
if (switch_core_session_check_interface(b_session, sofia_endpoint_interface)) {
|
||||
b_tech_pvt = switch_core_session_get_private(b_session);
|
||||
@@ -7712,7 +7814,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
if (switch_core_session_check_interface(c_session, sofia_endpoint_interface)) {
|
||||
c_tech_pvt = switch_core_session_get_private(c_session);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!one_leg &&
|
||||
(!b_tech_pvt || !sofia_test_flag(b_tech_pvt, TFLAG_SIP_HOLD)) &&
|
||||
@@ -7729,21 +7831,25 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
}
|
||||
|
||||
if (do_conf) {
|
||||
destination_number = switch_core_session_sprintf(session, "answer,conference:%s@sla+flags{mintwo}", uuid);
|
||||
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
|
||||
"answer,conference:%s@sla+flags{mintwo}", uuid);
|
||||
} else {
|
||||
if (one_leg && c_app) {
|
||||
if (c_data) {
|
||||
destination_number = switch_core_session_sprintf(session, "answer,%s:%s", c_app, c_data);
|
||||
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
|
||||
"answer,%s:%s", c_app, c_data);
|
||||
} else {
|
||||
destination_number = switch_core_session_sprintf(session, "answer,%s", c_app);
|
||||
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
|
||||
"answer,%s", c_app);
|
||||
}
|
||||
} else {
|
||||
switch_channel_mark_hold(b_channel, SWITCH_FALSE);
|
||||
destination_number = switch_core_session_sprintf(session, "answer,intercept:%s", uuid);
|
||||
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
|
||||
"answer,intercept:%s", uuid);
|
||||
}
|
||||
}
|
||||
|
||||
dialplan = "inline";
|
||||
tech_pvt->caller_profile->dialplan = "inline";
|
||||
}
|
||||
}
|
||||
switch_core_session_rwunlock(b_session);
|
||||
@@ -7752,11 +7858,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_
|
||||
nua_handle_unref(bnh);
|
||||
}
|
||||
|
||||
check_decode(displayname, session);
|
||||
tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
|
||||
from_user,
|
||||
dialplan,
|
||||
displayname, from_user, network_ip, from_user, aniii, NULL, MODNAME, context, destination_number);
|
||||
|
||||
|
||||
if (tech_pvt->caller_profile) {
|
||||
|
||||
|
||||
@@ -48,12 +48,23 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
|
||||
uint32_t port = t38_options->local_port;
|
||||
const char *family = "IP4";
|
||||
const char *username = tech_pvt->profile->username;
|
||||
char MMR[32] = "";
|
||||
char JBIG[32] = "";
|
||||
char FILLBIT[32] = "";
|
||||
const char *bit_removal_on = "a=T38FaxFillBitRemoval\n";
|
||||
const char *bit_removal_off = "";
|
||||
|
||||
const char *mmr_on = "a=T38FaxTranscodingMMR\n";
|
||||
const char *mmr_off = "";
|
||||
|
||||
const char *jbig_on = "a=T38FaxTranscodingJBIG\n";
|
||||
const char *jbig_off = "";
|
||||
const char *var;
|
||||
int broken_boolean;
|
||||
|
||||
//sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA);
|
||||
|
||||
var = switch_channel_get_variable(tech_pvt->channel, "t38_broken_boolean");
|
||||
|
||||
broken_boolean = switch_true(var);
|
||||
|
||||
if (!ip) {
|
||||
if (!(ip = tech_pvt->adv_sdp_audio_ip)) {
|
||||
ip = tech_pvt->proxy_sdp_audio_ip;
|
||||
@@ -94,48 +105,6 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
|
||||
"o=%s %010u %010u IN %s %s\n"
|
||||
"s=%s\n" "c=IN %s %s\n" "t=0 0\n", username, tech_pvt->owner_id, tech_pvt->session_id, family, ip, username, family, ip);
|
||||
|
||||
if(t38_options->T38FaxFillBitRemoval) {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval"))) {
|
||||
switch_snprintf(FILLBIT, sizeof(FILLBIT), "a=T38FaxFillBitRemoval:1\n");
|
||||
} else {
|
||||
switch_set_string(FILLBIT, "a=T38FaxFillBitRemoval\n");
|
||||
}
|
||||
} else {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval"))) {
|
||||
switch_snprintf(FILLBIT, sizeof(FILLBIT), "a=T38FaxFillBitRemoval:0\n");
|
||||
} else {
|
||||
switch_set_string(FILLBIT, "");
|
||||
}
|
||||
}
|
||||
|
||||
if( t38_options->T38FaxTranscodingMMR) {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR"))) {
|
||||
switch_snprintf(MMR, sizeof(MMR), "a=T38FaxTranscodingMMR:1\n");
|
||||
} else {
|
||||
switch_set_string(MMR, "a=T38FaxTranscodingMMR\n");
|
||||
}
|
||||
} else {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR"))) {
|
||||
switch_snprintf(MMR, sizeof(MMR), "a=T38FaxTranscodingMMR:0\n");
|
||||
} else {
|
||||
switch_set_string(MMR, "");
|
||||
}
|
||||
}
|
||||
|
||||
if( t38_options->T38FaxTranscodingJBIG) {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG"))) {
|
||||
switch_snprintf(JBIG, sizeof(JBIG), "a=T38FaxTranscodingJBIG:1\n");
|
||||
} else {
|
||||
switch_set_string(JBIG, "a=T38FaxTranscodingJBIG\n");
|
||||
}
|
||||
} else {
|
||||
if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG"))) {
|
||||
switch_snprintf(JBIG, sizeof(JBIG), "a=T38FaxTranscodingJBIG:0\n");
|
||||
} else {
|
||||
switch_set_string(JBIG, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (t38_options->T38FaxMaxBuffer) {
|
||||
switch_snprintf(max_buf, sizeof(max_buf), "a=T38FaxMaxBuffer:%d\n", t38_options->T38FaxMaxBuffer);
|
||||
};
|
||||
@@ -145,6 +114,21 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
if (broken_boolean) {
|
||||
bit_removal_on = "a=T38FaxFillBitRemoval:1\n";
|
||||
bit_removal_off = "a=T38FaxFillBitRemoval:0\n";
|
||||
|
||||
mmr_on = "a=T38FaxTranscodingMMR:1\n";
|
||||
mmr_off = "a=T38FaxTranscodingMMR:0\n";
|
||||
|
||||
jbig_on = "a=T38FaxTranscodingJBIG:1\n";
|
||||
jbig_off = "a=T38FaxTranscodingJBIG:0\n";
|
||||
|
||||
}
|
||||
|
||||
|
||||
switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
|
||||
"m=image %d udptl t38\n"
|
||||
"a=T38FaxVersion:%d\n"
|
||||
@@ -160,9 +144,9 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
|
||||
port,
|
||||
t38_options->T38FaxVersion,
|
||||
t38_options->T38MaxBitRate,
|
||||
FILLBIT,
|
||||
MMR,
|
||||
JBIG,
|
||||
t38_options->T38FaxFillBitRemoval ? bit_removal_on : bit_removal_off,
|
||||
t38_options->T38FaxTranscodingMMR ? mmr_on : mmr_off,
|
||||
t38_options->T38FaxTranscodingJBIG ? jbig_on : jbig_off,
|
||||
t38_options->T38FaxRateManagement,
|
||||
max_buf,
|
||||
max_data,
|
||||
@@ -201,7 +185,12 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen,
|
||||
|
||||
for (i = 0; i < tech_pvt->num_codecs; i++) {
|
||||
const switch_codec_implementation_t *imp = tech_pvt->codecs[i];
|
||||
|
||||
int this_ptime = (imp->microseconds_per_packet / 1000);
|
||||
|
||||
if (!strcasecmp(imp->iananame, "ilbc")) {
|
||||
this_ptime = 20;
|
||||
}
|
||||
|
||||
if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) {
|
||||
continue;
|
||||
}
|
||||
@@ -210,12 +199,12 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen,
|
||||
if (!cur_ptime) {
|
||||
#if 0
|
||||
if (ptime) {
|
||||
if (ptime != imp->microseconds_per_packet / 1000) {
|
||||
if (ptime != this_ptime) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
|
||||
"Codec %s payload %d added to sdp wanting ptime %d but it's already %d (%s:%d:%d), disabling ptime.\n",
|
||||
imp->iananame,
|
||||
tech_pvt->ianacodes[i],
|
||||
imp->microseconds_per_packet / 1000,
|
||||
this_ptime,
|
||||
ptime,
|
||||
tech_pvt->codecs[0]->iananame,
|
||||
tech_pvt->codecs[0]->ianacode,
|
||||
@@ -224,15 +213,15 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen,
|
||||
noptime = 1;
|
||||
}
|
||||
} else {
|
||||
ptime = imp->microseconds_per_packet / 1000;
|
||||
ptime = this_ptime;
|
||||
}
|
||||
#else
|
||||
if (!ptime) {
|
||||
ptime = imp->microseconds_per_packet / 1000;
|
||||
ptime = this_ptime;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if ((imp->microseconds_per_packet / 1000) != cur_ptime) {
|
||||
if (this_ptime != cur_ptime) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -266,18 +255,23 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen,
|
||||
for (i = 0; i < tech_pvt->num_codecs; i++) {
|
||||
const switch_codec_implementation_t *imp = tech_pvt->codecs[i];
|
||||
char *fmtp = imp->fmtp;
|
||||
|
||||
int this_ptime = imp->microseconds_per_packet / 1000;
|
||||
|
||||
if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcasecmp(imp->iananame, "ilbc")) {
|
||||
this_ptime = 20;
|
||||
}
|
||||
|
||||
if (!noptime) {
|
||||
if (!cur_ptime) {
|
||||
if (!ptime) {
|
||||
ptime = imp->microseconds_per_packet / 1000;
|
||||
ptime = this_ptime;
|
||||
}
|
||||
} else {
|
||||
if ((imp->microseconds_per_packet / 1000) != cur_ptime) {
|
||||
if (this_ptime != cur_ptime) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -496,7 +490,7 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch
|
||||
|
||||
switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt);
|
||||
|
||||
if (tech_pvt->dtmf_type == DTMF_2833 && tech_pvt->te > 95) {
|
||||
if ((tech_pvt->dtmf_type == DTMF_2833 || sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || sofia_test_flag(tech_pvt, TFLAG_LIBERAL_DTMF)) && tech_pvt->te > 95) {
|
||||
switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->te);
|
||||
}
|
||||
|
||||
@@ -583,6 +577,10 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch
|
||||
|
||||
this_ptime = imp->microseconds_per_packet / 1000;
|
||||
|
||||
if (!strcasecmp(imp->iananame, "ilbc")) {
|
||||
this_ptime = 20;
|
||||
}
|
||||
|
||||
if (cur_ptime != this_ptime) {
|
||||
char *bp = buf;
|
||||
cur_ptime = this_ptime;
|
||||
@@ -658,6 +656,8 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch
|
||||
|
||||
if (ov_fmtp) {
|
||||
pass_fmtp = ov_fmtp;
|
||||
} else {
|
||||
pass_fmtp = switch_channel_get_variable(tech_pvt->channel, "sip_video_fmtp");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,6 +874,7 @@ void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t *
|
||||
switch_channel_set_cap(tech_pvt->channel, CC_PROXY_MEDIA);
|
||||
switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER);
|
||||
switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP);
|
||||
switch_channel_set_cap(tech_pvt->channel, CC_QUEUEABLE_DTMF_DELAY);
|
||||
|
||||
switch_core_session_set_private(session, tech_pvt);
|
||||
|
||||
@@ -1055,8 +1056,9 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int forc
|
||||
tech_pvt->adv_sdp_audio_port = sdp_port;
|
||||
tech_pvt->adv_sdp_audio_ip = tech_pvt->extrtpip = switch_core_session_strdup(tech_pvt->session, use_ip);
|
||||
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->local_sdp_audio_ip);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, "%d", sdp_port);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1907,7 +1909,7 @@ void sofia_glue_set_extra_headers(switch_channel_t *channel, sip_t const *sip, c
|
||||
}
|
||||
|
||||
for (un = sip->sip_unknown; un; un = un->un_next) {
|
||||
if (!strncasecmp(un->un_name, "X-", 2) || !strncasecmp(un->un_name, "P-", 2)) {
|
||||
if ((!strncasecmp(un->un_name, "X-", 2) && strncasecmp(un->un_name, "X-FS-", 5)) || !strncasecmp(un->un_name, "P-", 2)) {
|
||||
if (!zstr(un->un_value)) {
|
||||
switch_snprintf(name, sizeof(name), "%s%s", prefix, un->un_name);
|
||||
switch_channel_set_variable(channel, name, un->un_value);
|
||||
@@ -2107,14 +2109,11 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
|
||||
if (zstr(tech_pvt->invite_contact)) {
|
||||
const char *contact;
|
||||
if ((contact = switch_channel_get_variable(channel, "sip_contact_user"))) {
|
||||
char *ip_addr;
|
||||
char *ip_addr = tech_pvt->profile->sipip;
|
||||
char *ipv6;
|
||||
|
||||
if (!zstr(tech_pvt->remote_ip) && sofia_glue_check_nat(tech_pvt->profile, tech_pvt->remote_ip)) {
|
||||
ip_addr = (switch_check_network_list_ip(tech_pvt->remote_ip, tech_pvt->profile->local_network))
|
||||
? tech_pvt->profile->sipip : tech_pvt->profile->extsipip;
|
||||
} else {
|
||||
ip_addr = tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip;
|
||||
if ( !zstr(tech_pvt->remote_ip) && sofia_glue_check_nat(tech_pvt->profile, tech_pvt->remote_ip ) ) {
|
||||
ip_addr = tech_pvt->profile->extsipip;
|
||||
}
|
||||
|
||||
ipv6 = strchr(ip_addr, ':');
|
||||
@@ -2271,6 +2270,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
|
||||
use_number = tech_pvt->caller_profile->caller_id_number;
|
||||
}
|
||||
|
||||
check_decode(use_name, session);
|
||||
|
||||
switch (cid_type) {
|
||||
case CID_TYPE_PID:
|
||||
@@ -2366,6 +2366,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
|
||||
extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_HEADER_PREFIX);
|
||||
|
||||
session_timeout = tech_pvt->profile->session_timeout;
|
||||
|
||||
if ((val = switch_channel_get_variable(channel, SOFIA_SESSION_TIMEOUT))) {
|
||||
int v_session_timeout = atoi(val);
|
||||
if (v_session_timeout >= 0) {
|
||||
@@ -2420,17 +2421,21 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
|
||||
sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA);
|
||||
}
|
||||
|
||||
if (sofia_test_flag(tech_pvt, TFLAG_RECOVERED)) {
|
||||
session_timeout = 0;
|
||||
if ((tech_pvt->session_timeout = session_timeout)) {
|
||||
tech_pvt->session_refresher = switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? nua_local_refresher : nua_remote_refresher;
|
||||
} else {
|
||||
tech_pvt->session_refresher = nua_no_refresher;
|
||||
}
|
||||
|
||||
|
||||
if (sofia_use_soa(tech_pvt)) {
|
||||
nua_invite(tech_pvt->nh,
|
||||
NUTAG_AUTOANSWER(0),
|
||||
TAG_IF(zstr(tech_pvt->local_sdp_str), NUTAG_AUTOACK(0)),
|
||||
TAG_IF(!zstr(tech_pvt->local_sdp_str), NUTAG_AUTOACK(1)),
|
||||
NUTAG_SESSION_TIMER(session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(session_timeout ? nua_local_refresher : nua_no_refresher),
|
||||
//TAG_IF(zstr(tech_pvt->local_sdp_str), NUTAG_AUTOACK(0)),
|
||||
//TAG_IF(!zstr(tech_pvt->local_sdp_str), NUTAG_AUTOACK(1)),
|
||||
// The code above is breaking things...... grrr WE need this because we handle our own acks and there are 3pcc cases in there too
|
||||
NUTAG_AUTOACK(0),
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(sofia_test_flag(tech_pvt, TFLAG_RECOVERED), NUTAG_INVITE_TIMER(UINT_MAX)),
|
||||
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
|
||||
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
|
||||
@@ -2463,8 +2468,8 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
|
||||
nua_invite(tech_pvt->nh,
|
||||
NUTAG_AUTOANSWER(0),
|
||||
NUTAG_AUTOACK(0),
|
||||
NUTAG_SESSION_TIMER(session_timeout),
|
||||
TAG_IF(session_timeout, NUTAG_SESSION_REFRESHER(nua_remote_refresher)),
|
||||
NUTAG_SESSION_TIMER(tech_pvt->session_timeout),
|
||||
NUTAG_SESSION_REFRESHER(tech_pvt->session_refresher),
|
||||
TAG_IF(sofia_test_flag(tech_pvt, TFLAG_RECOVERED), NUTAG_INVITE_TIMER(UINT_MAX)),
|
||||
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
|
||||
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
|
||||
@@ -3029,7 +3034,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f
|
||||
}
|
||||
|
||||
if (!(sofia_test_pflag(tech_pvt->profile, PFLAG_REWRITE_TIMESTAMPS) ||
|
||||
((val = switch_channel_get_variable(tech_pvt->channel, "rtp_rewrite_timestamps")) && !switch_true(val)))) {
|
||||
((val = switch_channel_get_variable(tech_pvt->channel, "rtp_rewrite_timestamps")) && switch_true(val)))) {
|
||||
flags |= SWITCH_RTP_FLAG_RAW_WRITE;
|
||||
}
|
||||
|
||||
@@ -3109,8 +3114,9 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f
|
||||
}
|
||||
|
||||
switch_snprintf(tmp, sizeof(tmp), "%d", tech_pvt->local_sdp_audio_port);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->local_sdp_audio_ip);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp);
|
||||
switch_channel_set_variable(tech_pvt->channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip);
|
||||
|
||||
if (tech_pvt->rtp_session && sofia_test_flag(tech_pvt, TFLAG_REINVITE)) {
|
||||
const char *rport = NULL;
|
||||
@@ -3350,11 +3356,13 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f
|
||||
if (tech_pvt->te) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", tech_pvt->te);
|
||||
switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", tech_pvt->te);
|
||||
}
|
||||
|
||||
if (tech_pvt->recv_te) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set 2833 dtmf receive payload to %u\n", tech_pvt->recv_te);
|
||||
switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, tech_pvt->recv_te);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_recv_payload", "%d", tech_pvt->recv_te);
|
||||
}
|
||||
|
||||
if (tech_pvt->audio_recv_pt != tech_pvt->agreed_pt) {
|
||||
@@ -3983,11 +3991,11 @@ void sofia_glue_copy_t38_options(switch_t38_options_t *t38_options, switch_core_
|
||||
switch_channel_set_private(channel, "t38_options", local_t38_options);
|
||||
|
||||
}
|
||||
|
||||
static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_session_t *sdp, sdp_media_t *m)
|
||||
{
|
||||
switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
|
||||
sdp_attribute_t *attr;
|
||||
const char *var;
|
||||
|
||||
if (!t38_options) {
|
||||
t38_options = switch_core_session_alloc(tech_pvt->session, sizeof(switch_t38_options_t));
|
||||
@@ -4003,6 +4011,12 @@ static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_
|
||||
|
||||
t38_options->remote_port = (switch_port_t)m->m_port;
|
||||
|
||||
if (sdp->sdp_origin) {
|
||||
t38_options->sdp_o_line = switch_core_session_strdup(tech_pvt->session, sdp->sdp_origin->o_username);
|
||||
} else {
|
||||
t38_options->sdp_o_line = "unknown";
|
||||
}
|
||||
|
||||
if (m->m_connections && m->m_connections->c_address) {
|
||||
t38_options->remote_ip = switch_core_session_strdup(tech_pvt->session, m->m_connections->c_address);
|
||||
} else if (sdp && sdp->sdp_connection && sdp->sdp_connection->c_address) {
|
||||
@@ -4015,41 +4029,11 @@ static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_
|
||||
} else if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) {
|
||||
t38_options->T38MaxBitRate = (uint32_t) atoi(attr->a_value);
|
||||
} else if (!strcasecmp(attr->a_name, "T38FaxFillBitRemoval")) {
|
||||
if (switch_stristr("T38FaxFillBitRemoval:", tech_pvt->remote_sdp_str)) {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval", "true");
|
||||
if (atoi(attr->a_value) == 0) {
|
||||
t38_options->T38FaxFillBitRemoval = SWITCH_FALSE;
|
||||
} else {
|
||||
t38_options->T38FaxFillBitRemoval = SWITCH_TRUE;
|
||||
}
|
||||
} else {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval", "false");
|
||||
t38_options->T38FaxFillBitRemoval = SWITCH_TRUE;
|
||||
}
|
||||
t38_options->T38FaxFillBitRemoval = switch_safe_atoi(attr->a_value, 1);
|
||||
} else if (!strcasecmp(attr->a_name, "T38FaxTranscodingMMR")) {
|
||||
if (switch_stristr("T38FaxTranscodingMMR:", tech_pvt->remote_sdp_str)) {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR", "true");
|
||||
if (atoi(attr->a_value) == 0) {
|
||||
t38_options->T38FaxTranscodingMMR = SWITCH_FALSE;
|
||||
} else {
|
||||
t38_options->T38FaxTranscodingMMR = SWITCH_TRUE;
|
||||
}
|
||||
} else {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR", "false");
|
||||
t38_options->T38FaxTranscodingMMR = SWITCH_TRUE;
|
||||
}
|
||||
t38_options->T38FaxTranscodingMMR = switch_safe_atoi(attr->a_value, 1);
|
||||
} else if (!strcasecmp(attr->a_name, "T38FaxTranscodingJBIG")) {
|
||||
if (switch_stristr("T38FaxTranscodingJBIG:", tech_pvt->remote_sdp_str)) {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG", "true");
|
||||
if (atoi(attr->a_value) == 0) {
|
||||
t38_options->T38FaxTranscodingJBIG = SWITCH_FALSE;
|
||||
} else {
|
||||
t38_options->T38FaxTranscodingJBIG = SWITCH_TRUE;
|
||||
}
|
||||
} else {
|
||||
switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG", "false");
|
||||
t38_options->T38FaxTranscodingJBIG = SWITCH_TRUE;
|
||||
}
|
||||
t38_options->T38FaxTranscodingJBIG = switch_safe_atoi(attr->a_value, 1);
|
||||
} else if (!strcasecmp(attr->a_name, "T38FaxRateManagement") && attr->a_value) {
|
||||
t38_options->T38FaxRateManagement = switch_core_session_strdup(tech_pvt->session, attr->a_value);
|
||||
} else if (!strcasecmp(attr->a_name, "T38FaxMaxBuffer") && attr->a_value) {
|
||||
@@ -4067,20 +4051,8 @@ static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_
|
||||
switch_channel_set_private(tech_pvt->channel, "t38_options", t38_options);
|
||||
switch_channel_set_app_flag_key("T38", tech_pvt->channel, CF_APP_T38);
|
||||
|
||||
if ((var = switch_channel_get_variable(tech_pvt->channel, "sip_execute_on_image"))) {
|
||||
char *app, *arg = NULL;
|
||||
app = switch_core_session_strdup(tech_pvt->session, var);
|
||||
|
||||
if (strstr(app, "::")) {
|
||||
switch_core_session_execute_application_async(tech_pvt->session, app, arg);
|
||||
} else {
|
||||
if ((arg = strchr(app, ' '))) {
|
||||
*arg++ = '\0';
|
||||
}
|
||||
|
||||
switch_core_session_execute_application(tech_pvt->session, app, arg);
|
||||
}
|
||||
}
|
||||
switch_channel_execute_on(tech_pvt->channel, "sip_execute_on_image");
|
||||
switch_channel_api_on(tech_pvt->channel, "sip_api_on_image");
|
||||
|
||||
return t38_options;
|
||||
}
|
||||
@@ -4339,7 +4311,9 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
}
|
||||
}
|
||||
|
||||
if ((m = sdp->sdp_media) && (m->m_mode == sdp_sendonly || m->m_mode == sdp_inactive)) {
|
||||
if ((m = sdp->sdp_media) &&
|
||||
(m->m_mode == sdp_sendonly || m->m_mode == sdp_inactive ||
|
||||
(m->m_connections && m->m_connections->c_address && !strcmp(m->m_connections->c_address, "0.0.0.0")))) {
|
||||
sendonly = 2; /* global sendonly always wins */
|
||||
}
|
||||
|
||||
@@ -4408,6 +4382,11 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
total_codecs = tech_pvt->num_negotiated_codecs;
|
||||
}
|
||||
|
||||
if (switch_stristr("T38FaxFillBitRemoval:", r_sdp) || switch_stristr("T38FaxTranscodingMMR:", r_sdp) ||
|
||||
switch_stristr("T38FaxTranscodingJBIG:", r_sdp)) {
|
||||
switch_channel_set_variable(tech_pvt->channel, "t38_broken_boolean", "true");
|
||||
}
|
||||
|
||||
for (m = sdp->sdp_media; m; m = m->m_next) {
|
||||
sdp_connection_t *connection;
|
||||
switch_core_session_t *other_session;
|
||||
@@ -4426,6 +4405,11 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
if (got_udptl && m->m_type == sdp_media_image && m->m_port) {
|
||||
switch_t38_options_t *t38_options = tech_process_udptl(tech_pvt, sdp, m);
|
||||
|
||||
if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_NEGOTIATED)) {
|
||||
match = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38)) {
|
||||
sofia_set_flag(tech_pvt, TFLAG_NOREPLY);
|
||||
}
|
||||
@@ -4442,10 +4426,12 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
pass = switch_true(var);
|
||||
}
|
||||
|
||||
/* can't remember if this is necessary but its causing a bug so i'll leave this comment here to remind me
|
||||
if (sofia_test_flag(tech_pvt, TFLAG_T38_PASSTHRU)) {
|
||||
pass = 0;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE) ||
|
||||
switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA) || !switch_rtp_ready(tech_pvt->rtp_session)) {
|
||||
pass = 0;
|
||||
@@ -4606,7 +4592,7 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
greed:
|
||||
x = 0;
|
||||
|
||||
if (tech_pvt->rm_encoding) { // && !sofia_test_flag(tech_pvt, TFLAG_REINVITE)) {
|
||||
if (tech_pvt->rm_encoding && !(sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || sofia_test_flag(tech_pvt, TFLAG_LIBERAL_DTMF))) { // && !sofia_test_flag(tech_pvt, TFLAG_REINVITE)) {
|
||||
char *remote_host = tech_pvt->remote_sdp_audio_ip;
|
||||
switch_port_t remote_port = tech_pvt->remote_sdp_audio_port;
|
||||
int same = 0;
|
||||
@@ -4831,27 +4817,41 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s
|
||||
}
|
||||
}
|
||||
|
||||
if (!best_te && (sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || sofia_test_flag(tech_pvt, TFLAG_LIBERAL_DTMF))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
|
||||
"No 2833 in SDP. Liberal DTMF mode adding %d as telephone-event.\n", tech_pvt->profile->te);
|
||||
best_te = tech_pvt->profile->te;
|
||||
}
|
||||
|
||||
if (best_te) {
|
||||
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
|
||||
te = tech_pvt->te = (switch_payload_t) best_te;
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", best_te);
|
||||
if (tech_pvt->rtp_session) {
|
||||
switch_rtp_set_telephony_event(tech_pvt->rtp_session, (switch_payload_t) best_te);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", best_te);
|
||||
}
|
||||
} else {
|
||||
te = tech_pvt->recv_te = tech_pvt->te = (switch_payload_t) best_te;
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send/recv payload to %u\n", te);
|
||||
if (tech_pvt->rtp_session) {
|
||||
switch_rtp_set_telephony_event(tech_pvt->rtp_session, te);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", te);
|
||||
switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, te);
|
||||
switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_recv_payload", "%d", te);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (tech_pvt->dtmf_type == DTMF_2833) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Disable 2833 dtmf\n");
|
||||
/* by default, use SIP INFO if 2833 is not in the SDP */
|
||||
if (!switch_false(switch_channel_get_variable(channel, "sip_info_when_no_2833"))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No 2833 in SDP. Disable 2833 dtmf and switch to INFO\n");
|
||||
switch_channel_set_variable(tech_pvt->channel, "dtmf_type", "info");
|
||||
tech_pvt->dtmf_type = DTMF_INFO;
|
||||
te = tech_pvt->recv_te = tech_pvt->te = 0;
|
||||
} else {
|
||||
switch_channel_set_variable(tech_pvt->channel, "dtmf_type", "none");
|
||||
tech_pvt->dtmf_type = DTMF_NONE;
|
||||
te = tech_pvt->recv_te = 0;
|
||||
te = tech_pvt->recv_te = tech_pvt->te = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5381,9 +5381,19 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
|
||||
tech_pvt->remote_port = atoi(switch_str_nil(switch_channel_get_variable(channel, "sip_network_port")));
|
||||
tech_pvt->caller_profile = switch_channel_get_caller_profile(channel);
|
||||
|
||||
if ((tmp = switch_channel_get_variable(tech_pvt->channel, "sip_2833_send_payload"))) {
|
||||
int te = atoi(tmp);
|
||||
if (te > 64) {
|
||||
tech_pvt->te = te;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((tmp = switch_channel_get_variable(tech_pvt->channel, "sip_2833_recv_payload"))) {
|
||||
int te = atoi(tmp);
|
||||
if (te > 64) {
|
||||
tech_pvt->recv_te = te;
|
||||
}
|
||||
}
|
||||
|
||||
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
|
||||
tech_pvt->dest = switch_core_session_sprintf(session, "sip:%s", switch_channel_get_variable(channel, "sip_req_uri"));
|
||||
@@ -5437,6 +5447,7 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
|
||||
if (session) {
|
||||
switch_caller_extension_t *extension = NULL;
|
||||
const char *ip = switch_channel_get_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE);
|
||||
const char *a_ip = switch_channel_get_variable(channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE);
|
||||
const char *port = switch_channel_get_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE);
|
||||
const char *r_ip = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE);
|
||||
const char *r_port = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE);
|
||||
@@ -5476,11 +5487,15 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
|
||||
tech_pvt->adv_sdp_audio_ip = tech_pvt->extrtpip = (char *) ip;
|
||||
tech_pvt->adv_sdp_audio_port = tech_pvt->local_sdp_audio_port = (switch_port_t)atoi(port);
|
||||
|
||||
if ((tmp = switch_channel_get_variable(channel, "local_media_ip"))) {
|
||||
tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, tmp);
|
||||
if (!zstr(ip)) {
|
||||
tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, ip);
|
||||
tech_pvt->rtpip = tech_pvt->local_sdp_audio_ip;
|
||||
}
|
||||
|
||||
if (!zstr(a_ip)) {
|
||||
tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(session, a_ip);
|
||||
}
|
||||
|
||||
if (r_ip && r_port) {
|
||||
tech_pvt->remote_sdp_audio_ip = (char *) r_ip;
|
||||
tech_pvt->remote_sdp_audio_port = (switch_port_t)atoi(r_port);
|
||||
@@ -5538,6 +5553,14 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
|
||||
}
|
||||
}
|
||||
|
||||
if (tech_pvt->te) {
|
||||
switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te);
|
||||
}
|
||||
|
||||
if (tech_pvt->recv_te) {
|
||||
switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, tech_pvt->recv_te);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) {
|
||||
@@ -6546,6 +6569,14 @@ void sofia_glue_parse_rtp_bugs(switch_rtp_bug_flag_t *flag_pole, const char *str
|
||||
if (switch_stristr("~IGNORE_DTMF_DURATION", str)) {
|
||||
*flag_pole &= ~RTP_BUG_IGNORE_DTMF_DURATION;
|
||||
}
|
||||
|
||||
if (switch_stristr("ACCEPT_ANY_PACKETS", str)) {
|
||||
*flag_pole |= RTP_BUG_ACCEPT_ANY_PACKETS;
|
||||
}
|
||||
|
||||
if (switch_stristr("~ACCEPT_ANY_PACKETS", str)) {
|
||||
*flag_pole &= ~RTP_BUG_ACCEPT_ANY_PACKETS;
|
||||
}
|
||||
}
|
||||
|
||||
char *sofia_glue_gen_contact_str(sofia_profile_t *profile, sip_t const *sip, sofia_dispatch_event_t *de, sofia_nat_parse_t *np)
|
||||
|
||||
@@ -72,8 +72,8 @@ struct presence_helper {
|
||||
char last_uuid[512];
|
||||
};
|
||||
|
||||
switch_status_t sofia_presence_chat_send(const char *proto, const char *from, const char *to, const char *subject,
|
||||
const char *body, const char *type, const char *hint)
|
||||
switch_status_t sofia_presence_chat_send(switch_event_t *message_event)
|
||||
|
||||
{
|
||||
char *prof = NULL, *user = NULL, *host = NULL;
|
||||
sofia_profile_t *profile = NULL;
|
||||
@@ -93,6 +93,27 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
char *dup_dest = NULL;
|
||||
char *p = NULL;
|
||||
char *remote_host = NULL;
|
||||
const char *proto;
|
||||
const char *from;
|
||||
const char *to;
|
||||
//const char *subject;
|
||||
const char *body;
|
||||
const char *type;
|
||||
const char *from_full;
|
||||
char header[256] = "";
|
||||
char *route_uri = NULL;
|
||||
const char *network_ip = NULL, *network_port = NULL;
|
||||
|
||||
proto = switch_event_get_header(message_event, "proto");
|
||||
from = switch_event_get_header(message_event, "from");
|
||||
to = switch_event_get_header(message_event, "to");
|
||||
//subject = switch_event_get_header(message_event, "subject");
|
||||
body = switch_event_get_body(message_event);
|
||||
type = switch_event_get_header(message_event, "type");
|
||||
from_full = switch_event_get_header(message_event, "from_full");
|
||||
|
||||
network_ip = switch_event_get_header(message_event, "to_sip_ip");
|
||||
network_port = switch_event_get_header(message_event, "to_sip_port");
|
||||
|
||||
if (!to) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing To: header.\n");
|
||||
@@ -114,6 +135,10 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
user = prof;
|
||||
prof = NULL;
|
||||
}
|
||||
|
||||
if (!prof) {
|
||||
prof = switch_event_get_header(message_event, "sip_profile");
|
||||
}
|
||||
|
||||
if (!strncasecmp(user, "sip:", 4)) {
|
||||
to_uri = user;
|
||||
@@ -125,13 +150,14 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
} else {
|
||||
host++;
|
||||
}
|
||||
if (!prof)
|
||||
if (!prof) {
|
||||
prof = host;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prof || !(profile = sofia_glue_find_profile(prof))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
|
||||
"Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to,
|
||||
"Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to,
|
||||
body ? body : "[no body]", prof ? prof : "NULL");
|
||||
goto end;
|
||||
}
|
||||
@@ -143,24 +169,34 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (to_uri) {
|
||||
switch_console_push_match(&list, to_uri);
|
||||
} else if (!(list = sofia_reg_find_reg_url_multi(profile, user, host))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find registered user %s@%s\n", user, host);
|
||||
goto end;
|
||||
sofia_profile_t *test;
|
||||
|
||||
if ((test = sofia_glue_find_profile(host))) {
|
||||
sofia_glue_release_profile(test);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending to local box for %s@%s\n", user, host);
|
||||
/* our box let's not send it */
|
||||
} else {
|
||||
char *tmp;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't find registered user %s@%s\n", user, host);
|
||||
tmp = switch_mprintf("sip:%s@%s", user, host);
|
||||
switch_console_push_match(&list, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!strcasecmp(proto, SOFIA_CHAT_PROTO)) {
|
||||
from = hint;
|
||||
from = from_full;
|
||||
} else {
|
||||
char *fp, *p = NULL;
|
||||
|
||||
fp = strdup(from);
|
||||
switch_assert(fp);
|
||||
|
||||
if (!fp) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((p = strchr(fp, '@'))) {
|
||||
*p++ = '\0';
|
||||
@@ -173,12 +209,23 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
}
|
||||
}
|
||||
|
||||
ffrom = switch_mprintf("\"%s\" <sip:%s+%s@%s>", fp, proto, fp, p);
|
||||
if (switch_stristr("global", proto)) {
|
||||
ffrom = switch_mprintf("\"%s\" <sip:%s@%s>", fp, fp, p);
|
||||
} else {
|
||||
ffrom = switch_mprintf("\"%s\" <sip:%s+%s@%s>", fp, proto, fp, p);
|
||||
}
|
||||
|
||||
from = ffrom;
|
||||
switch_safe_free(fp);
|
||||
}
|
||||
|
||||
if (!list) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
|
||||
"Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nNobody to send to: Profile %s\n", proto, from, to,
|
||||
body ? body : "[no body]", prof ? prof : "NULL");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (m = list->head; m; m = m->next) {
|
||||
|
||||
if (!(dst = sofia_glue_get_destination(m->val))) {
|
||||
@@ -233,22 +280,35 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
}
|
||||
|
||||
/* if this cries, add contact here too, change the 1 to 0 and omit the safe_free */
|
||||
|
||||
|
||||
//printf("DEBUG To: [%s] From: [%s] Contact: [%s] RURI [%s] ip [%s] port [%s]\n", to, from, contact, dst->route_uri, network_ip, network_port);
|
||||
|
||||
//DUMP_EVENT(message_event);
|
||||
|
||||
if (zstr(dst->route_uri) && !zstr(user) && !zstr(network_ip) && (zstr(host) || strcmp(network_ip, host))) {
|
||||
route_uri = switch_mprintf("sip:%s@%s:%s", user, network_ip, network_port);
|
||||
}
|
||||
|
||||
msg_nh = nua_handle(profile->nua, NULL,
|
||||
TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)),
|
||||
TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
|
||||
SIPTAG_FROM_STR(from),
|
||||
TAG_IF(contact, NUTAG_URL(contact)),
|
||||
SIPTAG_TO_STR(dup_dest),
|
||||
SIPTAG_CONTACT_STR(contact_str),
|
||||
TAG_END());
|
||||
|
||||
nua_handle_bind(msg_nh, &mod_sofia_globals.destroy_private);
|
||||
|
||||
switch_snprintf(header, sizeof(header), "X-FS-Sending-Message: %s", switch_core_get_uuid());
|
||||
|
||||
nua_message(msg_nh,
|
||||
TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)),
|
||||
TAG_IF(route_uri, NUTAG_PROXY(route_uri)),
|
||||
TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
|
||||
SIPTAG_FROM_STR(from),
|
||||
TAG_IF(contact, NUTAG_URL(contact)),
|
||||
SIPTAG_TO_STR(dup_dest),
|
||||
SIPTAG_CONTACT_STR(contact_str),
|
||||
|
||||
TAG_IF(user_via, SIPTAG_VIA_STR(user_via)),
|
||||
SIPTAG_CONTENT_TYPE_STR(ct),
|
||||
SIPTAG_PAYLOAD_STR(body),
|
||||
SIPTAG_HEADER_STR(header),
|
||||
TAG_END());
|
||||
|
||||
sofia_glue_free_destination(dst);
|
||||
@@ -261,6 +321,7 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co
|
||||
end:
|
||||
|
||||
switch_safe_free(contact);
|
||||
switch_safe_free(route_uri);
|
||||
switch_safe_free(ffrom);
|
||||
switch_safe_free(dup);
|
||||
|
||||
@@ -702,7 +763,7 @@ static void actual_sofia_presence_event_handler(switch_event_t *event)
|
||||
"from sip_registrations "
|
||||
|
||||
"left join sip_dialogs on "
|
||||
"sip_dialogs.presence_id = sip_registrations.sip_user || '@' || sip_registrations.sip_host "
|
||||
"sip_dialogs.presence_id = sip_registrations.sip_user %q '@' %q sip_registrations.sip_host "
|
||||
"or (sip_dialogs.sip_from_user = sip_registrations.sip_user "
|
||||
"and sip_dialogs.sip_from_host = sip_registrations.sip_host) "
|
||||
|
||||
@@ -713,6 +774,7 @@ static void actual_sofia_presence_event_handler(switch_event_t *event)
|
||||
"(sip_registrations.orig_server_host='%q' or sip_registrations.sip_host='%q' "
|
||||
"or sip_registrations.presence_hosts like '%%%q%%'))",
|
||||
dh.status, dh.rpid,
|
||||
switch_sql_concat(), switch_sql_concat(),
|
||||
probe_euser, probe_host, probe_euser, probe_host, probe_host, probe_host);
|
||||
switch_assert(sql);
|
||||
|
||||
@@ -1983,14 +2045,21 @@ static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv
|
||||
if (uuid && (session = switch_core_session_locate(uuid))) {
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
if (zstr((callee_name = switch_channel_get_variable(channel, "effective_callee_id_name"))) &&
|
||||
zstr((callee_name = switch_channel_get_variable(channel, "sip_callee_id_name")))) {
|
||||
callee_name = switch_channel_get_variable(channel, "callee_id_name");
|
||||
}
|
||||
|
||||
if (zstr((callee_number = switch_channel_get_variable(channel, "effective_callee_id_number"))) &&
|
||||
zstr((callee_number = switch_channel_get_variable(channel, "sip_callee_id_number")))) {
|
||||
callee_number = switch_channel_get_variable(channel, "destination_number");
|
||||
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
|
||||
|
||||
if (zstr((callee_name = switch_channel_get_variable(channel, "effective_callee_id_name"))) &&
|
||||
zstr((callee_name = switch_channel_get_variable(channel, "sip_callee_id_name")))) {
|
||||
callee_name = switch_channel_get_variable(channel, "callee_id_name");
|
||||
}
|
||||
|
||||
if (zstr((callee_number = switch_channel_get_variable(channel, "effective_callee_id_number"))) &&
|
||||
zstr((callee_number = switch_channel_get_variable(channel, "sip_callee_id_number"))) &&
|
||||
zstr((callee_number = switch_channel_get_variable(channel, "callee_id_number")))) {
|
||||
callee_number = switch_channel_get_variable(channel, "destination_number");
|
||||
}
|
||||
} else {
|
||||
callee_name = switch_channel_get_variable(channel, "caller_id_name");
|
||||
callee_number = switch_channel_get_variable(channel, "caller_id_number");
|
||||
}
|
||||
|
||||
if (zstr(callee_name) && !zstr(callee_number)) {
|
||||
@@ -2849,13 +2918,25 @@ void sofia_presence_handle_sip_i_message(int status,
|
||||
const char *to_host = NULL;
|
||||
sip_payload_t *payload = sip->sip_payload;
|
||||
char *msg = NULL;
|
||||
const char *us;
|
||||
char network_ip[80];
|
||||
int network_port = 0;
|
||||
|
||||
if ((us = sofia_glue_get_unknown_header(sip, "X-FS-Sending-Message")) && !strcmp(us, switch_core_get_uuid())) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending message to ourselves!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (sip->sip_content_type && sip->sip_content_type->c_subtype) {
|
||||
if (strstr(sip->sip_content_type->c_subtype, "composing")) {
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
|
||||
|
||||
|
||||
if (from) {
|
||||
from_user = from->a_url->url_user;
|
||||
from_host = from->a_url->url_host;
|
||||
@@ -2867,7 +2948,7 @@ void sofia_presence_handle_sip_i_message(int status,
|
||||
}
|
||||
|
||||
if (!to_user) {
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (payload) {
|
||||
@@ -2906,33 +2987,56 @@ void sofia_presence_handle_sip_i_message(int status,
|
||||
sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip);
|
||||
}
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT) && (tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) {
|
||||
if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from_addr);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", full_from);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to_addr);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE");
|
||||
if (msg) {
|
||||
switch_event_add_body(event, "%s", msg);
|
||||
}
|
||||
if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from_addr);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", from_user);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", from_host);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_user", to_user);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_host", to_host);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_sip_ip", network_ip);
|
||||
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from_sip_port", "%d", network_port);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to_addr);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE");
|
||||
|
||||
if (switch_core_session_queue_event(tech_pvt->session, &event) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true");
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
|
||||
if (sip->sip_content_type && sip->sip_content_type->c_subtype) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", sip->sip_content_type->c_type);
|
||||
} else {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain");
|
||||
}
|
||||
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_full", full_from);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
|
||||
|
||||
if (msg) {
|
||||
switch_event_add_body(event, "%s", msg);
|
||||
}
|
||||
} else {
|
||||
switch_core_chat_send(proto, SOFIA_CHAT_PROTO, from_addr, to_addr, "", msg, NULL, full_from);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT) && (tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) {
|
||||
switch_core_session_queue_event(tech_pvt->session, &event);
|
||||
} else {
|
||||
switch_core_chat_send(proto, event);
|
||||
switch_event_destroy(&event);
|
||||
}
|
||||
|
||||
switch_safe_free(to_addr);
|
||||
switch_safe_free(from_addr);
|
||||
|
||||
if (full_from) {
|
||||
su_free(nh->nh_home, full_from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
|
||||
|
||||
}
|
||||
|
||||
void sofia_presence_set_chat_hash(private_object_t *tech_pvt, sip_t const *sip)
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
|
||||
* David Knell <>
|
||||
* Eliot Gable <egable AT.AT broadvox.com>
|
||||
* Leon de Rooij <leon@scarlet-internet.nl>
|
||||
*
|
||||
*
|
||||
* sofia_ref.c -- SOFIA SIP Endpoint (registration code)
|
||||
* sofia_reg.c -- SOFIA SIP Endpoint (registration code)
|
||||
*
|
||||
*/
|
||||
#include "mod_sofia.h"
|
||||
@@ -240,37 +241,41 @@ void sofia_sub_check_gateway(sofia_profile_t *profile, time_t now)
|
||||
|
||||
void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now)
|
||||
{
|
||||
sofia_gateway_t *gateway_ptr, *last = NULL;
|
||||
sofia_gateway_t *check, *gateway_ptr, *last = NULL;
|
||||
switch_event_t *event;
|
||||
char *pkey;
|
||||
|
||||
switch_mutex_lock(profile->gw_mutex);
|
||||
for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) {
|
||||
if (gateway_ptr->deleted && gateway_ptr->state == REG_STATE_NOREG) {
|
||||
if (last) {
|
||||
last->next = gateway_ptr->next;
|
||||
} else {
|
||||
profile->gateways = gateway_ptr->next;
|
||||
if (gateway_ptr->deleted) {
|
||||
if ((check = switch_core_hash_find(mod_sofia_globals.gateway_hash, gateway_ptr->name)) && check == gateway_ptr) {
|
||||
char *pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name);
|
||||
switch_assert(pkey);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway %s from hash.\n", pkey);
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey);
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, gateway_ptr->name);
|
||||
free(pkey);
|
||||
}
|
||||
|
||||
pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name);
|
||||
if (gateway_ptr->state == REG_STATE_NOREG) {
|
||||
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey);
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, gateway_ptr->name);
|
||||
|
||||
free(pkey);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleted gateway %s\n", gateway_ptr->name);
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_DEL) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile-name", gateway_ptr->profile->name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Gateway", gateway_ptr->name);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
if (gateway_ptr->ob_vars) {
|
||||
switch_event_destroy(&gateway_ptr->ob_vars);
|
||||
}
|
||||
if (gateway_ptr->ib_vars) {
|
||||
switch_event_destroy(&gateway_ptr->ib_vars);
|
||||
if (last) {
|
||||
last->next = gateway_ptr->next;
|
||||
} else {
|
||||
profile->gateways = gateway_ptr->next;
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleted gateway %s\n", gateway_ptr->name);
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_DEL) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile-name", gateway_ptr->profile->name);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Gateway", gateway_ptr->name);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
if (gateway_ptr->ob_vars) {
|
||||
switch_event_destroy(&gateway_ptr->ob_vars);
|
||||
}
|
||||
if (gateway_ptr->ib_vars) {
|
||||
switch_event_destroy(&gateway_ptr->ib_vars);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
last = gateway_ptr;
|
||||
@@ -466,6 +471,39 @@ int sofia_reg_find_callback(void *pArg, int argc, char **argv, char **columnName
|
||||
return cbt->matches == 1 ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
int sofia_reg_find_reg_with_positive_expires_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||||
{
|
||||
struct callback_t *cbt = (struct callback_t *) pArg;
|
||||
sofia_destination_t *dst = NULL;
|
||||
long int expires;
|
||||
char *contact = NULL;
|
||||
|
||||
expires = atol(argv[1]) - 60 - (long) switch_epoch_time_now(NULL);
|
||||
|
||||
if (expires > 0) {
|
||||
dst = sofia_glue_get_destination(argv[0]);
|
||||
contact = switch_mprintf("<%s>;expires=%ld", dst->contact, expires);
|
||||
|
||||
if (!cbt->len) {
|
||||
switch_console_push_match(&cbt->list, contact);
|
||||
switch_safe_free(contact);
|
||||
sofia_glue_free_destination(dst);
|
||||
cbt->matches++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch_copy_string(cbt->val, contact, cbt->len);
|
||||
switch_safe_free(contact);
|
||||
sofia_glue_free_destination(dst);
|
||||
cbt->matches++;
|
||||
return cbt->matches == 1 ? 0 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int sofia_reg_nat_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||||
{
|
||||
sofia_profile_t *profile = (sofia_profile_t *) pArg;
|
||||
@@ -875,6 +913,29 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_multi(sofia_profile_t *p
|
||||
}
|
||||
|
||||
|
||||
switch_console_callback_match_t *sofia_reg_find_reg_url_with_positive_expires_multi(sofia_profile_t *profile, const char *user, const char *host)
|
||||
{
|
||||
struct callback_t cbt = { 0 };
|
||||
char sql[512] = "";
|
||||
|
||||
if (!user) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Called with null user!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (host) {
|
||||
switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')",
|
||||
user, host, host);
|
||||
} else {
|
||||
switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s'", user);
|
||||
}
|
||||
|
||||
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_reg_find_reg_with_positive_expires_callback, &cbt);
|
||||
|
||||
return cbt.list;
|
||||
}
|
||||
|
||||
|
||||
void sofia_reg_auth_challenge(sofia_profile_t *profile, nua_handle_t *nh, sofia_dispatch_event_t *de,
|
||||
sofia_regtype_t regtype, const char *realm, int stale)
|
||||
{
|
||||
@@ -983,8 +1044,8 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
}
|
||||
}
|
||||
|
||||
/* all callers must confirm that sip, sip->sip_request and sip->sip_contact are not NULL */
|
||||
switch_assert(sip != NULL && sip->sip_contact != NULL && sip->sip_request != NULL);
|
||||
/* all callers must confirm that sip and sip->sip_request are not NULL */
|
||||
switch_assert(sip != NULL && sip->sip_request != NULL);
|
||||
|
||||
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
|
||||
|
||||
@@ -1032,7 +1093,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
sub_host = to_host;
|
||||
}
|
||||
|
||||
if (contact->m_url) {
|
||||
if (contact && contact->m_url) {
|
||||
const char *port = contact->m_url->url_port;
|
||||
char new_port[25] = "";
|
||||
const char *contact_host = contact->m_url->url_host;
|
||||
@@ -1130,7 +1191,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
|
||||
if (expires) {
|
||||
exptime = expires->ex_delta;
|
||||
} else if (contact->m_expires) {
|
||||
} else if (contact && contact->m_expires) {
|
||||
exptime = atol(contact->m_expires);
|
||||
}
|
||||
|
||||
@@ -1163,11 +1224,13 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", to_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", reg_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
if (contact)
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", rpid);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "status", reg_desc);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
if (contact)
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "to-user", from_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "to-host", from_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "network-ip", network_ip);
|
||||
@@ -1178,7 +1241,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
switch_event_fire(&s_event);
|
||||
}
|
||||
|
||||
if (exptime && v_event && *v_event) {
|
||||
if (contact && exptime && v_event && *v_event) {
|
||||
char *exp_var;
|
||||
char *allow_multireg = NULL;
|
||||
int auto_connectile = 0;
|
||||
@@ -1218,13 +1281,14 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
|
||||
if (auto_connectile || (v_contact_str = switch_event_get_header(*v_event, "sip-force-contact"))) {
|
||||
if (auto_connectile || (!strcasecmp(v_contact_str, "NDLB-connectile-dysfunction-2.0"))) {
|
||||
char *path_encoded;
|
||||
char *path_encoded = NULL;
|
||||
size_t path_encoded_len;
|
||||
char my_contact_str[1024];
|
||||
|
||||
switch_snprintf(my_contact_str, sizeof(my_contact_str), "sip:%s@%s:%d", contact->m_url->url_user, url_ip, network_port);
|
||||
path_encoded_len = (strlen(my_contact_str) * 3) + 1;
|
||||
|
||||
if (!switch_stristr("fs_path=", contact_str)) {
|
||||
switch_zmalloc(path_encoded, path_encoded_len);
|
||||
switch_copy_string(path_encoded, ";fs_nat=yes;fs_path=", 21);
|
||||
switch_url_encode(my_contact_str, path_encoded + 20, path_encoded_len - 20);
|
||||
@@ -1237,7 +1301,8 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
} else {
|
||||
switch_snprintf(contact_str + strlen(contact_str), sizeof(contact_str) - strlen(contact_str), "%s", path_encoded);
|
||||
}
|
||||
free(path_encoded);
|
||||
switch_safe_free(path_encoded);
|
||||
}
|
||||
} else {
|
||||
if (*received_data && sofia_test_pflag(profile, PFLAG_RECIEVED_IN_NAT_REG_CONTACT)) {
|
||||
switch_snprintf(received_data, sizeof(received_data), ";received=%s:%d", url_ip, network_port);
|
||||
@@ -1308,11 +1373,13 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", to_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", reg_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
if (contact)
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", rpid);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "status", reg_desc);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
if (contact)
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "to-user", from_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "to-host", from_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "network-ip", network_ip);
|
||||
@@ -1334,13 +1401,17 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
}
|
||||
/* Log line added to support Fail2Ban */
|
||||
if (sofia_test_pflag(profile, PFLAG_LOG_AUTH_FAIL)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SIP auth challenge (%s) on sofia profile '%s' "
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "SIP auth challenge (%s) on sofia profile '%s' "
|
||||
"for [%s@%s] from ip %s\n", (regtype == REG_INVITE) ? "INVITE" : "REGISTER",
|
||||
profile->name, to_user, to_host, network_ip);
|
||||
}
|
||||
|
||||
switch_goto_int(r, 1, end);
|
||||
}
|
||||
|
||||
if (!contact)
|
||||
goto respond_200_ok;
|
||||
|
||||
reg:
|
||||
|
||||
|
||||
@@ -1587,69 +1658,113 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
}
|
||||
}
|
||||
|
||||
respond_200_ok:
|
||||
|
||||
if (regtype == REG_REGISTER) {
|
||||
char exp_param[128] = "";
|
||||
char date[80] = "";
|
||||
switch_event_t *s_mwi_event = NULL;
|
||||
|
||||
switch_console_callback_match_t *contact_list = NULL;
|
||||
tagi_t *contact_tags;
|
||||
switch_console_callback_match_node_t *m;
|
||||
int i;
|
||||
|
||||
s_event = NULL;
|
||||
|
||||
if (exptime) {
|
||||
switch_snprintf(exp_param, sizeof(exp_param), "expires=%ld", exptime);
|
||||
sip_contact_add_param(nua_handle_home(nh), sip->sip_contact, exp_param);
|
||||
if (contact) {
|
||||
if (exptime) {
|
||||
switch_snprintf(exp_param, sizeof(exp_param), "expires=%ld", exptime);
|
||||
sip_contact_add_param(nua_handle_home(nh), sip->sip_contact, exp_param);
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_MESSAGE_QUERY_ON_REGISTER) ||
|
||||
(reg_count == 1 && sofia_test_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER))) {
|
||||
if (switch_event_create(&s_mwi_event, SWITCH_EVENT_MESSAGE_QUERY) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header(s_mwi_event, SWITCH_STACK_BOTTOM, "Message-Account", "sip:%s@%s", mwi_user, mwi_host);
|
||||
switch_event_add_header_string(s_mwi_event, SWITCH_STACK_BOTTOM, "VM-Sofia-Profile", profile->name);
|
||||
switch_event_add_header_string(s_mwi_event, SWITCH_STACK_BOTTOM, "VM-Call-ID", call_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_PRESENCE_ON_REGISTER) ||
|
||||
(reg_count == 1 && sofia_test_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER))
|
||||
|| send_pres == 1 || (reg_count == 1 && send_pres == 2)) {
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_PRESENCE_PROBE_ON_REGISTER)) {
|
||||
if (switch_event_create(&s_event, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "login", profile->name);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "to", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
||||
switch_event_fire(&s_event);
|
||||
if (sofia_test_pflag(profile, PFLAG_MESSAGE_QUERY_ON_REGISTER) ||
|
||||
(reg_count == 1 && sofia_test_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER))) {
|
||||
if (switch_event_create(&s_mwi_event, SWITCH_EVENT_MESSAGE_QUERY) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header(s_mwi_event, SWITCH_STACK_BOTTOM, "Message-Account", "sip:%s@%s", mwi_user, mwi_host);
|
||||
switch_event_add_header_string(s_mwi_event, SWITCH_STACK_BOTTOM, "VM-Sofia-Profile", profile->name);
|
||||
switch_event_add_header_string(s_mwi_event, SWITCH_STACK_BOTTOM, "VM-Call-ID", call_id);
|
||||
}
|
||||
} else {
|
||||
if (switch_event_create(&s_event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "login", profile->name);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "status", "Registered");
|
||||
switch_event_fire(&s_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch_core_del_registration(to_user, reg_host, call_id);
|
||||
|
||||
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_UNREGISTER) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", to_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", reg_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", rpid);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
if (sofia_test_pflag(profile, PFLAG_PRESENCE_ON_REGISTER) ||
|
||||
(reg_count == 1 && sofia_test_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER))
|
||||
|| send_pres == 1 || (reg_count == 1 && send_pres == 2)) {
|
||||
|
||||
if (sofia_test_pflag(profile, PFLAG_PRESENCE_PROBE_ON_REGISTER)) {
|
||||
if (switch_event_create(&s_event, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "login", profile->name);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "to", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
||||
switch_event_fire(&s_event);
|
||||
}
|
||||
} else {
|
||||
if (switch_event_create(&s_event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "login", profile->name);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, sub_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "status", "Registered");
|
||||
switch_event_fire(&s_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *username = "unknown";
|
||||
if (auth_params) {
|
||||
username = switch_event_get_header(auth_params, "sip_auth_username");
|
||||
}
|
||||
|
||||
switch_core_del_registration(to_user, reg_host, call_id);
|
||||
|
||||
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_UNREGISTER) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "username", username);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", to_user);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", reg_host);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
||||
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "rpid", rpid);
|
||||
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_rfc822_date(date, switch_micro_time_now());
|
||||
nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(sip->sip_contact),
|
||||
TAG_IF(path_val, SIPTAG_PATH_STR(path_val)), NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_DATE_STR(date), TAG_END());
|
||||
|
||||
/* generate and respond a 200 OK */
|
||||
|
||||
if (mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup) {
|
||||
/* handle backwards compatibility - contacts will not be looked up but only copied from the request into the response
|
||||
remove this condition later if nobody complains about the extra select of the below new behavior
|
||||
also remove the parts in mod_sofia.h, sofia.c and sofia_reg.c that refer to reg_deny_binding_fetch_and_no_lookup */
|
||||
nua_respond(nh, SIP_200_OK, TAG_IF(contact, SIPTAG_CONTACT(sip->sip_contact)), TAG_IF(path_val, SIPTAG_PATH_STR(path_val)),
|
||||
NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_DATE_STR(date), TAG_END());
|
||||
|
||||
} else if ((contact_list = sofia_reg_find_reg_url_with_positive_expires_multi(profile, from_user, reg_host))) {
|
||||
/* all + 1 tag_i elements initialized as NULL - last one implies TAG_END() */
|
||||
switch_zmalloc(contact_tags, sizeof(*contact_tags) * (contact_list->count + 1));
|
||||
i = 0;
|
||||
for (m = contact_list->head; m; m = m->next) {
|
||||
contact_tags[i].t_tag = siptag_contact_str;
|
||||
contact_tags[i].t_value = (tag_value_t) m->val;
|
||||
++i;
|
||||
}
|
||||
|
||||
nua_respond(nh, SIP_200_OK, TAG_IF(path_val, SIPTAG_PATH_STR(path_val)),
|
||||
NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_DATE_STR(date), TAG_NEXT(contact_tags));
|
||||
|
||||
switch_safe_free(contact_tags);
|
||||
switch_console_free_matches(&contact_list);
|
||||
|
||||
} else {
|
||||
/* respond without any contacts */
|
||||
nua_respond(nh, SIP_200_OK, TAG_IF(path_val, SIPTAG_PATH_STR(path_val)),
|
||||
NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_DATE_STR(date), TAG_END());
|
||||
}
|
||||
|
||||
|
||||
if (s_event) {
|
||||
switch_event_fire(&s_event);
|
||||
@@ -1659,7 +1774,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand
|
||||
switch_event_fire(&s_mwi_event);
|
||||
}
|
||||
|
||||
if (*contact_str && sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE_SYLANTRO)) {
|
||||
if (contact && *contact_str && sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE_SYLANTRO)) {
|
||||
sofia_sla_handle_register(nua, profile, sip, de, exptime, contact_str);
|
||||
}
|
||||
|
||||
@@ -1705,7 +1820,8 @@ void sofia_reg_handle_sip_i_register(nua_t *nua, sofia_profile_t *profile, nua_h
|
||||
|
||||
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
|
||||
|
||||
if (!(sip->sip_contact && sip->sip_contact->m_url)) {
|
||||
/* backwards compatibility */
|
||||
if (mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup && !(sip->sip_contact && sip->sip_contact->m_url)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT! ip: %s, port: %i\n", network_ip, network_port);
|
||||
nua_respond(nh, 400, "Missing Contact Header", TAG_END());
|
||||
goto end;
|
||||
@@ -2703,7 +2819,7 @@ sofia_gateway_t *sofia_reg_find_gateway_by_realm__(const char *file, const char
|
||||
switch_mutex_lock(mod_sofia_globals.hash_mutex);
|
||||
for (hi = switch_hash_first(NULL, mod_sofia_globals.gateway_hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, &var, NULL, &val);
|
||||
if (key && (gateway = (sofia_gateway_t *) val) && gateway->register_realm && !strcasecmp(gateway->register_realm, key)) {
|
||||
if (key && (gateway = (sofia_gateway_t *) val) && !gateway->deleted && gateway->register_realm && !strcasecmp(gateway->register_realm, key)) {
|
||||
break;
|
||||
} else {
|
||||
gateway = NULL;
|
||||
@@ -2757,6 +2873,7 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key,
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_FALSE;
|
||||
char *pkey = switch_mprintf("%s::%s", profile->name, key);
|
||||
sofia_gateway_t *gp;
|
||||
|
||||
switch_mutex_lock(profile->gw_mutex);
|
||||
|
||||
@@ -2766,12 +2883,21 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key,
|
||||
switch_mutex_unlock(profile->gw_mutex);
|
||||
|
||||
switch_mutex_lock(mod_sofia_globals.hash_mutex);
|
||||
if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, key)) {
|
||||
status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway);
|
||||
|
||||
if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, key))) {
|
||||
if (gp->deleted) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing deleted gateway from hash.\n");
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, gp->name);
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey);
|
||||
switch_core_hash_delete(mod_sofia_globals.gateway_hash, key);
|
||||
}
|
||||
}
|
||||
|
||||
if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) {
|
||||
if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, key) && !switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) {
|
||||
status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway);
|
||||
status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, pkey, gateway);
|
||||
} else {
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
}
|
||||
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
|
||||
|
||||
|
||||
@@ -265,9 +265,8 @@ static switch_status_t my_on_reporting(switch_core_session_t *session)
|
||||
}
|
||||
|
||||
|
||||
static void event_handler(switch_event_t *event)
|
||||
static void do_rotate_all()
|
||||
{
|
||||
const char *sig = switch_event_get_header(event, "Trapped-Signal");
|
||||
switch_hash_index_t *hi;
|
||||
void *val;
|
||||
cdr_fd_t *fd;
|
||||
@@ -276,18 +275,38 @@ static void event_handler(switch_event_t *event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (sig && !strcmp(sig, "HUP")) {
|
||||
for (hi = switch_hash_first(NULL, globals.fd_hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, NULL, NULL, &val);
|
||||
fd = (cdr_fd_t *) val;
|
||||
switch_mutex_lock(fd->mutex);
|
||||
do_rotate(fd);
|
||||
switch_mutex_unlock(fd->mutex);
|
||||
}
|
||||
for (hi = switch_hash_first(NULL, globals.fd_hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, NULL, NULL, &val);
|
||||
fd = (cdr_fd_t *) val;
|
||||
switch_mutex_lock(fd->mutex);
|
||||
do_rotate(fd);
|
||||
switch_mutex_unlock(fd->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void event_handler(switch_event_t *event)
|
||||
{
|
||||
const char *sig = switch_event_get_header(event, "Trapped-Signal");
|
||||
|
||||
if (sig && !strcmp(sig, "HUP")) {
|
||||
do_rotate_all();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SWITCH_STANDARD_API(cdr_csv_function)
|
||||
{
|
||||
if (!strcmp(cmd, "rotate")) {
|
||||
do_rotate_all();
|
||||
stream->write_function(stream, "+OK");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
||||
static switch_state_handler_table_t state_handlers = {
|
||||
/*.on_init */ NULL,
|
||||
/*.on_routing */ NULL,
|
||||
@@ -387,6 +406,7 @@ static switch_status_t load_config(switch_memory_pool_t *pool)
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_csv_load)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_api_interface_t *api_interface;
|
||||
|
||||
load_config(pool);
|
||||
|
||||
@@ -403,6 +423,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_csv_load)
|
||||
switch_core_add_state_handler(&state_handlers);
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
SWITCH_ADD_API(api_interface, "cdr_csv", "cdr_csv controls", cdr_csv_function, "parameters");
|
||||
switch_console_set_complete("add cdr_csv rotate");
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -410,6 +432,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_csv_load)
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_csv_shutdown)
|
||||
{
|
||||
switch_console_set_complete("del cdr_csv");
|
||||
|
||||
globals.shutdown = 1;
|
||||
switch_event_unbind_callback(event_handler);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
include ../../../../build/modmake.rules
|
||||
|
||||
MONGODB_DRIVER=./driver/src
|
||||
LOCAL_CFLAGS=-I$(MONGODB_DRIVER)
|
||||
LOCAL_OBJS=$(MONGODB_DRIVER)/md5.o \
|
||||
$(MONGODB_DRIVER)/mongo.o $(MONGODB_DRIVER)/net.o \
|
||||
$(MONGODB_DRIVER)/bson.o $(MONGODB_DRIVER)/numbers.o $(MONGODB_DRIVER)/encoding.o \
|
||||
|
||||
local_depend: $(LOCAL_OBJS)
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,139 @@
|
||||
# MongoDB C Driver History
|
||||
|
||||
## 0.4
|
||||
|
||||
THIS RELEASE INCLUDES NUMEROUS BACKWARD-BREAKING CHANGES.
|
||||
These changes have been made for extensibility, consistency,
|
||||
and ease of use. Please read the following release notes
|
||||
carefully, and study the updated tutorial.
|
||||
|
||||
API Principles:
|
||||
|
||||
1. Present a consistent interface for all objects: connections,
|
||||
cursors, bson objects, and bson iterators.
|
||||
2. Require no knowledge of an object's implementation to use the API.
|
||||
3. Allow users to allocate objects on the stack or on the heap.
|
||||
4. Integrate API with new error reporting strategy.
|
||||
5. Be concise, except where it impairs clarity.
|
||||
|
||||
Changes:
|
||||
|
||||
* mongo_replset_init_conn has been renamed to mongo_replset_init.
|
||||
* bson_buffer has been removed. All functions for building bson
|
||||
objects now take objects of type bson. The new pattern looks like this:
|
||||
|
||||
Example:
|
||||
|
||||
bson b[1];
|
||||
bson_init( b );
|
||||
bson_append_int( b, "foo", 1 );
|
||||
bson_finish( b );
|
||||
/* The object is ready to use.
|
||||
When finished, destroy it. */
|
||||
bson_destroy( b );
|
||||
|
||||
* mongo_connection has been renamed to mongo.
|
||||
|
||||
Example:
|
||||
|
||||
mongo conn[1];
|
||||
mongo_connect( conn, '127.0.0.1', 27017 );
|
||||
/* Connection is ready. Destroy when down. */
|
||||
mongo_destroy( conn );
|
||||
|
||||
* New cursor builder API for clearer code:
|
||||
|
||||
Example:
|
||||
|
||||
mongo_cursor cursor[1];
|
||||
mongo_cursor_init( cursor, conn, "test.foo" );
|
||||
|
||||
bson query[1];
|
||||
|
||||
bson_init( query );
|
||||
bson_append_int( query, "bar", 1 );
|
||||
bson_finish( query );
|
||||
|
||||
bson fields[1];
|
||||
|
||||
bson_init( fields );
|
||||
bson_append_int( fields, "baz", 1 );
|
||||
bson_finish( fields );
|
||||
|
||||
mongo_cursor_set_query( cursor, query );
|
||||
mongo_cursor_set_fields( cursor, fields );
|
||||
mongo_cursor_set_limit( cursor, 10 );
|
||||
mongo_cursor_set_skip( cursor, 10 );
|
||||
|
||||
while( mongo_cursor_next( cursor ) == MONGO_OK )
|
||||
bson_print( mongo_cursor_bson( cursor ) );
|
||||
|
||||
* bson_iterator_init now takes a (bson*) instead of a (const char*). This is consistent
|
||||
with bson_find, which also takes a (bson*). If you want to initiate a bson iterator
|
||||
with a buffer, use the new function bson_iterator_from_buffer.
|
||||
* With the addition of the mongo_cursor_bson function, it's now no
|
||||
longer necessary to know how bson and mongo_cursor objects are implemented.
|
||||
|
||||
Example:
|
||||
|
||||
bson b[1];
|
||||
bson_iterator i[1];
|
||||
|
||||
bson_iterator_init( i, b );
|
||||
|
||||
/* With a cursor */
|
||||
bson_iterator_init( i, mongo_cursor_bson( cursor ) );
|
||||
|
||||
* Added mongo_cursor_data and bson_data functions, which return the
|
||||
raw bson buffer as a (const char *).
|
||||
* All constants that were once lower case are now
|
||||
upper case. These include: MONGO_OP_MSG, MONGO_OP_UPDATE, MONGO_OP_INSERT,
|
||||
MONGO_OP_QUERY, MONGO_OP_GET_MORE, MONGO_OP_DELETE, MONGO_OP_KILL_CURSORS
|
||||
BSON_EOO, BSON_DOUBLE, BSON_STRING, BSON_OBJECT, BSON_ARRAY, BSON_BINDATA,
|
||||
BSON_UNDEFINED, BSON_OID, BSON_BOOL, BSON_DATE, BSON_NULL, BSON_REGEX, BSON_DBREF,
|
||||
BSON_CODE, BSON_SYMBOL, BSON_CODEWSCOPE, BSON_INT, BSON_TIMESTAMP, BSON_LONG,
|
||||
MONGO_CONN_SUCCESS, MONGO_CONN_BAD_ARG, MONGO_CONN_NO_SOCKET, MONGO_CONN_FAIL,
|
||||
MONGO_CONN_NOT_MASTER, MONGO_CONN_BAD_SET_NAME, MONGO_CONN_CANNOT_FIND_PRIMARY
|
||||
If your programs use any of these constants, you must convert them to their
|
||||
upper case forms, or you will see compile errors.
|
||||
* The error handling strategy has been changed. Exceptions are not longer being used.
|
||||
* Functions taking a mongo_connection object now return either MONGO_OK or MONGO_ERROR.
|
||||
In case of an error, an error code of type mongo_error_t will be indicated on the
|
||||
mongo_connection->err field.
|
||||
* Functions taking a bson object now return either BSON_OK or BSON_ERROR.
|
||||
In case of an error, an error code of type bson_validity_t will be indicated on the
|
||||
bson->err or bson_buffer->err field.
|
||||
* Calls to mongo_cmd_get_last_error store the error status on the
|
||||
mongo->lasterrcode and mongo->lasterrstr fields.
|
||||
* bson_print now prints all types.
|
||||
* Users may now set custom malloc, realloc, free, printf, sprintf, and fprintf fields.
|
||||
* Groundwork for modules for supporting platform-specific features (e.g., socket timeouts).
|
||||
* Added mongo_set_op_timeout for setting socket timeout. To take advantage of this, you must
|
||||
compile with --use-platform=LINUX. The compiles with platform/linux/net.h instead of the
|
||||
top-level net.h.
|
||||
* Fixed tailable cursors.
|
||||
* GridFS API is now in-line with the new driver API. In particular, all of the
|
||||
following functions now return MONGO_OK or MONGO_ERROR: gridfs_init,
|
||||
gridfile_init, gridfile_writer_done, gridfs_store_buffer, gridfs_store_file,
|
||||
and gridfs_find_query.
|
||||
* Fixed a few memory leaks.
|
||||
|
||||
## 0.3
|
||||
2011-4-14
|
||||
|
||||
* Support replica sets.
|
||||
* Better standard connection API.
|
||||
* GridFS write buffers iteratively.
|
||||
* Fixes for working with large GridFS files (> 3GB)
|
||||
* bson_append_string_n and family (Gergely Nagy)
|
||||
|
||||
## 0.2
|
||||
2011-2-11
|
||||
|
||||
* GridFS support (Chris Triolo).
|
||||
* BSON Timestamp type support.
|
||||
|
||||
## 0.1
|
||||
2009-11-30
|
||||
|
||||
* Initial release.
|
||||
@@ -0,0 +1,60 @@
|
||||
# MongoDB C Driver
|
||||
|
||||
This is then 10gen-supported MongoDB C driver. There are two goals for this driver.
|
||||
The first is to provide a strict, default compilation option for ultimate portability,
|
||||
no dependencies, and generic embeddability.
|
||||
|
||||
The second is to support more advanced, platform-specific features, like socket timeout,
|
||||
by providing an interface for platform-specific modules.
|
||||
|
||||
Until the 1.0 release, this driver should be considered alpha. Keep in mind that the API will be in flux until then.
|
||||
|
||||
# Building
|
||||
|
||||
First check out the version you want to build. *Always build from a particular tag, since HEAD may be
|
||||
a work in progress.* For example, to build version 0.4, run:
|
||||
|
||||
git checkout v0.4
|
||||
|
||||
You can then build the driver with scons:
|
||||
|
||||
scons
|
||||
|
||||
## Running the tests
|
||||
Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set
|
||||
test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port
|
||||
30000. Note that the driver does not recognize 'localhost' as a valid host name.
|
||||
|
||||
To compile and run the tests:
|
||||
|
||||
scons test
|
||||
|
||||
# Error Handling
|
||||
Most functions return MONGO_OK or BSON_OK on success and MONGO_ERROR or BSON_ERROR on failure.
|
||||
Specific error codes and error strings are then stored in the `err` and `errstr` fields of the
|
||||
`mongo` and `bson` objects. It is the client's responsibility to check for errors and handle
|
||||
them appropriately.
|
||||
|
||||
# Docs
|
||||
The docs are built using Sphinx and Doxygen. If you have these tools installed, then
|
||||
you can build the docs with scons:
|
||||
|
||||
scons docs
|
||||
|
||||
The html docs will appear in docs/html.
|
||||
|
||||
# ISSUES
|
||||
|
||||
You can report bugs, request new features, and view this driver's roadmap
|
||||
using [JIRA](http://jira.mongodb.org/browse/CDRIVER).
|
||||
|
||||
# CREDITS
|
||||
|
||||
* Gergely Nagy - Non-null-terminated string support.
|
||||
* Josh Rotenberg - Initial Doxygen setup and a significant chunk of documentation.
|
||||
|
||||
# LICENSE
|
||||
|
||||
Unless otherwise specified in a source file, sources in this
|
||||
repository are published under the terms of the Apache License version
|
||||
2.0, a copy of which is in this repository as APACHE-2.0.txt.
|
||||
@@ -0,0 +1,178 @@
|
||||
# -*- mode: python; -*-
|
||||
|
||||
VERSION = "0.4"
|
||||
|
||||
# --- options ----
|
||||
AddOption('--test-server',
|
||||
dest='test_server',
|
||||
default='127.0.0.1',
|
||||
type='string',
|
||||
nargs=1,
|
||||
action='store',
|
||||
help='IP address of server to use for testing')
|
||||
|
||||
AddOption('--seed-start-port',
|
||||
dest='seed_start_port',
|
||||
default=30000,
|
||||
type='int',
|
||||
nargs=1,
|
||||
action='store',
|
||||
help='IP address of server to use for testing')
|
||||
|
||||
AddOption('--c99',
|
||||
dest='use_c99',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Compile with c99 (recommended for gcc)')
|
||||
|
||||
AddOption('--d',
|
||||
dest='optimize',
|
||||
default=True,
|
||||
action='store_false',
|
||||
help='disable optimizations')
|
||||
|
||||
AddOption('--use-platform',
|
||||
dest='compile_platform',
|
||||
default='GENERIC',
|
||||
type='string',
|
||||
nargs=1,
|
||||
action='store',
|
||||
help='Compile for a specific platform to take advantage '
|
||||
' of particular system features. For the moment, this include timeouts only.'
|
||||
' Current options include LINUX, '
|
||||
' GENERIC, and CUSTOM. If you specific CUSTOM, you must place a'
|
||||
' system-specific implementation of net.h and net.c in src/platform/custom/')
|
||||
|
||||
import os, sys
|
||||
|
||||
env = Environment( ENV=os.environ )
|
||||
|
||||
# ---- Docs ----
|
||||
def build_docs(env, target, source):
|
||||
buildscript_path = os.path.join(os.path.abspath("docs"))
|
||||
sys.path.insert(0, buildscript_path)
|
||||
import buildscripts
|
||||
from buildscripts import docs
|
||||
docs.main()
|
||||
|
||||
env.Alias("docs", [], [build_docs])
|
||||
env.AlwaysBuild("docs")
|
||||
|
||||
# ---- Platforms ----
|
||||
PLATFORM_TEST_DIR = None
|
||||
if "LINUX" == GetOption('compile_platform'):
|
||||
env.Append( CPPFLAGS=" -D_MONGO_USE_LINUX_SYSTEM" )
|
||||
NET_LIB = "src/platform/linux/net.c"
|
||||
PLATFORM_TEST_DIR = "test/platform/linux/"
|
||||
PLATFORM_TESTS = [ "timeouts" ]
|
||||
elif "CUSTOM" == GetOption('compile_platform'):
|
||||
env.Append( CPPFLAGS=" -D_MONGO_USE_CUSTOM_SYSTEM" )
|
||||
NET_LIB = "src/platform/custom/net.c"
|
||||
else:
|
||||
NET_LIB = "src/net.c"
|
||||
|
||||
# ---- Libraries ----
|
||||
if os.sys.platform in ["darwin", "linux2"]:
|
||||
env.Append( CPPFLAGS=" -pedantic -Wall -ggdb -DMONGO_HAVE_STDINT" )
|
||||
env.Append( CPPPATH=["/opt/local/include/"] )
|
||||
env.Append( LIBPATH=["/opt/local/lib/"] )
|
||||
|
||||
if GetOption('use_c99'):
|
||||
env.Append( CFLAGS=" -std=c99 " )
|
||||
env.Append( CXXDEFINES="MONGO_HAVE_STDINT" )
|
||||
else:
|
||||
env.Append( CFLAGS=" -ansi " )
|
||||
|
||||
if GetOption('optimize'):
|
||||
env.Append( CPPFLAGS=" -O3 " )
|
||||
# -O3 benchmarks *significantly* faster than -O2 when disabling networking
|
||||
elif 'win32' == os.sys.platform:
|
||||
env.Append( LIBS='ws2_32' )
|
||||
|
||||
#we shouldn't need these options in c99 mode
|
||||
if not GetOption('use_c99'):
|
||||
conf = Configure(env)
|
||||
|
||||
if not conf.CheckType('int64_t'):
|
||||
if conf.CheckType('int64_t', '#include <stdint.h>\n'):
|
||||
conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" )
|
||||
elif conf.CheckType('int64_t', '#include <unistd.h>\n'):
|
||||
conf.env.Append( CPPDEFINES="MONGO_HAVE_UNISTD" )
|
||||
elif conf.CheckType('__int64'):
|
||||
conf.env.Append( CPPDEFINES="MONGO_USE__INT64" )
|
||||
elif conf.CheckType('long long int'):
|
||||
conf.env.Append( CPPDEFINES="MONGO_USE_LONG_LONG_INT" )
|
||||
else:
|
||||
print "*** what is your 64 bit int type? ****"
|
||||
Exit(1)
|
||||
|
||||
env = conf.Finish()
|
||||
|
||||
have_libjson = False
|
||||
conf = Configure(env)
|
||||
if conf.CheckLib('json'):
|
||||
have_libjson = True
|
||||
env = conf.Finish()
|
||||
|
||||
if sys.byteorder == 'big':
|
||||
env.Append( CPPDEFINES="MONGO_BIG_ENDIAN" )
|
||||
|
||||
env.Append( CPPPATH=["src/"] )
|
||||
|
||||
coreFiles = ["src/md5.c" ]
|
||||
mFiles = [ "src/mongo.c", NET_LIB, "src/gridfs.c"]
|
||||
bFiles = [ "src/bson.c", "src/numbers.c", "src/encoding.c"]
|
||||
mLibFiles = coreFiles + mFiles + bFiles
|
||||
bLibFiles = coreFiles + bFiles
|
||||
m = env.Library( "mongoc" , mLibFiles )
|
||||
b = env.Library( "bson" , bLibFiles )
|
||||
env.Default( env.Alias( "lib" , [ m[0] , b[0] ] ) )
|
||||
|
||||
if os.sys.platform == "linux2":
|
||||
env.Append( SHLINKFLAGS="-shared -Wl,-soname,libmongoc.so." + VERSION )
|
||||
env.Append( SHLINKFLAGS = "-shared -Wl,-soname,libbson.so." + VERSION )
|
||||
|
||||
dynm = env.SharedLibrary( "mongoc" , mLibFiles )
|
||||
dynb = env.SharedLibrary( "bson" , bLibFiles )
|
||||
env.Default( env.Alias( "sharedlib" , [ dynm[0] , dynb[0] ] ) )
|
||||
|
||||
|
||||
|
||||
# ---- Benchmarking ----
|
||||
benchmarkEnv = env.Clone()
|
||||
benchmarkEnv.Append( CPPDEFINES=[('TEST_SERVER', r'\"%s\"'%GetOption('test_server')),
|
||||
('SEED_START_PORT', r'%d'%GetOption('seed_start_port'))] )
|
||||
benchmarkEnv.Append( LIBS=[m, b] )
|
||||
benchmarkEnv.Prepend( LIBPATH=["."] )
|
||||
benchmarkEnv.Program( "benchmark" , [ "test/benchmark.c"] )
|
||||
|
||||
# ---- Tests ----
|
||||
testEnv = benchmarkEnv.Clone()
|
||||
testCoreFiles = [ ]
|
||||
|
||||
def run_tests( root, tests ):
|
||||
for name in tests:
|
||||
filename = "%s/%s.c" % (root, name)
|
||||
exe = "test_" + name
|
||||
test = testEnv.Program( exe , testCoreFiles + [filename] )
|
||||
test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> ' + os.path.devnull)
|
||||
AlwaysBuild(test_alias)
|
||||
|
||||
tests = Split("sizes resize endian_swap bson bson_subobject simple update errors "
|
||||
"count_delete auth gridfs validate examples helpers oid functions cursors replica_set")
|
||||
|
||||
# Run standard tests
|
||||
run_tests("test", tests)
|
||||
|
||||
# Run platform tests
|
||||
if not PLATFORM_TEST_DIR is None:
|
||||
run_tests( PLATFORM_TEST_DIR, PLATFORM_TESTS )
|
||||
|
||||
if have_libjson:
|
||||
tests.append('json')
|
||||
testEnv.Append( LIBS=["json"] )
|
||||
|
||||
# special case for cpptest
|
||||
test = testEnv.Program( 'test_cpp' , testCoreFiles + ['test/cpptest.cpp'] )
|
||||
test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> '+ os.path.devnull)
|
||||
AlwaysBuild(test_alias)
|
||||
@@ -0,0 +1,978 @@
|
||||
/* bson.c */
|
||||
|
||||
/* Copyright 2009, 2010 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "bson.h"
|
||||
#include "encoding.h"
|
||||
|
||||
const int initialBufferSize = 128;
|
||||
|
||||
/* only need one of these */
|
||||
static const int zero = 0;
|
||||
|
||||
/* Custom standard function pointers. */
|
||||
void *( *bson_malloc_func )( size_t ) = malloc;
|
||||
void *( *bson_realloc_func )( void *, size_t ) = realloc;
|
||||
void ( *bson_free )( void * ) = free;
|
||||
bson_printf_func bson_printf = printf;
|
||||
bson_fprintf_func bson_fprintf = fprintf;
|
||||
bson_sprintf_func bson_sprintf = sprintf;
|
||||
|
||||
static int _bson_errprintf( const char *, ... );
|
||||
bson_printf_func bson_errprintf = _bson_errprintf;
|
||||
|
||||
/* ObjectId fuzz functions. */
|
||||
static int ( *oid_fuzz_func )( void ) = NULL;
|
||||
static int ( *oid_inc_func )( void ) = NULL;
|
||||
|
||||
/* ----------------------------
|
||||
READING
|
||||
------------------------------ */
|
||||
|
||||
bson *bson_empty( bson *obj ) {
|
||||
static char *data = "\005\0\0\0\0";
|
||||
bson_init_data( obj, data );
|
||||
obj->finished = 1;
|
||||
obj->err = 0;
|
||||
obj->stackPos = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void bson_copy_basic( bson *out, const bson *in ) {
|
||||
if ( !out ) return;
|
||||
bson_init_size( out, bson_size( in ) );
|
||||
memcpy( out->data, in->data, bson_size( in ) );
|
||||
}
|
||||
|
||||
void bson_copy( bson *out, const bson *in ) {
|
||||
int i;
|
||||
|
||||
if ( !out ) return;
|
||||
bson_copy_basic( out, in );
|
||||
out->cur = out->data + ( in->cur - in->data );
|
||||
out->dataSize = in->dataSize;
|
||||
out->finished = in->finished;
|
||||
out->stackPos = in->stackPos;
|
||||
out->err = in->err;
|
||||
for( i=0; i<out->stackPos; i++ )
|
||||
out->stack[i] = in->stack[i];
|
||||
}
|
||||
|
||||
int bson_init_data( bson *b, char *data ) {
|
||||
b->data = data;
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
static void _bson_reset( bson *b ) {
|
||||
b->finished = 0;
|
||||
b->stackPos = 0;
|
||||
b->err = 0;
|
||||
b->errstr = NULL;
|
||||
}
|
||||
|
||||
int bson_size( const bson *b ) {
|
||||
int i;
|
||||
if ( ! b || ! b->data )
|
||||
return 0;
|
||||
bson_little_endian32( &i, b->data );
|
||||
return i;
|
||||
}
|
||||
|
||||
const char *bson_data( bson *b ) {
|
||||
return (const char *)b->data;
|
||||
}
|
||||
|
||||
static char hexbyte( char hex ) {
|
||||
switch ( hex ) {
|
||||
case '0':
|
||||
return 0x0;
|
||||
case '1':
|
||||
return 0x1;
|
||||
case '2':
|
||||
return 0x2;
|
||||
case '3':
|
||||
return 0x3;
|
||||
case '4':
|
||||
return 0x4;
|
||||
case '5':
|
||||
return 0x5;
|
||||
case '6':
|
||||
return 0x6;
|
||||
case '7':
|
||||
return 0x7;
|
||||
case '8':
|
||||
return 0x8;
|
||||
case '9':
|
||||
return 0x9;
|
||||
case 'a':
|
||||
case 'A':
|
||||
return 0xa;
|
||||
case 'b':
|
||||
case 'B':
|
||||
return 0xb;
|
||||
case 'c':
|
||||
case 'C':
|
||||
return 0xc;
|
||||
case 'd':
|
||||
case 'D':
|
||||
return 0xd;
|
||||
case 'e':
|
||||
case 'E':
|
||||
return 0xe;
|
||||
case 'f':
|
||||
case 'F':
|
||||
return 0xf;
|
||||
default:
|
||||
return 0x0; /* something smarter? */
|
||||
}
|
||||
}
|
||||
|
||||
void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
|
||||
int i;
|
||||
for ( i=0; i<12; i++ ) {
|
||||
oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
|
||||
}
|
||||
}
|
||||
|
||||
void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
|
||||
static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
|
||||
int i;
|
||||
for ( i=0; i<12; i++ ) {
|
||||
str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4];
|
||||
str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ];
|
||||
}
|
||||
str[24] = '\0';
|
||||
}
|
||||
|
||||
void bson_set_oid_fuzz( int ( *func )( void ) ) {
|
||||
oid_fuzz_func = func;
|
||||
}
|
||||
|
||||
void bson_set_oid_inc( int ( *func )( void ) ) {
|
||||
oid_inc_func = func;
|
||||
}
|
||||
|
||||
void bson_oid_gen( bson_oid_t *oid ) {
|
||||
static int incr = 0;
|
||||
static int fuzz = 0;
|
||||
int i;
|
||||
int t = time( NULL );
|
||||
|
||||
if( oid_inc_func )
|
||||
i = oid_inc_func();
|
||||
else
|
||||
i = incr++;
|
||||
|
||||
if ( !fuzz ) {
|
||||
if ( oid_fuzz_func )
|
||||
fuzz = oid_fuzz_func();
|
||||
else {
|
||||
srand( t );
|
||||
fuzz = rand();
|
||||
}
|
||||
}
|
||||
|
||||
bson_big_endian32( &oid->ints[0], &t );
|
||||
oid->ints[1] = fuzz;
|
||||
bson_big_endian32( &oid->ints[2], &i );
|
||||
}
|
||||
|
||||
time_t bson_oid_generated_time( bson_oid_t *oid ) {
|
||||
time_t out;
|
||||
bson_big_endian32( &out, &oid->ints[0] );
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void bson_print( bson *b ) {
|
||||
bson_print_raw( b->data , 0 );
|
||||
}
|
||||
|
||||
void bson_print_raw( const char *data , int depth ) {
|
||||
bson_iterator i;
|
||||
const char *key;
|
||||
int temp;
|
||||
bson_timestamp_t ts;
|
||||
char oidhex[25];
|
||||
bson scope;
|
||||
bson_iterator_from_buffer( &i, data );
|
||||
|
||||
while ( bson_iterator_next( &i ) ) {
|
||||
bson_type t = bson_iterator_type( &i );
|
||||
if ( t == 0 )
|
||||
break;
|
||||
key = bson_iterator_key( &i );
|
||||
|
||||
for ( temp=0; temp<=depth; temp++ )
|
||||
printf( "\t" );
|
||||
bson_printf( "%s : %d \t " , key , t );
|
||||
switch ( t ) {
|
||||
case BSON_DOUBLE:
|
||||
printf( "%f" , bson_iterator_double( &i ) );
|
||||
break;
|
||||
case BSON_STRING:
|
||||
printf( "%s" , bson_iterator_string( &i ) );
|
||||
break;
|
||||
case BSON_SYMBOL:
|
||||
printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
|
||||
break;
|
||||
case BSON_OID:
|
||||
bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
|
||||
printf( "%s" , oidhex );
|
||||
break;
|
||||
case BSON_BOOL:
|
||||
printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
|
||||
break;
|
||||
case BSON_DATE:
|
||||
printf( "%ld" , ( long int )bson_iterator_date( &i ) );
|
||||
break;
|
||||
case BSON_BINDATA:
|
||||
printf( "BSON_BINDATA" );
|
||||
break;
|
||||
case BSON_UNDEFINED:
|
||||
printf( "BSON_UNDEFINED" );
|
||||
break;
|
||||
case BSON_NULL:
|
||||
printf( "BSON_NULL" );
|
||||
break;
|
||||
case BSON_REGEX:
|
||||
printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
|
||||
break;
|
||||
case BSON_CODE:
|
||||
printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
|
||||
break;
|
||||
case BSON_CODEWSCOPE:
|
||||
printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
|
||||
bson_init( &scope );
|
||||
bson_iterator_code_scope( &i, &scope );
|
||||
printf( "\n\t SCOPE: " );
|
||||
bson_print( &scope );
|
||||
break;
|
||||
case BSON_INT:
|
||||
printf( "%d" , bson_iterator_int( &i ) );
|
||||
break;
|
||||
case BSON_LONG:
|
||||
printf( "%lld" , ( long long int )bson_iterator_long( &i ) );
|
||||
break;
|
||||
case BSON_TIMESTAMP:
|
||||
ts = bson_iterator_timestamp( &i );
|
||||
printf( "i: %d, t: %d", ts.i, ts.t );
|
||||
break;
|
||||
case BSON_OBJECT:
|
||||
case BSON_ARRAY:
|
||||
printf( "\n" );
|
||||
bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
|
||||
break;
|
||||
default:
|
||||
bson_errprintf( "can't print type : %d\n" , t );
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------
|
||||
ITERATOR
|
||||
------------------------------ */
|
||||
|
||||
void bson_iterator_init( bson_iterator *i, const bson *b ) {
|
||||
i->cur = b->data + 4;
|
||||
i->first = 1;
|
||||
}
|
||||
|
||||
void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
|
||||
i->cur = buffer + 4;
|
||||
i->first = 1;
|
||||
}
|
||||
|
||||
bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
|
||||
bson_iterator_init( it, (bson *)obj );
|
||||
while( bson_iterator_next( it ) ) {
|
||||
if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
|
||||
break;
|
||||
}
|
||||
return bson_iterator_type( it );
|
||||
}
|
||||
|
||||
bson_bool_t bson_iterator_more( const bson_iterator *i ) {
|
||||
return *( i->cur );
|
||||
}
|
||||
|
||||
bson_type bson_iterator_next( bson_iterator *i ) {
|
||||
int ds;
|
||||
|
||||
if ( i->first ) {
|
||||
i->first = 0;
|
||||
return ( bson_type )( *i->cur );
|
||||
}
|
||||
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_EOO:
|
||||
return BSON_EOO; /* don't advance */
|
||||
case BSON_UNDEFINED:
|
||||
case BSON_NULL:
|
||||
ds = 0;
|
||||
break;
|
||||
case BSON_BOOL:
|
||||
ds = 1;
|
||||
break;
|
||||
case BSON_INT:
|
||||
ds = 4;
|
||||
break;
|
||||
case BSON_LONG:
|
||||
case BSON_DOUBLE:
|
||||
case BSON_TIMESTAMP:
|
||||
case BSON_DATE:
|
||||
ds = 8;
|
||||
break;
|
||||
case BSON_OID:
|
||||
ds = 12;
|
||||
break;
|
||||
case BSON_STRING:
|
||||
case BSON_SYMBOL:
|
||||
case BSON_CODE:
|
||||
ds = 4 + bson_iterator_int_raw( i );
|
||||
break;
|
||||
case BSON_BINDATA:
|
||||
ds = 5 + bson_iterator_int_raw( i );
|
||||
break;
|
||||
case BSON_OBJECT:
|
||||
case BSON_ARRAY:
|
||||
case BSON_CODEWSCOPE:
|
||||
ds = bson_iterator_int_raw( i );
|
||||
break;
|
||||
case BSON_DBREF:
|
||||
ds = 4+12 + bson_iterator_int_raw( i );
|
||||
break;
|
||||
case BSON_REGEX: {
|
||||
const char *s = bson_iterator_value( i );
|
||||
const char *p = s;
|
||||
p += strlen( p )+1;
|
||||
p += strlen( p )+1;
|
||||
ds = p-s;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
char msg[] = "unknown type: 000000000000";
|
||||
bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
|
||||
bson_fatal_msg( 0, msg );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
|
||||
|
||||
return ( bson_type )( *i->cur );
|
||||
}
|
||||
|
||||
bson_type bson_iterator_type( const bson_iterator *i ) {
|
||||
return ( bson_type )i->cur[0];
|
||||
}
|
||||
|
||||
const char *bson_iterator_key( const bson_iterator *i ) {
|
||||
return i->cur + 1;
|
||||
}
|
||||
|
||||
const char *bson_iterator_value( const bson_iterator *i ) {
|
||||
const char *t = i->cur + 1;
|
||||
t += strlen( t ) + 1;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* types */
|
||||
|
||||
int bson_iterator_int_raw( const bson_iterator *i ) {
|
||||
int out;
|
||||
bson_little_endian32( &out, bson_iterator_value( i ) );
|
||||
return out;
|
||||
}
|
||||
|
||||
double bson_iterator_double_raw( const bson_iterator *i ) {
|
||||
double out;
|
||||
bson_little_endian64( &out, bson_iterator_value( i ) );
|
||||
return out;
|
||||
}
|
||||
|
||||
int64_t bson_iterator_long_raw( const bson_iterator *i ) {
|
||||
int64_t out;
|
||||
bson_little_endian64( &out, bson_iterator_value( i ) );
|
||||
return out;
|
||||
}
|
||||
|
||||
bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
|
||||
return bson_iterator_value( i )[0];
|
||||
}
|
||||
|
||||
bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
|
||||
return ( bson_oid_t * )bson_iterator_value( i );
|
||||
}
|
||||
|
||||
int bson_iterator_int( const bson_iterator *i ) {
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_INT:
|
||||
return bson_iterator_int_raw( i );
|
||||
case BSON_LONG:
|
||||
return bson_iterator_long_raw( i );
|
||||
case BSON_DOUBLE:
|
||||
return bson_iterator_double_raw( i );
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double bson_iterator_double( const bson_iterator *i ) {
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_INT:
|
||||
return bson_iterator_int_raw( i );
|
||||
case BSON_LONG:
|
||||
return bson_iterator_long_raw( i );
|
||||
case BSON_DOUBLE:
|
||||
return bson_iterator_double_raw( i );
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t bson_iterator_long( const bson_iterator *i ) {
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_INT:
|
||||
return bson_iterator_int_raw( i );
|
||||
case BSON_LONG:
|
||||
return bson_iterator_long_raw( i );
|
||||
case BSON_DOUBLE:
|
||||
return bson_iterator_double_raw( i );
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
|
||||
bson_timestamp_t ts;
|
||||
bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
|
||||
bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
|
||||
return ts;
|
||||
}
|
||||
|
||||
bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_BOOL:
|
||||
return bson_iterator_bool_raw( i );
|
||||
case BSON_INT:
|
||||
return bson_iterator_int_raw( i ) != 0;
|
||||
case BSON_LONG:
|
||||
return bson_iterator_long_raw( i ) != 0;
|
||||
case BSON_DOUBLE:
|
||||
return bson_iterator_double_raw( i ) != 0;
|
||||
case BSON_EOO:
|
||||
case BSON_NULL:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *bson_iterator_string( const bson_iterator *i ) {
|
||||
return bson_iterator_value( i ) + 4;
|
||||
}
|
||||
|
||||
int bson_iterator_string_len( const bson_iterator *i ) {
|
||||
return bson_iterator_int_raw( i );
|
||||
}
|
||||
|
||||
const char *bson_iterator_code( const bson_iterator *i ) {
|
||||
switch ( bson_iterator_type( i ) ) {
|
||||
case BSON_STRING:
|
||||
case BSON_CODE:
|
||||
return bson_iterator_value( i ) + 4;
|
||||
case BSON_CODEWSCOPE:
|
||||
return bson_iterator_value( i ) + 8;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
|
||||
if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
|
||||
int code_len;
|
||||
bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
|
||||
bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) );
|
||||
} else {
|
||||
bson_empty( scope );
|
||||
}
|
||||
}
|
||||
|
||||
bson_date_t bson_iterator_date( const bson_iterator *i ) {
|
||||
return bson_iterator_long_raw( i );
|
||||
}
|
||||
|
||||
time_t bson_iterator_time_t( const bson_iterator *i ) {
|
||||
return bson_iterator_date( i ) / 1000;
|
||||
}
|
||||
|
||||
int bson_iterator_bin_len( const bson_iterator *i ) {
|
||||
return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
|
||||
? bson_iterator_int_raw( i ) - 4
|
||||
: bson_iterator_int_raw( i );
|
||||
}
|
||||
|
||||
char bson_iterator_bin_type( const bson_iterator *i ) {
|
||||
return bson_iterator_value( i )[4];
|
||||
}
|
||||
|
||||
const char *bson_iterator_bin_data( const bson_iterator *i ) {
|
||||
return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
|
||||
? bson_iterator_value( i ) + 9
|
||||
: bson_iterator_value( i ) + 5;
|
||||
}
|
||||
|
||||
const char *bson_iterator_regex( const bson_iterator *i ) {
|
||||
return bson_iterator_value( i );
|
||||
}
|
||||
|
||||
const char *bson_iterator_regex_opts( const bson_iterator *i ) {
|
||||
const char *p = bson_iterator_value( i );
|
||||
return p + strlen( p ) + 1;
|
||||
|
||||
}
|
||||
|
||||
void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
|
||||
bson_init_data( sub, ( char * )bson_iterator_value( i ) );
|
||||
_bson_reset( sub );
|
||||
sub->finished = 1;
|
||||
}
|
||||
|
||||
void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
|
||||
bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
|
||||
}
|
||||
|
||||
/* ----------------------------
|
||||
BUILDING
|
||||
------------------------------ */
|
||||
|
||||
static void _bson_init_size( bson *b, int size ) {
|
||||
if( size == 0 )
|
||||
b->data = NULL;
|
||||
else
|
||||
b->data = ( char * )bson_malloc( size );
|
||||
b->dataSize = size;
|
||||
b->cur = b->data + 4;
|
||||
_bson_reset( b );
|
||||
}
|
||||
|
||||
void bson_init( bson *b ) {
|
||||
_bson_init_size( b, initialBufferSize );
|
||||
}
|
||||
|
||||
void bson_init_size( bson *b, int size ) {
|
||||
_bson_init_size( b, size );
|
||||
}
|
||||
|
||||
void bson_append_byte( bson *b, char c ) {
|
||||
b->cur[0] = c;
|
||||
b->cur++;
|
||||
}
|
||||
|
||||
void bson_append( bson *b, const void *data, int len ) {
|
||||
memcpy( b->cur , data , len );
|
||||
b->cur += len;
|
||||
}
|
||||
|
||||
void bson_append32( bson *b, const void *data ) {
|
||||
bson_little_endian32( b->cur, data );
|
||||
b->cur += 4;
|
||||
}
|
||||
|
||||
void bson_append64( bson *b, const void *data ) {
|
||||
bson_little_endian64( b->cur, data );
|
||||
b->cur += 8;
|
||||
}
|
||||
|
||||
int bson_ensure_space( bson *b, const int bytesNeeded ) {
|
||||
int pos = b->cur - b->data;
|
||||
char *orig = b->data;
|
||||
int new_size;
|
||||
|
||||
if ( pos + bytesNeeded <= b->dataSize )
|
||||
return BSON_OK;
|
||||
|
||||
new_size = 1.5 * ( b->dataSize + bytesNeeded );
|
||||
|
||||
if( new_size < b->dataSize ) {
|
||||
if( ( b->dataSize + bytesNeeded ) < INT_MAX )
|
||||
new_size = INT_MAX;
|
||||
else {
|
||||
b->err = BSON_SIZE_OVERFLOW;
|
||||
return BSON_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
b->data = bson_realloc( b->data, new_size );
|
||||
if ( !b->data )
|
||||
bson_fatal_msg( !!b->data, "realloc() failed" );
|
||||
|
||||
b->dataSize = new_size;
|
||||
b->cur += b->data - orig;
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_finish( bson *b ) {
|
||||
int i;
|
||||
|
||||
if( b->err & BSON_NOT_UTF8 )
|
||||
return BSON_ERROR;
|
||||
|
||||
if ( ! b->finished ) {
|
||||
if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
bson_append_byte( b, 0 );
|
||||
i = b->cur - b->data;
|
||||
bson_little_endian32( b->data, &i );
|
||||
b->finished = 1;
|
||||
}
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
void bson_destroy( bson *b ) {
|
||||
bson_free( b->data );
|
||||
b->err = 0;
|
||||
b->data = 0;
|
||||
b->cur = 0;
|
||||
b->finished = 1;
|
||||
}
|
||||
|
||||
static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
|
||||
const int len = strlen( name ) + 1;
|
||||
|
||||
if ( b->finished ) {
|
||||
b->err |= BSON_ALREADY_FINISHED;
|
||||
return BSON_ERROR;
|
||||
}
|
||||
|
||||
if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
|
||||
return BSON_ERROR;
|
||||
}
|
||||
|
||||
if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
|
||||
bson_builder_error( b );
|
||||
return BSON_ERROR;
|
||||
}
|
||||
|
||||
bson_append_byte( b, ( char )type );
|
||||
bson_append( b, name, len );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
/* ----------------------------
|
||||
BUILDING TYPES
|
||||
------------------------------ */
|
||||
|
||||
int bson_append_int( bson *b, const char *name, const int i ) {
|
||||
if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append32( b , &i );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_long( bson *b, const char *name, const int64_t i ) {
|
||||
if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append64( b , &i );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_double( bson *b, const char *name, const double d ) {
|
||||
if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append64( b , &d );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
|
||||
if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append_byte( b , i != 0 );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_null( bson *b, const char *name ) {
|
||||
if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_undefined( bson *b, const char *name ) {
|
||||
if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_string_base( bson *b, const char *name,
|
||||
const char *value, int len, bson_type type ) {
|
||||
|
||||
int sl = len + 1;
|
||||
if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
|
||||
return BSON_ERROR;
|
||||
}
|
||||
bson_append32( b , &sl );
|
||||
bson_append( b , value , sl - 1 );
|
||||
bson_append( b , "\0" , 1 );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_string( bson *b, const char *name, const char *value ) {
|
||||
return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
|
||||
}
|
||||
|
||||
int bson_append_symbol( bson *b, const char *name, const char *value ) {
|
||||
return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
|
||||
}
|
||||
|
||||
int bson_append_code( bson *b, const char *name, const char *value ) {
|
||||
return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
|
||||
}
|
||||
|
||||
int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
|
||||
return bson_append_string_base( b, name, value, len, BSON_STRING );
|
||||
}
|
||||
|
||||
int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
|
||||
return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
|
||||
}
|
||||
|
||||
int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
|
||||
return bson_append_string_base( b, name, value, len, BSON_CODE );
|
||||
}
|
||||
|
||||
int bson_append_code_w_scope_n( bson *b, const char *name,
|
||||
const char *code, int len, const bson *scope ) {
|
||||
|
||||
int sl = len + 1;
|
||||
int size = 4 + 4 + sl + bson_size( scope );
|
||||
if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append32( b, &size );
|
||||
bson_append32( b, &sl );
|
||||
bson_append( b, code, sl );
|
||||
bson_append( b, scope->data, bson_size( scope ) );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
|
||||
return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
|
||||
}
|
||||
|
||||
int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
|
||||
if ( type == BSON_BIN_BINARY_OLD ) {
|
||||
int subtwolen = len + 4;
|
||||
if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append32( b, &subtwolen );
|
||||
bson_append_byte( b, type );
|
||||
bson_append32( b, &len );
|
||||
bson_append( b, str, len );
|
||||
} else {
|
||||
if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append32( b, &len );
|
||||
bson_append_byte( b, type );
|
||||
bson_append( b, str, len );
|
||||
}
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
|
||||
if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append( b , oid , 12 );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_new_oid( bson *b, const char *name ) {
|
||||
bson_oid_t oid;
|
||||
bson_oid_gen( &oid );
|
||||
return bson_append_oid( b, name, &oid );
|
||||
}
|
||||
|
||||
int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
|
||||
const int plen = strlen( pattern )+1;
|
||||
const int olen = strlen( opts )+1;
|
||||
if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append( b , pattern , plen );
|
||||
bson_append( b , opts , olen );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_bson( bson *b, const char *name, const bson *bson ) {
|
||||
if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append( b , bson->data , bson_size( bson ) );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
|
||||
bson_iterator next = *elem;
|
||||
int size;
|
||||
|
||||
bson_iterator_next( &next );
|
||||
size = next.cur - elem->cur;
|
||||
|
||||
if ( name_or_null == NULL ) {
|
||||
if( bson_ensure_space( b, size ) == BSON_ERROR )
|
||||
return BSON_ERROR;
|
||||
bson_append( b, elem->cur, size );
|
||||
} else {
|
||||
int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
|
||||
bson_append_estart( b, elem->cur[0], name_or_null, data_size );
|
||||
bson_append( b, bson_iterator_value( elem ), data_size );
|
||||
}
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
|
||||
if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
|
||||
bson_append32( b , &( ts->i ) );
|
||||
bson_append32( b , &( ts->t ) );
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
|
||||
if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
bson_append64( b , &millis );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_time_t( bson *b, const char *name, time_t secs ) {
|
||||
return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
|
||||
}
|
||||
|
||||
int bson_append_start_object( bson *b, const char *name ) {
|
||||
if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
b->stack[ b->stackPos++ ] = b->cur - b->data;
|
||||
bson_append32( b , &zero );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_start_array( bson *b, const char *name ) {
|
||||
if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
b->stack[ b->stackPos++ ] = b->cur - b->data;
|
||||
bson_append32( b , &zero );
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_finish_object( bson *b ) {
|
||||
char *start;
|
||||
int i;
|
||||
if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
|
||||
bson_append_byte( b , 0 );
|
||||
|
||||
start = b->data + b->stack[ --b->stackPos ];
|
||||
i = b->cur - start;
|
||||
bson_little_endian32( start, &i );
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
int bson_append_finish_array( bson *b ) {
|
||||
return bson_append_finish_object( b );
|
||||
}
|
||||
|
||||
|
||||
/* Error handling and allocators. */
|
||||
|
||||
static bson_err_handler err_handler = NULL;
|
||||
|
||||
bson_err_handler set_bson_err_handler( bson_err_handler func ) {
|
||||
bson_err_handler old = err_handler;
|
||||
err_handler = func;
|
||||
return old;
|
||||
}
|
||||
|
||||
void *bson_malloc( int size ) {
|
||||
void *p;
|
||||
p = bson_malloc_func( size );
|
||||
bson_fatal_msg( !!p, "malloc() failed" );
|
||||
return p;
|
||||
}
|
||||
|
||||
void *bson_realloc( void *ptr, int size ) {
|
||||
void *p;
|
||||
p = bson_realloc_func( ptr, size );
|
||||
bson_fatal_msg( !!p, "realloc() failed" );
|
||||
return p;
|
||||
}
|
||||
|
||||
int _bson_errprintf( const char *format, ... ) {
|
||||
va_list ap;
|
||||
int ret;
|
||||
va_start( ap, format );
|
||||
ret = vfprintf( stderr, format, ap );
|
||||
va_end( ap );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked when a non-fatal bson error is encountered.
|
||||
* Calls the error handler if available.
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
void bson_builder_error( bson *b ) {
|
||||
if( err_handler )
|
||||
err_handler( "BSON error." );
|
||||
}
|
||||
|
||||
void bson_fatal( int ok ) {
|
||||
bson_fatal_msg( ok, "" );
|
||||
}
|
||||
|
||||
void bson_fatal_msg( int ok , const char *msg ) {
|
||||
if ( ok )
|
||||
return;
|
||||
|
||||
if ( err_handler ) {
|
||||
err_handler( msg );
|
||||
}
|
||||
|
||||
bson_errprintf( "error: %s\n" , msg );
|
||||
exit( -5 );
|
||||
}
|
||||
|
||||
|
||||
/* Efficiently copy an integer to a string. */
|
||||
extern const char bson_numstrs[1000][4];
|
||||
|
||||
void bson_numstr( char *str, int i ) {
|
||||
if( i < 1000 )
|
||||
memcpy( str, bson_numstrs[i], 4 );
|
||||
else
|
||||
bson_sprintf( str,"%d", i );
|
||||
}
|
||||
@@ -0,0 +1,975 @@
|
||||
/**
|
||||
* @file bson.h
|
||||
* @brief BSON Declarations
|
||||
*/
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BSON_H_
|
||||
#define _BSON_H_
|
||||
|
||||
#include "platform.h"
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
#define BSON_OK 0
|
||||
#define BSON_ERROR -1
|
||||
|
||||
enum bson_error_t {
|
||||
BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */
|
||||
};
|
||||
|
||||
enum bson_validity_t {
|
||||
BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */
|
||||
BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */
|
||||
BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */
|
||||
BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */
|
||||
BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */
|
||||
};
|
||||
|
||||
enum bson_binary_subtype_t {
|
||||
BSON_BIN_BINARY = 0,
|
||||
BSON_BIN_FUNC = 1,
|
||||
BSON_BIN_BINARY_OLD = 2,
|
||||
BSON_BIN_UUID = 3,
|
||||
BSON_BIN_MD5 = 5,
|
||||
BSON_BIN_USER = 128
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
BSON_EOO = 0,
|
||||
BSON_DOUBLE = 1,
|
||||
BSON_STRING = 2,
|
||||
BSON_OBJECT = 3,
|
||||
BSON_ARRAY = 4,
|
||||
BSON_BINDATA = 5,
|
||||
BSON_UNDEFINED = 6,
|
||||
BSON_OID = 7,
|
||||
BSON_BOOL = 8,
|
||||
BSON_DATE = 9,
|
||||
BSON_NULL = 10,
|
||||
BSON_REGEX = 11,
|
||||
BSON_DBREF = 12, /**< Deprecated. */
|
||||
BSON_CODE = 13,
|
||||
BSON_SYMBOL = 14,
|
||||
BSON_CODEWSCOPE = 15,
|
||||
BSON_INT = 16,
|
||||
BSON_TIMESTAMP = 17,
|
||||
BSON_LONG = 18
|
||||
} bson_type;
|
||||
|
||||
typedef int bson_bool_t;
|
||||
|
||||
typedef struct {
|
||||
const char *cur;
|
||||
bson_bool_t first;
|
||||
} bson_iterator;
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
char *cur;
|
||||
int dataSize;
|
||||
bson_bool_t finished;
|
||||
int stack[32];
|
||||
int stackPos;
|
||||
int err; /**< Bitfield representing errors or warnings on this buffer */
|
||||
char *errstr; /**< A string representation of the most recent error or warning. */
|
||||
} bson;
|
||||
|
||||
#pragma pack(1)
|
||||
typedef union {
|
||||
char bytes[12];
|
||||
int ints[3];
|
||||
} bson_oid_t;
|
||||
#pragma pack()
|
||||
|
||||
typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
|
||||
|
||||
typedef struct {
|
||||
int i; /* increment */
|
||||
int t; /* time in seconds */
|
||||
} bson_timestamp_t;
|
||||
|
||||
/* ----------------------------
|
||||
READING
|
||||
------------------------------ */
|
||||
|
||||
/**
|
||||
* Size of a BSON object.
|
||||
*
|
||||
* @param b the BSON object.
|
||||
*
|
||||
* @return the size.
|
||||
*/
|
||||
int bson_size( const bson *b );
|
||||
|
||||
/**
|
||||
* Print a string representation of a BSON object.
|
||||
*
|
||||
* @param b the BSON object to print.
|
||||
*/
|
||||
void bson_print( bson *b );
|
||||
|
||||
/**
|
||||
* Return a pointer to the raw buffer stored by this bson object.
|
||||
*
|
||||
* @param b a BSON object
|
||||
*/
|
||||
const char *bson_data( bson *b );
|
||||
|
||||
/**
|
||||
* Print a string representation of a BSON object.
|
||||
*
|
||||
* @param bson the raw data to print.
|
||||
* @param depth the depth to recurse the object.x
|
||||
*/
|
||||
void bson_print_raw( const char *bson , int depth );
|
||||
|
||||
/**
|
||||
* Advance a bson_iterator to the named field.
|
||||
*
|
||||
* @param it the bson_iterator to use.
|
||||
* @param obj the BSON object to use.
|
||||
* @param name the name of the field to find.
|
||||
*
|
||||
* @return the type of the found object or BSON_EOO if it is not found.
|
||||
*/
|
||||
bson_type bson_find( bson_iterator *it, const bson *obj, const char *name );
|
||||
|
||||
/**
|
||||
* Initialize a bson_iterator.
|
||||
*
|
||||
* @param i the bson_iterator to initialize.
|
||||
* @param bson the BSON object to associate with the iterator.
|
||||
*/
|
||||
void bson_iterator_init( bson_iterator *i , const bson *b );
|
||||
|
||||
/**
|
||||
* Initialize a bson iterator from a const char* buffer. Note
|
||||
* that this is mostly used internally.
|
||||
*
|
||||
* @param i the bson_iterator to initialize.
|
||||
* @param buffer the buffer to point to.
|
||||
*/
|
||||
void bson_iterator_from_buffer( bson_iterator *i, const char *buffer );
|
||||
|
||||
/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
|
||||
/**
|
||||
* Check to see if the bson_iterator has more data.
|
||||
*
|
||||
* @param i the iterator.
|
||||
*
|
||||
* @return returns true if there is more data.
|
||||
*/
|
||||
bson_bool_t bson_iterator_more( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Point the iterator at the next BSON object.
|
||||
*
|
||||
* @param i the bson_iterator.
|
||||
*
|
||||
* @return the type of the next BSON object.
|
||||
*/
|
||||
bson_type bson_iterator_next( bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the type of the BSON object currently pointed to by the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the type of the current BSON object.
|
||||
*/
|
||||
bson_type bson_iterator_type( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the key of the BSON object currently pointed to by the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the key of the current BSON object.
|
||||
*/
|
||||
const char *bson_iterator_key( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the value of the BSON object currently pointed to by the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
const char *bson_iterator_value( const bson_iterator *i );
|
||||
|
||||
/* these convert to the right type (return 0 if non-numeric) */
|
||||
/**
|
||||
* Get the double value of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
double bson_iterator_double( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the int value of the BSON object currently pointed to by the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
int bson_iterator_int( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the long value of the BSON object currently pointed to by the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
int64_t bson_iterator_long( const bson_iterator *i );
|
||||
|
||||
/* return the bson timestamp as a whole or in parts */
|
||||
/**
|
||||
* Get the timestamp value of the BSON object currently pointed to by
|
||||
* the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the boolean value of the BSON object currently pointed to by
|
||||
* the iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
/* false: boolean false, 0 in any type, or null */
|
||||
/* true: anything else (even empty strings and objects) */
|
||||
bson_bool_t bson_iterator_bool( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the double value of the BSON object currently pointed to by the
|
||||
* iterator. Assumes the correct type is used.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
/* these assume you are using the right type */
|
||||
double bson_iterator_double_raw( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the int value of the BSON object currently pointed to by the
|
||||
* iterator. Assumes the correct type is used.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
int bson_iterator_int_raw( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the long value of the BSON object currently pointed to by the
|
||||
* iterator. Assumes the correct type is used.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
int64_t bson_iterator_long_raw( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the bson_bool_t value of the BSON object currently pointed to by the
|
||||
* iterator. Assumes the correct type is used.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
bson_bool_t bson_iterator_bool_raw( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the bson_oid_t value of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
bson_oid_t *bson_iterator_oid( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the string value of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON object.
|
||||
*/
|
||||
/* these can also be used with bson_code and bson_symbol*/
|
||||
const char *bson_iterator_string( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the string length of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the length of the current BSON object.
|
||||
*/
|
||||
int bson_iterator_string_len( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the code value of the BSON object currently pointed to by the
|
||||
* iterator. Works with bson_code, bson_codewscope, and BSON_STRING
|
||||
* returns NULL for everything else.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the code value of the current BSON object.
|
||||
*/
|
||||
/* works with bson_code, bson_codewscope, and BSON_STRING */
|
||||
/* returns NULL for everything else */
|
||||
const char *bson_iterator_code( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Calls bson_empty on scope if not a bson_codewscope
|
||||
*
|
||||
* @param i the bson_iterator.
|
||||
* @param scope the bson scope.
|
||||
*/
|
||||
/* calls bson_empty on scope if not a bson_codewscope */
|
||||
void bson_iterator_code_scope( const bson_iterator *i, bson *scope );
|
||||
|
||||
/**
|
||||
* Get the date value of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the date value of the current BSON object.
|
||||
*/
|
||||
/* both of these only work with bson_date */
|
||||
bson_date_t bson_iterator_date( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the time value of the BSON object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the time value of the current BSON object.
|
||||
*/
|
||||
time_t bson_iterator_time_t( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the length of the BSON binary object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the length of the current BSON binary object.
|
||||
*/
|
||||
int bson_iterator_bin_len( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the type of the BSON binary object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the type of the current BSON binary object.
|
||||
*/
|
||||
char bson_iterator_bin_type( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the value of the BSON binary object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON binary object.
|
||||
*/
|
||||
const char *bson_iterator_bin_data( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the value of the BSON regex object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator
|
||||
*
|
||||
* @return the value of the current BSON regex object.
|
||||
*/
|
||||
const char *bson_iterator_regex( const bson_iterator *i );
|
||||
|
||||
/**
|
||||
* Get the options of the BSON regex object currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator.
|
||||
*
|
||||
* @return the options of the current BSON regex object.
|
||||
*/
|
||||
const char *bson_iterator_regex_opts( const bson_iterator *i );
|
||||
|
||||
/* these work with BSON_OBJECT and BSON_ARRAY */
|
||||
/**
|
||||
* Get the BSON subobject currently pointed to by the
|
||||
* iterator.
|
||||
*
|
||||
* @param i the bson_iterator.
|
||||
* @param sub the BSON subobject destination.
|
||||
*/
|
||||
void bson_iterator_subobject( const bson_iterator *i, bson *sub );
|
||||
|
||||
/**
|
||||
* Get a bson_iterator that on the BSON subobject.
|
||||
*
|
||||
* @param i the bson_iterator.
|
||||
* @param sub the iterator to point at the BSON subobject.
|
||||
*/
|
||||
void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub );
|
||||
|
||||
/* str must be at least 24 hex chars + null byte */
|
||||
/**
|
||||
* Create a bson_oid_t from a string.
|
||||
*
|
||||
* @param oid the bson_oid_t destination.
|
||||
* @param str a null terminated string comprised of at least 24 hex chars.
|
||||
*/
|
||||
void bson_oid_from_string( bson_oid_t *oid, const char *str );
|
||||
|
||||
/**
|
||||
* Create a string representation of the bson_oid_t.
|
||||
*
|
||||
* @param oid the bson_oid_t source.
|
||||
* @param str the string representation destination.
|
||||
*/
|
||||
void bson_oid_to_string( const bson_oid_t *oid, char *str );
|
||||
|
||||
/**
|
||||
* Create a bson_oid object.
|
||||
*
|
||||
* @param oid the destination for the newly created bson_oid_t.
|
||||
*/
|
||||
void bson_oid_gen( bson_oid_t *oid );
|
||||
|
||||
/**
|
||||
* Set a function to be used to generate the second four bytes
|
||||
* of an object id.
|
||||
*
|
||||
* @param func a pointer to a function that returns an int.
|
||||
*/
|
||||
void bson_set_oid_fuzz( int ( *func )( void ) );
|
||||
|
||||
/**
|
||||
* Set a function to be used to generate the incrementing part
|
||||
* of an object id (last four bytes). If you need thread-safety
|
||||
* in generating object ids, you should set this function.
|
||||
*
|
||||
* @param func a pointer to a function that returns an int.
|
||||
*/
|
||||
void bson_set_oid_inc( int ( *func )( void ) );
|
||||
|
||||
/**
|
||||
* Get the time a bson_oid_t was created.
|
||||
*
|
||||
* @param oid the bson_oid_t.
|
||||
*/
|
||||
time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */
|
||||
|
||||
/* ----------------------------
|
||||
BUILDING
|
||||
------------------------------ */
|
||||
|
||||
/**
|
||||
* Initialize a new bson object. If not created
|
||||
* with bson_new, you must initialize each new bson
|
||||
* object using this function.
|
||||
*
|
||||
* @note When finished, you must pass the bson object to
|
||||
* bson_destroy( ).
|
||||
*/
|
||||
void bson_init( bson *b );
|
||||
|
||||
/**
|
||||
* Initialize a BSON object, and point its data
|
||||
* pointer to the provided char*.
|
||||
*
|
||||
* @param b the BSON object to initialize.
|
||||
* @param data the raw BSON data.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_init_data( bson *b , char *data );
|
||||
|
||||
/**
|
||||
* Initialize a BSON object, and set its
|
||||
* buffer to the given size.
|
||||
*
|
||||
* @param b the BSON object to initialize.
|
||||
* @param size the initial size of the buffer.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
void bson_init_size( bson *b, int size );
|
||||
|
||||
/**
|
||||
* Grow a bson object.
|
||||
*
|
||||
* @param b the bson to grow.
|
||||
* @param bytesNeeded the additional number of bytes needed.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR with the bson error object set.
|
||||
* Exits if allocation fails.
|
||||
*/
|
||||
int bson_ensure_space( bson *b, const int bytesNeeded );
|
||||
|
||||
/**
|
||||
* Finalize a bson object.
|
||||
*
|
||||
* @param b the bson object to finalize.
|
||||
*
|
||||
* @return the standard error code. To deallocate memory,
|
||||
* call bson_destroy on the bson object.
|
||||
*/
|
||||
int bson_finish( bson *b );
|
||||
|
||||
/**
|
||||
* Destroy a bson object.
|
||||
*
|
||||
* @param b the bson object to destroy.
|
||||
*
|
||||
*/
|
||||
void bson_destroy( bson *b );
|
||||
|
||||
/**
|
||||
* Returns a pointer to a static empty BSON object.
|
||||
*
|
||||
* @param obj the BSON object to initialize.
|
||||
*
|
||||
* @return the empty initialized BSON object.
|
||||
*/
|
||||
/* returns pointer to static empty bson object */
|
||||
bson *bson_empty( bson *obj );
|
||||
|
||||
/**
|
||||
* Copy BSON data only from one object to another.
|
||||
*
|
||||
* @param out the copy destination BSON object.
|
||||
* @param in the copy source BSON object.
|
||||
*/
|
||||
void bson_copy_basic( bson *out, const bson *in );
|
||||
|
||||
/**
|
||||
* Make a complete copy of the a BSON object.
|
||||
*
|
||||
* @param out the copy destination BSON object.
|
||||
* @param in the copy source BSON object.
|
||||
*/
|
||||
void bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */
|
||||
|
||||
/**
|
||||
* Append a previously created bson_oid_t to a bson object.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the bson_oid_t.
|
||||
* @param oid the bson_oid_t to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid );
|
||||
|
||||
/**
|
||||
* Append a bson_oid_t to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the bson_oid_t.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_new_oid( bson *b, const char *name );
|
||||
|
||||
/**
|
||||
* Append an int to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the int.
|
||||
* @param i the int to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_int( bson *b, const char *name, const int i );
|
||||
|
||||
/**
|
||||
* Append an long to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the long.
|
||||
* @param i the long to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_long( bson *b, const char *name, const int64_t i );
|
||||
|
||||
/**
|
||||
* Append an double to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the double.
|
||||
* @param d the double to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_double( bson *b, const char *name, const double d );
|
||||
|
||||
/**
|
||||
* Append a string to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the string.
|
||||
* @param str the string to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_string( bson *b, const char *name, const char *str );
|
||||
|
||||
/**
|
||||
* Append len bytes of a string to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the string.
|
||||
* @param str the string to append.
|
||||
* @param len the number of bytes from str to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_string_n( bson *b, const char *name, const char *str, int len );
|
||||
|
||||
/**
|
||||
* Append a symbol to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the symbol.
|
||||
* @param str the symbol to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_symbol( bson *b, const char *name, const char *str );
|
||||
|
||||
/**
|
||||
* Append len bytes of a symbol to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the symbol.
|
||||
* @param str the symbol to append.
|
||||
* @param len the number of bytes from str to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_symbol_n( bson *b, const char *name, const char *str, int len );
|
||||
|
||||
/**
|
||||
* Append code to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the code.
|
||||
* @param str the code to append.
|
||||
* @param len the number of bytes from str to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_code( bson *b, const char *name, const char *str );
|
||||
|
||||
/**
|
||||
* Append len bytes of code to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the code.
|
||||
* @param str the code to append.
|
||||
* @param len the number of bytes from str to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_code_n( bson *b, const char *name, const char *str, int len );
|
||||
|
||||
/**
|
||||
* Append code to a bson with scope.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the code.
|
||||
* @param str the string to append.
|
||||
* @param scope a BSON object containing the scope.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope );
|
||||
|
||||
/**
|
||||
* Append len bytes of code to a bson with scope.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the code.
|
||||
* @param str the string to append.
|
||||
* @param len the number of bytes from str to append.
|
||||
* @param scope a BSON object containing the scope.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope );
|
||||
|
||||
/**
|
||||
* Append binary data to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the data.
|
||||
* @param type the binary data type.
|
||||
* @param str the binary data.
|
||||
* @param len the length of the data.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_binary( bson *b, const char *name, char type, const char *str, int len );
|
||||
|
||||
/**
|
||||
* Append a bson_bool_t to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the boolean value.
|
||||
* @param v the bson_bool_t to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_bool( bson *b, const char *name, const bson_bool_t v );
|
||||
|
||||
/**
|
||||
* Append a null value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the null value.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_null( bson *b, const char *name );
|
||||
|
||||
/**
|
||||
* Append an undefined value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the undefined value.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_undefined( bson *b, const char *name );
|
||||
|
||||
/**
|
||||
* Append a regex value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the regex value.
|
||||
* @param pattern the regex pattern to append.
|
||||
* @param the regex options.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts );
|
||||
|
||||
/**
|
||||
* Append bson data to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the bson data.
|
||||
* @param bson the bson object to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_bson( bson *b, const char *name, const bson *bson );
|
||||
|
||||
/**
|
||||
* Append a BSON element to a bson from the current point of an iterator.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name_or_null the key for the BSON element, or NULL.
|
||||
* @param elem the bson_iterator.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem );
|
||||
|
||||
/**
|
||||
* Append a bson_timestamp_t value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the timestampe value.
|
||||
* @param ts the bson_timestamp_t value to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts );
|
||||
|
||||
/* these both append a bson_date */
|
||||
/**
|
||||
* Append a bson_date_t value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the date value.
|
||||
* @param millis the bson_date_t to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_date( bson *b, const char *name, bson_date_t millis );
|
||||
|
||||
/**
|
||||
* Append a time_t value to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the key for the date value.
|
||||
* @param secs the time_t to append.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_time_t( bson *b, const char *name, time_t secs );
|
||||
|
||||
/**
|
||||
* Start appending a new object to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the name of the new object.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_start_object( bson *b, const char *name );
|
||||
|
||||
/**
|
||||
* Start appending a new array to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
* @param name the name of the new array.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_start_array( bson *b, const char *name );
|
||||
|
||||
/**
|
||||
* Finish appending a new object or array to a bson.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_finish_object( bson *b );
|
||||
|
||||
/**
|
||||
* Finish appending a new object or array to a bson. This
|
||||
* is simply an alias for bson_append_finish_object.
|
||||
*
|
||||
* @param b the bson to append to.
|
||||
*
|
||||
* @return BSON_OK or BSON_ERROR.
|
||||
*/
|
||||
int bson_append_finish_array( bson *b );
|
||||
|
||||
void bson_numstr( char *str, int i );
|
||||
|
||||
void bson_incnumstr( char *str );
|
||||
|
||||
/* Error handling and stadard library function over-riding. */
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
/* bson_err_handlers shouldn't return!!! */
|
||||
typedef void( *bson_err_handler )( const char *errmsg );
|
||||
|
||||
typedef int (*bson_printf_func)( const char *, ... );
|
||||
typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
|
||||
typedef int (*bson_sprintf_func)( char *, const char *, ... );
|
||||
|
||||
extern void *( *bson_malloc_func )( size_t );
|
||||
extern void *( *bson_realloc_func )( void *, size_t );
|
||||
extern void ( *bson_free )( void * );
|
||||
|
||||
extern bson_printf_func bson_printf;
|
||||
extern bson_fprintf_func bson_fprintf;
|
||||
extern bson_sprintf_func bson_sprintf;
|
||||
|
||||
extern bson_printf_func bson_errprintf;
|
||||
|
||||
/**
|
||||
* Allocates memory and checks return value, exiting fatally if malloc() fails.
|
||||
*
|
||||
* @param size bytes to allocate.
|
||||
*
|
||||
* @return a pointer to the allocated memory.
|
||||
*
|
||||
* @sa malloc(3)
|
||||
*/
|
||||
void *bson_malloc( int size );
|
||||
|
||||
/**
|
||||
* Changes the size of allocated memory and checks return value,
|
||||
* exiting fatally if realloc() fails.
|
||||
*
|
||||
* @param ptr pointer to the space to reallocate.
|
||||
* @param size bytes to allocate.
|
||||
*
|
||||
* @return a pointer to the allocated memory.
|
||||
*
|
||||
* @sa realloc()
|
||||
*/
|
||||
void *bson_realloc( void *ptr, int size );
|
||||
|
||||
/**
|
||||
* Set a function for error handling.
|
||||
*
|
||||
* @param func a bson_err_handler function.
|
||||
*
|
||||
* @return the old error handling function, or NULL.
|
||||
*/
|
||||
bson_err_handler set_bson_err_handler( bson_err_handler func );
|
||||
|
||||
/* does nothing if ok != 0 */
|
||||
/**
|
||||
* Exit fatally.
|
||||
*
|
||||
* @param ok exits if ok is equal to 0.
|
||||
*/
|
||||
void bson_fatal( int ok );
|
||||
|
||||
/**
|
||||
* Exit fatally with an error message.
|
||||
*
|
||||
* @param ok exits if ok is equal to 0.
|
||||
* @param msg prints to stderr before exiting.
|
||||
*/
|
||||
void bson_fatal_msg( int ok, const char *msg );
|
||||
|
||||
/**
|
||||
* Invoke the error handler, but do not exit.
|
||||
*
|
||||
* @param b the buffer object.
|
||||
*/
|
||||
void bson_builder_error( bson *b );
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2009-2011 10gen, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Portions Copyright 2001 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
|
||||
#include "bson.h"
|
||||
#include "encoding.h"
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
*/
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
||||
* This must be called with the length pre-determined by the first byte.
|
||||
* The length can be set by:
|
||||
* length = trailingBytesForUTF8[*source]+1;
|
||||
* and the sequence is illegal right away if there aren't that many bytes
|
||||
* available.
|
||||
* If presented with a length > 4, this returns 0. The Unicode
|
||||
* definition of UTF-8 goes up to 4-byte sequences.
|
||||
*/
|
||||
static int isLegalUTF8( const unsigned char *source, int length ) {
|
||||
unsigned char a;
|
||||
const unsigned char *srcptr = source + length;
|
||||
switch ( length ) {
|
||||
default:
|
||||
return 0;
|
||||
/* Everything else falls through when "true"... */
|
||||
case 4:
|
||||
if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
|
||||
case 3:
|
||||
if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
|
||||
case 2:
|
||||
if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
|
||||
switch ( *source ) {
|
||||
/* no fall-through in this inner switch */
|
||||
case 0xE0:
|
||||
if ( a < 0xA0 ) return 0;
|
||||
break;
|
||||
case 0xF0:
|
||||
if ( a < 0x90 ) return 0;
|
||||
break;
|
||||
case 0xF4:
|
||||
if ( a > 0x8F ) return 0;
|
||||
break;
|
||||
default:
|
||||
if ( a < 0x80 ) return 0;
|
||||
}
|
||||
case 1:
|
||||
if ( *source >= 0x80 && *source < 0xC2 ) return 0;
|
||||
if ( *source > 0xF4 ) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bson_validate_string( bson *b, const unsigned char *string,
|
||||
const int length, const char check_utf8, const char check_dot,
|
||||
const char check_dollar ) {
|
||||
|
||||
int position = 0;
|
||||
int sequence_length = 1;
|
||||
|
||||
if( check_dollar && string[0] == '$' ) {
|
||||
b->err |= BSON_FIELD_INIT_DOLLAR;
|
||||
}
|
||||
|
||||
while ( position < length ) {
|
||||
if ( check_dot && *( string + position ) == '.' ) {
|
||||
b->err |= BSON_FIELD_HAS_DOT;
|
||||
}
|
||||
|
||||
if ( check_utf8 ) {
|
||||
sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
|
||||
if ( ( position + sequence_length ) > length ) {
|
||||
b->err |= BSON_NOT_UTF8;
|
||||
return BSON_ERROR;
|
||||
}
|
||||
if ( !isLegalUTF8( string + position, sequence_length ) ) {
|
||||
b->err |= BSON_NOT_UTF8;
|
||||
return BSON_ERROR;
|
||||
}
|
||||
}
|
||||
position += sequence_length;
|
||||
}
|
||||
|
||||
return BSON_OK;
|
||||
}
|
||||
|
||||
|
||||
int bson_check_string( bson *b, const char *string,
|
||||
const int length ) {
|
||||
|
||||
return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 );
|
||||
}
|
||||
|
||||
int bson_check_field_name( bson *b, const char *string,
|
||||
const int length ) {
|
||||
|
||||
return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 );
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2009-2011 10gen, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BSON_ENCODING_H_
|
||||
#define _BSON_ENCODING_H_
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
/**
|
||||
* Check that a field name is valid UTF8, does not start with a '$',
|
||||
* and contains no '.' characters. Set bson bit field appropriately.
|
||||
* Note that we don't need to check for '\0' because we're using
|
||||
* strlen(3), which stops at '\0'.
|
||||
*
|
||||
* @param b The bson object to which field name will be appended.
|
||||
* @param string The field name as char*.
|
||||
* @param length The length of the field name.
|
||||
*
|
||||
* @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be
|
||||
* valid UTF8. This function will also check whether the string
|
||||
* contains '.' or starts with '$', since the validity of this depends on context.
|
||||
* Set the value of b->err appropriately.
|
||||
*/
|
||||
int bson_check_field_name( bson *b, const char *string,
|
||||
const int length );
|
||||
|
||||
/**
|
||||
* Check that a string is valid UTF8. Sets the buffer bit field appropriately.
|
||||
*
|
||||
* @param b The bson object to which string will be appended.
|
||||
* @param string The string to check.
|
||||
* @param length The length of the string.
|
||||
*
|
||||
* @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR.
|
||||
* Sets b->err on error.
|
||||
*/
|
||||
bson_bool_t bson_check_string( bson *b, const char *string,
|
||||
const int length );
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
#endif
|
||||
@@ -0,0 +1,685 @@
|
||||
/* gridfs.c */
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "gridfs.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static bson *chunk_new( bson_oid_t id, int chunkNumber,
|
||||
const char *data, int len ) {
|
||||
bson *b = bson_malloc( sizeof( bson ) );
|
||||
|
||||
bson_init( b );
|
||||
bson_append_oid( b, "files_id", &id );
|
||||
bson_append_int( b, "n", chunkNumber );
|
||||
bson_append_binary( b, "data", BSON_BIN_BINARY, data, len );
|
||||
bson_finish( b );
|
||||
return b;
|
||||
}
|
||||
|
||||
static void chunk_free( bson *oChunk ) {
|
||||
bson_destroy( oChunk );
|
||||
bson_free( oChunk );
|
||||
}
|
||||
|
||||
int gridfs_init( mongo *client, const char *dbname, const char *prefix,
|
||||
gridfs *gfs ) {
|
||||
|
||||
int options;
|
||||
bson b;
|
||||
bson_bool_t success;
|
||||
|
||||
gfs->client = client;
|
||||
|
||||
/* Allocate space to own the dbname */
|
||||
gfs->dbname = ( const char * )bson_malloc( strlen( dbname )+1 );
|
||||
strcpy( ( char * )gfs->dbname, dbname );
|
||||
|
||||
/* Allocate space to own the prefix */
|
||||
if ( prefix == NULL ) prefix = "fs";
|
||||
gfs->prefix = ( const char * )bson_malloc( strlen( prefix )+1 );
|
||||
strcpy( ( char * )gfs->prefix, prefix );
|
||||
|
||||
/* Allocate space to own files_ns */
|
||||
gfs->files_ns =
|
||||
( const char * ) bson_malloc ( strlen( prefix )+strlen( dbname )+strlen( ".files" )+2 );
|
||||
strcpy( ( char * )gfs->files_ns, dbname );
|
||||
strcat( ( char * )gfs->files_ns, "." );
|
||||
strcat( ( char * )gfs->files_ns, prefix );
|
||||
strcat( ( char * )gfs->files_ns, ".files" );
|
||||
|
||||
/* Allocate space to own chunks_ns */
|
||||
gfs->chunks_ns = ( const char * ) bson_malloc( strlen( prefix ) + strlen( dbname )
|
||||
+ strlen( ".chunks" ) + 2 );
|
||||
strcpy( ( char * )gfs->chunks_ns, dbname );
|
||||
strcat( ( char * )gfs->chunks_ns, "." );
|
||||
strcat( ( char * )gfs->chunks_ns, prefix );
|
||||
strcat( ( char * )gfs->chunks_ns, ".chunks" );
|
||||
|
||||
bson_init( &b );
|
||||
bson_append_int( &b, "filename", 1 );
|
||||
bson_finish( &b );
|
||||
options = 0;
|
||||
success = ( mongo_create_index( gfs->client, gfs->files_ns, &b, options, NULL ) == MONGO_OK );
|
||||
bson_destroy( &b );
|
||||
if ( !success ) {
|
||||
bson_free( ( char * )gfs->dbname );
|
||||
bson_free( ( char * )gfs->prefix );
|
||||
bson_free( ( char * )gfs->files_ns );
|
||||
bson_free( ( char * )gfs->chunks_ns );
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
bson_init( &b );
|
||||
bson_append_int( &b, "files_id", 1 );
|
||||
bson_append_int( &b, "n", 1 );
|
||||
bson_finish( &b );
|
||||
options = MONGO_INDEX_UNIQUE;
|
||||
success = ( mongo_create_index( gfs->client, gfs->chunks_ns, &b, options, NULL ) == MONGO_OK );
|
||||
bson_destroy( &b );
|
||||
if ( !success ) {
|
||||
bson_free( ( char * )gfs->dbname );
|
||||
bson_free( ( char * )gfs->prefix );
|
||||
bson_free( ( char * )gfs->files_ns );
|
||||
bson_free( ( char * )gfs->chunks_ns );
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
void gridfs_destroy( gridfs *gfs ) {
|
||||
if ( gfs == NULL ) return;
|
||||
if ( gfs->dbname ) bson_free( ( char * )gfs->dbname );
|
||||
if ( gfs->prefix ) bson_free( ( char * )gfs->prefix );
|
||||
if ( gfs->files_ns ) bson_free( ( char * )gfs->files_ns );
|
||||
if ( gfs->chunks_ns ) bson_free( ( char * )gfs->chunks_ns );
|
||||
}
|
||||
|
||||
static int gridfs_insert_file( gridfs *gfs, const char *name,
|
||||
const bson_oid_t id, gridfs_offset length,
|
||||
const char *contenttype ) {
|
||||
bson command;
|
||||
bson ret;
|
||||
bson res;
|
||||
bson_iterator it;
|
||||
int result;
|
||||
|
||||
/* Check run md5 */
|
||||
bson_init( &command );
|
||||
bson_append_oid( &command, "filemd5", &id );
|
||||
bson_append_string( &command, "root", gfs->prefix );
|
||||
bson_finish( &command );
|
||||
assert( mongo_run_command( gfs->client, gfs->dbname, &command, &res ) == MONGO_OK );
|
||||
bson_destroy( &command );
|
||||
|
||||
/* Create and insert BSON for file metadata */
|
||||
bson_init( &ret );
|
||||
bson_append_oid( &ret, "_id", &id );
|
||||
if ( name != NULL && *name != '\0' ) {
|
||||
bson_append_string( &ret, "filename", name );
|
||||
}
|
||||
bson_append_long( &ret, "length", length );
|
||||
bson_append_int( &ret, "chunkSize", DEFAULT_CHUNK_SIZE );
|
||||
bson_append_date( &ret, "uploadDate", ( bson_date_t )1000*time( NULL ) );
|
||||
bson_find( &it, &res, "md5" );
|
||||
bson_append_string( &ret, "md5", bson_iterator_string( &it ) );
|
||||
bson_destroy( &res );
|
||||
if ( contenttype != NULL && *contenttype != '\0' ) {
|
||||
bson_append_string( &ret, "contentType", contenttype );
|
||||
}
|
||||
bson_finish( &ret );
|
||||
result = mongo_insert( gfs->client, gfs->files_ns, &ret );
|
||||
bson_destroy( &ret );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int gridfs_store_buffer( gridfs *gfs, const char *data,
|
||||
gridfs_offset length, const char *remotename,
|
||||
const char *contenttype ) {
|
||||
|
||||
char const *end = data + length;
|
||||
const char *data_ptr = data;
|
||||
bson_oid_t id;
|
||||
int chunkNumber = 0;
|
||||
int chunkLen;
|
||||
bson *oChunk;
|
||||
|
||||
/* Large files Assertion */
|
||||
assert( length <= 0xffffffff );
|
||||
|
||||
/* Generate and append an oid*/
|
||||
bson_oid_gen( &id );
|
||||
|
||||
/* Insert the file's data chunk by chunk */
|
||||
while ( data_ptr < end ) {
|
||||
chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ?
|
||||
DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr );
|
||||
oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen );
|
||||
mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
|
||||
chunk_free( oChunk );
|
||||
chunkNumber++;
|
||||
data_ptr += chunkLen;
|
||||
}
|
||||
|
||||
/* Inserts file's metadata */
|
||||
return gridfs_insert_file( gfs, remotename, id, length, contenttype );
|
||||
}
|
||||
|
||||
void gridfile_writer_init( gridfile *gfile, gridfs *gfs,
|
||||
const char *remote_name, const char *content_type ) {
|
||||
gfile->gfs = gfs;
|
||||
|
||||
bson_oid_gen( &( gfile->id ) );
|
||||
gfile->chunk_num = 0;
|
||||
gfile->length = 0;
|
||||
gfile->pending_len = 0;
|
||||
gfile->pending_data = NULL;
|
||||
|
||||
gfile->remote_name = ( char * )bson_malloc( strlen( remote_name ) + 1 );
|
||||
strcpy( ( char * )gfile->remote_name, remote_name );
|
||||
|
||||
gfile->content_type = ( char * )bson_malloc( strlen( content_type ) + 1 );
|
||||
strcpy( ( char * )gfile->content_type, content_type );
|
||||
}
|
||||
|
||||
void gridfile_write_buffer( gridfile *gfile, const char *data,
|
||||
gridfs_offset length ) {
|
||||
|
||||
int bytes_left = 0;
|
||||
int data_partial_len = 0;
|
||||
int chunks_to_write = 0;
|
||||
char *buffer;
|
||||
bson *oChunk;
|
||||
gridfs_offset to_write = length + gfile->pending_len;
|
||||
|
||||
if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */
|
||||
if( gfile->pending_data ) {
|
||||
gfile->pending_data = ( char * )bson_realloc( ( void * )gfile->pending_data, gfile->pending_len + to_write );
|
||||
memcpy( gfile->pending_data + gfile->pending_len, data, length );
|
||||
} else if ( to_write > 0 ) {
|
||||
gfile->pending_data = ( char * )bson_malloc( to_write );
|
||||
memcpy( gfile->pending_data, data, length );
|
||||
}
|
||||
gfile->pending_len += length;
|
||||
|
||||
} else { /* At least one chunk of data to write */
|
||||
|
||||
/* If there's a pending chunk to be written, we need to combine
|
||||
* the buffer provided up to DEFAULT_CHUNK_SIZE.
|
||||
*/
|
||||
if ( gfile->pending_len > 0 ) {
|
||||
chunks_to_write = to_write / DEFAULT_CHUNK_SIZE;
|
||||
bytes_left = to_write % DEFAULT_CHUNK_SIZE;
|
||||
|
||||
data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len;
|
||||
buffer = ( char * )bson_malloc( DEFAULT_CHUNK_SIZE );
|
||||
memcpy( buffer, gfile->pending_data, gfile->pending_len );
|
||||
memcpy( buffer + gfile->pending_len, data, data_partial_len );
|
||||
|
||||
oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE );
|
||||
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
|
||||
chunk_free( oChunk );
|
||||
gfile->chunk_num++;
|
||||
gfile->length += DEFAULT_CHUNK_SIZE;
|
||||
data += data_partial_len;
|
||||
|
||||
chunks_to_write--;
|
||||
|
||||
bson_free( buffer );
|
||||
}
|
||||
|
||||
while( chunks_to_write > 0 ) {
|
||||
oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE );
|
||||
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
|
||||
chunk_free( oChunk );
|
||||
gfile->chunk_num++;
|
||||
chunks_to_write--;
|
||||
gfile->length += DEFAULT_CHUNK_SIZE;
|
||||
data += DEFAULT_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
bson_free( gfile->pending_data );
|
||||
|
||||
/* If there are any leftover bytes, store them as pending data. */
|
||||
if( bytes_left == 0 )
|
||||
gfile->pending_data = NULL;
|
||||
else {
|
||||
gfile->pending_data = ( char * )bson_malloc( bytes_left );
|
||||
memcpy( gfile->pending_data, data, bytes_left );
|
||||
}
|
||||
|
||||
gfile->pending_len = bytes_left;
|
||||
}
|
||||
}
|
||||
|
||||
int gridfile_writer_done( gridfile *gfile ) {
|
||||
|
||||
/* write any remaining pending chunk data.
|
||||
* pending data will always take up less than one chunk */
|
||||
bson *oChunk;
|
||||
int response;
|
||||
if( gfile->pending_data ) {
|
||||
oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len );
|
||||
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
|
||||
chunk_free( oChunk );
|
||||
bson_free( gfile->pending_data );
|
||||
gfile->length += gfile->pending_len;
|
||||
}
|
||||
|
||||
/* insert into files collection */
|
||||
response = gridfs_insert_file( gfile->gfs, gfile->remote_name, gfile->id,
|
||||
gfile->length, gfile->content_type );
|
||||
|
||||
bson_free( gfile->remote_name );
|
||||
bson_free( gfile->content_type );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
int gridfs_store_file( gridfs *gfs, const char *filename,
|
||||
const char *remotename, const char *contenttype ) {
|
||||
|
||||
char buffer[DEFAULT_CHUNK_SIZE];
|
||||
FILE *fd;
|
||||
bson_oid_t id;
|
||||
int chunkNumber = 0;
|
||||
gridfs_offset length = 0;
|
||||
gridfs_offset chunkLen = 0;
|
||||
bson *oChunk;
|
||||
|
||||
/* Open the file and the correct stream */
|
||||
if ( strcmp( filename, "-" ) == 0 ) fd = stdin;
|
||||
else fd = fopen( filename, "rb" );
|
||||
assert( fd != NULL ); /* No such file */
|
||||
|
||||
/* Generate and append an oid*/
|
||||
bson_oid_gen( &id );
|
||||
|
||||
/* Insert the file chunk by chunk */
|
||||
chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
|
||||
do {
|
||||
oChunk = chunk_new( id, chunkNumber, buffer, chunkLen );
|
||||
mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
|
||||
chunk_free( oChunk );
|
||||
length += chunkLen;
|
||||
chunkNumber++;
|
||||
chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
|
||||
} while ( chunkLen != 0 );
|
||||
|
||||
/* Close the file stream */
|
||||
if ( fd != stdin ) fclose( fd );
|
||||
|
||||
/* Large files Assertion */
|
||||
/* assert(length <= 0xffffffff); */
|
||||
|
||||
/* Optional Remote Name */
|
||||
if ( remotename == NULL || *remotename == '\0' ) {
|
||||
remotename = filename;
|
||||
}
|
||||
|
||||
/* Inserts file's metadata */
|
||||
return gridfs_insert_file( gfs, remotename, id, length, contenttype );
|
||||
}
|
||||
|
||||
void gridfs_remove_filename( gridfs *gfs, const char *filename ) {
|
||||
bson query;
|
||||
mongo_cursor *files;
|
||||
bson file;
|
||||
bson_iterator it;
|
||||
bson_oid_t id;
|
||||
bson b;
|
||||
|
||||
bson_init( &query );
|
||||
bson_append_string( &query, "filename", filename );
|
||||
bson_finish( &query );
|
||||
files = mongo_find( gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0 );
|
||||
bson_destroy( &query );
|
||||
|
||||
/* Remove each file and it's chunks from files named filename */
|
||||
while ( mongo_cursor_next( files ) == MONGO_OK ) {
|
||||
file = files->current;
|
||||
bson_find( &it, &file, "_id" );
|
||||
id = *bson_iterator_oid( &it );
|
||||
|
||||
/* Remove the file with the specified id */
|
||||
bson_init( &b );
|
||||
bson_append_oid( &b, "_id", &id );
|
||||
bson_finish( &b );
|
||||
mongo_remove( gfs->client, gfs->files_ns, &b );
|
||||
bson_destroy( &b );
|
||||
|
||||
/* Remove all chunks from the file with the specified id */
|
||||
bson_init( &b );
|
||||
bson_append_oid( &b, "files_id", &id );
|
||||
bson_finish( &b );
|
||||
mongo_remove( gfs->client, gfs->chunks_ns, &b );
|
||||
bson_destroy( &b );
|
||||
}
|
||||
|
||||
mongo_cursor_destroy( files );
|
||||
}
|
||||
|
||||
int gridfs_find_query( gridfs *gfs, bson *query,
|
||||
gridfile *gfile ) {
|
||||
|
||||
bson uploadDate;
|
||||
bson finalQuery;
|
||||
bson out;
|
||||
int i;
|
||||
|
||||
bson_init( &uploadDate );
|
||||
bson_append_int( &uploadDate, "uploadDate", -1 );
|
||||
bson_finish( &uploadDate );
|
||||
|
||||
bson_init( &finalQuery );
|
||||
bson_append_bson( &finalQuery, "query", query );
|
||||
bson_append_bson( &finalQuery, "orderby", &uploadDate );
|
||||
bson_finish( &finalQuery );
|
||||
|
||||
i = ( mongo_find_one( gfs->client, gfs->files_ns,
|
||||
&finalQuery, NULL, &out ) == MONGO_OK );
|
||||
bson_destroy( &uploadDate );
|
||||
bson_destroy( &finalQuery );
|
||||
if ( !i )
|
||||
return MONGO_ERROR;
|
||||
else {
|
||||
gridfile_init( gfs, &out, gfile );
|
||||
bson_destroy( &out );
|
||||
return MONGO_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int gridfs_find_filename( gridfs *gfs, const char *filename,
|
||||
gridfile *gfile )
|
||||
|
||||
{
|
||||
bson query;
|
||||
int i;
|
||||
|
||||
bson_init( &query );
|
||||
bson_append_string( &query, "filename", filename );
|
||||
bson_finish( &query );
|
||||
i = gridfs_find_query( gfs, &query, gfile );
|
||||
bson_destroy( &query );
|
||||
return i;
|
||||
}
|
||||
|
||||
int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile )
|
||||
|
||||
{
|
||||
gfile->gfs = gfs;
|
||||
gfile->pos = 0;
|
||||
gfile->meta = ( bson * )bson_malloc( sizeof( bson ) );
|
||||
if ( gfile->meta == NULL ) return MONGO_ERROR;
|
||||
bson_copy( gfile->meta, meta );
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
void gridfile_destroy( gridfile *gfile )
|
||||
|
||||
{
|
||||
bson_destroy( gfile->meta );
|
||||
bson_free( gfile->meta );
|
||||
}
|
||||
|
||||
bson_bool_t gridfile_exists( gridfile *gfile ) {
|
||||
return ( bson_bool_t )( gfile != NULL || gfile->meta == NULL );
|
||||
}
|
||||
|
||||
const char *gridfile_get_filename( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, "filename" );
|
||||
return bson_iterator_string( &it );
|
||||
}
|
||||
|
||||
int gridfile_get_chunksize( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, "chunkSize" );
|
||||
return bson_iterator_int( &it );
|
||||
}
|
||||
|
||||
gridfs_offset gridfile_get_contentlength( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, "length" );
|
||||
|
||||
if( bson_iterator_type( &it ) == BSON_INT )
|
||||
return ( gridfs_offset )bson_iterator_int( &it );
|
||||
else
|
||||
return ( gridfs_offset )bson_iterator_long( &it );
|
||||
}
|
||||
|
||||
const char *gridfile_get_contenttype( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
if ( bson_find( &it, gfile->meta, "contentType" ) )
|
||||
return bson_iterator_string( &it );
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
bson_date_t gridfile_get_uploaddate( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, "uploadDate" );
|
||||
return bson_iterator_date( &it );
|
||||
}
|
||||
|
||||
const char *gridfile_get_md5( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, "md5" );
|
||||
return bson_iterator_string( &it );
|
||||
}
|
||||
|
||||
const char *gridfile_get_field( gridfile *gfile, const char *name ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, name );
|
||||
return bson_iterator_value( &it );
|
||||
}
|
||||
|
||||
bson_bool_t gridfile_get_boolean( gridfile *gfile, const char *name ) {
|
||||
bson_iterator it;
|
||||
|
||||
bson_find( &it, gfile->meta, name );
|
||||
return bson_iterator_bool( &it );
|
||||
}
|
||||
|
||||
bson gridfile_get_metadata( gridfile *gfile ) {
|
||||
bson sub;
|
||||
bson_iterator it;
|
||||
|
||||
if ( bson_find( &it, gfile->meta, "metadata" ) ) {
|
||||
bson_iterator_subobject( &it, &sub );
|
||||
return sub;
|
||||
} else {
|
||||
bson_empty( &sub );
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
|
||||
int gridfile_get_numchunks( gridfile *gfile ) {
|
||||
bson_iterator it;
|
||||
gridfs_offset length;
|
||||
gridfs_offset chunkSize;
|
||||
double numchunks;
|
||||
|
||||
bson_find( &it, gfile->meta, "length" );
|
||||
|
||||
if( bson_iterator_type( &it ) == BSON_INT )
|
||||
length = ( gridfs_offset )bson_iterator_int( &it );
|
||||
else
|
||||
length = ( gridfs_offset )bson_iterator_long( &it );
|
||||
|
||||
bson_find( &it, gfile->meta, "chunkSize" );
|
||||
chunkSize = bson_iterator_int( &it );
|
||||
numchunks = ( ( double )length/( double )chunkSize );
|
||||
return ( numchunks - ( int )numchunks > 0 )
|
||||
? ( int )( numchunks+1 )
|
||||
: ( int )( numchunks );
|
||||
}
|
||||
|
||||
bson gridfile_get_chunk( gridfile *gfile, int n ) {
|
||||
bson query;
|
||||
bson out;
|
||||
bson_iterator it;
|
||||
bson_oid_t id;
|
||||
|
||||
bson_init( &query );
|
||||
bson_find( &it, gfile->meta, "_id" );
|
||||
id = *bson_iterator_oid( &it );
|
||||
bson_append_oid( &query, "files_id", &id );
|
||||
bson_append_int( &query, "n", n );
|
||||
bson_finish( &query );
|
||||
|
||||
assert( mongo_find_one( gfile->gfs->client,
|
||||
gfile->gfs->chunks_ns,
|
||||
&query, NULL, &out ) == MONGO_OK );
|
||||
|
||||
bson_destroy( &query );
|
||||
return out;
|
||||
}
|
||||
|
||||
mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ) {
|
||||
bson_iterator it;
|
||||
bson_oid_t id;
|
||||
bson gte;
|
||||
bson query;
|
||||
bson orderby;
|
||||
bson command;
|
||||
mongo_cursor *cursor;
|
||||
|
||||
bson_find( &it, gfile->meta, "_id" );
|
||||
id = *bson_iterator_oid( &it );
|
||||
|
||||
bson_init( &query );
|
||||
bson_append_oid( &query, "files_id", &id );
|
||||
if ( size == 1 ) {
|
||||
bson_append_int( &query, "n", start );
|
||||
} else {
|
||||
bson_init( >e );
|
||||
bson_append_int( >e, "$gte", start );
|
||||
bson_finish( >e );
|
||||
bson_append_bson( &query, "n", >e );
|
||||
bson_destroy( >e );
|
||||
}
|
||||
bson_finish( &query );
|
||||
|
||||
bson_init( &orderby );
|
||||
bson_append_int( &orderby, "n", 1 );
|
||||
bson_finish( &orderby );
|
||||
|
||||
bson_init( &command );
|
||||
bson_append_bson( &command, "query", &query );
|
||||
bson_append_bson( &command, "orderby", &orderby );
|
||||
bson_finish( &command );
|
||||
|
||||
cursor = mongo_find( gfile->gfs->client, gfile->gfs->chunks_ns,
|
||||
&command, NULL, size, 0, 0 );
|
||||
|
||||
bson_destroy( &command );
|
||||
bson_destroy( &query );
|
||||
bson_destroy( &orderby );
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ) {
|
||||
int i;
|
||||
size_t len;
|
||||
bson chunk;
|
||||
bson_iterator it;
|
||||
const char *data;
|
||||
const int num = gridfile_get_numchunks( gfile );
|
||||
|
||||
for ( i=0; i<num; i++ ) {
|
||||
chunk = gridfile_get_chunk( gfile, i );
|
||||
bson_find( &it, &chunk, "data" );
|
||||
len = bson_iterator_bin_len( &it );
|
||||
data = bson_iterator_bin_data( &it );
|
||||
fwrite( data , sizeof( char ), len, stream );
|
||||
bson_destroy( &chunk );
|
||||
}
|
||||
|
||||
return gridfile_get_contentlength( gfile );
|
||||
}
|
||||
|
||||
gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf ) {
|
||||
mongo_cursor *chunks;
|
||||
bson chunk;
|
||||
|
||||
int first_chunk;
|
||||
int last_chunk;
|
||||
int total_chunks;
|
||||
gridfs_offset chunksize;
|
||||
gridfs_offset contentlength;
|
||||
gridfs_offset bytes_left;
|
||||
int i;
|
||||
bson_iterator it;
|
||||
gridfs_offset chunk_len;
|
||||
const char *chunk_data;
|
||||
|
||||
contentlength = gridfile_get_contentlength( gfile );
|
||||
chunksize = gridfile_get_chunksize( gfile );
|
||||
size = ( contentlength - gfile->pos < size )
|
||||
? contentlength - gfile->pos
|
||||
: size;
|
||||
bytes_left = size;
|
||||
|
||||
first_chunk = ( gfile->pos )/chunksize;
|
||||
last_chunk = ( gfile->pos+size-1 )/chunksize;
|
||||
total_chunks = last_chunk - first_chunk + 1;
|
||||
chunks = gridfile_get_chunks( gfile, first_chunk, total_chunks );
|
||||
|
||||
for ( i = 0; i < total_chunks; i++ ) {
|
||||
mongo_cursor_next( chunks );
|
||||
chunk = chunks->current;
|
||||
bson_find( &it, &chunk, "data" );
|
||||
chunk_len = bson_iterator_bin_len( &it );
|
||||
chunk_data = bson_iterator_bin_data( &it );
|
||||
if ( i == 0 ) {
|
||||
chunk_data += ( gfile->pos )%chunksize;
|
||||
chunk_len -= ( gfile->pos )%chunksize;
|
||||
}
|
||||
if ( bytes_left > chunk_len ) {
|
||||
memcpy( buf, chunk_data, chunk_len );
|
||||
bytes_left -= chunk_len;
|
||||
buf += chunk_len;
|
||||
} else {
|
||||
memcpy( buf, chunk_data, bytes_left );
|
||||
}
|
||||
}
|
||||
|
||||
mongo_cursor_destroy( chunks );
|
||||
gfile->pos = gfile->pos + size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ) {
|
||||
gridfs_offset length;
|
||||
|
||||
length = gridfile_get_contentlength( gfile );
|
||||
gfile->pos = length < offset ? length : offset;
|
||||
return gfile->pos;
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/** @file gridfs.h
|
||||
*
|
||||
* @brief GridFS declarations
|
||||
*
|
||||
* */
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "mongo.h"
|
||||
|
||||
#ifndef GRIDFS_INCLUDED
|
||||
#define GRIDFS_INCLUDED
|
||||
|
||||
enum {DEFAULT_CHUNK_SIZE = 256 * 1024};
|
||||
|
||||
typedef uint64_t gridfs_offset;
|
||||
|
||||
/* A GridFS represents a single collection of GridFS files in the database. */
|
||||
typedef struct {
|
||||
mongo *client; /**> The client to db-connection. */
|
||||
const char *dbname; /**> The root database name */
|
||||
const char *prefix; /**> The prefix of the GridFS's collections, default is NULL */
|
||||
const char *files_ns; /**> The namespace where the file's metadata is stored */
|
||||
const char *chunks_ns; /**. The namespace where the files's data is stored in chunks */
|
||||
} gridfs;
|
||||
|
||||
/* A GridFile is a single GridFS file. */
|
||||
typedef struct {
|
||||
gridfs *gfs; /**> The GridFS where the GridFile is located */
|
||||
bson *meta; /**> The GridFile's bson object where all its metadata is located */
|
||||
gridfs_offset pos; /**> The position is the offset in the file */
|
||||
bson_oid_t id; /**> The files_id of the gridfile */
|
||||
char *remote_name; /**> The name of the gridfile as a string */
|
||||
char *content_type; /**> The gridfile's content type */
|
||||
gridfs_offset length; /**> The length of this gridfile */
|
||||
int chunk_num; /**> The number of the current chunk being written to */
|
||||
char *pending_data; /**> A buffer storing data still to be written to chunks */
|
||||
int pending_len; /**> Length of pending_data buffer */
|
||||
} gridfile;
|
||||
|
||||
/**
|
||||
* Initializes a GridFS object
|
||||
* @param client - db connection
|
||||
* @param dbname - database name
|
||||
* @param prefix - collection prefix, default is fs if NULL or empty
|
||||
* @param gfs - the GridFS object to initialize
|
||||
*
|
||||
* @return - MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfs_init( mongo *client, const char *dbname,
|
||||
const char *prefix, gridfs *gfs );
|
||||
|
||||
/**
|
||||
* Destroys a GridFS object. Call this when finished with
|
||||
* the object..
|
||||
*
|
||||
* @param gfs a grid
|
||||
*/
|
||||
void gridfs_destroy( gridfs *gfs );
|
||||
|
||||
/**
|
||||
* Initializes a gridfile for writing incrementally with gridfs_write_buffer.
|
||||
* Once initialized, you can write any number of buffers with gridfs_write_buffer.
|
||||
* When done, you must call gridfs_writer_done to save the file metadata.
|
||||
*
|
||||
*/
|
||||
void gridfile_writer_init( gridfile *gfile, gridfs *gfs, const char *remote_name,
|
||||
const char *content_type );
|
||||
|
||||
/**
|
||||
* Write to a GridFS file incrementally. You can call this function any number
|
||||
* of times with a new buffer each time. This allows you to effectively
|
||||
* stream to a GridFS file. When finished, be sure to call gridfs_writer_done.
|
||||
*
|
||||
*/
|
||||
void gridfile_write_buffer( gridfile *gfile, const char *data,
|
||||
gridfs_offset length );
|
||||
|
||||
/**
|
||||
* Signal that writing of this gridfile is complete by
|
||||
* writing any buffered chunks along with the entry in the
|
||||
* files collection.
|
||||
*
|
||||
* @return - MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfile_writer_done( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Store a buffer as a GridFS file.
|
||||
* @param gfs - the working GridFS
|
||||
* @param data - pointer to buffer to store in GridFS
|
||||
* @param length - length of the buffer
|
||||
* @param remotename - filename for use in the database
|
||||
* @param contenttype - optional MIME type for this object
|
||||
*
|
||||
* @return - MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfs_store_buffer( gridfs *gfs, const char *data, gridfs_offset length,
|
||||
const char *remotename,
|
||||
const char *contenttype );
|
||||
|
||||
/**
|
||||
* Open the file referenced by filename and store it as a GridFS file.
|
||||
* @param gfs - the working GridFS
|
||||
* @param filename - local filename relative to the process
|
||||
* @param remotename - optional filename for use in the database
|
||||
* @param contenttype - optional MIME type for this object
|
||||
*
|
||||
* @return - MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfs_store_file( gridfs *gfs, const char *filename,
|
||||
const char *remotename, const char *contenttype );
|
||||
|
||||
/**
|
||||
* Removes the files referenced by filename from the db
|
||||
* @param gfs - the working GridFS
|
||||
* @param filename - the filename of the file/s to be removed
|
||||
*/
|
||||
void gridfs_remove_filename( gridfs *gfs, const char *filename );
|
||||
|
||||
/**
|
||||
* Find the first file matching the provided query within the
|
||||
* GridFS files collection, and return the file as a GridFile.
|
||||
*
|
||||
* @param gfs - the working GridFS
|
||||
* @param query - a pointer to the bson with the query data
|
||||
* @param gfile - the output GridFile to be initialized
|
||||
*
|
||||
* @return MONGO_OK if successful, MONGO_ERROR otherwise
|
||||
*/
|
||||
int gridfs_find_query( gridfs *gfs, bson *query, gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Find the first file referenced by filename within the GridFS
|
||||
* and return it as a GridFile
|
||||
* @param gfs - the working GridFS
|
||||
* @param filename - filename of the file to find
|
||||
* @param gfile - the output GridFile to be intialized
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfs_find_filename( gridfs *gfs, const char *filename, gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Initializes a GridFile containing the GridFS and file bson
|
||||
* @param gfs - the GridFS where the GridFile is located
|
||||
* @param meta - the file object
|
||||
* @param gfile - the output GridFile that is being initialized
|
||||
*
|
||||
* @return - MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Destroys the GridFile
|
||||
*
|
||||
* @param oGridFIle - the GridFile being destroyed
|
||||
*/
|
||||
void gridfile_destroy( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns whether or not the GridFile exists
|
||||
* @param gfile - the GridFile being examined
|
||||
*/
|
||||
bson_bool_t gridfile_exists( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the filename of GridFile
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the filename of the Gridfile
|
||||
*/
|
||||
const char *gridfile_get_filename( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the size of the chunks of the GridFile
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the size of the chunks of the Gridfile
|
||||
*/
|
||||
int gridfile_get_chunksize( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the length of GridFile's data
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the length of the Gridfile's data
|
||||
*/
|
||||
gridfs_offset gridfile_get_contentlength( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the MIME type of the GridFile
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the MIME type of the Gridfile
|
||||
* (NULL if no type specified)
|
||||
*/
|
||||
const char *gridfile_get_contenttype( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the upload date of GridFile
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the upload date of the Gridfile
|
||||
*/
|
||||
bson_date_t gridfile_get_uploaddate( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the MD5 of GridFile
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the MD5 of the Gridfile
|
||||
*/
|
||||
const char *gridfile_get_md5( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the field in GridFile specified by name
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
* @param name - the name of the field to be returned
|
||||
*
|
||||
* @return - the data of the field specified
|
||||
* (NULL if none exists)
|
||||
*/
|
||||
const char *gridfile_get_field( gridfile *gfile,
|
||||
const char *name );
|
||||
|
||||
/**
|
||||
* Returns a boolean field in GridFile specified by name
|
||||
* @param gfile - the working GridFile
|
||||
* @param name - the name of the field to be returned
|
||||
*
|
||||
* @return - the boolean of the field specified
|
||||
* (NULL if none exists)
|
||||
*/
|
||||
bson_bool_t gridfile_get_boolean( gridfile *gfile,
|
||||
const char *name );
|
||||
|
||||
/**
|
||||
* Returns the metadata of GridFile
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the metadata of the Gridfile in a bson object
|
||||
* (an empty bson is returned if none exists)
|
||||
*/
|
||||
bson gridfile_get_metadata( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns the number of chunks in the GridFile
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the number of chunks in the Gridfile
|
||||
*/
|
||||
int gridfile_get_numchunks( gridfile *gfile );
|
||||
|
||||
/**
|
||||
* Returns chunk n of GridFile
|
||||
* @param gfile - the working GridFile
|
||||
*
|
||||
* @return - the nth chunk of the Gridfile
|
||||
*/
|
||||
bson gridfile_get_chunk( gridfile *gfile, int n );
|
||||
|
||||
/**
|
||||
* Returns a mongo_cursor of *size* chunks starting with chunk *start*
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
* @param start - the first chunk in the cursor
|
||||
* @param size - the number of chunks to be returned
|
||||
*
|
||||
* @return - mongo_cursor of the chunks (must be destroyed after use)
|
||||
*/
|
||||
mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size );
|
||||
|
||||
/**
|
||||
* Writes the GridFile to a stream
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
* @param stream - the file stream to write to
|
||||
*/
|
||||
gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream );
|
||||
|
||||
/**
|
||||
* Reads length bytes from the GridFile to a buffer
|
||||
* and updates the position in the file.
|
||||
* (assumes the buffer is large enough)
|
||||
* (if size is greater than EOF gridfile_read reads until EOF)
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
* @param size - the amount of bytes to be read
|
||||
* @param buf - the buffer to read to
|
||||
*
|
||||
* @return - the number of bytes read
|
||||
*/
|
||||
gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf );
|
||||
|
||||
/**
|
||||
* Updates the position in the file
|
||||
* (If the offset goes beyond the contentlength,
|
||||
* the position is updated to the end of the file.)
|
||||
*
|
||||
* @param gfile - the working GridFile
|
||||
* @param offset - the position to update to
|
||||
*
|
||||
* @return - resulting offset location
|
||||
*/
|
||||
gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset );
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <string.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef MONGO_BIG_ENDIAN
|
||||
# define BYTE_ORDER 1
|
||||
#else
|
||||
# define BYTE_ORDER -1
|
||||
#endif
|
||||
|
||||
#define T_MASK ((mongo_md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
|
||||
static void
|
||||
mongo_md5_process(mongo_md5_state_t *pms, const mongo_md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
mongo_md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
mongo_md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
mongo_md5_word_t X[16];
|
||||
#else
|
||||
/* Define storage for little-endian or both types of CPUs. */
|
||||
mongo_md5_word_t xbuf[16];
|
||||
const mongo_md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const mongo_md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const mongo_md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const mongo_md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const mongo_md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* Round 1. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + F(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, T16);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, T32);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, T48);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, T64);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
pms->abcd[2] += c;
|
||||
pms->abcd[3] += d;
|
||||
}
|
||||
|
||||
void
|
||||
mongo_md5_init(mongo_md5_state_t *pms)
|
||||
{
|
||||
pms->count[0] = pms->count[1] = 0;
|
||||
pms->abcd[0] = 0x67452301;
|
||||
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void
|
||||
mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes)
|
||||
{
|
||||
const mongo_md5_byte_t *p = data;
|
||||
int left = nbytes;
|
||||
int offset = (pms->count[0] >> 3) & 63;
|
||||
mongo_md5_word_t nbits = (mongo_md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
mongo_md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
mongo_md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16])
|
||||
{
|
||||
static const mongo_md5_byte_t pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
mongo_md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (mongo_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
mongo_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
mongo_md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (mongo_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef md5_INCLUDED
|
||||
# define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* This package supports both compile-time and run-time determination of CPU
|
||||
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
|
||||
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
|
||||
* defined as non-zero, the code will be compiled to run only on big-endian
|
||||
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
|
||||
* run on either big- or little-endian CPUs, but will run slightly less
|
||||
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
|
||||
*/
|
||||
|
||||
typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */
|
||||
typedef unsigned int mongo_md5_word_t; /* 32-bit word */
|
||||
|
||||
/* Define the state of the MD5 Algorithm. */
|
||||
typedef struct mongo_md5_state_s {
|
||||
mongo_md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
mongo_md5_word_t abcd[4]; /* digest buffer */
|
||||
mongo_md5_byte_t buf[64]; /* accumulate block */
|
||||
} mongo_md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
void mongo_md5_init(mongo_md5_state_t *pms);
|
||||
|
||||
/* Append a string to the message. */
|
||||
void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes);
|
||||
|
||||
/* Finish the message and return the digest. */
|
||||
void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,648 @@
|
||||
/**
|
||||
* @file mongo.h
|
||||
* @brief Main MongoDB Declarations
|
||||
*/
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _MONGO_H_
|
||||
#define _MONGO_H_
|
||||
|
||||
#include "bson.h"
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
#define MONGO_MAJOR 0
|
||||
#define MONGO_MINOR 4
|
||||
#define MONGO_PATCH 0
|
||||
|
||||
#define MONGO_OK 0
|
||||
#define MONGO_ERROR -1
|
||||
|
||||
#define MONGO_DEFAULT_PORT 27017
|
||||
|
||||
typedef enum mongo_error_t {
|
||||
MONGO_CONN_SUCCESS = 0, /**< Connection success! */
|
||||
MONGO_CONN_NO_SOCKET, /**< Could not create a socket. */
|
||||
MONGO_CONN_FAIL, /**< An error occured while calling connect(). */
|
||||
MONGO_CONN_ADDR_FAIL, /**< An error occured while calling getaddrinfo(). */
|
||||
MONGO_CONN_NOT_MASTER, /**< Warning: connected to a non-master node (read-only). */
|
||||
MONGO_CONN_BAD_SET_NAME, /**< Given rs name doesn't match this replica set. */
|
||||
MONGO_CONN_NO_PRIMARY, /**< Can't find primary in replica set. Connection closed. */
|
||||
|
||||
MONGO_IO_ERROR, /**< An error occurred while reading or writing on socket. */
|
||||
MONGO_READ_SIZE_ERROR, /**< The response is not the expected length. */
|
||||
MONGO_COMMAND_FAILED, /**< The command returned with 'ok' value of 0. */
|
||||
MONGO_CURSOR_EXHAUSTED, /**< The cursor has no more results. */
|
||||
MONGO_CURSOR_INVALID, /**< The cursor has timed out or is not recognized. */
|
||||
MONGO_CURSOR_PENDING, /**< Tailable cursor still alive but no data. */
|
||||
MONGO_BSON_INVALID, /**< BSON not valid for the specified op. */
|
||||
MONGO_BSON_NOT_FINISHED /**< BSON object has not been finished. */
|
||||
} mongo_error_t;
|
||||
|
||||
enum mongo_cursor_flags {
|
||||
MONGO_CURSOR_MUST_FREE = 1, /**< mongo_cursor_destroy should free cursor. */
|
||||
MONGO_CURSOR_QUERY_SENT = ( 1<<1 ) /**< Initial query has been sent. */
|
||||
};
|
||||
|
||||
enum mongo_index_opts {
|
||||
MONGO_INDEX_UNIQUE = ( 1<<0 ),
|
||||
MONGO_INDEX_DROP_DUPS = ( 1<<2 ),
|
||||
MONGO_INDEX_BACKGROUND = ( 1<<3 ),
|
||||
MONGO_INDEX_SPARSE = ( 1<<4 )
|
||||
};
|
||||
|
||||
enum mongo_update_opts {
|
||||
MONGO_UPDATE_UPSERT = 0x1,
|
||||
MONGO_UPDATE_MULTI = 0x2,
|
||||
MONGO_UPDATE_BASIC = 0x4
|
||||
};
|
||||
|
||||
enum mongo_cursor_opts {
|
||||
MONGO_TAILABLE = ( 1<<1 ), /**< Create a tailable cursor. */
|
||||
MONGO_SLAVE_OK = ( 1<<2 ), /**< Allow queries on a non-primary node. */
|
||||
MONGO_NO_CURSOR_TIMEOUT = ( 1<<4 ), /**< Disable cursor timeouts. */
|
||||
MONGO_AWAIT_DATA = ( 1<<5 ), /**< Momentarily block for more data. */
|
||||
MONGO_EXHAUST = ( 1<<6 ), /**< Stream in multiple 'more' packages. */
|
||||
MONGO_PARTIAL = ( 1<<7 ) /**< Allow reads even if a shard is down. */
|
||||
};
|
||||
|
||||
enum mongo_operations {
|
||||
MONGO_OP_MSG = 1000,
|
||||
MONGO_OP_UPDATE = 2001,
|
||||
MONGO_OP_INSERT = 2002,
|
||||
MONGO_OP_QUERY = 2004,
|
||||
MONGO_OP_GET_MORE = 2005,
|
||||
MONGO_OP_DELETE = 2006,
|
||||
MONGO_OP_KILL_CURSORS = 2007
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
int len;
|
||||
int id;
|
||||
int responseTo;
|
||||
int op;
|
||||
} mongo_header;
|
||||
|
||||
typedef struct {
|
||||
mongo_header head;
|
||||
char data;
|
||||
} mongo_message;
|
||||
|
||||
typedef struct {
|
||||
int flag; /* FIX THIS COMMENT non-zero on failure */
|
||||
int64_t cursorID;
|
||||
int start;
|
||||
int num;
|
||||
} mongo_reply_fields;
|
||||
|
||||
typedef struct {
|
||||
mongo_header head;
|
||||
mongo_reply_fields fields;
|
||||
char objs;
|
||||
} mongo_reply;
|
||||
#pragma pack()
|
||||
|
||||
typedef struct mongo_host_port {
|
||||
char host[255];
|
||||
int port;
|
||||
struct mongo_host_port *next;
|
||||
} mongo_host_port;
|
||||
|
||||
typedef struct {
|
||||
mongo_host_port *seeds; /**< List of seeds provided by the user. */
|
||||
mongo_host_port *hosts; /**< List of host/ports given by the replica set */
|
||||
char *name; /**< Name of the replica set. */
|
||||
bson_bool_t primary_connected; /**< Primary node connection status. */
|
||||
} mongo_replset;
|
||||
|
||||
typedef struct mongo {
|
||||
mongo_host_port *primary; /**< Primary connection info. */
|
||||
mongo_replset *replset; /**< replset object if connected to a replica set. */
|
||||
int sock; /**< Socket file descriptor. */
|
||||
int flags; /**< Flags on this connection object. */
|
||||
int conn_timeout_ms; /**< Connection timeout in milliseconds. */
|
||||
int op_timeout_ms; /**< Read and write timeout in milliseconds. */
|
||||
bson_bool_t connected; /**< Connection status. */
|
||||
|
||||
mongo_error_t err; /**< Most recent driver error code. */
|
||||
char *errstr; /**< String version of most recent driver error code. */
|
||||
int lasterrcode; /**< getlasterror given by the server on calls. */
|
||||
char *lasterrstr; /**< getlasterror string generated by server. */
|
||||
} mongo;
|
||||
|
||||
typedef struct {
|
||||
mongo_reply *reply; /**< reply is owned by cursor */
|
||||
mongo *conn; /**< connection is *not* owned by cursor */
|
||||
const char *ns; /**< owned by cursor */
|
||||
int flags; /**< Flags used internally by this drivers. */
|
||||
int seen; /**< Number returned so far. */
|
||||
bson current; /**< This cursor's current bson object. */
|
||||
mongo_error_t err; /**< Errors on this cursor. */
|
||||
bson *query; /**< Bitfield containing cursor options. */
|
||||
bson *fields; /**< Bitfield containing cursor options. */
|
||||
int options; /**< Bitfield containing cursor options. */
|
||||
int limit; /**< Bitfield containing cursor options. */
|
||||
int skip; /**< Bitfield containing cursor options. */
|
||||
} mongo_cursor;
|
||||
|
||||
/* Connection API */
|
||||
|
||||
/** Initialize a new mongo connection object. If not created
|
||||
* with mongo_new, you must initialize each mongo
|
||||
* object using this function.
|
||||
*
|
||||
* @note When finished, you must pass this object to
|
||||
* mongo_destroy( ).
|
||||
*
|
||||
* @param conn a mongo connection object allocated on the stack
|
||||
* or heap.
|
||||
*/
|
||||
void mongo_init( mongo *conn );
|
||||
|
||||
/**
|
||||
* Connect to a single MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param host a numerical network address or a network hostname.
|
||||
* @param port the port to connect to.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
|
||||
* mongo_conn_return_t will be set on the conn->err field.
|
||||
*/
|
||||
int mongo_connect( mongo *conn , const char *host, int port );
|
||||
|
||||
/**
|
||||
* Set up this connection object for connecting to a replica set.
|
||||
* To connect, pass the object to mongo_replset_connect().
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param name the name of the replica set to connect to.
|
||||
* */
|
||||
void mongo_replset_init( mongo *conn, const char *name );
|
||||
|
||||
/**
|
||||
* Add a seed node to the replica set connection object.
|
||||
*
|
||||
* You must specify at least one seed node before connecting to a replica set.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param host a numerical network address or a network hostname.
|
||||
* @param port the port to connect to.
|
||||
*/
|
||||
void mongo_replset_add_seed( mongo *conn, const char *host, int port );
|
||||
|
||||
/**
|
||||
* Utility function for converting a host-port string to a mongo_host_port.
|
||||
*
|
||||
* @param host_string a string containing either a host or a host and port separated
|
||||
* by a colon.
|
||||
* @param host_port the mongo_host_port object to write the result to.
|
||||
*/
|
||||
void mongo_parse_host( const char *host_string, mongo_host_port *host_port );
|
||||
|
||||
/**
|
||||
* Connect to a replica set.
|
||||
*
|
||||
* Before passing a connection object to this function, you must already have called
|
||||
* mongo_set_replset and mongo_replset_add_seed.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
|
||||
* mongo_conn_return_t will be set on the conn->err field.
|
||||
*/
|
||||
int mongo_replset_connect( mongo *conn );
|
||||
|
||||
/** Set a timeout for operations on this connection. This
|
||||
* is a platform-specific feature, and only work on *nix
|
||||
* system. You must also compile for linux to support this.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param millis timeout time in milliseconds.
|
||||
*
|
||||
* @return MONGO_OK. On error, return MONGO_ERROR and
|
||||
* set the conn->err field.
|
||||
*/
|
||||
int mongo_set_op_timeout( mongo *conn, int millis );
|
||||
|
||||
/**
|
||||
* Ensure that this connection is healthy by performing
|
||||
* a round-trip to the server.
|
||||
*
|
||||
* @param conn a mongo connection
|
||||
*
|
||||
* @return MONGO_OK if connected; otherwise, MONGO_ERROR.
|
||||
*/
|
||||
int mongo_check_connection( mongo *conn );
|
||||
|
||||
/**
|
||||
* Try reconnecting to the server using the existing connection settings.
|
||||
*
|
||||
* This function will disconnect the current socket. If you've authenticated,
|
||||
* you'll need to re-authenticate after calling this function.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR and
|
||||
* set the conn->err field.
|
||||
*/
|
||||
int mongo_reconnect( mongo *conn );
|
||||
|
||||
/**
|
||||
* Close the current connection to the server. After calling
|
||||
* this function, you may call mongo_reconnect with the same
|
||||
* connection object.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
*/
|
||||
void mongo_disconnect( mongo *conn );
|
||||
|
||||
/**
|
||||
* Close any existing connection to the server and free all allocated
|
||||
* memory associated with the conn object.
|
||||
*
|
||||
* You must always call this function when finished with the connection object.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
*/
|
||||
void mongo_destroy( mongo *conn );
|
||||
|
||||
/**
|
||||
* Insert a BSON document into a MongoDB server. This function
|
||||
* will fail if the supplied BSON struct is not UTF-8 or if
|
||||
* the keys are invalid for insert (contain '.' or start with '$').
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param data the bson data.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR. If the conn->err
|
||||
* field is MONGO_BSON_INVALID, check the err field
|
||||
* on the bson struct for the reason.
|
||||
*/
|
||||
int mongo_insert( mongo *conn, const char *ns, bson *data );
|
||||
|
||||
/**
|
||||
* Insert a batch of BSON documents into a MongoDB server. This function
|
||||
* will fail if any of the documents to be inserted is invalid.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param data the bson data.
|
||||
* @param num the number of documents in data.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR.
|
||||
*
|
||||
*/
|
||||
int mongo_insert_batch( mongo *conn , const char *ns ,
|
||||
bson **data , int num );
|
||||
|
||||
/**
|
||||
* Update a document in a MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param cond the bson update query.
|
||||
* @param op the bson update data.
|
||||
* @param flags flags for the update.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR with error stored in conn object.
|
||||
*
|
||||
*/
|
||||
int mongo_update( mongo *conn, const char *ns, const bson *cond,
|
||||
const bson *op, int flags );
|
||||
|
||||
/**
|
||||
* Remove a document from a MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param cond the bson query.
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR with error stored in conn object.
|
||||
*/
|
||||
int mongo_remove( mongo *conn, const char *ns, const bson *cond );
|
||||
|
||||
/**
|
||||
* Find documents in a MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param query the bson query.
|
||||
* @param fields a bson document of fields to be returned.
|
||||
* @param limit the maximum number of documents to retrun.
|
||||
* @param skip the number of documents to skip.
|
||||
* @param options A bitfield containing cursor options.
|
||||
*
|
||||
* @return A cursor object allocated on the heap or NULL if
|
||||
* an error has occurred. For finer-grained error checking,
|
||||
* use the cursor builder API instead.
|
||||
*/
|
||||
mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query,
|
||||
bson *fields, int limit, int skip, int options );
|
||||
|
||||
/**
|
||||
* Initalize a new cursor object.
|
||||
*
|
||||
* @param cursor
|
||||
* @param ns the namespace, represented as the the database
|
||||
* name and collection name separated by a dot. e.g., "test.users"
|
||||
*/
|
||||
void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns );
|
||||
|
||||
/**
|
||||
* Set the bson object specifying this cursor's query spec. If
|
||||
* your query is the empty bson object "{}", then you need not
|
||||
* set this value.
|
||||
*
|
||||
* @param cursor
|
||||
* @param query a bson object representing the query spec. This may
|
||||
* be either a simple query spec or a complex spec storing values for
|
||||
* $query, $orderby, $hint, and/or $explain. See
|
||||
* http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details.
|
||||
*/
|
||||
void mongo_cursor_set_query( mongo_cursor *cursor, bson *query );
|
||||
|
||||
/**
|
||||
* Set the fields to return for this cursor. If you want to return
|
||||
* all fields, you need not set this value.
|
||||
*
|
||||
* @param cursor
|
||||
* @param fields a bson object representing the fields to return.
|
||||
* See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields.
|
||||
*/
|
||||
void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields );
|
||||
|
||||
/**
|
||||
* Set the number of documents to skip.
|
||||
*
|
||||
* @param cursor
|
||||
* @param skip
|
||||
*/
|
||||
void mongo_cursor_set_skip( mongo_cursor *cursor, int skip );
|
||||
|
||||
/**
|
||||
* Set the number of documents to return.
|
||||
*
|
||||
* @param cursor
|
||||
* @param limit
|
||||
*/
|
||||
void mongo_cursor_set_limit( mongo_cursor *cursor, int limit );
|
||||
|
||||
/**
|
||||
* Set any of the available query options (e.g., MONGO_TAILABLE).
|
||||
*
|
||||
* @param cursor
|
||||
* @param options a bitfield storing query options. See
|
||||
* mongo_cursor_bitfield_t for available constants.
|
||||
*/
|
||||
void mongo_cursor_set_options( mongo_cursor *cursor, int options );
|
||||
|
||||
/**
|
||||
* Return the current BSON object data as a const char*. This is useful
|
||||
* for creating bson iterators with bson_iterator_init.
|
||||
*
|
||||
* @param cursor
|
||||
*/
|
||||
const char *mongo_cursor_data( mongo_cursor *cursor );
|
||||
|
||||
/**
|
||||
* Return the current BSON object data as a const char*. This is useful
|
||||
* for creating bson iterators with bson_iterator_init.
|
||||
*
|
||||
* @param cursor
|
||||
*/
|
||||
const bson *mongo_cursor_bson( mongo_cursor *cursor );
|
||||
|
||||
/**
|
||||
* Iterate the cursor, returning the next item. When successful,
|
||||
* the returned object will be stored in cursor->current;
|
||||
*
|
||||
* @param cursor
|
||||
*
|
||||
* @return MONGO_OK. On error, returns MONGO_ERROR and sets
|
||||
* cursor->err with a value of mongo_error_t.
|
||||
*/
|
||||
int mongo_cursor_next( mongo_cursor *cursor );
|
||||
|
||||
/**
|
||||
* Destroy a cursor object. When finished with a cursor, you
|
||||
* must pass it to this function.
|
||||
*
|
||||
* @param cursor the cursor to destroy.
|
||||
*
|
||||
* @return MONGO_OK or an error code. On error, check cursor->conn->err
|
||||
* for errors.
|
||||
*/
|
||||
int mongo_cursor_destroy( mongo_cursor *cursor );
|
||||
|
||||
/**
|
||||
* Find a single document in a MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param query the bson query.
|
||||
* @param fields a bson document of the fields to be returned.
|
||||
* @param out a bson document in which to put the query result.
|
||||
*
|
||||
*/
|
||||
/* out can be NULL if you don't care about results. useful for commands */
|
||||
bson_bool_t mongo_find_one( mongo *conn, const char *ns, bson *query,
|
||||
bson *fields, bson *out );
|
||||
|
||||
/* MongoDB Helper Functions */
|
||||
|
||||
/**
|
||||
* Count the number of documents in a collection matching a query.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the db name.
|
||||
* @param coll the collection name.
|
||||
* @param query the BSON query.
|
||||
*
|
||||
* @return the number of matching documents. If the command fails,
|
||||
* MONGO_ERROR is returned.
|
||||
*/
|
||||
int64_t mongo_count( mongo *conn, const char *db, const char *coll,
|
||||
bson *query );
|
||||
|
||||
/**
|
||||
* Create a compouned index.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param data the bson index data.
|
||||
* @param options a bitfield for setting index options. Possibilities include
|
||||
* MONGO_INDEX_UNIQUE, MONGO_INDEX_DROP_DUPS, MONGO_INDEX_BACKGROUND,
|
||||
* and MONGO_INDEX_SPARSE.
|
||||
* @param out a bson document containing errors, if any.
|
||||
*
|
||||
* @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR.
|
||||
*/
|
||||
int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out );
|
||||
|
||||
/**
|
||||
* Create an index with a single key.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param ns the namespace.
|
||||
* @param field the index key.
|
||||
* @param options index options.
|
||||
* @param out a BSON document containing errors, if any.
|
||||
*
|
||||
* @return true if the index was created.
|
||||
*/
|
||||
bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out );
|
||||
|
||||
/* ----------------------------
|
||||
COMMANDS
|
||||
------------------------------ */
|
||||
|
||||
/**
|
||||
* Run a command on a MongoDB server.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param command the BSON command to run.
|
||||
* @param out the BSON result of the command.
|
||||
*
|
||||
* @return true if the command ran without error.
|
||||
*/
|
||||
bson_bool_t mongo_run_command( mongo *conn, const char *db, bson *command, bson *out );
|
||||
|
||||
/**
|
||||
* Run a command that accepts a simple string key and integer value.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param cmd the command to run.
|
||||
* @param arg the integer argument to the command.
|
||||
* @param out the BSON result of the command.
|
||||
*
|
||||
* @return MONGO_OK or an error code.
|
||||
*
|
||||
*/
|
||||
int mongo_simple_int_command( mongo *conn, const char *db,
|
||||
const char *cmd, int arg, bson *out );
|
||||
|
||||
/**
|
||||
* Run a command that accepts a simple string key and value.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param cmd the command to run.
|
||||
* @param arg the string argument to the command.
|
||||
* @param out the BSON result of the command.
|
||||
*
|
||||
* @return true if the command ran without error.
|
||||
*
|
||||
*/
|
||||
bson_bool_t mongo_simple_str_command( mongo *conn, const char *db, const char *cmd, const char *arg, bson *out );
|
||||
|
||||
/**
|
||||
* Drop a database.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database to drop.
|
||||
*
|
||||
* @return MONGO_OK or an error code.
|
||||
*/
|
||||
int mongo_cmd_drop_db( mongo *conn, const char *db );
|
||||
|
||||
/**
|
||||
* Drop a collection.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param collection the name of the collection to drop.
|
||||
* @param out a BSON document containing the result of the command.
|
||||
*
|
||||
* @return true if the collection drop was successful.
|
||||
*/
|
||||
bson_bool_t mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out );
|
||||
|
||||
/**
|
||||
* Add a database user.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the database in which to add the user.
|
||||
* @param user the user name
|
||||
* @param pass the user password
|
||||
*
|
||||
* @return MONGO_OK or MONGO_ERROR.
|
||||
*/
|
||||
int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass );
|
||||
|
||||
/**
|
||||
* Authenticate a user.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the database to authenticate against.
|
||||
* @param user the user name to authenticate.
|
||||
* @param pass the user's password.
|
||||
*
|
||||
* @return MONGO_OK on sucess and MONGO_ERROR on failure.
|
||||
*/
|
||||
int mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass );
|
||||
|
||||
/**
|
||||
* Check if the current server is a master.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param out a BSON result of the command.
|
||||
*
|
||||
* @return true if the server is a master.
|
||||
*/
|
||||
/* return value is master status */
|
||||
bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *out );
|
||||
|
||||
/**
|
||||
* Get the error for the last command with the current connection.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param out a BSON object containing the error details.
|
||||
*
|
||||
* @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
|
||||
* of conn->lasterrcode and conn->lasterrstr for the error status.
|
||||
*/
|
||||
int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out );
|
||||
|
||||
/**
|
||||
* Get the most recent error with the current connection.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
* @param out a BSON object containing the error details.
|
||||
*
|
||||
* @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
|
||||
* of conn->lasterrcode and conn->lasterrstr for the error status.
|
||||
*/
|
||||
int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out );
|
||||
|
||||
/**
|
||||
* Reset the error state for the connection.
|
||||
*
|
||||
* @param conn a mongo object.
|
||||
* @param db the name of the database.
|
||||
*/
|
||||
void mongo_cmd_reset_error( mongo *conn, const char *db );
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/* net.c */
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Implementation for generic version of net.h */
|
||||
#include "net.h"
|
||||
#include <string.h>
|
||||
|
||||
int mongo_write_socket( mongo *conn, const void *buf, int len ) {
|
||||
const char *cbuf = buf;
|
||||
while ( len ) {
|
||||
int sent = send( conn->sock, cbuf, len, 0 );
|
||||
if ( sent == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
cbuf += sent;
|
||||
len -= sent;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
int mongo_read_socket( mongo *conn, void *buf, int len ) {
|
||||
char *cbuf = buf;
|
||||
while ( len ) {
|
||||
int sent = recv( conn->sock, cbuf, len, 0 );
|
||||
if ( sent == 0 || sent == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
cbuf += sent;
|
||||
len -= sent;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
/* This is a no-op in the generic implementation. */
|
||||
int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
static int mongo_create_socket( mongo *conn ) {
|
||||
int fd;
|
||||
|
||||
if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
|
||||
conn->err = MONGO_CONN_NO_SOCKET;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
conn->sock = fd;
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t addressSize;
|
||||
int flag = 1;
|
||||
|
||||
if( mongo_create_socket( conn ) != MONGO_OK )
|
||||
return MONGO_ERROR;
|
||||
|
||||
memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons( port );
|
||||
sa.sin_addr.s_addr = inet_addr( host );
|
||||
addressSize = sizeof( sa );
|
||||
|
||||
if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
|
||||
mongo_close_socket( conn->sock );
|
||||
conn->connected = 0;
|
||||
conn->sock = 0;
|
||||
conn->err = MONGO_CONN_FAIL;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
|
||||
if( conn->op_timeout_ms > 0 )
|
||||
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
|
||||
|
||||
conn->connected = 1;
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/** @file net.h */
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Header for generic net.h */
|
||||
#ifndef _MONGO_NET_H_
|
||||
#define _MONGO_NET_H_
|
||||
|
||||
#include "mongo.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsock.h>
|
||||
#define mongo_close_socket(sock) ( closesocket(sock) )
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#define mongo_close_socket(sock) ( close(sock) )
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
|
||||
#define _MONGO_USE_GETADDRINFO
|
||||
#endif
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
/* This is a no-op in the generic implementation. */
|
||||
int mongo_set_socket_op_timeout( mongo *conn, int millis );
|
||||
int mongo_read_socket( mongo *conn, void *buf, int len );
|
||||
int mongo_write_socket( mongo *conn, const void *buf, int len );
|
||||
int mongo_socket_connect( mongo *conn, const char *host, int port );
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* all the numbers that fit in a 4 byte string */
|
||||
const char bson_numstrs[1000][4] = {
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
|
||||
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
|
||||
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
|
||||
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
|
||||
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
|
||||
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
|
||||
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
|
||||
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
|
||||
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
|
||||
|
||||
"100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
|
||||
"110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
|
||||
"120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
|
||||
"130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
|
||||
"140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
|
||||
"150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
|
||||
"160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
|
||||
"170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
|
||||
"180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
|
||||
"190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
|
||||
|
||||
"200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
|
||||
"210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
|
||||
"220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
|
||||
"230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
|
||||
"240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
|
||||
"250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
|
||||
"260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
|
||||
"270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
|
||||
"280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
|
||||
"290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
|
||||
|
||||
"300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
|
||||
"310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
|
||||
"320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
|
||||
"330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
|
||||
"340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
|
||||
"350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
|
||||
"360", "361", "362", "363", "364", "365", "366", "367", "368", "369",
|
||||
"370", "371", "372", "373", "374", "375", "376", "377", "378", "379",
|
||||
"380", "381", "382", "383", "384", "385", "386", "387", "388", "389",
|
||||
"390", "391", "392", "393", "394", "395", "396", "397", "398", "399",
|
||||
|
||||
"400", "401", "402", "403", "404", "405", "406", "407", "408", "409",
|
||||
"410", "411", "412", "413", "414", "415", "416", "417", "418", "419",
|
||||
"420", "421", "422", "423", "424", "425", "426", "427", "428", "429",
|
||||
"430", "431", "432", "433", "434", "435", "436", "437", "438", "439",
|
||||
"440", "441", "442", "443", "444", "445", "446", "447", "448", "449",
|
||||
"450", "451", "452", "453", "454", "455", "456", "457", "458", "459",
|
||||
"460", "461", "462", "463", "464", "465", "466", "467", "468", "469",
|
||||
"470", "471", "472", "473", "474", "475", "476", "477", "478", "479",
|
||||
"480", "481", "482", "483", "484", "485", "486", "487", "488", "489",
|
||||
"490", "491", "492", "493", "494", "495", "496", "497", "498", "499",
|
||||
|
||||
"500", "501", "502", "503", "504", "505", "506", "507", "508", "509",
|
||||
"510", "511", "512", "513", "514", "515", "516", "517", "518", "519",
|
||||
"520", "521", "522", "523", "524", "525", "526", "527", "528", "529",
|
||||
"530", "531", "532", "533", "534", "535", "536", "537", "538", "539",
|
||||
"540", "541", "542", "543", "544", "545", "546", "547", "548", "549",
|
||||
"550", "551", "552", "553", "554", "555", "556", "557", "558", "559",
|
||||
"560", "561", "562", "563", "564", "565", "566", "567", "568", "569",
|
||||
"570", "571", "572", "573", "574", "575", "576", "577", "578", "579",
|
||||
"580", "581", "582", "583", "584", "585", "586", "587", "588", "589",
|
||||
"590", "591", "592", "593", "594", "595", "596", "597", "598", "599",
|
||||
|
||||
"600", "601", "602", "603", "604", "605", "606", "607", "608", "609",
|
||||
"610", "611", "612", "613", "614", "615", "616", "617", "618", "619",
|
||||
"620", "621", "622", "623", "624", "625", "626", "627", "628", "629",
|
||||
"630", "631", "632", "633", "634", "635", "636", "637", "638", "639",
|
||||
"640", "641", "642", "643", "644", "645", "646", "647", "648", "649",
|
||||
"650", "651", "652", "653", "654", "655", "656", "657", "658", "659",
|
||||
"660", "661", "662", "663", "664", "665", "666", "667", "668", "669",
|
||||
"670", "671", "672", "673", "674", "675", "676", "677", "678", "679",
|
||||
"680", "681", "682", "683", "684", "685", "686", "687", "688", "689",
|
||||
"690", "691", "692", "693", "694", "695", "696", "697", "698", "699",
|
||||
|
||||
"700", "701", "702", "703", "704", "705", "706", "707", "708", "709",
|
||||
"710", "711", "712", "713", "714", "715", "716", "717", "718", "719",
|
||||
"720", "721", "722", "723", "724", "725", "726", "727", "728", "729",
|
||||
"730", "731", "732", "733", "734", "735", "736", "737", "738", "739",
|
||||
"740", "741", "742", "743", "744", "745", "746", "747", "748", "749",
|
||||
"750", "751", "752", "753", "754", "755", "756", "757", "758", "759",
|
||||
"760", "761", "762", "763", "764", "765", "766", "767", "768", "769",
|
||||
"770", "771", "772", "773", "774", "775", "776", "777", "778", "779",
|
||||
"780", "781", "782", "783", "784", "785", "786", "787", "788", "789",
|
||||
"790", "791", "792", "793", "794", "795", "796", "797", "798", "799",
|
||||
|
||||
"800", "801", "802", "803", "804", "805", "806", "807", "808", "809",
|
||||
"810", "811", "812", "813", "814", "815", "816", "817", "818", "819",
|
||||
"820", "821", "822", "823", "824", "825", "826", "827", "828", "829",
|
||||
"830", "831", "832", "833", "834", "835", "836", "837", "838", "839",
|
||||
"840", "841", "842", "843", "844", "845", "846", "847", "848", "849",
|
||||
"850", "851", "852", "853", "854", "855", "856", "857", "858", "859",
|
||||
"860", "861", "862", "863", "864", "865", "866", "867", "868", "869",
|
||||
"870", "871", "872", "873", "874", "875", "876", "877", "878", "879",
|
||||
"880", "881", "882", "883", "884", "885", "886", "887", "888", "889",
|
||||
"890", "891", "892", "893", "894", "895", "896", "897", "898", "899",
|
||||
|
||||
"900", "901", "902", "903", "904", "905", "906", "907", "908", "909",
|
||||
"910", "911", "912", "913", "914", "915", "916", "917", "918", "919",
|
||||
"920", "921", "922", "923", "924", "925", "926", "927", "928", "929",
|
||||
"930", "931", "932", "933", "934", "935", "936", "937", "938", "939",
|
||||
"940", "941", "942", "943", "944", "945", "946", "947", "948", "949",
|
||||
"950", "951", "952", "953", "954", "955", "956", "957", "958", "959",
|
||||
"960", "961", "962", "963", "964", "965", "966", "967", "968", "969",
|
||||
"970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
|
||||
"980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
|
||||
"990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
/** @file platform.h */
|
||||
|
||||
/** Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/* all platform-specific ifdefs should go here */
|
||||
|
||||
#ifndef _PLATFORM_HACKS_H_
|
||||
#define _PLATFORM_HACKS_H_
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define MONGO_INLINE static __inline__
|
||||
#else
|
||||
#define MONGO_INLINE static
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MONGO_EXTERN_C_START extern "C" {
|
||||
#define MONGO_EXTERN_C_END }
|
||||
#else
|
||||
#define MONGO_EXTERN_C_START
|
||||
#define MONGO_EXTERN_C_END
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
#elif defined(MONGO_HAVE_UNISTD)
|
||||
#include <unistd.h>
|
||||
#elif defined(MONGO_USE__INT64)
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#elif defined(MONGO_USE_LONG_LONG_INT)
|
||||
typedef long long int int64_t;
|
||||
typedef unsigned long long int uint64_t;
|
||||
#else
|
||||
#error must have a 64bit int type
|
||||
#endif
|
||||
|
||||
/* big endian is only used for OID generation. little is used everywhere else */
|
||||
#ifdef MONGO_BIG_ENDIAN
|
||||
#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) )
|
||||
#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) )
|
||||
#define bson_big_endian64(out, in) ( memcpy(out, in, 8) )
|
||||
#define bson_big_endian32(out, in) ( memcpy(out, in, 4) )
|
||||
#else
|
||||
#define bson_little_endian64(out, in) ( memcpy(out, in, 8) )
|
||||
#define bson_little_endian32(out, in) ( memcpy(out, in, 4) )
|
||||
#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) )
|
||||
#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
|
||||
#endif
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
MONGO_INLINE void bson_swap_endian64( void *outp, const void *inp ) {
|
||||
const char *in = ( const char * )inp;
|
||||
char *out = ( char * )outp;
|
||||
|
||||
out[0] = in[7];
|
||||
out[1] = in[6];
|
||||
out[2] = in[5];
|
||||
out[3] = in[4];
|
||||
out[4] = in[3];
|
||||
out[5] = in[2];
|
||||
out[6] = in[1];
|
||||
out[7] = in[0];
|
||||
|
||||
}
|
||||
MONGO_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
|
||||
const char *in = ( const char * )inp;
|
||||
char *out = ( char * )outp;
|
||||
|
||||
out[0] = in[3];
|
||||
out[1] = in[2];
|
||||
out[2] = in[1];
|
||||
out[3] = in[0];
|
||||
}
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
/* net.c */
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Implementation for Linux version of net.h */
|
||||
#include "net.h"
|
||||
#include <string.h>
|
||||
|
||||
int mongo_write_socket( mongo *conn, const void *buf, int len ) {
|
||||
const char *cbuf = buf;
|
||||
while ( len ) {
|
||||
int sent = send( conn->sock, cbuf, len, 0 );
|
||||
if ( sent == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
cbuf += sent;
|
||||
len -= sent;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
int mongo_read_socket( mongo *conn, void *buf, int len ) {
|
||||
char *cbuf = buf;
|
||||
while ( len ) {
|
||||
int sent = recv( conn->sock, cbuf, len, 0 );
|
||||
if ( sent == 0 || sent == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
cbuf += sent;
|
||||
len -= sent;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
static int mongo_create_socket( mongo *conn ) {
|
||||
int fd;
|
||||
|
||||
if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
|
||||
conn->err = MONGO_CONN_NO_SOCKET;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
conn->sock = fd;
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
static int mongo_set_blocking_status( mongo *conn ) {
|
||||
int flags;
|
||||
int blocking;
|
||||
|
||||
blocking = ( conn->conn_timeout_ms == 0 );
|
||||
if( blocking )
|
||||
return MONGO_OK;
|
||||
else {
|
||||
if( ( flags = fcntl( conn->sock, F_GETFL ) ) == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
mongo_close_socket( conn->sock );
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if( ( flags = fcntl( conn->sock, F_SETFL, flags ) ) == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
mongo_close_socket( conn->sock );
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = millis / 1000;
|
||||
tv.tv_usec = ( millis % 1000 ) * 1000;
|
||||
|
||||
if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) ) == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) ) == -1 ) {
|
||||
conn->err = MONGO_IO_ERROR;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
|
||||
#ifdef _MONGO_USE_GETADDRINFO
|
||||
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
|
||||
|
||||
struct addrinfo *addrs = NULL;
|
||||
struct addrinfo hints;
|
||||
int flag = 1;
|
||||
char port_str[12];
|
||||
int ret;
|
||||
|
||||
conn->sock = 0;
|
||||
conn->connected = 0;
|
||||
|
||||
memset( &hints, 0, sizeof( hints ) );
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
sprintf( port_str, "%d", port );
|
||||
|
||||
if( mongo_create_socket( conn ) != MONGO_OK )
|
||||
return MONGO_ERROR;
|
||||
|
||||
if( getaddrinfo( host, port_str, &hints, &addrs ) != 0 ) {
|
||||
bson_errprintf( "getaddrinfo failed: %s", gai_strerror( ret ) );
|
||||
conn->err = MONGO_CONN_ADDR_FAIL;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
if ( connect( conn->sock, addrs->ai_addr, addrs->ai_addrlen ) == -1 ) {
|
||||
mongo_close_socket( conn->sock );
|
||||
freeaddrinfo( addrs );
|
||||
conn->err = MONGO_CONN_FAIL;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * )&flag, sizeof( flag ) );
|
||||
if( conn->op_timeout_ms > 0 )
|
||||
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
|
||||
|
||||
conn->connected = 1;
|
||||
freeaddrinfo( addrs );
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
#else
|
||||
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t addressSize;
|
||||
int flag = 1;
|
||||
|
||||
if( mongo_create_socket( conn ) != MONGO_OK )
|
||||
return MONGO_ERROR;
|
||||
|
||||
memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons( port );
|
||||
sa.sin_addr.s_addr = inet_addr( host );
|
||||
addressSize = sizeof( sa );
|
||||
|
||||
if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
|
||||
mongo_close_socket( conn->sock );
|
||||
conn->connected = 0;
|
||||
conn->sock = 0;
|
||||
conn->err = MONGO_CONN_FAIL;
|
||||
return MONGO_ERROR;
|
||||
}
|
||||
|
||||
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
|
||||
|
||||
if( conn->op_timeout_ms > 0 )
|
||||
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
|
||||
|
||||
conn->connected = 1;
|
||||
|
||||
return MONGO_OK;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file net.h
|
||||
* @brief Networking.
|
||||
*/
|
||||
|
||||
/* Copyright 2009-2011 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Header for Linux net.h */
|
||||
#ifndef _MONGO_NET_H_
|
||||
#define _MONGO_NET_H_
|
||||
|
||||
#include "mongo.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#define mongo_close_socket(sock) ( close(sock) )
|
||||
|
||||
#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
|
||||
#define _MONGO_USE_GETADDRINFO
|
||||
#endif
|
||||
|
||||
MONGO_EXTERN_C_START
|
||||
|
||||
int mongo_set_socket_op_timeout( mongo *conn, int millis );
|
||||
int mongo_read_socket( mongo *conn, void *buf, int len );
|
||||
int mongo_write_socket( mongo *conn, const void *buf, int len );
|
||||
int mongo_socket_connect( mongo *conn, const char *host, int port );
|
||||
|
||||
MONGO_EXTERN_C_END
|
||||
#endif
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Daniel Swarbrick <daniel.swarbrick@seventhsignal.de>
|
||||
*
|
||||
* mod_cdr_mongodb.c -- MongoDB CDR Module
|
||||
*
|
||||
* Derived from:
|
||||
* mod_xml_cdr.c -- XML CDR Module to files or curl
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
#include <mongo.h>
|
||||
|
||||
static struct {
|
||||
switch_memory_pool_t *pool;
|
||||
int shutdown;
|
||||
char *mongo_host;
|
||||
uint32_t mongo_port;
|
||||
char *mongo_namespace;
|
||||
mongo mongo_conn[1];
|
||||
switch_mutex_t *mongo_mutex;
|
||||
switch_bool_t log_b;
|
||||
} globals;
|
||||
|
||||
static switch_xml_config_item_t config_settings[] = {
|
||||
/* key, flags, ptr, default_value, syntax, helptext */
|
||||
SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_REQUIRED, &globals.mongo_host, "127.0.0.1", NULL, "MongoDB server host address"),
|
||||
SWITCH_CONFIG_ITEM_STRING_STRDUP("namespace", CONFIG_REQUIRED, &globals.mongo_namespace, NULL, "database.collection", "MongoDB namespace"),
|
||||
|
||||
/* key, type, flags, ptr, default_value, data, syntax, helptext */
|
||||
SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_REQUIRED, &globals.mongo_port, 27017, NULL, NULL, "MongoDB server TCP port"),
|
||||
SWITCH_CONFIG_ITEM("log-b-leg", SWITCH_CONFIG_BOOL, CONFIG_RELOADABLE, &globals.log_b, SWITCH_TRUE, NULL, NULL, "Log B-leg in addition to A-leg"),
|
||||
|
||||
SWITCH_CONFIG_ITEM_END()
|
||||
};
|
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load);
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown);
|
||||
SWITCH_MODULE_DEFINITION(mod_cdr_mongodb, mod_cdr_mongodb_load, mod_cdr_mongodb_shutdown, NULL);
|
||||
|
||||
static void set_bson_profile_data(bson *b, switch_caller_profile_t *caller_profile)
|
||||
{
|
||||
bson_append_string(b, "username", caller_profile->username);
|
||||
bson_append_string(b, "dialplan", caller_profile->dialplan);
|
||||
bson_append_string(b, "caller_id_name", caller_profile->caller_id_name);
|
||||
bson_append_string(b, "ani", caller_profile->ani);
|
||||
bson_append_string(b, "aniii", caller_profile->aniii);
|
||||
bson_append_string(b, "caller_id_number", caller_profile->caller_id_number);
|
||||
bson_append_string(b, "network_addr", caller_profile->network_addr);
|
||||
bson_append_string(b, "rdnis", caller_profile->rdnis);
|
||||
bson_append_string(b, "destination_number", caller_profile->destination_number);
|
||||
bson_append_string(b, "uuid", caller_profile->uuid);
|
||||
bson_append_string(b, "source", caller_profile->source);
|
||||
bson_append_string(b, "context", caller_profile->context);
|
||||
bson_append_string(b, "chan_name", caller_profile->chan_name);
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t my_on_reporting(switch_core_session_t *session)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_event_header_t *hi;
|
||||
switch_caller_profile_t *caller_profile;
|
||||
switch_app_log_t *app_log;
|
||||
bson cdr;
|
||||
int is_b;
|
||||
char *tmp;
|
||||
|
||||
if (globals.shutdown) {
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
is_b = channel && switch_channel_get_originator_caller_profile(channel);
|
||||
if (!globals.log_b && is_b) {
|
||||
const char *force_cdr = switch_channel_get_variable(channel, SWITCH_FORCE_PROCESS_CDR_VARIABLE);
|
||||
if (!switch_true(force_cdr)) {
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
bson_init(&cdr);
|
||||
|
||||
/* Channel data */
|
||||
bson_append_start_object(&cdr, "channel_data");
|
||||
bson_append_string(&cdr, "state", switch_channel_state_name(switch_channel_get_state(channel)));
|
||||
bson_append_string(&cdr, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
|
||||
bson_append_int(&cdr, "state_number", switch_channel_get_state(channel));
|
||||
|
||||
if ((tmp = switch_channel_get_flag_string(channel))) {
|
||||
bson_append_string(&cdr, "flags", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if ((tmp = switch_channel_get_cap_string(channel))) {
|
||||
bson_append_string(&cdr, "caps", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
bson_append_finish_object(&cdr); /* channel_data */
|
||||
|
||||
|
||||
/* Channel variables */
|
||||
bson_append_start_object(&cdr, "variables");
|
||||
for (hi = switch_channel_variable_first(channel); hi; hi = hi->next) {
|
||||
if (!zstr(hi->name) && !zstr(hi->value)) {
|
||||
bson_append_string(&cdr, hi->name, hi->value);
|
||||
}
|
||||
}
|
||||
bson_append_finish_object(&cdr); /* variables */
|
||||
|
||||
|
||||
/* App log */
|
||||
if ((app_log = switch_core_session_get_app_log(session))) {
|
||||
switch_app_log_t *ap;
|
||||
|
||||
bson_append_start_object(&cdr, "app_log");
|
||||
for (ap = app_log; ap; ap = ap->next) {
|
||||
bson_append_start_object(&cdr, "application");
|
||||
bson_append_string(&cdr, "app_name", ap->app);
|
||||
bson_append_string(&cdr, "app_data", ap->arg);
|
||||
bson_append_long(&cdr, "app_stamp", ap->stamp);
|
||||
bson_append_finish_object(&cdr); /* application */
|
||||
}
|
||||
|
||||
bson_append_finish_object(&cdr); /* app_log */
|
||||
}
|
||||
|
||||
|
||||
/* Callflow */
|
||||
caller_profile = switch_channel_get_caller_profile(channel);
|
||||
|
||||
while (caller_profile) {
|
||||
bson_append_start_object(&cdr, "callflow");
|
||||
|
||||
if (!zstr(caller_profile->dialplan)) {
|
||||
bson_append_string(&cdr, "dialplan", caller_profile->dialplan);
|
||||
}
|
||||
|
||||
if (!zstr(caller_profile->profile_index)) {
|
||||
bson_append_string(&cdr, "profile_index", caller_profile->profile_index);
|
||||
}
|
||||
|
||||
if (caller_profile->caller_extension) {
|
||||
switch_caller_application_t *ap;
|
||||
|
||||
bson_append_start_object(&cdr, "extension");
|
||||
|
||||
bson_append_string(&cdr, "name", caller_profile->caller_extension->extension_name);
|
||||
bson_append_string(&cdr, "number", caller_profile->caller_extension->extension_number);
|
||||
|
||||
if (caller_profile->caller_extension->current_application) {
|
||||
bson_append_string(&cdr, "current_app", caller_profile->caller_extension->current_application->application_name);
|
||||
}
|
||||
|
||||
for (ap = caller_profile->caller_extension->applications; ap; ap = ap->next) {
|
||||
bson_append_start_object(&cdr, "application");
|
||||
if (ap == caller_profile->caller_extension->current_application) {
|
||||
bson_append_bool(&cdr, "last_executed", 1);
|
||||
}
|
||||
bson_append_string(&cdr, "app_name", ap->application_name);
|
||||
bson_append_string(&cdr, "app_data", ap->application_data);
|
||||
bson_append_finish_object(&cdr);
|
||||
}
|
||||
|
||||
if (caller_profile->caller_extension->children) {
|
||||
switch_caller_profile_t *cp = NULL;
|
||||
|
||||
for (cp = caller_profile->caller_extension->children; cp; cp = cp->next) {
|
||||
|
||||
if (!cp->caller_extension) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bson_append_start_object(&cdr, "sub_extensions");
|
||||
bson_append_start_object(&cdr, "extension");
|
||||
|
||||
bson_append_string(&cdr, "name", cp->caller_extension->extension_name);
|
||||
bson_append_string(&cdr, "number", cp->caller_extension->extension_number);
|
||||
bson_append_string(&cdr, "dialplan", cp->dialplan);
|
||||
if (cp->caller_extension->current_application) {
|
||||
bson_append_string(&cdr, "current_app", cp->caller_extension->current_application->application_name);
|
||||
}
|
||||
|
||||
for (ap = cp->caller_extension->applications; ap; ap = ap->next) {
|
||||
bson_append_start_object(&cdr, "application");
|
||||
if (ap == cp->caller_extension->current_application) {
|
||||
bson_append_bool(&cdr, "last_executed", 1);
|
||||
}
|
||||
bson_append_string(&cdr, "app_name", ap->application_name);
|
||||
bson_append_string(&cdr, "app_data", ap->application_data);
|
||||
bson_append_finish_object(&cdr);
|
||||
}
|
||||
|
||||
bson_append_finish_object(&cdr); /* extension */
|
||||
bson_append_finish_object(&cdr); /* sub_extensions */
|
||||
}
|
||||
}
|
||||
|
||||
bson_append_finish_object(&cdr); /* extension */
|
||||
}
|
||||
|
||||
bson_append_start_object(&cdr, "caller_profile");
|
||||
set_bson_profile_data(&cdr, caller_profile);
|
||||
|
||||
if (caller_profile->origination_caller_profile) {
|
||||
switch_caller_profile_t *cp = NULL;
|
||||
|
||||
bson_append_start_object(&cdr, "origination");
|
||||
for (cp = caller_profile->origination_caller_profile; cp; cp = cp->next) {
|
||||
bson_append_start_object(&cdr, "origination_caller_profile");
|
||||
set_bson_profile_data(&cdr, cp);
|
||||
bson_append_finish_object(&cdr);
|
||||
}
|
||||
bson_append_finish_object(&cdr); /* origination */
|
||||
}
|
||||
|
||||
if (caller_profile->originator_caller_profile) {
|
||||
switch_caller_profile_t *cp = NULL;
|
||||
|
||||
bson_append_start_object(&cdr, "originator");
|
||||
for (cp = caller_profile->originator_caller_profile; cp; cp = cp->next) {
|
||||
bson_append_start_object(&cdr, "originator_caller_profile");
|
||||
set_bson_profile_data(&cdr, cp);
|
||||
bson_append_finish_object(&cdr);
|
||||
}
|
||||
bson_append_finish_object(&cdr); /* originator */
|
||||
}
|
||||
|
||||
if (caller_profile->originatee_caller_profile) {
|
||||
switch_caller_profile_t *cp = NULL;
|
||||
|
||||
bson_append_start_object(&cdr, "originatee");
|
||||
for (cp = caller_profile->originatee_caller_profile; cp; cp = cp->next) {
|
||||
bson_append_start_object(&cdr, "originatee_caller_profile");
|
||||
set_bson_profile_data(&cdr, cp);
|
||||
bson_append_finish_object(&cdr);
|
||||
}
|
||||
bson_append_finish_object(&cdr); /* originatee */
|
||||
}
|
||||
|
||||
bson_append_finish_object(&cdr); /* caller_profile */
|
||||
|
||||
/* Timestamps */
|
||||
if (caller_profile->times) {
|
||||
bson_append_start_object(&cdr, "times");
|
||||
|
||||
/* Insert timestamps as long ints (microseconds) to preserve accuracy */
|
||||
bson_append_long(&cdr, "created_time", caller_profile->times->created);
|
||||
bson_append_long(&cdr, "profile_created_time", caller_profile->times->profile_created);
|
||||
bson_append_long(&cdr, "progress_time", caller_profile->times->progress);
|
||||
bson_append_long(&cdr, "progress_media_time", caller_profile->times->progress_media);
|
||||
bson_append_long(&cdr, "answered_time", caller_profile->times->answered);
|
||||
bson_append_long(&cdr, "bridged_time", caller_profile->times->bridged);
|
||||
bson_append_long(&cdr, "last_hold_time", caller_profile->times->last_hold);
|
||||
bson_append_long(&cdr, "hold_accum_time", caller_profile->times->hold_accum);
|
||||
bson_append_long(&cdr, "hangup_time", caller_profile->times->hungup);
|
||||
bson_append_long(&cdr, "resurrect_time", caller_profile->times->resurrected);
|
||||
bson_append_long(&cdr, "transfer_time", caller_profile->times->transferred);
|
||||
bson_append_finish_object(&cdr); /* times */
|
||||
}
|
||||
|
||||
bson_append_finish_object(&cdr); /* callflow */
|
||||
caller_profile = caller_profile->next;
|
||||
}
|
||||
|
||||
bson_finish(&cdr);
|
||||
|
||||
switch_mutex_lock(globals.mongo_mutex);
|
||||
|
||||
if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
|
||||
if (globals.mongo_conn->err == MONGO_IO_ERROR) {
|
||||
mongo_error_t db_status;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n");
|
||||
db_status = mongo_reconnect(globals.mongo_conn);
|
||||
|
||||
if (db_status != MONGO_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MongoDB reconnect failed with error code %d\n", db_status);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n");
|
||||
if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_unlock(globals.mongo_mutex);
|
||||
bson_destroy(&cdr);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static switch_state_handler_table_t state_handlers = {
|
||||
/*.on_init */ NULL,
|
||||
/*.on_routing */ NULL,
|
||||
/*.on_execute */ NULL,
|
||||
/*.on_hangup */ NULL,
|
||||
/*.on_exchange_media */ NULL,
|
||||
/*.on_soft_execute */ NULL,
|
||||
/*.on_consume_media */ NULL,
|
||||
/*.on_hibernate */ NULL,
|
||||
/*.on_reset */ NULL,
|
||||
/*.on_park */ NULL,
|
||||
/*.on_reporting */ my_on_reporting
|
||||
};
|
||||
|
||||
|
||||
static switch_status_t load_config(switch_memory_pool_t *pool)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
if (switch_xml_config_parse_module_settings("cdr_mongodb.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
mongo_error_t db_status;
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
globals.pool = pool;
|
||||
if (load_config(pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to load or parse config!\n");
|
||||
}
|
||||
|
||||
db_status = mongo_connect(globals.mongo_conn, globals.mongo_host, globals.mongo_port);
|
||||
|
||||
if (db_status != MONGO_OK) {
|
||||
switch (globals.mongo_conn->err) {
|
||||
case MONGO_CONN_NO_SOCKET:
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: no socket\n");
|
||||
break;
|
||||
case MONGO_CONN_FAIL:
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: connection failed\n");
|
||||
break;
|
||||
case MONGO_CONN_NOT_MASTER:
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: not master\n");
|
||||
break;
|
||||
default:
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: unknown error %d\n", db_status);
|
||||
}
|
||||
return SWITCH_STATUS_FALSE;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to MongoDB server %s:%d\n", globals.mongo_host, globals.mongo_port);
|
||||
}
|
||||
|
||||
switch_mutex_init(&globals.mongo_mutex, SWITCH_MUTEX_NESTED, pool);
|
||||
|
||||
switch_core_add_state_handler(&state_handlers);
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown)
|
||||
{
|
||||
globals.shutdown = 1;
|
||||
switch_core_remove_state_handler(&state_handlers);
|
||||
switch_mutex_destroy(globals.mongo_mutex);
|
||||
|
||||
mongo_destroy(globals.mongo_conn);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
|
||||
*/
|
||||
@@ -64,7 +64,6 @@ void ei_link(listener_t *listener, erlang_pid * from, erlang_pid * to)
|
||||
char msgbuf[2048];
|
||||
char *s;
|
||||
int index = 0;
|
||||
int ret;
|
||||
|
||||
index = 5; /* max sizes: */
|
||||
ei_encode_version(msgbuf, &index); /* 1 */
|
||||
@@ -81,9 +80,11 @@ void ei_link(listener_t *listener, erlang_pid * from, erlang_pid * to)
|
||||
|
||||
switch_mutex_lock(listener->sock_mutex);
|
||||
#ifdef WIN32
|
||||
ret = send(listener->sockfd, msgbuf, index, 0);
|
||||
send(listener->sockfd, msgbuf, index, 0);
|
||||
#else
|
||||
ret = write(listener->sockfd, msgbuf, index);
|
||||
if (write(listener->sockfd, msgbuf, index) == -1) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", listener->peer_nodename);
|
||||
}
|
||||
#endif
|
||||
switch_mutex_unlock(listener->sock_mutex);
|
||||
}
|
||||
|
||||
@@ -549,6 +549,7 @@ static switch_status_t check_attached_sessions(listener_t *listener)
|
||||
const void *key;
|
||||
void * value;
|
||||
switch_hash_index_t *iter;
|
||||
/* event used to track sessions to remove */
|
||||
switch_event_t *event = NULL;
|
||||
switch_event_header_t *header = NULL;
|
||||
switch_event_create_subclass(&event, SWITCH_EVENT_CLONE, NULL);
|
||||
@@ -570,6 +571,7 @@ static switch_status_t check_attached_sessions(listener_t *listener)
|
||||
status = notify_new_session(listener, sp);
|
||||
if (status != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(sp->uuid_str), SWITCH_LOG_DEBUG, "Notifying new session failed\n");
|
||||
/* mark this session for removal */
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delete", (const char *) key);
|
||||
continue;
|
||||
}
|
||||
@@ -650,6 +652,10 @@ static switch_status_t check_attached_sessions(listener_t *listener)
|
||||
}
|
||||
|
||||
switch_thread_rwlock_unlock(listener->session_rwlock);
|
||||
|
||||
/* remove the temporary event */
|
||||
switch_event_destroy(&event);
|
||||
|
||||
if (prefs.done) {
|
||||
return SWITCH_STATUS_FALSE; /* we're shutting down */
|
||||
} else {
|
||||
|
||||
@@ -61,6 +61,7 @@ static struct {
|
||||
char *psk;
|
||||
switch_mutex_t *mutex;
|
||||
switch_hash_t *peer_hash;
|
||||
int loopback;
|
||||
} globals;
|
||||
|
||||
struct peer_status {
|
||||
@@ -88,6 +89,7 @@ static switch_status_t load_config(void)
|
||||
|
||||
globals.ttl = 1;
|
||||
globals.key_count = 0;
|
||||
globals.loopback = 0;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
|
||||
@@ -119,6 +121,8 @@ static switch_status_t load_config(void)
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid ttl '%s' specified, using default of 1\n", val);
|
||||
}
|
||||
} else if (!strcasecmp(var, "loopback")) {
|
||||
globals.loopback = switch_true(val);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -408,8 +412,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_event_multicast_load)
|
||||
switch_goto_status(SWITCH_STATUS_TERM, fail);
|
||||
}
|
||||
|
||||
if (switch_mcast_loopback(globals.udp_socket, 0) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to disable loopback\n");
|
||||
if (switch_mcast_loopback(globals.udp_socket, globals.loopback) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set loopback to '%d'\n", globals.loopback);
|
||||
switch_goto_status(SWITCH_STATUS_TERM, fail);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ static switch_status_t socket_logger(const switch_log_node_t *node, switch_log_l
|
||||
} else {
|
||||
switch_log_node_free(&dnode);
|
||||
if (++l->lost_logs > MAX_MISSED) {
|
||||
kill_listener(l, "Disconnected due to log queue failure.\n");
|
||||
kill_listener(l, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,7 +384,7 @@ static void event_handler(switch_event_t *event)
|
||||
}
|
||||
} else {
|
||||
if (++l->lost_events > MAX_MISSED) {
|
||||
kill_listener(l, "Disconnected due to event queue failure.\n");
|
||||
kill_listener(l, NULL);
|
||||
}
|
||||
switch_event_destroy(&clone);
|
||||
}
|
||||
@@ -1180,7 +1180,6 @@ static switch_status_t read_packet(listener_t *listener, switch_event_t **event,
|
||||
}
|
||||
}
|
||||
if (var && val) {
|
||||
switch_event_del_header(*event, var);
|
||||
switch_event_add_header_string(*event, SWITCH_STACK_BOTTOM, var, val);
|
||||
if (!strcasecmp(var, "content-length")) {
|
||||
clen = atoi(val);
|
||||
@@ -1884,11 +1883,30 @@ static switch_status_t parse_command(listener_t *listener, switch_event_t **even
|
||||
char *uuid;
|
||||
|
||||
if ((uuid = cmd + 9)) {
|
||||
char *fmt;
|
||||
strip_cr(uuid);
|
||||
|
||||
|
||||
if ((fmt = strchr(uuid, ' '))) {
|
||||
*fmt++ = '\0';
|
||||
}
|
||||
|
||||
if (!(listener->session = switch_core_session_locate(uuid))) {
|
||||
switch_snprintf(reply, reply_len, "-ERR invalid uuid");
|
||||
goto done;
|
||||
if (fmt) {
|
||||
switch_snprintf(reply, reply_len, "-ERR invalid uuid");
|
||||
goto done;
|
||||
} else {
|
||||
fmt = uuid;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fmt = strchr(uuid, ' '))) {
|
||||
if (!strcasecmp(fmt, "xml")) {
|
||||
listener->format = EVENT_FORMAT_XML;
|
||||
} else if (!strcasecmp(fmt, "plain")) {
|
||||
listener->format = EVENT_FORMAT_PLAIN;
|
||||
} else if (!strcasecmp(fmt, "json")) {
|
||||
listener->format = EVENT_FORMAT_JSON;
|
||||
}
|
||||
}
|
||||
|
||||
switch_set_flag_locked(listener, LFLAG_SESSION);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
BASE=../../../..
|
||||
|
||||
ZMQ=zeromq-2.1.7
|
||||
ZMQ=zeromq-2.1.9
|
||||
|
||||
ZMQ_BASEURL=http://download.zeromq.org
|
||||
ZMQ_BASEURL_ALT=http://download.zeromq.org/historic
|
||||
|
||||
@@ -407,7 +407,7 @@ static switch_status_t generate_json_cdr(switch_core_session_t *session, struct
|
||||
json_object_object_add(j_application, "last_executed", json_object_new_string("true"));
|
||||
}
|
||||
json_object_object_add(j_application, "app_name", json_object_new_string(ap->application_name));
|
||||
json_object_object_add(j_application, "app_data", json_object_new_string(ap->application_data));
|
||||
json_object_object_add(j_application, "app_data", json_object_new_string(switch_str_nil(ap->application_data)));
|
||||
}
|
||||
|
||||
if (caller_profile->caller_extension->children) {
|
||||
@@ -454,7 +454,7 @@ static switch_status_t generate_json_cdr(switch_core_session_t *session, struct
|
||||
json_object_object_add(j_application, "last_executed", json_object_new_string("true"));
|
||||
}
|
||||
json_object_object_add(j_application, "app_name", json_object_new_string(ap->application_name));
|
||||
json_object_object_add(j_application, "app_data", json_object_new_string(ap->application_data));
|
||||
json_object_object_add(j_application, "app_data", json_object_new_string(switch_str_nil(ap->application_data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user