Module VOIP Client (PJSIP)

Share your advanced PureBasic knowledge/code with the community.
User avatar
holzhacker
Enthusiast
Enthusiast
Posts: 123
Joined: Mon Mar 08, 2010 9:14 pm
Location: "Mens sana in corpore sano"
Contact:

Module VOIP Client (PJSIP)

Post by holzhacker »

This is a module for VOIP connections using the features of an asterisk server (https://www.asterisk.org/) using the PJSIP library (https://www.pjsip.org/).

This module is under development, so it does not do many things besides dialing for an extension or external number, but with time I will add a small example GUI and a few functionalities. I will use it here to expedite support for some customers.

The pjsipDll_notls DLL/SO can be found at: https://github.com/pavram/sipeksdk

I hope it will be useful for someone, and I would like to thank you for the collaboration you received at: viewtopic.php?f=4&t=71605

Over time I will update the code here.

Thank you all.

Code: Select all


; Comment: Module_PJSIP.pbi
; Author : holzhacker - Romerio Medeiros - romerio@gmail.com
; Version: v0.20
; Create : 10/10/2018
; Update : 05/12/2018
; OS     : All
; ***************************************************************************************

EnableExplicit

DeclareModule SIP 
  
  ;UseOGGSoundDecoder()
  
  ;-1xx—Provisional Responses
  #Trying                       = 100 ;Extended search being performed may take a significant time so a forking proxy must send a 100 Trying response.
  #Ringing                      = 180 ;Destination user agent received INVITE, And is alerting user of call.
  #CallIsBeingForwarded         = 181 ;Servers can optionally send this response To indicate a call is being forwarded.
  #Queued                       = 182 ;Indicates that the destination was temporarily unavailable, so the server has queued the call Until the destination is available. A server may send multiple 182 responses To update progress of the queue.
  #SessionProgress              = 183 ;This response may be used To send extra information For a call which is still being set up
  #EarlyDialogTerminated        = 199 ;Can be used by User Agent Server to indicate to upstream SIP entities (including the User Agent Client (UAC)) that an early dialog has been terminated  
  
  ;-2xx—Successful Responses
  #OK                           = 200 ;Indicates the request was successful.
  #Accepted                     = 202 ;Indicates that the request has been accepted For processing, but the processing has Not been completed.
  #NoNotification               = 204 ;Indicates the request was successful, but the corresponding response will Not be received.[7]  
  
  ;- 3xx—Redirection Responses
  #MultipleChoices              = 300 ;The address resolved To one of several options For the user Or client To choose between, which are listed in the message body Or the message's Contact fields.
  #MovedPermanently             = 301 ;The original Request-URI is no longer valid, the new address is given in the Contact header field, And the client should update any records of the original Request-URI With the new value.
  #MovedTemporarily             = 302 ;The client should try at the address in the Contact field. If an Expires field is present, the client may cache the result For that period of time.
  #UseProxy                     = 305 ;The Contact field details a proxy that must be used To access the requested destination.
  #AlternativeService           = 380 ; The call failed, but alternatives are detailed in the message body.  
    
  ;-4xx—Client Failure Responses
  #BadRequest                   = 400 ;The request could Not be understood due To malformed syntax.
  #Unauthorized                 = 401 ;The request requires user authentication. This response is issued by UASs And registrars.
  #PaymentRequired              = 402 ;Reserved For future use.
  #Forbidden                    = 403 ;The server understood the request, but is refusing To fulfill it. Sometimes (but Not always) this means the call has been rejected by the receiver.
  #NotFound                     = 404 ;The server has definitive information that the user does Not exist at the domain specified in the Request-URI. This status is also returned If the domain in the Request-URI does Not match any of the domains handled by the recipient of the request.
  #MethodNotAllowed             = 405 ;The method specified in the Request-Line is understood, but Not allowed For the address identified by the Request-URI.
  #NotAcceptable_406            = 406 ;The resource identified by the request is only capable of generating response entities that have content characteristics but Not acceptable according To the Accept header field sent in the request.
  #ProxyAuthenticationRequired  = 407 ;The request requires user authentication. This response is issued by proxys.
  #RequestTimeout               = 408 ;Couldn't find the user in time. The server could not produce a response within a suitable amount of time, for example, if it could not determine the location of the user in time. The client MAY repeat the request without modifications at any later time. 
  #Conflict                     = 409 ;User already registered.[8]:§7.4.10 Deprecated by omission from later RFCs[1] And by non-registration With the IANA.
  #Gone                         = 410 ;The user existed once, but is Not available here any more.
  #LengthRequired               = 411 ;The server will Not accept the request without a valid Content-Length.[8]:§7.4.12 Deprecated by omission from later RFCs[1] And by non-registration With the IANA.
  #ConditionalRequestFailed     = 412 ;The given precondition has Not been met.[9]
  #RequestEntityTooLarge        = 413 ;Request body too large.
  #RequestURITooLong            = 414 ;The server is refusing To service the request because the Request-URI is longer than the server is willing To interpret.
  #UnsupportedMediaType         = 415 ;Request body in a format Not supported.
  #UnsupportedURIScheme         = 416 ;Request-URI is unknown To the server.
  #UnknownResourcePriority      = 417 ;There was a resource-priority option tag, but no Resource-Priority header.
  #BadExtension                 = 420 ;Bad SIP Protocol Extension used, Not understood by the server.
  #ExtensionRequired            = 421 ;The server needs a specific extension Not listed in the Supported header.
  #SessionIntervalTooSmall      = 422 ;The received request contains a Session-Expires header field With a duration below the minimum timer.
  #IntervalTooBrief             = 423 ;Expiration time of the resource is too short.
  #BadLocationInformation       = 424 ;The request's location content was malformed or otherwise unsatisfactory.
  #UseIdentityHeader            = 428 ;The server policy requires an Identity header, And one has Not been provided.
  #ProvideReferrerIdentity      = 429 ;The server did Not receive a valid Referred-By token on the request.
  #FlowFailed                   = 430 ;A specific flow To a user agent has failed, although other flows may succeed. This response is intended For use between proxy devices, And should Not be seen by an endpoint (And If it is seen by one, should be treated As a 400 Bad Request response).
  #AnonymityDisallowed          = 433 ;The request has been rejected because it was anonymous.
  #BadIdentityInfo              = 436 ;The request has an Identity-Info header, And the URI scheme in that header cannot be dereferenced.
  #UnsupportedCertificate       = 437 ;The server was unable To validate a certificate For the domain that signed the request.
  #InvalidIdentityHeader        = 438 ;The server obtained a valid certificate that the request claimed was used To sign the request, but was unable To verify that signature.
  #FirstHopLacksOutboundSupport = 439 ;The first outbound proxy the user is attempting To register through does Not support the "outbound" feature of RFC 5626, although the registrar does.
  #MaxBreadthExceeded           = 440 ;If a SIP proxy determines a response context has insufficient Incoming Max-Breadth To carry out a desired parallel fork, And the proxy is unwilling/unable To compensate by forking serially Or sending a redirect, that proxy MUST Return a 440 response. A client receiving a 440 response can infer that its request did Not reach all possible destinations.
  #BadInfoPackage               = 469 ;If a SIP UA receives an INFO request associated With an Info Package that the UA has Not indicated willingness To receive, the UA MUST send a 469 response, which contains a Recv-Info header field With Info Packages For which the UA is willing To receive INFO requests.
  #ConsentNeeded                = 470 ;The source of the request did Not have the permission of the recipient To make such a request.
  #TemporarilyUnavailable       = 480 ;Callee currently unavailable.
  #CallTransactionDoesNotExist  = 481 ;Server received a request that does Not match any dialog Or transaction.
  #LoopDetected                 = 482 ;Server has detected a loop.
  #TooManyHops                  = 483 ;Max-Forwards header has reached the value '0'.
  #AddressIncomplete            = 484 ;Request-URI incomplete.
  #Ambiguous                    = 485 ;Request-URI is ambiguous.
  #BusyHere                     = 486 ;Callee is busy.
  #RequestTerminated            = 487 ;Request has terminated by bye Or cancel.
  #NotAcceptableHere            = 488 ;Some aspect of the session description Or the Request-URI is Not acceptable, Or Codec issue.
  #BadEvent                     = 489 ;The server did Not understand an event package specified in an Event header field.
  #RequestPending               = 491 ;Server has some pending request from the same dialog.
  #Undecipherable               = 493  ;Request contains an encrypted MIME body, which recipient can Not decrypt.
  #SecurityAgreementRequired    = 494 ;The server has received a request that requires a negotiated security mechanism, And the response contains a List of suitable security mechanisms For the requester To choose between, Or a digest authentication challenge.  
  
  ;-5xx—Server Failure Responses
  #ServerInternalError          = 500 ;The server could Not fulfill the request due To some unexpected condition.
  #NotImplemented               = 501 ;The server does Not have the ability To fulfill the request, such As because it does Not recognize the request method. (Compare With 405 Method Not Allowed, where the server recognizes the method but does Not allow Or support it.)
  #BadGateway                   = 502 ;The server is acting As a gateway Or proxy, And received an invalid response from a downstream server While attempting To fulfill the request.
  #ServiceUnavailable           = 503 ;The server is undergoing maintenance Or is temporarily overloaded And so cannot process the request. A "Retry-After" header field may specify when the client may reattempt its request.
  #ServerTimeOut                = 504 ;The server attempted To access another server in attempting To process the request, And did Not receive a prompt response.
  #VersionNotSupported          = 505 ;The SIP protocol version in the request is Not supported by the server.
  #MessageTooLarge              = 513 ;The request message length is longer than the server can process.
  #PreconditionFailure          = 580 ;The server is unable Or unwilling To meet some constraints specified in the offer  
  
  ;-6xx—Global Failure Responses
  #BusyEverywhere               = 600 ;All possible destinations are busy. Unlike the 486 response, this response indicates the destination knows there are no alternative destinations (such As a voicemail server) able To accept the call.
  #Decline                      = 603 ;The destination does Not wish To participate in the call, Or cannot do so, And additionally the destination knows there are no alternative destinations (such As a voicemail server) willing To accept the call.
  #DoesNotExistAnywhere         = 604 ;The server has authoritative information that the requested user does Not exist anywhere.
  #NotAcceptable_606            = 606 ;The user's agent was contacted successfully but some aspects of the session description such as the requested media, bandwidth, or addressing style were not acceptable.
  #Unwanted                     = 607      ;The called party did Not want this call from the calling party. Future attempts from the calling party are likely To be similarly rejected.

;   #SIP_OK        = 200
;   #SIP_FORBIDDEN = 403
  
  Global NewMap _RegState.s()
  
  _RegState(Str(#Ok))                 = "Ok"
  _RegState(Str(#Forbidden))          = "Forbidden"
  _RegState(Str(#ServiceUnavailable)) = "Service Unavailable"
  
  
  Enumeration 1;-CallStates
    #Calling
    #IncoingCall
    #RingingUp
    #Attended
    #StopingCalling
    #HungUp
  EndEnumeration
  
  Enumeration 1
    #Register
    #State
    #Incomming
    #Hold
    #Received
    #Status
    #DTMFDigit
    #Waiting
  EndEnumeration

  Structure cfg
    domain.s
    port.i
    protocol.s
    name.s
    username.s
    password.s
    proxy.s
    isdefault.i
    uri.s
    reguri.s
  EndStructure
  
  Global cfg.cfg

  Global RegisterAccount, getNumOfCodecs, GetCodec, MakeCall, AnswerCall, HoldCall
  
  Global onRegStateCallback, onCallStateCallback, onCallIncoming, onCallHoldConfirmCallback, onMessageReceivedCallback, onBuddyStatusChangedCallback, 
         onDtmfDigitCallback, onMessageWaitingCallback  

  
  PrototypeC.i _dll_RegisterAccount(uri.p-ascii, reguri.p-ascii, name.p-ascii, username.p-ascii, password.p-ascii, proxy.p-ascii, isdefault.i)
  PrototypeC.i _dll_makeCall(accountId.i, uri.p-ascii)
  PrototypeC.s _dll_getCodec(index.i)
  PrototypeC.i _dll_getNumOfCodecs()
  PrototypeC.i _dll_answerCall(callId.i, code.i)
  PrototypeC.i _dll_holdCall(callId.i)
  
  PrototypeC.i _dll_onRegStateCallback(onRegStateCallback.i)
  PrototypeC.i _dll_onCallStateCallback(onCallStateCallback.i)
  PrototypeC.i _dll_onCallIncoming(onCallIncoming.i)
  PrototypeC.i _dll_onCallHoldConfirmCallback(onCallHoldConfirmCallback.i)
  PrototypeC.i _dll_onMessageReceivedCallback(onMessageReceivedCallback.i)
  PrototypeC.i _dll_onBuddyStatusChangedCallback(onBuddyStatusChangedCallback.i)
  PrototypeC.i _dll_onDtmfDigitCallback(onDtmfDigitCallback.i)
  PrototypeC.i _dll_onMessageWaitingCallback(onMessageWaitingCallback.i)
  
  
  
  PrototypeC.i _dll_setSipConfig(configStruc.i)
  PrototypeC.i _dll_init()
  PrototypeC.i _dll_shutdown()
  PrototypeC.i _dll_main()
  PrototypeC.i _dll_setCodecPriority(name.p-ascii, index.i)
  
  
  PrototypeC.i _dll_releaseCall(callId.i)
  PrototypeC.i _dll_retrieveCall(callId.i)
  PrototypeC.i _dll_xferCall(callid.i, uri.p-ascii)
  PrototypeC.i _dll_xferCallWithReplaces(callId.i, dstSession.i)
  PrototypeC.i _dll_serviceReq(callId.i, serviceCode.i, destUri.p-ascii)
  PrototypeC.i _dll_dialDtmf(callId.i, digits.p-ascii, mode.i)
  PrototypeC.i _dll_removeAccounts()
  PrototypeC.i _dll_sendInfo(callid.i, content.p-ascii)
  PrototypeC.i _dll_getCurrentCodec(callId.i, codec.p-ascii)
  PrototypeC.i _dll_makeConference(callId.i)
  PrototypeC.i _dll_sendCallMessage(callId.i, message.p-ascii)
  ;// IM & Presence api
  PrototypeC.i _dll_addBuddy(uri.p-ascii, subscribe.b)
  PrototypeC.i _dll_removeBuddy(buddyId.i)
  PrototypeC.i _dll_sendMessage(accId.i,uri.p-ascii, message.p-ascii)
  PrototypeC.i _dll_setStatus(accId.i, presence_state.i)
  PrototypeC.i _dll_setSoundDevice(playbackDeviceId.i, recordingDeviceId.i)
  PrototypeC.i _dll_pollForEvents()
    
  
  
  
  PrototypeC.i dll_SetSIPCallBack(target.i, function.i)
  
  Declare   Init()
  Declare   Destroy()
 

;   Declare   Register()
  Declare   getNumCodecs()
;   Declare.s getCodec_(index.i)
  Declare   Call(accountId.i, uri.s)
  
  Declare SetAccount(domain.s,port.i,protocol.s,name.s,username.s,password.s,proxy.s="",isdefault.i=#True)
    
  DataSection
    PJSIP:
      IncludeBinary "pjsipDll_notls.dll"
    PJSIPEnd:  
  EndDataSection

EndDeclareModule

Module SIP
  
  Procedure Init()
    
    OpenFile(0,(GetTemporaryDirectory()+"pjsipDll_notls.dll"))
    WriteData(0,?PJSIP,(?PJSIPend-?PJSIP)):CloseFile(0)
    ;OpenLibrary(0,(GetTemporaryDirectory()+"pjsipDll_notls.dll"))
    
    CompilerIf #PB_Compiler_Debugger
      PJSIPLib = OpenLibrary(0, "pjsipDll_notls.dll")
    CompilerElse
      PJSIPLib = OpenLibrary(0, GetTemporaryDirectory()+"pjsipDll_notls.dll")
    CompilerEndIf
    
      Debug PJSIPLib
      
    If PJSIPLib
      
      CompilerIf #PB_Compiler_Debugger
        ExamineLibraryFunctions(0)
        While NextLibraryFunction()
          Debug LibraryFunctionName()
        Wend 
      CompilerEndIf

      ;-Prototype
      RegisterAccount._dll_registerAccount                           = GetFunction(0, "dll_registerAccount")          ;(char* uri, char* reguri, char* name, char* username, char* password, char* proxy, bool isdefault);
      getNumOfCodecs._dll_getNumOfCodecs                             = GetFunction(0, "dll_getNumOfCodecs")           ;();
      GetCodec._dll_getCodec                                         = GetFunction(0, "dll_getCodec")                 ;(int index, char* codec);
      MakeCall._dll_makeCall                                         = GetFunction(0, "dll_makeCall")                 ;(int accountId, char* uri); 
      AnswerCall._dll_answerCall                                     = GetFunction(0, "dll_answerCall")               ;(int callId, int code);
      HoldCall._dll_holdCall                                         = GetFunction(0, "dll_holdCall")                 ;(int callId);
      
      ;// Callback registration 
      onRegStateCallback._dll_onRegStateCallback                     = GetFunction(0, "onRegStateCallback")           ;(fptr_regstate cb);	    // register registration notifier
      onCallStateCallback._dll_onCallStateCallback                   = GetFunction(0, "onCallStateCallback")          ;(fptr_callstate cb);    // register call notifier
      onCallIncoming._dll_onCallIncoming                             = GetFunction(0, "onCallIncoming")               ;(fptr_callincoming cb); // register incoming call notifier
      onCallHoldConfirmCallback._dll_onCallHoldConfirmCallback       = GetFunction(0, "onCallHoldConfirmCallback")    ;(fptr_callholdconf cb); // register call notifier
      onMessageReceivedCallback._dll_onMessageReceivedCallback       = GetFunction(0, "onMessageReceivedCallback")    ;(fptr_msgrec cb);       // register call notifier
      onBuddyStatusChangedCallback._dll_onBuddyStatusChangedCallback = GetFunction(0, "onBuddyStatusChangedCallback") ;(fptr_buddystatus cb);  // register call notifier
      onDtmfDigitCallback._dll_onDtmfDigitCallback                   = GetFunction(0, "onDtmfDigitCallback")          ;(fptr_dtmfdigit cb);    // register dtmf digit notifier
      onMessageWaitingCallback._dll_onMessageWaitingCallback         = GetFunction(0, "onMessageWaitingCallback")     ;(fptr_mwi cb);          // register MWI notifier
      
      ;// pjsip common API
      setSipConfig._dll_setSipConfig                                 = GetFunction(0, "dll_setSipConfig")             ;(SipConfigStruct* config);
      PJSIP_init._dll_init                                           = GetFunction(0, "dll_init")
      shutdown._dll_shutdown                                         = GetFunction(0, "dll_shutdown")
      main._dll_main                                                 = GetFunction(0, "dll_main")                     ;(void);
      setCodecPriority._dll_setCodecPriority                         = GetFunction(0, "dll_setCodecPriority")         ;(char* name, int index);
      
      ;// pjsip call API
      releaseCall._dll_releaseCall                                   = GetFunction(0, "dll_releaseCall")              ;(int callId); 
      retrieveCall._dll_retrieveCall                                 = GetFunction(0, "dll_retrieveCall")             ;(int callId);
      xferCall._dll_xferCall                                         = GetFunction(0, "dll_xferCall")                 ;(int callid, char* uri);
      xferCallWithReplaces._dll_xferCallWithReplaces                 = GetFunction(0, "dll_xferCallWithReplaces")     ;(int callId, int dstSession);
      serviceReq._dll_serviceReq                                     = GetFunction(0, "dll_serviceReq")               ;(int callId, int serviceCode, const char* destUri);
      dialDtmf._dll_dialDtmf                                         = GetFunction(0, "dll_dialDtmf")                 ;(int callId, char* digits, int mode);
      removeAccounts._dll_removeAccounts                             = GetFunction(0, "dll_removeAccounts")
      sendInfo._dll_sendInfo                                         = GetFunction(0, "dll_sendInfo")                 ;(int callid, char* content);
      getCurrentCodec._dll_getCurrentCodec                           = GetFunction(0, "dll_getCurrentCodec")          ;(int callId, char* codec);
      makeConference._dll_makeConference                             = GetFunction(0, "dll_makeConference")           ;(int callId);
      sendCallMessage._dll_sendCallMessage                           = GetFunction(0, "dll_sendCallMessage")          ;(int callId, char* message);
      
      ;// IM & Presence api
      addBuddy._dll_addBuddy                                         = GetFunction(0, "dll_addBuddy")                 ;(char* uri, bool subscribe);
      removeBuddy._dll_removeBuddy                                   = GetFunction(0, "dll_removeBuddy")              ;(int buddyId);
      sendMessage._dll_sendMessage                                   = GetFunction(0, "dll_sendMessage")              ;(int accId, char* uri, char* message);
      setStatus._dll_setStatus                                       = GetFunction(0, "dll_setStatus")                ;(int accId, int presence_state);
      setSoundDevice._dll_setSoundDevice                             = GetFunction(0, "dll_setSoundDevice")           ;(char* playbackDeviceId, char* recordingDeviceId);
      pollForEvents._dll_pollForEvents                               = GetFunction(0, "dll_pollForEvents")            ;(int timeout);      
      
      PJSIP_init()
      main()       
      
    Else
      MessageRequester("Attention!", "pjsipDll_notls.dll Not loaded!", #PB_MessageRequester_Error)
      End 
    EndIf
    
  EndProcedure
  
  Procedure getNumCodecs()
    ProcedureReturn getNumOfCodecs()
  EndProcedure
  
;   Procedure.s  getCodec_(index.i)
;    *buffer = UTF8(getCodec(index))
;     ProcedureReturn  PeekS(*buffer)    
;   EndProcedure
  
  Procedure SetAccount(domain.s,port.i,protocol.s,name.s,username.s,password.s,proxy.s="",isdefault.i=#True)
    cfg\domain    = domain
    cfg\port      = port
    cfg\protocol  = protocol
    cfg\name      = name
    cfg\username  = username
    cfg\password  = password
    cfg\proxy     = proxy
    cfg\isdefault = isdefault
    
    cfg\uri       = "sip:" + cfg\username + "@" + cfg\domain
    cfg\reguri    = cfg\uri + ":" + Str( cfg\port ) + ";transport=" + cfg\protocol 
  EndProcedure

  Procedure Destroy()
    If IsLibrary(0): CloseLibrary(0): EndIf 
    Delay(100)
    If FileSize(GetTemporaryDirectory()+"pjsipDll_notls.dll") > 0
      DeleteFile(GetTemporaryDirectory()+"pjsipDll_notls.dll")
    EndIf
  EndProcedure
  

;   Procedure Register()
;     ProcedureReturn RegisterAccount(cfg\uri, cfg\reguri, cfg\name, cfg\username, cfg\password, cfg\proxy, cfg\isdefault)
;   EndProcedure
  
  Procedure Call(accountId.i, uri.s)
    MakeCall(accountId, uri)
  EndProcedure
  
EndModule

;-TEST
CompilerIf #PB_Compiler_IsMainFile
  
;   UseModule SIP

  Structure reg
    accountId.i
    regState.i
    stateName.s
  EndStructure
  
  Global Dim reg.reg(1)      
  
  Procedure onRegStateChanged(accountId, regState)
    Define c
    
    Debug "onRegStateChanged"
    
    Debug accountId
    Debug regState
    Debug c
    
    
    Debug "Status ("+Str(regState)+"): " + SIP::_RegState(Str(regState))
    

  EndProcedure
  
  Procedure OnCallStateChanged(a, b)
    Debug "OnCallStateChanged "
    Debug a
    Debug b
  
    Select b
      Case SIP::#Calling
        Debug "Calling"
      Case SIP::#IncoingCall
        Debug "Incoing Call: " + b 
  ;      answerCall(b, 200)
  
      Case SIP::#RingingUp
        Debug "Ringing Up"
      Case SIP::#Attended
        Debug "Attended"
       
        
      Case SIP::#StopingCalling
        Debug "Stoping Calling"
      Case SIP::#HungUp
        Debug "Hung Up"
      Default
        Debug "DEFAULT = " + Str(b)
    EndSelect
    
  EndProcedure
  
  Procedure onCallIncomingChange(a, *b)
    Debug "onCallIncomingChange"
    Debug a
    Debug *b
    Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  Procedure onCallHoldConfirmCallbackChange(a, b)
    Debug "onCallHoldConfirmCallbackChange"
    Debug a
    Debug b
  ;   Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  Procedure onMessageReceivedCallbackChange(a, b)
    Debug "onMessageReceivedCallbackChange"
    Debug a
    Debug b
  ;   Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  Procedure onBuddyStatusChangedCallbackChange(a, b)
    Debug "onBuddyStatusChangedCallbackChange"
    Debug a
    Debug b
  ;   Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  Procedure onDtmfDigitCallbackChange(a, b)
    Debug "onDtmfDigitCallbackChange"
    Debug a
    Debug b
  ;   Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  Procedure onMessageWaitingCallbackChange(a, *b)
    Protected.s msg
    Debug "onMessageWaitingCallbackChange"
    Debug a
    Debug *b
    msg = PeekS(*b, -1, #PB_UTF8)
    Debug "msg.....: " + msg
  
  EndProcedure
  
  Procedure onCallReplacedChange(a, b)
    Debug "onCallReplaced"
    Debug a
    Debug b
  ;   Debug PeekS(*b, -1, #PB_UTF8)
  EndProcedure
  
  ;When you cancel this line the call is performed but it shows error in the MakeCall line
;   #SimulateGhostError = #True 
  
  Procedure SIPTest()  
    
    SIP::Init()

    SIP::onRegStateCallback(@onRegStateChanged())
    SIP::onCallStateCallback(@OnCallStateChanged())
    SIP::onCallIncoming(@onCallIncomingChange())
    SIP::onCallHoldConfirmCallback(@onCallHoldConfirmCallbackChange())
    SIP::onMessageReceivedCallback(@onMessageReceivedCallbackChange())
    SIP::onBuddyStatusChangedCallback(@onBuddyStatusChangedCallbackChange())
    SIP::onDtmfDigitCallback(@onDtmfDigitCallbackChange())
    SIP::onMessageWaitingCallback(@onMessageWaitingCallbackChange())
       
       
       
       
    Define.i ID, nrCodecs, time
    
    ;Format Parameters 
    ;
    ;uri.p-ascii      = "sip:username@ip_or_domain"
    ;reguri.p-ascii   = "sip:username@ip_or_domain:port;transport=udp"
    ;name.p-ascii     = "name_account or * (example)"
    ;username.p-ascii = "username"
    ;password.p-ascii = "passwords"
    ;proxy.p-ascii    = "ip_or_domaind or "" (example)"
    ;isdefault.i      = #true or #false     
    
    ID = SIP::RegisterAccount("sip:YOUREXTENSION@YOURSERVER", "sip:YOUREXTENSION@YOURSERVER:5060;transport=udp", "*", "YOUREXTENSION", "YOURPASSWORD", "",#True)
    Debug "ID: " + Str(ID)
    
    If ID > 0
      ReDim reg.reg(ID)
   
      Debug "SessionID: " + Str(SIP::MakeCall(ID, "sip:EXTENSIONtoCALL@YOURSERVER"))

   
      nrCodecs = SIP::getNumCodecs()
      Debug "Number of Codecs: " + Str(nrCodecs)
      
      For time=1 To 10000
        Delay(1)  
      Next
      
    Else
      MessageRequester("Error", "Error ID: " + Str(ID))
    EndIf
    
    SIP::Destroy()
  EndProcedure
  
  SIPTest()

CompilerEndIf 
Greetings and thanks!

Romerio Medeiros
romerio@gmail.com