local ipairs , pairs = ipairs , pairs ;
local setmetatable = setmetatable ;
local tostring = tostring ;
local next = next ;
local t_remove = table.remove ;
local os_remove = os.remove ;
local io_open = io.open ;
local paths = require " util.paths " ;
local st = require " util.stanza " ;
local parse_xml_real = require " util.xml " . parse ;
local function getXml ( user , host )
local jid = user .. " @ " .. host ;
local path = paths.join ( prosody.paths . data , jid .. " .xml " ) ;
local f = io_open ( path ) ;
if not f then return ; end
local s = f : read ( " *a " ) ;
f : close ( ) ;
return parse_xml_real ( s ) ;
end
local function setXml ( user , host , xml )
local jid = user .. " @ " .. host ;
local path = paths.join ( prosody.paths . data , jid .. " .xml " ) ;
local f , err = io_open ( path , " w " ) ;
if not f then return f , err ; end
if xml then
local s = tostring ( xml ) ;
f : write ( s ) ;
f : close ( ) ;
return true ;
else
f : close ( ) ;
return os_remove ( path ) ;
end
end
local function getUserElement ( xml )
if xml and xml.name == " server-data " then
local host = xml.tags [ 1 ] ;
if host and host.name == " host " then
local user = host.tags [ 1 ] ;
if user and user.name == " user " then
return user ;
end
end
end
end
local function createOuterXml ( user , host )
return st.stanza ( " server-data " , { xmlns = ' urn:xmpp:pie:0 ' } )
: tag ( " host " , { jid = host } )
: tag ( " user " , { name = user } ) ;
end
local function removeFromArray ( array , value )
for i , item in ipairs ( array ) do
if item == value then
t_remove ( array , i ) ;
return ;
end
end
end
local function removeStanzaChild ( s , child )
removeFromArray ( s.tags , child ) ;
removeFromArray ( s , child ) ;
end
local handlers = { } ;
-- In order to support mod_auth_internal_hashed
local extended = " http://prosody.im/protocol/extended-xep0227 \1 " ;
handlers.accounts = {
get = function ( self , user )
user = getUserElement ( getXml ( user , self.host ) ) ;
if user and user.attr . password then
return { password = user.attr . password } ;
elseif user then
local data = { } ;
for k , v in pairs ( user.attr ) do
if k : sub ( 1 , # extended ) == extended then
data [ k : sub ( # extended + 1 ) ] = v ;
end
end
return data ;
end
end ;
set = function ( self , user , data )
if data then
local xml = getXml ( user , self.host ) ;
if not xml then xml = createOuterXml ( user , self.host ) ; end
local usere = getUserElement ( xml ) ;
for k , v in pairs ( data ) do
if k == " password " then
usere.attr . password = v ;
else
usere.attr [ extended .. k ] = v ;
end
end
return setXml ( user , self.host , xml ) ;
else
return setXml ( user , self.host , nil ) ;
end
end ;
} ;
handlers.vcard = {
get = function ( self , user )
user = getUserElement ( getXml ( user , self.host ) ) ;
if user then
local vcard = user : get_child ( " vCard " , ' vcard-temp ' ) ;
if vcard then
return st.preserialize ( vcard ) ;
end
end
end ;
set = function ( self , user , data )
local xml = getXml ( user , self.host ) ;
local usere = xml and getUserElement ( xml ) ;
if usere then
local vcard = usere : get_child ( " vCard " , ' vcard-temp ' ) ;
if vcard then
removeStanzaChild ( usere , vcard ) ;
elseif not data then
return true ;
end
if data then
vcard = st.deserialize ( data ) ;
usere : add_child ( vcard ) ;
end
return setXml ( user , self.host , xml ) ;
end
return true ;
end ;
} ;
handlers.private = {
get = function ( self , user )
user = getUserElement ( getXml ( user , self.host ) ) ;
if user then
local private = user : get_child ( " query " , " jabber:iq:private " ) ;
if private then
local r = { } ;
for _ , tag in ipairs ( private.tags ) do
r [ tag.name .. " : " .. tag.attr . xmlns ] = st.preserialize ( tag ) ;
end
return r ;
end
end
end ;
set = function ( self , user , data )
local xml = getXml ( user , self.host ) ;
local usere = xml and getUserElement ( xml ) ;
if usere then
local private = usere : get_child ( " query " , ' jabber:iq:private ' ) ;
if private then removeStanzaChild ( usere , private ) ; end
if data and next ( data ) ~= nil then
private = st.stanza ( " query " , { xmlns = ' jabber:iq:private ' } ) ;
for _ , tag in pairs ( data ) do
private : add_child ( st.deserialize ( tag ) ) ;
end
usere : add_child ( private ) ;
end
return setXml ( user , self.host , xml ) ;
end
return true ;
end ;
} ;
-----------------------------
local driver = { } ;
function driver : open ( datastore , typ )
local handler = handlers [ datastore ] ;
if not handler then return nil , " unsupported-datastore " ; end
local instance = setmetatable ( { host = module.host ; datastore = datastore ; } , { __index = handler } ) ;
if instance.init then instance : init ( ) ; end
return instance ;
end
module : provides ( " storage " , driver ) ;