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:
Moises Silva
2011-09-28 12:19:48 -04:00
407 changed files with 25813 additions and 41133 deletions
+2 -1
View File
@@ -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;
+7
View File
@@ -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
+15 -7
View File
@@ -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);
+2
View File
@@ -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:
+4 -1
View File
@@ -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
*
+9 -1
View File
@@ -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,
+8 -2
View File
@@ -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
+37 -14
View File
@@ -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); \
+23 -2
View File
@@ -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 */
+1
View File
@@ -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);\
+77 -9
View File
@@ -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)
+14
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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");
+234 -101
View File
@@ -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];
}
}
}
+1 -1
View File
@@ -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);
+20 -7
View File
@@ -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) {
+1 -1
View File
@@ -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);
+94 -40
View File
@@ -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)
);
+86 -12
View File
@@ -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;
}
+6 -7
View File
@@ -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
+23 -16
View File
@@ -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
-135
View File
@@ -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;
}
-53
View File
@@ -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_ */
-250
View File
@@ -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
*/
-30
View File
@@ -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);
-743
View File
@@ -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
*/
-32
View File
@@ -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>
-397
View File
@@ -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 -->
-175
View File
@@ -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);
}
-16
View File
@@ -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>
+508
View File
@@ -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;
}
+2 -2
View File
@@ -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(&params, 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(&params, 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))) {
+1 -1
View File
@@ -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
+9 -7
View File
@@ -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);
+3
View File
@@ -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)
+6 -4
View File
@@ -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;
}
+9 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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");
}
+1 -1
View File
@@ -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;
+64 -18
View File
@@ -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 {
+26
View File
@@ -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);
+79 -33
View File
@@ -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());
+12 -4
View File
@@ -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);
+156 -54
View File
@@ -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) {
+166 -135
View File
@@ -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)
+150 -46
View File
@@ -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)
+216 -90
View File
@@ -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( &gte );
bson_append_int( &gte, "$gte", start );
bson_finish( &gte );
bson_append_bson( &query, "n", &gte );
bson_destroy( &gte );
}
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