more clean up

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14001 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Brian West
2009-06-27 00:40:56 +00:00
parent 3be99f4aa0
commit 8d8609ab56
29 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
DEPENDENCIES
============
The State Machine Compiler is needed for the freepy connection library to function.
* wget http://ufpr.dl.sourceforge.net/sourceforge/smc/smc_5_0_0.tgz
* tar xvfz smc_5_0_0.tgz
* cd smc_5_0_0
* cd lib
* python setup.py install
PYTHONPATH
==========
- Add /path/to/freeswitch/scripts/socket to your PYTHONPATH
TESTING
=======
- Copy/Paste the code from test1() in fshelper.py to your own test module
- Adapt code as needed and run

View File

@@ -0,0 +1,40 @@
DESCRIPTION
===========
Socket library to interface w/ freeswitch mod_event_socket from Twisted python applications. Used by WikiPBX for all non-http communication w/ Freeswitch.
Install
=======
See INSTALL
Using/Examples
==============
The WikiPBX project uses this lib heavily, so looking at that code (wikipbx.org) is a good example until more docs are added.
Debugging
=========
Set FREEPY_DEBUG_ON = True in globals.py
TODO: pull this from an environment variable or a config file
Rebulding State Machines
========================
(you only need to do this if you changed an .sm file)
for each .sm file:
java -jar /usr/src/smc/bin/Smc.jar -python -g THE.sm
and loginrequest.sm -> loginrequest_sm.py with the state machine.
TODO: create a Makefile to do this stuff
TODO: use Miro Samek's HSM framework instead of SMC

View File

@@ -0,0 +1,298 @@
"""
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
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 <anthmct@yahoo.com>
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s): Traun Leyden <tleyden@branchcut.com>
"""
import sys
from twisted.internet import reactor, defer
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.python import failure
import time, re
from time import strftime
from Queue import Queue
from freepy import request
import freepy.globals
from freepy.globals import debug
"""
freepy library -- connect to freeswitch mod_socket_event via python/twisted
All commands currently use api instead of bgapi. For the networking model
used (twisted), this seems to work well and is simpler.
"""
DEBUG_ON = "see globals.py to turn on debugging"
class FreepyDispatcher(LineReceiver):
def __init__(self, conncb, discocb=None):
self.delimiter='\n' # parent class uses this
self.conncb=conncb
self.discocb=discocb
self.requestq = Queue() # queue of pending requests
self.active_request = None # the current active (de-queued) request
def connectionMade(self):
debug("FREEPY: Connection made")
self.conncb(self)
def connectionLost(self, reason):
if self.discocb:
self.discocb(reason)
debug("connectionLost: %s" % reason)
def login(self, passwd):
"""
send login request
"""
msg = "auth %s" % passwd
req = request.LoginRequest()
self.requestq.put(req)
self.transport.write("%s\n\n" % msg)
debug(">> %s" % msg)
return req.getDeferred()
def _sendCommand(self, command, bgapi):
"""
there is a lot of duplication in this object, and as many
methods as possible should be changed to use this method
rather than repeating the code
"""
command = ("bgapi %s" if bgapi else "api %s") % command
req = (request.BgDialoutRequest if bgapi else
request.DialoutRequest)()
self.requestq.put(req)
self.transport.write("%s\n\n" % command)
debug(">> %s" % command)
return req.getDeferred()
def confdialout(self, conf_name, sofia_url, bgapi=True):
"""
Instruct conference to join a particular user via dialout
@param conf_name - the name of the conference (arbitrary)
@param party2dial - a freeswitch sofia url, eg, sofia/mydomain.com/foo@bar.com
@return - a deferred that will be called back with a string like:
Reply-Text: +OK Job-UUID: 4d410a8e-2409-11dc-99bf-a5e17fab9c65
"""
if bgapi == True:
msg = "bgapi conference %s dial %s" % (conf_name,
sofia_url)
req = request.BgDialoutRequest()
else:
msg = "api conference %s dial %s" % (conf_name,
sofia_url)
req = request.DialoutRequest()
self.requestq.put(req)
self.transport.write("%s\n\n" % msg)
debug(">> %s" % msg)
return req.getDeferred()
def originate(self, party2dial, dest_ext_app, bgapi=True):
if bgapi == True:
msg = "bgapi originate %s %s" % (party2dial,
dest_ext_app)
req = request.BgDialoutRequest()
else:
msg = "api originate %s %s" % (party2dial,
dest_ext_app)
req = request.DialoutRequest()
self.requestq.put(req)
self.transport.write("%s\n\n" % msg)
debug(">> %s" % msg)
return req.getDeferred()
def listconf(self, conf_name):
"""
List users in a conf
@param conf_name - the name of the conference (arbitrary)
@return - a deferred that will be called back with an array
of models.ConfMember instances
"""
msg = "api conference %s list" % (conf_name)
req = request.ListConfRequest()
self.requestq.put(req)
self.transport.write("%s\n\n" % msg)
debug(">> %s" % msg)
return req.getDeferred()
def confkick(self, member_id, conf_name, bgapi=False):
"""
Kick member_id from conf
conf_name - name of conf
member_id - member id of user to kick, eg, "7"
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
if bgapi == True:
msg = "bgapi conference %s kick %s" % (conf_name, member_id)
req = request.BgConfKickRequest()
else:
msg = "api conference %s kick %s" % (conf_name, member_id)
req = request.ConfKickRequest()
self.requestq.put(req)
self.transport.write("%s\n\n" % msg)
debug(">> %s" % msg)
return req.getDeferred()
def confdtmf(self, member_id, conf_name, dtmf, bgapi=False):
"""
Send dtmf to member_id or to all members
conf_name - name of conf
member_id - member id of user to kick, eg, "7"
dtmf - a single dtmf or a string of dtms, eg "1" or "123"
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
msg = "conference %s dtmf %s %s" % \
(conf_name, member_id, dtmf)
return self._sendCommand(msg, bgapi)
def confsay(self, conf_name, text2speak, bgapi=False):
"""
Speak text all members
conf_name - name of conf
dtmf - text to speak
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
msg = "conference %s say %s" % (conf_name, text2speak)
return self._sendCommand(msg, bgapi)
def confplay(self, conf_name, snd_url, bgapi=False):
"""
Play a file to all members
conf_name - name of conf
dtmf - text to speak
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
msg = "conference %s play %s" % (conf_name, snd_url)
return self._sendCommand(msg, bgapi)
def confstop(self, conf_name, bgapi=False):
"""
Stop playback of all sound files
conf_name - name of conf
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
msg = "conference %s stop" % (conf_name)
return self._sendCommand(msg, bgapi)
def showchannels(self, bgapi=False):
"""
Get a list of all live channels on switch
returns - a deferred that will be called back with a result
<result row_count="2">
<row row_id="1">
<uuid>21524b8c-6d19-11dc-9380-357de4a7a612</uuid>
<created>2007-09-27 11:46:01</created>
<name>sofia/test/4761</name>
<state>CS_LOOPBACK</state>
<cid_name>FreeSWITCH</cid_name>
<cid_num>0000000000</cid_num>
<ip_addr></ip_addr>
<dest>outgoing2endpoint-6207463</dest>
<application>echo</application>
<application_data></application_data>
<read_codec>PCMU</read_codec>
<read_rate>8000</read_rate>
<write_codec>PCMU</write_codec>
<write_rate>8000</write_rate>
</row>
...
</result>
"""
msg = "show channels as xml"
return self._sendCommand(msg, bgapi)
def sofia_status_profile(self, profile_name, bgapi=False):
msg = "sofia status profile %s as xml" % (profile_name)
return self._sendCommand(msg, bgapi)
def sofia_profile_restart(self, sofia_profile_name, bgapi = False):
msg = "sofia profile %s restart" % (sofia_profile_name)
return self._sendCommand(msg, bgapi)
def killchan(self, uuid, bgapi = False):
return self._sendCommand("uuid_kill %s" % (uuid), bgapi)
def broadcast(self, uuid, path, legs, bgapi = False):
msg = "uuid_broadcast %s %s %s" % (uuid, path, legs)
return self._sendCommand(msg, bgapi)
def transfer(self, uuid, dest_ext, legs, bgapi = False):
"""
transfer <uuid> [-bleg|-both] <dest-exten>
"""
msg = "uuid_transfer %s %s %s" % (uuid, legs, dest_ext)
return self._sendCommand(msg, bgapi)
def lineReceived(self, line):
debug("<< %s" % line)
if not self.active_request:
# if no active request pending, we ignore
# blank lines
if not line.strip():
return
# if no active request, dequeue a new one
if self.requestq.empty():
# we are receiving non-empty data from fs without an
# active request pending. that means that
# there is a bug in the protocol handler
# (or possibly in fs)
raise Exception("Received line: %s w/ no pending requests" % line)
self.active_request = self.requestq.get()
# tell the request to process the line, and tell us
# if its finished or not. if its finished, we remove it
# as the active request so that a new active request will
# be de-queued.
finished = self.active_request.process(line)
if finished == True:
self.active_request = None

View File

@@ -0,0 +1,96 @@
%start MainMap::Startup
%class ApiRequest
%map MainMap
%%
Startup
{
ApiResponse
ApiResponseStarted
{
}
}
ApiResponseStarted
{
ContentLength
ContentPreStarted
{
}
}
ContentPreStarted
{
BlankLine
ContentStarted
{
}
}
ContentStarted
{
ProcessLine(line)
[ctxt.add_content(line) == True]
Startup
{
setRequestFinished(); callbackDeferred(ctxt.getResponse());
}
ProcessLine(line)
nil
{
// for some reason, have to add doNothing() here or
// importing smc will fail. looks like smc bug.
doNothing();
}
}
Default
{
BlankLine
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting blank line");
}
ContentFinished
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting content to be finished");
}
ContentLength
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting content-length header");
}
ApiResponse
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting api response");
}
ProcessLine(line)
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting needing to process a line");
}
}
%%

View File

@@ -0,0 +1,208 @@
# DO NOT EDIT.
# generated by smc (http://smc.sourceforge.net/)
# from file : apirequest.sm
import statemap
class ApiRequestState(statemap.State):
def Entry(self, fsm):
pass
def Exit(self, fsm):
pass
def ApiResponse(self, fsm):
self.Default(fsm)
def BlankLine(self, fsm):
self.Default(fsm)
def ContentFinished(self, fsm):
self.Default(fsm)
def ContentLength(self, fsm):
self.Default(fsm)
def ProcessLine(self, fsm, line):
self.Default(fsm)
def Default(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write('TRANSITION : Default\n')
msg = "\n\tState: %s\n\tTransition: %s" % (
fsm.getState().getName(), fsm.getTransition())
raise statemap.TransitionUndefinedException, msg
class MainMap_Default(ApiRequestState):
def BlankLine(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting blank line")
finally:
fsm.setState(endState)
def ContentFinished(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ContentFinished()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting content to be finished")
finally:
fsm.setState(endState)
def ContentLength(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ContentLength()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting content-length header")
finally:
fsm.setState(endState)
def ApiResponse(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ApiResponse()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting api response")
finally:
fsm.setState(endState)
def ProcessLine(self, fsm, line):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting needing to process a line")
finally:
fsm.setState(endState)
class MainMap_Startup(MainMap_Default):
def ApiResponse(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.ApiResponse()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.ApiResponseStarted)
fsm.getState().Entry(fsm)
class MainMap_ApiResponseStarted(MainMap_Default):
def ContentLength(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.ApiResponseStarted.ContentLength()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.ContentPreStarted)
fsm.getState().Entry(fsm)
class MainMap_ContentPreStarted(MainMap_Default):
def BlankLine(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.ContentPreStarted.BlankLine()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.ContentStarted)
fsm.getState().Entry(fsm)
class MainMap_ContentStarted(MainMap_Default):
def ProcessLine(self, fsm, line):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.ContentStarted.ProcessLine(line)\n")
if ctxt.add_content(line) == True :
fsm.getState().Exit(fsm)
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.callbackDeferred(ctxt.getResponse())
finally:
fsm.setState(MainMap.Startup)
fsm.getState().Entry(fsm)
else:
endState = fsm.getState()
fsm.clearState()
try:
ctxt.doNothing()
finally:
fsm.setState(endState)
class MainMap:
Startup = MainMap_Startup('MainMap.Startup', 0)
ApiResponseStarted = MainMap_ApiResponseStarted('MainMap.ApiResponseStarted', 1)
ContentPreStarted = MainMap_ContentPreStarted('MainMap.ContentPreStarted', 2)
ContentStarted = MainMap_ContentStarted('MainMap.ContentStarted', 3)
Default = MainMap_Default('MainMap.Default', -1)
class ApiRequest_sm(statemap.FSMContext):
def __init__(self, owner):
statemap.FSMContext.__init__(self)
self._owner = owner
self.setState(MainMap.Startup)
MainMap.Startup.Entry(self)
def ApiResponse(self):
self._transition = 'ApiResponse'
self.getState().ApiResponse(self)
self._transition = None
def BlankLine(self):
self._transition = 'BlankLine'
self.getState().BlankLine(self)
self._transition = None
def ContentFinished(self):
self._transition = 'ContentFinished'
self.getState().ContentFinished(self)
self._transition = None
def ContentLength(self):
self._transition = 'ContentLength'
self.getState().ContentLength(self)
self._transition = None
def ProcessLine(self, *arglist):
self._transition = 'ProcessLine'
self.getState().ProcessLine(self, *arglist)
self._transition = None
def getState(self):
if self._state == None:
raise statemap.StateUndefinedException
return self._state
def getOwner(self):
return self._owner

View File

@@ -0,0 +1,76 @@
%start MainMap::Startup
%class BgApiRequest
%map MainMap
%%
Startup
{
CommandReply
ApiResponseStarted
{
}
}
ApiResponseStarted
{
ReplyText
GotReplyText
{
}
}
GotReplyText
{
BlankLine
nil
{
}
JobUuid
Startup
{
setRequestFinished(); callOrErrback();
}
}
Default
{
BlankLine
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting blank line"); }
CommandReply
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting command reply");
}
ReplyText
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting reply text");
}
ProcessLine(line)
nil
{
setRequestFinished();
errbackDeferred("Protocol failure handling bgapi response - was not expecting line needing to be processed");
}
}
%%

View File

@@ -0,0 +1,180 @@
# DO NOT EDIT.
# generated by smc (http://smc.sourceforge.net/)
# from file : bgapirequest.sm
import statemap
class BgApiRequestState(statemap.State):
def Entry(self, fsm):
pass
def Exit(self, fsm):
pass
def BlankLine(self, fsm):
self.Default(fsm)
def CommandReply(self, fsm):
self.Default(fsm)
def JobUuid(self, fsm):
self.Default(fsm)
def ProcessLine(self, fsm, line):
self.Default(fsm)
def ReplyText(self, fsm):
self.Default(fsm)
def Default(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write('TRANSITION : Default\n')
msg = "\n\tState: %s\n\tTransition: %s" % (
fsm.getState().getName(), fsm.getTransition())
raise statemap.TransitionUndefinedException, msg
class MainMap_Default(BgApiRequestState):
def BlankLine(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting blank line")
finally:
fsm.setState(endState)
def CommandReply(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.CommandReply()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting command reply")
finally:
fsm.setState(endState)
def ReplyText(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ReplyText()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting reply text")
finally:
fsm.setState(endState)
def ProcessLine(self, fsm, line):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure handling bgapi response - was not expecting line needing to be processed")
finally:
fsm.setState(endState)
class MainMap_Startup(MainMap_Default):
def CommandReply(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.CommandReply()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.ApiResponseStarted)
fsm.getState().Entry(fsm)
class MainMap_ApiResponseStarted(MainMap_Default):
def ReplyText(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.ApiResponseStarted.ReplyText()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.GotReplyText)
fsm.getState().Entry(fsm)
class MainMap_GotReplyText(MainMap_Default):
def BlankLine(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.BlankLine()\n")
def JobUuid(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.JobUuid()\n")
fsm.getState().Exit(fsm)
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.callOrErrback()
finally:
fsm.setState(MainMap.Startup)
fsm.getState().Entry(fsm)
class MainMap:
Startup = MainMap_Startup('MainMap.Startup', 0)
ApiResponseStarted = MainMap_ApiResponseStarted('MainMap.ApiResponseStarted', 1)
GotReplyText = MainMap_GotReplyText('MainMap.GotReplyText', 2)
Default = MainMap_Default('MainMap.Default', -1)
class BgApiRequest_sm(statemap.FSMContext):
def __init__(self, owner):
statemap.FSMContext.__init__(self)
self._owner = owner
self.setState(MainMap.Startup)
MainMap.Startup.Entry(self)
def BlankLine(self):
self._transition = 'BlankLine'
self.getState().BlankLine(self)
self._transition = None
def CommandReply(self):
self._transition = 'CommandReply'
self.getState().CommandReply(self)
self._transition = None
def JobUuid(self):
self._transition = 'JobUuid'
self.getState().JobUuid(self)
self._transition = None
def ProcessLine(self, *arglist):
self._transition = 'ProcessLine'
self.getState().ProcessLine(self, *arglist)
self._transition = None
def ReplyText(self):
self._transition = 'ReplyText'
self.getState().ReplyText(self)
self._transition = None
def getState(self):
if self._state == None:
raise statemap.StateUndefinedException
return self._state
def getOwner(self):
return self._owner

View File

@@ -0,0 +1,287 @@
"""
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
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 <anthmct@yahoo.com>
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s): Traun Leyden <tleyden@branchcut.com>
"""
import sys
from twisted.internet import reactor, defer
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.python import failure
import time, re
from time import strftime
from Queue import Queue
from freepy import request
"""
This class connects to freeswitch and listens for
events and calls callback with the events.
Example messages
=================
Content-Length: 675
Content-Type: text/event-xml
<event>
<header name="force-contact" value="nat-connectile-dysfunction"></header>
etc..
</event>
Content-Length: 875
Content-Type: text/event-xml
<event>
...
</event>
"""
class FreeswitchEventListener(LineReceiver):
def __init__(self, conncb, discocb=None):
self.delimiter='\n' # parent class uses this
self.conncb=conncb
self.discocb=discocb
self.bufferlines = []
self.receiving_event = False # state to track if in <event>..</event>
self.requestq = Queue() # queue of pending requests
self.active_request = None # the current active (de-queued) request
def connectionMade(self):
self.conncb(self)
def connectionLost(self, reason):
if self.discocb:
self.discocb(reason)
print "connectionLost: %s" % reason
def login(self, passwd):
"""
send login request
"""
msg = "auth %s" % passwd
req = request.LoginRequest()
self.active_request = req
self.transport.write("%s\n\n" % str(msg))
return req.getDeferred()
def sniff_events(self, output_type, events):
"""
@param output_type - eg, xml or plain
@param events - list of events, eg ['all']
"""
event_list = " ".join(events)
msg = "event %s %s" % (output_type, event_list)
self.transport.write("%s\n\n" % str(msg))
def sniff_custom_events(self, output_type, events):
"""
when sniffing custom events, the CUSTOM keyword
must be present in message
http://wiki.freeswitch.org/wiki/Event_Socket#event
@param output_type - eg, xml or plain
@param events - list of events, eg ['all']
"""
event_list = " ".join(events)
msg = "event %s CUSTOM %s" % (output_type, event_list)
self.transport.write("%s\n\n" % msg)
def sniff_all_events(self, output_type):
"""
@param output_type - eg, xml or plain
"""
msg = "event %s all" % output_type
self.transport.write("%s\n\n" % str(msg))
def lineReceived(self, line):
if not self.active_request:
if line.find("<event>") != -1:
self.receiving_event = True
if self.receiving_event:
self.bufferlines.append(line)
if line.find("</event>") != -1:
event_xml_str = "\n".join(self.bufferlines)
self.eventReceived(event_xml_str)
self.bufferlines = []
self.receiving_event = False
else:
# we have an active request (seperate state machine)
# tell the request to process the line, and tell us
# if its finished or not. if its finished, we remove it
# as the active request so that a new active request will
# be de-queued.
finished = self.active_request.process(line)
if finished == True:
self.active_request = None
def eventReceived(self, event_xml_str):
"""
should be overridden by subclasses.
"""
raise Exception("This is an abstract class, should be overridden "
"in a subclass")
class FreeswitchEventListenerFactory(ClientFactory):
def __init__(self, protoclass, host=None, passwd=None, port=None):
"""
@param protoclass - a class (not instance) of the protocol
should be a subclass of a FreeswitchEventListener
"""
# dictionary of observers. key: event name, value: list of observers
self.event2observer = {}
self.protoclass=protoclass
if host:
self.host = host
if passwd:
self.passwd = passwd
if port:
self.port = port
self.protocol = None
self.connection_deferred = None
self.num_attempts = 0
def addobserver(self, event_name, observer):
"""
@param event_name, eg "CHANNEL_ANSWER"
@param observer (instance of object that has an eventReceived() method
"""
observers = self.event2observer.get(event_name, [])
observers.append(observer)
self.event2observer[event_name] = observers
def dispatch2observers(self, event_name, event_xml_str, event_dom):
"""
called back by the underlying protocol upon receiving an
event from freeswitch. Currently subclasses must explicitly
call this method from their eventReceived method for observers
to get the message. TODO: move this call to FreeswitchEventListener
and use observer pattern instead of any subclassing.
"""
observers = self.event2observer.get(event_name, [])
for observer in observers:
observer.eventReceived(event_name, event_xml_str, event_dom)
def reset(self):
self.protocol = None
self.connection_deferred = None
def connect(self):
if self.protocol:
# if we have a protocol object, we are connected (since we always
# null it upon any disconnection)
return defer.succeed(self.protocol)
#if self.connection_deferred:
# we are already connecting, return existing dfrd
# return self.connection_deferred
# connect and automatically login after connection
if not self.connection_deferred:
self.connection_deferred = defer.Deferred()
self.connection_deferred.addCallback(self.dologin)
self.connection_deferred.addErrback(self.generalError)
reactor.connectTCP(self.host, self.port, self)
return self.connection_deferred
def conncb(self, protocol):
self.protocol = protocol
self.protocol.__dict__["factory"] = self
deferred2callback = self.connection_deferred
self.connection_deferred = None
deferred2callback.callback(self.protocol)
def generalError(self, failure):
print "General error: %s" % failure
return failure
def startedConnecting(self, connector):
pass
def buildProtocol(self, addr):
return self.protoclass(self.conncb, self.discocb)
def clientConnectionLost(self, connector, reason):
print "clientConnectionLost! conn=%s, reason=%s" % (connector,
reason)
self.connection_deferred = None
self.protocol = None
def clientConnectionFailed(self, connector, reason):
if self.num_attempts < 10000:
self.num_attempts += 1
print "Connection to %s:%s refused, retrying attempt #%s in " \
"5 seconds" % (self.host, self.port, self.num_attempts)
return reactor.callLater(5, self.connect)
else:
print "clientConnectionFailed! conn=%s, reason=%s" % (connector,
reason)
print ("Retry attempts exhausted, total attempts: %s" %
self.num_attempts)
deferred2callback = self.connection_deferred
deferred2callback.errback(reason)
def discocb(self, reason):
print "disconnected. reason: %s" % reason
self.protocol = None
def dologin(self, connectmsg):
return self.protocol.login(self.passwd)
def test1():
fel = FreeswitchEventListener
factory = FreeswitchEventListenerFactory(protoclass=fel,
host="127.0.0.1",
port=8021,
passwd="ClueCon")
def connected(result):
print "We connected, result: %s" % result
events=['sofia::register','sofia::expire']
factory.protocol.sniff_custom_events(output_type="xml", events=events)
#factory.protocol.sniff_all_events(output_type="xml")
def failure(failure):
print "Failed to connect: %s" % failure
d = factory.connect()
d.addCallbacks(connected, failure)
d.addErrback(failure)
reactor.run()
if __name__=="__main__":
test1()

View File

@@ -0,0 +1,532 @@
#!/usr/bin/env python
"""
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
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 <anthmct@yahoo.com>
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s): Traun Leyden <tleyden@branchcut.com>
"""
from twisted.internet import reactor, defer
from twisted.internet.protocol import ClientFactory
import freepy
class FsHelper(ClientFactory):
def __init__(self, host=None, passwd=None, port=None):
if host:
self.host = host
if passwd:
self.passwd = passwd
if port:
self.port = port
self.freepyd = None
self.connection_deferred = None
def reset(self):
self.freepyd = None
self.connection_deferred = None
def connect(self):
if self.freepyd:
# if we have a protocol object, we are connected (since we always
# null it upon any disconnection)
return defer.succeed("Connected")
if self.connection_deferred:
# we are already connecting, return existing dfrd
return self.connection_deferred
self.connection_deferred = defer.Deferred()
self.connection_deferred.addCallback(self.dologin)
self.connection_deferred.addErrback(self.generalError)
print "freepy connecting to %s:%s" % (self.host, self.port)
reactor.connectTCP(self.host, self.port, self)
return self.connection_deferred
def conncb(self, freepyd):
self.freepyd = freepyd
deferred2callback = self.connection_deferred
self.connection_deferred = None
deferred2callback.callback("Connected")
def generalError(self, failure):
print "General error: %s" % failure
return failure
def startedConnecting(self, connector):
pass
def buildProtocol(self, addr):
return freepy.FreepyDispatcher(self.conncb, self.discocb)
def clientConnectionLost(self, connector, reason):
print "clientConnectionLost! conn=%s, reason=%s" % (connector,
reason)
self.connection_deferred = None
self.freepyd = None
def clientConnectionFailed(self, connector, reason):
print "clientConnectionFailed! conn=%s, reason=%s" % (connector,
reason)
self.freepyd = None
deferred2callback = self.connection_deferred
self.connection_deferred = None
deferred2callback.errback(reason)
def discocb(self, reason):
print "disconnected. reason: %s" % reason
self.freepyd = None
def dologin(self, connectmsg):
return self.freepyd.login(self.passwd)
def originate(self, party2dial, dest_ext_app, bgapi=True):
"""
party2dial - the first argument to the originate command,
eg, sofia/profile_name/1234@domain.com
dest_ext_app - the second argument to the originate command,
eg, &park() or 4761
returns - a deferred that will be called back with a result
like:
([(True, 'Reply-Text: +OK Job-UUID: d07ad7de-2406-11dc-aea3-e3b2e56b7a2c')],)
"""
def originate_inner(ignored):
deferreds = []
deferred = self.freepyd.originate(party2dial,
dest_ext_app,
bgapi)
return deferred
d = self.connect()
d.addCallback(originate_inner)
return d
def dialconf(self, people2dial, conf_name, bgapi=True):
"""
conf_name - name of conf TODO: change to match db
people2dial - an array of dictionaries:
'name': name
'number': number
returns - a deferred that will be called back with a result
like:
([(True, 'Reply-Text: +OK Job-UUID: d07ad7de-2406-11dc-aea3-e3b2e56b7a2c')],)
Its a bit ugly because its a deferred list callback.
"""
def dialconf_inner(ignored):
deferreds = []
for person2dial in people2dial:
sofia_url = person2dial['number']
deferred = self.freepyd.confdialout(conf_name,
sofia_url,
bgapi)
deferreds.append(deferred)
return defer.DeferredList(deferreds)
d = self.connect()
d.addCallback(dialconf_inner)
return d
def listconf(self, conf_name):
"""
conf_name - name of conf
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def listconf_inner(ignored):
deferred = self.freepyd.listconf(conf_name)
return deferred
d = self.connect()
d.addCallback(listconf_inner)
return d
def confkick(self, member_id, conf_name, bgapi=True):
"""
conf_name - name of conf
member_id - member id of user to kick, eg, "7"
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def confkick_inner(ignored):
#if type(member_id) == type(""):
# member_id = int(member_id)
deferred = self.freepyd.confkick(member_id, conf_name, bgapi)
return deferred
d = self.connect()
d.addCallback(confkick_inner)
return d
def confdtmf(self, member_id, conf_name, dtmf, bgapi=True):
"""
Send dtmf(s) to a conference
conf_name - name of conf
member_id - member id of user to kick, eg, "7", or "all"
dtmf - a single dtmf or a string of dtms, eg "1" or "123"
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def confdtmf_inner(ignored):
print "confdtmf_inner called"
deferred = self.freepyd.confdtmf(member_id, conf_name, dtmf, bgapi)
return deferred
d = self.connect()
d.addCallback(confdtmf_inner)
return d
def confsay(self, conf_name, text2speak, bgapi=True):
"""
conf_name - name of conf
text2speak - text to speak
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def confsay_inner(ignored):
deferred = self.freepyd.confsay(conf_name, text2speak, bgapi)
return deferred
d = self.connect()
d.addCallback(confsay_inner)
return d
def confplay(self, conf_name, snd_url, bgapi=True):
"""
conf_name - name of conf
snd_url - url to sound file
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def confplay_inner(ignored):
deferred = self.freepyd.confplay(conf_name, snd_url, bgapi)
return deferred
d = self.connect()
d.addCallback(confplay_inner)
return d
def confstop(self, conf_name, bgapi=True):
"""
stop playback of all sounds
conf_name - name of conf
returns - a deferred that will be called back with a result
like:
TODO: add this
"""
def confstop_inner(ignored):
deferred = self.freepyd.confstop(conf_name, bgapi)
return deferred
d = self.connect()
d.addCallback(confstop_inner)
return d
def showchannels(self, bgapi=True):
def showchannels_inner(ignored):
df = self.freepyd.showchannels(bgapi)
return df
d = self.connect()
d.addCallback(showchannels_inner)
return d
def killchan(self, uuid, bgapi=True):
def killchan_inner(ignored):
df = self.freepyd.killchan(uuid, bgapi)
return df
d = self.connect()
d.addCallback(killchan_inner)
return d
def broadcast(self, uuid, path, legs="both", bgapi=True):
"""
@legs - one of the following strings: aleg|bleg|both
"""
def broadcast_inner(ignored):
df = self.freepyd.broadcast(uuid, path, legs, bgapi)
return df
d = self.connect()
d.addCallback(broadcast_inner)
return d
def transfer(self, uuid, dest_ext, legs="-both", bgapi=True):
"""
@legs -bleg|-both
"""
def transfer_inner(ignored):
df = self.freepyd.transfer(uuid, dest_ext, legs, bgapi)
return df
d = self.connect()
d.addCallback(transfer_inner)
return d
def sofia_profile_restart(self, profile_name, bgapi=True):
def sofia_profile_restart_inner(ignored):
df = self.freepyd.sofia_profile_restart(profile_name,
bgapi)
return df
d = self.connect()
d.addCallback(sofia_profile_restart_inner)
return d
def sofia_status_profile(self, profile_name, bgapi=True):
def sofia_status_profile_inner(ignored):
df = self.freepyd.sofia_status_profile(profile_name,
bgapi)
return df
d = self.connect()
d.addCallback(sofia_status_profile_inner)
return d
class FsHelperTest:
def __init__(self, fshelper):
self.fshelper=fshelper
pass
def test_dialconf(self):
# the following parties will be dialed out from the conference
# called "freeswitch" on the local freeswitch instance.
# one party is actually another conference, just to make
# the example more confusing.
people2dial = [{'name':'freeswitch',
'number':'888@conference.freeswitch.org'},
{'name':'mouselike',
'number':'904@mouselike.org'}]
d = self.fshelper.dialconf(people2dial, "freeswitch", bgapi=False)
def failed(error):
print "Failed to dial users!"
reactor.stop()
return error
d.addErrback(failed)
def worked(*args):
print "Worked! Dialed user result: %s" % str(args)
d.addCallback(worked)
return d
def test_listconf(self):
d = self.fshelper.listconf("freeswitch")
def failed(failure):
print "Failed to list users!"
reactor.stop()
return failure
d.addErrback(failed)
def worked(*args):
print "List of users in conf: %s" % str(args)
return args[0]
d.addCallback(worked)
return d
def test_confkick(self, member_id="6", conf_name="freeswitch"):
d = self.fshelper.confkick(member_id, conf_name)
def failed(failure):
print "Failed to kick user!"
reactor.stop()
return failure
d.addErrback(failed)
def worked(*args):
print "Kicked user from conf, result: %s" % str(args)
d.addCallback(worked)
def test1():
kick_everyone = False
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
fsht = FsHelperTest(fshelper)
fsht.test_dialconf()
d = fsht.test_listconf()
def kickeveryone(members):
print "Kickeveryone called w/ %s (type: %s)" % (members,
type(members))
for member in members:
fsht.test_confkick(member.member_id)
def failed(failure):
print "failed: %s" % str(failure)
reactor.stop()
if kick_everyone:
d.addCallback(kickeveryone)
d.addErrback(failed)
#fsht.test_confkick()
#d = fshelper.connect()
#def connected(*args):
# fsht.test_dialconf()
# fsht.test_listconf()
#d.addCallback(connected)
reactor.run()
def test2():
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
fshelper.sofia_profile_restart("mydomain.com")
reactor.run()
def test3():
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
print "Calling originate.."
party2dial="sofia/foo/600@192.168.1.202:5080"
d = fshelper.originate(party2dial=party2dial,
dest_ext_app="101",
bgapi=True)
def worked(result):
print "Originate succeeded: %s" % result
reactor.stop()
def failed(failure):
print "failed: %s" % str(failure)
reactor.stop()
d.addCallback(worked)
d.addErrback(failed)
reactor.run()
def test4():
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
def worked(result):
print "Originate succeeded: %s" % result
#reactor.stop()
def failed(failure):
print "failed: %s" % str(failure)
#reactor.stop()
dest_ext_app = "101"
party2dial="sofia/foo/600@192.168.1.202:5080"
d = fshelper.originate(party2dial=party2dial,
dest_ext_app=dest_ext_app,
bgapi=True)
d.addCallback(worked)
d.addErrback(failed)
party2dial="sofia/foo/someone@bar.com"
d2 = fshelper.originate(party2dial=party2dial,
dest_ext_app=dest_ext_app,
bgapi=True)
d2.addCallback(worked)
d2.addErrback(failed)
reactor.run()
def test5():
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
def worked(result):
print "Originate succeeded: %s" % result
#reactor.stop()
def failed(failure):
print "failed: %s" % str(failure)
#reactor.stop()
for i in xrange(20):
party2dial="sofia/foo/600@192.168.1.202:5080"
d = fshelper.originate(party2dial=party2dial,
dest_ext_app="700",
bgapi=True)
d.addCallback(worked)
d.addErrback(failed)
reactor.run()
def test6():
"""
show channels for a given sofia profile
"""
fshelper = FsHelper(host="127.0.0.1",
passwd="ClueCon",
port=8021)
from wikipbx import channelsutil
def show_chanels(raw_xml):
print raw_xml
def failure(failure):
print failure
d = fshelper.showchannels(bgapi=False)
d.addCallback(show_chanels)
d.addErrback(failure)
reactor.run()
if __name__=="__main__":
#test1()
#test2()
#test3()
test4()
#test5()

View File

@@ -0,0 +1,14 @@
import os
if os.environ.has_key('FREEPY_DEBUG_ON'):
# pull from environment if avail
FREEPY_DEBUG_ON = os.environ['FREEPY_DEBUG_ON']
else:
# fall back to hardcoded value
#FREEPY_DEBUG_ON = False
FREEPY_DEBUG_ON = True
def debug(msg):
if FREEPY_DEBUG_ON:
print msg

View File

@@ -0,0 +1,92 @@
%start MainMap::Startup
%class LoginRequest
%map MainMap
%%
Startup
{
AuthRequest
AuthRequestStarted
{
}
}
AuthRequestStarted
{
BlankLine
AuthRequestFinished
{
}
}
AuthRequestFinished
{
CommandReply
CommandReplyStarted
{
}
}
CommandReplyStarted
{
ReplyText
GotReplyText
{
}
}
GotReplyText
{
BlankLine
Startup
{
setRequestFinished(); callOrErrback();
}
}
Default
{
BlankLine
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - Was not expecting a blank line");
}
AuthRequest
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - Was not expecting auth request");
}
CommandReply
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting a command reply");
}
ReplyText
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting reply text");
}
ProcessLine(line)
nil
{
setRequestFinished();
errbackDeferred("Protocol failure - was not expecting need to receive a line that needs to be processed");
}
}
%%

View File

@@ -0,0 +1,210 @@
# DO NOT EDIT.
# generated by smc (http://smc.sourceforge.net/)
# from file : loginrequest.sm
import statemap
class LoginRequestState(statemap.State):
def Entry(self, fsm):
pass
def Exit(self, fsm):
pass
def AuthRequest(self, fsm):
self.Default(fsm)
def BlankLine(self, fsm):
self.Default(fsm)
def CommandReply(self, fsm):
self.Default(fsm)
def ProcessLine(self, fsm, line):
self.Default(fsm)
def ReplyText(self, fsm):
self.Default(fsm)
def Default(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write('TRANSITION : Default\n')
msg = "\n\tState: %s\n\tTransition: %s" % (
fsm.getState().getName(), fsm.getTransition())
raise statemap.TransitionUndefinedException, msg
class MainMap_Default(LoginRequestState):
def BlankLine(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - Was not expecting a blank line")
finally:
fsm.setState(endState)
def AuthRequest(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.AuthRequest()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - Was not expecting auth request")
finally:
fsm.setState(endState)
def CommandReply(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.CommandReply()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting a command reply")
finally:
fsm.setState(endState)
def ReplyText(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ReplyText()\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting reply text")
finally:
fsm.setState(endState)
def ProcessLine(self, fsm, line):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
endState = fsm.getState()
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.errbackDeferred("Protocol failure - was not expecting need to receive a line that needs to be processed")
finally:
fsm.setState(endState)
class MainMap_Startup(MainMap_Default):
def AuthRequest(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.AuthRequest()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.AuthRequestStarted)
fsm.getState().Entry(fsm)
class MainMap_AuthRequestStarted(MainMap_Default):
def BlankLine(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.AuthRequestStarted.BlankLine()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.AuthRequestFinished)
fsm.getState().Entry(fsm)
class MainMap_AuthRequestFinished(MainMap_Default):
def CommandReply(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.AuthRequestFinished.CommandReply()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.CommandReplyStarted)
fsm.getState().Entry(fsm)
class MainMap_CommandReplyStarted(MainMap_Default):
def ReplyText(self, fsm):
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.CommandReplyStarted.ReplyText()\n")
fsm.getState().Exit(fsm)
fsm.setState(MainMap.GotReplyText)
fsm.getState().Entry(fsm)
class MainMap_GotReplyText(MainMap_Default):
def BlankLine(self, fsm):
ctxt = fsm.getOwner()
if fsm.getDebugFlag() == True:
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.BlankLine()\n")
fsm.getState().Exit(fsm)
fsm.clearState()
try:
ctxt.setRequestFinished()
ctxt.callOrErrback()
finally:
fsm.setState(MainMap.Startup)
fsm.getState().Entry(fsm)
class MainMap:
Startup = MainMap_Startup('MainMap.Startup', 0)
AuthRequestStarted = MainMap_AuthRequestStarted('MainMap.AuthRequestStarted', 1)
AuthRequestFinished = MainMap_AuthRequestFinished('MainMap.AuthRequestFinished', 2)
CommandReplyStarted = MainMap_CommandReplyStarted('MainMap.CommandReplyStarted', 3)
GotReplyText = MainMap_GotReplyText('MainMap.GotReplyText', 4)
Default = MainMap_Default('MainMap.Default', -1)
class LoginRequest_sm(statemap.FSMContext):
def __init__(self, owner):
statemap.FSMContext.__init__(self)
self._owner = owner
self.setState(MainMap.Startup)
MainMap.Startup.Entry(self)
def AuthRequest(self):
self._transition = 'AuthRequest'
self.getState().AuthRequest(self)
self._transition = None
def BlankLine(self):
self._transition = 'BlankLine'
self.getState().BlankLine(self)
self._transition = None
def CommandReply(self):
self._transition = 'CommandReply'
self.getState().CommandReply(self)
self._transition = None
def ProcessLine(self, *arglist):
self._transition = 'ProcessLine'
self.getState().ProcessLine(self, *arglist)
self._transition = None
def ReplyText(self):
self._transition = 'ReplyText'
self.getState().ReplyText(self)
self._transition = None
def getState(self):
if self._state == None:
raise statemap.StateUndefinedException
return self._state
def getOwner(self):
return self._owner

View File

@@ -0,0 +1,81 @@
"""
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
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 <anthmct@yahoo.com>
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s): Traun Leyden <tleyden@branchcut.com>
"""
"""
Data models for objects inside freeswitch
"""
import re
class ConfMember:
def __init__(self, rawstring):
self.rawstring = rawstring
self.member_id = None
self.member_uri = None
self.uuid = None
self.caller_id_name = None
self.caller_id_number = None
self.flags = None
self.volume_in = None
self.volume_out = None
self.energy_level = None
self.parse(self.rawstring)
def parse(self, rawstring):
"""
1;sofia/mydomain.com/user@somewhere.com;898e6552-24ab-11dc-9df7-9fccd4095451;FreeSWITCH;0000000000;hear|speak;0;0;300
"""
fields = rawstring.split(";")
self.member_id = fields[0]
self.member_uri = fields[1]
self.uuid = fields[2]
self.caller_id_name = fields[3]
self.caller_id_number = fields[4]
self.flags = fields[5]
self.volume_in = fields[6]
self.volume_out = fields[7]
self.energy_level = fields[8]
def brief_member_uri(self):
"""
if self.member_uri is sofia/mydomain.com/foo@bar.com
return foo@bar.com
"""
if not self.member_uri:
return None
if self.member_uri.find("/") == -1:
return self.member_uri
r = self.member_uri.split("/")[-1] # tokenize on "/" and return last item
return r
def __repr__(self):
return self.__str__()
def __str__(self):
return "%s (%s)" % (self.member_id, self.member_uri)

View File

@@ -0,0 +1,364 @@
"""
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
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 <anthmct@yahoo.com>
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s): Traun Leyden <tleyden@branchcut.com>
"""
import sys
from twisted.internet import reactor, defer
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.python import failure
from twisted.python.failure import Failure
import time, re
from time import strftime
from Queue import Queue
from freepy import models
import freepy.globals
from freepy.globals import debug
"""
These are response handlers for different types of requests.
It reads the response from freeswitch, and calls back
self.deferred with the result.
The naming could be improved, but here is the translation:
LoginRequest - Response handler for a login request
"""
class FreepyRequest(object):
def __init__(self):
self.deferred = defer.Deferred()
self.response_content = ""
self.finished = False
def isRequestFinished(self):
return self.finished
def setRequestFinished(self):
debug("setRequestFinished called. response_content: %s " %
self.response_content)
self.finished = True
def getDeferred(self):
return self.deferred
def callbackDeferred(self, cbval):
self.deferred.callback(cbval)
def errbackDeferred(self, result):
self.deferred.errback(Exception(str(result)))
def process(self, line):
"""
processs a line from the fs response. if the fs response has been
detected to be finished, then:
* create an appropriate response based on request type
* callback deferred with response
* rturn True to indicate we are finished
otherwise, if the fs response is incomplete, just buffer the data
"""
if not line.strip() or len(line.strip()) == 0:
self._fsm.BlankLine()
return self.isRequestFinished()
matchstr = re.compile("auth/request", re.I)
result = matchstr.search(line)
if (result != None):
self._fsm.AuthRequest()
return self.isRequestFinished()
matchstr = re.compile("command/reply", re.I)
result = matchstr.search(line)
if (result != None):
self._fsm.CommandReply()
return self.isRequestFinished()
matchstr = re.compile("Reply-Text", re.I)
result = matchstr.search(line)
if (result != None):
debug("FREEPY: got Reply-Text")
fields = line.split(":") # eg, ['Reply-Text','+OK Job-UUID', '882']
endfields = fields[1:]
self.response_content = "".join(endfields)
self._fsm.ReplyText()
return self.isRequestFinished()
matchstr = re.compile("Job-UUID", re.I)
result = matchstr.search(line)
if (result != None):
fields = line.split(":") # eg, ['Job-UUID','c9eee07e-508-..']
endfields = fields[1:]
# ignore job uuid given on this line, take the one sent
# in Reply-Text response line
# self.response_content = "".join(endfields)
self._fsm.JobUuid()
return self.isRequestFinished()
matchstr = re.compile("api/response", re.I)
result = matchstr.search(line)
if (result != None):
self._fsm.ApiResponse()
return self.isRequestFinished()
matchstr = re.compile("Content-Length", re.I)
result = matchstr.search(line)
if (result != None):
# line: Content-Length: 34
self.content_length = int(line.split(":")[1].strip())
self._fsm.ContentLength()
return self.isRequestFinished()
self._fsm.ProcessLine(line)
return self.isRequestFinished()
def callOrErrback(self):
matchstr = re.compile("OK", re.I)
result = matchstr.search(self.response_content)
if (result != None):
self.callbackDeferred(self.response_content)
return
self.errbackDeferred(Failure(Exception(self.response_content)))
def doNothing(self):
# weird smc issue workaround attempt
pass
class LoginRequest(FreepyRequest):
"""
Example success response
========================
lineReceived: Content-Type: auth/request
lineReceived:
lineReceived: Content-Type: command/reply
lineReceived: Reply-Text: +OK accepted
lineReceived:
Example failure response
========================
lineReceived: Content-Type: auth/request
lineReceived:
lineReceived: Content-Type: command/reply
lineReceived: Reply-Text: -ERR invalid
lineReceived:
"""
def __init__(self):
super(LoginRequest, self).__init__()
import loginrequest_sm
self._fsm = loginrequest_sm.LoginRequest_sm(self)
def callOrErrback(self):
matchstr = re.compile("OK", re.I)
result = matchstr.search(self.response_content)
if (result != None):
self.callbackDeferred(self.response_content)
return
msg = "Login failed, most likely a bad password"
self.errbackDeferred(Failure(Exception(msg)))
def getReplyText(self):
self.response_content
class BgApiRequest(FreepyRequest):
"""
Here is one of the 'bgapi requests' this class
supports:
linereceived: Content-Type: command/reply
linereceived: Reply-Text: +OK Job-UUID: 788da080-24e0-11dc-85f6-3d7b12..
linereceived:
"""
def __init__(self):
super(BgApiRequest, self).__init__()
import bgapirequest_sm
self._fsm = bgapirequest_sm.BgApiRequest_sm(self)
def getResponse(self):
# subclasses may want to parse this into a meaningful
# object or set of objects (eg, see ListConfRequest)
# By default, just return accumulated string
return self.response_content
class ApiRequest(FreepyRequest):
"""
Here is one of the 'api requests' this class
supports:
lineReceived: Content-Type: api/response
lineReceived: Content-Length: 34
lineReceived:
lineReceived: Call Requested: result: [SUCCESS]
"""
def __init__(self):
super(ApiRequest, self).__init__()
import apirequest_sm
self._fsm = apirequest_sm.ApiRequest_sm(self)
self.response_content = ""
def doNothing(self):
# weird smc issue workaround attempt
pass
def add_content(self, line):
"""
Add content to local buffer
return - True if finished adding content, False otherwise
"""
# since the twisted LineReceiver strips off the newline,
# we need to add it back .. otherwise the Content-length
# will be off by one
line += "\n"
self.response_content += line
if len(self.response_content) == self.content_length:
return True
elif len(self.response_content) > self.content_length:
return True
else:
return False
def getResponse(self):
# subclasses may want to parse this into a meaningful
# object or set of objects (eg, see ListConfRequest)
# By default, just return accumulated string
return self.response_content
class DialoutRequest(ApiRequest):
"""
Example raw dialout response
============================
lineReceived: Content-Type: api/response
lineReceived: Content-Length: 34
lineReceived:
lineReceived: Call Requested: result: [SUCCESS]
"""
def __init__(self):
super(DialoutRequest, self).__init__()
class BgDialoutRequest(BgApiRequest):
def __init__(self):
super(BgDialoutRequest, self).__init__()
class ConfKickRequest(ApiRequest):
"""
Example response
================
"""
def __init__(self):
super(ConfKickRequest, self).__init__()
class BgConfKickRequest(BgApiRequest):
"""
Example response
================
"""
def __init__(self):
super(BgConfKickRequest, self).__init__()
class ListConfRequest(ApiRequest):
"""
Response to request to list conferences:
========================================
lineReceived: Content-Type: api/response
lineReceived: Content-Length: 233
lineReceived:
lineReceived: 2;sofia/mydomain.com/foo@bar.com;e9be6e72-2410-11dc-8daf-7bcec6dda2ae;FreeSWITCH;0000000000;hear|speak;0;0;300
lineReceived: 1;sofia/mydomain.com/foo2@bar.com;e9be5fcc-2410-11dc-8daf-7bcec6dda2ae;FreeSWITCH;0000000000;hear|speak;0;0;300
"""
def __init__(self):
super(ListConfRequest, self).__init__()
self.conf_members = []
def add_content(self, line):
"""
conf not empty example
======================
1;sofia/mydomain.com/888@conference.freeswitch.org;898e6552-24ab-11dc-9df7-9fccd4095451;FreeSWITCH;0000000000;hear|speak;0;0;300
conf empty example
==================
Conference foo not found
"""
matchstr = re.compile("not found", re.I)
result = matchstr.search(line)
if (result != None):
# no conf found..
pass
else:
confmember = models.ConfMember(line)
self.conf_members.append(confmember)
return super(ListConfRequest, self).add_content(line)
def getResponse(self):
# TODO: parse this content into a meaningful
# 'object' .. though, not sure this is really
# necessary. wait till there's a need
return self.conf_members