freeswitch/scripts/python/freepy/request.py

365 lines
10 KiB
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>
"""
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