2006-12-21 17:11:43 +00:00
# include "freeswitch_python.h"
2007-05-07 21:27:42 +00:00
# define sanity_check(x) do { if (!session) { switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR, "session is not initalized\n"); return x;}} while(0)
2007-06-22 19:14:53 +00:00
# define init_vars() do { caller_profile.source = "mod_python"; swapstate = S_SWAPPED_IN; } while(0)
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
PySession : : PySession ( ) : CoreSession ( )
2007-06-20 08:56:34 +00:00
{
init_vars ( ) ;
}
2008-05-27 04:54:52 +00:00
PySession : : PySession ( char * uuid ) : CoreSession ( uuid )
2007-06-20 08:56:34 +00:00
{
init_vars ( ) ;
}
2008-05-27 04:54:52 +00:00
PySession : : PySession ( switch_core_session_t * new_session ) : CoreSession ( new_session )
2007-06-20 08:56:34 +00:00
{
init_vars ( ) ;
}
2006-12-21 17:11:43 +00:00
2007-05-07 21:27:42 +00:00
2008-05-27 04:54:52 +00:00
void PySession : : setDTMFCallback ( PyObject * pyfunc , char * funcargs )
2006-12-21 17:11:43 +00:00
{
2008-05-27 04:54:52 +00:00
sanity_check ( ) ;
2006-12-21 17:11:43 +00:00
2008-05-27 04:54:52 +00:00
if ( ! PyCallable_Check ( pyfunc ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " DTMF function is not a python function. \n " ) ;
return ;
}
Py_XINCREF ( pyfunc ) ;
CoreSession : : setDTMFCallback ( ( void * ) pyfunc , funcargs ) ;
2007-06-22 19:14:53 +00:00
}
2008-05-27 04:54:52 +00:00
void PySession : : setHangupHook ( PyObject * pyfunc )
{
2007-06-09 23:07:00 +00:00
2008-05-27 04:54:52 +00:00
if ( ! PyCallable_Check ( pyfunc ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Hangup hook is not a python function. \n " ) ;
return ;
}
// without this Py_XINCREF, there will be segfaults. basically the python
// interpreter will not know that it should not GC this object.
// callback example: http://docs.python.org/ext/callingPython.html
Py_XINCREF ( pyfunc ) ;
CoreSession : : setHangupHook ( ( void * ) pyfunc ) ;
2006-12-21 17:11:43 +00:00
}
2007-05-10 18:51:47 +00:00
2008-05-27 04:54:52 +00:00
void PySession : : check_hangup_hook ( )
{
PyObject * func ;
PyObject * result ;
char * resultStr ;
bool did_swap_in = false ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
" check_hangup_hook has been DISABLED, please do not use hangup hooks in python code until further notice! \n " ) ;
2007-06-15 17:25:41 +00:00
2008-05-27 04:54:52 +00:00
if ( ! session ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " No valid session \n " ) ;
return ;
}
2006-12-21 17:11:43 +00:00
2007-06-22 19:14:53 +00:00
return ;
2008-05-27 04:54:52 +00:00
/*! NEEDS TO BE FIXED:
// The did_swap_in boolean was added to fix the following problem:
// Design flaw - we swap in threadstate based on the assumption that thread state
// is currently _swapped out_ when this hangup hook is called. However, nothing known to
// guarantee that, and if thread state is already swapped in when this is invoked,
// bad things will happen.
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " check hangup hook end_allow_threads \n " ) ;
did_swap_in = end_allow_threads ( ) ;
if ( on_hangup = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " on_hangup is null \n " ) ;
return ;
}
func = ( PyObject * ) on_hangup ;
// TODO: to match js implementation, should pass the _python_ PySession
// object instance wrapping this C++ PySession instance. but how do we do that?
// for now, pass the uuid since its better than nothing
PyObject * func_arg = Py_BuildValue ( " (s) " , uuid ) ;
result = PyEval_CallObject ( func , func_arg ) ;
Py_XDECREF ( func_arg ) ;
if ( result ) {
resultStr = ( char * ) PyString_AsString ( result ) ;
// currently just ignore the result
}
else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to call python hangup callback \n " ) ;
PyErr_Print ( ) ;
PyErr_Clear ( ) ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " check hangup hook begin_allow_threads \n " ) ;
if ( did_swap_in ) {
begin_allow_threads ( ) ;
}
Py_XDECREF ( result ) ;
*/
2007-06-22 19:14:53 +00:00
}
2008-05-27 04:54:52 +00:00
switch_status_t PySession : : run_dtmf_callback ( void * input , switch_input_type_t itype )
{
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
PyObject * func , * arglist ;
PyObject * pyresult ;
PyObject * headerdict ;
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
char * resultStr ;
char * funcargs ;
switch_file_handle_t * fh = NULL ;
bool did_swap_in = false ;
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " run_dtmf_callback \n " ) ;
2007-06-22 19:14:53 +00:00
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
if ( ! cb_state . function ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " cb_state->function is null \n " ) ;
return SWITCH_STATUS_FALSE ;
}
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
func = ( PyObject * ) cb_state . function ;
if ( ! func ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " cb_state->function is null \n " ) ;
return SWITCH_STATUS_FALSE ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " cb_state->function is NOT null \n " ) ;
}
if ( ! PyCallable_Check ( func ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " function not callable \n " ) ;
return SWITCH_STATUS_FALSE ;
}
funcargs = ( char * ) cb_state . funcargs ;
if ( itype = = SWITCH_INPUT_TYPE_DTMF ) {
arglist = Py_BuildValue ( " (sis) " , input , itype , funcargs ) ;
} else if ( itype = = SWITCH_INPUT_TYPE_EVENT ) {
// DUNNO if this is correct in the case we have an event
// will be of type switch_event_t *event;
// http://www.freeswitch.org/docs/structswitch__event.html
switch_event_t * event = ( switch_event_t * ) input ;
arglist = Py_BuildValue ( " ({s:s}is) " , " body " , event - > body , itype , funcargs ) ;
// build a dictionary with all the headers
switch_event_header_t * hp ;
headerdict = PyDict_New ( ) ;
for ( hp = event - > headers ; hp ; hp = hp - > next ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Adding event header to result " ) ;
// TODO: create PyObject pointers for name and value
// and explicitly decref them. all ref counting stuff is
// a mess and needs to be tested and looked at closer.
PyDict_SetItem ( headerdict , Py_BuildValue ( " s " , hp - > name ) , Py_BuildValue ( " s " , hp - > value ) ) ;
}
// add it to the main event dictionary (first arg in list)
// under key 'headers'
PyObject * dict = PyTuple_GetItem ( arglist , 0 ) ;
PyDict_SetItemString ( dict , " headers " , headerdict ) ;
Py_XDECREF ( headerdict ) ;
2007-06-20 08:56:34 +00:00
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unknown input type: %d \n " , itype ) ;
return SWITCH_STATUS_FALSE ;
}
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
if ( ! arglist ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " error building arglist " ) ;
return SWITCH_STATUS_FALSE ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " run_dtmf_callback end_allow_threads \n " ) ;
did_swap_in = end_allow_threads ( ) ;
pyresult = PyEval_CallObject ( func , arglist ) ;
Py_XDECREF ( arglist ) ; // Trash arglist
if ( pyresult & & pyresult ! = Py_None ) {
resultStr = ( char * ) PyString_AsString ( pyresult ) ;
switch_status_t cbresult = process_callback_result ( resultStr ) ;
return cbresult ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Python callback \n returned None " ) ;
PyErr_Print ( ) ;
PyErr_Clear ( ) ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " run_dtmf_callback begin_allow_threads \n " ) ;
if ( did_swap_in ) {
begin_allow_threads ( ) ;
}
Py_XDECREF ( pyresult ) ;
return SWITCH_STATUS_SUCCESS ;
2007-06-22 19:14:53 +00:00
}
2008-05-27 04:54:52 +00:00
bool PySession : : begin_allow_threads ( void )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " PySession::begin_allow_threads() called \n " ) ;
if ( ! session ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " No valid session \n " ) ;
return false ;
}
// swap out threadstate and store in instance variable
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
PyThreadState * swapin_tstate = ( PyThreadState * ) switch_channel_get_private ( channel , " SwapInThreadState " ) ;
// so lets assume the thread state was swapped in when the python script was started,
// therefore swapin_tstate will be NULL (because there is nothing to swap in, since its
// _already_ swapped in.)
if ( swapin_tstate = = NULL ) {
// currently swapped in
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Threadstate swap-out! \n " ) ;
swapin_tstate = PyEval_SaveThread ( ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " swapin_tstate: %p \n " , swapin_tstate ) ;
// give future swapper-inners something to actually swap in
switch_channel_set_private ( channel , " SwapInThreadState " , ( void * ) swapin_tstate ) ;
cb_state . threadState = threadState ; // TODO: get rid of this
args . buf = & cb_state ;
ap = & args ;
return true ;
} else {
// currently swapped out
return false ;
}
}
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
bool PySession : : end_allow_threads ( void )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " PySession::end_allow_threads() called \n " ) ;
// swap in threadstate from instance variable saved earlier
if ( ! session ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " No valid session \n " ) ;
return false ;
}
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
PyThreadState * swapin_tstate = ( PyThreadState * ) switch_channel_get_private ( channel , " SwapInThreadState " ) ;
if ( swapin_tstate = = NULL ) {
// currently swapped in
return false ;
} else {
// currently swapped out
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Threadstate swap-in! \n " ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " swapin_tstate: %p \n " , swapin_tstate ) ;
PyEval_RestoreThread ( swapin_tstate ) ;
// dont give any swapper-inners the opportunity to do a double swap
switch_channel_set_private ( channel , " SwapInThreadState " , NULL ) ;
return true ;
}
2007-06-22 19:14:53 +00:00
}
2008-05-27 04:54:52 +00:00
void PySession : : hangup ( char * cause )
{
// since we INCREF'd this function pointer earlier (so the py gc didnt reclaim it)
// we have to DECREF it, or else the PySession dtor will never get called and
// a zombie channel will be left over using up resources
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
if ( cb_state . function ! = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " xdecref on cb_state_function \n " ) ;
PyObject * func = ( PyObject * ) cb_state . function ;
Py_XDECREF ( func ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " cb_state.function is null \n " ) ;
}
2007-06-22 19:14:53 +00:00
2008-05-27 04:54:52 +00:00
CoreSession : : hangup ( cause ) ;
}
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
PySession : : ~ PySession ( )
{
// Should we do any cleanup here?
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " PySession::~PySession started \n " ) ;
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
if ( on_hangup ) {
PyObject * func = ( PyObject * ) on_hangup ;
Py_XDECREF ( func ) ;
}
2007-06-20 08:56:34 +00:00
2008-05-27 04:54:52 +00:00
if ( cb_state . function ! = NULL ) {
PyObject * func = ( PyObject * ) cb_state . function ;
Py_XDECREF ( func ) ;
}
2007-06-15 17:25:41 +00:00
2008-05-27 04:54:52 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " PySession::~PySession finished \n " ) ;
}