Advertisement
Guest User

Untitled

a guest
Jul 26th, 2011
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.37 KB | None | 0 0
  1. import datetime, optparse, socket
  2. import sys
  3.  
  4. import logging
  5. logger = logging.getLogger('CstaXmlNoSoap_SnomOne')
  6. logger_handler = logging.StreamHandler() # logging.FileHandler('/var/tmp/myapp.log')
  7. logger_formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
  8. logger_handler.setFormatter ( logger_formatter )
  9. logger.addHandler ( logger_handler )
  10. logger.setLevel ( logging.DEBUG ) # DEBUG / INFO / WARN / ERROR / CRITICAL
  11.  
  12. def parse_args():
  13.     usage = """usage: %prog hostname[:port] user[@domain] pass
  14.    default port is 80
  15.    default domain is the hostname
  16. """
  17.  
  18.     hostport = sys.argv[1]
  19.     username = sys.argv[2]
  20.     password = sys.argv[3]
  21.  
  22.     if not hostport or not username or not password:
  23.         print usage
  24.         logger.shutdown()
  25.         sys.exit(-1)
  26.  
  27.     if ':' not in hostport:
  28.         host = hostport
  29.         port = '80'
  30.     else:
  31.         host, port = hostport.split(':', 1)
  32.  
  33.     if '@' not in username:
  34.         domain = host
  35.     else:
  36.         username, domain = username.split('@', 1)
  37.  
  38.     logger.debug ( 'host=%s, port=%s, user=%s, pass=%s, domain=%s' % ( host, port, username, password, domain ) )
  39.  
  40.     if not port.isdigit():
  41.         logger.critical ( 'Ports must be integers.' )
  42.         sys.exit(-1)
  43.  
  44.     return host, int(port), username, password, domain
  45.  
  46. class XmlGen:
  47.     def __init__(me):
  48.         me.prevTags = []
  49.         me.inTag = False
  50.         me.buf = ''
  51.  
  52.     def tag ( me, pTag ):
  53.         if me.inTag:
  54.             me.buf += '>'
  55.             me.inTag = False
  56.         me.prevTags.append ( pTag )
  57.         me.buf += '<' + pTag
  58.         me.inTag = True
  59.  
  60.     def attr ( me, pName, pValue ):
  61.         if not me.inTag:
  62.             raise Exception ( "Cannot create an attribute when not in a tag" )
  63.         me.buf += ' ' + pName + '="' + pValue + '"'
  64.  
  65.     def text ( me, pText ):
  66.         if me.inTag:
  67.             me.buf += '>'
  68.             me.inTag = False
  69.         me.buf += str(pText)
  70.  
  71.     def endTag ( me, allowSelfTerminate = None ):
  72.         if allowSelfTerminate == None:
  73.             allowSelfTerminate = True
  74.         if me.inTag and allowSelfTerminate:
  75.             me.buf += '/>'
  76.         else:
  77.             if me.inTag:
  78.                 me.buf += '>'
  79.             me.buf += '</' + me.prevTags[-1] + '>'
  80.         me.inTag = False
  81.         #print "endTag.prevTags.pre:", me.prevTags
  82.         me.prevTags = me.prevTags[:-1]
  83.         #print "endTag.prevTags.aft:", me.prevTags
  84.  
  85.     def tagText ( me, pTag, pText ):
  86.         me.tag ( pTag )
  87.         me.text ( pText )
  88.         me.endTag()
  89.  
  90.     def toString ( me ):
  91.         return me.buf
  92.        
  93.     # class XmlGen
  94.  
  95. def TypeName ( x ):
  96.     if type(x).__name__ == 'instance':
  97.         return x.__class__.__name__
  98.     else:
  99.         return type(x).__name__
  100.  
  101. class Timer:
  102.     def __init__ ( me ):
  103.         me.reset()
  104.    
  105.     def reset ( me ):
  106.         import time
  107.         me.mStart = time.time()
  108.  
  109.     def elapsed ( me ):
  110.         import time
  111.         return time.time() - me.mStart
  112.    
  113.     # class Timer
  114.  
  115. def binStrN2Short ( buf ):
  116.     return ord(buf[0])<<8 + ord(buf[1])
  117.  
  118. def binStrH2Short ( buf ):
  119.     return ord(buf[1])<<8 + ord(buf[0])
  120.  
  121. def short2binStrN ( d ):
  122.     return chr((d>>8) & 255) + chr(d & 255)
  123.  
  124. def short2binStrH ( d ):
  125.     return chr(d & 255) + chr((d>>8) & 255)
  126.  
  127. def xmlDrill ( x, path ):
  128.     import xml.dom.minidom
  129.     ar = path.split('/')
  130.     e = x
  131.     for i in range(len(ar)):
  132.         if i > 0 or ar[i] != '?xml':
  133.             #logger.debug ( "xmlDrill looking for: %s" % ar[i] )
  134.             if i == len(ar)-1 and ar[i] == "*":
  135.                 e = e.childNodes # TODO FIXME - process/return arrays
  136.             else:
  137.                 e = e.getElementsByTagName(ar[i]) # TODO FIXME - process/return arrays
  138.             #logger.debug ( "found type: %s" % TypeName(e) )
  139.             if TypeName(e) == "NodeList":
  140.                 e = e[0]
  141.                 #logger.debug ( "NodeList -> %s" % TypeName(e) )
  142.             if e == None: return e
  143.     return e
  144.  
  145. def xmlDrillValue ( x, path ):
  146.     ar = []
  147.     e = xmlDrill ( x, path )
  148.     e.nodeValue = xmlGetText ( e )
  149.     #logger.debug ( "xmlDrillValue name=%s value=%s" % ( e.nodeName, e.nodeValue ) )
  150.     return e.nodeValue
  151.  
  152. def xmlGetText(x):
  153.     from xml.dom import Node
  154.     import xml.dom.minidom
  155.     rc = []
  156.     if x.nodeType == Node.ELEMENT_NODE:
  157.         x = x.childNodes
  158.     for e in x:
  159.         if e.nodeType == Node.TEXT_NODE:
  160.             rc.append(e.data)
  161.     return ''.join(rc)
  162.  
  163. class CstaXmlNoSoap_SnomOne:
  164.     #' this class implements the SnomONE variant of CSTA3/XML Annex J2
  165.     #' see Ecma-323, ECMA-354 and specifically TR-087
  166.  
  167.     #' The final word on the CTI api's is the source code, but hopefully I'll keep this up to date:
  168.     #'
  169.     #' CTI INTERFACE API...
  170.     #' Function Connect(ByVal pHost$, ByVal pPort%, ByVal pUser%, ByVal pPass$, ByVal pDomain$, ByRef pCallBack) As Boolean
  171.     #' Function MakeCall(...)
  172.     #' Sub KeepAlive() ' reminds cti to keep session going, if necessary
  173.     #' Function Answer() As Boolean ' answer the ringing call
  174.     #'
  175.     #' CTI PROPERTIES
  176.     #' Property User As Short
  177.     #'
  178.     #' CTI CALLBACK API
  179.     #' Sub ctiError(ByVal info$) ' for reporting errors
  180.     #' Sub ctiCallEstablished(ByVal ani$, ByVal dnis$, ByVal cid$) ' ani = caller-id, dnis = dialed-number, cid = unique call id
  181.     #' Sub ctiCallEnded(ByVal byWhom$, ByVal cid$)
  182.  
  183.     # info from user...
  184.     #Private mHost$, mPort%, mUser%, mDomain$
  185.  
  186.     # info from server...
  187.     #Private mProtocolVersion$, mSessionId$, mSessionDuration&, mMonitorCrossRefId$, mAni$, mDnis$, mCid$
  188.  
  189.     #' internal objects...
  190.     #Private WithEvents mTcp As dhRichClient3.cTCPClient
  191.     #Private mSocket&, mCallBack, mBuf$, mUseShortTags As Boolean
  192.  
  193.     def __init__ ( me ):
  194.         me.mProtocolVersion = "http://www.ecma-international.org/standards/ecma-323/csta/ed3"
  195.         me.mUseShortTags = False
  196.         me.mAllowTrace = True
  197.         me.mBuf = ''
  198.  
  199.     def connect ( me, pHost, pPort, pUser, pPass, pDomain, pCallBack ):
  200.         logger.debug ( TypeName(me) + ".Connect called for " + pUser + "@" + pDomain )
  201.         me.mTcp = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
  202.         me.mHost = pHost
  203.         me.mPort = pPort
  204.         me.mUser = pUser
  205.         me.mPass = pPass
  206.         me.mDomain = pDomain
  207.         me.mCallBack = pCallBack
  208.         logger.info ( 'connecting to %s:%d' % ( me.mHost, me.mPort ) )
  209.         try:
  210.             me.mTcp.connect ( ( me.mHost, me.mPort ) )
  211.         except:
  212.             print 'Error connecting to %s:%d' % ( me.mHost, me.mPort )
  213.             raise
  214.        
  215.         me.mSessionId = ""
  216.        
  217.         # now send StartApplicationSession...
  218.         me._cstaStartApplicationSession ( TypeName(me), me.mUser, me.mPass, me.mDomain )
  219.        
  220.         # wait for reply...
  221.         t = Timer()
  222.         while t.elapsed() < 0.5 and 0 == len(me.mSessionId):
  223.             me.doRecv()
  224.  
  225.         if me.mSessionId == "":
  226.             # TODO FIXME: detect error response specifically to this request...
  227.             me.mCallBack.ctiError ( "Timeout waiting for Application Session initialization" )
  228.             return False
  229.  
  230.         logger.info ( "got StartApplicationSession.sessionId: " + me.mSessionId )
  231.        
  232.         me.mMonitorCrossRefId = ""
  233.         # now start monitor...
  234.         me._cstaMonitorStart ( pUser, True, True )
  235.        
  236.         # wait for reply
  237.         t.reset()
  238.         while t.elapsed() < 0.5 and 0 == len(me.mMonitorCrossRefId):
  239.             me.doRecv()
  240.  
  241.         if 0 == len(me.mMonitorCrossRefId):
  242.             # TODO FIXME: detect error response specifically to this request...
  243.             me.mCallBack.ctiError ( "Timeout waiting for MonitorStart confirmation" )
  244.             return False
  245.  
  246.         logger.info ( "got MonitorStart.MonitorCrossRefId: " + me.mMonitorCrossRefId )
  247.        
  248.         # all ready!
  249.        
  250.         return True
  251.  
  252.     def KeepAlive ( me ):
  253.         me._cstaResetApplicationSessionTimer()
  254.  
  255.     def Answer ( me ):
  256.         cstaAnswerCall ( me.mCid )
  257.  
  258.     def User ( me ):
  259.         return me.mUser
  260.  
  261.     def mTopTag ( me, xgen, pTag ):
  262.         xgen.tag ( "?xml" )
  263.         xgen.   attr ( "version", "1.0" )
  264.         xgen.   attr ( "encoding", "UTF-8" )
  265.         xgen.tag ( pTag )
  266.         xgen.   attr ( "xmlns", me.mProtocolVersion )
  267.         xgen.   attr ( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" )
  268.  
  269.     def _mSend ( me, pnInvokeId, pData ):
  270.         if me.mAllowTrace:
  271.             logger.debug ( TypeName(me) + " Raw Send: " + str(pData) )
  272.         if False: # prepend a Csta/XML No-Soap header?
  273.             # SnomONE just won't take these packets!!!!
  274.             data = Short2BinStrN(0) + Short2BinStrN(Len(pData) + 8) + ("000" + nInvokeId)[-4:] + pData
  275.             if True:
  276.                 for i in range(8):
  277.                     logger.debug ( "mSend[%d]=0x%x '%s'" % ( i, ord(pData[i]), pData[i] ) )
  278.  
  279.         me.mTcp.send ( pData )
  280.  
  281.     def _cstaStartApplicationSession ( me, pApplicationId, pUserId, pPass, pDomain ):
  282.         if 0 == len(pApplicationId):
  283.             pApplicationId = TypeName(me)
  284.         me.mDomain = pDomain
  285.         x = XmlGen()
  286.         me.mTopTag ( x, "StartApplicationSession" )
  287.         x.  tag ( "applicationInfo" )
  288.         x.      tagText ( "applicationID", pApplicationId )
  289.         x.      tag ( "applicationSpecificInfo" )
  290.         x.          tag ( "tns:SessionLoginInfo" )
  291.         x.              attr ( "xmlns:tns", "http://www.pbxnsip.com/schemas/csta/login" )
  292.         x.              tagText ( "tns:userName", pUserId )
  293.         x.              tagText ( "tns:password", pPass )
  294.         x.              tagText ( "tns:domain", pDomain )
  295.         x.              tagText ( "tns:sessionCleanupDelay", 60 )
  296.         x.          endTag() # tns:SessionLoginInfo
  297.         x.      endTag() # applicationSpecificInfo
  298.         x.  endTag() # applicationInfo
  299.         x.  tag ( "requestedProtocolVersions" )
  300.         x.      tagText ( "protocolVersion", "http://www.ecma-international.org/standards/ecma-323/csta/ed3" )
  301.         x.      tagText ( "protocolVersion", "http://www.ecma-international.org/standards/ecma-323/csta/ed4" )
  302.         x.  endTag() # requestedProtocolVersions
  303.         x.  tagText ( "requestedSessionDuration", 180 )
  304.         x.endTag() # StartApplicationSession
  305.         me._mSend ( 1, x.toString() )
  306.  
  307.     def _cstaResetApplicationSessionTimer ( me ):
  308.         #<?xml version="1.0" encoding="UTF-8"?>
  309.         #<ResetApplicationSessionTimer
  310.         #xmlns = "http://www.ecma-international.org/standards/ecma-354/appl_session"
  311.         #xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  312.         #  <sessionID>AEF12111212</sessionID>
  313.         #  <requestedSessionDuration>500</requestedSessionDuration>
  314.         #</ResetApplicationSessionTimer>
  315.         logger.debug ( "sending keep-alive" )
  316.         x = XmlGen()
  317.         me.mTopTag ( x, "ResetApplicationSessionTimer" )
  318.         x.  tagText ( "sessionID", me.mSessionId )
  319.         x.  tagText ( "requestedSessionDuration", 180 )
  320.         x.endTag() # ResetApplicationSessionTimer
  321.         me.mAllowTrace = False
  322.         me._mSend ( 0, x.toString() )
  323.  
  324.     def _cstaMonitorStart ( me, pnDevice, pbVoice, pbIm ):
  325.         x = XmlGen()
  326.         me.mTopTag ( x, "MonitorStart" )
  327.         x.  tag ( "monitorObject")
  328.         #       formatting of deviceObject is correct for SnomONE, but seems to conflict w/ spec (which wants sip:)
  329.         x.      tagText ( "deviceObject", pnDevice + "@" + me.mDomain ) # or mHost?
  330.         x.  endTag() # monitorObject
  331.         x.  tagText ( "monitorType", "device" ) # call | device
  332.         x.  tag ( "requestedMonitorMediaClass" )
  333.         x.      tagText ( "voice", "true" if pbVoice else "false" )
  334.         x.      tagText ( "im", "true" if pbIm else "false" )
  335.         x.  endTag() # requestedMonitorMediaClass
  336.         x.endTag() # MonitorStart
  337.         me._mSend ( pnDevice, x.toString() )
  338.  
  339.     def _cstaMonitorStop ( me, pnCrossRefId ):
  340.         #<?xml version="1.0" encoding="UTF-8"?>
  341.         #<MonitorStop xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3"
  342.         #xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  343.         #  <monitorCrossRefID>5665621</monitorCrossRefID>
  344.         #</MonitorStop>
  345.         x = XmlGen()
  346.         me.mTopTag ( x, "MonitorStop" )
  347.         x.  tagText ( "monitorCrossRefID", pnCrossRefId )
  348.         x.endTag() # MonitorStop
  349.         me._mSend ( pnCrossRefId, x.ToString() )
  350.  
  351.     #AlternateCall
  352.    
  353.     def _cstaAnswerCall ( me, pCid ):
  354.         #<?xml version="1.0" encoding="UTF-8"?>
  355.         #<AnswerCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3">
  356.         #  <callToBeAnswered>
  357.         #   <callID>call-id:123456789, remote-tag=1222, local-tag=7777</callID>
  358.         #   <deviceID>sip:tom@domain.com</deviceID>
  359.         #  </callToBeAnswered>
  360.         #</AnswerCall>
  361.         x = XmlGen()
  362.         me.mTopTag ( x, "AnswerCall" )
  363.         x.  tag ( "callToBeAnswered" )
  364.         x.      tagText ( "callID", "call-id:" + pCid )
  365.         #x.     TagText ( "deviceId", mDnis )
  366.         x.  endTag() # callToBeAnswered
  367.         x.endTag() # AnswerCall
  368.         me._mSend ( 0, x.ToString() )
  369.  
  370.     #ClearConnection
  371.     #ConsultationCall
  372.     #DeflectCall
  373.     #GenerateDigits
  374.     #HoldCall
  375.    
  376.     def _cstaMakeCall ( me, pnFrom, psTo ):
  377.         #<?xml version="1.0" encoding="UTF-8"?>
  378.         #<MakeCall xmlns="http://www.ecma-international.org/standards/ecma-323/csta/ed3">
  379.         #  <callingDevice>sip:ua1@123.123.123.123</callingDevice>
  380.         #  <calledDirectoryNumber>sip:alice@domain.com</calledDirectoryNumber>
  381.         #</MakeCall>
  382.         x = XmlGen()
  383.         me.mTopTag ( x, "MakeCall" )
  384.         x.  tagText ( "callingDevice", nFrom + "@" + mHost )
  385.         x.  tagText ( "calledDirectoryNumber", sTo )
  386.         x.endTag # MakeCall
  387.         me._mSend ( 0, x.ToString() )
  388.  
  389.     #ReconnectCall
  390.     #RetrieveCall
  391.     #SingleStepTransferCall
  392.     #TransferCall
  393.     #DirectedPickupCall
  394.  
  395.     def doRecv ( me ):
  396.         me.mTcp.settimeout ( 0.05 )
  397.         try:
  398.             me.mBuf += me.mTcp.recv(1024)
  399.         except socket.timeout:
  400.             #logger.debug ( 'len(me.mBuf) = %d' % len(me.mBuf) )
  401.             pass
  402.         while len(me.mBuf) > 8:
  403.             if False:
  404.                 for i in range(8):
  405.                     logger.debug ( "mRecv[%d]=0x%x '%s'" % ( i, ord(me.mBuf[i]), me.mBuf[i] ) )
  406.             hdr = binStrN2Short ( me.mBuf[0:2] )
  407.             nPkt = binStrN2Short ( me.mBuf[2:4] )
  408.            
  409.             nInvokeId = 0
  410.             if hdr == 0: # CSTA/XML over TCP w/o SOAP
  411.                 data = me.mBuf[8:nPkt]
  412.                 #nInvokeId = binStrN2Long(me.mBuf[5:9])
  413.                 #Debug.Print "got invokeId " & nInvokeId + "(" + me.mBuf[5:8] + ")"
  414.             elif hdr == 1: # CSTA/XML over TCP w/ SOAP
  415.                 # TODO FIXME - unimplemented
  416.                 data = ""
  417.             else:
  418.                 me.mCallBack.ctiError ( "bad header, flushing buffer" )
  419.                 data = ""
  420.                 nPkt = len(me.mBuf) # flush entire contents of buffer I guess...
  421.  
  422.             me.mBuf = me.mBuf[nPkt:]
  423.             if data <> "":
  424.                 from xml.dom.minidom import parseString
  425.                 me.mAllowTrace = True
  426.                 try:
  427.                     x = parseString ( data )
  428.                 except:
  429.                     me.mCallBack.ctiError ( "xml parsing error: " + data )
  430.                 else:
  431.                     # get first ( i.e. top-level ) element
  432.                     e = xmlDrill(x,"?xml/*")
  433.                     f = getattr(me, "handle_%s" % e.nodeName, None)
  434.                     if f is None:
  435.                         me.mCallBack.ctiError ( "CSTA Unrecognized response object: " + e.nodeName )
  436.                     else:
  437.                         f ( e )
  438.                     x.unlink() # encourage memory cleanup
  439.                 if me.mAllowTrace:
  440.                     logger.debug ( TypeName(me) + " Raw Recv: " + data )
  441.  
  442.     def handle_CSTAErrorCode ( me, e ):
  443.         e = drillXml(e,"operation")
  444.         if e is None:
  445.             me.mCallBack.ctiError ( "CSTAErrorCode should have <operation>: " + data )
  446.         else:
  447.             me.mCallBack.ctiError ( "CSTA Error Code: " + e.Value )
  448.  
  449.     def handle_StartApplicationSessionPosResponse ( me, e ):
  450.         #mProtocolVersion = e.DrillValue("actualProtocolVersion")
  451.         me.mSessionId = xmlDrillValue(e,"sessionID") # FYI not necessarily numeric
  452.         me.mSessionDuration = long(xmlDrillValue(e,"actualSessionDuration"))
  453.         logger.debug ( "StartApplicationSessionPosResponse(%s,%s)" % ( me.mSessionId, me.mSessionDuration ) )
  454.        
  455.     def handle_ResetApplicationSessionTimerPosResponse ( me, e ):
  456.         me.mAllowTrace = False
  457.         logger.warn ( "ignoring ResetApplicationSessionTimerPosResponse" )
  458.        
  459.     def handle_MonitorStartResponse ( me, e ):
  460.         me.mMonitorCrossRefId = xmlDrillValue ( e, "monitorCrossRefID" )
  461.         if 0 == len(me.mMonitorCrossRefId):
  462.             me.mCallBack.ctiError ( "<MonitorStartResponse> should have a <monitorCrossRefID>: " + data )
  463.        
  464.     def handle_MakeCallResponse ( me, e ):
  465.         e = xmlDrill(e,"callingDevice")
  466.         me.mCallBack.ctiMakingCall ( xmlDrillValue(e,"callID"), xmlDrillValue(e,"deviceID") )
  467.        
  468.     def handle_ServiceInitiatedEvent ( me, e ):
  469.         # a call has been requested to be established... just ignore for now...
  470.         logger.warn ( "ignoring ServiceInitiatedEvent" )
  471.        
  472.     def handle_OriginatedEvent ( me, e ):
  473.         # caller has gone off-hook to make the call they requested... just ignore for now
  474.         logger.warn ( "ignoring OriginatedEvent" )
  475.        
  476.     def handle_DeliveredEvent ( me, e ):
  477.         # call is ringing
  478.         me.mAni = me.ParseUser(x.DrillValue("callingDevice/deviceIdentifier"))
  479.         me.mDnis = me.ParseUser(x.DrillValue("calledDevice/deviceIdentifier"))
  480.         me.mCid = x.DrillValue("connection/callID")
  481.         me.mCallBack.ctiCallRinging ( me.mAni, me.mDnis, me.mCid )
  482.  
  483.     def handle_EstablishedEvent ( me, e ):
  484.         me.mAni = me.ParseUser(x.DrillValue("callingDevice/deviceIdentifier"))
  485.         me.mDnis = me.ParseUser(x.DrillValue("calledDevice/deviceIdentifier"))
  486.         me.mCid = x.DrillValue("establishedConnection/callID")
  487.         me.mCallBack.ctiCallEstablished ( me.mAni, me.mDnis, me.mCid )
  488.  
  489.     def handle_ConnectionClearedEvent ( me, e ):
  490.         by = me.ParseUser(xmlDrillValue(e,"releasingDevice/deviceIdentifier"))
  491.         me.mCid = xmlDrillValue(e,"droppedConnection/callID")
  492.         me.mCallBack.ctiCallEnded ( by, me.mCid )
  493.         me.mAni = ""
  494.         me.mDnis = ""
  495.         me.mCid = ""
  496.        
  497.     def handle_BackInServiceEvent ( me, e ):
  498.         logger.warn ( "ignoring BackInServiceEvent (all is good)" )
  499.  
  500.     def mTcp_SockError ( me, hSocket, ErrString ):
  501.         me.mCallBack.ctiError ( "Socket Error: " + ErrString )
  502.  
  503.     def mTcp_TCPDisConnect ( me, hSocket ):
  504.         me.mCallBack.ctiError ( "Socked Closed" )
  505.  
  506.     def ParseUser ( me, userHost ):
  507.         if userHost.find("@"):
  508.             return userHost.split("@",1)[0]
  509.         return userHost
  510.  
  511.     # class CstaXmlNoSoap_SnomOne
  512.  
  513.  
  514.  
  515. def format_address(address):
  516.     host, port = address
  517.     return '%s:%s' % (host or '127.0.0.1', port)
  518.  
  519. class CtiCallBack:
  520.     def ctiError ( me, s ):
  521.         logger.error ( s )
  522.  
  523.     def ctiCallRinging ( me, ani, dnis, cid ):
  524.         logger.info ( "call ringing: ani=%s, dnis=%s, cid=%s" % ( ani, dnis, cid ) )
  525.  
  526.     def ctiCallEstablished ( me, ani, dnis, cid ):
  527.         logger.info ( "call ans'd: ani=%s, dnis=%s, cid=%s" % ( ani, dnis, cid ) )
  528.  
  529.     def ctiCallEnded ( me, byWhom, cid ):
  530.         logger.info ( "call ended: by=%s, cid=%s" % ( byWhom, cid ) )
  531.  
  532.  
  533. def main():
  534.     host, port, username, password, domain = parse_args()
  535.  
  536.     cb = CtiCallBack()
  537.  
  538.     cti = CstaXmlNoSoap_SnomOne()
  539.     if not cti.connect ( host, port, username, password, domain, cb ):
  540.         logger.critical ( "Connect failed, quitting" )
  541.         sys.exit(-1)
  542.  
  543.     t = Timer()
  544.     while True:
  545.         while t.elapsed() < 60:
  546.             cti.doRecv()
  547.         cti.KeepAlive()
  548.         t.reset()
  549.  
  550.  
  551. if __name__ == '__main__':
  552.     try:
  553.         main()
  554.     except KeyboardInterrupt:
  555.         pass
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement