#! /usr/bin/env lua5.1
-- 
-- Released under the terms of GPLv3 or at your option any later version.
-- No warranties.
-- Copyright 2008-2010 Enrico Tassi <gares@fettunta.org>

require 'syncmaildir'

-- export to the global namespace all symbols
for k,v in pairs(syncmaildir) do _G[k] = v end

-- ============================= MAIN =====================================

function main()
	-- sanity checks
	assert_exists(MDDIFF)
	assert_exists(XDELTA)
	assert_exists(SHA1SUM)

	-- argument parsing
	local usage = "Usage: "..arg[0].." [-vd] endpointname mailboxes...\n"
	while #arg > 2 do
		if arg[1] == '-v' or arg[1] == '--verbose' then
			set_verbose(true)
			table.remove(arg,1)
		elseif arg[1] == '-d' or arg[1] == '--dry-run' then
			set_dry_run(true)
			table.remove(arg,1)
		else
			break
		end
	end
	
	if #arg < 2 then
		io.stderr:write(usage)
		os.exit(2)
	end

	local endpoint = arg[1]
	table.remove(arg,1)
	local dbfile = dbfile_name(endpoint, arg)
	local xdelta = dbfile .. '.xdelta'
	local dbfilemt = dbfile .. '.mtime'
	local newdb = dbfile .. '.new'
	local newdbmt = dbfilemt .. '.new'

	local database_opt = '--db-file '.. dbfile
	local mailbox_opt = table.concat(arg,' ')
	local dry_opt = ''
	if dry_run() then dry_opt = '-d' end

	-- we check the protocol version and dbfile fingerprint
	handshake(dbfile)
	
	-- run mddiff and send the output to the client
	local mddiff = MDDIFF..' '..dry_opt..' '..database_opt..' '..mailbox_opt
	local r = io.popen(mddiff,"r")
	local sent = 0
	while true do
		local l = r:read("*l")
		if l ~= nil then
			sent = sent + 1
			io.write(l,'\n')
		else
			break
		end
	end
	r:close()
	
	-- end of the first phase, now the client should
	-- apply the diff eventually asking for the transmission
	-- of some data
	io.write('END\n')
	io.flush()
	
	-- process client commands
	while true do
		local l = io.read('*l')
		if l == nil then 
			-- end of input stream, client is dead
			log_error('Communication with client died unexpectedly\n')
			os.exit(3)
		end
		if l:match('^COMMIT$') then
			-- the client applied the diff, the new mailbox
			-- fingerprint should be used for the next sync
			local rc
			if not dry_run() then
				rc = os.execute(
					XDELTA..' delta '..dbfile..' '..newdb..' '..xdelta)
			else
				local f = io.open(xdelta,'w')
				f:close()
				rc = 0 -- there is no newdb if --dry-run is given
			end
			if rc ~= 0 and rc ~= 256 then
				log_error('Failed running `xdelta delta` on db file: '..rc)
				os.exit(4)
			end
			transmit(io.stdout, xdelta, "all")
			os.remove(xdelta)
		elseif l:match('^DONE$') then
			if not dry_run() then os.rename(newdb, dbfile) end 
			if not dry_run() then os.rename(newdbmt, dbfilemt) end 
			os.exit(0)
		elseif l:match('^ABORT$') then
			-- the client failed in applying the diff
			log_error('Client aborted, removing '..
				newdb..' and '..newdbmt..'\n')
			if not dry_run() then os.remove(newdb) end
			if not dry_run() then os.remove(newdbmt) end
			os.exit(5)
		elseif l:match('^GET ') then
			local path = parse(l, '^GET (%S+)$')
			transmit(io.stdout, path, "all")
		elseif l:match('^GETHEADER ') then
			local path = parse(l, '^GETHEADER (%S+)$')
			transmit(io.stdout, path, "header")
		elseif l:match('^GETBODY ') then
			local path = parse(l, '^GETBODY (%S+)$')
			transmit(io.stdout, path, "body")
		else
			-- protocol error
			log_error('Invalid command '..l..'\n')
			os.exit(6)
		end
	end
end

-- no more globals
set_strict()

-- parachute for error
parachute(main, 7)

-- vim:set ts=4:
