class UaStateGeneric(object):
    sname = 'Generic'
    ua = None
    connected = False
    dead = False

    def __init__(self, ua):
        self.ua = ua

    def recvRequest(self, req):
        return None

    def recvResponse(self, resp, tr):
        return None

    def recvEvent(self, event):
        return None

    def cancel(self, rtime, req):
        return None

    def onStateChange(self, newstate):
        pass

    def __str__(self):
        return self.sname class SipAlso(SipAddressHF):
    hf_names = ('also',) class SipAddress(object):
    name = None
    url = None
    params = None
    params_order = None
    hadbrace = None
    transtable = maketrans('-.!%*_+`\'~', 'a' * 10)

    def __init__(self, address = None, name = None, url = None, params = None,
      hadbrace = None, params_order = None, relaxedparser = False):
        self.params = {}
        self.params_order = []
        self.hadbrace = True
        if address == None:
            self.name = name
            self.url = url
            if params != None:
                self.params = params
            if params_order != None:
                self.params_order = params_order
            if hadbrace != None:
                self.hadbrace = hadbrace
            return
        # simple 'sip:foo' case
        if address.lower().startswith('sip:') and address.find('<') == -1:
            parts = address.split(';', 1)
            self.url = SipURL(parts[0], relaxedparser = relaxedparser)
            if len(parts) == 2:
                for l in parts[1].split(';'):
                    if not l:
                        continue
                    k_v = l.split('=', 1)
                    if len(k_v) == 2:
                        k, v = k_v
                    else:
                        k = k_v[0]
                        v = None
                    self.params[k] = v
                    if self.params_order.count(k) > 0:
                        self.params_order.remove(k)
                    self.params_order.append(k)
            self.hadbrace = False
            return
        if address.startswith('"'):
            qpos = findquotes(address)
            url = None
            if len(qpos) == 1:
                self.name = address[1:qpos[0]]
                url = address[qpos[0] + 1:].strip()
            else:
                for i in range(1, len(qpos)):
                    if address.find('<', 1, qpos[i]) != -1:
                        self.name = address[1:qpos[i - 1]]
                        url = address[qpos[i - 1] + 1:].strip()
                        break
                else:
                    self.name = address[1:qpos[i]]
                    url = address[qpos[i] + 1:].strip()
            if url == None:
                raise ValueError('Cannot separate name from URI: %s' % address)
            if url.startswith('<'):
                url = url[1:]
        else:
            self.name, url = address.split('<', 1)
            self.name = self.name.strip()
        url, paramstring = url.split('>', 1)
        self.url = SipURL(url, relaxedparser = relaxedparser)
        paramstring = paramstring.strip()
        if paramstring:
            for l in paramstring.split(';'):
                if not l:
                    continue
                k_v = l.split('=', 1)
                if len(k_v) == 2:
                    k, v = k_v
                else:
                    k = k_v[0]
                    v = None
                self.params[k] = v
                if self.params_order.count(k) > 0:
                    self.params_order.remove(k)
                self.params_order.append(k)

    def __str__(self):
        return self.localStr()

    def localStr(self, local_addr = None, local_port = None):
        if self.hadbrace:
            od = '<'
            cd = '>'
        else:
            od = ''
            cd = ''
        s = ''
        if self.name != None and len(self.name) > 0:
            if needquotes(self.name, self.transtable):
                s += '"%s" ' % self.name
            else:
                s += self.name + ' '
            od = '<'
            cd = '>'
        s += od + self.url.localStr(local_addr, local_port) + cd
        for k in self.params_order:
            v = self.params[k]
            if v != None:
                s += ';' + k + '=' + v
            else:
                s += ';' + k
        return s

    def getCopy(self):
        return SipAddress(name = self.name, url = self.url.getCopy(), params = self.params.copy(), \
          hadbrace = self.hadbrace, params_order = self.params_order[:])

    def getParam(self, name):
        return self.params.get(name, None)

    def setParam(self, name, value = None):
        self.params[name] = value
        if self.params_order.count(name) == 0:
            self.params_order.append(name)

    def delParam(self, name):
        del self.params[name]
        self.params_order.remove(name) class SipHeader(object):
    name = None
    body = None

    def __init__(self, s = None, name = None, body = None, bodys = None, fixname = False):
        if s != None:
            name, bodys = [x.strip() for x in s.split(':', 1)]
        if name != None:
            self.name = name.lower()
        if body == None:
            try:
                try:
                    body = hf_types[self.name](bodys)
                except KeyError:
                    body = SipGenericHF(bodys, name)
            except ESipHeaderCSV as einst:
                einst.name = self.name
                raise einst
        self.body = body
        # If no name is provided use canonic name from the body-specific
        # class.
        if self.name == None or fixname:
            self.name = body.hf_names[0]

    def __str__(self):
        return str(self.body.getCanName(self.name)) + ': ' + str(self.body)

    def localStr(self, local_addr = None, local_port = None, compact = False):
        return str(self.body.getCanName(self.name, compact)) + ': ' + \
          self.body.localStr(local_addr, local_port)

    def getBody(self):
        if not self.body.parsed:
            self.body.parse()
        return self.body

    def getBCopy(self):
        return self.body.getCopy()

    def getCopy(self):
        cself = self.__class__(name = self.name, body = self.body.getCopy())
        return cself  class SipReferTo(SipAddressHF):
    hf_names = ('refer-to', 'r') class SipReferredBy(SipAddressHF):
    hf_names = ('referred-by',) class SipMaxForwards(SipNumericHF):
    hf_names = ('max-forwards',)

    def __init__(self, body = None, number = 70):
        SipNumericHF.__init__(self, body, number)

    def getCanName(self, name, compact = False):
        return 'Max-Forwards' class CCEventDisconnect(CCEventGeneric):
    name = 'CCEventDisconnect'
    pass class CCEventFail(CCEventGeneric):
    name = 'CCEventFail'
    challenges = None
    warning = None

    def getCopy(self):
        cself = CCEventGeneric.getCopy(self)
        if self.challenges != None:
            cself.challenges = [x.getCopy() for x in self.challenges]
        return cself

    def setWarning(self, eistr):
        self.warning = SipHeader(body = SipWarning(text = eistr)) class CCEventRedirect(CCEventGeneric):
    name = 'CCEventRedirect'
    pass class CCEventUpdate(CCEventGeneric):
    name = 'CCEventUpdate'
    max_forwards = None

    def getCopy(self):
        cself = CCEventGeneric.getCopy(self)
        cself.max_forwards = self.max_forwards
        return cself class CCEventInfo(CCEventGeneric):
    name = 'CCEventInfo'
    pass class CCEventConnect(CCEventGeneric):
    name = 'CCEventConnect'
    pass
from sippy.Time.Timeout import Timeout
from sippy.UaStateGeneric import UaStateGeneric
from sippy.SipAlso import SipAlso
from sippy.SipAddress import SipAddress
from sippy.SipHeader import SipHeader
from sippy.SipReferTo import SipReferTo
from sippy.SipReferredBy import SipReferredBy
from sippy.SipMaxForwards import SipMaxForwards
from sippy.CCEvents import CCEventDisconnect, CCEventFail, CCEventRedirect, CCEventUpdate, CCEventInfo, CCEventConnect
    from sippy.UaStateDisconnected import UaStateDisconnected
    from sippy.UasStateUpdating import UasStateUpdating
    from sippy.UacStateUpdating import UacStateUpdating
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


class UaStateConnected(UaStateGeneric):
    sname = 'Connected'
    triedauth = None
    keepalives = None
    ka_tr = None
    connected = True

    def __init__(self, ua):
        self.keepalives = 0
        self.ka_tr = None
        UaStateGeneric.__init__(self, ua)
        self.ua.branch = None
        if self.ua.kaInterval > 0:
            Timeout(self.keepAlive, self.ua.kaInterval)

    def recvRequest(self, req):
        if req.getMethod() == 'REFER':
            if req.countHFs('refer-to') == 0:
                self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(400, 'Bad Request', server = self.ua.local_ua))
                return None
            self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(202, 'Accepted', server = self.ua.local_ua))
            also = req.getHFBody('refer-to').getCopy()
            self.ua.equeue.append(CCEventDisconnect(also, rtime = req.rtime, origin = self.ua.origin))
            self.ua.recvEvent(CCEventDisconnect(rtime = req.rtime, origin = self.ua.origin))
            return None
        if req.getMethod() == 'INVITE':
            self.ua.uasResp = req.genResponse(100, 'Trying', server = self.ua.local_ua)
            self.ua.global_config['_sip_tm'].sendResponse(self.ua.uasResp)
            body = req.getBody()
            if body == None:
                # Some brain-damaged stacks use body-less re-INVITE as a means
                # for putting session on hold. Quick and dirty hack to make this
                # scenario working.
                body = self.ua.rSDP.getCopy()
                body.parse()
                for sect in body.content.sections:
                    sect.c_header.addr = '0.0.0.0'
            elif str(self.ua.rSDP) == str(body):
                self.ua.sendUasResponse(200, 'OK', self.ua.lSDP, (self.ua.lContact,))
                return None
            event = CCEventUpdate(body, rtime = req.rtime, origin = self.ua.origin)
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
            try:
                event.max_forwards = req.getHFBody('max-forwards').getNum()
            except:
                pass
            if body != None:
                if self.ua.on_remote_sdp_change != None:
                    self.ua.on_remote_sdp_change(body, lambda x: self.ua.delayed_remote_sdp_update(event, x))
                    return (UasStateUpdating,)
                else:
                    self.ua.rSDP = body.getCopy()
            else:
                self.ua.rSDP = None
            self.ua.equeue.append(event)
            return (UasStateUpdating,)
        if req.getMethod() == 'BYE':
            self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(200, 'OK', server = self.ua.local_ua))
            #print 'BYE received in the Connected state, going to the Disconnected state'
            if req.countHFs('also') > 0:
                also = req.getHFBody('also').getCopy()
            else:
                also = None
            event = CCEventDisconnect(also, rtime = req.rtime, origin = self.ua.origin)
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
            self.ua.equeue.append(event)
            self.ua.cancelCreditTimer()
            self.ua.disconnect_ts = req.rtime
            return (UaStateDisconnected, self.ua.disc_cbs, req.rtime, self.ua.origin)
        if req.getMethod() == 'INFO':
            self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(200, 'OK', server = self.ua.local_ua))
            event = CCEventInfo(req.getBody(), rtime = req.rtime, origin = self.ua.origin)
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
            self.ua.equeue.append(event)
            return None
        if req.getMethod() == 'OPTIONS':
            self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(200, 'OK', server = self.ua.local_ua))
            return None
        #print 'wrong request %s in the state Connected' % req.getMethod()
        return None

    def recvACK(self, req):
        body = req.getBody()
        scode = ('ACK', 'ACK', body)
        event = CCEventConnect(scode, rtime = req.rtime, origin = self.ua.origin)
        if self.ua.expire_timer != None:
            self.ua.expire_timer.cancel()
            self.ua.expire_timer = None
        self.ua.startCreditTimer(req.rtime)
        self.ua.connect_ts = req.rtime
        for callback in self.ua.conn_cbs:
            callback(self.ua, req.rtime, self.ua.origin)
        if body != None:
            if self.ua.on_remote_sdp_change != None:
                self.ua.on_remote_sdp_change(body, lambda x: self.ua.delayed_remote_sdp_update(event, x))
                return None
            else:
                self.ua.rSDP = body.getCopy()
        else:
            self.ua.rSDP = None
        self.ua.equeue.append(event)
        return None

    def recvEvent(self, event):

        if isinstance(event, CCEventDisconnect) or isinstance(event, CCEventFail) or isinstance(event, CCEventRedirect):