Example of registrar module
-- Copyright (c) 2008 Xeepe project http://xeepe.com
-- Under the terms of the MIT License
-- http://www.opensource.org/licenses/mit-license.html
local r = require"resip"
local DebugLog = r.DebugLog
local InfoLog = r.InfoLog
local WarnLog = r.WarnLog
local ErrLog = r.ErrLog
local app = require"app"
module(..., package.seeall)
--module(...)
--in memory registration database ;-)
db = {}
function getRegDatabase()
return db
end
--object passed to this module from main application
--exposes basic interface function like send()
local stack
-- headers we use, make local for fast access
local h = r.h
local h_Contacts = h.Contacts
local h_To = h.To
local h_Expires = h.Expires
local h_Authorizations = h.Authorizations
-- parameters we use, make local for fast access
local p = r.p
local p_username = p.username
local p_realm = p.realm
local p_expires = p.expires
local DEFAULT_EXPIRE = 3600
local function updateContact(aorString,uri,expires)
InfoLog( "updateContact: aor:",aorString,"contact:",uri,"expires:",expires)
local contacts = db[aorString]
if not contacts then
DebugLog( "No existing contact, create new record:" )
contacts = r.newNameAddrs()
local newContact = r.newNameAddr()
newContact:uri():set(uri)
newContact:paramset(p_expires,expires)
contacts:push_back( newContact )
db[aorString] = contacts
DebugLog( tostring(db[aorString]))
return
end
local find,err = contacts:iterate( function(item)
if item:uri() == uri then
item:paramset(p_expires,expires)
return true
end
end)
if not find and not err then
local newContact = r.newNameAddr()
newContact:uri(uri)
newContact:paramset(p_expires,expires)
contacts:push_back( newContact )
end
if err then
ErrLog( err )
end
end
local function removeContact(aorString,uri)
InfoLog( "removeContact: aor: "..tostring(aor).." contacts: "..tostring(uri))
local contacts = db[aorString]
if not contacts then
return
end
local updatedContact = contacts:clone()
local find,err = contacts:iterate( function(item)
if item:uri() ~= uri then
updatedContact:push_back(item)
end
end)
if updatedContact:size() == 0 then
db[aorString] = nil
else
db[aorString] = updatedContacts
end
end
--in contacts - with absolute expires values (as stored within db)
--in now current time
--return contacts with relative expires
local function fixExpires(from,to,now)
to:clear()
from:iterate( function(item)
local e = tonumber(item:param(p_expires):value()) -- now
local newContact = r.newNameAddr()
newContact:uri():set(item:uri())
newContact:paramset(p_expires,e)
to:push_back( newContact )
end )
end
-- return response to send if we must stop processing
-- return nil otherwise
function authenticate(msg)
--don't check for ACK,BYE - it is for registrar!
InfoLog("authenticate")
if msg:exists(h_Authorizations) then
local auths = msg:header(h_Authorizations)
-- find our auth
local auth,err = auths:iterate( function(item)
tostring(item) --ToDo - workaround about access to parameters
if app.isMyDomain( item:param(p_realm):value() ) then
return item
end
end)
if err then
return r.makeResponse(msg,500)
end
if auth then
InfoLog("authenticate found our auth")
local user = auth:param(p_username):value()
--may username be empty??
local a1 = app.getA1(user)
if not a1 then
return r.makeResponse( msg, 403 )
end
InfoLog("authenticate 1")
local result = r.advancedAuthenticateRequest(msg,app.getMyRealm(),a1,600)
InfoLog("authenticate 2")
if result == 1 then --Failed
return r.makeResponse(msg, 403, "Authentication Failed")
elseif result == 2 then --Authenticated
return
elseif result == 3 then --Expired
return r.makeChallenge(msg, app.getMyRealm(),
false, --useAuth
true ) --stale
elseif result == 4 then --BadlyFormed
return r.makeResponse(msg, 403, "Badly formed")
else
ErrLog( "advancedAuthenticateRequest return wrong value: "..result )
return r.makeResponse(msg,500)
end
end
end
return r.makeChallenge(msg, app.getMyRealm(), false)
end
function processSipMessage( msg )
InfoLog( "processSipMessage" )
if msg:method() ~= "REGISTER" or msg:isResponse() then
return
end
assert( msg:isRequest() )
local aor = msg:header(h_To):uri():getAorAsUri()
local aorString = tostring( aor )
if aor:scheme() ~= "sip" then
local failure = r.makeResponse( msg, 400 )
stack:send(failure)
return
end
local response = authenticate(msg)
if response then
stack:send(response)
return
end
local globalExpire
if msg:exists(h_Expires) and msg:header(h_Expires):isWellFormed() then
globalExpire = msg:header(h_Expires):value()
else
globalExpire = DEFAULT_EXPIRE
end
local now = os.time()
if not msg:exists(h_Contacts) then
--Query
local ok = r.makeResponse(msg,200)
if db[aorString] then
fixExpires(db[aorString],ok:header(h_Contacts),now)
stack:send(ok)
end
stack:send(ok)
return
end
local contacts = msg:header(h_Contacts)
local done, err = contacts:iterate( function( item )
DebugLog( "iterate: "..tostring(item) )
if not item:isWellFormed() then
local failure = r.makeResponse( msg, 400, "Mailformed contact" )
stack:send(failure)
return 1
end
local expires
if item:exists(p_expires) then
expires = item:param(p_expires):value()
else
expires = globalExpire
end
--Check for "Contact: *" style deregistration
if item:isAllContacts() then
if contacts:size() > 1 or expires ~= 0 then
local failure = r.makeResponse( msg, 400, "Invalid use of 'Contact: *'" )
stack:send(failure)
return 1
end
db[aorString] = nil
return
end
if expires == 0 then
removeContact(aorString,item:uri())
return
end
updateContact(aorString,item:uri(),expires+now)
end )
if done then
InfoLog("stop")
assert( not err ) --ToDo - send 500??!!
return
end
local ok = r.makeResponse(msg,200)
if db[aorString] then
fixExpires(db[aorString],ok:header(h_Contacts),now)
else
ok:remove(h_Contacts)
end
stack:send(ok)
end
function processAppMessage(msg)
ErrLog( "processAppMessage: Why we are here?" )
end
function processTermMessage( msg )
end
function init( stack_, params )
stack = stack_
end
function fini()
end
