6 /* http://cr.yp.to/proto/maildir.html */
16 /* Sanely change the info of a message */
17 const changeinfo : (msg : byte[:], i : info -> std.result(byte[:], byte[:]))
19 /* Unlink old messages in tmp/, messages marked 'T' */
20 const cleanfs : (dirs : byte[:][:] -> std.result(void, byte[:]))
22 /* Get the info flags from the info field of a filename */
23 const info : (msg : byte[:] -> info)
25 /* List maildirs (rel paths) under dir. Result must be deep-free'd */
26 const maildirs : (dir : byte[:] -> std.result(byte[:][:], byte[:]))
28 /* List messages (rel paths) under dir. Result must be deep-free'd */
29 const messages : (dir : byte[:] -> std.result(byte[:][:], byte[:]))
31 /* Sanely move a message around */
32 const movemessage : (msg : byte[:], dir : byte[:] -> std.result(void, byte[:]))
35 const infofmt = {s : std.strbuf#, ap : std.valist#, opts : (byte[:], byte[:])[:] -> void
36 var i : info = std.vanext(ap)
64 var i : info = [.passed = true]
65 std.fmtinstall(std.typeof(i), infofmt)
68 const ismaildir = {dir : byte[:] -> bool
69 var s : std.strbuf# = std.mksb()
71 std.sbfmt(s, "{}", dir)
74 m = m && std.fisdir(std.sbpeek(s))
76 std.sbtrim(s, dir.len)
78 m = m && std.fisdir(std.sbpeek(s))
80 std.sbtrim(s, dir.len)
82 m = m && std.fisdir(std.sbpeek(s))
87 /* Sanely change the info of a message */
88 const changeinfo = {msg : byte[:], i : info
90 var base : byte[:] = [][:]
91 var newname : byte[:] = [][:]
92 var s : std.strbuf# = std.mksb()
93 var err : std.strbuf# = std.mksb()
95 for j = msg.len - 1; j >= 0; --j
96 if msg[j] == (':' : byte)
100 if msg[j] == ('/' : byte)
111 std.sbfmt(s, "{}:2,{}", base, i)
112 newname = std.sbpeek(s)
114 match sys.link(sys.cstring(msg), sys.cstring(newname))
117 std.sbfmt(err, "link(\"{}\", \"{}\"): {}", msg, newname, e)
121 match sys.unlink(msg)
124 std.sbfmt(err, "unlink(\"{}\"): {}", msg, e)
132 -> `std.Ok std.sbfin(s)
135 -> `std.Err std.sbfin(err)
140 /* Cleanup messages older than 36 hrs in tmp, */
141 const cleanfs = { dirs : byte[:][:]
142 var err : std.strbuf# = std.mksb()
143 var thisdir : byte[:] = [][:]
144 var thisfile : byte[:] = [][:]
145 var tokill : byte[:][:] = std.slalloc(0)
146 var cutoff : std.time = std.now() - (36 * 60 * 60 * std.Sec)
153 thisdir = std.pathcat(p, "tmp")
155 match std.diropen(thisdir)
157 std.sbfmt(err, "cannot open {}: {}", thisdir, e)
166 if std.eq(f, ".") || std.eq(f, "..")
171 thisfile = std.pathcat(thisdir, f)
173 match std.fmtime(thisfile)
175 if t * std.Msec < cutoff
176 std.slpush(&tokill, thisfile)
180 std.sbfmt(err, "cannot stat {}: {}", thisfile, e)
188 thisdir = std.pathcat(p, "cur")
190 match std.diropen(thisdir)
192 std.sbfmt(err, "cannot open {}: {}", thisdir, e)
202 thisfile = std.pathcat(thisdir, f)
204 if info(thisfile).trashed
205 std.slpush(&tokill, thisfile)
232 | _: -> `std.Err std.sbfin(err)
236 /* Parse a message filename for info flags */
237 const info = {msg : byte[:]
248 for j = msg.len - 1; j >= 0; --j
249 if (msg[j] : char) == ':'
254 if j >= msg.len - 3 || j <= 0
258 if !std.sleq(msg[j:j+3], ":2,")
264 | 'P': i.passed = true
265 | 'R': i.replied = true
267 | 'T': i.trashed = true
268 | 'D': i.draft = true
269 | 'F': i.flagged = true
277 /* Returns immediate subdirs that meet the maildir(5) format */
278 const maildirs = {dir : byte[:] -> std.result(byte[:][:], byte[:])
279 var m : byte[:][:] = std.slalloc(0)
280 var p : byte[:] = [][:]
281 var err : std.strbuf# = std.mksb()
283 match std.diropen(dir)
285 std.sbfmt(err, "cannot open {}: {}", dir, e)
294 p = std.pathcat(dir, e)
309 -> `std.Ok std.sort(m, std.strcmp)
316 -> `std.Err std.sbfin(err)
320 /* Returns things that might be messages */
321 const messages = {dir : byte[:] -> std.result(byte[:][:], byte[:])
322 var m : byte[:][:] = std.slalloc(0)
323 var err : std.strbuf# = std.mksb()
324 var thisdir : byte[:] = [][:]
327 std.sbfmt(err, "{} is not a maildir", dir)
331 for subdir : [ "cur", "new" ][:]
333 thisdir = std.pathcat(dir, subdir)
335 match std.diropen(thisdir)
337 std.sbfmt(err, "cannot open {}: {}", thisdir, e)
345 if e.len > 0 && e[0] == ('.' : byte)
350 std.slpush(&m, std.pathcat(thisdir, e))
365 -> `std.Ok std.sort(m, std.strcmp)
372 -> `std.Err std.sbfin(err)
376 const movemessage = {msg : byte[:], dir : byte[:]
377 var err : std.strbuf# = std.mksb()
378 var basename : byte[:] = msg
379 var newname : byte[:] = [][:]
383 std.sbfmt(err, "{} is not a maildir", dir)
387 for j = msg.len - 1; j >= 0; --j
388 if msg[j] == ('/' : byte)
394 newname = std.pathjoin([dir, "cur", basename][:])
396 /* TODO: once the syscalls get redone, this can be simpler */
397 match sys.link(sys.cstring(msg), sys.cstring(newname))
400 std.sbfmt(err, "link(\"{}\", \"{}\"): {}", msg, newname, e)
404 match sys.unlink(msg)
407 std.sbfmt(err, "unlink(\"{}\"): {}", msg, e)
417 | _: -> `std.Err std.sbfin(err)