123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- #! /usr/bin/env lua5.1
- --
- -- Released under the terms of GPLv3 or at your option any later version.
- -- No warranties.
- -- Copyright 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)
-
- -- argument parsing
- local exclude = {}
- local no_delete = false
- local no_move = false
- local just_mddiff = false
- local stop_after_diff = false
- local override_db = nil
- local dump_stdin_to = nil
- local usage = "Usage: "..arg[0]:match('[^/]+$')..
- " [-vdn] 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)
- elseif arg[1] == '-n' or arg[1] == '--no-delete' then
- no_delete = true
- table.remove(arg,1)
- elseif arg[1] == '--no-move' then
- no_move = true
- table.remove(arg,1)
- elseif arg[1] == '--exclude' then
- exclude[#exclude + 1] = arg[1]
- exclude[#exclude + 1] = arg[2]
- table.remove(arg,1)
- table.remove(arg,1)
- elseif arg[1] == '--get-mddiff-cmdline' then
- just_mddiff = true
- table.remove(arg,1)
- elseif arg[1] == '--override-db' then
- override_db = arg[2]
- table.remove(arg,1)
- table.remove(arg,1)
- elseif arg[1] == '--stop-after-diff' then
- stop_after_diff = true
- table.remove(arg,1)
- elseif arg[1] == '--dump-stdin' then
- dump_stdin_to = arg[2]
- table.remove(arg,1)
- table.remove(arg,1)
- else
- break
- end
- end
-
- if #arg < 2 then
- io.stderr:write(usage)
- os.exit(2)
- end
-
- if dump_stdin_to ~= nil then
- dump_stdin_to = dump_stdin_to:gsub('^~',os.getenv('HOME'))
- mkdir_p(dump_stdin_to)
- local f = io.open(dump_stdin_to,'w')
- local data = nil
- repeat
- data = io.read(4096)
- if data then f:write(data) end
- until data == nil
- f:close()
- os.exit(0)
- end
-
- local endpoint = arg[1]
- table.remove(arg,1)
- local dbfile = nil
- if override_db ~= nil then
- dbfile = override_db:gsub('^~',os.getenv('HOME'))
- else
- dbfile = dbfile_name(endpoint, arg)
- end
- 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 no_delete_opt = ''
- if no_delete then no_delete_opt = '-n' end
- local no_move_opt = ''
- if no_move then no_move_opt = '--no-move' end
- local dry_opt = ''
- if dry_run() then dry_opt = '-d' end
- local exclude_opt = table.concat(exclude, ' ')
- local mddiff = MDDIFF..' '..dry_opt..' '..database_opt..' '..
- exclude_opt..' '..no_delete_opt..' '..no_move_opt..' '..
- mailbox_opt
-
- -- to call mddiff from another tool with the same cmdline
- if just_mddiff then
- print(mddiff)
- os.exit(0)
- end
-
-
- -- we check the protocol version and dbfile fingerprint
- handshake(dbfile)
-
- -- run mddiff and send the output to the client
- 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()
-
- -- if renaming mode, we exit
- if stop_after_diff then
- if override_db ~= nil then
- os.remove(dbfile)
- os.remove(dbfilemt)
- os.remove(newdb)
- os.remove(newdbmt)
- end
- os.exit(0)
- end
-
- -- 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:
|