Compare commits

..

181 commits

Author SHA1 Message Date
79e19d3d15
add a very limited INCLUDE_ONLY option 2023-12-04 15:23:04 -08:00
e0690b07eb
add docker-based build 2023-12-04 15:22:21 -08:00
6faf91a806
add an ignore filter based on substring of the file 2023-11-09 12:26:33 -08:00
Oswald Buddenhagen
bb5e98e9ec bump version 2021-12-03 11:56:16 +01:00
Oswald Buddenhagen
f2b1e80033 modernize configure.ac 2021-12-03 11:56:16 +01:00
Oswald Buddenhagen
e686f88318 don't complain about concurrent flagging as deleted
the result of propagating a deletion is flagging as deleted, so shut up
if the only remote change is exactly that.
2021-12-03 11:56:16 +01:00
Oswald Buddenhagen
51673214ab fix read beyond end of input in copy_msg_convert()
the input isn't necessarily null-terminated (it currently is for imap,
but not for maildir), so if the message ended somewhere within the
header field name, we'd read beyond its end, which theoretically could
cause a crash. no other adverse effects could result, as we'd stop
processing such a broken message right afterwards.

amends 70bad661.
2021-12-03 11:46:33 +01:00
Oswald Buddenhagen
127003ee37 reject unreasonably long mailbox names from IMAP LIST
this wasn't really a security problem, as the name mapping we actually
do does not change the string length, and the iteration was already
safe after the literal length fix, but it's still better to catch weird
input.
2021-12-01 10:07:40 +01:00
Oswald Buddenhagen
92921b1d3b reject messages that grow too large due to conversion
that shouldn't really be a problem, as we have 2GB of headroom, and most
growth would happen when sending an all-newlines message from maildir to
imap (due to CR additions), which is mostly non-critical. but better
safe than sorry.
2021-12-01 10:07:40 +01:00
Oswald Buddenhagen
bc15e571b6 report conversion errors directly in copy_msg_convert()
that makes it easier to report various conditions without introducing
separate error codes.
2021-12-01 10:07:40 +01:00
Oswald Buddenhagen
ba13362a52 deal with oversized messages in maildirs
don't try to read messages > 2G, as that will only lead to trouble down
the line.

this wouldn't have worked on linux anyway (we read in one chunk, and
that is limited to (2^31 - 2^12) on all architectures), but on
platforms were big reads work, this was a security problem if one
synchronized other users' maildirs.

as a minor fix on the side, we now also clip the reported message size,
so MaxSize works for excessively big messages.
2021-12-01 10:07:40 +01:00
Oswald Buddenhagen
463272eab8 CVE-2021-3657: reject excessively large IMAP literals
we didn't limit the 32-bit size of literals so far, which, given that we
use int-sized lengths & offsets, permitted all kinds of buffer
overflows. malicious/compromised servers may have been able to exploit
this. actual email senders would be constrained by size limits for
delivered mails, and to cause more than a crash they'd have to predict
the exact size of the final message.

we now limit to 2GB, which, given that we use unsigned ints since
e2d3b4d55 (v1.4.0), gives the handlers downstream plenty of headroom.

an alternative would have been using 64-bit offsets, but this seems like
major overkill, even if IMAP4rev2 recently mandated it (we talk only
IMAP4rev1, so we can ignore it).
2021-12-01 10:07:24 +01:00
Oswald Buddenhagen
87065c12b4 CVE-2021-44143: don't overflow heap on messages without headers
when a broken/compromised/malicious server gives us a message that
starts with an empty line, we'd enter the path for inserting a pristine
placeholder subject, for which we unfortunately didn't actually allocate
space (unless MaxSize is in use and the message exceeds it).

note that this cannot be triggered by merely receiving a crafted mail
with no headers (yes, it's actually possible to send such a thing), as
the delivery of mails adds plenty of headers.

amends 70bad661.
2021-11-25 16:14:32 +01:00
Oswald Buddenhagen
6e5dc6c2f2 bump version 2021-07-29 13:14:24 +02:00
Oswald Buddenhagen
7979782676 limit maildir nesting depth
this is a cheap way to catch symlink loops. 10 seems like a reasonable
limit, as it's unlikely that anyone would be able to actually work with
such a deeply nested mailbox tree.

fixes debian bug #990117.
2021-07-29 13:14:18 +02:00
Oswald Buddenhagen
a846ab054d enable embedding arbitrarily long strings into IMAP commands
the AUTHENTICATE command may get insanely long for GSSAPI when SASL-IR
is available. instead of growing the buffers each time someone hits the
limit (as done in f7cec306), remove the limitation altogether.

imap_vprintf() still contains a fixed-size buffer which could overflow
when really long strings (e.g., mailbox names) need to be quoted. this
seems very unlikely, so we'll deal with it if someone actually hits it.

REFMAIL: 87sg1qxdye.fsf@cern.ch
2021-06-11 18:24:00 +02:00
Oswald Buddenhagen
da65672f08 bump version 2021-06-03 11:07:35 +02:00
Oswald Buddenhagen
444601a1e0 Merge branch '1.3' into 1.4
Conflicts:
	configure.ac
	src/drv_imap.c
2021-06-03 11:04:56 +02:00
Oswald Buddenhagen
ed3bfdac4a bump version 2021-06-03 11:02:40 +02:00
Oswald Buddenhagen
589d2ed428 CVE-2021-3578: fix handling of unexpected APPENDUID response code
if the code was sent in response to anything but a STORE, we'd overwrite
a data pointer in one of our imap_cmd subclasses, an allocator data
structure, or the start of the next allocation, with an int that was
completely under the server's control. it's plausible that this could be
exploited for remote code execution.

to avoid this, we could ensure that the object is of the right type
prior to casting, by using a new flag in the parameter block. but it's
easier to just dispose of the out_uid field altogether and reuse the uid
field that is present in the parameter block anyway, but was used only
for FETCH commands so far.

this problem was found by Lukas Braun <koomi@moshbit.net> using a
fuzzer.
2021-06-03 11:02:23 +02:00
Oswald Buddenhagen
a86e6f8c7c don't crash on malformed CAPABILITY responses
amends 95a83c822.

this problem was found by Lukas Braun <koomi@moshbit.net> using a
fuzzer.
2021-06-02 15:51:23 +02:00
Oswald Buddenhagen
d8feb67dae tolerate INBOX mis-casing in Path
while it's technically reasonable to expect the user to match the
server's casing of INBOX if they set Path, this might come as a
surprise to those who know that the IMAP INBOX is case-insensitive.
so tolerate any casing instead. as a minor side effect, we'd now even be
able to deal with a server using different casing in NAMESPACE and LIST.
2021-03-19 18:21:34 +01:00
Oswald Buddenhagen
4b185e35fe Merge branch '1.3' into 1.4
Conflicts:
	configure.ac
	src/drv_imap.c
2021-02-21 21:26:54 +01:00
Oswald Buddenhagen
d55ced04ed bump version 2021-02-21 21:24:48 +01:00
Oswald Buddenhagen
594e60bd74 make UIDVALIDITY recovery more strict about vanished messages
in particular, this covers the case of a mailbox being replaced with an
empty new one, which would subsequently lead to the opposite end being
emptied as well, which would typically be undesired.

also add plenty of comments.
2021-02-21 21:11:58 +01:00
Oswald Buddenhagen
6796e041ae improve error messages about irrecoverably changed UIDVALIDITY
don't print the actual values, which are meaningless technicalities
to the average user, and can be obtained separately for debugging if
really necessary.
also, fix the omission of the affected mailboxes from one of the
messages.
2021-02-21 21:11:58 +01:00
Oswald Buddenhagen
fe5d59f8e3 CVE-2021-20247: reject funny mailbox names from IMAP LIST/LSUB
in particular, '..' in the name could be used to escape the Path/Inbox
of a Maildir Store, which could be exploited for stealing or deleting
data, or staging a (mild) DoS attack.
2021-02-21 20:40:22 +01:00
Oswald Buddenhagen
95a83c8220 be more tolerant of formally malformed response codes
fastmail sends flags containing ']' in PERMANENTFLAGS, which is formally
illegal. however, if we parse the embedded list before looking for the
response code's closing ']', things work out fine.

as a side effect we won't complain about similarly or completely
malformed response codes we don't recognize at all, which may or may not
be considered an improvement ...
2021-02-14 23:47:14 +01:00
Oswald Buddenhagen
8c86f34bf0 fix bogus continuation of IMAP list parsing
on error, parse_imap_list() needs to reset the nesting level in the
state, as imap_socket_read() uses that as an indicator whether list
parsing is ongoing.
2021-02-14 23:47:14 +01:00
Oswald Buddenhagen
32392adbe3 accept unsolicited FETCH responses (without payload) after all
while the spec says that the server SHOULD not send FETCH responses
about STORE FLAGS when .SILENT is used, at least gmail and fastmail seem
to do it nonetheless. also, in case of concurrent flag updates on the
affected messages such responses can be legitimately sent.

in earlier versions of mbsync this would lead to duplicate messages
piling up in the store, though that would pose no problem at that point.
2021-02-14 23:47:14 +01:00
Oswald Buddenhagen
9e3041de93 bump version 2021-02-14 23:47:14 +01:00
Nihal Jere
7a0ea1f15c use correct <poll.h> header
In POSIX, poll() should be accessible using <poll.h>, although most
implementations keep <sys/poll.h> to avoid breakage. This fixes some
warnings when building on musl.
2021-02-08 17:26:11 +01:00
Oswald Buddenhagen
062706fcbf Merge branch '1.3'
Conflicts:
	configure.ac
	src/drv_imap.c
2021-02-03 15:53:05 +01:00
Oswald Buddenhagen
e4eac03a9a bump version 2021-02-03 14:44:31 +01:00
Oswald Buddenhagen
c8b73acad2 unbreak handling of 'INBOX.' NAMESPACE again
INBOX matching must not prevent prefix (namespace) stripping, as INBOX
may be the namespace.

amends 04fc586e7.

REFMAIL: 186391612191752@vla1-ea7e194e8506.qloud-c.yandex.net
2021-02-03 14:43:11 +01:00
Anton Khirnov
fc300fd811 Set authentication id for the SASL EXTERNAL mechanism
The SASL library will refuse to use the EXTERNAL module when no auth id
is set a priori.

Tested to work with Dovecot, using TLS client certificates for
authentication.
2021-01-05 19:50:21 +01:00
Oswald Buddenhagen
e67cf01eb8 improve SASL error messages
provide context, and remove the redundant numeric codes.
2021-01-05 19:46:29 +01:00
Oswald Buddenhagen
c2e6e962b5 tune SASL-related comments
- add explanations to the callbacks
- remove bogus comment - EXTERNAL can be in fact missing (when no
  authentication id is set)
2021-01-05 19:46:29 +01:00
Oswald Buddenhagen
e295f483d9 save errno in sys_error()
the print functions prior to perror() might otherwise clobber it.
2021-01-01 14:46:31 +01:00
Oswald Buddenhagen
6e56f39fa9 autotest: remove unused boxname parameter from printbox() 2020-12-19 13:22:29 +01:00
Oswald Buddenhagen
9fbf5c2f6c autotest: pass containers by reference
this makes function prototypes a lot more useful for parameter checking.
2020-12-19 13:22:29 +01:00
Oswald Buddenhagen
4423a932f3 add forced async mode to proxy driver
to test async operation of the syncing core while using the synchronous
maildir driver, we add a mode to the proxy driver where it queues
callback invocations to the next main loop iteration.
2020-12-19 13:22:29 +01:00
Oswald Buddenhagen
be657530ee localize a variable more appropriately 2020-12-17 22:18:10 +01:00
Oswald Buddenhagen
30af61fb24 consolidate testing options behind common switch
don't pollute the namespace with random uppercase switches. instead,
have a new -T switch with suboptions, just like -D.
2020-12-17 22:18:10 +01:00
Oswald Buddenhagen
c3d91ae1e8 introduce new inheritance model based on C11 anonymous structs
the struct declarations got uglier, but their usage requires a lot fewer
explicit references to the parent struct (though some are added where
using the derived struct is more practical now).

we also use something i'd term "covariant members": derivatives of
store_t also reference derivatives of store_conf_t, etc., which
drastically cuts down the number of casts.
fwiw, to achieve this with "proper" inheritance in C++, we'd use
covariant getter functions which hide the still existing casts.

C11 is almost a decade old now, and compilers supported that feature
even longer than that, so i don't expect this to be a problem.
2020-12-17 22:18:10 +01:00
Oswald Buddenhagen
bf66f210bd add some error checking to proxy template processor
debugging is a lot easier when the unconsumed (and therefore likely
mistyped) replacements are complained about.
2020-12-17 22:18:10 +01:00
Oswald Buddenhagen
cd6f18fd2b handle indentation in proxy driver template code more flexibly
use the indentation of the placeholder, not the replacement.
this doesn't matter right now, as all placeholders are indented by one
step, but that will change soon.

the indent function cannot be inlined into the substitution, as for some
reason ^ then matches the end of the string, not the embedded line
starts (with perl v5.32). also, $1 needs to go into a temporary anyway.
2020-12-17 22:17:11 +01:00
Oswald Buddenhagen
ba7b634186 make FALLTHROUGH work with qtcreator's code model
the code model inspector claims that __GNUC__ is 10, but the #if works
only with >= 4, which is plain wrong. so just handle clang explicitly.
2020-12-16 13:42:40 +01:00
Oswald Buddenhagen
5b4766fbe4 improve docu for {Pass,User}Cmd 2020-12-12 14:56:27 +01:00
Oswald Buddenhagen
4ad82686f2 fix build with macOS keychain support
we use symbols from CoreFoundation directly, so we need to link it
explicitly.

amends 198ca65b.
2020-11-29 13:47:18 +01:00
Oswald Buddenhagen
dec4b36595 improve wording in man page 2020-10-05 13:50:23 +02:00
Oswald Buddenhagen
a9ce7be962 streamline init of type & name in imap_parse_store() 2020-10-05 13:15:28 +02:00
Oswald Buddenhagen
09341c10c5 make complaints about unrecognized keywords more verbose
tell the user in what section the keyword appeared, as that may help
spotting mistakes like stray empty lines.
2020-10-05 13:14:48 +02:00
Oswald Buddenhagen
217764bd35 complain about global options following sections
while harmless for most options, such usage is counter to the
documentation, and actually breaks CopyArrivalDate, MaxMessages, and
ExpireUnread.
2020-10-05 12:41:35 +02:00
Oswald Buddenhagen
ee39e684aa make exit from parsing Group sections less convoluted
this is a de-optimization, but it makes the code consistent with the
other sections (which do not use the shortcut due to having to
post-process the data or being encapsulated by a function call).
2020-10-05 12:31:14 +02:00
Oswald Buddenhagen
6463a72f12 remove the -cT option
it was another vestige from the compat wrapper.

amends cbac8aa75.
2020-10-05 11:56:30 +02:00
Oswald Buddenhagen
23513564df improve error handling in post-STORE UIDNEXT fallback
that's mostly hypothetical, but let's not make assumptions.

this also adds EXPUNGE response handling to make total_msgs reliable. in
principle, this affects the post-SELECT UIDNEXT fallback as well, but
there the racing window is so short that this barely improves anything.

amends 94022a67.
2020-08-24 12:51:47 +02:00
Oswald Buddenhagen
42f165ecf7 fix UIDNEXT query vs. concurrent imap_fetch_msg()
the uidnext query following message stores can be interleaved with
message fetches. that means that we cannot rely on the 1st command in
flight being that query. but instead of iterating over all commands in
flight, move the uidnext query flag to imap_store (and make sure to
check for the presence of a message body before testing it) - this
avoids the loop and an extra byte in every command.

this also makes it clear that the query is mutually exclusive with
loading messages (the untagged responses are not distinguishable).
2020-08-24 12:51:47 +02:00
Oswald Buddenhagen
f099141e42 make item tracking in parse_fetch_rsp() more uniform
amends 67ea5bea7 & a5a8783ea.
2020-08-24 12:51:42 +02:00
Oswald Buddenhagen
ec47c90554 delay allocation of msgdata.msgid field
this allows us to simplify the exit path of parse_fetch_rsp().
2020-08-05 17:59:28 +02:00
Oswald Buddenhagen
b37d6b1c00 fix invalid free() in error path
the tuid isn't actually allocated - it's a pointer into the raw data.

amends a5a8783e.
2020-08-05 17:36:35 +02:00
Oswald Buddenhagen
c69718baab remove redundant zero initializations
we already use calloc().

amends 130664b6.
2020-08-05 17:29:58 +02:00
Oswald Buddenhagen
b148fd9e44 de-duplicate exit paths of imap_alloc_store() 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
c83330ffe8 don't unnecessarily re-initialize some members of imap_store
... when recycling server connections.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
8457225a50 use more appropriate return value in driver_t::select_box()
don't say DRV_CANCELED when it's really DRV_STORE_BAD, as apart from
being just wrong, it lead to the confusing effect of canceling a store
as the result of a supposed cancellation of the same store.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
481c12a8b3 handle CertificateFile more cleanly
properly distribute the certificates between the SSL context's trust
store and our host cert list.

as a drive-by, clean up some nasty type casts at the cost of including
a second OpenSSL header into socket.h.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
cfaa4848dd actually implement imap_commit_cmds()
delay reporting success of STORE FLAGS until a subsequent CHECK
succeeds.

this fixes (inverse flag change propagation) and (deletes not being
propagated) after an interruption due to prematurely logged flag
updates.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
70bad66129 create placeholders for messages over MaxSize
this is vastly more useful than just omitting the messages with no
indication at all.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
68a412115a don't rewrite state gratuitously
delay the creation of the new state and journal until there is actually
something interesting to write. this saves some cpu cycles and prolongs
ssd life a whee bit.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
0e5046e14a add/fix/de-duplicate comments 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
fd7b5659ab de-duplicate updating of uid in sync records 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
e9efc49b6c do away with newmaxuid
now that expiration order is determined by a single loop ordered by
far-side UIDs, it is no longer necessary to accurately track the highest
seen UID.

as a side effect, this fixes a problem reported (way too long ago) by
Yuri D'Elia: we failed to up newmaxuid for messages we produced
ourselves, so we would keep enumerating the same messages until we also
propagated externally generated messages from that mailbox - which might
have been never for the server side of archive/trash mailboxes.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
de6dc699c9 make expiration loops solely far-side-driven
we can do that, as unpaired near-side messages are ignored anyway.

this mildly changes expiration order, as near-side messages that
existed for a long time but were propagated much later will be expired
later. however, that has no practical relevance.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
ca72383fe9 don't forget to skip dead messages on far side during expiration
this is mostly theoretical, as at this point no updates to the message
list can have actually happened. but it's future-proof and consistent
with the near-side loop.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
5d6741a9a8 streamline counting of currently pushed messages wrt. expiration
don't count them as alive just to ignore them in the next step.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
8df1f5dd64 re-nest conditions for syncing new messages
this makes the logic easier to follow and document in place.
also, make the comments actually match reality.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
ceb09fcd44 handle messages which are newly doomed after an interruption
we already didn't propagate messages which would be instantly expunged
from the target, but failed to cancel propagations that were already
scheduled before we got interrupted. this matters a bit when the
resumption happens significantly later than the initial attempt, giving
the user time to mark messages on the source as deleted.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
4aaada18e9 remove redundant condition
the 'pending' and 'skipped' sync record states are mutually exclusive
with having a complementary message, so there is no point in testing it
explicitly.

amends bd5fb6ff.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
395f802500 fix loading of some messages' sizes in some partial sync scenarios
we need to pass a different "boundary" UID to driver_t::load_box() for
every OPEN_* flag that queries a partial range:
- OPEN_FIND refers to messages newer than all we know about
- OPEN_OLD_IDS refers to messages which are paired
- OPEN_{OLD,NEW}_SIZE refers to messages (not) above the committed
  boundary of already propagated messages

we treated the 3rd like the 2nd, which was just wrong - the actual
boundary may be lower or higher, so we'd produce wrong results when
MaxSize was set and only one of New and ReNew was requested.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
c8f402e43f deprecate master/slave terminology
the underlying metaphor refers to an inhumane practice, so using it
casually is rightfully offensive to many people. it isn't even a
particularly apt metaphor, as it suggests a strict hierarchy that is
counter to mbsync's highly symmetrical mode of operation.

the far/near terminology has been chosen as the replacement, as it is a
natural fit for the push/pull terminology. on the downside, due to these
not being nouns, a few uses are a bit awkward, and several others had to
be amended to include 'side'. also, it's conceptually quite close to
remote/local, which matches the typical use case, but is maybe a bit too
suggestive of actually non-existing limitations.

the new f/n suffixes of the -C/-R/-X options clash with pre-existing
options, so direct concatenation of short options is even less practical
than before (some suffixes of -D already clashed), but doing that leads
to unreadable command lines anyway.

as with previous deprecations, all pre-existing command line and config
options keep working, but yield a warning. the state files are silently
upgraded.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
b514d9ddbc purge handling of pending sync entries from state file
these cannot actually end up in the committed state.

amends bd5fb6ff.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
d93726067d wrap jFprintf()+debug() into a macro
this ensures that everything that is logged to the journal also appears
in the debug output, and it makes the code less noisy.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
198ca65b6e add option to get password from macOS Keychain
this is better than using PassCmd, as it allows the keychain manager to
identify the calling process and therefore use a selective whitelist.

unlike in the now removed example, we use an "internet password" for the
imap protocol, rather than a "generic password" - this seems more
appropriate.

based on a patch by Oliver Runge <oliver.runge@gmail.com>
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
03b15dbdd3 add ability to script IMAP user query
It was already possible to retrieve passwords from arbitrary commands.
But this goes only half the way to allowing automated derivation of
login credentials, as some environments may also have different user
names based on the system. Therefore, add the UserCmd option to
complement PassCmd.

Based on a patch series by Patrick Steinhardt <ps@pks.im>
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
503478533c de-duplicate FETCH response data item traversal somewhat 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
8acf56b311 complain about malformed item names in FETCH responses 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
47b477b3fb re-nest parse_fetch_rsp()
prefer early exits over else branches, which is easier to follow.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
81c4bfeefa extract parse_fetched_flags() from parse_fetch_rsp() 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
d4ead05a02 extract parse_fetched_header() from parse_fetch_rsp() 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
67ea5bea7f handle bogus IMAP FETCH responses more robustly
don't use assert()s when the error condition can stem not only from
errors in mbsync's logic, but also from the IMAP stream being corrupted.

amends 72be55b0e.

REFMAIL: 20191021233411.55ctuvslkfqf2pna@koblih.localdomain
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
a5a8783ea3 sanitize error handling in IMAP FETCH response processing
abort on actual error conditions (protocol errors) and downgrade the
rest to warnings.

REFMAIL: 20191102164509.dxayakg3hrmozjnm@carbon
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
b91dd5b3bc centralize disposal of parsed IMAP lists
makes the code less cluttered, and it's harder to introduce leaks.

this has the hypothetical disadvantage that due to freeing being
delayed, the peak memory usage would rise significantly if we chained to
another parse_list() call which produces a big list while already
holding a big list, but that isn't the case anywhere.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
7af7354dbc fully decompose NAMESPACE response early on
that way the code becomes clearer, and we don't keep useless nodes in
memory.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
6fd4e8de24 don't store 'shared' and 'other' namespaces
they are never used anyway, and aren't going to be (because configuring
that would be more annoying than just specifying Path manually).
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
c391b06b07 drop redundant conn->writing member
this information is already encoded in the socket notifier's config.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
6010fe104e rewrite nonsensical struct packing magic
this couldn't have possibly worked - the alignment also determines the
sizeof, thus defeating the intent of the packing.
2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
a6bb26091a modernize list of gcc warning flags somewhat 2020-08-04 17:16:03 +02:00
Oswald Buddenhagen
64e5f07ad3 consistently use NULL for null pointers
makes the code noisier, but also somewhat more expressive.
2020-08-04 17:16:01 +02:00
Oswald Buddenhagen
e2d3b4d55b fix lots of sign conversion warnings
... by making a lot of objects unsigned, and some signed.
casts which lose precision and change the sign in one go (ssize_t and
time_t to uint on LP64) are made explicit as well.
2020-08-04 17:15:39 +02:00
Oswald Buddenhagen
cc176df2c3 make some narrowing of integers explicit
this does specifically *not* cover about a bazillion warnings about
size_t being shrunk to uint - these make no sense given the expected
data set size.
2020-08-04 17:14:55 +02:00
Oswald Buddenhagen
4d7e169e57 shrink some data at the source to avoid subsequent narrowing 2020-08-04 17:14:55 +02:00
Oswald Buddenhagen
def22db096 constness fixes
add missing const qualifications, and add "const cast" suppressions
where unavoidable.
2020-08-04 17:14:55 +02:00
Oswald Buddenhagen
5c2e8d3e14 make more objects static 2020-08-04 17:14:55 +02:00
Oswald Buddenhagen
71d7d3e6df add some ATTR_* (mostly)
mostly ATTR_PRINTFLIKE(*, 0) for functions with a va_list argument.

also, one ATTR_NORETURN and one ATTR_UNUSED, both on functions.

also, an explicit suppression for a format string stored in a variable.
2020-08-04 17:13:56 +02:00
Oswald Buddenhagen
df22514ced turn maildir_again() into a proper varargs function
this is mostly to work around the fact that both gcc and clang won't
accept the format string declaration (i.e., will complain with
-Wformat-nonliteral) if the *called* function does not actually take a
va_list.

on the upside, it makes one caller cleaner. yay ...
2020-08-04 16:54:28 +02:00
Oswald Buddenhagen
234becf530 remove support for SSLv3
it's insecure and default builds of openssl don't include it any more.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
d09f988c70 add support for (disabling) TLS v1.3
this is actually potentially counterproductive, as people who have set
SSLVersions and fail to adjust it will _lose_ tls 1.3 support. however,
without the option being there, people (incorrectly) believe that tls
1.3 is not supported.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
990cc112f1 Add option to use IMAP LSUB instead of LIST
Based on patch by Cedric Ware <cedric.ware__bml@normalesup.org>
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
e9407cc1f7 IMAP: reject unqualified non-uppercased INBOX spellings
otherwise the server would interpret it as INBOX contrary to our
expectations, which might lead to moderately surprising effects.

if you really want to sync your ~/maildir/inbox to the IMAP INBOX,
specify it as the Maildir Store's Inbox.
2020-08-04 14:49:58 +02:00
Jaroslav Suchanek
07cb422cbb Add support for specifying cipher string used for ssl connection
Some distributions (e.g. Fedora) added support for system wide crypto
policies. This is supported in most common crypto libraries including
OpenSSL. Applications can override this policy using their own cipher
string. This commit adds support for specifying the cipher string in
the mbsync configuration.

For example, to exclude Diffie-Hellman, the user can specify
  CipherString "DEFAULT:!DH"
in the IMAP Account's configuration.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
25b1c2b9e7 set sync record's flags only after propagating new message
this is semantically cleaner, and fixes storing the flags in the rare
case that flags are not being synced and the target is not being
expunged, as in this case flags are queried only during the actual
propagation.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
abdca388f6 atomize & document conditions in load() exception list construction 2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
b677bfe7e5 de-noise msg_copied() and flags_set() somewhat
assign temporary srec object instead of always spelling out the
indirection.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
841f07efd0 de-noise initialization of sync records
use calloc() instead of malloc().
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
e7bc402d09 rename socket_expect_read() => socket_expect_activity()
... to better reflect its (mostly new) function.
2020-08-04 14:49:58 +02:00
Oswald Buddenhagen
8a03651dd8 re-nest conditions in socket_fd_cb()
conn->state == SCK_STARTTLS implies conn->ssl != NULL.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
2117945838 move state assignment to a more natural place
... so it's right next to the related callback assignment.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
c5d3565db5 de-noise -Dd output somewhat
drop commas and left-align fields in message lists.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
2f3cb5f481 fix signedness issues surrounding UIDs
amends bb632d1c.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
e334eb3580 make find_uint_array() never create negative indices 2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
01348f6f7c centralize "const cast" in make_key() 2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
27a1935361 fix strftime() format string warning properly
the workaround for -Wformat triggered -Wformat-nonliteral in turn.
so instead go back to using pragmas and add a proper gcc version check.

this also works with clang - mostly for qt-creator's code model, which
is clang-based.

amends/reverts 55e65147.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
b885e0e03a don't use reserved identifier pattern in stringify() 2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
85688d1c1a don't leak the channel list after all
unlike the actual configuration data, it's not kept in global variables,
so it shows up in memcheck.

amends 1de3ecd88.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
09d11245cd don't leak box list from the command line
we also free the box list obtained from IMAP, so there isn't a real
reason not to do that for one from the command line.

amends 1de3ecd88.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
83adb9a39b actually implement maildir_get_uidnext()
the assumption was that this wouldn't be needed, as maildir_store_msg()
reliably delivers a UID. however, if we crash right before the callback
can record that UID, we'd still use OPEN_FIND in the next run, which
requires the saved next UID.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
c84235b068 get rid of maildir_find_new_msgs() stub
a failed assertion isn't any better than a clean segfault with an
obvious backtrace.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
38e7b4db22 Maildir: fix setting flags on messages without ":2," part
this is mostly hypothetical, as all messages i've encountered actually
have it even if no flags are set on them.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
1004756659 kill TODO item about killing INBOX
while it's correct that mbsync doesn't strictly need to support both
Inbox and Path in a single Channel due to being able to Group Channels,
this "simplification" would have some undesirable effects:
- the concept is part of IMAP and provides a certain level of
  "zero-conf" (in particular via NAMESPACE). having to set up two Stores
  and associated Channels for one Account to reproduce this
  functionality would add quite some redundancy to common
  configurations.
- implementing MapInbox and move detection across Channels would add
  significant complexity.

one reason why one would want this change in the first place is to get
rid of the ambiguity of INBOX appearing right under Path. this could be
avoided by either using a different magic prefix that cannot appear in
actual mailbox names, or requiring a prefix for boxes inside path as
well. neither approach seems worth the effort, given that nesting
"INBOX" under Path causes problems for some other IMAP clients anyway.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
30e166aa18 give the coverity build result archive a more descriptive name 2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
406931bc45 autotest: use symbolic message subjects
... instead of numbers. otherwise there is too much confusion with UIDs.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
6734b9ce7d autotest: re-order mailbox contents according to UIDs
... instead of subjects, because that's way more natural and thus less
confusing.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
f3629c69e1 autotest: re-organize mailbox storage by UID
an effect of 7ce658d is that we can index messages by UID rather than
content (or more specifically, subject). apart from being cleaner, it
allows duplicated subjects.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
b59ee239a4 autotest: assume that each message has a UID
followup to 7ce658d14.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
50eab08509 autotest: show the right state file after idempotence test failure
amends efd72b85.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
d59392e901 autotest: fix prototype of ckbox() 2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
ef2caa074e autotest: create the temp dir in $TEMP
on modern systems, this makes it likely to end up on tmpfs, which is a
lot faster and ssd-friendlier.

the symlink is not deleted at the end, to minimize fs churn. that means
it will be dangling after a reboot, which gets fixed in the next run.
2020-08-04 14:49:57 +02:00
Oswald Buddenhagen
5fee222f84 Merge branch '1.3' 2020-08-04 14:49:27 +02:00
Oswald Buddenhagen
c97e650c24 bump version 2020-08-04 14:44:17 +02:00
Oswald Buddenhagen
30261fe6f1 fix version comparison in LibreSSL conditional
the operator was exactly inverted. that means that it actually wouldn't
compile with both older versions (that needed the aliases) and
potentially new versions (that will hide the data members - still not
the case as of 3.2).

amends 8a40554f0.
2020-08-04 14:42:42 +02:00
Oswald Buddenhagen
542e38dd49 fix re-using server connections for new stores
we failed to reset the box list pointer after freeing it, which would
lead to a crash.
we also failed to reset the listing status, which would lead to
malfunction if we hadn't already crashed.

this inlines imap_cleanup_store(), as there isn't much value in keeping
it. the message list is already freed when disowning the store anyway.
2020-08-04 14:42:42 +02:00
Oswald Buddenhagen
c82397cf6e don't crash in proxy_invoke_bad_callback()
we need to hold a ref to the proxy store, as after the bad_callback()
it's otherwise likely gone.
2020-08-04 14:40:19 +02:00
Oswald Buddenhagen
813ad67c56 fix simultaneously connecting to multiple hosts in non-IPv6 builds
we need to deep-copy the struct hostent data, as otherwise the
concurrent connects will overwrite each other's lookup results.

this is a rather hypothetical fix, as the bug currently affects only
channels connecting two IMAP accounts, and only if the first host's
first address asynchronously fails to connect.
2020-08-04 14:39:34 +02:00
Oswald Buddenhagen
3651c30296 increase PassCmd output buffer even more
apparently, some XOAUTH2 tokens are at 2.4K already, so make it 8K to be
*really* safe for a while.

REFMAIL: <20200716000515.GA2111668@lysator.liu.se>
2020-08-04 14:33:00 +02:00
Oswald Buddenhagen
09540b5648 unbreak CertificateFile documentation
the file may in fact contain CA certs.

amends 7d9d3e15.
2020-08-04 14:28:37 +02:00
Oswald Buddenhagen
cab14608ca Merge branch '1.3' 2020-07-08 12:51:20 +02:00
Oswald Buddenhagen
80deabf520 bump version 2020-07-08 11:42:47 +02:00
Oswald Buddenhagen
96afe8d0c2 fix propagation of flagged oversized messages
... when not syncing flags and the target is not being expunged, as in
that case flags were not queried in time.
2020-07-08 11:14:02 +02:00
Oswald Buddenhagen
aff0c88a38 fix printf length arguments on lp64
found by coverity.
2020-07-08 11:14:02 +02:00
Oswald Buddenhagen
04fc586e75 handle case-insensitivity of IMAP's INBOX
this is relevant only when listing an IMAP Store's contents, as that's
the only place where we aren't imposing the spelling ourselves.

we need to be careful not to treat our own canonical (prefix-stripped
and always slash-delimited) box names like that; codify that in
comments.

this reveals that commit 6f2160f1 may be deemed to have been incorrect -
the TODO item was ambiguous, and could quite possibly have meant this
fix. unsurprisingly, 380ccdd4 re-introduced it with more explicit
wording.
2020-07-08 11:14:02 +02:00
Oswald Buddenhagen
94022a6752 catch server's failure to FETCH *
the query is untypical enough to have caused problems with davmail (when
we still used *:*) and mailo.com (until it got fixed), so better check
that the result (not) returned by the server makes sense.
2020-07-08 11:14:02 +02:00
Oswald Buddenhagen
dfa8c16f27 don't timeout while uploading big messages
we did already set up the timeout when starting to send commands, but so
far we did not reset it when succeeding to send out data. rectify that.

REFFAIL: 87sgy92we3.fsf@jnanam.net
2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
121448ceb9 make -DC work with yama ptrace protection 2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
bee4fc54e7 fix overflows in uint comparisons 2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
a33e44758b fix roff abuse in man pages
".." is not valid. use "." instead, as recommended by groff_man(7).
this also necessitated adjusting the markup of the license blurbs.
2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
7d9d3e15f5 improve documentation of the server certificate related options 2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
a2fe8c155a re-generate ChangeLog only if it's newer than the git index
otherwise, we'd re-generate it during 'install' as well.

note that this does not work with new-style git worktrees, where .git is
only a file - but there the log generation itself already doesn't work
anyway.
2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
281a87ed89 update build requirements
in particular, mention perl-related things, which might not be
immediately obvious.
2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
48038fede1 mention --remove in --help output
amends d9a983a.
2020-07-08 11:12:59 +02:00
Oswald Buddenhagen
93fb3c29c6 fix UIDNEXT error message 2020-07-08 10:50:36 +02:00
Oswald Buddenhagen
e565d08246 don't try to propagate flags the target store does not support
$Forwarded is not standard, so it will most likely fail with mailboxes
that do not support keywords.

amends c4d7f018.
2020-01-08 18:22:48 +01:00
Oswald Buddenhagen
e8caaaaf65 rename F_PASSED => F_FORWARDED
the flag names are supposed to reflect the IMAP names (that's why
their #defines are followed by comments with the Maildir names).

amends c4d7f018.
2020-01-08 18:11:55 +01:00
Oswald Buddenhagen
462fed556a Merge branch '1.3' 2019-10-03 20:17:54 +02:00
Oswald Buddenhagen
d0a8551703 fix error handling of SSL_set_tlsext_host_name()
it is not one of the functions to be checked with SSL_get_error().

amends 17babc169.
2019-09-10 13:26:42 +02:00
Oswald Buddenhagen
6a874b5877 error-check more openssl function calls 2019-09-10 13:26:42 +02:00
Oswald Buddenhagen
5f908b7672 attempt to improve ssl error handling (again)
the error queue may contain multiple errors, so make sure to drain it
completely. also, don't try to fall back to errno in case of
SSL_ERROR_SSL.
2019-09-10 13:26:42 +02:00
Oswald Buddenhagen
91abf2b830 modernize ssl context init
with openssl 1.1+, use TLS_client_method() instead of the deprecated
SSLv23_client_method().
2019-09-10 13:26:42 +02:00
Oswald Buddenhagen
f63e4338e8 fix leak of openssl X509 objects
SSL_get_peer_certificate() increments the refcount of the object.
2019-09-10 13:26:42 +02:00
Oswald Buddenhagen
8959c6b791 fix libcrypto detection in openssl 1.1+ without pkg-config
CRYPTO_lock() was removed. test for X509_cmp() instead, which we
actually use.

inspired by report from FX <coudert@users.sourceforge.net>.
2019-09-10 13:26:19 +02:00
Oswald Buddenhagen
36062c5220 wording fixes in mbsync.1
inspired by Ulrich Ölmann <u.oelmann@pengutronix.de>.
2019-09-10 13:18:49 +02:00
Oswald Buddenhagen
a310e7e2ba purge vestiges of the compat wrapper
amends cbac8aa75.
2019-08-30 13:13:30 +02:00
Caspar Schutijser
7607e53d56 Do not crash when using Tunnel in an IPv6-enabled build
socket_connected() is also called on the tunnel pipe.

amends 3ceb55310.
2019-08-19 13:23:03 +02:00
Dmitry Torokhov
bf14798700 Bump up PassCmd buffer size to 2KiB
While ordinary passwords are rarely longer than 80 bytes, XOAUTH2 tokens
easily exceed this limit. Let's bump it up to 2K to be really safe.
2019-07-19 12:58:04 +02:00
Oswald Buddenhagen
fbc432aace fix parsing of NIL hierarchy delimiters in IMAP LIST responses
a server which does not support hierarchical mailboxes (e.g., seznam.cz
as of oct 2018) can legitimately send NIL (rather than an empty string).
2019-05-28 17:27:09 +02:00
Oswald Buddenhagen
95d18e2778 Merge branch '1.3' 2018-11-27 00:51:03 +01:00
Oswald Buddenhagen
37feeddbfb Merge branch '1.3' 2018-07-01 13:26:10 +02:00
Michael J Gruber
c4d7f0189c implement Forwarded flag
maildir supports a 'P' flag which denotes the fact that a message has
been 'passed' on (forwarded, bounced). notmuch syncs this to the
'passed' tag.

Per https://tools.ietf.org/html/rfc5788, IMAP has a user-defined flag
(keyword) '$Forwarded' that is supported by many servers and clients
these days. (Technically, one should check for '$Forwarded' in the
server response.)

Restructure mbsync's flag parser to accept keywords (flags starting with
'$') but still bail out on unknown system flags (flags starting with '\').
Support '$Forwarded' as a first keyword since it maps to maildir's 'P'
and needs to be sorted in between the system flags.

Signed-off-by: Michael J Gruber <github@grubix.eu>
2018-07-01 12:36:28 +02:00
Michael J Gruber
e71f0ccc2a mark MAILBOX_DRIVER_FLAG locations in code
Mailbox driver flags are defined in several places. It is essential that
they are kept in sync, so mark them with the same string for easy
grepping with an alerting boiler plate.

Signed-off-by: Michael J Gruber <github@grubix.eu>
2018-07-01 12:30:59 +02:00
Oswald Buddenhagen
904858365d Merge branch '1.3'
Conflicts:
	configure.ac
2018-04-08 18:17:10 +02:00
Oswald Buddenhagen
cbac8aa75c delete the compat wrapper
it was deprecated in 1.2. until 1.4 gets released, enough time will have
passed for sure.
2017-10-01 15:30:07 +02:00
Oswald Buddenhagen
094bc883e8 bump version 2017-10-01 15:06:37 +02:00
39 changed files with 3654 additions and 4316 deletions

41
Dockerfile Normal file
View file

@ -0,0 +1,41 @@
#FROM debian:bookworm-20231030-slim
FROM debian:bullseye-20220801
# need to add
# removed
# libsasl2-modules \
# ca-certificates \
# version pinning is being handled in our from line
# hadolint ignore=DL3008
RUN true && \
apt-get update && \
groupadd --gid 1000 user && \
useradd -m --home-dir /home/user --shell /bin/sh --uid 1000 --gid 1000 user && \
apt-get install -y --no-install-recommends \
libsasl2-2 \
libsasl2-dev \
perl \
libdatetime-format-dateparse-perl \
autoconf \
automake \
zlib1g-dev \
libdb-dev \
libsasl2-dev \
libssl-dev \
gcc \
make \
git \
&& \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
true
# Built with
# apt install build-essential dh-autoreconf git
# apt install libssl-dev
# apt install zlib1g-dev
# apt install libsasl2-dev
WORKDIR /home/user
USER user

View file

@ -47,7 +47,10 @@ LOG_PL = \
print $$log."\n"; \
}
$(srcdir)/ChangeLog: log
$(srcdir)/.git/index:
$(srcdir)/ChangeLog: $(srcdir)/.git/index
$(MAKE) log
log:
@test -z "$(srcdir)" || cd $(srcdir) && \
( ! test -d .git || \
@ -56,7 +59,7 @@ log:
cov-scan: clean
/opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE)
tar cavf isync.tar.xz cov-int
tar cavf isync-cov.tar.xz cov-int
deb:
CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign

21
NEWS
View file

@ -1,3 +1,24 @@
[1.4.0]
The 'isync' compatibility wrapper was removed.
Added support for disabling TLS v1.3 - adjust SSLVersions if you set it.
Removed support for obsolete/insecure SSL v3.
The IMAP '$Forwarded' / Maildir 'P' (passed) flag is supported now.
Support for configuring a TLS cipher string was added.
IMAP mailbox subscriptions are supported now.
The IMAP user query can be scripted now.
Added built-in support for macOS Keychain.
Messages excluded by MaxSize will now result in placeholders.
The use of Master/Slave terminology has been deprecated.
[1.3.0]
Network timeout handling has been added.

10
README
View file

@ -23,8 +23,7 @@ Mailboxes can be safely modified while ``mbsync'' operates.
Multiple replicas of each mailbox can be maintained.
isync is the project name, while mbsync is the current executable name; this
change was necessary because of massive changes in the user interface. An
isync executable still exists; it is a compatibility wrapper around mbsync.
change was necessary because of massive changes in the user interface.
* Features
@ -55,11 +54,18 @@ isync executable still exists; it is a compatibility wrapper around mbsync.
* Requirements
perl v5.14+
Berkeley DB 4.1+ (optional)
OpenSSL for TLS/SSL support (optional)
Cyrus SASL (optional)
zlib (optional)
The build from git also requires:
GNU autotools (autoconf & automake)
perl module Date::Parse (libtimedate-perl on Debian, perl-TimeDate on
Fedora and Suse)
* Installation
./autogen.sh (only when building from git)

19
TODO
View file

@ -15,7 +15,7 @@ should complain when multiple Channels match the same folders.
propagate folder deletions even when the folders are non-empty.
- verify that "most" of the folders in the Channel are still there.
- refuse to delete unpropagated messages when trashing on the remote side.
- refuse to delete master if it has unpropagated messages. symmetry?
- refuse to delete far side if it has unpropagated messages. symmetry?
add message expiration based on arrival date (message date would be too
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
@ -28,23 +28,15 @@ add support for event notification callbacks.
it would be also possible to report more differentiated exit codes, but
that seems too limiting in the general case.
make it possible to have different mailbox names for Master and Slave in
make it possible to have different mailbox names for far and near side in
Patterns.
- use master:slave for the pattern
- use far:near for the pattern
- for quoting, use more colons: the longest sequence of colons is the
separator
- this makes Groups mostly useless, as they are mostly a workaround for this
function being missing so far
- this is needed for move detection, which would work only within one Channel
normalize INBOX capitalization received from IMAP, to avoid anomalies.
kill the concept of an INBOX, it is a relic from single-channel operation.
if somebody needs it, he can have two stores with different Paths. the path
can name a single (in-)box (curr. broken with maildir). an empty box name
actually means empty, so the IMAP mailbox should use INBOX for Path (can't
make that the default, as it would mess up the NAMESPACE).
add regexp-based mailbox path rewriting to the drivers. user would provide
expressions for both directions. every transformation would be immediately
verified with the inverse transform. PathDelimiter and Flatten would become
@ -69,9 +61,8 @@ messages.
use MULTIAPPEND and FETCH with multiple messages.
create dummies describing MIME structure of messages bigger than MaxSize.
flagging the dummy would fetch the real message. possibly remove --renew.
note that all interaction needs to happen on the slave side probably.
dummy messages resulting from MaxSize should contain a dump of the original
message's MIME structure and its (reasonably sized) text parts.
don't SELECT boxes unless really needed; in particular not for appending,
and in write-only mode not before changes are made.

3
build Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
# Note we assume this is run with podman. Take -u 0:0 out if using docker
docker run -u 0:0 -it --rm -v $(pwd):/make -w /make isync-build make

View file

@ -1,14 +1,47 @@
AC_INIT([isync], [1.3.1])
AC_INIT([isync], [1.4.4])
AC_CONFIG_HEADERS([autodefs.h])
AM_INIT_AUTOMAKE
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE
AC_PROG_CC_C99
AC_PROG_CC
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wstrict-prototypes -std=c99 -pedantic -Wno-overlength-strings"
warnings="
-Wall -Wextra
-Wshadow
-Wcast-qual
-Wformat=2 -Wformat-signedness -Wformat-nonliteral
-Wstrict-prototypes
-Wno-overlength-strings
"
CFLAGS="$CFLAGS -pipe -std=c11 -pedantic $(echo $warnings)"
fi
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void fkt(void)
{
int a = 42; // c99 comment
for (int i = 0; i < a; i++) {} // declaration inside for()
int b; // declaration after code
}
// c11 anonymous structs/unions
struct base {
int a;
};
union deriv {
struct base gen;
struct {
int a;
int b;
};
};
])], , [AC_MSG_ERROR([compiler does not support required C11 features])])
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
AC_CHECK_PROG(PERL, perl, perl)
@ -29,8 +62,8 @@ if test "x$ob_cv_perl_ver" = "xno"; then
fi
AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z,
[AC_TRY_RUN(
[#include <time.h>
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <time.h>
#include <string.h>
int main(void)
@ -40,12 +73,12 @@ int main(void)
strftime(buf, sizeof(buf), "%z", localtime(&t));
return !(buf[0] == '+' || buf[0] == '-');
}
], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
]])], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
if test "x$ob_cv_strftime_z" = x"no"; then
AC_MSG_ERROR([libc lacks necessary feature])
fi
AC_CHECK_HEADERS(sys/poll.h sys/select.h)
AC_CHECK_HEADERS(poll.h sys/select.h)
AC_CHECK_FUNCS(vasprintf strnlen memrchr timegm)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
@ -63,7 +96,7 @@ fi
have_ssl_paths=
AC_ARG_WITH(ssl,
AC_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
AS_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
[ob_cv_with_ssl=$withval])
if test "x$ob_cv_with_ssl" != xno; then
case $ob_cv_with_ssl in
@ -94,7 +127,7 @@ if test "x$ob_cv_with_ssl" != xno; then
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl])
AC_CHECK_LIB(crypto, CRYPTO_lock, [LIBCRYPTO=-lcrypto])
AC_CHECK_LIB(crypto, X509_cmp, [LIBCRYPTO=-lcrypto])
AC_CHECK_LIB(ssl, SSL_connect,
[SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes])
LDFLAGS=$sav_LDFLAGS
@ -160,12 +193,13 @@ AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4,
[ac_cv_berkdb4=no
sav_LIBS=$LIBS
LIBS="$LIBS -ldb"
AC_TRY_LINK([#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)],
[ac_cv_berkdb4=yes])
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0);
])], [ac_cv_berkdb4=yes], [])
LIBS=$sav_LIBS
])
if test "x$ac_cv_berkdb4" = xyes; then
@ -187,16 +221,32 @@ if test "x$ob_cv_with_zlib" != xno; then
)
fi
AC_ARG_ENABLE(compat,
AC_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]),
[ob_cv_enable_compat=$enableval])
if test "x$ob_cv_enable_compat" != xno; then
AC_CHECK_FUNCS(getopt_long)
fi
AM_CONDITIONAL(with_compat, test "x$ob_cv_enable_compat" != xno -a "x$ac_cv_berkdb4" = xyes)
AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes)
AC_CONFIG_FILES([Makefile src/Makefile src/compat/Makefile isync.spec])
case $target_os in
darwin*)
darwin=yes
;;
*)
darwin=no
;;
esac
AC_ARG_WITH(
macos-keychain,
[AS_HELP_STRING([--with-macos-keychain], [Support macOS keychain])],
[have_macos_keychain=$withval],
[have_macos_keychain=$darwin])
if test "x$have_macos_keychain" != xno; then
if test $darwin = no; then
AC_MSG_ERROR([Cannot use macOS Keychain outside macOS.])
fi
have_macos_keychain=yes
AC_DEFINE(HAVE_MACOS_KEYCHAIN, 1, [Define to 1 if you have the macOS Keychain Services API.])
AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"])
fi
AC_CONFIG_FILES([Makefile src/Makefile isync.spec])
AC_OUTPUT
AC_MSG_RESULT()
@ -220,4 +270,11 @@ if test "x$ac_cv_berkdb4" = xyes; then
else
AC_MSG_RESULT([Not using Berkeley DB])
fi
if test $darwin = yes; then
if test "x$have_macos_keychain" = xyes; then
AC_MSG_RESULT([Using macOS Keychain])
else
AC_MSG_RESULT([Not using macOS Keychain])
fi
fi
AC_MSG_RESULT()

13
debian/README.Debian vendored
View file

@ -3,10 +3,11 @@ A note from isync's web site:
isync can be integrated into Mutt fairly easily with a few hooks:
folder-hook ~A bind index $ <sync-mailbox>
folder-hook +maildir 'macro index $ "<sync-mailbox>!isync -e maildir\n"'
folder-hook +maildir 'macro index $ "<sync-mailbox>!mbsync the_channel:maildir\n"'
where maildir is the name of the local mailbox (or its alias). This works well
so long as you are not modifying the IMAP mailbox outside of Mutt. However, if
you are using another mail program simultaneously Mutt will have the wrong idea
of the local mailbox flags and messages will start disappearing from its index
display (don't worry, they are still on disk).
where the_channel is the Channel used to sync this mailbox, and maildir is the
name of the local mailbox itself. This works well so long as you are not
modifying the IMAP mailbox outside of Mutt. However, if you are using another
mail program simultaneously, Mutt will have the wrong idea of the local mailbox
flags and messages will start disappearing from its index display (don't worry,
they are still on disk).

5
debian/control vendored
View file

@ -26,11 +26,6 @@ Description: IMAP and MailDir mailbox synchronizer
deletions and flag changes can be propagated both ways. isync is suitable
for use in IMAP-disconnected mode.
.
The main application was much improved in version 1.0. Those improvements
lead to interface changes and the application being renamed to mbsync. The
application isync is now only a wrapper to keep compatibility with earlier
versions.
.
Features:
* Fine-grained selection of synchronization operations to perform
* Synchronizes single mailboxes or entire mailbox collections

View file

@ -30,6 +30,6 @@ rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name}
rm -rf $RPM_BUILD_ROOT
%files
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample src/compat/isyncrc.sample
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample
%{_bindir}/*
%{_mandir}/man1/*

View file

@ -1,10 +1,5 @@
if with_compat
compat_dir = compat
endif
SUBDIRS = $(compat_dir)
mbsync_SOURCES = main.c sync.c config.c util.c socket.c driver.c drv_imap.c drv_maildir.c drv_proxy.c
mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS)
mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS) $(KEYCHAIN_LIBS)
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
drv_proxy.$(OBJEXT): drv_proxy.inc

View file

@ -33,28 +33,50 @@
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#define __stringify(x) #x
#define stringify(x) __stringify(x)
#define stringify__(x) #x
#define stringify(x) stringify__(x)
// From https://stackoverflow.com/a/62984543/3685191
#define deparen(x) esc_(ish_ x)
#define esc_(...) esc__(__VA_ARGS__)
#define esc__(...) van_ ## __VA_ARGS__
#define ish_(...) ish_ __VA_ARGS__
#define van_ish_
#define shifted_bit(in, from, to) \
(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to)
((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
# define ATTR_PACKED(ref) __attribute__((packed,aligned(sizeof(ref))))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
# define ATTR_PACKED(ref)
#endif
#if __GNUC__ >= 7
#if defined(__clang__)
# define DO_PRAGMA__(text) _Pragma(#text)
# define DIAG_PUSH DO_PRAGMA__(clang diagnostic push)
# define DIAG_POP DO_PRAGMA__(clang diagnostic pop)
# define DIAG_DISABLE(text) DO_PRAGMA__(clang diagnostic ignored text)
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
# define DO_PRAGMA__(text) _Pragma(#text)
# define DIAG_PUSH DO_PRAGMA__(GCC diagnostic push)
# define DIAG_POP DO_PRAGMA__(GCC diagnostic pop)
# define DIAG_DISABLE(text) DO_PRAGMA__(GCC diagnostic ignored text)
#else
# define DIAG_PUSH
# define DIAG_POP
# define DIAG_DISABLE(text)
#endif
#if __GNUC__ >= 7 || defined(__clang__)
# define FALLTHROUGH __attribute__((fallthrough));
#else
# define FALLTHROUGH
@ -85,6 +107,7 @@ typedef unsigned int uint;
#define VERBOSE 0x800
#define KEEPJOURNAL 0x1000
#define ZERODELAY 0x2000
#define FORCEASYNC 0x4000
extern int DFlags;
extern int JLimit;
@ -95,7 +118,7 @@ extern int Pid;
extern char Hostname[256];
extern const char *Home;
extern int BufferLimit;
extern uint BufferLimit;
extern int new_total[2], new_done[2];
extern int flags_total[2], flags_done[2];
@ -105,23 +128,24 @@ void stats( void );
/* util.c */
void vdebug( int, const char *, va_list va );
void vdebugn( int, const char *, va_list va );
void ATTR_PRINTFLIKE(2, 0) vdebug( int, const char *, va_list va );
void ATTR_PRINTFLIKE(2, 0) vdebugn( int, const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) info( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) infon( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) progress( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) notice( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) warn( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) error( const char *, ... );
void ATTR_PRINTFLIKE(1, 0) vsys_error( const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... );
void flushn( void );
typedef struct string_list {
struct string_list *next;
char string[1];
} ATTR_PACKED(void *) string_list_t;
} string_list_t;
void add_string_list_n( string_list_t **list, const char *str, int len );
void add_string_list_n( string_list_t **list, const char *str, uint len );
void add_string_list( string_list_t **list, const char *str );
void free_string_list( string_list_t *list );
@ -132,9 +156,9 @@ void *memrchr( const void *s, int c, size_t n );
size_t strnlen( const char *str, size_t maxlen );
#endif
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );
int starts_with( const char *str, int strl, const char *cmp, uint cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl );
int equals( const char *str, int strl, const char *cmp, uint cmpl );
#ifndef HAVE_TIMEGM
time_t timegm( struct tm *tm );
@ -145,35 +169,40 @@ void *nfcalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrndup( const char *str, size_t nchars );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int ATTR_PRINTFLIKE(2, 0) nfvasprintf( char **str, const char *fmt, va_list va );
int ATTR_PRINTFLIKE(2, 3) nfasprintf( char **str, const char *fmt, ... );
int ATTR_PRINTFLIKE(3, 4) nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );
void ATTR_NORETURN oom( void );
char *expand_strdup( const char *s );
int map_name( const char *arg, char **result, int reserve, const char *in, const char *out );
int map_name( const char *arg, char **result, uint reserve, const char *in, const char *out );
#define DEFINE_ARRAY_TYPE(T) \
typedef struct { \
T *data; \
int size; \
} ATTR_PACKED(T *) T##_array_t; \
typedef struct { \
uint size; \
} T##_array_t; \
typedef union { \
T##_array_t array; \
int alloc; \
} ATTR_PACKED(T *) T##_array_alloc_t; \
struct { \
T *data; \
uint size; \
uint alloc; \
}; \
} T##_array_alloc_t; \
static INLINE T *T##_array_append( T##_array_alloc_t *arr ) \
{ \
if (arr->array.size == arr->alloc) { \
if (arr->size == arr->alloc) { \
arr->alloc = arr->alloc * 2 + 100; \
arr->array.data = nfrealloc( arr->array.data, arr->alloc * sizeof(T) ); \
arr->data = nfrealloc( arr->data, arr->alloc * sizeof(T) ); \
} \
return &arr->array.data[arr->array.size++]; \
return &arr->data[arr->size++]; \
}
#define ARRAY_INIT(arr) \
do { (arr)->array.data = 0; (arr)->array.size = (arr)->alloc = 0; } while (0)
do { (arr)->data = NULL; (arr)->size = (arr)->alloc = 0; } while (0)
#define ARRAY_SQUEEZE(arr) \
do { \
@ -187,7 +216,7 @@ int find_uint_array( const uint_array_t array, uint value );
void arc4_init( void );
uchar arc4_getbyte( void );
int bucketsForSize( int size );
uint bucketsForSize( uint size );
typedef struct list_head {
struct list_head *next, *prev;
@ -197,15 +226,16 @@ typedef struct notifier {
struct notifier *next;
void (*cb)( int what, void *aux );
void *aux;
#ifdef HAVE_SYS_POLL_H
int index;
#ifdef HAVE_POLL_H
uint index;
#else
int fd, events;
int fd;
short events;
#endif
} notifier_t;
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#ifdef HAVE_POLL_H
# include <poll.h>
#else
# define POLLIN 1
# define POLLOUT 4
@ -213,7 +243,8 @@ typedef struct notifier {
#endif
void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux );
void conf_notifier( notifier_t *sn, int and_events, int or_events );
void conf_notifier( notifier_t *sn, short and_events, short or_events );
short notifier_config( notifier_t *sn );
void wipe_notifier( notifier_t *sn );
typedef struct {
@ -226,7 +257,7 @@ typedef struct {
void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux );
void conf_wakeup( wakeup_t *tmr, int timeout );
void wipe_wakeup( wakeup_t *tmr );
static INLINE int pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != 0; }
static INLINE int ATTR_UNUSED pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != NULL; }
void main_loop( void );

View file

@ -1 +0,0 @@
/isync

View file

@ -1,12 +0,0 @@
bin_PROGRAMS = isync
isync_SOURCES = main.c config.c convert.c util.c
isync_LDADD = $(DB_LIBS)
noinst_HEADERS = isync.h
man_MANS = isync.1
exampledir = $(docdir)/examples
example_DATA = isyncrc.sample
EXTRA_DIST = $(example_DATA) $(man_MANS)

View file

@ -1,551 +0,0 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( sizeof(char) * (nchars + 1) );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
struct passwd *pw;
const char *p, *q;
char *r;
if (*s == '~') {
s++;
if (!*s) {
p = 0;
q = Home;
} else if (*s == '/') {
p = s + 1;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = my_strndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
p++;
} else
pw = getpwnam( s );
if (!pw)
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s/%s", q, p ? p : "" );
return r;
} else if (*s != '/' && xmaildir) {
nfasprintf( &r, "%s/%s", xmaildir, s );
return r;
} else
return nfstrdup( s );
}
static int
is_true( const char *val )
{
return
!strcasecmp( val, "yes" ) ||
!strcasecmp( val, "true" ) ||
!strcasecmp( val, "on" ) ||
!strcmp( val, "1" );
}
void
load_config( const char *path, config_t ***stor )
{
config_t **sstor, *cfg;
FILE *fp;
char *p, *cmd, *val;
int line = 0;
char buf[1024];
if (!(fp = fopen( path, "r" ))) {
if (errno != ENOENT)
sys_error( "Cannot read config file '%s'", path );
return;
}
if (!Quiet && !Debug && !Verbose)
printf( "Reading configuration file %s\n", path );
buf[sizeof(buf) - 1] = 0;
cfg = &global;
while (fgets( buf, sizeof(buf) - 1, fp )) {
p = buf;
cmd = next_arg( &p );
val = next_arg( &p );
line++;
if (!cmd || *cmd == '#')
continue;
if (!val) {
fprintf( stderr, "%s:%d: parameter missing\n", path, line );
continue;
}
if (!strcasecmp( "Mailbox", cmd )) {
if (o2o)
break;
cfg = **stor = nfmalloc( sizeof(config_t) );
*stor = &cfg->next;
memcpy( cfg, &global, sizeof(config_t) );
/* not expanded at this point */
cfg->path = nfstrdup( val );
} else if (!strcasecmp( "OneToOne", cmd )) {
if (boxes) {
forbid:
fprintf( stderr,
"%s:%d: keyword '%s' allowed only in global section\n",
path, line, cmd );
continue;
}
o2o = is_true( val );
} else if (!strcasecmp( "Maildir", cmd )) {
if (boxes)
goto forbid;
maildir = nfstrdup( val );
xmaildir = expand_strdup( val );
} else if (!strcasecmp( "Folder", cmd )) {
if (boxes)
goto forbid;
folder = nfstrdup( val );
} else if (!strcasecmp( "Inbox", cmd )) {
if (boxes)
goto forbid;
inbox = nfstrdup( val );
} else if (!strcasecmp( "Host", cmd )) {
if (starts_with( val, -1, "imaps:", 6 )) {
val += 6;
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_sslv3 = 1;
}
cfg->host = nfstrdup( val );
} else if (!strcasecmp( "User", cmd ))
cfg->user = nfstrdup( val );
else if (!strcasecmp( "Pass", cmd ))
cfg->pass = nfstrdup( val );
else if (!strcasecmp ( "Port", cmd ))
cfg->port = atoi( val );
else if (!strcasecmp ( "Box", cmd ))
cfg->box = nfstrdup( val );
else if (!strcasecmp ( "Alias", cmd )) {
if (!boxes) {
fprintf( stderr,
"%s:%d: keyword 'Alias' allowed only in mailbox specification\n",
path, line );
continue;
}
cfg->alias = nfstrdup( val );
} else if (!strcasecmp( "MaxSize", cmd ))
cfg->max_size = atol( val );
else if (!strcasecmp ( "MaxMessages", cmd ))
cfg->max_messages = atol( val );
else if (!strcasecmp ( "UseNamespace", cmd ))
cfg->use_namespace = is_true( val );
else if (!strcasecmp ( "CopyDeletedTo", cmd ))
cfg->copy_deleted_to = nfstrdup( val );
else if (!strcasecmp ( "Tunnel", cmd ))
cfg->tunnel = nfstrdup( val );
else if (!strcasecmp ( "Expunge", cmd ))
cfg->expunge = is_true( val );
else if (!strcasecmp( "Delete", cmd ))
cfg->delete = is_true( val );
else if (!strcasecmp( "CertificateFile", cmd ))
cfg->cert_file = expand_strdup( val );
else if (!strcasecmp( "RequireSSL", cmd ))
cfg->require_ssl = is_true( val );
else if (!strcasecmp( "UseSSLv2", cmd ))
fprintf( stderr, "Warning: UseSSLv2 is no longer supported\n" );
else if (!strcasecmp( "UseSSLv3", cmd ))
cfg->use_sslv3 = is_true( val );
else if (!strcasecmp( "UseTLSv1", cmd ))
cfg->use_tlsv1 = is_true( val );
else if (!strcasecmp( "RequireCRAM", cmd ))
cfg->require_cram = is_true( val );
else if (buf[0])
fprintf( stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd );
}
fclose( fp );
if (o2o) {
if (!global.host && !global.tunnel) {
fprintf( stderr, "Neither Host nor Tunnel given to OneToOne. Aborting.\n" );
exit( 1 );
}
} else
for (sstor = &boxes; (cfg = *sstor); ) {
if (!cfg->host && !cfg->tunnel) {
fprintf( stderr, "Mailbox '%s' has neither Host nor Tunnel. Skipping.\n",
cfg->alias ? cfg->alias : cfg->path );
if (&cfg->next == *stor)
*stor = sstor;
*sstor = cfg->next;
continue;
}
sstor = &cfg->next;
}
}
static const char *
tb( int on )
{
return on ? "yes" : "no";
}
static const char *
quotify( const char *str )
{
char *ostr;
int i;
for (i = 0; str[i]; i++) {
if (isspace( str[i] )) {
nfasprintf( &ostr, "\"%s\"", str );
return ostr;
}
}
return str;
}
static void
write_imap_server( FILE *fp, config_t *cfg )
{
config_t *pbox;
char *p, *p2;
int hl, a1, a2, a3, a4;
char buf[128], ubuf[64];
static int tunnels;
/* The old server names determine the derived store names. They are kinda stupid,
* but can't be changed, because store names are encoded in state file names. */
if (cfg->tunnel)
nfasprintf( (char **)&cfg->old_server_name, "tunnel%d", ++tunnels );
else {
if (sscanf( cfg->host, "%d.%d.%d.%d", &a1, &a2, &a3, &a4 ) == 4)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
/* XXX this does not avoid clashes. add port? */
p = strrchr( cfg->host, '.' );
if (!p)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
hl = nfsnprintf( buf, sizeof(buf), "%.*s", p - cfg->host, cfg->host );
p2 = strrchr( buf, '.' );
if (p2)
hl = sprintf( buf, "%s", p2 + 1 );
}
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (equals( pbox->old_server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->old_server_name, "%s-%d", buf, ++pbox->old_servers );
goto gotsrv;
}
cfg->old_server_name = nfstrdup( buf );
cfg->old_servers = 1;
gotsrv: ;
}
/* The "new" server names determine the names of the accounts themselves.
* They are optimized for descriptiveness, e.g. in password prompts. */
if (cfg->user)
nfsnprintf( ubuf, sizeof(ubuf), "%s@", cfg->user );
else
ubuf[0] = 0;
if (!cfg->host)
hl = nfsnprintf( buf, sizeof(buf), "%stunnel", ubuf );
else {
if (cfg->port != (cfg->use_imaps ? 993 : 143))
hl = nfsnprintf( buf, sizeof(buf), "%s%s_%d", ubuf, cfg->host, cfg->port );
else
hl = nfsnprintf( buf, sizeof(buf), "%s%s", ubuf, cfg->host );
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (equals( pbox->server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->server_name, "%s-%d", buf, ++pbox->servers );
goto ngotsrv;
}
cfg->server_name = nfstrdup( buf );
cfg->servers = 1;
ngotsrv: ;
fprintf( fp, "IMAPAccount %s\n", cfg->server_name );
if (cfg->tunnel)
fprintf( fp, "Tunnel \"%s\"\n", cfg->tunnel );
else {
if (cfg->use_imaps)
fprintf( fp, "Host imaps:%s\n", cfg->host );
else
fprintf( fp, "Host %s\n", cfg->host );
fprintf( fp, "Port %d\n", cfg->port );
}
if (cfg->user)
fprintf( fp, "User %s\n", quotify( cfg->user ) );
if (cfg->pass)
fprintf( fp, "Pass %s\n", quotify( cfg->pass ) );
fprintf( fp, "RequireCRAM %s\nRequireSSL %s\n"
"UseSSLv3 %s\nUseTLSv1 %s\nUseTLSv1.1 %s\nUseTLSv1.2 %s\n",
tb(cfg->require_cram), tb(cfg->require_ssl),
tb(cfg->use_sslv3), tb(cfg->use_tlsv1), tb(cfg->use_tlsv1), tb(cfg->use_tlsv1) );
if ((cfg->use_imaps || cfg->use_sslv3 || cfg->use_tlsv1) && cfg->cert_file)
fprintf( fp, "CertificateFile %s\n", quotify( cfg->cert_file ) );
fputc( '\n', fp );
}
static void
write_imap_store( FILE *fp, config_t *cfg )
{
if (cfg->stores > 1)
nfasprintf( (char **)&cfg->store_name, "%s-%d", cfg->old_server_name, cfg->stores );
else
cfg->store_name = cfg->old_server_name;
fprintf( fp, "IMAPStore %s\nAccount %s\n",
cfg->store_name, cfg->server_name );
if (*folder)
fprintf( fp, "Path %s\n", quotify( folder ) );
else
fprintf( fp, "UseNamespace %s\n", tb(cfg->use_namespace) );
if (inbox)
fprintf( fp, "MapInbox %s\n", quotify( inbox ) );
if (cfg->copy_deleted_to)
fprintf( fp, "Trash %s\n", quotify( cfg->copy_deleted_to ) );
fputc( '\n', fp );
}
static void
write_channel_parm( FILE *fp, config_t *cfg )
{
if (cfg->max_size)
fprintf( fp, "MaxSize %d\n", cfg->max_size );
if (cfg->max_messages)
fprintf( fp, "MaxMessages %d\n", cfg->max_messages );
if (cfg->delete && !global.delete && !delete)
fputs( "Sync All\n", fp );
if (cfg->expunge && !global.expunge && !expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
}
static int
mstrcmp( const char *s1, const char *s2 )
{
if (s1 == s2)
return 0;
if (!s1 || !s2)
return 1;
return strcmp( s1, s2 );
}
void
write_config( int fd )
{
FILE *fp;
const char *cn, *scn;
char *path, *local_box, *local_store;
config_t *box, *sbox, *pbox;
int pl;
if (!(fp = fdopen( fd, "w" ))) {
perror( "fdopen" );
return;
}
fputs( "SyncState *\n", fp );
if (!global.delete && !delete)
fputs( "Sync New ReNew Flags\n", fp );
if (global.expunge || expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
if (o2o) {
write_imap_server( fp, &global );
write_imap_store( fp, &global );
fprintf( fp, "MaildirStore local\nPath %s/\n", quotify( maildir ) );
if (!inbox) { /* just in case listing actually produces an INBOX ... */
nfasprintf( (char **)&inbox, "%s/INBOX", maildir );
fprintf( fp, "Inbox %s\n", quotify( inbox ) );
}
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fprintf( fp, "\nChannel o2o\nMaster :%s:\nSlave :local:\nPattern %%\n", global.store_name );
write_channel_parm( fp, &global );
} else {
for (box = boxes; box; box = box->next) {
for (pbox = boxes; pbox != box; pbox = pbox->next) {
if (box->tunnel) {
if (mstrcmp( pbox->tunnel, box->tunnel ))
continue;
} else {
if (mstrcmp( pbox->host, box->host ) ||
pbox->use_imaps != box->use_imaps ||
pbox->port != box->port)
continue;
}
if (mstrcmp( pbox->user, box->user ) ||
mstrcmp( pbox->pass, box->pass )) /* nonsense */
continue;
if ((box->use_imaps ||
box->use_sslv3 || box->use_tlsv1) &&
mstrcmp( pbox->cert_file, box->cert_file )) /* nonsense */
continue;
if (pbox->use_imaps != box->use_imaps ||
pbox->use_sslv3 != box->use_sslv3 ||
pbox->use_tlsv1 != box->use_tlsv1)
continue;
box->server_name = pbox->server_name;
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->server_name != box->server_name ||
mstrcmp( sbox->copy_deleted_to, box->copy_deleted_to ) ||
(!*folder && sbox->use_namespace != box->use_namespace))
continue;
box->store_name = sbox->store_name;
goto gotall;
}
box->stores = ++pbox->stores;
goto gotsrv;
}
write_imap_server( fp, box );
box->stores = 1;
gotsrv:
write_imap_store( fp, box );
gotall:
path = expand_strdup( box->path );
if (starts_with( path, -1, Home, HomeLen ) && path[HomeLen] == '/')
nfasprintf( &path, "~%s", path + HomeLen );
local_store = local_box = strrchr( path, '/' ) + 1;
pl = local_store - path;
/* try to re-use existing store */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_path && equals( pbox->local_store_path, -1, path, pl ))
goto gotstor;
box->local_store_path = my_strndup( path, pl );
/* derive a suitable name */
if (!strcmp( box->local_store_path, "/var/mail/" ) || !strcmp( box->local_store_path, "/var/spool/mail/" )) {
local_store = nfstrdup( "spool" );
} else if (!strcmp( box->local_store_path, "~/" )) {
local_store = nfstrdup( "home" );
} else {
local_store = memrchr( box->local_store_path, '/', pl - 1 );
if (local_store) {
local_store = my_strndup( local_store + 1, pl - 2 - (local_store - box->local_store_path) );
for (pl = 0; local_store[pl]; pl++)
local_store[pl] = tolower( local_store[pl] );
} else {
local_store = nfstrdup( "local" );
}
}
/* avoid name clashes with imap stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (!strcmp( pbox->store_name, local_store )) {
nfasprintf( &local_store, "local_%s", local_store );
goto gotsdup;
}
gotsdup:
/* avoid name clashes with other local stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_name && !strcmp( pbox->local_store_name, local_store )) {
nfasprintf( (char **)&box->local_store_name, "%s-%d", local_store, ++pbox->local_stores );
goto gotdup;
}
box->local_store_name = local_store;
box->local_stores = 1;
gotdup:
fprintf( fp, "MaildirStore %s\nPath %s\n", box->local_store_name, quotify( box->local_store_path ) );
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fputc( '\n', fp );
pbox = box;
gotstor:
if (box->alias)
cn = box->alias;
else {
cn = strrchr( box->path, '/' );
if (cn)
cn++;
else
cn = box->path;
}
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->alias)
scn = sbox->alias;
else {
scn = strrchr( sbox->path, '/' );
if (scn)
scn++;
else
scn = sbox->path;
}
if (mstrcmp( cn, scn ))
continue;
nfasprintf( (char **)&box->channel_name, "%s-%d", cn, ++sbox->channels );
goto gotchan;
}
box->channels = 1;
box->channel_name = cn;
gotchan:
fprintf( fp, "Channel %s\nMaster :%s:%s\nSlave :%s:%s\n",
box->channel_name, box->store_name, quotify( box->box ), pbox->local_store_name, quotify( local_box ) );
write_channel_parm( fp, box );
}
}
fclose( fp );
}
config_t *
find_box( const char *s )
{
config_t *p;
char *t;
if (starts_with( s, -1, Home, HomeLen ) && s[HomeLen] == '/')
s += HomeLen + 1;
for (p = boxes; p; p = p->next) {
if (!strcmp( s, p->path ) || (p->alias && !strcmp( s, p->alias )))
return p;
/* check to see if the full pathname was specified on the
* command line.
*/
t = expand_strdup( p->path );
if (!strcmp( s, t )) {
free( t );
return p;
}
free( t );
}
return 0;
}

View file

@ -1,265 +0,0 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <db.h>
static const char *subdirs[] = { "cur", "new", "tmp" };
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
static int
parse_info( const char *s )
{
unsigned i;
int flags;
flags = 0;
if (s && *(s + 1) == '2' && *(s + 2) == ',')
for (s += 3, i = 0; i < as(Flags); i++)
if (strchr( s, Flags[i] ))
flags |= (1 << i);
return flags;
}
typedef struct {
int uid, flags;
} msg_t;
static int
compare_uids( const void *l, const void *r )
{
return ((msg_t *)l)->uid - ((msg_t *)r)->uid;
}
static DBT key, value;
static struct flock lck;
void
convert( config_t *box )
{
DIR *d;
struct dirent *e;
char *s, *p, *mboxdir;
FILE *fp;
msg_t *msgs;
DB *db;
int i, ret, fd, uidval, maxuid, uid, rmsgs, nmsgs, uv[2];
unsigned u;
struct stat sb;
char buf[_POSIX_PATH_MAX], diumname[_POSIX_PATH_MAX],
uvname[_POSIX_PATH_MAX], sname[_POSIX_PATH_MAX],
iuvname[_POSIX_PATH_MAX], imuname[_POSIX_PATH_MAX],
ilname[_POSIX_PATH_MAX], iumname[_POSIX_PATH_MAX];
mboxdir = expand_strdup( box->path );
nfsnprintf( iuvname, sizeof(iuvname), "%s/isyncuidvalidity", mboxdir );
nfsnprintf( diumname, sizeof(iumname), "%s/.isyncuidmap.db", mboxdir );
nfsnprintf( uvname, sizeof(uvname), "%s/.uidvalidity", mboxdir );
if (stat( iuvname, &sb )) {
if (!stat( diumname, &sb ))
altmap++;
else if (!stat( uvname, &sb ))
altmap--;
err1:
free( mboxdir );
return;
}
for (i = 0; i < 3; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
if (stat( buf, &sb )) {
sys_error( "ERROR: cannot access %s", buf );
fprintf( stderr,
"ERROR: '%s' does not appear to be a valid maildir style mailbox\n",
mboxdir );
goto err1;
}
}
nfsnprintf( iumname, sizeof(iumname), "%s/isyncuidmap.db", mboxdir );
nfsnprintf( imuname, sizeof(imuname), "%s/isyncmaxuid", mboxdir );
nfsnprintf( ilname, sizeof(ilname), "%s/isynclock", mboxdir );
nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );
if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
sys_error( "Cannot create %s", ilname );
goto err1;
}
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
lck.l_type = F_WRLCK;
#endif
if (fcntl( fd, F_SETLKW, &lck )) {
sys_error( "Cannot lock %s", ilname );
err2:
close( fd );
goto err1;
}
if (!(fp = fopen( iuvname, "r" ))) {
sys_error( "Cannot open %s", iuvname );
goto err2;
}
if (fscanf( fp, "%d", &uidval ) != 1) {
sys_error( "Cannot read %s", iuvname );
err3:
fclose( fp );
goto err2;
}
fclose( fp );
if (!(fp = fopen( imuname, "r" ))) {
sys_error( "Cannot open %s", imuname );
goto err2;
}
if (fscanf( fp, "%d", &maxuid ) != 1) {
sys_error( "Cannot read %s", imuname );
goto err3;
}
fclose( fp );
if (!stat( iumname, &sb )) {
if (db_create( &db, 0, 0 )) {
fputs( "dbcreate failed\n", stderr );
goto err2;
}
if ((db->open)( db, 0, iumname, 0, DB_HASH, 0, 0 )) {
fputs( "cannot open db\n", stderr );
db->close( db, 0 );
goto err2;
}
altmap++;
} else {
db = 0;
altmap--;
}
msgs = 0;
rmsgs = 0;
nmsgs = 0;
for (i = 0; i < 2; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
if (!(d = opendir( buf ))) {
sys_error( "Cannot list %s", buf );
err4:
free( msgs );
if (db)
db->close( db, 0 );
goto err2;
}
while ((e = readdir( d ))) {
if (*e->d_name == '.')
continue;
s = strchr( e->d_name, ':' );
if (db) {
key.data = e->d_name;
key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND)
db->err( db, ret, "Maildir error: db->get()" );
continue;
}
uid = *(int *)value.data;
} else if ((p = strstr( e->d_name, ",U=" )))
uid = atoi( p + 3 );
else
continue;
if (nmsgs == rmsgs) {
rmsgs = rmsgs * 2 + 100;
msgs = nfrealloc( msgs, rmsgs * sizeof(msg_t) );
}
msgs[nmsgs].uid = uid;
msgs[nmsgs++].flags = parse_info( s );
}
closedir( d );
}
qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );
if (!(fp = fopen( sname, "w" ))) {
sys_error( "Cannot create %s", sname );
goto err4;
}
if (box->max_messages) {
if (!nmsgs)
i = maxuid;
else {
i = nmsgs - box->max_messages;
if (i < 0)
i = 0;
i = msgs[i].uid - 1;
}
} else
i = 0;
fprintf( fp, "%d:%d %d:%d:%d\n", uidval, maxuid, uidval, i, maxuid );
for (i = 0; i < nmsgs; i++) {
fprintf( fp, "%d %d ", msgs[i].uid, msgs[i].uid );
for (u = 0; u < as(Flags); u++)
if (msgs[i].flags & (1 << u))
fputc( Flags[u], fp );
fputc( '\n', fp );
}
fclose( fp );
if (db) {
key.data = (void *)"UIDVALIDITY";
key.size = 11;
uv[0] = uidval;
uv[1] = maxuid;
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Maildir error: db->put()" );
goto err4;
}
db->close( db, 0 );
if (rename( iumname, diumname )) {
sys_error( "Cannot rename %s to %s", iumname, diumname );
goto err4;
}
} else {
if (!(fp = fopen( uvname, "w" ))) {
sys_error( "Cannot create %s", uvname );
goto err4;
}
fprintf( fp, "%d\n%d\n", uidval, maxuid );
fclose( fp );
}
unlink( iuvname );
unlink( imuname );
close( fd );
unlink( ilname );
free( msgs );
free( mboxdir );
return;
}

View file

@ -1,328 +0,0 @@
.ig
\" isync - mbsync wrapper: IMAP4 to Maildir mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
\"
\" This program is free software; you can redistribute it and/or modify
\" it under the terms of the GNU General Public License as published by
\" the Free Software Foundation; either version 2 of the License, or
\" (at your option) any later version.
\"
\" This program is distributed in the hope that it will be useful,
\" but WITHOUT ANY WARRANTY; without even the implied warranty of
\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\" GNU General Public License for more details.
\"
\" You should have received a copy of the GNU General Public License
\" along with this program. If not, see <http://www.gnu.org/licenses/>.
\"
..
.TH isync 1 "2010 Feb 7"
..
.SH NAME
isync - synchronize IMAP4 and Maildir mailboxes
..
.SH SYNOPSIS
\fBisync\fR [\fIoptions\fR ...] {\fImailbox\fR ...|\fI-a\fR|\fI-l\fR}
..
.SH DESCRIPTION
\fBisync\fR is a command line application which synchronizes local
Maildir mailboxes with remote IMAP4 mailboxes, suitable for use in
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailboxes can
be maintained, and all flags are synchronized.
.br
\fBisync\fR is only a wrapper binary around \fBmbsync\fR to simplify upgrades.
It will automatically migrate the UID mapping from previous versions of
\fBisync\fR (even before 0.8) to the new format, and transparently call
\fBmbsync\fR. If you were using \fBisync\fR version 0.8 or 0.9.x you might
want to use \fBmdconvert\fR to convert the mailboxes to the more efficient
\fBnative\fR UID storage scheme after migrating them.
.br
\fBisync\fR is deprecated. Please use the \fB-w\fR option to permanently
migrate the configuration and start using \fBmbsync\fR directly.
..
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from \fIfile\fR.
By default, the configuration is read from ~/.isyncrc if it exists.
.TP
\fB-1\fR, \fB--one-to-one\fR
Instead of using the mailbox specifications in ~/.isyncrc, isync will pick up
all mailboxes from the local directory and remote folder and map them 1:1
onto each other according to their names.
.TP
\fB-I\fR, \fB--inbox\fR \fImailbox\fR
Exception to the 1:1 mapping created by -1: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the maildir).
.TP
\fB-a\fR, \fB--all\fR
Synchronize all mailboxes (either specified in ~/.isyncrc or determined by the
1:1 mapping).
.TP
\fB-l\fR, \fB--list\fR
Don't synchronize anything, but list all mailboxes and exit.
.TP
\fB-L\fR, \fB--create-local\fR
Automatically create the local Maildir mailbox if it doesn't already
exist.
.TP
\fB-R\fR, \fB--create-remote\fR
Automatically create the remote IMAP mailbox if it doesn't already exist.
.TP
\fB-C\fR, \fB--create\fR
Automatically create any mailboxes if they don't already exist.
This is simply a combination of -L and -R.
.TP
\fB-d\fR, \fB--delete\fR
Causes \fBisync\fR to propagate message deletions.
By default, \fIdead\fR messages are \fBnot\fR deleted.
.TP
\fB-e\fR, \fB--expunge\fR
Causes \fBisync\fR to permanently remove all messages marked for deletion.
By default, \fIdeleted\fR messages are \fBnot\fR expunged.
.TP
\fB-f\fR, \fB--fast\fR
Only fetch new messages existing on the server into the local mailbox.
Message deletions and flag changes will not be propagated.
.TP
\fB-h\fR, \fB--help\fR
Displays a summary of command line options
.TP
\fB-p\fR, \fB--port\fR \fIport\fR
Specifies the port on the IMAP server to connect to (default: 143 for imap,
993 for imaps)
.TP
\fB-q\fR, \fB--quiet\fR
Suppress informational messages.
If specified twice, suppress warning messages as well.
.TP
\fB-r\fR, \fB--remote\fR \fIbox\fR
Specifies the name of the remote IMAP mailbox to synchronize with
(Default: INBOX)
.TP
\fB-s\fR, \fB--host\fR [\fBimaps:\fR]\fIhost\fR
Specifies the hostname of the IMAP server
.TP
\fB-u\fR, \fB--user\fR \fIuser\fR
Specifies the login name to access the IMAP server (default: $USER)
.TP
\fB-P\fR, \fB--pass\fR \fIpassword\fR
Specifies the password to access the IMAP server (prompted for by default)
.TP
\fB-M\fR, \fB--maildir\fR \fIdir\fR
Specifies the location for your local mailboxes.
.TP
\fB-F\fR, \fB--folder\fR \fIfolder\fR/
Specifies the location for your remote mailboxes.
.TP
\fB-v\fR, \fB--version\fR
Displays \fBisync\fR version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enables \fIverbose\fR mode, which displays the IMAP4 network traffic.
.TP
\fB-D\fR, \fB--debug\fR
Enable printing of \fIdebug\fR messages.
.TP
\fB-w\fR, \fB--write\fR
Don't run \fBmbsync\fR, but instead write a permanent config file for it.
The UID mappings of all configured mailboxes will be migrated.
Note that most command line options that would affect an actual sync operation
will be incorporated into the new config file as well; exceptions are
--fast and --create[-remote|-local].
The name of the new config file is determined by replacing the last occurrence
of "isync" with "mbsync", or appending ".mbsync" if "isync" was not found.
.TP
\fB-W\fR, \fB--writeto\fR \fIfile\fR
Like \fB-w\fR, but use the specified name for the new config file.
..
.SH CONFIGURATION
\fBisync\fR by default reads \fI~/.isyncrc\fR to load configuration data.
Each non-empty line of the configuration file that does not start with a
hash mark consists of a command.
The following commands are understood:
.TP
\fBMailbox\fR \fIpath\fR
Defines a local Maildir mailbox. All configuration commands following this
line, up until the next \fIMailbox\fR command, apply to this mailbox only.
..
.TP
\fBHost\fR [\fBimaps:\fR]\fIname\fR
Defines the DNS name or IP address of the IMAP server. If the hostname is
prefixed with \fBimaps:\fR the connection is assumed to be a SSL connection
to port 993 (though you can change this by placing a \fBPort\fR command
\fBafter\fR the \fBHost\fR command).
Note that modern servers support SSL on the default port 143.
\fBisync\fR will always attempt to use SSL if available.
..
.TP
\fBPort\fR \fIport\fR
Defines the TCP port number of the IMAP server (Default: 143 for imap,
993 for imaps)
..
.TP
\fBBox\fR \fImailbox\fR
Defines the name of the remote IMAP mailbox associated with the local
Maildir mailbox (Default: INBOX)
..
.TP
\fBUser\fR \fIusername\fR
Defines the login name on the IMAP server (Default: current user)
..
.TP
\fBPass\fR \fIpassword\fR
Defines the password for \fIusername\fR on the IMAP server.
Note that this option is \fBNOT\fR required.
If no password is specified in the configuration file, \fBisync\fR
will prompt you for it.
..
.TP
\fBAlias\fR \fIstring\fR
Defines an alias for the mailbox which can be used as a shortcut on the
command line.
..
.TP
\fBCopyDeletedTo\fR \fImailbox\fR
Specifies the remote IMAP mailbox to copy deleted messages to prior to
expunging (Default: none).
..
.TP
\fBDelete\fR \fIyes\fR|\fIno\fR
Specifies whether message deletions are propagated. (Default: no).
\fBNOTE:\fR The \fI-d\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\fBExpunge\fR \fIyes\fR|\fIno\fR
Specifies whether deleted messages are expunged. (Default: no).
\fBNOTE:\fR The \fI-e\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\fBMailDir\fR \fIdirectory\fR
Specifies the location of your local mailboxes if a relative path is
specified in a \fIMailbox\fR command (Default: \fI~\fR).
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBFolder\fR \fIdirectory\fR/
Specifies the location of your IMAP mailboxes
specified in \fIBox\fR commands (Default: \fI""\fR).
\fBNOTE:\fR You \fBmust\fR append the hierarchy delimiter (usually
a slash) to this specification.
\fBNOTE 2:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the number of messages \fBisync\fR should keep in the local copy of a
mailbox.
This is useful for mailboxes where you keep a complete archive on the server,
but want to mirror only the last messages (for instance, for mailing lists).
The messages that were the first to arrive in the mailbox (independently of the
actual date of the message) will be deleted first.
Messages that are flagged (marked as important) and unread messages will not be
automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR.
(Default: 0)
..
.TP
\fBMaxSize\fR \fIbytes\fR
Messages larger than that many bytes will not be transferred over the wire.
This is useful for weeding out messages with large attachments.
If \fIbytes\fR is 0, the maximum file size is \fBunlimited\fR.
(Default: 0)
..
.TP
\fBTunnel\fR \fIcommand\fR
Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.TP
\fBUseNamespace\fR \fIyes\fR|\fIno\fR
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fIFolder\fR was specified.
(Default: \fIyes\fR)
..
.TP
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
If set to \fIyes\fR, \fBisync\fR will abort the connection if no CRAM-MD5
authentication is possible. (Default: \fIno\fR)
..
.TP
\fBRequireSSL\fR \fIyes\fR|\fIno\fR
\fBisync\fR will abort the connection if a TLS/SSL session cannot be
established with the IMAP server. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing additional X.509 certificates used to verify server
identities. Directly matched peer certificates are always trusted,
regardless of validity.
.br
Note that the system's default certificate store is always used
and should not be specified here.
..
.TP
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIno\fR)
..
.TP
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
..
.TP
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use TLSv1.x for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
..
.TP
\fBOneToOne\fR
\fBisync\fR will ignore any \fIMailbox\fR specifications and instead pick up
all mailboxes from the local \fIMailDir\fR and remote \fIFolder\fR and map
them 1:1 onto each other according to their names.
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBInbox\fR \fImailbox\fR
Exception to the OneToOne mapping: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the \fIMailDir\fR).
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.P
Configuration commands that appear prior to the first \fBMailbox\fR
command are considered to be \fIglobal\fR
options which are used as defaults when those specific options are not
specifically set for a defined Mailbox. For example, if you use the same
login name for several IMAP servers, you can put a \fBUser\fR command before
the first \fBMailbox\fR command, and then leave out the \fBUser\fR command
in the sections for each mailbox.
\fBisync\fR will then use the global value by default.
..
.SH FILES
.TP
.B ~/.isyncrc
Default configuration file
..
.SH BUGS
The configuration file takes precedence over command line options.
.br
Use -c /dev/null to work around.
.P
See the \fBINHERENT PROBLEMS\fR section in the \fBmbsync\fR man page, too.
..
.SH SEE ALSO
mbsync(1), mdconvert(1), mutt(1), maildir(5)
.P
Up to date information on \fBisync\fR can be found at http://isync.sf.net/
..
.SH AUTHORS
Originally written by Michael R. Elkins,
currently maintained by Oswald Buddenhagen.

View file

@ -1,116 +0,0 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <autodefs.h>
#include <sys/types.h>
#include <stdarg.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
#if __GNUC__ >= 7
# define FALLTHROUGH __attribute__((fallthrough));
#else
# define FALLTHROUGH
#endif
typedef struct config {
struct config *next;
const char *server_name;
const char *old_server_name;
int servers;
int old_servers;
char *host;
int port;
char *user;
char *pass;
char *tunnel;
unsigned int require_cram:1;
unsigned int require_ssl:1;
unsigned int use_imaps:1;
unsigned int use_sslv3:1;
unsigned int use_tlsv1:1;
char *cert_file;
const char *store_name;
int stores;
const char *local_store_name;
const char *local_store_path;
int local_stores;
char *copy_deleted_to;
unsigned int use_namespace:1;
const char *channel_name;
int channels;
const char *alias;
const char *box;
const char *path; /* path relative to .maildir, or absolute path */
int max_size;
unsigned int max_messages;
unsigned int expunge:1;
unsigned int delete:1;
} config_t;
extern int Quiet, Verbose, Debug;
extern const char *Home;
extern int HomeLen;
extern config_t global, *boxes;
extern const char *maildir, *xmaildir, *folder, *inbox;
extern int o2o, altmap, delete, expunge;
/* config.c */
void load_config( const char *, config_t *** );
void write_config( int );
char *expand_strdup( const char * );
config_t *find_box( const char * );
/* convert.c */
void convert( config_t * );
/* util.c */
char *next_arg( char ** );
void *nfmalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void sys_error( const char *, ... );
void ATTR_NORETURN oob( void );
#ifndef HAVE_MEMRCHR
void *memrchr( const void *s, int c, size_t n );
#endif
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );

View file

@ -1,55 +0,0 @@
# Global configuration section
# Values here are used as defaults for any following Mailbox section that
# doesn't specify it.
# SSL server certificate file
CertificateFile ~/.isync.certs
# by default, expunge deleted messages (same as -e on command line)
Expunge yes
# by default delete messages in the local mailbox which no longer exist
# on the server
Delete yes
# copy deleted messages to the IMAP "Trash" folder
CopyDeletedTo "Trash"
# my default username, if different from the local username
User me
#Port 143
#Box INBOX
# don't download messages larger than 200K bytes
MaxSize 200000
###
### work mailbox
###
Mailbox /home/me/Mail/work
Host work.host.com
Pass xxxxxxxx
# define a shortcut so I can just use "isync work" from the command line
Alias work
# don't auto expunge messages in this box (overridden by -e on command line)
Expunge no
###
### personal mailbox
###
Mailbox /home/me/Mail/personal
Host host.play.com
# use a non-default port for this connection
Port 6789
Alias personal
###
### Remote mailbox over a SSH tunnel
###
Mailbox /home/me/Mail/remote
Host host.remote.com
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Alias remote

View file

@ -1,438 +0,0 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
struct option Opts[] = {
{"write", 0, NULL, 'w' },
{"writeto", 0, NULL, 'W' },
{"all", 0, NULL, 'a' },
{"list", 0, NULL, 'l'},
{"config", 1, NULL, 'c'},
{"create", 0, NULL, 'C'},
{"create-local", 0, NULL, 'L'},
{"create-remote", 0, NULL, 'R'},
{"delete", 0, NULL, 'd'},
{"expunge", 0, NULL, 'e'},
{"fast", 0, NULL, 'f'},
{"help", 0, NULL, 'h'},
{"remote", 1, NULL, 'r'},
{"folder", 1, NULL, 'F'},
{"maildir", 1, NULL, 'M'},
{"one-to-one", 0, NULL, '1'},
{"inbox", 1, NULL, 'I'},
{"host", 1, NULL, 's'},
{"port", 1, NULL, 'p'},
{"debug", 0, NULL, 'D'},
{"quiet", 0, NULL, 'q'},
{"user", 1, NULL, 'u'},
{"pass", 1, NULL, 'P'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
static void ATTR_NORETURN
version( void )
{
puts( PACKAGE " " VERSION );
exit( 0 );
}
static void ATTR_NORETURN
usage( int code )
{
fputs(
PACKAGE " " VERSION " - mbsync wrapper: IMAP4 to maildir synchronizer\n"
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
"Copyright (C) 2002-2006,2008,2010-2012 Oswald Buddenhagen <ossi@users.sf.net>\n"
"Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
"usage:\n"
" " PACKAGE " [ flags ] mailbox [mailbox ...]\n"
" " PACKAGE " [ flags ] -a\n"
" " PACKAGE " [ flags ] -l\n"
" -a, --all synchronize all defined mailboxes\n"
" -l, --list list all defined mailboxes and exit\n"
" -L, --create-local create local maildir mailbox if nonexistent\n"
" -R, --create-remote create remote imap mailbox if nonexistent\n"
" -C, --create create both local and remote mailboxes if nonexistent\n"
" -d, --delete delete local msgs that don't exist on the server\n"
" -e, --expunge expunge deleted messages\n"
" -f, --fast only fetch new messages\n"
" -r, --remote BOX remote mailbox\n"
" -F, --folder DIR remote IMAP folder containing mailboxes\n"
" -M, --maildir DIR local directory containing mailboxes\n"
" -1, --one-to-one map every IMAP <folder>/box to <maildir>/box\n"
" -I, --inbox BOX map IMAP INBOX to <maildir>/BOX (exception to -1)\n"
" -s, --host HOST IMAP server address\n"
" -p, --port PORT server IMAP port\n"
" -u, --user USER IMAP user name\n"
" -P, --pass PASSWORD IMAP password\n"
" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)\n"
" -D, --debug print debugging messages\n"
" -V, --verbose verbose mode (display network traffic)\n"
" -q, --quiet don't display progress info\n"
" -v, --version display version\n"
" -h, --help display this help message\n\n"
"Note that this is a wrapper binary only; the \"real\" isync is named \"mbsync\".\n"
"Options to permanently transform your old isync configuration:\n"
" -w, --write write permanent mbsync configuration\n"
" -W, --writeto FILE write permanent mbsync configuration to FILE\n",
code ? stderr : stdout );
exit( code );
}
static const char *
strrstr( const char *h, const char *n )
{
char *p = strstr( h, n );
if (!p)
return 0;
do {
h = p;
p = strstr( h + 1, n );
} while (p);
return h;
}
static void
add_arg( char ***args, const char *arg )
{
int nu = 0;
if (*args)
for (; (*args)[nu]; nu++);
*args = nfrealloc( *args, sizeof(char *) * (nu + 2));
(*args)[nu] = nfstrdup( arg );
(*args)[nu + 1] = 0;
}
#define OP_FAST (1<<2)
#define OP_CREATE_REMOTE (1<<3)
#define OP_CREATE_LOCAL (1<<4)
int Quiet, Verbose, Debug;
config_t global, *boxes;
const char *maildir, *xmaildir, *folder, *inbox;
int o2o, altmap, delete, expunge;
const char *Home;
int HomeLen;
int
main( int argc, char **argv )
{
config_t *box, **stor;
char *config = 0, *outconfig = 0, **args;
int i, pl, fd, mod, all, list, ops, writeout;
struct stat st;
char path1[_POSIX_PATH_MAX], path2[_POSIX_PATH_MAX];
if (!(Home = getenv("HOME"))) {
fputs( "Fatal: $HOME not set\n", stderr );
return 1;
}
HomeLen = strlen( Home );
/* defaults */
/* XXX the precedence is borked:
it's defaults < cmdline < file instead of defaults < file < cmdline */
#ifdef BSD
global.user = getenv( "USER" );
#else
global.user = getenv( "LOGNAME" );
#endif
global.port = 143;
global.box = ""; /* implicit INBOX in resulting Master/Slave entries */
global.use_namespace = 1;
global.require_ssl = 1;
global.use_tlsv1 = 1;
folder = "";
maildir = "~";
xmaildir = Home;
#define FLAGS "wW:alCLRc:defhp:qu:P:r:F:M:1I:s:vVD"
mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
#ifdef HAVE_GETOPT_LONG
while ((i = getopt_long( argc, argv, FLAGS, Opts, NULL )) != -1)
#else
while ((i = getopt( argc, argv, FLAGS )) != -1)
#endif
{
switch (i) {
case 'W':
outconfig = optarg;
FALLTHROUGH
case 'w':
writeout = 1;
break;
case 'l':
list = 1;
FALLTHROUGH
case 'a':
all = 1;
break;
case '1':
o2o = 1;
mod = 1;
break;
case 'C':
ops |= OP_CREATE_REMOTE|OP_CREATE_LOCAL;
break;
case 'L':
ops |= OP_CREATE_LOCAL;
break;
case 'R':
ops |= OP_CREATE_REMOTE;
break;
case 'c':
config = optarg;
break;
case 'd':
delete = 1;
break;
case 'e':
expunge = 1;
break;
case 'f':
ops |= OP_FAST;
break;
case 'p':
global.port = atoi( optarg );
mod = 1;
break;
case 'r':
global.box = optarg;
mod = 1;
break;
case 'F':
folder = optarg;
mod = 1;
break;
case 'M':
maildir = optarg;
mod = 1;
break;
case 'I':
inbox = optarg;
mod = 1;
break;
case 's':
#ifdef HAVE_LIBSSL
if (!strncasecmp( "imaps:", optarg, 6 )) {
global.use_imaps = 1;
global.port = 993;
global.use_sslv3 = 1;
optarg += 6;
}
#endif
global.host = optarg;
mod = 1;
break;
case 'u':
global.user = optarg;
mod = 1;
break;
case 'P':
global.pass = optarg;
mod = 1;
break;
case 'D':
Debug = 1;
break;
case 'V':
Verbose++;
break;
case 'q':
Quiet++;
break;
case 'v':
version();
case 'h':
usage( 0 );
default:
usage( 1 );
}
}
if (!writeout)
fputs( "Notice: please run 'isync -w' and start using 'mbsync' directly.\n", stderr );
if (config) {
if (*config != '/') {
if (!getcwd( path1, sizeof(path1) )) {
fprintf( stderr, "Can't obtain working directory\n" );
return 1;
}
pl = strlen( path1 );
nfsnprintf( path1 + pl, sizeof(path1) - pl, "/%s", config );
config = path1;
}
} else {
nfsnprintf( path1, sizeof(path1), "%s/.isyncrc", Home );
config = path1;
}
stor = &boxes;
load_config( config, &stor );
if (!all && !o2o)
for (i = optind; argv[i]; i++)
if (!find_box( argv[i] )) {
box = nfmalloc( sizeof(config_t) );
memcpy( box, &global, sizeof(config_t) );
box->path = argv[i];
*stor = box;
stor = &box->next;
mod = 1;
}
if (writeout) {
all = 1;
if (mod)
fprintf( stderr,
"Warning: command line switches that influence the resulting config file\n"
"have been supplied.\n" );
} else {
if (!argv[optind] && !all) {
fprintf( stderr, "No mailbox specified. Try isync -h\n" );
return 1;
}
}
if (all) {
if (o2o) {
DIR * dir;
struct dirent *de;
if (!(dir = opendir( xmaildir ))) {
sys_error( "Cannot list '%s'", xmaildir );
return 1;
}
while ((de = readdir( dir ))) {
if (*de->d_name == '.')
continue;
nfsnprintf( path2, sizeof(path2), "%s/%s/cur", xmaildir, de->d_name );
if (stat( path2, &st ) || !S_ISDIR( st.st_mode ))
continue;
global.path = de->d_name;
global.box = (inbox && !strcmp( inbox, global.path )) ?
"INBOX" : global.path;
convert( &global );
}
closedir( dir );
} else
for (box = boxes; box; box = box->next)
convert( box );
} else {
for (i = optind; argv[i]; i++)
if (o2o) {
global.path = argv[i];
global.box =
(inbox && !strcmp( global.path, inbox )) ?
"INBOX" : global.path;
convert( &global );
} else
convert( find_box( argv[i] ) );
}
if (writeout) {
if (!outconfig) {
const char *p = strrchr( config, '/' );
if (!p)
p = config;
p = strrstr( p, "isync" );
if (!p)
nfsnprintf( path2, sizeof(path2), "%s.mbsync", config );
else
nfsnprintf( path2, sizeof(path2), "%.*smb%s", p - config, config, p + 1 );
outconfig = path2;
}
if ((fd = creat( outconfig, 0666 )) < 0) {
sys_error( "Error: cannot create config file '%s'", outconfig );
return 1;
}
} else {
strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
if ((fd = mkstemp( path2 )) < 0) {
sys_error( "Error: cannot create temporary config file" );
return 1;
}
}
write_config( fd );
if (writeout)
return 0;
args = 0;
add_arg( &args, "mbsync" );
while (--Verbose >= 0)
add_arg( &args, "-V" );
if (Debug)
add_arg( &args, "-D" );
for (; Quiet; Quiet--)
add_arg( &args, "-q" );
add_arg( &args, "-cT" );
add_arg( &args, path2 );
if (ops & OP_FAST)
add_arg( &args, "-Ln" );
if (ops & OP_CREATE_REMOTE)
add_arg( &args, "-Cm" );
if (ops & OP_CREATE_LOCAL)
add_arg( &args, "-Cs" );
if (list)
add_arg( &args, "-lC" );
if (o2o) {
if (all)
add_arg( &args, "o2o" );
else {
char buf[1024];
strcpy( buf, "o2o:" );
strcat( buf, argv[optind] );
while (argv[++optind]) {
strcat( buf, "," );
strcat( buf, argv[optind] );
}
add_arg( &args, buf );
}
} else {
if (all)
add_arg( &args, "-a" );
else
for (; argv[optind]; optind++)
add_arg( &args, find_box( argv[optind] )->channel_name );
}
execvp( args[0], args );
sys_error( "Cannot execute %s", args[0] );
return 1;
}

View file

@ -1,208 +0,0 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
void
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
va_start( va, msg );
if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
perror( buf );
}
char *
next_arg( char **s )
{
char *ret;
if (!s || !*s)
return 0;
while (isspace( (unsigned char) **s ))
(*s)++;
if (!**s) {
*s = 0;
return 0;
}
if (**s == '"') {
++*s;
ret = *s;
*s = strchr( *s, '"' );
} else {
ret = *s;
while (**s && !isspace( (unsigned char) **s ))
(*s)++;
}
if (*s) {
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
}
return ret;
}
#ifndef HAVE_VASPRINTF
static int
vasprintf( char **strp, const char *fmt, va_list ap )
{
int len;
char tmp[1024];
if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = malloc( len + 1 )))
return -1;
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
return len;
}
#endif
#ifndef HAVE_MEMRCHR
void *
memrchr( const void *s, int c, size_t n )
{
u_char *b = (u_char *)s, *e = b + n;
while (--e >= b)
if (*e == c)
return (void *)e;
return 0;
}
#endif
#ifndef HAVE_STRNLEN
int
strnlen( const char *str, size_t maxlen )
{
size_t len;
/* It's tempting to use memchr(), but it's allowed to read past the end of the actual string. */
for (len = 0; len < maxlen && str[len]; len++) {}
return len;
}
#endif
int
starts_with( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
equals( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl == cmpl) && !memcmp( str, cmp, cmpl );
}
void
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
va_end( va );
return ret;
}
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
abort();
}
void *
nfmalloc( size_t sz )
{
void *ret;
if (!(ret = malloc( sz )))
oom();
return ret;
}
void *
nfrealloc( void *mem, size_t sz )
{
char *ret;
if (!(ret = realloc( mem, sz )) && sz)
oom();
return ret;
}
char *
nfstrdup( const char *str )
{
char *ret;
if (!(ret = strdup( str )))
oom();
return ret;
}
int
nfvasprintf( char **str, const char *fmt, va_list va )
{
int ret = vasprintf( str, fmt, va );
if (ret < 0)
oom();
return ret;
}
int
nfasprintf( char **str, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
ret = nfvasprintf( str, fmt, va );
va_end( va );
return ret;
}

View file

@ -34,7 +34,7 @@
#include <stdlib.h>
#include <stdio.h>
store_conf_t *stores;
static store_conf_t *stores;
char *
get_arg( conffile_t *cfile, int required, int *comment )
@ -54,7 +54,7 @@ get_arg( conffile_t *cfile, int required, int *comment )
error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
cfile->err = 1;
}
ret = 0;
ret = NULL;
} else {
for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
p++;
@ -74,19 +74,19 @@ get_arg( conffile_t *cfile, int required, int *comment )
if (escaped) {
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
cfile->err = 1;
ret = 0;
ret = NULL;
}
if (quoted) {
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
cfile->err = 1;
ret = 0;
ret = NULL;
}
}
cfile->rest = p;
return ret;
}
int
char
parse_bool( conffile_t *cfile )
{
if (!strcasecmp( cfile->val, "yes" ) ||
@ -121,13 +121,13 @@ parse_int( conffile_t *cfile )
return ret;
}
int
uint
parse_size( conffile_t *cfile )
{
char *p;
int ret;
uint ret;
ret = strtol (cfile->val, &p, 10);
ret = strtoul( cfile->val, &p, 10 );
if (*p == 'k' || *p == 'K')
ret *= 1024, p++;
else if (*p == 'm' || *p == 'M')
@ -174,21 +174,21 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
else if (!strcasecmp( "Flags", arg ))
*cops |= OP_FLAGS;
else if (!strcasecmp( "PullReNew", arg ))
conf->ops[S] |= OP_RENEW;
conf->ops[N] |= OP_RENEW;
else if (!strcasecmp( "PullNew", arg ))
conf->ops[S] |= OP_NEW;
conf->ops[N] |= OP_NEW;
else if (!strcasecmp( "PullDelete", arg ))
conf->ops[S] |= OP_DELETE;
conf->ops[N] |= OP_DELETE;
else if (!strcasecmp( "PullFlags", arg ))
conf->ops[S] |= OP_FLAGS;
conf->ops[N] |= OP_FLAGS;
else if (!strcasecmp( "PushReNew", arg ))
conf->ops[M] |= OP_RENEW;
conf->ops[F] |= OP_RENEW;
else if (!strcasecmp( "PushNew", arg ))
conf->ops[M] |= OP_NEW;
conf->ops[F] |= OP_NEW;
else if (!strcasecmp( "PushDelete", arg ))
conf->ops[M] |= OP_DELETE;
conf->ops[F] |= OP_DELETE;
else if (!strcasecmp( "PushFlags", arg ))
conf->ops[M] |= OP_FLAGS;
conf->ops[F] |= OP_FLAGS;
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
@ -196,8 +196,8 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
cfile->file, cfile->line, arg );
cfile->err = 1;
}
while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
conf->ops[M] |= XOP_HAVE_TYPE;
while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "SyncState", cfile->cmd ))
conf->sync_state = expand_strdup( cfile->val );
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
@ -214,17 +214,23 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
do {
if (!strcasecmp( "Both", arg )) {
*cops |= op;
} else if (!strcasecmp( "Master", arg )) {
conf->ops[M] |= op;
} else if (!strcasecmp( "Slave", arg )) {
conf->ops[S] |= op;
} else if (!strcasecmp( "Far", arg )) {
conf->ops[F] |= op;
} else if (!strcasecmp( "Master", arg )) { // Pre-1.4 legacy
conf->ops[F] |= op;
cfile->ms_warn = 1;
} else if (!strcasecmp( "Near", arg )) {
conf->ops[N] |= op;
} else if (!strcasecmp( "Slave", arg )) { // Pre-1.4 legacy
conf->ops[N] |= op;
cfile->ms_warn = 1;
} else if (strcasecmp( "None", arg )) {
error( "%s:%d: invalid %s arg '%s'\n",
cfile->file, cfile->line, boxOps[i].name, arg );
cfile->err = 1;
}
} while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
conf->ops[M] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
return 1;
}
}
@ -239,7 +245,7 @@ getcline( conffile_t *cfile )
char *arg;
int comment;
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, 0 ))) {
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL ))) {
error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg );
cfile->err = 1;
}
@ -251,7 +257,7 @@ getcline( conffile_t *cfile )
continue;
return 1;
}
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, 0 )))
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
continue;
return 1;
}
@ -265,25 +271,25 @@ merge_ops( int cops, int ops[] )
int aops, op;
uint i;
aops = ops[M] | ops[S];
if (ops[M] & XOP_HAVE_TYPE) {
aops = ops[F] | ops[N];
if (ops[F] & XOP_HAVE_TYPE) {
if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) {
cfl:
error( "Conflicting Sync args specified.\n" );
return 1;
}
ops[M] |= cops & OP_MASK_TYPE;
ops[S] |= cops & OP_MASK_TYPE;
ops[F] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
if (cops & XOP_PULL) {
if (ops[S] & OP_MASK_TYPE)
if (ops[N] & OP_MASK_TYPE)
goto cfl;
ops[S] |= OP_MASK_TYPE;
ops[N] |= OP_MASK_TYPE;
}
if (cops & XOP_PUSH) {
if (ops[M] & OP_MASK_TYPE)
if (ops[F] & OP_MASK_TYPE)
goto cfl;
ops[M] |= OP_MASK_TYPE;
ops[F] |= OP_MASK_TYPE;
}
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
if (!(cops & OP_MASK_TYPE))
@ -291,27 +297,27 @@ merge_ops( int cops, int ops[] )
else if (!(cops & XOP_MASK_DIR))
cops |= XOP_PULL|XOP_PUSH;
if (cops & XOP_PULL)
ops[S] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
if (cops & XOP_PUSH)
ops[M] |= cops & OP_MASK_TYPE;
ops[F] |= cops & OP_MASK_TYPE;
}
}
for (i = 0; i < as(boxOps); i++) {
op = boxOps[i].op;
if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (ops[F] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (aops & cops & op) {
error( "Conflicting %s args specified.\n", boxOps[i].name );
return 1;
}
ops[M] |= cops & op;
ops[S] |= cops & op;
ops[F] |= cops & op;
ops[N] |= cops & op;
}
}
return 0;
}
int
load_config( const char *where, int pseudo )
load_config( const char *where )
{
conffile_t cfile;
store_conf_t *store, **storeapp = &stores;
@ -319,19 +325,18 @@ load_config( const char *where, int pseudo )
group_conf_t *group, **groupapp = &groups;
string_list_t *chanlist, **chanlistapp;
char *arg, *p;
int len, cops, gcops, max_size, ms, i;
uint len, max_size;
int cops, gcops, glob_ok, fn, i;
char path[_POSIX_PATH_MAX];
char buf[1024];
if (!where) {
assert( !pseudo );
nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home );
cfile.file = path;
} else
cfile.file = where;
if (!pseudo)
info( "Reading configuration file %s\n", cfile.file );
info( "Reading configuration file %s\n", cfile.file );
if (!(cfile.fp = fopen( cfile.file, "r" ))) {
sys_error( "Cannot open config file '%s'", cfile.file );
@ -342,9 +347,11 @@ load_config( const char *where, int pseudo )
cfile.bufl = sizeof(buf) - 1;
cfile.line = 0;
cfile.err = 0;
cfile.rest = 0;
cfile.ms_warn = 0;
cfile.rest = NULL;
gcops = 0;
glob_ok = 1;
global_conf.expire_unread = -1;
reloop:
while (getcline( &cfile )) {
@ -354,13 +361,14 @@ load_config( const char *where, int pseudo )
if (drivers[i]->parse_store( &cfile, &store )) {
if (store) {
if (!store->max_size)
store->max_size = INT_MAX;
store->max_size = UINT_MAX;
if (!store->flat_delim)
store->flat_delim = "";
*storeapp = store;
storeapp = &store->next;
*storeapp = 0;
*storeapp = NULL;
}
glob_ok = 0;
goto reloop;
}
if (!strcasecmp( "Channel", cfile.cmd ))
@ -371,7 +379,7 @@ load_config( const char *where, int pseudo )
channel->expire_unread = global_conf.expire_unread;
channel->use_internal_date = global_conf.use_internal_date;
cops = 0;
max_size = -1;
max_size = UINT_MAX;
while (getcline( &cfile ) && cfile.cmd) {
if (!strcasecmp( "MaxSize", cfile.cmd ))
max_size = parse_size( &cfile );
@ -381,13 +389,21 @@ load_config( const char *where, int pseudo )
arg = cfile.val;
do
add_string_list( &channel->patterns, arg );
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 )));
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
}
else if (!strcasecmp( "Master", cfile.cmd )) {
ms = M;
else if (!strcasecmp( "Far", cfile.cmd )) {
fn = F;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) {
ms = S;
} else if (!strcasecmp( "Master", cfile.cmd )) { // Pre-1.4 legacy
fn = F;
goto olinkst;
} else if (!strcasecmp( "Near", cfile.cmd )) {
fn = N;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) { // Pre-1.4 legacy
fn = N;
olinkst:
cfile.ms_warn = 1;
linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
error( "%s:%d: malformed mailbox spec\n",
@ -398,7 +414,7 @@ load_config( const char *where, int pseudo )
*p = 0;
for (store = stores; store; store = store->next)
if (!strcmp( store->name, cfile.val + 1 )) {
channel->stores[ms] = store;
channel->stores[fn] = store;
goto stpcom;
}
error( "%s:%d: unknown store '%s'\n",
@ -407,29 +423,32 @@ load_config( const char *where, int pseudo )
continue;
stpcom:
if (*++p)
channel->boxes[ms] = nfstrdup( p );
channel->boxes[fn] = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, channel )) {
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
error( "%s:%d: keyword '%s' is not recognized in Channel sections\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
}
}
if (!channel->stores[M]) {
error( "channel '%s' refers to no master store\n", channel->name );
if (!channel->stores[F]) {
error( "channel '%s' refers to no far side store\n", channel->name );
cfile.err = 1;
} else if (!channel->stores[S]) {
error( "channel '%s' refers to no slave store\n", channel->name );
} else if (!channel->stores[N]) {
error( "channel '%s' refers to no near side store\n", channel->name );
cfile.err = 1;
} else if (merge_ops( cops, channel->ops ))
cfile.err = 1;
else {
if (max_size >= 0) {
if (max_size != UINT_MAX) {
if (!max_size)
max_size = INT_MAX;
channel->stores[M]->max_size = channel->stores[S]->max_size = max_size;
max_size = UINT_MAX;
channel->stores[F]->max_size = channel->stores[N]->max_size = max_size;
}
*channelapp = channel;
channelapp = &channel->next;
}
glob_ok = 0;
goto reloop;
}
else if (!strcasecmp( "Group", cfile.cmd ))
{
@ -437,21 +456,19 @@ load_config( const char *where, int pseudo )
group->name = nfstrdup( cfile.val );
*groupapp = group;
groupapp = &group->next;
*groupapp = 0;
*groupapp = NULL;
chanlistapp = &group->channels;
*chanlistapp = 0;
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 ))) {
*chanlistapp = NULL;
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL ))) {
addone:
len = strlen( arg );
chanlist = nfmalloc( sizeof(*chanlist) + len );
memcpy( chanlist->string, arg, len + 1 );
*chanlistapp = chanlist;
chanlistapp = &chanlist->next;
*chanlistapp = 0;
*chanlistapp = NULL;
}
while (getcline( &cfile )) {
if (!cfile.cmd)
goto reloop;
while (getcline( &cfile ) && cfile.cmd) {
if (!strcasecmp( "Channel", cfile.cmd ) ||
!strcasecmp( "Channels", cfile.cmd ))
{
@ -460,12 +477,13 @@ load_config( const char *where, int pseudo )
}
else
{
error( "%s:%d: unknown keyword '%s'\n",
error( "%s:%d: keyword '%s' is not recognized in Group sections\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
}
}
break;
glob_ok = 0;
goto reloop;
}
else if (!strcasecmp( "FSync", cfile.cmd ))
{
@ -487,14 +505,14 @@ load_config( const char *where, int pseudo )
else if (!strcasecmp( "BufferLimit", cfile.cmd ))
{
BufferLimit = parse_size( &cfile );
if (BufferLimit <= 0) {
error( "%s:%d: BufferLimit must be positive\n", cfile.file, cfile.line );
if (!BufferLimit) {
error( "%s:%d: BufferLimit cannot be zero\n", cfile.file, cfile.line );
cfile.err = 1;
}
}
else if (!getopt_helper( &cfile, &gcops, &global_conf ))
{
error( "%s:%d: unknown section keyword '%s'\n",
error( "%s:%d: '%s' is not a recognized section-starting or global keyword\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
while (getcline( &cfile ))
@ -502,12 +520,17 @@ load_config( const char *where, int pseudo )
goto reloop;
break;
}
if (!glob_ok) {
error( "%s:%d: global options may not follow sections\n",
cfile.file, cfile.line );
cfile.err = 1;
}
}
fclose (cfile.fp);
if (cfile.ms_warn)
warn( "Notice: Master/Slave are deprecated; use Far/Near instead.\n" );
cfile.err |= merge_ops( gcops, global_conf.ops );
if (!global_conf.sync_state)
global_conf.sync_state = expand_strdup( "~/." EXE "/" );
if (!cfile.err && pseudo)
unlink( where );
return cfile.err;
}

View file

@ -32,6 +32,7 @@ typedef struct {
int bufl;
int line;
int err;
int ms_warn;
char *cmd, *val, *rest;
} conffile_t;
@ -40,11 +41,11 @@ typedef struct {
char *get_arg( conffile_t *cfile, int required, int *comment );
int parse_bool( conffile_t *cfile );
char parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
int parse_size( conffile_t *cfile );
uint parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int ops[] );
int load_config( const char *filename, int pseudo );
int load_config( const char *filename );
#endif

View file

@ -27,10 +27,10 @@
driver_t *drivers[N_DRIVERS] = { &maildir_driver, &imap_driver };
int
uint
count_generic_messages( message_t *msgs )
{
int count = 0;
uint count = 0;
for (; msgs; msgs = msgs->next)
count++;
return count;
@ -49,7 +49,7 @@ free_generic_messages( message_t *msgs )
}
void
parse_generic_store( store_conf_t *store, conffile_t *cfg )
parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type )
{
if (!strcasecmp( "Trash", cfg->cmd )) {
store->trash = nfstrdup( cfg->val );
@ -72,7 +72,7 @@ parse_generic_store( store_conf_t *store, conffile_t *cfg )
}
store->flat_delim = nfstrdup( cfg->val );
} else {
error( "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd );
error( "%s:%d: keyword '%s' is not recognized in %s sections\n", cfg->file, cfg->line, cfg->cmd, type );
cfg->err = 1;
}
}

View file

@ -31,51 +31,65 @@ typedef struct driver driver_t;
#define FAIL_WAIT 1 /* Retry after some time (if at all) */
#define FAIL_FINAL 2 /* Don't retry until store reconfiguration */
typedef struct store_conf {
struct store_conf *next;
char *name;
driver_t *driver;
const char *path; /* should this be here? its interpretation is driver-specific */
const char *flat_delim;
const char *map_inbox;
const char *trash;
int max_size; /* off_t is overkill */
#define STORE_CONF \
struct store_conf *next; \
char *name; \
driver_t *driver; \
const char *flat_delim; \
const char *map_inbox; \
const char *trash; \
uint max_size; /* off_t is overkill */ \
char trash_remote_new, trash_only_new;
typedef struct store_conf {
STORE_CONF
} store_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync! */
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* The order is according to alphabetical maildir flag sort */
#define F_DRAFT (1<<0) /* Draft */
#define F_FLAGGED (1<<1) /* Flagged */
#define F_ANSWERED (1<<2) /* Replied */
#define F_SEEN (1<<3) /* Seen */
#define F_DELETED (1<<4) /* Trashed */
#define NUM_FLAGS 5
#define F_FORWARDED (1<<2) /* Passed */
#define F_ANSWERED (1<<3) /* Replied */
#define F_SEEN (1<<4) /* Seen */
#define F_DELETED (1<<5) /* Trashed */
#define NUM_FLAGS 6
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
#define M_FLAGS (1<<2) /* flags fetched */
// The following are only for IMAP FETCH response parsing
#define M_DATE (1<<3)
#define M_SIZE (1<<4)
#define M_BODY (1<<5)
#define M_HEADER (1<<6)
#define TUIDL 12
typedef struct message {
struct message *next;
struct sync_rec *srec;
char *msgid; /* owned */
/* string_list_t *keywords; */
int size; /* zero implies "not fetched" */
uint uid;
uchar flags, status;
#define MESSAGE(message) \
message *next; \
struct sync_rec *srec; \
char *msgid; /* owned */ \
/* string_list_t *keywords; */ \
uint size; /* zero implies "not fetched" */ \
uint uid; \
uchar flags, status; \
char tuid[TUIDL];
typedef struct message {
MESSAGE(struct message)
} message_t;
/* For opts, both in store and driver_t->select() */
#define OPEN_OLD (1<<0)
#define OPEN_NEW (1<<1)
#define OPEN_FLAGS (1<<2)
#define OPEN_OLD_SIZE (1<<3)
// For driver_t->prepare_load_box(), which may amend the passed flags.
// The drivers don't use the first two, but may set them if loading the
// particular range is required to handle some other flag; note that these
// ranges may overlap.
#define OPEN_OLD (1<<0) // Paired messages *in* this store.
#define OPEN_NEW (1<<1) // Messages (possibly) not yet propagated *from* this store.
#define OPEN_FLAGS (1<<2) // Note that fetch_msg() gets the flags regardless.
#define OPEN_NEW_SIZE (1<<4)
#define OPEN_EXPUNGE (1<<5)
#define OPEN_SETFLAGS (1<<6)
@ -85,15 +99,18 @@ typedef struct message {
#define UIDVAL_BAD ((uint)-1)
#define STORE(store) \
store *next; \
driver_t *driver; \
store##_conf *conf; /* foreign */
typedef struct store {
struct store *next;
driver_t *driver;
store_conf_t *conf; /* foreign */
STORE(struct store)
} store_t;
typedef struct {
char *data;
int len;
uint len;
time_t date;
uchar flags;
} msg_data_t;
@ -109,6 +126,9 @@ typedef struct {
#define DRV_CANCELED 4
/* All memory belongs to the driver's user, unless stated otherwise. */
// If the driver is NOT DRV_ASYNC, memory owned by the driver returned
// through callbacks MUST remain valid until a related subsequent command
// is invoked, as the proxy driver may deliver these pointers with delay.
/*
This flag says that the driver CAN store messages with CRLFs,
@ -120,12 +140,16 @@ typedef struct {
This flag says that the driver will act upon (DFlags & VERBOSE).
*/
#define DRV_VERBOSE 2
/*
This flag says that the driver operates asynchronously.
*/
#define DRV_ASYNC 4
#define LIST_INBOX 1
#define LIST_PATH 2
#define LIST_PATH_MAYBE 4
#define xint int // For auto-generation of appropriate printf() formats.
#define xint uint // For auto-generation of appropriate printf() formats.
struct driver {
/* Return driver capabilities. */
@ -174,10 +198,13 @@ struct driver {
/* Open the selected mailbox.
* Note that this should not directly complain about failure to open. */
void (*open_box)( store_t *ctx,
void (*cb)( int sts, int uidvalidity, void *aux ), void *aux );
void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux );
/* Return the minimal UID the next stored message will have. */
int (*get_uidnext)( store_t *ctx );
uint (*get_uidnext)( store_t *ctx );
/* Return the flags that can be stored in the selected mailbox. */
xint (*get_supported_flags)( store_t *ctx );
/* Confirm that the open mailbox is empty. */
int (*confirm_box_empty)( store_t *ctx );
@ -201,16 +228,18 @@ struct driver {
* Consider only messages with UIDs between minuid and maxuid (inclusive)
* and those named in the excs array (smaller than minuid).
* The driver takes ownership of the excs array.
* Messages starting with newuid need to have the TUID populated when OPEN_FIND is set.
* Messages up to seenuid need to have the Message-Id populated when OPEN_OLD_IDS is set.
* Messages up to seenuid need to have the size populated when OPEN_OLD_SIZE is set;
* likewise messages above seenuid when OPEN_NEW_SIZE is set.
* Messages starting with finduid need to have the TUID populated when OPEN_FIND is set.
* Messages up to pairuid need to have the Message-Id populated when OPEN_OLD_IDS is set.
* Messages up to newuid need to have the size populated when OPEN_OLD_SIZE is set;
* likewise messages above newuid when OPEN_NEW_SIZE is set.
* The returned message list remains owned by the driver. */
void (*load_box)( store_t *ctx, uint minuid, uint maxuid, uint newuid, uint seenuid, uint_array_t excs,
void (*load_box)( store_t *ctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs,
void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux );
/* Fetch the contents and flags of the given message from the current mailbox. */
void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data,
/* Fetch the contents and flags of the given message from the current mailbox.
* If minimal is non-zero, fetch only a placeholder for the requested message -
* ideally, this is precisely the header, but it may be more. */
void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data, int minimal,
void (*cb)( int sts, void *aux ), void *aux );
/* Store the given message to either the current mailbox or the trash folder.
@ -229,17 +258,17 @@ struct driver {
* a pre-fetched one (in which case the in-memory representation is updated),
* or it may be identifed by UID only. The operation may be delayed until commit()
* is called. */
void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del, /* msg can be null, therefore uid as a fallback */
void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del,
void (*cb)( int sts, void *aux ), void *aux );
/* Move the given message from the current mailbox to the trash folder.
* This may expunge the original message immediately, but it needn't to. */
void (*trash_msg)( store_t *ctx, message_t *msg, /* This may expunge the original message immediately, but it needn't to */
void (*trash_msg)( store_t *ctx, message_t *msg,
void (*cb)( int sts, void *aux ), void *aux );
/* Expunge deleted messages from the current mailbox and close it.
* There is no need to explicitly close a mailbox if no expunge is needed. */
void (*close_box)( store_t *ctx, /* IMAP-style: expunge inclusive */
void (*close_box)( store_t *ctx,
void (*cb)( int sts, void *aux ), void *aux );
/* Cancel queued commands which are not in flight yet; they will have their
@ -253,16 +282,16 @@ struct driver {
void (*commit_cmds)( store_t *ctx );
/* Get approximate amount of memory occupied by the driver. */
int (*get_memory_usage)( store_t *ctx );
uint (*get_memory_usage)( store_t *ctx );
/* Get the FAIL_* state of the driver. */
int (*get_fail_state)( store_conf_t *conf );
};
int count_generic_messages( message_t * );
uint count_generic_messages( message_t * );
void free_generic_messages( message_t * );
void parse_generic_store( store_conf_t *store, conffile_t *cfg );
void parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type );
store_t *proxy_alloc_store( store_t *real_ctx, const char *label );

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -21,18 +21,27 @@
#include "driver.h"
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
typedef struct {
store_t gen;
const char *label; // foreign
int ref_count;
driver_t *real_driver;
store_t *real_store;
typedef struct gen_cmd gen_cmd_t;
void (*bad_callback)( void *aux );
void *bad_callback_aux;
typedef union proxy_store {
store_t gen;
struct {
STORE(union proxy_store)
const char *label; // foreign
uint ref_count;
driver_t *real_driver;
store_t *real_store;
gen_cmd_t *done_cmds, **done_cmds_append;
gen_cmd_t *check_cmds, **check_cmds_append;
wakeup_t wakeup;
void (*bad_callback)( void *aux );
void *bad_callback_aux;
};
} proxy_store_t;
static void ATTR_PRINTFLIKE(1, 2)
@ -55,10 +64,13 @@ debugn( const char *msg, ... )
va_end( va );
}
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* The order is according to alphabetical maildir flag sort */
static const char Flags[] = { 'D', 'F', 'P', 'R', 'S', 'T' };
static char *
proxy_make_flags( int flags, char *buf )
proxy_make_flags( uchar flags, char *buf )
{
uint i, d;
@ -72,20 +84,38 @@ proxy_make_flags( int flags, char *buf )
static void
proxy_store_deref( proxy_store_t *ctx )
{
if (!--ctx->ref_count)
if (!--ctx->ref_count) {
assert( !pending_wakeup( &ctx->wakeup ) );
free( ctx );
}
}
static int curr_tag;
typedef struct {
int ref_count;
int tag;
proxy_store_t *ctx;
} gen_cmd_t;
#define GEN_CMD \
uint ref_count; \
int tag; \
proxy_store_t *ctx; \
gen_cmd_t *next; \
void (*queued_cb)( gen_cmd_t *gcmd );
struct gen_cmd {
GEN_CMD
};
#define GEN_STS_CMD \
GEN_CMD \
int sts;
typedef union {
gen_cmd_t gen;
struct {
GEN_STS_CMD
};
} gen_sts_cmd_t;
static gen_cmd_t *
proxy_cmd_new( proxy_store_t *ctx, int sz )
proxy_cmd_new( proxy_store_t *ctx, uint sz )
{
gen_cmd_t *cmd = nfmalloc( sz );
cmd->ref_count = 2;
@ -104,6 +134,67 @@ proxy_cmd_done( gen_cmd_t *cmd )
}
}
static void
proxy_wakeup( void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)aux;
gen_cmd_t *cmd = ctx->done_cmds;
assert( cmd );
if (!(ctx->done_cmds = cmd->next))
ctx->done_cmds_append = &ctx->done_cmds;
else
conf_wakeup( &ctx->wakeup, 0 );
cmd->queued_cb( cmd );
proxy_cmd_done( cmd );
}
static void
proxy_invoke_cb( gen_cmd_t *cmd, void (*cb)( gen_cmd_t * ), int checked, const char *name )
{
if (DFlags & FORCEASYNC) {
debug( "%s[% 2d] Callback queue %s%s\n", cmd->ctx->label, cmd->tag, name, checked ? " (checked)" : "" );
cmd->queued_cb = cb;
cmd->next = NULL;
if (checked) {
*cmd->ctx->check_cmds_append = cmd;
cmd->ctx->check_cmds_append = &cmd->next;
} else {
*cmd->ctx->done_cmds_append = cmd;
cmd->ctx->done_cmds_append = &cmd->next;
conf_wakeup( &cmd->ctx->wakeup, 0 );
}
} else {
cb( cmd );
proxy_cmd_done( cmd );
}
}
static void
proxy_flush_checked_cmds( proxy_store_t *ctx )
{
if (ctx->check_cmds) {
*ctx->done_cmds_append = ctx->check_cmds;
ctx->done_cmds_append = ctx->check_cmds_append;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->check_cmds = NULL;
conf_wakeup( &ctx->wakeup, 0 );
}
}
static void
proxy_cancel_checked_cmds( proxy_store_t *ctx )
{
gen_cmd_t *cmd;
while ((cmd = ctx->check_cmds)) {
if (!(ctx->check_cmds = cmd->next))
ctx->check_cmds_append = &ctx->check_cmds;
((gen_sts_cmd_t *)cmd)->sts = DRV_CANCELED;
cmd->queued_cb( cmd );
}
}
#if 0
//# TEMPLATE GETTER
static @type@proxy_@name@( store_t *gctx )
@ -131,7 +222,7 @@ static @type@proxy_@name@( store_t *gctx@decl_args@ )
//# END
//# TEMPLATE REGULAR_VOID
static void proxy_@name@( store_t *gctx@decl_args@ )
static @type@proxy_@name@( store_t *gctx@decl_args@ )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@ -145,28 +236,39 @@ static void proxy_@name@( store_t *gctx@decl_args@ )
//# END
//# TEMPLATE CALLBACK
typedef struct {
gen_cmd_t gen;
void (*callback)( @decl_cb_args@void *aux );
void *callback_aux;
@decl_state@
typedef union {
@gen_cmd_t@ gen;
struct {
@GEN_CMD@
@decl_cb_state@
void (*callback)( @decl_cb_args@void *aux );
void *callback_aux;
@decl_state@
};
} @name@_cmd_t;
static void
proxy_do_@name@_cb( gen_cmd_t *gcmd )
{
@name@_cmd_t *cmd = (@name@_cmd_t *)gcmd;
@pre_print_cb_args@
debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->ctx->label, cmd->tag@print_pass_cb_args@ );
@print_cb_args@
cmd->callback( @pass_cb_args@cmd->callback_aux );
debug( "%s[% 2d] Callback leave @name@\n", cmd->ctx->label, cmd->tag );
}
static void
proxy_@name@_cb( @decl_cb_args@void *aux )
{
@name@_cmd_t *cmd = (@name@_cmd_t *)aux;
@pre_print_cb_args@
debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->gen.ctx->label, cmd->gen.tag@print_pass_cb_args@ );
@print_cb_args@
cmd->callback( @pass_cb_args@cmd->callback_aux );
debug( "%s[% 2d] Callback leave @name@\n", cmd->gen.ctx->label, cmd->gen.tag );
proxy_cmd_done( &cmd->gen );
@save_cb_args@
proxy_invoke_cb( @gen_cmd@, proxy_do_@name@_cb, @checked@, "@name@" );
}
static void
proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@ -175,19 +277,19 @@ proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), v
cmd->callback_aux = aux;
@assign_state@
@pre_print_args@
debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->gen.tag@print_pass_args@ );
debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->tag@print_pass_args@ );
@print_args@
ctx->real_driver->@name@( ctx->real_store@pass_args@, proxy_@name@_cb, cmd );
debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->gen.tag );
proxy_cmd_done( &cmd->gen );
debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->tag );
proxy_cmd_done( @gen_cmd@ );
}
//# END
//# UNDEFINE list_store_print_fmt_cb_args
//# UNDEFINE list_store_print_pass_cb_args
//# DEFINE list_store_print_cb_args
if (sts == DRV_OK) {
for (string_list_t *box = boxes; box; box = box->next)
if (cmd->sts == DRV_OK) {
for (string_list_t *box = cmd->boxes; box; box = box->next)
debug( " %s\n", box->string );
}
//# END
@ -195,35 +297,33 @@ proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), v
//# DEFINE load_box_pre_print_args
static char ubuf[12];
//# END
//# DEFINE load_box_print_fmt_args , [%u,%s] (new >= %u, seen <= %u)
//# DEFINE load_box_print_pass_args , minuid, (maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", maxuid ), ubuf), newuid, seenuid
//# DEFINE load_box_print_fmt_args , [%u,%s] (find >= %u, paired <= %u, new > %u)
//# DEFINE load_box_print_pass_args , minuid, (maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", maxuid ), ubuf), finduid, pairuid, newuid
//# DEFINE load_box_print_args
if (excs.size) {
debugn( " excs:" );
for (int t = 0; t < excs.size; t++)
debugn( " %d", excs.data[t] );
for (uint t = 0; t < excs.size; t++)
debugn( " %u", excs.data[t] );
debug( "\n" );
}
//# END
//# DEFINE load_box_pre_print_cb_args
static char fbuf[as(Flags) + 1];
//# END
//# DEFINE load_box_print_fmt_cb_args , sts=%d, total=%d, recent=%d
//# DEFINE load_box_print_pass_cb_args , sts, total_msgs, recent_msgs
//# DEFINE load_box_print_pass_cb_args , cmd->sts, cmd->total_msgs, cmd->recent_msgs
//# DEFINE load_box_print_cb_args
if (sts == DRV_OK) {
for (message_t *msg = msgs; msg; msg = msg->next)
debug( " uid=%5u, flags=%4s, size=%6d, tuid=%." stringify(TUIDL) "s\n",
if (cmd->sts == DRV_OK) {
static char fbuf[as(Flags) + 1];
for (message_t *msg = cmd->msgs; msg; msg = msg->next)
debug( " uid=%-5u flags=%-4s size=%-6u tuid=%." stringify(TUIDL) "s\n",
msg->uid, (msg->status & M_FLAGS) ? (proxy_make_flags( msg->flags, fbuf ), fbuf) : "?", msg->size, *msg->tuid ? msg->tuid : "?" );
}
//# END
//# DEFINE find_new_msgs_print_fmt_cb_args , sts=%d
//# DEFINE find_new_msgs_print_pass_cb_args , sts
//# DEFINE find_new_msgs_print_pass_cb_args , cmd->sts
//# DEFINE find_new_msgs_print_cb_args
if (sts == DRV_OK) {
for (message_t *msg = msgs; msg; msg = msg->next)
debug( " uid=%5u, tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid );
if (cmd->sts == DRV_OK) {
for (message_t *msg = cmd->msgs; msg; msg = msg->next)
debug( " uid=%-5u tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid );
}
//# END
@ -239,13 +339,13 @@ proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), v
static char fbuf[as(Flags) + 1];
proxy_make_flags( cmd->data->flags, fbuf );
//# END
//# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%lld, size=%d
//# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%lld, size=%u
//# DEFINE fetch_msg_print_pass_cb_args , fbuf, (long long)cmd->data->date, cmd->data->len
//# DEFINE fetch_msg_print_cb_args
if (sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) {
printf( "%s=========\n", cmd->gen.ctx->label );
if (cmd->sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) {
printf( "%s=========\n", cmd->ctx->label );
fwrite( cmd->data->data, cmd->data->len, 1, stdout );
printf( "%s=========\n", cmd->gen.ctx->label );
printf( "%s=========\n", cmd->ctx->label );
fflush( stdout );
}
//# END
@ -254,7 +354,7 @@ proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), v
static char fbuf[as(Flags) + 1];
proxy_make_flags( data->flags, fbuf );
//# END
//# DEFINE store_msg_print_fmt_args , flags=%s, date=%lld, size=%d, to_trash=%s
//# DEFINE store_msg_print_fmt_args , flags=%s, date=%lld, size=%u, to_trash=%s
//# DEFINE store_msg_print_pass_args , fbuf, (long long)data->date, data->len, to_trash ? "yes" : "no"
//# DEFINE store_msg_print_args
if (DFlags & DEBUG_DRV_ALL) {
@ -272,27 +372,34 @@ proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), v
//# END
//# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s
//# DEFINE set_msg_flags_print_pass_args , uid, fbuf1, fbuf2
//# DEFINE set_msg_flags_checked sts == DRV_OK
//# DEFINE trash_msg_print_fmt_args , uid=%u
//# DEFINE trash_msg_print_pass_args , msg->uid
//# DEFINE commit_cmds_print_args
proxy_flush_checked_cmds( ctx );
//# END
//# DEFINE cancel_cmds_print_cb_args
proxy_cancel_checked_cmds( cmd->ctx );
//# END
//# DEFINE free_store_print_args
proxy_cancel_checked_cmds( ctx );
//# END
//# DEFINE free_store_action
proxy_store_deref( ctx );
//# END
//# DEFINE cancel_store_print_args
proxy_cancel_checked_cmds( ctx );
//# END
//# DEFINE cancel_store_action
proxy_store_deref( ctx );
//# END
#endif
//# SPECIAL commit_cmds
static void
proxy_commit_cmds( store_t *gctx )
{
// Currently a dummy in all real drivers.
(void) gctx;
}
//# SPECIAL set_bad_callback
static void
proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
@ -306,9 +413,11 @@ proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
static void
proxy_invoke_bad_callback( proxy_store_t *ctx )
{
ctx->ref_count++;
debug( "%sCallback enter bad store\n", ctx->label );
ctx->bad_callback( ctx->bad_callback_aux );
debug( "%sCallback leave bad store\n", ctx->label ); \
debug( "%sCallback leave bad store\n", ctx->label );
proxy_store_deref( ctx );
}
//# EXCLUDE alloc_store
@ -318,13 +427,16 @@ proxy_alloc_store( store_t *real_ctx, const char *label )
proxy_store_t *ctx;
ctx = nfcalloc( sizeof(*ctx) );
ctx->gen.driver = &proxy_driver;
ctx->driver = &proxy_driver;
ctx->gen.conf = real_ctx->conf;
ctx->ref_count = 1;
ctx->label = label;
ctx->done_cmds_append = &ctx->done_cmds;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->real_driver = real_ctx->driver;
ctx->real_store = real_ctx;
ctx->real_driver->set_bad_callback( ctx->real_store, (void (*)(void *))proxy_invoke_bad_callback, ctx );
init_wakeup( &ctx->wakeup, proxy_wakeup, ctx );
return &ctx->gen;
}

View file

@ -50,6 +50,7 @@ while (<$ins>) {
$defines{$define} = $conts;
$define = undef;
} else {
($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_");
$conts .= $_;
}
} else {
@ -122,13 +123,19 @@ sub make_format($)
return $_;
}
sub indent($$)
{
my ($str, $indent) = @_;
return $str =~ s,^(?=.),$indent,smgr;
}
open(my $outh, ">".$out_source) or die("Cannot create $out_source: $!\n");
for (@ptypes) {
/^([\w* ]+)\(\*(\w+)\)\( (.*) \)$/ or die("Cannot parse prototype '$_'\n");
my ($cmd_type, $cmd_name, $cmd_args) = ($1, $2, $3);
if (defined($excluded{$cmd_name})) {
push @cmd_table, "0";
push @cmd_table, "NULL";
next;
}
push @cmd_table, "proxy_$cmd_name";
@ -137,34 +144,55 @@ for (@ptypes) {
$replace{'name'} = $cmd_name;
$replace{'type'} = $cmd_type;
$cmd_args =~ s/^store_t \*ctx// or die("Arguments '$cmd_args' don't start with 'store_t *ctx'\n");
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) {
my $cmd_cb_args = $1;
$replace{'decl_cb_args'} = $cmd_cb_args;
$replace{'pass_cb_args'} = make_args($cmd_cb_args);
my $cmd_print_cb_args = $cmd_cb_args =~ s/(.*), $/, $1/r;
$replace{'print_pass_cb_args'} = make_args($cmd_print_cb_args);
$replace{'print_fmt_cb_args'} = make_format($cmd_print_cb_args);
$template = "CALLBACK";
} elsif ($cmd_name =~ /^get_/) {
if ($cmd_name =~ /^get_/) {
$template = "GETTER";
$replace{'fmt'} = type_to_format($cmd_type);
} elsif ($cmd_type eq "void ") {
$template = "REGULAR_VOID";
} else {
$template = "REGULAR";
$replace{'fmt'} = type_to_format($cmd_type);
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) {
my $cmd_cb_args = $1;
if (length($cmd_cb_args)) {
$replace{'decl_cb_args'} = $cmd_cb_args;
my $r_cmd_cb_args = $cmd_cb_args;
$r_cmd_cb_args =~ s/^int sts, // or die("Callback arguments of $cmd_name don't start with sts.\n");
$replace{'decl_cb_state'} = $r_cmd_cb_args =~ s/, /\;\n/gr;
my $pass_cb_args = make_args($cmd_cb_args);
$replace{'save_cb_args'} = $pass_cb_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr;
$pass_cb_args =~ s/([^, ]+)/cmd->$1/g;
$replace{'pass_cb_args'} = $pass_cb_args;
$replace{'print_pass_cb_args'} = $pass_cb_args =~ s/(.*), $/, $1/r;
$replace{'print_fmt_cb_args'} = make_format($cmd_cb_args =~ s/(.*), $/, $1/r);
$replace{'gen_cmd_t'} = "gen_sts_cmd_t";
$replace{'GEN_CMD'} = "GEN_STS_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen.gen";
} else {
$replace{'gen_cmd_t'} = "gen_cmd_t";
$replace{'GEN_CMD'} = "GEN_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen";
}
$replace{'checked'} //= '0';
$template = "CALLBACK";
} elsif ($cmd_type eq "void ") {
$template = "REGULAR_VOID";
} else {
$template = "REGULAR";
$replace{'fmt'} = type_to_format($cmd_type);
}
$replace{'decl_args'} = $cmd_args;
$replace{'print_pass_args'} = $replace{'pass_args'} = make_args($cmd_args);
$replace{'print_fmt_args'} = make_format($cmd_args);
}
$replace{'decl_args'} = $cmd_args;
$replace{'print_pass_args'} = $replace{'pass_args'} = make_args($cmd_args);
$replace{'print_fmt_args'} = make_format($cmd_args);
for (keys %defines) {
$replace{$1} = $defines{$_} if (/^${cmd_name}_(.*)$/);
$replace{$1} = delete $defines{$_} if (/^${cmd_name}_(.*)$/);
}
my %used;
my $text = $templates{$template};
$text =~ s/^\h*\@(\w+)\@\n/$replace{$1} \/\/ ""/smeg;
$text =~ s/\@(\w+)\@/$replace{$1} \/\/ ""/eg;
$text =~ s/^(\h*)\@(\w+)\@\n/$used{$2} = 1; indent($replace{$2} \/\/ "", $1)/smeg;
$text =~ s/\@(\w+)\@/$used{$1} = 1; $replace{$1} \/\/ ""/eg;
print $outh $text."\n";
my @not_used = grep { !defined($used{$_}) } keys %replace;
die("Fatal: unconsumed replacements in $cmd_name: ".join(" ", @not_used)."\n") if (@not_used);
}
die("Fatal: unconsumed DEFINEs: ".join(" ", keys %defines)."\n") if (%defines);
print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n";
close $outh;

View file

@ -30,6 +30,9 @@
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#ifdef __linux__
# include <sys/prctl.h>
#endif
int DFlags;
int JLimit;
@ -44,10 +47,10 @@ int Pid; /* for maildir and imap */
char Hostname[256]; /* for maildir */
const char *Home; /* for config */
int BufferLimit = 10 * 1024 * 1024;
uint BufferLimit = 10 * 1024 * 1024;
int chans_total, chans_done;
int boxes_total, boxes_done;
static int chans_total, chans_done;
static int boxes_total, boxes_done;
int new_total[2], new_done[2];
int flags_total[2], flags_done[2];
int trash_total[2], trash_done[2];
@ -75,9 +78,10 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
" -d, --delete propagate message deletions\n"
" -f, --flags propagate message flag changes\n"
" -N, --renew propagate previously not propagated new messages\n"
" -L, --pull propagate from master to slave\n"
" -H, --push propagate from slave to master\n"
" -C, --create create mailboxes if nonexistent\n"
" -L, --pull propagate from far to near side\n"
" -H, --push propagate from near to far side\n"
" -C, --create propagate creations of mailboxes\n"
" -R, --remove propagate deletions of mailboxes\n"
" -X, --expunge expunge deleted messages\n"
" -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
" -D, --debug debugging modes (see manual)\n"
@ -88,7 +92,8 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
"\nIf neither --pull nor --push are specified, both are active.\n"
"If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
"Direction and operation can be concatenated like --pull-new, etc.\n"
"--create and --expunge can be suffixed with -master/-slave. Read the man page.\n"
"--create, --remove, and --expunge can be suffixed with -far/-near.\n"
"See the man page for details.\n"
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
"\nCompile time options:\n"
#ifdef HAVE_LIBSSL
@ -131,7 +136,7 @@ debug( const char *msg, ... )
}
#ifdef __linux__
static void
static void ATTR_NORETURN
crashHandler( int n )
{
int dpid;
@ -142,18 +147,35 @@ crashHandler( int n )
dup2( 0, 1 );
dup2( 0, 2 );
error( "*** " EXE " caught signal %d. Starting debugger ...\n", n );
#ifdef PR_SET_PTRACER
int pip[2];
if (pipe( pip ) < 0) {
perror( "pipe()" );
exit( 3 );
}
#endif
switch ((dpid = fork())) {
case -1:
perror( "fork()" );
break;
case 0:
#ifdef PR_SET_PTRACER
close( pip[1] );
read( pip[0], pbuf, 1 );
close( pip[0] );
#endif
sprintf( pbuf, "%d", Pid );
sprintf( pabuf, "/proc/%d/exe", Pid );
execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 );
perror( "execlp()" );
_exit( 1 );
default:
waitpid( dpid, 0, 0 );
#ifdef PR_SET_PTRACER
prctl( PR_SET_PTRACER, (ulong)dpid );
close( pip[1] );
close( pip[0] );
#endif
waitpid( dpid, NULL, 0 );
break;
}
exit( 3 );
@ -183,7 +205,7 @@ stats( void )
if (l > cls)
buf[t][cls - 1] = '~';
}
progress( "\r%s M: %.*s S: %.*s", buf[2], cls, buf[0], cls, buf[1] );
progress( "\r%s F: %.*s N: %.*s", buf[2], cls, buf[0], cls, buf[1] );
}
static int
@ -226,8 +248,8 @@ is_inbox( const char *name )
static int
cmp_box_names( const void *a, const void *b )
{
const char *as = *(const char **)a;
const char *bs = *(const char **)b;
const char *as = *(const char * const *)a;
const char *bs = *(const char * const *)b;
int ai = is_inbox( as );
int bi = is_inbox( bs );
int di = bi - ai;
@ -240,9 +262,9 @@ static char **
filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns )
{
string_list_t *cpat;
char **boxarr = 0;
char **boxarr = NULL;
const char *ps;
int not, fnot, pfxl, num = 0, rnum = 0;
uint not, fnot, pfxl, num = 0, rnum = 0;
pfxl = prefix ? strlen( prefix ) : 0;
for (; boxes; boxes = boxes->next) {
@ -265,7 +287,7 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
if (num + 1 >= rnum)
boxarr = nfrealloc( boxarr, (rnum = (rnum + 10) * 2) * sizeof(*boxarr) );
boxarr[num++] = nfstrdup( boxes->string + pfxl );
boxarr[num] = 0;
boxarr[num] = NULL;
}
}
qsort( boxarr, num, sizeof(*boxarr), cmp_box_names );
@ -275,18 +297,18 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
static void
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
{
if (ops[M] & have) {
chan->ops[M] &= ~mask;
chan->ops[M] |= ops[M] & mask;
chan->ops[S] &= ~mask;
chan->ops[S] |= ops[S] & mask;
} else if (!(chan->ops[M] & have)) {
if (global_conf.ops[M] & have) {
chan->ops[M] |= global_conf.ops[M] & mask;
chan->ops[S] |= global_conf.ops[S] & mask;
if (ops[F] & have) {
chan->ops[F] &= ~mask;
chan->ops[F] |= ops[F] & mask;
chan->ops[N] &= ~mask;
chan->ops[N] |= ops[N] & mask;
} else if (!(chan->ops[F] & have)) {
if (global_conf.ops[F] & have) {
chan->ops[F] |= global_conf.ops[F] & mask;
chan->ops[N] |= global_conf.ops[N] & mask;
} else {
chan->ops[M] |= def;
chan->ops[S] |= def;
chan->ops[F] |= def;
chan->ops[N] |= def;
}
}
}
@ -326,9 +348,10 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
{
channel_conf_t *chan;
chan_ent_t *ce;
box_ent_t *boxes = 0, **mboxapp = &boxes, *mbox;
box_ent_t *boxes = NULL, **mboxapp = &boxes, *mbox;
char *boxp, *nboxp;
int boxl, boxlist = 0;
size_t boxl;
char boxlist = 0;
if ((boxp = strchr( channame, ':' )))
*boxp++ = 0;
@ -336,18 +359,18 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
if (!strcmp( chan->name, channame ))
goto gotchan;
error( "No channel or group named '%s' defined.\n", channame );
return 0;
return NULL;
gotchan:
if (boxp) {
if (!chan->patterns) {
error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame );
return 0;
return NULL;
}
boxlist = 1;
do {
nboxp = strpbrk( boxp, ",\n" );
if (nboxp) {
boxl = nboxp - boxp;
boxl = (size_t)(nboxp - boxp);
*nboxp++ = 0;
} else {
boxl = strlen( boxp );
@ -357,8 +380,8 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
mbox->name = nfstrndup( boxp, boxl );
else
mbox->name = nfstrndup( "INBOX", 5 );
mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
mbox->next = 0;
mbox->present[F] = mbox->present[N] = BOX_POSSIBLE;
mbox->next = NULL;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
@ -403,12 +426,12 @@ int
main( int argc, char **argv )
{
main_vars_t mvars[1];
chan_ent_t *chans = 0, **chanapp = &chans;
chan_ent_t *chans = NULL, **chanapp = &chans;
group_conf_t *group;
channel_conf_t *chan;
string_list_t *channame;
char *config = 0, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0;
char *config = NULL, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, ms_warn = 0;
tzset();
gethostname( Hostname, sizeof(Hostname) );
@ -424,7 +447,7 @@ main( int argc, char **argv )
memset( mvars, 0, sizeof(*mvars) );
mvars->t[1] = 1;
for (oind = 1, ochar = 0; ; ) {
for (oind = 1, ochar = NULL; ; ) {
if (!ochar || !*ochar) {
if (oind >= argc)
break;
@ -481,22 +504,26 @@ main( int argc, char **argv )
goto badopt;
DFlags |= op;
} else if (!strcmp( opt, "pull" ))
cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE;
cops |= XOP_PULL, ops[F] |= XOP_HAVE_TYPE;
else if (!strcmp( opt, "push" ))
cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE;
cops |= XOP_PUSH, ops[F] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "create", 6 )) {
opt += 6;
op = OP_CREATE|XOP_HAVE_CREATE;
lcop:
if (!*opt)
cops |= op;
else if (!strcmp( opt, "-master" ))
ops[M] |= op;
else if (!strcmp( opt, "-slave" ))
ops[S] |= op;
else if (!strcmp( opt, "-far" ))
ops[F] |= op;
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1;
else if (!strcmp( opt, "-near" ))
ops[N] |= op;
else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1;
else
goto badopt;
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
} else if (starts_with( opt, -1, "remove", 6 )) {
opt += 6;
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -506,15 +533,15 @@ main( int argc, char **argv )
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto lcop;
} else if (!strcmp( opt, "no-expunge" ))
ops[M] |= XOP_HAVE_EXPUNGE;
ops[F] |= XOP_HAVE_EXPUNGE;
else if (!strcmp( opt, "no-create" ))
ops[M] |= XOP_HAVE_CREATE;
ops[F] |= XOP_HAVE_CREATE;
else if (!strcmp( opt, "no-remove" ))
ops[M] |= XOP_HAVE_REMOVE;
ops[F] |= XOP_HAVE_REMOVE;
else if (!strcmp( opt, "full" ))
ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
ops[F] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
else if (!strcmp( opt, "noop" ))
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "pull", 4 )) {
op = XOP_PULL;
lcac:
@ -546,11 +573,11 @@ main( int argc, char **argv )
return 1;
}
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
}
continue;
}
@ -568,10 +595,6 @@ main( int argc, char **argv )
mvars->list = 1;
break;
case 'c':
if (*ochar == 'T') {
ochar++;
pseudo = 1;
}
if (oind >= argc) {
error( "-c requires an argument.\n" );
return 1;
@ -581,15 +604,19 @@ main( int argc, char **argv )
case 'C':
op = OP_CREATE|XOP_HAVE_CREATE;
cop:
if (*ochar == 'm')
ops[M] |= op, ochar++;
else if (*ochar == 's')
ops[S] |= op, ochar++;
if (*ochar == 'f')
ops[F] |= op, ochar++;
else if (*ochar == 'm') // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1, ochar++;
else if (*ochar == 'n')
ops[N] |= op, ochar++;
else if (*ochar == 's') // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1, ochar++;
else if (*ochar == '-')
ochar++;
else
cops |= op;
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
break;
case 'R':
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -601,7 +628,7 @@ main( int argc, char **argv )
cops |= XOP_PULL|XOP_PUSH;
FALLTHROUGH
case '0':
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
break;
case 'n':
case 'd':
@ -624,13 +651,13 @@ main( int argc, char **argv )
}
if (op & OP_MASK_TYPE)
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
else
cops |= op;
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
break;
case 'L':
op = XOP_PULL;
@ -683,12 +710,24 @@ main( int argc, char **argv )
op = DEBUG_ALL | VERBOSE;
DFlags |= op;
break;
case 'J':
DFlags |= KEEPJOURNAL;
JLimit = strtol( ochar, &ochar, 10 );
break;
case 'Z':
DFlags |= ZERODELAY;
case 'T':
for (; *ochar; ) {
switch (*ochar++) {
case 'a':
DFlags |= FORCEASYNC;
break;
case 'j':
DFlags |= KEEPJOURNAL;
JLimit = strtol( ochar, &ochar, 10 );
break;
case 'z':
DFlags |= ZERODELAY;
break;
default:
error( "Unknown -T flag '%c'\n", *(ochar - 1) );
return 1;
}
}
break;
case 'v':
version();
@ -699,6 +738,8 @@ main( int argc, char **argv )
return 1;
}
}
if (ms_warn)
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
DFlags |= PROGRESS;
@ -714,7 +755,7 @@ main( int argc, char **argv )
if (merge_ops( cops, ops ))
return 1;
if (load_config( config, pseudo ))
if (load_config( config ))
return 1;
if (!channels) {
@ -816,30 +857,33 @@ sync_chans( main_vars_t *mvars, int ent )
int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] );
if (st != FAIL_TEMP) {
info( "Skipping due to %sfailed %s store %s.\n",
(st == FAIL_WAIT) ? "temporarily " : "", str_ms[t], mvars->chan->stores[t]->name );
(st == FAIL_WAIT) ? "temporarily " : "", str_fn[t], mvars->chan->stores[t]->name );
mvars->skip = 1;
}
}
if (mvars->skip)
goto next2;
mvars->state[M] = mvars->state[S] = ST_FRESH;
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[M]->driver->get_caps( 0 ) & mvars->chan->stores[S]->driver->get_caps( 0 ) & DRV_VERBOSE))
labels[M] = "M: ", labels[S] = "S: ";
else
labels[M] = labels[S] = "";
mvars->state[F] = mvars->state[N] = ST_FRESH;
uint dcaps[2];
for (t = 0; t < 2; t++) {
driver_t *drv = mvars->chan->stores[t]->driver;
store_t *ctx = drv->alloc_store( mvars->chan->stores[t], labels[t] );
if (DFlags & DEBUG_DRV) {
drv = &proxy_driver;
mvars->drv[t] = mvars->chan->stores[t]->driver;
dcaps[t] = mvars->drv[t]->get_caps( NULL );
}
if ((DFlags & DEBUG_DRV) || (dcaps[F] & dcaps[N] & DRV_VERBOSE))
labels[F] = "F: ", labels[N] = "N: ";
else
labels[F] = labels[N] = "";
for (t = 0; t < 2; t++) {
store_t *ctx = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] );
if ((DFlags & DEBUG_DRV) || ((DFlags & FORCEASYNC) && !(dcaps[t] & DRV_ASYNC))) {
mvars->drv[t] = &proxy_driver;
ctx = proxy_alloc_store( ctx, labels[t] );
}
mvars->drv[t] = drv;
mvars->ctx[t] = ctx;
drv->set_bad_callback( ctx, store_bad, AUX );
mvars->drv[t]->set_bad_callback( ctx, store_bad, AUX );
}
for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
info( "Opening %s store %s...\n", str_fn[t], mvars->chan->stores[t]->name );
mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
if (t || mvars->skip)
break;
@ -849,44 +893,44 @@ sync_chans( main_vars_t *mvars, int ent )
opened:
if (mvars->skip)
goto next;
if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
if (mvars->state[F] != ST_OPEN || mvars->state[N] != ST_OPEN)
return;
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
mvars->chanptr->boxlist = 2;
boxes[M] = filter_boxes( mvars->boxes[M], mvars->chan->boxes[M], mvars->chan->patterns );
boxes[S] = filter_boxes( mvars->boxes[S], mvars->chan->boxes[S], mvars->chan->patterns );
boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns );
boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns );
mboxapp = &mvars->chanptr->boxes;
for (mb = sb = 0; ; ) {
char *mname = boxes[M] ? boxes[M][mb] : 0;
char *sname = boxes[S] ? boxes[S][sb] : 0;
char *mname = boxes[F] ? boxes[F][mb] : NULL;
char *sname = boxes[N] ? boxes[N][sb] : NULL;
if (!mname && !sname)
break;
mbox = nfmalloc( sizeof(*mbox) );
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
mbox->name = mname;
free( sname );
mbox->present[M] = mbox->present[S] = BOX_PRESENT;
mbox->present[F] = mbox->present[N] = BOX_PRESENT;
mb++;
sb++;
} else if (cmp < 0) {
mbox->name = mname;
mbox->present[M] = BOX_PRESENT;
mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[F] = BOX_PRESENT;
mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mb++;
} else {
mbox->name = sname;
mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[S] = BOX_PRESENT;
mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[N] = BOX_PRESENT;
sb++;
}
mbox->next = 0;
mbox->next = NULL;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
}
free( boxes[M] );
free( boxes[S] );
free( boxes[F] );
free( boxes[N] );
if (!mvars->list)
stats();
}
@ -915,14 +959,14 @@ sync_chans( main_vars_t *mvars, int ent )
if (!mvars->skip)
goto syncml;
} else
printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
printf( "%s <=> %s\n", nz( mvars->chan->boxes[F], "INBOX" ), nz( mvars->chan->boxes[N], "INBOX" ) );
}
next:
mvars->cben = 0;
for (t = 0; t < 2; t++) {
free_string_list( mvars->boxes[t] );
mvars->boxes[t] = 0;
mvars->boxes[t] = NULL;
if (mvars->state[t] == ST_FRESH) {
/* An unconnected store may be only cancelled. */
mvars->state[t] = ST_CLOSED;
@ -933,17 +977,17 @@ sync_chans( main_vars_t *mvars, int ent )
}
}
mvars->cben = 1;
if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) {
mvars->skip = 1;
return;
}
if (mvars->chanptr->boxlist == 2) {
if (mvars->chanptr->boxlist) {
for (nmbox = mvars->chanptr->boxes; (mbox = nmbox); ) {
nmbox = mbox->next;
free( mbox->name );
free( mbox );
}
mvars->chanptr->boxes = 0;
mvars->chanptr->boxes = NULL;
mvars->chanptr->boxlist = 0;
}
next2:
@ -951,7 +995,10 @@ sync_chans( main_vars_t *mvars, int ent )
chans_done++;
stats();
}
} while ((mvars->chanptr = mvars->chanptr->next));
chan_ent_t *nchan = mvars->chanptr->next;
free( mvars->chanptr );
mvars->chanptr = nchan;
} while (mvars->chanptr);
for (t = 0; t < N_DRIVERS; t++)
drivers[t]->cleanup();
}
@ -1041,7 +1088,7 @@ store_listed( int sts, string_list_t *boxes, void *aux )
}
}
if (mvars->ctx[t]->conf->map_inbox) {
debug( "adding mapped inbox to %s: %s\n", str_ms[t], mvars->ctx[t]->conf->map_inbox );
debug( "adding mapped inbox to %s store: %s\n", str_fn[t], mvars->ctx[t]->conf->map_inbox );
add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox );
}
break;
@ -1056,20 +1103,20 @@ store_listed( int sts, string_list_t *boxes, void *aux )
static int
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
{
if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
const char *mpfx = nz( mvars->chan->boxes[M], "" );
const char *spfx = nz( mvars->chan->boxes[S], "" );
if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) {
const char *mpfx = nz( mvars->chan->boxes[F], "" );
const char *spfx = nz( mvars->chan->boxes[N], "" );
if (!mvars->list) {
nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
nfasprintf( &mvars->names[F], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[N], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
return 1;
}
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
} else {
if (!mvars->list) {
mvars->names[M] = mvars->names[S] = mbox->name;
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
mvars->names[F] = mvars->names[N] = mbox->name;
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
return 1;
}
puts( mbox->name );
@ -1082,8 +1129,8 @@ done_sync_2_dyn( int sts, void *aux )
{
main_vars_t *mvars = (main_vars_t *)aux;
free( mvars->names[M] );
free( mvars->names[S] );
free( mvars->names[F] );
free( mvars->names[N] );
done_sync( sts, aux );
}
@ -1097,11 +1144,11 @@ done_sync( int sts, void *aux )
stats();
if (sts) {
mvars->ret = 1;
if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
if (sts & SYNC_BAD(M))
mvars->state[M] = ST_CLOSED;
if (sts & SYNC_BAD(S))
mvars->state[S] = ST_CLOSED;
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
if (sts & SYNC_BAD(F))
mvars->state[F] = ST_CLOSED;
if (sts & SYNC_BAD(N))
mvars->state[N] = ST_CLOSED;
mvars->skip = 1;
}
}

View file

@ -1,33 +1,32 @@
.ig
\" mbsync - mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004,2011-2015 Oswald Buddenhagen <ossi@users.sf.net>
\" Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
\"
\" This program is free software; you can redistribute it and/or modify
\" it under the terms of the GNU General Public License as published by
\" the Free Software Foundation; either version 2 of the License, or
\" (at your option) any later version.
\"
\" This program is distributed in the hope that it will be useful,
\" but WITHOUT ANY WARRANTY; without even the implied warranty of
\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\" GNU General Public License for more details.
\"
\" You should have received a copy of the GNU General Public License
\" along with this program. If not, see <http://www.gnu.org/licenses/>.
\"
\" As a special exception, mbsync may be linked with the OpenSSL library,
\" despite that library's more restrictive license.
..
.\" mbsync - mailbox synchronizer
.\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
.\" Copyright (C) 2002-2004,2011-2015 Oswald Buddenhagen <ossi@users.sf.net>
.\" Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.\"
.\" As a special exception, mbsync may be linked with the OpenSSL library,
.\" despite that library's more restrictive license.
.
.TH mbsync 1 "2015 Mar 22"
..
.
.SH NAME
mbsync - synchronize IMAP4 and Maildir mailboxes
..
.
.SH SYNOPSIS
\fBmbsync\fR [\fIoptions\fR ...] {{\fIchannel\fR[\fB:\fIbox\fR[{\fB,\fR|\fB\\n\fR}...]]|\fIgroup\fR} ...|\fB-a\fR}
..
.
.SH DESCRIPTION
\fBmbsync\fR is a command line application which synchronizes mailboxes;
currently Maildir and IMAP4 mailboxes are supported.
@ -43,7 +42,7 @@ these files are protected against concurrent \fBmbsync\fR processes.
Mailboxes can be safely modified while \fBmbsync\fR operates
(see \fBINHERENT PROBLEMS\fR below for a minor exception).
Multiple replicas of each mailbox can be maintained.
..
.
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
@ -58,13 +57,13 @@ line are ignored.
Don't synchronize anything, but list all mailboxes in the selected channels
and exit.
.TP
\fB-C\fR[\fBm\fR][\fBs\fR], \fB--create\fR[\fB-master\fR|\fB-slave\fR]
\fB-C\fR[\fBf\fR][\fBn\fR], \fB--create\fR[\fB-far\fR|\fB-near\fR]
Override any \fBCreate\fR options from the config file. See below.
.TP
\fB-R\fR[\fBm\fR][\fBs\fR], \fB--remove\fR[\fB-master\fR|\fB-slave\fR]
\fB-R\fR[\fBf\fR][\fBn\fR], \fB--remove\fR[\fB-far\fR|\fB-near\fR]
Override any \fBRemove\fR options from the config file. See below.
.TP
\fB-X\fR[\fBm\fR][\fBs\fR], \fB--expunge\fR[\fB-master\fR|\fB-slave\fR]
\fB-X\fR[\fBf\fR][\fBn\fR], \fB--expunge\fR[\fB-far\fR|\fB-near\fR]
Override any \fBExpunge\fR options from the config file. See below.
.TP
{\fB-n\fR|\fB-N\fR|\fB-d\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\
@ -110,7 +109,7 @@ Without category specification, all categories except net-all are enabled.
Suppress progress counters (this is implicit if stdout is no TTY,
or any debugging categories are enabled) and notices.
If specified twice, suppress warning messages as well.
..
.
.SH CONFIGURATION
The configuration file is mandatory; \fBmbsync\fR will not run without it.
Lines starting with a hash mark (\fB#\fR) are comments and are ignored entirely.
@ -120,9 +119,9 @@ and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths.
There are a few global options, the rest applies to particular sections.
Sections are started by a section keyword and are terminated by an empty line
or end of file.
There are a few global options, the others apply to particular sections.
Sections begin with a section-starting keyword and are terminated by an empty
line or end of file.
Every section defines an object with an identifier unique within that
object class.
.P
@ -131,8 +130,8 @@ a collection of mailboxes; basically a folder, either local or remote.
A Channel connects two Stores, describing the way the two are synchronized.
.br
There are two auxiliary object classes: Accounts and Groups. An Account
describes the connection part of remote Stores, so a server connection can be
shared between multiple Stores. A Group aggregates multiple Channels to
describes the connection part of network Stores, so server configurations can
be shared between multiple Stores. A Group aggregates multiple Channels to
save typing on the command line.
.P
File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the
@ -141,7 +140,7 @@ even combinations thereof.
.br
Mailbox names, OTOH, always use canonical path separators, which are
Unix-like forward slashes.
..
.
.SS All Stores
These options can be used in all supported Store types.
.br
@ -150,35 +149,43 @@ and not necessarily a remote server.
.br
The special mailbox \fBINBOX\fR exists in every Store; its physical location
in the file system is Store type specific.
..
.
.TP
\fBPath\fR \fIpath\fR
The location of the Store in the (server's) file system.
If this is no absolute path, the reference point is Store type specific.
This string is prepended to the mailbox names addressed in this Store,
but is not considered part of them; this is important for \fBPatterns\fR
in the Channels section.
and \fBCreate\fR in the Channels section.
Note that you \fBmust\fR append a slash if you want to specify an entire
directory.
(Default: none)
..
.
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Messages larger than that will not be propagated into this Store.
This is useful for weeding out messages with large attachments.
Messages larger than \fIsize\fR will have only a small placeholder message
propagated into this Store. To propagate the full message, it must be
flagged in either Store; that can be done retroactively, in which case
the \fBReNew\fR operation needs to be executed instead of \fBNew\fR.
This is useful for avoiding downloading messages with large attachments
unless they are actually needed.
Caveat: Setting a size limit on a Store you never read directly (which is
typically the case for servers) is not recommended, as you may never
notice that affected messages were not propagated to it.
.br
\fBK\fR and \fBM\fR can be appended to the size to specify KiBytes resp.
MeBytes instead of bytes. \fBB\fR is accepted but superfluous.
If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
(Default: \fI0\fR)
..
.
.TP
\fBMapInbox\fR \fImailbox\fR
Create a virtual mailbox (relative to \fBPath\fR) which aliases
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section, though with a Maildir slave, you probably want to
Channels section, though with a Maildir near side, you probably want to
place \fBInbox\fR under \fBPath\fR instead.
This virtual mailbox does not support subfolders.
..
.
.TP
\fBFlatten\fR \fIdelim\fR
Flatten the hierarchy within this Store by substituting the canonical
@ -190,27 +197,27 @@ A common choice for the delimiter is \fB.\fR.
.br
Note that flattened sub-folders of the \fBINBOX\fR always end up
under \fBPath\fR, including the "INBOX\fIdelim\fR" prefix.
..
.
.TP
\fBTrash\fR \fImailbox\fR
Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to
prior to expunging.
See \fBRECOMMENDATIONS\fR and \fBINHERENT PROBLEMS\fR below.
(Default: none)
..
.
.TP
\fBTrashNewOnly\fR \fByes\fR|\fBno\fR
When trashing, copy only not yet propagated messages. This makes sense if the
remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fBno\fR).
(Default: \fBno\fR)
..
.
.TP
\fBTrashRemoteNew\fR \fByes\fR|\fBno\fR
When expunging the remote Store, copy not yet propagated messages to this
Store's \fBTrash\fR. When using this, the remote Store does not need an own
\fBTrash\fR at all, yet all messages are archived.
(Default: \fBno\fR)
..
.
.SS Maildir Stores
The reference point for relative \fBPath\fRs is the current working directory.
.P
@ -238,11 +245,11 @@ message deletion and a new message, resulting in unnecessary traffic.
\fBMutt\fR is known to work fine with both schemes.
.br
Use \fBmdconvert\fR to convert mailboxes from one scheme to the other.
..
.
.TP
\fBMaildirStore\fR \fIname\fR
Define the Maildir Store \fIname\fR, opening a section for its parameters.
..
.
.TP
\fBAltMap\fR \fByes\fR|\fBno\fR
Use the \fBalternative\fR UID storage scheme for mailboxes in this Store.
@ -250,24 +257,24 @@ This does not affect mailboxes that do already have a UID storage scheme;
use \fBmdconvert\fR to change it.
See \fBRECOMMENDATIONS\fR below.
(Default: \fBno\fR)
..
.
.TP
\fBInbox\fR \fIpath\fR
The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR,
but it is allowed to place the \fBINBOX\fR inside the \fBPath\fR.
(Default: \fI~/Maildir\fR)
..
.
.TP
\fBInfoDelimiter\fR \fIdelim\fR
The character used to delimit the info field from a message's basename.
The Maildir standard defines this to be the colon, but this is incompatible
with DOS/Windows file systems.
(Default: the value of \fBFieldDelimiter\fR)
..
.
.TP
\fBSubFolders\fR \fBVerbatim\fR|\fBMaildir++\fR|\fBLegacy\fR
The on-disk folder naming style used for hierarchical mailboxes.
This has option has no effect when \fBFlatten\fR is used.
This option has no effect when \fBFlatten\fR is used.
.br
Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and
\fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths:
@ -284,12 +291,12 @@ Note that attempts to set \fBPath\fR are rejected in this mode.
(this is \fBmbsync\fR's historical style)
.br
(Default: unset; will error out when sub-folders are encountered)
..
.
.SS IMAP4 Accounts
.TP
\fBIMAPAccount\fR \fIname\fR
Define the IMAP4 Account \fIname\fR, opening a section for its parameters.
..
.
.TP
\fBHost\fR \fIhost\fR
Specify the DNS name or IP address of the IMAP server.
@ -297,47 +304,78 @@ Specify the DNS name or IP address of the IMAP server.
If \fBTunnel\fR is used, this setting is needed only if \fBSSLType\fR is
not \fBNone\fR and \fBCertificateFile\fR is not used,
in which case the host name is used for certificate subject verification.
..
.
.TP
\fBPort\fR \fIport\fR
Specify the TCP port number of the IMAP server. (Default: 143 for IMAP,
993 for IMAPS)
.br
If \fBTunnel\fR is used, this setting is ignored.
..
.
.TP
\fBTimeout\fR \fItimeout\fR
Specify the connect and data timeout for the IMAP server in seconds.
Zero means unlimited.
(Default: \fI20\fR)
..
.
.TP
\fBUser\fR \fIusername\fR
Specify the login name on the IMAP server.
..
.
.TP
\fBUserCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a user rather than specifying a
user directly. This allows you to script retrieving user names.
.br
The command must produce exactly one line on stdout; the trailing newline
is optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a prompt); failure to do so will merely produce messier output.
Remember to backslash-escape double quotes and backslashes embedded into
the command.
.
.TP
\fBPass\fR \fIpassword\fR
Specify the password for \fIusername\fR on the IMAP server.
Note that this option is \fInot\fR required.
If neither a password nor a password command is specified in the
configuration file, \fBmbsync\fR will prompt you for a password.
..
.
.TP
\fBPassCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a password rather than specifying a
password directly. This allows you to use password files and agents.
The command must produce exactly one line on stdout; the trailing newline is
optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a decryption password prompt); failure to do so will merely produce
messier output.
..
.br
See \fBUserCmd\fR above for details.
.
.TP
\fBUseKeychain\fR \fByes\fR|\fBno\fR
Whether to use the macOS Keychain to obtain the password.
(Default: \fBno\fR)
.IP
The neccessary keychain item can be created this way:
.RS
.IP
.nh
.B security add-internet-password \-r imap \-s
.I Host
.B \-a
.I User
.B \-w
.I password
[
.B \-T
.I /path/to/mbsync
]
.hy
.RE
.
.TP
\fBTunnel\fR \fIcommand\fR
Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
..
.
.TP
\fBAuthMechs\fR \fItype\fR ...
The list of acceptable authentication mechanisms.
@ -348,7 +386,7 @@ enough for the current \fBSSLType\fR setting.
The actually used mechanism is the most secure choice from the intersection
of this list, the list supplied by the server, and the installed SASL modules.
(Default: \fB*\fR)
..
.
.TP
\fBSSLType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR}
Select the connection security/encryption method:
@ -362,28 +400,46 @@ so it is the default (unless a tunnel is used).
.br
\fBIMAPS\fR - security is established by starting SSL/TLS negotiation
right after connecting the secure IMAP port 993.
..
.
.TP
\fBSSLVersions\fR [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR]
\fBSSLVersions\fR [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]
Select the acceptable SSL/TLS versions.
Use old versions only when the server has problems with newer ones.
(Default: [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR]).
..
(Default: [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]).
.
.TP
\fBSystemCertificates\fR \fByes\fR|\fBno\fR
Whether the system's default root cerificate store should be loaded.
Whether the system's default CA (certificate authority) certificate
store should be used to verify certificate trust chains. Disable this
if you want to trust only hand-picked certificates.
(Default: \fByes\fR)
..
.
.TP
\fBCertificateFile\fR \fIpath\fR
File containing additional X.509 certificates used to verify server
identities. Directly matched peer certificates are always trusted,
regardless of validity.
identities.
It may contain two types of certificates:
.RS
.IP Host
These certificates are matched only against the received server certificate
itself.
They are always trusted, regardless of validity.
A typical use case would be forcing acceptance of an expired certificate.
.br
Note that the system's default certificate store is always used
(unless \fBSystemCertificates\fR is disabled)
and should not be specified here.
..
These certificates may be obtained using the \fBmbsync-get-cert\fR tool;
make sure to verify their fingerprints before trusting them, or transfer
them securely from the server's network (if it can be trusted beyond the
server itself).
.IP CA
These certificates are used as trust anchors when building the certificate
chain for the received server certificate.
They are used to supplant or supersede the system's trust store, depending
on the \fBSystemCertificates\fR setting;
it is not necessary and not recommended to specify the system's trust store
itself here.
The trust chains are fully validated.
.RE
.
.TP
\fBClientCertificate\fR \fIpath\fR
File containing a client certificate to send to the server.
@ -391,11 +447,18 @@ File containing a client certificate to send to the server.
.br
Note that client certificate verification is usually not required,
so it is unlikely that you need this option.
..
.
.TP
\fBClientKey\fR \fIpath\fR
File containing the private key corresponding to \fBClientCertificate\fR.
..
.
.TP
\fBCipherString\fR \fIstring\fR
Specify OpenSSL cipher string for connections secured with TLS up to
version 1.2 (but not 1.3 and above).
The format is described in \fBciphers\fR\|(1).
(Default: empty, which implies system wide policy).
.
.TP
\fBPipelineDepth\fR \fIdepth\fR
Maximum number of IMAP commands which can be simultaneously in flight.
@ -404,14 +467,14 @@ This is mostly a debugging option, but may also be used to limit average
bandwidth consumption (GMail may require this if you have a very fast
connection), or to spare flaky servers like M$ Exchange.
(Default: \fIunlimited\fR)
..
.
.TP
\fBDisableExtension\fR[\fBs\fR] \fIextension\fR ...
Disable the use of specific IMAP extensions.
This can be used to work around bugs in servers
(and possibly \fBmbsync\fR itself).
(Default: empty)
..
.
.SS IMAP Stores
The reference point for relative \fBPath\fRs is whatever the server likes it
to be; probably the user's $HOME or $HOME/Mail on that server. The location
@ -419,21 +482,21 @@ of \fBINBOX\fR is up to the server as well and is usually irrelevant.
.TP
\fBIMAPStore\fR \fIname\fR
Define the IMAP4 Store \fIname\fR, opening a section for its parameters.
..
.
.TP
\fBAccount\fR \fIaccount\fR
Specify which IMAP4 Account to use. Instead of defining an Account and
referencing it here, it is also possible to specify all the Account options
directly in the Store's section - this makes sense if an Account is used for
one Store only anyway.
..
.
.TP
\fBUseNamespace\fR \fByes\fR|\fBno\fR
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fBPath\fR was specified.
(Default: \fByes\fR)
..
.
.TP
\fBPathDelimiter\fR \fIdelim\fR
Specify the server's hierarchy delimiter.
@ -441,25 +504,33 @@ Specify the server's hierarchy delimiter.
.br
Do \fInot\fR abuse this to re-interpret the hierarchy.
Use \fBFlatten\fR instead.
..
.
.TP
\fBSubscribedOnly\fR \fByes\fR|\fBno\fR
Selects whether to synchronize only mailboxes that are subscribed to on the
IMAP server. In technical terms, if this option is set, \fBmbsync\fR will use
the IMAP command LSUB instead of LIST to look for mailboxes in this Store.
This option make sense only in conjunction with \fBPatterns\fR.
(Default: \fBno\fR)
.
.SS Channels
.TP
\fBChannel\fR \fIname\fR
Define the Channel \fIname\fR, opening a section for its parameters.
..
.
.TP
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
{\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the far resp. near side Store to be connected by this Channel.
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
prefix which is not matched against the patterns, and which is not
affected by mailbox list overrides.
Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
..
.
.TP
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
that match the \fIpattern\fR(s). The mailbox names are the same on both
Master and Slave. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
that match the \fIpattern\fR(s). The mailbox names are the same on the far
and near side. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
and \fB%\fR matches anything up to the next hierarchy delimiter. Prepending
\fB!\fR to a pattern makes it an exclusion. Multiple patterns can be specified
(either by supplying multiple arguments or by using \fBPattern\fR multiple
@ -472,16 +543,16 @@ The mailbox list selected by \fBPatterns\fR can be overridden by a mailbox
list in a channel reference (a \fBGroup\fR specification or the command line).
.br
Example: "\fBPatterns\fR\ \fI%\ !Trash\fR"
..
.
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Analogous to the homonymous option in the Stores section, but applies equally
to Master and Slave. Note that this actually modifies the Stores, so take care
to Far and Near. Note that this actually modifies the Stores, so take care
not to provide conflicting settings if you use the Stores in multiple Channels.
..
.
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the maximum number of messages to keep in each Slave mailbox.
Sets the maximum number of messages to keep in each near side mailbox.
This is useful for mailboxes where you keep a complete archive on the server,
but want to mirror only the last messages (for instance, for mailing lists).
The messages that were the first to arrive in the mailbox (independently of
@ -490,7 +561,7 @@ Messages that are flagged (marked as important) and (by default) unread
messages will not be automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: \fI0\fR).
..
.
.TP
\fBExpireUnread\fR \fByes\fR|\fBno\fR
Selects whether unread messages should be affected by \fBMaxMessages\fR.
@ -500,20 +571,19 @@ However, if your archive contains large amounts of unread messages by design,
treating them as important would practically defeat \fBMaxMessages\fR. In this
case you need to enable this option.
(Default: \fBno\fR).
..
.
.TP
\fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBReNew\fR] [\fBDelete\fR] [\fBFlags\fR]|\fBAll\fR}
Select the synchronization operation(s) to perform:
.br
\fBPull\fR - propagate changes from Master to Slave.
\fBPull\fR - propagate changes from far to near side.
.br
\fBPush\fR - propagate changes from Slave to Master.
\fBPush\fR - propagate changes from near to far side.
.br
\fBNew\fR - propagate newly appeared messages.
.br
\fBReNew\fR - previously refused messages are re-evaluated for propagation.
Useful after flagging affected messages in the source Store or enlarging
MaxSize in the destination Store.
\fBReNew\fR - upgrade placeholders to full messages. Useful only with
a configured \fBMaxSize\fR.
.br
\fBDelete\fR - propagate message deletions. This applies only to messages that
are actually gone, i.e., were expunged. The affected messages in the remote
@ -539,10 +609,10 @@ In the first style, the flags select entire rows/colums in the matrix. Only
the cells which are selected both horizontally and vertically are asserted.
Specifying no flags from a class is like specifying all flags from this class.
For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate
new messages and flag changes from the Master to the Slave,
new messages and flag changes from the far side to the near side,
"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
from the Slave to the Master.
from the near side to the far side.
.br
In the second style, direction flags are concatenated with type flags; every
compound flag immediately asserts a cell in the matrix. In addition to at least
@ -550,22 +620,22 @@ one compound flag, the individual flags can be used as well, but as opposed to
the first style, they immediately assert all cells in their respective
row/column. For example,
"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate
message arrivals and deletions from the Master to the Slave and any changes
from the Slave to the Master.
message arrivals and deletions from the far side to the near side and any
changes from the near side to the far side.
Note that it is not allowed to assert a cell in two ways, e.g.
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
..
.
.TP
\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
\fBCreate\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the far/near side].
Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist.
(Global default: \fBNone\fR)
..
.
.TP
\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Propagate mailbox deletions [to the Master/Slave].
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Propagate mailbox deletions [to the far/near side].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does.
.br
@ -575,13 +645,13 @@ mark them as deleted. This ensures compatibility with \fBSyncState *\fR.
Note that for safety, non-empty mailboxes are never deleted.
.br
(Global default: \fBNone\fR)
..
.
.TP
\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Permanently remove all messages [on the far/near side] marked for deletion.
See \fBRECOMMENDATIONS\fR below.
(Global default: \fBNone\fR)
..
.
.TP
\fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR}
Selects whether their arrival time should be propagated together with
@ -591,30 +661,31 @@ sorting intact.
Note that IMAP does not guarantee that the time stamp (termed \fBinternal
date\fR) is actually the arrival time, but it is usually close enough.
(Default: \fBno\fR)
..
.
.P
\fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR,
\fBMaxMessages\fR, and \fBCopyArrivalDate\fR
can be used before any section for a global effect.
The global settings are overridden by Channel-specific options,
which in turn are overridden by command line switches.
..
.
.TP
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
Set the location of this Channel's synchronization state files. \fB*\fR means
that the state should be saved in a file named .mbsyncstate in the
Slave mailbox itself; this has the advantage that you needn't to care for the
state file if you delete the mailbox, but it works only with Maildir mailboxes,
obviously. Otherwise this is interpreted as a string to prepend to the Slave
mailbox name to make up a complete path.
Set the location of this Channel's synchronization state files.
\fB*\fR means that the state should be saved in a file named .mbsyncstate
in the near side mailbox itself; this has the advantage that you do not need
to handle the state file separately if you delete the mailbox, but it works
only with Maildir mailboxes, obviously.
Otherwise this is interpreted as a string to prepend to the near side mailbox
name to make up a complete path.
.br
This option can be used outside any section for a global effect. In this case
the appended string is made up according to the pattern
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR
\fB:\fIfar-store\fB:\fIfar-box\fB_:\fInear-store\fB:\fInear-box\fR
(see also \fBFieldDelimiter\fR below).
.br
(Global default: \fI~/.mbsync/\fR).
..
.
.SS Groups
.TP
\fBGroup\fR \fIname\fR [\fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]]] ...
@ -628,12 +699,12 @@ If you supply one or more \fIbox\fRes to a \fIchannel\fR, they will be used
instead of what is specified in the Channel's Patterns.
The same can be done on the command line, except that there newlines can be
used as mailbox name separators as well.
..
.
.TP
\fBChannel\fR[\fBs\fR] \fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]] ...
Add the specified channels to the group. This option can be specified multiple
times within a Group.
..
.
.SS Global Options
.TP
\fBFSync\fR \fByes\fR|\fBno\fR
@ -646,7 +717,7 @@ Enabling it is a wise choice for file systems mounted with data=writeback,
in particular modern systems like ext4, btrfs and xfs. The performance impact
on older file systems may be disproportionate.
(Default: \fByes\fR)
..
.
.TP
\fBFieldDelimiter\fR \fIdelim\fR
The character to use to delimit fields in the string appended to a global
@ -656,7 +727,7 @@ DOS/Windows file systems.
This option is meaningless for \fBSyncState\fR if the latter is \fB*\fR,
obviously. However, it also determines the default of \fBInfoDelimiter\fR.
(Global default: \fI;\fR on Windows, \fI:\fR everywhere else)
..
.
.TP
\fBBufferLimit\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
The per-Channel, per-direction instantaneous memory usage above which
@ -664,22 +735,22 @@ The per-Channel, per-direction instantaneous memory usage above which
absolute limit, as even a single message can consume more memory than
this.
(Default: \fI10M\fR)
..
.
.SH CONSOLE OUTPUT
If \fBmbsync\fR's output is connected to a console, it will print progress
counters by default. The output will look like this:
.P
.in +4
C: 1/2 B: 3/4 M: +13/13 *23/42 #0/0 S: +0/7 *0/0 #0/0
C: 1/2 B: 3/4 F: +13/13 *23/42 #0/0 N: +0/7 *0/0 #0/0
.in -4
.P
This represents the cumulative progress over channels, boxes, and messages
affected on master and slave, respectively.
affected on the far and near side, respectively.
The message counts represent added messages, messages with updated flags,
and trashed messages, respectively.
No attempt is made to calculate the totals in advance, so they grow over
time as more information is gathered.
..
.
.SH RECOMMENDATIONS
Make sure your IMAP server does not auto-expunge deleted messages - it is
slow, and semantically somewhat questionable. Specifically, Gmail needs to
@ -718,7 +789,7 @@ Mutt always does that, while mu4e needs to be configured to do it:
.in +4
(setq mu4e-change-filenames-when-moving t)
.in -4
..
.
.SH INHERENT PROBLEMS
Changes done after \fBmbsync\fR has retrieved the message list will not be
synchronised until the next time \fBmbsync\fR is invoked.
@ -729,7 +800,7 @@ lost if they are marked as deleted after the message list was retrieved but
before the mailbox is expunged.
There is no risk as long as the IMAP mailbox is accessed by only one client
(including \fBmbsync\fR) at a time.
..
.
.SH FILES
.TP
.B ~/.mbsyncrc
@ -737,15 +808,15 @@ Default configuration file
.TP
.B ~/.mbsync/
Directory containing synchronization state files
..
.
.SH SEE ALSO
mdconvert(1), isync(1), mutt(1), maildir(5)
mdconvert(1), mutt(1), maildir(5)
.P
Up to date information on \fBmbsync\fR can be found at http://isync.sf.net/
.P
SASL mechanisms are listed at
http://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml
..
.
.SH AUTHORS
Originally written by Michael R. Elkins,
rewritten and currently maintained by Oswald Buddenhagen,

View file

@ -21,16 +21,11 @@ Pass xxxxxxxx
#PassCmd "gpg --quiet --for-your-eyes-only --decrypt $HOME/imappassword.gpg"
# Fetch password from pwmd (http://pwmd.sourceforge.net/):
#PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile"
# On Mac OS X, run "KeyChain Access" -- File->New Password Item. Fill out form using
# "Keychain Item Name" http://IMAPSERVER (note: the "http://" is a hack)
# "Account Name" USERNAME
# "Password" PASSWORD
#PassCmd "/usr/bin/security find-internet-password -w -a USERNAME -s IMAPSERVER ~/Library/Keychains/login.keychain"
Channel work
Master :work:
Slave :local:work
Expunge Slave
Far :work:
Near :local:work
Expunge Near
Sync PullNew Push
@ -40,8 +35,8 @@ Port 6789
RequireSSL no
Channel personal
Master :personal:
Slave :local:personal
Far :personal:
Near :local:personal
Expunge Both
MaxMessages 150
MaxSize 200k
@ -50,8 +45,8 @@ IMAPStore remote
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Channel remote
Master :remote:
Slave :local:remote
Far :remote:
Near :local:remote
Group boxes
@ -70,8 +65,8 @@ RequireSSL no
UseTLSv1 no
Channel rst
Master :st1:somebox
Slave :st2:
Far :st1:somebox
Near :st2:
IMAPAccount server
@ -89,14 +84,14 @@ Path ~/Maildir/
SubFolders Verbatim
Channel o2o
Master :server:
Slave :mirror:
Far :server:
Near :mirror:
Patterns %
Group partial o2o:inbox,sent-mail,foobar
# INBOX => server, INBOX.foo => server.foo, etc.
Channel inbox
Master :server:INBOX
Slave :mirror:server
Far :server:INBOX
Near :mirror:server
Patterns *

View file

@ -117,11 +117,11 @@ convert( const char *box, int altmap )
sys_error( "Cannot create %s", tdpath );
goto sbork;
}
if (db_create( &db, 0, 0 )) {
if (db_create( &db, NULL, 0 )) {
fputs( "Error: db_create() failed\n", stderr );
goto tbork;
}
if ((ret = (db->open)( db, 0, dbpath, 0, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) {
if ((ret = (db->open)( db, NULL, dbpath, NULL, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) {
db->err( db, ret, "Error: db->open(%s)", dbpath );
dbork:
db->close( db, 0 );
@ -143,12 +143,12 @@ convert( const char *box, int altmap )
}
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
if ((ret = db->put( db, NULL, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot write UIDVALIDITY for '%s'", box );
goto dbork;
}
} else {
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if ((ret = db->get( db, NULL, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box );
goto dbork;
}
@ -188,7 +188,7 @@ convert( const char *box, int altmap )
uid = atoi( p + 3 );
value.data = &uid;
value.size = sizeof(uid);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
if ((ret = db->put( db, NULL, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot write UID for '%s'", box );
goto ebork;
}
@ -197,7 +197,7 @@ convert( const char *box, int altmap )
s = strpbrk( e->d_name, ",:" );
key.data = e->d_name;
key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if ((ret = db->get( db, NULL, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND) {
db->err( db, ret, "Error: cannot read UID for '%s'", box );
goto ebork;

View file

@ -20,28 +20,36 @@ use warnings;
use strict;
use Cwd;
use File::Path;
use File::Temp 'tempdir';
my $use_vg = $ENV{USE_VALGRIND};
my $mbsync = getcwd()."/mbsync";
-d "tmp" or mkdir "tmp";
if (!-d "tmp") {
unlink "tmp";
my $tdir = tempdir();
symlink $tdir, "tmp" or die "Cannot symlink temp directory: $!\n";
}
chdir "tmp" or die "Cannot enter temp direcory.\n";
sub show($$$);
sub test($$$@);
sub test($$$$);
################################################################################
# Format of the test defs: [ master, slave, state ]
# master/slave: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredMasterUid, MaxPushedUid, { muid, suid, flags }... ]
# Format of the test defs: [ far, near, state ]
# far/near: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
use enum qw(:=1 A..Z);
sub mn($) { chr(64 + shift) }
# generic syncing tests
my @x01 = (
[ 9,
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
[ 9,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 10, 9, "" ],
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "" ],
[ 8, 0, 0,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "" ],
);
@ -50,138 +58,184 @@ my @O01 = ("", "", "");
#show("01", "01", "01");
my @X01 = (
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 0, "", 7, 7, "FT", 0, 8, "", 10, 9, "", 9, 10, "" ],
);
test("full", \@x01, \@X01, @O01);
test("full", \@x01, \@X01, \@O01);
my @O02 = ("", "", "Expunge Both\n");
#show("01", "02", "02");
my @X02 = (
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 9, "", 10, 10, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", I, 9, "", J, 10, "" ],
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 10, 9, "", 9, 10, "" ],
);
test("full + expunge both", \@x01, \@X02, @O02);
test("full + expunge both", \@x01, \@X02, \@O02);
my @O03 = ("", "", "Expunge Slave\n");
my @O03 = ("", "", "Expunge Near\n");
#show("01", "03", "03");
my @X03 = (
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ],
);
test("full + expunge slave", \@x01, \@X03, @O03);
test("full + expunge near side", \@x01, \@X03, \@O03);
my @O04 = ("", "", "Sync Pull\n");
#show("01", "04", "04");
my @X04 = (
[ 9,
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
[ 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 10, "", 10, 9, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
[ 9, 0, 0,
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 0, 8, "", 9, 10, "" ],
);
test("pull", \@x01, \@X04, @O04);
test("pull", \@x01, \@X04, \@O04);
my @O05 = ("", "", "Sync Flags\n");
#show("01", "05", "05");
my @X05 = (
[ 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
[ 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "", 10, 9, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "", J, 9, "" ],
[ 8, 0, 0,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 8, 8, "" ],
);
test("flags", \@x01, \@X05, @O05);
test("flags", \@x01, \@X05, \@O05);
my @O06 = ("", "", "Sync Delete\n");
#show("01", "06", "06");
my @X06 = (
[ 9,
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "" ],
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "" ],
[ 9,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 9, "" ],
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
[ 8, 0, 0,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 0, "", 7, 7, "", 0, 8, "" ],
);
test("deletions", \@x01, \@X06, @O06);
test("deletions", \@x01, \@X06, \@O06);
my @O07 = ("", "", "Sync New\n");
#show("01", "07", "07");
my @X07 = (
[ 10,
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "", J, 10, "" ],
[ 10,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "", 10, 9, "", 9, 10, "" ],
);
test("new", \@x01, \@X07, @O07);
test("new", \@x01, \@X07, \@O07);
my @O08 = ("", "", "Sync PushFlags PullDelete\n");
#show("01", "08", "08");
my @X08 = (
[ 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
[ 9,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 9, "" ],
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
[ 8, 0, 0,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 0, 8, "" ],
);
test("push flags + pull deletions", \@x01, \@X08, @O08);
test("push flags + pull deletions", \@x01, \@X08, \@O08);
# size restriction tests
my @x10 = (
[ 2,
1, 1, "", 2, 2, "*" ],
A, 1, "", B, 2, "*" ],
[ 1,
3, 1, "*" ],
C, 1, "*" ],
[ 0, 0, 0,
],
);
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "");
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "Expunge Near");
#show("10", "11", "11");
my @X11 = (
[ 2,
1, 1, "", 2, 2, "*" ],
[ 2,
3, 1, "*", 1, 2, "" ],
[ 2, 0, 1,
0, 1, "^", 1, 2, "", 2, 0, "^" ],
);
test("max size", \@x10, \@X11, @O11);
my @O22 = ("", "MaxSize 1k\n", "");
#show("11", "22", "22");
my @X22 = (
[ 3,
1, 1, "", 2, 2, "*", 3, 3, "*" ],
[ 2,
3, 1, "*", 1, 2, "" ],
[ 2, 0, 1,
3, 1, "", 1, 2, "", 2, 0, "^" ],
A, 1, "", B, 2, "*", C, 3, "?" ],
[ 3,
C, 1, "*", A, 2, "", B, 3, "?" ],
[ 3, 0, 3,
3, 1, "<", 1, 2, "", 2, 3, ">" ],
);
test("slave max size", \@X11, \@X22, @O22);
test("max size", \@x10, \@X11, \@O11);
my @x22 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "?" ],
[ 3,
C, 1, "F*", A, 2, "", B, 3, "F?" ],
[ 3, 0, 3,
3, 1, "<", 1, 2, "", 2, 3, ">" ],
);
#show("22", "22", "11");
my @X22 = (
[ 4,
A, 1, "", B, 2, "*", C, 3, "T?", C, 4, "F*" ],
[ 4,
C, 1, "F*", A, 2, "", B, 4, "*" ],
[ 4, 0, 4,
4, 1, "F", 3, 0, "T", 1, 2, "", 2, 4, "" ],
);
test("max size + flagging", \@x22, \@X22, \@O11);
my @x23 = (
[ 2,
A, 1, "", B, 2, "F*" ],
[ 1,
C, 1, "F*" ],
[ 0, 0, 0,
],
);
my @X23 = (
[ 3,
A, 1, "", B, 2, "F*", C, 3, "F*" ],
[ 3,
C, 1, "F*", A, 2, "", B, 3, "F*" ],
[ 3, 0, 3,
3, 1, "F", 1, 2, "", 2, 3, "F" ]
);
test("max size + initial flagging", \@x23, \@X23, \@O11);
my @x24 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "F*" ],
[ 1,
A, 1, "" ],
[ 3, 0, 1,
1, 1, "", 2, 0, "^", 3, 0, "^" ],
);
my @X24 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "F*" ],
[ 3,
A, 1, "", B, 2, "?", C, 3, "F*" ],
[ 3, 0, 3,
1, 1, "", 2, 2, ">", 3, 3, "F" ],
);
test("max size (pre-1.4 legacy)", \@x24, \@X24, \@O11);
# expiration tests
my @x30 = (
[ 6,
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
[ 0,
],
[ 0, 0, 0,
@ -192,31 +246,31 @@ my @O31 = ("", "", "MaxMessages 3\n");
#show("30", "31", "31");
my @X31 = (
[ 6,
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
[ 5,
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
[ 6, 3, 0,
A, 1, "F", B, 2, "", D, 3, "", E, 4, "S", F, 5, "" ],
[ 6, 3, 5,
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
);
test("max messages", \@x30, \@X31, @O31);
test("max messages", \@x30, \@X31, \@O31);
my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n");
#show("30", "32", "32");
my @X32 = (
[ 6,
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
[ 4,
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
[ 6, 3, 0,
A, 1, "F", D, 2, "", E, 3, "S", F, 4, "" ],
[ 6, 3, 4,
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
);
test("max messages vs. unread", \@x30, \@X32, @O32);
test("max messages vs. unread", \@x30, \@X32, \@O32);
my @x50 = (
[ 6,
1, 1, "FS", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
A, 1, "FS", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
[ 6,
1, 1, "S", 2, 2, "ST", 4, 4, "", 5, 5, "", 6, 6, "" ],
A, 1, "S", B, 2, "ST", D, 4, "", E, 5, "", F, 6, "" ],
[ 6, 3, 0,
1, 1, "FS", 2, 2, "~S", 3, 3, "~S", 4, 4, "", 5, 5, "", 6, 6, "" ],
);
@ -225,19 +279,17 @@ my @O51 = ("", "", "MaxMessages 3\nExpunge Both\n");
#show("50", "51", "51");
my @X51 = (
[ 6,
1, 1, "S", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
A, 1, "S", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
[ 6,
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
[ 6, 3, 0,
B, 2, "FS", D, 4, "", E, 5, "", F, 6, "" ],
[ 6, 3, 6,
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
);
test("max messages + expunge", \@x50, \@X51, @O51);
test("max messages + expunge", \@x50, \@X51, \@O51);
################################################################################
chdir "..";
rmdir "tmp";
print "OK.\n";
exit 0;
@ -252,27 +304,29 @@ sub qm($)
return $_;
}
# $master, $slave, $channel
sub writecfg($$$)
# [ $far, $near, $channel ]
sub writecfg($)
{
my ($sfx) = @_;
open(FILE, ">", ".mbsyncrc") or
die "Cannot open .mbsyncrc.\n";
print FILE
"FSync no
MaildirStore master
MaildirStore far
Path ./
Inbox ./master
".shift()."
MaildirStore slave
Inbox ./far
".$$sfx[0]."
MaildirStore near
Path ./
Inbox ./slave
".shift()."
Inbox ./near
".$$sfx[1]."
Channel test
Master :master:
Slave :slave:
Far :far:
Near :near:
SyncState *
".shift();
".$$sfx[2];
close FILE;
}
@ -282,10 +336,11 @@ sub killcfg()
unlink ".mbsyncrc";
}
# $options
sub runsync($$)
# $run_async, $mbsync_options, $log_file
# Return: $exit_code, \@mbsync_output
sub runsync($$$)
{
my ($flags, $file) = @_;
my ($async, $flags, $file) = @_;
my $cmd;
if ($use_vg) {
@ -293,7 +348,8 @@ sub runsync($$)
} else {
$flags .= " -D";
}
$cmd .= "$mbsync -Z $flags -c .mbsyncrc test";
$flags .= " -Ta" if ($async);
$cmd .= "$mbsync -Tz $flags -c .mbsyncrc test";
open FILE, "$cmd 2>&1 |";
my @out = <FILE>;
close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** mbsync exited with signal ".($?&127).", code ".($?>>8)."\n");
@ -302,11 +358,12 @@ sub runsync($$)
print FILE @out;
close FILE;
}
return $?, @out;
return $?, \@out;
}
# $path
# Return: $max_uid, { uid => [ seq, flags ] }
sub readbox($)
{
my $bn = shift;
@ -323,11 +380,9 @@ sub readbox($)
for my $d ("cur", "new") {
opendir(DIR, $bn."/".$d) or next;
for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
my ($uid, $flg, $num);
my ($uid, $flg, $ph, $num);
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
($uid, $flg) = ($1, $2);
} elsif ($f =~ /^\d+\.\d+_(\d+)\.[-[:alnum:]]+:2,(.*)$/) {
($uid, $flg) = (0, $2);
} else {
print STDERR "unrecognided file name '$f' in '$bn'.\n";
exit 1;
@ -335,7 +390,7 @@ sub readbox($)
open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n";
my $sz = 0;
while (<FILE>) {
/^Subject: (\d+)$/ && ($num = $1);
/^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = $2);
$sz += length($_);
}
close FILE;
@ -343,10 +398,10 @@ sub readbox($)
print STDERR "message '$f' in '$bn' has no identifier.\n";
exit 1;
}
@{ $ms{$num} } = ($uid, $flg.($sz>1000?"*":""));
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":""));
}
}
return ($mu, %ms);
return $mu, \%ms;
}
# $boxname
@ -357,18 +412,18 @@ sub showbox($)
{
my ($bn) = @_;
my ($mu, %ms) = readbox($bn);
my @MS = ($mu);
for my $num (sort { $a <=> $b } keys %ms) {
push @MS, $num, $ms{$num}[0], $ms{$num}[1];
my ($mu, $ms) = readbox($bn);
my @bc = ($mu);
for my $uid (sort { $a <=> $b } keys %$ms) {
push @bc, $$ms{$uid}[0], $uid, $$ms{$uid}[1];
}
printbox($bn, @MS);
printbox(\@bc);
}
# $filename
# Output:
# [ maxuid[M], mmaxxuid, maxuid[S],
# uid[M], uid[S], "flags", ... ],
# [ maxuid[F], maxxfuid, maxuid[N],
# uid[F], uid[N], "flags", ... ],
sub showstate($)
{
my ($fn) = @_;
@ -396,13 +451,13 @@ sub showstate($)
return;
}
my @T = ($hdr{'MaxPulledUid'} // "missing",
$hdr{'MaxExpiredMasterUid'} // "0",
$hdr{'MaxExpiredFarUid'} // "0",
$hdr{'MaxPushedUid'} // "missing");
for (@ls) {
/^(\d+) (\d+) (.*)$/;
push @T, $1, $2, $3;
}
printstate(@T);
printstate(\@T);
}
# $filename
@ -410,8 +465,8 @@ sub showchan($)
{
my ($fn) = @_;
showbox("master");
showbox("slave");
showbox("far");
showbox("near");
showstate($fn);
}
@ -419,109 +474,105 @@ sub showchan($)
sub show($$$)
{
my ($sx, $tx, $sfxn) = @_;
my (@sp, @sfx);
eval "\@sp = \@x$sx";
eval "\@sfx = \@O$sfxn";
mkchan($sp[0], $sp[1], @{ $sp[2] });
my ($sp, $sfx);
eval "\$sp = \\\@x$sx";
eval "\$sfx = \\\@O$sfxn";
mkchan($$sp[0], $$sp[1], $$sp[2]);
print "my \@x$sx = (\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
print ");\n";
&writecfg(@sfx);
runsync("", "");
writecfg($sfx);
runsync(0, "", "");
killcfg();
print "my \@X$tx = (\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
print ");\n";
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
rmtree "slave";
rmtree "master";
print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n";
rmtree "near";
rmtree "far";
}
# $boxname, $maxuid, @msgs
sub mkbox($$@)
# $box_name, \@box_state
sub mkbox($$)
{
my ($bn, $mu, @ms) = @_;
my ($bn, $bs) = @_;
rmtree($bn);
(mkdir($bn) and mkdir($bn."/tmp") and mkdir($bn."/new") and mkdir($bn."/cur")) or
die "Cannot create mailbox $bn.\n";
open(FILE, ">", $bn."/.uidvalidity") or die "Cannot create UID validity for mailbox $bn.\n";
print FILE "1\n$mu\n";
print FILE "1\n$$bs[0]\n";
close FILE;
while (@ms) {
my ($num, $uid, $flg) = (shift @ms, shift @ms, shift @ms);
if ($uid) {
$uid = ",U=".$uid;
} else {
$uid = "";
}
for (my $i = 1; $i < @$bs; $i += 3) {
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
my $big = $flg =~ s/\*//;
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local".$uid.":2,".$flg) or
die "Cannot create message $num in mailbox $bn.\n";
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: $num\n\n".(("A"x50)."\n")x($big*30);
my $ph = $flg =~ s/\?//;
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local,U=".$uid.":2,".$flg) or
die "Cannot create message ".mn($num)." in mailbox $bn.\n";
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: ".($ph?"[placeholder] ":"").$num."\n\n".(("A"x50)."\n")x($big*30);
close FILE;
}
}
# \@master, \@slave, @syncstate
sub mkchan($$@)
# \@far_state, \@near_state, \@sync_state
sub mkchan($$$)
{
my ($m, $s, @t) = @_;
&mkbox("master", @{ $m });
&mkbox("slave", @{ $s });
open(FILE, ">", "slave/.mbsyncstate") or
my ($f, $n, $t) = @_;
mkbox("far", $f);
mkbox("near", $n);
open(FILE, ">", "near/.mbsyncstate") or
die "Cannot create sync state.\n";
print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
"SlaveUidValidity 1\nMaxExpiredMasterUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
while (@t) {
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
print FILE "FarUidValidity 1\nMaxPulledUid ".$$t[0]."\n".
"NearUidValidity 1\nMaxExpiredFarUid ".$$t[1]."\nMaxPushedUid ".$$t[2]."\n\n";
for (my $i = 3; $i < @$t; $i += 3) {
print FILE $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."\n";
}
close FILE;
}
# $config, $boxname, $maxuid, @msgs
sub ckbox($$$@)
# $box_name, \@box_state
sub ckbox($$)
{
my ($bn, $MU, @MS) = @_;
my ($bn, $bs) = @_;
my ($mu, %ms) = readbox($bn);
if ($mu != $MU) {
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $MU).\n";
my ($mu, $ms) = readbox($bn);
if ($mu != $$bs[0]) {
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\n";
return 1;
}
while (@MS) {
my ($num, $uid, $flg) = (shift @MS, shift @MS, shift @MS);
if (!defined $ms{$num}) {
print STDERR "No message $bn:$num.\n";
for (my $i = 1; $i < @$bs; $i += 3) {
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
my $m = delete $$ms{$uid};
if (!defined $m) {
print STDERR "No message $bn:$uid.\n";
return 1;
}
if ($ms{$num}[0] ne $uid) {
print STDERR "UID mismatch for $bn:$num.\n";
if ($$m[0] ne $num) {
print STDERR "Subject mismatch for $bn:$uid.\n";
return 1;
}
if ($ms{$num}[1] ne $flg) {
print STDERR "Flag mismatch for $bn:$num.\n";
if ($$m[1] ne $flg) {
print STDERR "Flag mismatch for $bn:$uid.\n";
return 1;
}
delete $ms{$num};
}
if (%ms) {
print STDERR "Excess messages in '$bn': ".join(", ", sort({$a <=> $b } keys(%ms))).".\n";
if (%$ms) {
print STDERR "Excess messages in '$bn': ".join(", ", sort({ $a <=> $b } keys(%$ms))).".\n";
return 1;
}
return 0;
}
# $filename, @syncstate
sub ckstate($@)
# $state_file, \@sync_state
sub ckstate($$)
{
my ($fn, $mmaxuid, $mmaxxuid, $smaxuid, @T) = @_;
my ($fn, $t) = @_;
my %hdr;
$hdr{'MasterUidValidity'} = "1";
$hdr{'SlaveUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $mmaxuid;
$hdr{'MaxPushedUid'} = $smaxuid;
$hdr{'MaxExpiredMasterUid'} = $mmaxxuid if ($mmaxxuid ne 0);
$hdr{'FarUidValidity'} = "1";
$hdr{'NearUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $$t[0];
$hdr{'MaxPushedUid'} = $$t[2];
$hdr{'MaxExpiredFarUid'} = $$t[1] if ($$t[1] ne 0);
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
chomp(my @ls = <FILE>);
close FILE;
@ -551,65 +602,68 @@ sub ckstate($@)
print STDERR "Keys missing from sync state header: @ky\n";
return 1;
}
my $i = 3;
for my $l (@ls) {
if (!@T) {
if ($i == @$t) {
print STDERR "Excess sync state entry: '$l'.\n";
return 1;
}
my $xl = shift(@T)." ".shift(@T)." ".shift(@T);
my $xl = $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2];
if ($l ne $xl) {
print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n";
return 1;
}
$i += 3;
}
if (@T) {
print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n";
if ($i < @$t) {
print STDERR "Missing sync state entry: '".$$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."'.\n";
return 1;
}
return 0;
}
# $statefile, \@chan_state
# $state_file, \@chan_state
sub ckchan($$)
{
my ($F, $cs) = @_;
my $rslt = ckstate($F, @{ $$cs[2] });
$rslt |= &ckbox("master", @{ $$cs[0] });
$rslt |= &ckbox("slave", @{ $$cs[1] });
my ($fn, $cs) = @_;
my $rslt = ckstate($fn, $$cs[2]);
$rslt |= ckbox("far", $$cs[0]);
$rslt |= ckbox("near", $$cs[1]);
return $rslt;
}
sub printbox($$@)
# \@box_state
sub printbox($)
{
my ($bn, $mu, @ms) = @_;
my ($bs) = @_;
print " [ $mu,\n ";
print " [ $$bs[0],\n ";
my $frst = 1;
while (@ms) {
for (my $i = 1; $i < @$bs; $i += 3) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print shift(@ms).", ".shift(@ms).", \"".shift(@ms)."\"";
print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\"";
}
print " ],\n";
}
# @syncstate
sub printstate(@)
# \@sync_state
sub printstate($)
{
my (@t) = @_;
my ($t) = @_;
print " [ ".shift(@t).", ".shift(@t).", ".shift(@t).",\n ";
print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n ";
my $frst = 1;
while (@t) {
for (my $i = 3; $i < @$t; $i += 3) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print((shift(@t) // "??").", ".(shift(@t) // "??").", \"".(shift(@t) // "??")."\"");
print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\"");
}
print " ],\n";
}
@ -619,9 +673,9 @@ sub printchan($)
{
my ($cs) = @_;
&printbox("master", @{ $$cs[0] });
&printbox("slave", @{ $$cs[1] });
printstate(@{ $$cs[2] });
printbox($$cs[0]);
printbox($$cs[1]);
printstate($$cs[2]);
}
sub readfile($)
@ -631,112 +685,122 @@ sub readfile($)
open(FILE, $file) or return;
my @nj = <FILE>;
close FILE;
return @nj;
return \@nj;
}
# $title, \@source_state, \@target_state, @channel_configs
sub test($$$@)
# $run_async, \@source_state, \@target_state, \@channel_configs
sub test_impl($$$$)
{
my ($ttl, $sx, $tx, @sfx) = @_;
my ($async, $sx, $tx, $sfx) = @_;
return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV);
print "Testing: ".$ttl." ...\n";
&writecfg(@sfx);
mkchan($$sx[0], $$sx[1], $$sx[2]);
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
my ($xc, @ret) = runsync("-J", "1-initial.log");
if ($xc || ckchan("slave/.mbsyncstate.new", $tx)) {
my ($xc, $ret) = runsync($async, "-Tj", "1-initial.log");
if ($xc || ckchan("near/.mbsyncstate.new", $tx)) {
print "Input:\n";
printchan($sx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
if (!$xc) {
print "Expected result:\n";
printchan($tx);
print "Actual result:\n";
showchan("slave/.mbsyncstate.new");
showchan("near/.mbsyncstate.new");
}
print "Debug output:\n";
print @ret;
print @$ret;
exit 1;
}
my @nj = readfile("slave/.mbsyncstate.journal");
my ($jxc, @jret) = runsync("-0 --no-expunge", "2-replay.log");
if ($jxc || ckstate("slave/.mbsyncstate", @{ $$tx[2] })) {
my $nj = readfile("near/.mbsyncstate.journal");
my ($jxc, $jret) = runsync($async, "-0 --no-expunge", "2-replay.log");
if ($jxc || ckstate("near/.mbsyncstate", $$tx[2])) {
print "Journal replay failed.\n";
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
print "Old State:\n";
printstate(@{ $$sx[2] });
print "Journal:\n".join("", @nj)."\n";
printstate($$sx[2]);
print "Journal:\n".join("", @$nj)."\n";
if (!$jxc) {
print "Expected New State:\n";
printstate(@{ $$tx[2] });
printstate($$tx[2]);
print "New State:\n";
showstate("slave/.mbsyncstate");
showstate("near/.mbsyncstate");
}
print "Debug output:\n";
print @jret;
print @$jret;
exit 1;
}
my ($ixc, @iret) = runsync("", "3-verify.log");
if ($ixc || ckchan("slave/.mbsyncstate", $tx)) {
my ($ixc, $iret) = runsync($async, "", "3-verify.log");
if ($ixc || ckchan("near/.mbsyncstate", $tx)) {
print "Idempotence verification run failed.\n";
print "Input == Expected result:\n";
printchan($tx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
if (!$ixc) {
print "Actual result:\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
}
print "Debug output:\n";
print @iret;
print @$iret;
exit 1;
}
rmtree "slave";
rmtree "master";
rmtree "near";
rmtree "far";
my $njl = (@nj - 1) * 2;
for (my $l = 2; $l < $njl; $l++) {
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
my $njl = (@$nj - 1) * 2;
for (my $l = 1; $l <= $njl; $l++) {
mkchan($$sx[0], $$sx[1], $$sx[2]);
my ($nxc, @nret) = runsync("-J$l", "4-interrupt.log");
my ($nxc, $nret) = runsync($async, "-Tj$l", "4-interrupt.log");
if ($nxc != (100 + ($l & 1)) << 8) {
print "Interrupting at step $l/$njl failed.\n";
print "Debug output:\n";
print @nret;
print @$nret;
exit 1;
}
($nxc, @nret) = runsync("-J", "5-resume.log");
if ($nxc || ckchan("slave/.mbsyncstate.new", $tx)) {
($nxc, $nret) = runsync($async, "-Tj", "5-resume.log");
if ($nxc || ckchan("near/.mbsyncstate.new", $tx)) {
print "Resuming from step $l/$njl failed.\n";
print "Input:\n";
printchan($sx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
my @nnj = readfile("slave/.mbsyncstate.journal");
print "Journal:\n".join("", @nnj[0..($l / 2 - 1)])."-------\n".join("", @nnj[($l / 2)..$#nnj])."\n";
print "Full journal:\n".join("", @nj)."\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
my $nnj = readfile("near/.mbsyncstate.journal");
my $ln = int($l / 2);
print "Journal:\n".join("", @$nnj[0..$ln])."-------\n".join("", @$nnj[($ln + 1)..$#$nnj])."\n";
print "Full journal:\n".join("", @$nj)."\n";
if (!$nxc) {
print "Expected result:\n";
printchan($tx);
print "Actual result:\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate.new");
}
print "Debug output:\n";
print @nret;
print @$nret;
exit 1;
}
rmtree "slave";
rmtree "master";
rmtree "near";
rmtree "far";
}
}
# $title, \@source_state, \@target_state, \@channel_configs
sub test($$$$)
{
my ($ttl, $sx, $tx, $sfx) = @_;
return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV);
print "Testing: ".$ttl." ...\n";
writecfg($sfx);
test_impl(0, $sx, $tx, $sfx);
test_impl(1, $sx, $tx, $sfx);
killcfg();
}

View file

@ -41,7 +41,7 @@
# include <openssl/err.h>
# include <openssl/x509v3.h>
# if OPENSSL_VERSION_NUMBER < 0x10100000L \
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070100fL)
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070100fL)
# define X509_OBJECT_get0_X509(o) ((o)->data.x509)
# define X509_STORE_get0_objects(o) ((o)->objs)
# endif
@ -63,6 +63,34 @@ socket_fail( conn_t *conn )
}
#ifdef HAVE_LIBSSL
static void ATTR_PRINTFLIKE(1, 2)
print_ssl_errors( const char *fmt, ... )
{
char *action;
va_list va;
ulong err;
va_start( va, fmt );
nfvasprintf( &action, fmt, va );
va_end( va );
while ((err = ERR_get_error()))
error( "Error while %s: %s\n", action, ERR_error_string( err, NULL ) );
free( action );
}
static int
print_ssl_socket_errors( const char *func, conn_t *conn )
{
ulong err;
int num = 0;
while ((err = ERR_get_error())) {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, NULL ) );
num++;
}
return num;
}
static int
ssl_return( const char *func, conn_t *conn, int ret )
{
@ -76,20 +104,20 @@ ssl_return( const char *func, conn_t *conn, int ret )
FALLTHROUGH
case SSL_ERROR_WANT_READ:
return 0;
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
if (!(err = ERR_get_error())) {
if (ret == 0) {
print_ssl_socket_errors( func, conn );
break;
case SSL_ERROR_SYSCALL:
if (print_ssl_socket_errors( func, conn ))
break;
if (ret == 0) {
case SSL_ERROR_ZERO_RETURN:
/* Callers take the short path out, so signal higher layers from here. */
conn->state = SCK_EOF;
conn->read_callback( conn->callback_aux );
return -1;
}
sys_error( "Socket error: secure %s %s", func, conn->name );
} else {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
/* Callers take the short path out, so signal higher layers from here. */
conn->state = SCK_EOF;
conn->read_callback( conn->callback_aux );
return -1;
}
sys_error( "Socket error: secure %s %s", func, conn->name );
break;
default:
error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err );
@ -166,7 +194,6 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
int i;
long err;
X509 *cert;
STACK_OF(X509_OBJECT) *trusted;
cert = SSL_get_peer_certificate( sock->ssl );
if (!cert) {
@ -174,39 +201,54 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
return -1;
}
trusted = (STACK_OF(X509_OBJECT) *)sock->conf->trusted_certs;
for (i = 0; i < sk_X509_OBJECT_num( trusted ); i++) {
if (!X509_cmp( cert, X509_OBJECT_get0_X509( sk_X509_OBJECT_value( trusted, i ) ) ))
for (i = 0; i < sk_X509_num( sock->conf->trusted_certs ); i++) {
if (!X509_cmp( cert, sk_X509_value( sock->conf->trusted_certs, i ) )) {
X509_free( cert );
return 0;
}
}
err = SSL_get_verify_result( sock->ssl );
if (err != X509_V_OK) {
error( "SSL error connecting %s: %s\n", sock->name, X509_verify_cert_error_string( err ) );
X509_free( cert );
return -1;
}
if (!conf->host) {
error( "SSL error connecting %s: Neither host nor matching certificate specified\n", sock->name );
X509_free( cert );
return -1;
}
return verify_hostname( cert, conf->host );
int ret = verify_hostname( cert, conf->host );
X509_free( cert );
return ret;
}
static int
init_ssl_ctx( const server_conf_t *conf )
{
DIAG_PUSH
DIAG_DISABLE("-Wcast-qual") // C has no 'mutable' or const_cast<> ...
server_conf_t *mconf = (server_conf_t *)conf;
int options = 0;
DIAG_POP
if (conf->SSLContext)
return conf->ssl_ctx_valid;
mconf->SSLContext = SSL_CTX_new( SSLv23_client_method() );
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
const SSL_METHOD *method = TLS_client_method();
#else
const SSL_METHOD *method = SSLv23_client_method();
#endif
if (!(mconf->SSLContext = SSL_CTX_new( method ))) {
print_ssl_errors( "initializing SSL context" );
return 0;
}
if (!(conf->ssl_versions & SSLv3))
options |= SSL_OP_NO_SSLv3;
uint options = SSL_OP_NO_SSLv3;
if (!(conf->ssl_versions & TLSv1))
options |= SSL_OP_NO_TLSv1;
#ifdef SSL_OP_NO_TLSv1_1
@ -217,29 +259,59 @@ init_ssl_ctx( const server_conf_t *conf )
if (!(conf->ssl_versions & TLSv1_2))
options |= SSL_OP_NO_TLSv1_2;
#endif
#ifdef SSL_OP_NO_TLSv1_3
if (!(conf->ssl_versions & TLSv1_3))
options |= SSL_OP_NO_TLSv1_3;
#endif
SSL_CTX_set_options( mconf->SSLContext, options );
if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) {
error( "Error while loading certificate file '%s': %s\n",
conf->cert_file, ERR_error_string( ERR_get_error(), 0 ) );
if (conf->cipher_string && !SSL_CTX_set_cipher_list( mconf->SSLContext, conf->cipher_string )) {
print_ssl_errors( "setting cipher string '%s'", conf->cipher_string );
return 0;
}
mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) );
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext ))
warn( "Warning: Unable to load default certificate files: %s\n",
ERR_error_string( ERR_get_error(), 0 ) );
if (!(mconf->trusted_certs = sk_X509_new_null()))
oom();
if (conf->cert_file) {
X509_STORE *store;
if (!(store = X509_STORE_new()))
oom();
if (!X509_STORE_load_locations( store, conf->cert_file, NULL )) {
print_ssl_errors( "loading certificate file '%s'", conf->cert_file );
return 0;
}
STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects( store );
for (int i = 0; i < sk_X509_OBJECT_num( objs ); i++) {
X509 *cert = X509_OBJECT_get0_X509( sk_X509_OBJECT_value( objs, i ) );
if (cert) {
if (X509_check_ca( cert )) {
if (!X509_STORE_add_cert( SSL_CTX_get_cert_store( mconf->SSLContext ), cert ))
oom();
} else {
X509_up_ref( cert ); // Locking failure assumed impossible
if (!sk_X509_push( mconf->trusted_certs, cert ))
oom();
}
}
}
X509_STORE_free( store );
}
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) {
ulong err;
while ((err = ERR_get_error()))
warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, NULL ) );
}
SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL );
if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) {
error( "Error while loading client certificate file '%s': %s\n",
conf->client_certfile, ERR_error_string( ERR_get_error(), 0 ) );
print_ssl_errors( "loading client certificate file '%s'", conf->client_certfile );
return 0;
}
if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) {
error( "Error while loading client private key '%s': %s\n",
conf->client_keyfile, ERR_error_string( ERR_get_error(), 0 ) );
print_ssl_errors( "loading client private key '%s'", conf->client_keyfile );
return 0;
}
@ -257,6 +329,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
static int ssl_inited;
conn->callbacks.starttls = cb;
conn->state = SCK_STARTTLS;
if (!ssl_inited) {
SSL_library_init();
@ -270,13 +343,23 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
}
init_wakeup( &conn->ssl_fake, ssl_fake_cb, conn );
conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext );
if (ssl_return( "set server name", conn, SSL_set_tlsext_host_name( conn->ssl, conn->conf->host ) ) < 0)
if (!(conn->ssl = SSL_new( ((server_conf_t const *)conn->conf)->SSLContext ))) {
print_ssl_errors( "initializing SSL connection" );
start_tls_p3( conn, 0 );
return;
SSL_set_fd( conn->ssl, conn->fd );
}
if (!SSL_set_tlsext_host_name( conn->ssl, conn->conf->host )) {
print_ssl_errors( "setting SSL server host name" );
start_tls_p3( conn, 0 );
return;
}
if (!SSL_set_fd( conn->ssl, conn->fd )) {
print_ssl_errors( "setting SSL socket fd" );
start_tls_p3( conn, 0 );
return;
}
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
socket_expect_read( conn, 1 );
conn->state = SCK_STARTTLS;
socket_expect_activity( conn, 1 );
start_tls_p2( conn );
}
@ -295,7 +378,7 @@ start_tls_p2( conn_t *conn )
static void start_tls_p3( conn_t *conn, int ok )
{
socket_expect_read( conn, 0 );
socket_expect_activity( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.starttls( ok, conn->callback_aux );
}
@ -377,6 +460,32 @@ socket_close_internal( conn_t *sock )
sock->fd = -1;
}
#ifndef HAVE_IPV6
struct addr_info {
struct addr_info *ai_next;
struct sockaddr_in ai_addr[1];
};
#define freeaddrinfo(ai) free( ai )
static struct addr_info *
init_addrinfo( struct hostent *he )
{
uint naddr = 0;
for (char **addr = he->h_addr_list; *addr; addr++)
naddr++;
struct addr_info *caddr = nfcalloc( naddr * sizeof(struct addrinfo) );
struct addr_info *ret, **caddrp = &ret;
for (char **addr = he->h_addr_list; *addr; addr++, caddr++) {
caddr->ai_addr->sin_family = AF_INET;
memcpy( &caddr->ai_addr->sin_addr.s_addr, *addr, sizeof(struct in_addr) );
*caddrp = caddr;
caddrp = &caddr->ai_next;
}
return ret;
}
#endif
void
socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
{
@ -426,8 +535,6 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
return;
}
info( "\vok\n" );
sock->curr_addr = sock->addrs;
#else
struct hostent *he;
@ -440,8 +547,9 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
}
info( "\vok\n" );
sock->curr_addr = he->h_addr_list;
sock->addrs = init_addrinfo( he );
#endif
sock->curr_addr = sock->addrs;
socket_connect_one( sock );
}
}
@ -453,16 +561,10 @@ socket_connect_one( conn_t *sock )
#ifdef HAVE_IPV6
struct addrinfo *ai;
#else
struct {
struct sockaddr_in ai_addr[1];
} ai[1];
struct addr_info *ai;
#endif
#ifdef HAVE_IPV6
if (!(ai = sock->curr_addr)) {
#else
if (!*sock->curr_addr) {
#endif
error( "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock );
return;
@ -479,11 +581,6 @@ socket_connect_one( conn_t *sock )
#endif
{
struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
#ifndef HAVE_IPV6
memset( in, 0, sizeof(*in) );
in->sin_family = AF_INET;
in->sin_addr.s_addr = *((int *)*sock->curr_addr);
#endif
in->sin_port = htons( sock->conf->port );
nfasprintf( &sock->name, "%s (%s:%hu)",
sock->conf->host, inet_ntoa( in->sin_addr ), sock->conf->port );
@ -511,7 +608,7 @@ socket_connect_one( conn_t *sock )
return;
}
conf_notifier( &sock->notify, 0, POLLOUT );
socket_expect_read( sock, 1 );
socket_expect_activity( sock, 1 );
sock->state = SCK_CONNECTING;
info( "\v\n" );
return;
@ -525,12 +622,8 @@ socket_connect_next( conn_t *conn )
{
sys_error( "Cannot connect to %s", conn->name );
free( conn->name );
conn->name = 0;
#ifdef HAVE_IPV6
conn->name = NULL;
conn->curr_addr = conn->curr_addr->ai_next;
#else
conn->curr_addr++;
#endif
socket_connect_one( conn );
}
@ -544,12 +637,12 @@ socket_connect_failed( conn_t *conn )
static void
socket_connected( conn_t *conn )
{
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
conn->addrs = 0;
#endif
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
}
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_read( conn, 0 );
socket_expect_activity( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.connect( 1, conn->callback_aux );
}
@ -557,14 +650,12 @@ socket_connected( conn_t *conn )
static void
socket_cleanup_names( conn_t *conn )
{
#ifdef HAVE_IPV6
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = 0;
conn->addrs = NULL;
}
#endif
free( conn->name );
conn->name = 0;
conn->name = NULL;
}
static void
@ -585,7 +676,7 @@ socket_close( conn_t *sock )
#ifdef HAVE_LIBSSL
if (sock->ssl) {
SSL_free( sock->ssl );
sock->ssl = 0;
sock->ssl = NULL;
wipe_wakeup( &sock->ssl_fake );
}
#endif
@ -593,23 +684,23 @@ socket_close( conn_t *sock )
if (sock->in_z) {
inflateEnd( sock->in_z );
free( sock->in_z );
sock->in_z = 0;
sock->in_z = NULL;
deflateEnd( sock->out_z );
free( sock->out_z );
sock->out_z = 0;
sock->out_z = NULL;
wipe_wakeup( &sock->z_fake );
}
#endif
while (sock->write_buf)
dispose_chunk( sock );
free( sock->append_buf );
sock->append_buf = 0;
sock->append_buf = NULL;
}
static int
prepare_read( conn_t *sock, char **buf, int *len )
prepare_read( conn_t *sock, char **buf, uint *len )
{
int n = sock->offset + sock->bytes;
uint n = sock->offset + sock->bytes;
if (!(*len = sizeof(sock->buf) - n)) {
error( "Socket error: receive buffer full. Probably protocol error.\n" );
socket_fail( sock );
@ -620,19 +711,17 @@ prepare_read( conn_t *sock, char **buf, int *len )
}
static int
do_read( conn_t *sock, char *buf, int len )
do_read( conn_t *sock, char *buf, uint len )
{
int n;
assert( sock->fd >= 0 );
if (pending_wakeup( &sock->fd_timeout ))
conf_wakeup( &sock->fd_timeout, sock->conf->timeout );
#ifdef HAVE_LIBSSL
if (sock->ssl) {
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, (int)len ) )) <= 0)
return n;
if (n == len && SSL_pending( sock->ssl ))
if (n == (int)len && SSL_pending( sock->ssl ))
conf_wakeup( &sock->ssl_fake, 0 );
} else
#endif
@ -655,7 +744,8 @@ static void
socket_fill_z( conn_t *sock )
{
char *buf;
int len, ret;
uint len;
int ret;
if (prepare_read( sock, &buf, &len ) < 0)
return;
@ -675,7 +765,7 @@ socket_fill_z( conn_t *sock )
if (!sock->in_z->avail_out)
conf_wakeup( &sock->z_fake, 0 );
if ((len = (char *)sock->in_z->next_out - buf)) {
if ((len = (uint)((char *)sock->in_z->next_out - buf))) {
sock->bytes += len;
sock->read_callback( sock->callback_aux );
}
@ -693,36 +783,37 @@ socket_fill( conn_t *sock )
sock->in_z->next_in = (uchar *)sock->z_buf;
if ((ret = do_read( sock, sock->z_buf, sizeof(sock->z_buf) )) <= 0)
return;
sock->in_z->avail_in = ret;
sock->in_z->avail_in = (uint)ret;
socket_fill_z( sock );
} else
#endif
{
char *buf;
int len;
uint len;
if (prepare_read( sock, &buf, &len ) < 0)
return;
if ((len = do_read( sock, buf, len )) <= 0)
int n;
if ((n = do_read( sock, buf, len )) <= 0)
return;
sock->bytes += len;
sock->bytes += (uint)n;
sock->read_callback( sock->callback_aux );
}
}
void
socket_expect_read( conn_t *conn, int expect )
socket_expect_activity( conn_t *conn, int expect )
{
if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 );
}
int
socket_read( conn_t *conn, char *buf, int len )
socket_read( conn_t *conn, char *buf, uint len )
{
int n = conn->bytes;
uint n = conn->bytes;
if (!n && conn->state == SCK_EOF)
return -1;
if (n > len)
@ -732,14 +823,14 @@ socket_read( conn_t *conn, char *buf, int len )
conn->offset = 0;
else
conn->offset += n;
return n;
return (int)n;
}
char *
socket_read_line( conn_t *b )
{
char *p, *s;
int n;
uint n;
s = b->buf + b->offset;
p = memchr( s + b->scanoff, '\n', b->bytes - b->scanoff );
@ -751,9 +842,9 @@ socket_read_line( conn_t *b )
}
if (b->state == SCK_EOF)
return (void *)~0;
return 0;
return NULL;
}
n = p + 1 - s;
n = (uint)(p + 1 - s);
b->offset += n;
b->bytes -= n;
b->scanoff = 0;
@ -764,14 +855,14 @@ socket_read_line( conn_t *b )
}
static int
do_write( conn_t *sock, char *buf, int len )
do_write( conn_t *sock, char *buf, uint len )
{
int n;
assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL
if (sock->ssl)
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, len ) );
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, (int)len ) );
#endif
n = write( sock->fd, buf, len );
if (n < 0) {
@ -782,7 +873,7 @@ do_write( conn_t *sock, char *buf, int len )
n = 0;
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
} else if (n != len) {
} else if (n != (int)len) {
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
return n;
@ -807,12 +898,12 @@ do_queued_write( conn_t *conn )
return 0;
while ((bc = conn->write_buf)) {
int n, len = bc->len - conn->write_offset;
int n;
uint len = bc->len - conn->write_offset;
if ((n = do_write( conn, bc->data + conn->write_offset, len )) < 0)
return -1;
if (n != len) {
conn->write_offset += n;
conn->writing = 1;
if (n != (int)len) {
conn->write_offset += (uint)n;
return 0;
}
conn->write_offset = 0;
@ -822,7 +913,6 @@ do_queued_write( conn_t *conn )
if (conn->ssl && SSL_pending( conn->ssl ))
conf_wakeup( &conn->ssl_fake, 0 );
#endif
conn->writing = 0;
conn->write_callback( conn->callback_aux );
return -1;
}
@ -830,7 +920,7 @@ do_queued_write( conn_t *conn )
static void
do_append( conn_t *conn, buff_chunk_t *bc )
{
bc->next = 0;
bc->next = NULL;
conn->buffer_mem += bc->len;
*conn->write_buf_append = bc;
conn->write_buf_append = &bc->next;
@ -846,7 +936,7 @@ do_flush( conn_t *conn )
buff_chunk_t *bc = conn->append_buf;
#ifdef HAVE_LIBZ
if (conn->out_z) {
int buf_avail = conn->append_avail;
uint buf_avail = conn->append_avail;
if (!conn->z_written)
return;
do {
@ -867,10 +957,10 @@ do_flush( conn_t *conn )
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
abort();
}
bc->len = (char *)conn->out_z->next_out - bc->data;
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
if (bc->len) {
do_append( conn, bc );
bc = 0;
bc = NULL;
buf_avail = 0;
} else {
buf_avail = conn->out_z->avail_out;
@ -883,7 +973,7 @@ do_flush( conn_t *conn )
#endif
if (bc) {
do_append( conn, bc );
conn->append_buf = 0;
conn->append_buf = NULL;
#ifdef HAVE_LIBZ
conn->append_avail = 0;
#endif
@ -893,7 +983,8 @@ do_flush( conn_t *conn )
void
socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
{
int i, buf_avail, len, offset = 0, total = 0;
int i;
uint buf_avail, len, offset = 0, total = 0;
buff_chunk_t *bc;
for (i = 0; i < iovcnt; i++)
@ -937,7 +1028,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
abort();
}
bc->len = (char *)conn->out_z->next_out - bc->data;
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
buf_avail = conn->out_z->avail_out;
len -= conn->out_z->avail_in;
conn->z_written = 1;
@ -960,7 +1051,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
}
if (!buf_avail) {
do_append( conn, bc );
bc = 0;
bc = NULL;
break;
}
}
@ -1000,12 +1091,15 @@ socket_fd_cb( int events, void *aux )
if (events & POLLOUT)
conf_notifier( &conn->notify, POLLIN, 0 );
if (pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, conn->conf->timeout );
#ifdef HAVE_LIBSSL
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (conn->ssl) {
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (do_queued_write( conn ) < 0)
return;
socket_fill( conn );
@ -1027,7 +1121,7 @@ socket_fake_cb( void *aux )
/* Ensure that a pending write gets queued. */
do_flush( conn );
/* If no writes are ongoing, start writing now. */
if (!conn->writing)
if (!(notifier_config( &conn->notify ) & POLLOUT))
do_queued_write( conn );
}

View file

@ -31,12 +31,13 @@
#ifdef HAVE_LIBSSL
# include <openssl/ssl.h>
# include <openssl/x509.h>
enum {
SSLv3 = 2,
TLSv1 = 4,
TLSv1_1 = 8,
TLSv1_2 = 16
TLSv1_2 = 16,
TLSv1_3 = 32
};
#endif
@ -49,19 +50,20 @@ typedef struct {
char *cert_file;
char *client_certfile;
char *client_keyfile;
char *cipher_string;
char system_certs;
char ssl_versions;
/* these are actually variables and are leaked at the end */
char ssl_ctx_valid;
_STACK *trusted_certs;
STACK_OF(X509) *trusted_certs;
SSL_CTX *SSLContext;
#endif
} server_conf_t;
typedef struct buff_chunk {
struct buff_chunk *next;
int len;
uint len;
char data[1];
} buff_chunk_t;
@ -73,7 +75,7 @@ typedef struct {
#ifdef HAVE_IPV6
struct addrinfo *addrs, *curr_addr; /* needed during connect */
#else
char **curr_addr; /* needed during connect */
struct addr_info *addrs, *curr_addr; /* needed during connect */
#endif
char *name;
#ifdef HAVE_LIBSSL
@ -102,17 +104,16 @@ typedef struct {
/* writing */
buff_chunk_t *append_buf; /* accumulating buffer */
buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */
int writing;
#ifdef HAVE_LIBZ
int append_avail; /* space left in accumulating buffer */
uint append_avail; /* space left in accumulating buffer */
#endif
int write_offset; /* offset into buffer head */
int buffer_mem; /* memory currently occupied by buffers in the queue */
uint write_offset; /* offset into buffer head */
uint buffer_mem; /* memory currently occupied by buffers in the queue */
/* reading */
int offset; /* start of filled bytes in buffer */
int bytes; /* number of filled bytes in buffer */
int scanoff; /* offset to continue scanning for newline at, relative to 'offset' */
uint offset; /* start of filled bytes in buffer */
uint bytes; /* number of filled bytes in buffer */
uint scanoff; /* offset to continue scanning for newline at, relative to 'offset' */
char buf[100000];
#ifdef HAVE_LIBZ
char z_buf[100000];
@ -133,20 +134,20 @@ static INLINE void socket_init( conn_t *conn,
conn->write_callback = write_callback;
conn->callback_aux = aux;
conn->fd = -1;
conn->name = 0;
conn->name = NULL;
conn->write_buf_append = &conn->write_buf;
}
void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_deflate( conn_t *conn );
void socket_close( conn_t *sock );
void socket_expect_read( conn_t *sock, int expect );
int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
void socket_expect_activity( conn_t *sock, int expect );
int socket_read( conn_t *sock, char *buf, uint len ); /* never waits */
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
typedef struct {
char *buf;
int len;
uint len;
ownership_t takeOwn;
} conn_iovec_t;
void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt );

1189
src/sync.c

File diff suppressed because it is too large Load diff

View file

@ -25,8 +25,8 @@
#include "driver.h"
#define M 0 /* master */
#define S 1 /* slave */
#define F 0 // far side
#define N 1 // near side
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
@ -40,6 +40,7 @@
#define XOP_PULL (1<<9)
#define XOP_MASK_DIR (XOP_PUSH|XOP_PULL)
#define XOP_HAVE_TYPE (1<<10)
// The following must all have the same bit shift from the corresponding OP_* flags.
#define XOP_HAVE_EXPUNGE (1<<11)
#define XOP_HAVE_CREATE (1<<12)
#define XOP_HAVE_REMOVE (1<<13)
@ -52,7 +53,7 @@ typedef struct channel_conf {
char *sync_state;
string_list_t *patterns;
int ops[2];
uint max_messages; /* for slave only */
int max_messages; // For near side only.
signed char expire_unread;
char use_internal_date;
} channel_conf_t;
@ -67,11 +68,11 @@ extern channel_conf_t global_conf;
extern channel_conf_t *channels;
extern group_conf_t *groups;
extern const char *str_ms[2], *str_hl[2];
extern const char *str_fn[2], *str_hl[2];
#define SYNC_OK 0 /* assumed to be 0 */
#define SYNC_FAIL 1
#define SYNC_BAD(ms) (4<<(ms))
#define SYNC_BAD(fn) (4<<(fn))
#define SYNC_NOGOOD 16 /* internal */
#define SYNC_CANCELED 32 /* internal */
@ -80,7 +81,7 @@ extern const char *str_ms[2], *str_hl[2];
#define BOX_PRESENT 1
/* All passed pointers must stay alive until cb is called. */
void sync_boxes( store_t *ctx[], const char *names[], int present[], channel_conf_t *chan,
void sync_boxes( store_t *ctx[], const char * const names[], int present[], channel_conf_t *chan,
void (*cb)( int sts, void *aux ), void *aux );
#endif

View file

@ -23,9 +23,11 @@
#include "common.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
@ -42,7 +44,7 @@ flushn( void )
}
}
static void
static void ATTR_PRINTFLIKE(1, 0)
printn( const char *msg, va_list va )
{
if (*msg == '\v')
@ -149,25 +151,34 @@ error( const char *msg, ... )
}
void
sys_error( const char *msg, ... )
vsys_error( const char *msg, va_list va )
{
va_list va;
char buf[1024];
int errno_bak = errno;
flushn();
va_start( va, msg );
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
errno = errno_bak;
perror( buf );
}
void
add_string_list_n( string_list_t **list, const char *str, int len )
sys_error( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vsys_error( msg, va );
va_end( va );
}
void
add_string_list_n( string_list_t **list, const char *str, uint len )
{
string_list_t *elem;
elem = nfmalloc( sizeof(*elem) + len );
elem = nfmalloc( offsetof(string_list_t, string) + len + 1 );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len );
@ -203,7 +214,7 @@ vasprintf( char **strp, const char *fmt, va_list ap )
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
memcpy( *strp, tmp, (size_t)len + 1 );
return len;
}
#endif
@ -232,34 +243,32 @@ strnlen( const char *str, size_t maxlen )
#endif
int
starts_with( const char *str, int strl, const char *cmp, int cmpl )
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl >= cmpl) && !memcmp( str, cmp, cmpl );
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
starts_with_upper( const char *str, int strl, const char *cmp, int cmpl )
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
{
int i;
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
if (strl < cmpl)
if ((uint)strl < cmpl)
return 0;
for (i = 0; i < cmpl; i++)
for (uint i = 0; i < cmpl; i++)
if (str[i] != cmp[i] && toupper( str[i] ) != cmp[i])
return 0;
return 1;
}
int
equals( const char *str, int strl, const char *cmp, int cmpl )
equals( const char *str, int strl, const char *cmp, uint cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl == cmpl) && !memcmp( str, cmp, cmpl );
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
}
#ifndef HAVE_TIMEGM
@ -341,13 +350,13 @@ nfsnprintf( char *buf, int blen, const char *fmt, ... )
va_list va;
va_start( va, fmt );
if (blen <= 0 || (uint)(ret = vsnprintf( buf, blen, fmt, va )) >= (uint)blen)
if (blen <= 0 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen)
oob();
va_end( va );
return ret;
}
static void ATTR_NORETURN
void
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
@ -450,20 +459,20 @@ expand_strdup( const char *s )
if (*s == '~') {
s++;
if (!*s) {
p = 0;
p = NULL;
q = Home;
} else if (*s == '/') {
p = s;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = nfstrndup( s, (int)(p - s) );
r = nfstrndup( s, (size_t)(p - s) );
pw = getpwnam( r );
free( r );
} else
pw = getpwnam( s );
if (!pw)
return 0;
return NULL;
q = pw->pw_dir;
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
@ -474,10 +483,10 @@ expand_strdup( const char *s )
/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */
int
map_name( const char *arg, char **result, int reserve, const char *in, const char *out )
map_name( const char *arg, char **result, uint reserve, const char *in, const char *out )
{
char *p;
int i, l, ll, num, inl, outl;
uint i, l, ll, num, inl, outl;
assert( arg );
l = strlen( arg );
@ -491,7 +500,7 @@ map_name( const char *arg, char **result, int reserve, const char *in, const cha
}
assert( out );
outl = strlen( out );
if (equals( in, inl, out, outl ))
if (equals( in, (int)inl, out, outl ))
goto copy;
for (num = 0, i = 0; i < l; ) {
for (ll = 0; ll < inl; ll++)
@ -534,7 +543,10 @@ map_name( const char *arg, char **result, int reserve, const char *in, const cha
static int
compare_uints( const void *l, const void *r )
{
return *(uint *)l - *(uint *)r;
uint li = *(const uint *)l, ri = *(const uint *)r;
if (li != ri) // Can't subtract, the result might not fit into signed int.
return li > ri ? 1 : -1;
return 0;
}
void
@ -546,16 +558,16 @@ sort_uint_array( uint_array_t array )
int
find_uint_array( uint_array_t array, uint value )
{
int bot = 0, top = array.size - 1;
while (bot <= top) {
int i = (bot + top) / 2;
uint bot = 0, top = array.size;
while (bot < top) {
uint i = (bot + top) / 2;
uint elt = array.data[i];
if (elt == value)
return 1;
if (elt < value)
bot = i + 1;
else
top = i - 1;
top = i;
}
return 0;
}
@ -582,7 +594,7 @@ arc4_init( void )
close( fd );
for (i = 0; i < 256; i++)
rs.s[i] = i;
rs.s[i] = (uchar)i;
for (i = j = 0; i < 256; i++) {
si = rs.s[i];
j += si + dat[i & 127];
@ -614,13 +626,13 @@ static const uchar prime_deltas[] = {
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
};
int
bucketsForSize( int size )
uint
bucketsForSize( uint size )
{
int base = 4, bits = 2;
uint base = 4, bits = 2;
for (;;) {
int prime = base + prime_deltas[bits];
uint prime = base + prime_deltas[bits];
if (prime >= size)
return prime;
base <<= 1;
@ -648,14 +660,14 @@ list_unlink( list_head_t *head )
assert( head->prev->next == head);
head->next->prev = head->prev;
head->prev->next = head->next;
head->next = head->prev = 0;
head->next = head->prev = NULL;
}
static notifier_t *notifiers;
static int changed; /* Iterator may be invalid now. */
#ifdef HAVE_SYS_POLL_H
#ifdef HAVE_POLL_H
static struct pollfd *pollfds;
static int npolls, rpolls;
static uint npolls, rpolls;
#else
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@ -665,8 +677,8 @@ static int npolls, rpolls;
void
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
{
#ifdef HAVE_SYS_POLL_H
int idx = npolls++;
#ifdef HAVE_POLL_H
uint idx = npolls++;
if (rpolls < npolls) {
rpolls = npolls;
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
@ -685,31 +697,41 @@ init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
}
void
conf_notifier( notifier_t *sn, int and_events, int or_events )
conf_notifier( notifier_t *sn, short and_events, short or_events )
{
#ifdef HAVE_SYS_POLL_H
int idx = sn->index;
#ifdef HAVE_POLL_H
uint idx = sn->index;
pollfds[idx].events = (pollfds[idx].events & and_events) | or_events;
#else
sn->events = (sn->events & and_events) | or_events;
#endif
}
short
notifier_config( notifier_t *sn )
{
#ifdef HAVE_POLL_H
return pollfds[sn->index].events;
#else
return sn->events;
#endif
}
void
wipe_notifier( notifier_t *sn )
{
notifier_t **snp;
#ifdef HAVE_SYS_POLL_H
int idx;
#ifdef HAVE_POLL_H
uint idx;
#endif
for (snp = &notifiers; *snp != sn; snp = &(*snp)->next)
assert( *snp );
*snp = sn->next;
sn->next = 0;
sn->next = NULL;
changed = 1;
#ifdef HAVE_SYS_POLL_H
#ifdef HAVE_POLL_H
idx = sn->index;
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
for (sn = notifiers; sn; sn = sn->next) {
@ -722,7 +744,7 @@ wipe_notifier( notifier_t *sn )
static time_t
get_now( void )
{
return time( 0 );
return time( NULL );
}
static list_head_t timers = { &timers, &timers };
@ -732,7 +754,7 @@ init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
{
tmr->cb = cb;
tmr->aux = aux;
tmr->links.next = tmr->links.prev = 0;
tmr->links.next = tmr->links.prev = NULL;
}
void
@ -781,7 +803,7 @@ event_wait( void )
notifier_t *sn;
int m;
#ifdef HAVE_SYS_POLL_H
#ifdef HAVE_POLL_H
int timeout = -1;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
@ -803,7 +825,7 @@ event_wait( void )
break;
}
for (sn = notifiers; sn; sn = sn->next) {
int n = sn->index;
uint n = sn->index;
if ((m = pollfds[n].revents)) {
assert( !(m & POLLNVAL) );
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );