No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smd-server 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #! /usr/bin/env lua5.1
  2. --
  3. -- Released under the terms of GPLv3 or at your option any later version.
  4. -- No warranties.
  5. -- Copyright Enrico Tassi <gares@fettunta.org>
  6. require 'syncmaildir'
  7. -- export to the global namespace all symbols
  8. for k,v in pairs(syncmaildir) do _G[k] = v end
  9. -- ============================= MAIN =====================================
  10. function main()
  11. -- sanity checks
  12. assert_exists(MDDIFF)
  13. assert_exists(XDELTA)
  14. -- argument parsing
  15. local exclude = {}
  16. local no_delete = false
  17. local no_move = false
  18. local just_mddiff = false
  19. local stop_after_diff = false
  20. local override_db = nil
  21. local dump_stdin_to = nil
  22. local usage = "Usage: "..arg[0]:match('[^/]+$')..
  23. " [-vdn] endpointname mailboxes...\n"
  24. while #arg > 2 do
  25. if arg[1] == '-v' or arg[1] == '--verbose' then
  26. set_verbose(true)
  27. table.remove(arg,1)
  28. elseif arg[1] == '-d' or arg[1] == '--dry-run' then
  29. set_dry_run(true)
  30. table.remove(arg,1)
  31. elseif arg[1] == '-n' or arg[1] == '--no-delete' then
  32. no_delete = true
  33. table.remove(arg,1)
  34. elseif arg[1] == '--no-move' then
  35. no_move = true
  36. table.remove(arg,1)
  37. elseif arg[1] == '--exclude' then
  38. exclude[#exclude + 1] = arg[1]
  39. exclude[#exclude + 1] = arg[2]
  40. table.remove(arg,1)
  41. table.remove(arg,1)
  42. elseif arg[1] == '--get-mddiff-cmdline' then
  43. just_mddiff = true
  44. table.remove(arg,1)
  45. elseif arg[1] == '--override-db' then
  46. override_db = arg[2]
  47. table.remove(arg,1)
  48. table.remove(arg,1)
  49. elseif arg[1] == '--stop-after-diff' then
  50. stop_after_diff = true
  51. table.remove(arg,1)
  52. elseif arg[1] == '--dump-stdin' then
  53. dump_stdin_to = arg[2]
  54. table.remove(arg,1)
  55. table.remove(arg,1)
  56. else
  57. break
  58. end
  59. end
  60. if #arg < 2 then
  61. io.stderr:write(usage)
  62. os.exit(2)
  63. end
  64. if dump_stdin_to ~= nil then
  65. dump_stdin_to = dump_stdin_to:gsub('^~',os.getenv('HOME'))
  66. mkdir_p(dump_stdin_to)
  67. local f = io.open(dump_stdin_to,'w')
  68. local data = nil
  69. repeat
  70. data = io.read(4096)
  71. if data then f:write(data) end
  72. until data == nil
  73. f:close()
  74. os.exit(0)
  75. end
  76. local endpoint = arg[1]
  77. table.remove(arg,1)
  78. local dbfile = nil
  79. if override_db ~= nil then
  80. dbfile = override_db:gsub('^~',os.getenv('HOME'))
  81. else
  82. dbfile = dbfile_name(endpoint, arg)
  83. end
  84. local xdelta = dbfile .. '.xdelta'
  85. local dbfilemt = dbfile .. '.mtime'
  86. local newdb = dbfile .. '.new'
  87. local newdbmt = dbfilemt .. '.new'
  88. local database_opt = '--db-file '.. dbfile
  89. local mailbox_opt = table.concat(arg,' ')
  90. local no_delete_opt = ''
  91. if no_delete then no_delete_opt = '-n' end
  92. local no_move_opt = ''
  93. if no_move then no_move_opt = '--no-move' end
  94. local dry_opt = ''
  95. if dry_run() then dry_opt = '-d' end
  96. local exclude_opt = table.concat(exclude, ' ')
  97. local mddiff = MDDIFF..' '..dry_opt..' '..database_opt..' '..
  98. exclude_opt..' '..no_delete_opt..' '..no_move_opt..' '..
  99. mailbox_opt
  100. -- to call mddiff from another tool with the same cmdline
  101. if just_mddiff then
  102. print(mddiff)
  103. os.exit(0)
  104. end
  105. -- we check the protocol version and dbfile fingerprint
  106. handshake(dbfile)
  107. -- run mddiff and send the output to the client
  108. local r = io.popen(mddiff,"r")
  109. local sent = 0
  110. while true do
  111. local l = r:read("*l")
  112. if l ~= nil then
  113. sent = sent + 1
  114. io.write(l,'\n')
  115. else
  116. break
  117. end
  118. end
  119. r:close()
  120. -- end of the first phase, now the client should
  121. -- apply the diff eventually asking for the transmission
  122. -- of some data
  123. io.write('END\n')
  124. io.flush()
  125. -- if renaming mode, we exit
  126. if stop_after_diff then
  127. if override_db ~= nil then
  128. os.remove(dbfile)
  129. os.remove(dbfilemt)
  130. os.remove(newdb)
  131. os.remove(newdbmt)
  132. end
  133. os.exit(0)
  134. end
  135. -- process client commands
  136. while true do
  137. local l = io.read('*l')
  138. if l == nil then
  139. -- end of input stream, client is dead
  140. log_error('Communication with client died unexpectedly\n')
  141. os.exit(3)
  142. end
  143. if l:match('^COMMIT$') then
  144. -- the client applied the diff, the new mailbox
  145. -- fingerprint should be used for the next sync
  146. local rc
  147. if not dry_run() then
  148. rc = os.execute(
  149. XDELTA..' delta '..dbfile..' '..newdb..' '..xdelta)
  150. else
  151. local f = io.open(xdelta,'w')
  152. f:close()
  153. rc = 0 -- there is no newdb if --dry-run is given
  154. end
  155. if rc ~= 0 and rc ~= 256 then
  156. log_error('Failed running `xdelta delta` on db file: '..rc)
  157. os.exit(4)
  158. end
  159. transmit(io.stdout, xdelta, "all")
  160. os.remove(xdelta)
  161. elseif l:match('^DONE$') then
  162. if not dry_run() then os.rename(newdb, dbfile) end
  163. if not dry_run() then os.rename(newdbmt, dbfilemt) end
  164. os.exit(0)
  165. elseif l:match('^ABORT$') then
  166. -- the client failed in applying the diff
  167. log_error('Client aborted, removing '..
  168. newdb..' and '..newdbmt..'\n')
  169. if not dry_run() then os.remove(newdb) end
  170. if not dry_run() then os.remove(newdbmt) end
  171. os.exit(5)
  172. elseif l:match('^GET ') then
  173. local path = parse(l, '^GET (%S.*)$')
  174. transmit(io.stdout, path, "all")
  175. elseif l:match('^GETHEADER ') then
  176. local path = parse(l, '^GETHEADER (%S.*)$')
  177. transmit(io.stdout, path, "header")
  178. elseif l:match('^GETBODY ') then
  179. local path = parse(l, '^GETBODY (%S.*)$')
  180. transmit(io.stdout, path, "body")
  181. else
  182. -- protocol error
  183. log_error('Invalid command '..l..'\n')
  184. os.exit(6)
  185. end
  186. end
  187. end
  188. -- no more globals
  189. set_strict()
  190. -- parachute for error
  191. parachute(main, 7)
  192. -- vim:set ts=4: