""" FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Copyright (C) 2005-2014, Anthony Minessale II 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 Portions created by the Initial Developer are Copyright (C) the Initial Developer. All Rights Reserved. Contributor(s): Traun Leyden """ 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