Compare commits
1061 commits
Author | SHA1 | Date | |
---|---|---|---|
79e19d3d15 | |||
e0690b07eb | |||
6faf91a806 | |||
|
bb5e98e9ec | ||
|
f2b1e80033 | ||
|
e686f88318 | ||
|
51673214ab | ||
|
127003ee37 | ||
|
92921b1d3b | ||
|
bc15e571b6 | ||
|
ba13362a52 | ||
|
463272eab8 | ||
|
87065c12b4 | ||
|
6e5dc6c2f2 | ||
|
7979782676 | ||
|
a846ab054d | ||
|
da65672f08 | ||
|
444601a1e0 | ||
|
ed3bfdac4a | ||
|
589d2ed428 | ||
|
a86e6f8c7c | ||
|
d8feb67dae | ||
|
4b185e35fe | ||
|
d55ced04ed | ||
|
594e60bd74 | ||
|
6796e041ae | ||
|
fe5d59f8e3 | ||
|
95a83c8220 | ||
|
8c86f34bf0 | ||
|
32392adbe3 | ||
|
9e3041de93 | ||
|
7a0ea1f15c | ||
|
062706fcbf | ||
|
e4eac03a9a | ||
|
c8b73acad2 | ||
|
fc300fd811 | ||
|
e67cf01eb8 | ||
|
c2e6e962b5 | ||
|
e295f483d9 | ||
|
6e56f39fa9 | ||
|
9fbf5c2f6c | ||
|
4423a932f3 | ||
|
be657530ee | ||
|
30af61fb24 | ||
|
c3d91ae1e8 | ||
|
bf66f210bd | ||
|
cd6f18fd2b | ||
|
ba7b634186 | ||
|
5b4766fbe4 | ||
|
4ad82686f2 | ||
|
dec4b36595 | ||
|
a9ce7be962 | ||
|
09341c10c5 | ||
|
217764bd35 | ||
|
ee39e684aa | ||
|
6463a72f12 | ||
|
23513564df | ||
|
42f165ecf7 | ||
|
f099141e42 | ||
|
ec47c90554 | ||
|
b37d6b1c00 | ||
|
c69718baab | ||
|
b148fd9e44 | ||
|
c83330ffe8 | ||
|
8457225a50 | ||
|
481c12a8b3 | ||
|
cfaa4848dd | ||
|
70bad66129 | ||
|
68a412115a | ||
|
0e5046e14a | ||
|
fd7b5659ab | ||
|
e9efc49b6c | ||
|
de6dc699c9 | ||
|
ca72383fe9 | ||
|
5d6741a9a8 | ||
|
8df1f5dd64 | ||
|
ceb09fcd44 | ||
|
4aaada18e9 | ||
|
395f802500 | ||
|
c8f402e43f | ||
|
b514d9ddbc | ||
|
d93726067d | ||
|
198ca65b6e | ||
|
03b15dbdd3 | ||
|
503478533c | ||
|
8acf56b311 | ||
|
47b477b3fb | ||
|
81c4bfeefa | ||
|
d4ead05a02 | ||
|
67ea5bea7f | ||
|
a5a8783ea3 | ||
|
b91dd5b3bc | ||
|
7af7354dbc | ||
|
6fd4e8de24 | ||
|
c391b06b07 | ||
|
6010fe104e | ||
|
a6bb26091a | ||
|
64e5f07ad3 | ||
|
e2d3b4d55b | ||
|
cc176df2c3 | ||
|
4d7e169e57 | ||
|
def22db096 | ||
|
5c2e8d3e14 | ||
|
71d7d3e6df | ||
|
df22514ced | ||
|
234becf530 | ||
|
d09f988c70 | ||
|
990cc112f1 | ||
|
e9407cc1f7 | ||
|
07cb422cbb | ||
|
25b1c2b9e7 | ||
|
abdca388f6 | ||
|
b677bfe7e5 | ||
|
841f07efd0 | ||
|
e7bc402d09 | ||
|
8a03651dd8 | ||
|
2117945838 | ||
|
c5d3565db5 | ||
|
2f3cb5f481 | ||
|
e334eb3580 | ||
|
01348f6f7c | ||
|
27a1935361 | ||
|
b885e0e03a | ||
|
85688d1c1a | ||
|
09d11245cd | ||
|
83adb9a39b | ||
|
c84235b068 | ||
|
38e7b4db22 | ||
|
1004756659 | ||
|
30e166aa18 | ||
|
406931bc45 | ||
|
6734b9ce7d | ||
|
f3629c69e1 | ||
|
b59ee239a4 | ||
|
50eab08509 | ||
|
d59392e901 | ||
|
ef2caa074e | ||
|
5fee222f84 | ||
|
c97e650c24 | ||
|
30261fe6f1 | ||
|
542e38dd49 | ||
|
c82397cf6e | ||
|
813ad67c56 | ||
|
3651c30296 | ||
|
09540b5648 | ||
|
cab14608ca | ||
|
80deabf520 | ||
|
96afe8d0c2 | ||
|
aff0c88a38 | ||
|
04fc586e75 | ||
|
94022a6752 | ||
|
dfa8c16f27 | ||
|
121448ceb9 | ||
|
bee4fc54e7 | ||
|
a33e44758b | ||
|
7d9d3e15f5 | ||
|
a2fe8c155a | ||
|
281a87ed89 | ||
|
48038fede1 | ||
|
93fb3c29c6 | ||
|
e565d08246 | ||
|
e8caaaaf65 | ||
|
462fed556a | ||
|
d0a8551703 | ||
|
6a874b5877 | ||
|
5f908b7672 | ||
|
91abf2b830 | ||
|
f63e4338e8 | ||
|
8959c6b791 | ||
|
36062c5220 | ||
|
a310e7e2ba | ||
|
7607e53d56 | ||
|
bf14798700 | ||
|
fbc432aace | ||
|
2e515bf842 | ||
|
702b6ec4a1 | ||
|
d61f462039 | ||
|
a8f6eebdd9 | ||
|
b72800944c | ||
|
95d18e2778 | ||
|
acfa3a2bbc | ||
|
f7cec3064d | ||
|
17babc1695 | ||
|
37feeddbfb | ||
|
f698f16967 | ||
|
470210fa86 | ||
|
8a40554f07 | ||
|
c4d7f0189c | ||
|
e71f0ccc2a | ||
|
f82c172d2b | ||
|
904858365d | ||
|
5072032939 | ||
|
0a5a847932 | ||
|
af1acdac97 | ||
|
c29eceaeed | ||
|
53e8e79488 | ||
|
c75001aa7d | ||
|
094af8720c | ||
|
5aab050198 | ||
|
c9cd1b59fb | ||
|
5d5a0461ce | ||
|
cbac8aa75c | ||
|
094bc883e8 | ||
|
aa617bb854 | ||
|
380ccdd43a | ||
|
cbc14ba032 | ||
|
0865977504 | ||
|
a5d4a0fe60 | ||
|
3a2e6b3793 | ||
|
d2e5134ebd | ||
|
1b354fa61a | ||
|
f2524d082b | ||
|
a9feea71fe | ||
|
33ee4a4ffe | ||
|
8bab938f69 | ||
|
d529bc3887 | ||
|
47bdbb4aab | ||
|
118fdc4f18 | ||
|
fed0dcc60e | ||
|
906dc989e4 | ||
|
ada0ae4b8e | ||
|
366ed7d762 | ||
|
56515abe94 | ||
|
4b37688062 | ||
|
1039ee25f7 | ||
|
bb632d1cd0 | ||
|
a0961d6505 | ||
|
bd5fb6fff3 | ||
|
4ffe149666 | ||
|
efd72b85cc | ||
|
7ce658d14c | ||
|
4cc5ad5a1a | ||
|
bbe4567bce | ||
|
48ad58b9a3 | ||
|
8d4918affd | ||
|
683e581340 | ||
|
74e9368121 | ||
|
6e32b88f3d | ||
|
8b7d3792e4 | ||
|
39247197f9 | ||
|
96b5ae8360 | ||
|
c886f71054 | ||
|
83ebe9022d | ||
|
d624c9af5d | ||
|
f46cf8c887 | ||
|
d54809e268 | ||
|
36666f7e52 | ||
|
677accfd84 | ||
|
990c8a1404 | ||
|
72be55b0e3 | ||
|
416ced25dd | ||
|
b9505301cc | ||
|
9eaa97923a | ||
|
2d1cfc2c7f | ||
|
763cb8341f | ||
|
28d52b2b18 | ||
|
0aa4c628df | ||
|
34993fbca6 | ||
|
887b2205ff | ||
|
5c2ce59217 | ||
|
7c466fc3e7 | ||
|
1ea2e69aa7 | ||
|
6705604c4a | ||
|
af4b8896f0 | ||
|
c3350753b0 | ||
|
1fdf793a3f | ||
|
1e939bafd8 | ||
|
d754608f55 | ||
|
f29bed526b | ||
|
991e809c38 | ||
|
2da7951fe0 | ||
|
373abcef02 | ||
|
ca43c57e85 | ||
|
b4d1a05365 | ||
|
8aed94420f | ||
|
71ced65fcc | ||
|
62808c9003 | ||
|
b45e711da5 | ||
|
f934e995d6 | ||
|
f62b3c7be9 | ||
|
3ebb066aba | ||
|
2457b2baa3 | ||
|
77acc26812 | ||
|
f9fe75602e | ||
|
ec4b21535f | ||
|
3d64f16702 | ||
|
1d3b36f89e | ||
|
3dffd68825 | ||
|
951b7e77f8 | ||
|
509a191474 | ||
|
3eea668052 | ||
|
357dd51405 | ||
|
67f4aeff1f | ||
|
0c36655201 | ||
|
1330f43034 | ||
|
879eb623be | ||
|
4db64967c9 | ||
|
3b615bba3c | ||
|
22145f6674 | ||
|
ab955ffe6b | ||
|
743968737c | ||
|
ef0e7fdd3e | ||
|
03e25db3b8 | ||
|
1a707ab156 | ||
|
1b235d3d46 | ||
|
fdb03b91f2 | ||
|
2f91e22371 | ||
|
bc51d0206a | ||
|
815822d81c | ||
|
2bba9b903c | ||
|
5b0c8cfa60 | ||
|
ae95490d52 | ||
|
7b567164ff | ||
|
0f24ca31b5 | ||
|
b2f6ef391b | ||
|
41308e4814 | ||
|
719d4a2437 | ||
|
2648ef578f | ||
|
46e792c3df | ||
|
7ddd8d1737 | ||
|
e054c575ea | ||
|
89dc7592ee | ||
|
cda596d530 | ||
|
8bd6eb433f | ||
|
9a0403f446 | ||
|
682a05a676 | ||
|
8979ebbdf2 | ||
|
167964933f | ||
|
57a0920fcb | ||
|
ccd1340bf4 | ||
|
570023c9a3 | ||
|
05e658bd49 | ||
|
17f3348ff1 | ||
|
72c2d695ac | ||
|
9d22641b62 | ||
|
246c417874 | ||
|
eb190d2bd5 | ||
|
1aaf713ffe | ||
|
a3b131b6e8 | ||
|
bcd43e2c66 | ||
|
4106de5c14 | ||
|
2013e50b1c | ||
|
02af3f4c73 | ||
|
6c08f568d0 | ||
|
2f7e60a3ed | ||
|
16aa17053d | ||
|
9ce90dfe01 | ||
|
b8d6d833c6 | ||
|
549e6739e8 | ||
|
064f579a92 | ||
|
da9adcc4bd | ||
|
3de60c8f5c | ||
|
d0494fef43 | ||
|
ea9f4f0b96 | ||
|
ef1f80abe3 | ||
|
79ef2ab360 | ||
|
825041fc8c | ||
|
a041766140 | ||
|
b85153f8eb | ||
|
1eb88d4fea | ||
|
5c4015aee5 | ||
|
e0171b71e7 | ||
|
ac7cd86c73 | ||
|
59ac6b3f20 | ||
|
98bd2b115d | ||
|
2d4ce72a8b | ||
|
41ed101224 | ||
|
83eaac8787 | ||
|
08dab9465b | ||
|
8dc776c528 | ||
|
0840026a4b | ||
|
bef8959815 | ||
|
774ca45f1b | ||
|
95276cd967 | ||
|
ba2b42ec9b | ||
|
138983c91e | ||
|
03a124051f | ||
|
0e1f8f9a3f | ||
|
8aa22a62e7 | ||
|
1de3ecd883 | ||
|
f361738ad2 | ||
|
05deb008db | ||
|
e00d0f1ac3 | ||
|
4d638c3cf2 | ||
|
d8225390fc | ||
|
bd0f3af578 | ||
|
06c1a43aa2 | ||
|
c333a36aee | ||
|
ee8b835c55 | ||
|
c0ba6f0395 | ||
|
4842f5148d | ||
|
1701e3d84f | ||
|
a8b26dc4ac | ||
|
4b31522fdf | ||
|
d4392c9220 | ||
|
9e15ab4a5a | ||
|
13c742529c | ||
|
c9b8cefc29 | ||
|
ef70bd4a40 | ||
|
f4240761f1 | ||
|
74c78c70b9 | ||
|
5f265ad7da | ||
|
4da89af7be | ||
|
f61efdbb9d | ||
|
2eece82276 | ||
|
4aad8c9e04 | ||
|
d9a983add6 | ||
|
a7eddc6ede | ||
|
7489ff8613 | ||
|
5f4e3b285e | ||
|
926788f3ae | ||
|
7b7304b625 | ||
|
f1809ddd2b | ||
|
f43617cd94 | ||
|
fb19d644f7 | ||
|
97a42cd825 | ||
|
5af1796777 | ||
|
9982e7bf08 | ||
|
00ebf45be2 | ||
|
bac2b00f1b | ||
|
6c959c3ee4 | ||
|
4f3ef54f3a | ||
|
139b90be29 | ||
|
f0b80e7d35 | ||
|
efb23ab96a | ||
|
3f629af07e | ||
|
a4e2f1a60d | ||
|
7b76d9ff7e | ||
|
1fd66195d8 | ||
|
f68e021b90 | ||
|
42cedc8f81 | ||
|
b730f66f7d | ||
|
9a4be0af5f | ||
|
2fa75cf159 | ||
|
958af473a0 | ||
|
9eba3d8cd9 | ||
|
3db3f4718e | ||
|
518b5630dc | ||
|
5dfca41422 | ||
|
6f7d416bb8 | ||
|
f377e7b696 | ||
|
85fd5ceb54 | ||
|
7ee0483436 | ||
|
eb1005151c | ||
|
360600b98d | ||
|
1217193fbb | ||
|
aba3524d9b | ||
|
7822bd8a91 | ||
|
7ce57b9c00 | ||
|
2745813367 | ||
|
aa4f7a7d00 | ||
|
3742fc475b | ||
|
2a3963af58 | ||
|
47897d2403 | ||
|
4f383a8074 | ||
|
003ddb2199 | ||
|
f385355bdb | ||
|
608834c6f1 | ||
|
6ad7371f46 | ||
|
de82023427 | ||
|
2a2c53ae43 | ||
|
313c9193f8 | ||
|
9a463768ea | ||
|
06c731cbf8 | ||
|
95db373e54 | ||
|
834a65d85c | ||
|
d9c78b7787 | ||
|
639c84ea28 | ||
|
2976459008 | ||
|
526231bc22 | ||
|
29b07ca7a6 | ||
|
060430b233 | ||
|
3d5539bb63 | ||
|
8513358e0a | ||
|
4ab12ae76e | ||
|
4d8575100d | ||
|
8844ff3063 | ||
|
532d964aea | ||
|
09db83809a | ||
|
2d4bc1e613 | ||
|
aa0118d047 | ||
|
c6ddad6ac4 | ||
|
d7d5fd20bc | ||
|
9932352df0 | ||
|
c5f2943ff6 | ||
|
31ba8375b0 | ||
|
ae49a37a3e | ||
|
fd872a7ff7 | ||
|
0dfbf6f6fb | ||
|
d34baeb886 | ||
|
df29c592d1 | ||
|
dec5f73f57 | ||
|
bee7ceb0fb | ||
|
19d86d2aa9 | ||
|
0edb606e0f | ||
|
183f256557 | ||
|
bf9d7c7695 | ||
|
f55f42bdfc | ||
|
3161540ab9 | ||
|
12be7dd1f3 | ||
|
1c758be695 | ||
|
f4a192f375 | ||
|
aee0fa3b68 | ||
|
6d2fd370a6 | ||
|
813b4942db | ||
|
f9386d0b83 | ||
|
760bfa2cc6 | ||
|
8b2bc912b4 | ||
|
4481702da3 | ||
|
4fa5779193 | ||
|
359091625d | ||
|
2bbd07ec87 | ||
|
5a21042e98 | ||
|
c47ee1c8c4 | ||
|
2f0fbcd306 | ||
|
03b3b566f1 | ||
|
92b892d247 | ||
|
27fa63a577 | ||
|
0b32734693 | ||
|
2cb483fb2e | ||
|
0ad8ef80b2 | ||
|
c293acaf24 | ||
|
cf0f32f800 | ||
|
6c6ad9710c | ||
|
decc33c2cf | ||
|
f485d69332 | ||
|
c6f08b8f17 | ||
|
540adbb8fd | ||
|
b6949c64d2 | ||
|
71524cb6b0 | ||
|
29a56e2dc4 | ||
|
8d5bd62537 | ||
|
f586c0bee5 | ||
|
c0ba0c7ecf | ||
|
1e427f5cd5 | ||
|
49a32910a7 | ||
|
fe3d19b7eb | ||
|
b1842617f7 | ||
|
d3f6347021 | ||
|
391ec01f28 | ||
|
7f784fd235 | ||
|
8b76412b0d | ||
|
ecb4c7ab07 | ||
|
273ac899f3 | ||
|
12676f28da | ||
|
9a62521cff | ||
|
014d9b9081 | ||
|
83b834cdfd | ||
|
9e186ae88b | ||
|
15216947fb | ||
|
6b7b2b1106 | ||
|
f1eea7d9a5 | ||
|
48754ecc74 | ||
|
83bb1cf716 | ||
|
f044adbfa4 | ||
|
5297425918 | ||
|
3d81ccbf21 | ||
|
3dcb393de2 | ||
|
3814f19661 | ||
|
e63e16ab45 | ||
|
8e49300cf7 | ||
|
62a60997c3 | ||
|
55e65147df | ||
|
a49893f32e | ||
|
2b27216b86 | ||
|
945e05cfdd | ||
|
32def5dc0a | ||
|
a9a331c98a | ||
|
03f8bfdfb2 | ||
|
00076a6971 | ||
|
61ef099cd5 | ||
|
080740f867 | ||
|
b10fd0c21c | ||
|
a893cba483 | ||
|
9a0e65f899 | ||
|
0a684bd933 | ||
|
394aca03a2 | ||
|
07377cb753 | ||
|
100f9487f4 | ||
|
2568459a7b | ||
|
b570c17766 | ||
|
17c4748dfa | ||
|
2213d6976c | ||
|
c0bf867669 | ||
|
3ceb553102 | ||
|
4a39cae8c4 | ||
|
0b59ee0df3 | ||
|
1b67c49965 | ||
|
eb1f10762f | ||
|
6577bf3e61 | ||
|
1847a4e12d | ||
|
6dfccb76a5 | ||
|
a0dc37339e | ||
|
fc77feacc5 | ||
|
bf049d6466 | ||
|
5ad83b4e6a | ||
|
e4243debb6 | ||
|
ca3a319e60 | ||
|
01358ec8b4 | ||
|
406e967430 | ||
|
e7c96f8891 | ||
|
daaf950878 | ||
|
167de3e438 | ||
|
842aa402c3 | ||
|
e07de2a336 | ||
|
aad7f903ec | ||
|
ff9bf4d91b | ||
|
10a146e1b9 | ||
|
89c81e382e | ||
|
4ab55dc468 | ||
|
9261897629 | ||
|
96be183acb | ||
|
15d57b95b7 | ||
|
7ba7be111e | ||
|
2ef6dc8a90 | ||
|
37a28d8133 | ||
|
d1900941f4 | ||
|
dead12efdd | ||
|
b142778e56 | ||
|
da5ce5d8f4 | ||
|
312f4be4b2 | ||
|
89add4f330 | ||
|
fbfcfea5dc | ||
|
3363ad0f11 | ||
|
a66034b23a | ||
|
acb1c870b4 | ||
|
d00d38836d | ||
|
2adebaa3b1 | ||
|
8310cf78ac | ||
|
fbba8f1cda | ||
|
c7ebe2da95 | ||
|
8dbb3fe7a9 | ||
|
d7eae525bd | ||
|
c23d251092 | ||
|
66895f9cce | ||
|
35851f133b | ||
|
49223b2df2 | ||
|
a326bf2f58 | ||
|
df6c3b64b7 | ||
|
bbf98bb165 | ||
|
18936f6696 | ||
|
16e5aade3f | ||
|
725a122e91 | ||
|
2e07e68630 | ||
|
47fe4b7998 | ||
|
0a8f19294c | ||
|
6f2160f136 | ||
|
f11504aa07 | ||
|
d4c786823d | ||
|
4e849196b8 | ||
|
87cb946eda | ||
|
c43fc90dcf | ||
|
6d49c343fc | ||
|
18225344c6 | ||
|
dfd7516b9a | ||
|
2585dd3324 | ||
|
4f94197e41 | ||
|
8121224744 | ||
|
a3f66f8f1d | ||
|
343f16771a | ||
|
28cccf4b35 | ||
|
1bc9c6d9cf | ||
|
40f2812a41 | ||
|
9bbb02b8fd | ||
|
233f563569 | ||
|
3386285205 | ||
|
a3bd10c04d | ||
|
04ca97920d | ||
|
e71ad53b7f | ||
|
9c86ec3442 | ||
|
b4cef554fc | ||
|
06b303da88 | ||
|
7c815538ab | ||
|
2aae866e80 | ||
|
6b3b6f12bb | ||
|
d2bed4990d | ||
|
dee9f51096 | ||
|
ae85e455d3 | ||
|
256a147945 | ||
|
96eaeb428d | ||
|
faeb9b5bf7 | ||
|
f5086f735c | ||
|
bd93d689db | ||
|
7867eb9009 | ||
|
a55354516b | ||
|
802c99edcf | ||
|
f1df2f40d1 | ||
|
886cd03e37 | ||
|
f8d73ac346 | ||
|
8a72d204c9 | ||
|
a85013d6ff | ||
|
a266f28f1c | ||
|
171f7d6cd3 | ||
|
3447694c2b | ||
|
ef41349035 | ||
|
d1ee94f02c | ||
|
584e51ed7d | ||
|
2ab689b3df | ||
|
e5d323cc47 | ||
|
3169c59e10 | ||
|
05fd0b9970 | ||
|
c741d5ffb5 | ||
|
7addc3bea8 | ||
|
424e0e7221 | ||
|
ea951a697f | ||
|
ec8f440383 | ||
|
b0bbd23512 | ||
|
6d86e5347e | ||
|
9554026443 | ||
|
57444e9df9 | ||
|
06ccac1fdd | ||
|
121ce76e46 | ||
|
d2e13f147c | ||
|
4d4de6e275 | ||
|
fd229040d8 | ||
|
17dc64b414 | ||
|
83efbe327d | ||
|
99cc328f17 | ||
|
1545ed90a0 | ||
|
c66afdc0a8 | ||
|
7bab2d6d94 | ||
|
f6a25b331f | ||
|
61d98c5a1d | ||
|
4afd31a457 | ||
|
cf6a7b4d18 | ||
|
7e1c16ae02 | ||
|
058d01f179 | ||
|
9e10e871fd | ||
|
2c729bf9e6 | ||
|
296ac0364c | ||
|
8df1ebaf40 | ||
|
39006d7f24 | ||
|
d637772339 | ||
|
e1fa867423 | ||
|
5ade279839 | ||
|
70e87eb99e | ||
|
90a38ea810 | ||
|
72fd2aafb7 | ||
|
122e09fe60 | ||
|
db2bbbfef8 | ||
|
516c3bfa99 | ||
|
da39690aec | ||
|
a8b4de463e | ||
|
5bc3bf5fbd | ||
|
7728278b9c | ||
|
4729b1ee23 | ||
|
474ce08b3a | ||
|
2a5ff54683 | ||
|
022d137b8c | ||
|
e6a356ffc7 | ||
|
d94dadbaeb | ||
|
09dfddb36b | ||
|
ce45692ca5 | ||
|
92914b37cc | ||
|
0d8bce1675 | ||
|
262999d092 | ||
|
06521da30d | ||
|
89519e343c | ||
|
f5f7dfb866 | ||
|
474923bc6b | ||
|
71fce2a622 | ||
|
2f62a7f608 | ||
|
a365e20660 | ||
|
1f7b81fb8b | ||
|
2b37288e8d | ||
|
5b857b3b19 | ||
|
42ca262e39 | ||
|
625f592fb7 | ||
|
023d3ee577 | ||
|
9056504483 | ||
|
6800f1636e | ||
|
e0d72cd5e3 | ||
|
6985da5848 | ||
|
0e8a8d120d | ||
|
2a9b0bd763 | ||
|
9b657a46a0 | ||
|
617d1a6e49 | ||
|
1b9f8b4c69 | ||
|
d2463a4cd8 | ||
|
9b7c09e4b6 | ||
|
aea4be19e3 | ||
|
4bf58c3e97 | ||
|
c8275e2aa7 | ||
|
dbbab78881 | ||
|
67b714ee0e | ||
|
f6f2d2461c | ||
|
21abb22c98 | ||
|
8a748d046d | ||
|
168e5f3282 | ||
|
bdcc285403 | ||
|
16eaf903db | ||
|
c7903f8003 | ||
|
b5d70aa596 | ||
|
f90b290650 | ||
|
72a2d4b690 | ||
|
b7389cb36f | ||
|
340bfcc4a8 | ||
|
3e3cf3ac9a | ||
|
7f9ece8e7e | ||
|
47e592b603 | ||
|
861dd7468e | ||
|
492ca8d332 | ||
|
31fc41a32c | ||
|
d7126dca5e | ||
|
bb7bbcf5b1 | ||
|
fbbb86738b | ||
|
bc39f10a1e | ||
|
7726ce3e0f | ||
|
5224b5bc9f | ||
|
630a04ad3e | ||
|
d7b8621f05 | ||
|
1453e61840 | ||
|
4e8fabf7e5 | ||
|
8174bdcbd4 | ||
|
d76c827a45 | ||
|
e567cc6919 | ||
|
850addecd5 | ||
|
19128f1587 | ||
|
ab11737b33 | ||
|
bbc0a877c8 | ||
|
58db1d05ac | ||
|
905ded175f | ||
|
8728dfdf21 | ||
|
5e01034aee | ||
|
d1c4f8a069 | ||
|
97f48f56ed | ||
|
1a536a3415 | ||
|
40fc6a6ac8 | ||
|
e205eb62f5 | ||
|
c7d938f965 | ||
|
2277ecefb6 | ||
|
24910e2cdf | ||
|
a41ea8f9f1 | ||
|
c1c7cb6d8e | ||
|
635b2d7b76 | ||
|
9c6c158ef3 | ||
|
a1a5a817bb | ||
|
185769640b | ||
|
f4ce961bab | ||
|
a1c402678c | ||
|
4e983506d3 | ||
|
1a6ee00d86 | ||
|
d414d0aae2 | ||
|
8b6ac97fe4 | ||
|
3c8ee66bfc | ||
|
f6ed69a8d3 | ||
|
2fa54425e7 | ||
|
76de0182a2 | ||
|
8c30ec4a25 | ||
|
61dfbea617 | ||
|
808001c0a9 | ||
|
4ec56f8cf6 | ||
|
f070f3cd72 | ||
|
d68dd7369e | ||
|
549c1cf13e | ||
|
716ff82540 | ||
|
ad5f5aa2b2 | ||
|
ab898f2f5c | ||
|
d3faf0d27f | ||
|
924e1a7f04 | ||
|
963f607c81 | ||
|
4dc23fee7b | ||
|
9740e7e852 | ||
|
d5a1f5876d | ||
|
044d8dfb73 | ||
|
5633236ed2 | ||
|
8db5ec1e5e | ||
|
6d229848a5 | ||
|
633869d040 | ||
|
25db6a3c31 | ||
|
7710b3dcb5 | ||
|
bf26a663da | ||
|
d2753c4188 | ||
|
1a77d2e650 | ||
|
bf4266eadc | ||
|
7e31f95470 | ||
|
7f26ca3c0a | ||
|
02babb0651 | ||
|
c2209c8470 | ||
|
556dfabc38 | ||
|
e8c769ea76 | ||
|
e75ebf75f6 | ||
|
6741bc94d9 | ||
|
f7406f530b | ||
|
3f8e820acb | ||
|
e0cc45044f | ||
|
7e323da056 | ||
|
ca9f751ebd | ||
|
16de402c9e | ||
|
efa062ccdb | ||
|
99b1cbf279 | ||
|
f5eeecaf45 | ||
|
f34cc9d917 | ||
|
c37f908d61 | ||
|
eb0bbf8b84 | ||
|
af0b75a5dc | ||
|
86d9a3140d | ||
|
acabdc43b1 | ||
|
1b75236135 | ||
|
5f46b66547 | ||
|
b8b4628197 | ||
|
918e105b0c | ||
|
789d5864ca | ||
|
7ff8eef06c | ||
|
2e8181a8d8 | ||
|
af8c4396fe | ||
|
130664b622 | ||
|
8d1b26aebe | ||
|
5bbe51ee46 | ||
|
851d12411d | ||
|
f9eaa549bd | ||
|
3e8673d71f | ||
|
e5894bdf04 | ||
|
a52fd7dde0 | ||
|
3e392b6aa2 | ||
|
c2c4490431 | ||
|
0e8455fde1 | ||
|
a7775798e3 | ||
|
f80f91d98b | ||
|
9e61cb1d69 | ||
|
fc9bd1b874 | ||
|
1eb6e35c80 | ||
|
3b91742ead | ||
|
b5307f0e16 | ||
|
8c5a1d94de | ||
|
27ea308bfb | ||
|
b92ad46b08 | ||
|
2a9f38f1c1 | ||
|
f63dfbd301 | ||
|
3d0d729ea9 | ||
|
9e8e31c1c2 | ||
|
e7a1647a30 | ||
|
4aae81e8d8 | ||
|
75bffd0947 | ||
|
4086e9d4fb | ||
|
089f7c01b8 | ||
|
c95d90bf58 | ||
|
7fe9fb7ef0 | ||
|
e1d0ea8a18 | ||
|
b239e7b814 | ||
|
0121220339 | ||
|
180f5fa201 | ||
|
052195c982 | ||
|
0b1f1bcd4d | ||
|
eca605d105 | ||
|
c96163a74b | ||
|
510c9ca492 | ||
|
50aa8dfc96 | ||
|
75b6b9c715 | ||
|
4d5a957e20 | ||
|
016d6d64e6 | ||
|
0dc9106dfa | ||
|
3de16206e7 | ||
|
24c57a244e | ||
|
4e5bfd4629 | ||
|
5a44a33ffc | ||
|
267355b3b6 | ||
|
52fb7c085d | ||
|
424db34ff2 | ||
|
6ed39d01d2 | ||
|
c34a2191ec | ||
|
8859fd45a8 | ||
|
228ba6b48b | ||
|
4fba9c8b74 | ||
|
b823fb3dbc | ||
|
6b580ac0ad | ||
|
dae086c92f | ||
|
5ace6fc45e | ||
|
4390613859 | ||
|
b535af4fc4 | ||
|
ff8991fd00 | ||
|
2c472c3999 | ||
|
2cde99958b | ||
|
b66e3c370e | ||
|
c59a017c62 | ||
|
28e240a36b | ||
|
d2b39d2c89 | ||
|
a1594ea19d | ||
|
958e74dfc8 | ||
|
38d46cc03e | ||
|
feee93c26d | ||
|
b3838ade09 | ||
|
8fa6cd3346 | ||
|
e68fc4b463 | ||
|
6f2550117a | ||
|
f37f4c29f6 | ||
|
4146c05c87 | ||
|
a4f2725bad | ||
|
2012aadc5b | ||
|
3327572fd2 | ||
|
39446bbef9 | ||
|
095e3ec92c | ||
|
d850150719 | ||
|
b10718cf20 | ||
|
d228fae394 | ||
|
7cd74a1179 | ||
|
0bd3709a9d | ||
|
52a721187f | ||
|
0162c9f5b8 | ||
|
d6fd1939ba | ||
|
eb5f92821c | ||
|
1a1d2af012 | ||
|
85b5c5b8c9 | ||
|
dd1b19a99a | ||
|
54d8140f6e | ||
|
418b5e9a7a | ||
|
f6c037c854 | ||
|
7741548ff8 | ||
|
fe438026b0 | ||
|
6267139b71 | ||
|
44d360d184 | ||
|
d00a65bebd | ||
|
dd22bd3f22 | ||
|
2f2b123d71 | ||
|
8c8f6916c6 | ||
|
d6fe5a92bd | ||
|
cb0d4b54b3 | ||
|
c121ec912f | ||
|
2c648da5cf | ||
|
3fe6f3f086 | ||
|
f0c7fdf008 | ||
|
de1f9e1941 | ||
|
e02975e888 | ||
|
06a5edb452 | ||
|
2881d173b5 | ||
|
bb62e2c18d | ||
|
49b36e7869 | ||
|
087fb470e7 | ||
|
c84a888a7d | ||
|
9647c79504 | ||
|
e3797d65da | ||
|
8ca8c2b289 | ||
|
c4050700c0 | ||
|
608386f918 | ||
|
cb2f6e3ee6 | ||
|
bfffeffff6 | ||
|
e015398ff2 | ||
|
1efcad03f8 | ||
|
6f647ae37b | ||
|
4b102ee6ec | ||
|
e2f1cd779b | ||
|
891fab1a7d | ||
|
0f7823a4bf | ||
|
5eaa4e5512 | ||
|
ea361ad116 | ||
|
8af21c5604 | ||
|
9a5b57eb7d | ||
|
af941779f4 | ||
|
f843ffcdd4 | ||
|
216c26cceb | ||
|
1b97128b47 | ||
|
1db31aabd7 | ||
|
6dba87f0ec | ||
|
77358f1706 | ||
|
91d2f60584 | ||
|
7173d07192 | ||
|
b3672634e5 | ||
|
bcecbe5eeb | ||
|
8944538399 | ||
|
32677da976 | ||
|
ee0de20cf7 |
56 changed files with 17178 additions and 2984 deletions
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/.autoconf_trace
|
||||
/ChangeLog
|
||||
/INSTALL
|
||||
/autom4te.cache/
|
||||
/aclocal.m4
|
||||
/autodefs.h
|
||||
/autodefs.h.in
|
||||
/autodefs.h.in~
|
||||
/build-stamp
|
||||
/compile
|
||||
/config.cache
|
||||
/config.guess
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/configure
|
||||
/configure.lineno
|
||||
/configure-stamp
|
||||
/cov-int/
|
||||
/depcomp
|
||||
/install-sh
|
||||
/isync.spec
|
||||
/isync-*.tar.gz
|
||||
/isync-*.tar.gz.asc
|
||||
/missing
|
||||
/patch-stamp
|
||||
/stamp-h
|
||||
/stamp-h.in
|
||||
/stamp-h1
|
||||
|
||||
Makefile
|
||||
Makefile.in
|
18
AUTHORS
18
AUTHORS
|
@ -1 +1,17 @@
|
|||
Michael R. Elkins <me@mutt.org>
|
||||
Oswald Buddenhagen <ossi@users.sf.net>
|
||||
* Contributor, current maintainer
|
||||
|
||||
Theodore Ts'o <tytso@mit.edu>
|
||||
* Contributor, Debian package co-maintainer
|
||||
|
||||
Nicolas Boullis <nboullis@debian.org>
|
||||
* Debian package maintainer and minor upstream contributions
|
||||
|
||||
Michael Elkins <me@mutt.org>
|
||||
* Original author
|
||||
|
||||
Send questions and bug reports to the isync-devel@lists.sourceforge.net
|
||||
mailing list.
|
||||
|
||||
_DON'T_ report bugs to Michael, not even in a CC: - he is not actively
|
||||
involved in isync development any more.
|
||||
|
|
158
ChangeLog
158
ChangeLog
|
@ -1,158 +0,0 @@
|
|||
2000-12-31 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* sync.c:
|
||||
display how many messages were fetched from the server
|
||||
|
||||
* imap.c:
|
||||
fixed compilation error with no libssl support ("lorenzo martignoni"
|
||||
<lorenzo.martignoni@technologist.com>)
|
||||
|
||||
2000-12-28 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* main.c:
|
||||
fixed config parser to accept arbitrary whitespace
|
||||
|
||||
2000-12-27 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* imap.c:
|
||||
use imap_close to terminate a connection in imap_open()
|
||||
|
||||
* imap.c, isync.1, isync.h, maildir.c, main.c:
|
||||
allow leading whitespace in config files
|
||||
|
||||
now possible to sync multiple mailboxes by specifying multiple aliases on
|
||||
the command line. IMAP connections are reused if possible.
|
||||
|
||||
don't initialize ssl unless we are going to use it.
|
||||
|
||||
2000-12-23 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* imap.c, isync.h:
|
||||
don't use NAMESPACE unless the server supports it
|
||||
|
||||
* Makefile.am, README, cram.c, imap.c, isync.h:
|
||||
added CRAM-MD5 authentication support.
|
||||
|
||||
parse server capability string to determine if STARTTLS is available
|
||||
|
||||
2000-12-22 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* README, imap.c, isync.1, isync.h, main.c:
|
||||
isync-brokenservers.diff (Jeremy Katz <katzj@linuxpower.org>)
|
||||
adds support for disabling NAMESPACE, and disable various flavors of TLS/SSL
|
||||
for use with some broken IMAP servers.
|
||||
|
||||
* imap.c, sync.c:
|
||||
prompt user if they wish to continue if the server's X.509 certificate can't
|
||||
be verified.
|
||||
|
||||
sync_mailbox should consider uid == 0 to be "unknown"
|
||||
|
||||
* main.c, sync.c:
|
||||
fixed sync_mailbox() to correctly write new messages to the local maildir
|
||||
box (Thomas Roessler <roessler@does-not-exist.org>)
|
||||
|
||||
* main.c: set default MaxSize to 0 (unlimited)
|
||||
|
||||
invert test for password being set after getpass() call (Magnus Jonsson
|
||||
<bigfoot@acc.umu.se>)
|
||||
|
||||
* ChangeLog, NEWS, configure.in, imap.c, isync.1, isync.h, maildir.c, main.c, sample.isyncrc, sync.c:
|
||||
added MaxSize configuration variable
|
||||
|
||||
fixed --fast to work robustly without relying on the \Recent flag in
|
||||
messages
|
||||
|
||||
2000-12-21 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* imap.c, isync.h, maildir.c, sync.c:
|
||||
RFC822.PEEK is obsolete in RFC2060. Use BODY.PEEK[] instead, which does
|
||||
the same thing
|
||||
|
||||
keep track of the uidvalidity so isync can detect if the mailbox on the
|
||||
server has changed since the last sync.
|
||||
|
||||
* NEWS: updated NEWS for 0.3 release
|
||||
|
||||
* Makefile.am, isync.spec:
|
||||
added support for building RPMS
|
||||
|
||||
* Makefile.am, isync.1:
|
||||
added target for creating html version of the man page
|
||||
|
||||
documented the imaps: prefix to the Host command
|
||||
|
||||
* imap.c, sync.c:
|
||||
can't assume flag order when fetching a message. just search for the
|
||||
first `{' to find the message size.
|
||||
|
||||
* isync.1, sync.c:
|
||||
added BUGS section to manpage detailing the fact that we break the
|
||||
maildir(5) spec by parsing the filename
|
||||
|
||||
change message delivery to use the method described in maildir(5)
|
||||
|
||||
* configure.in, main.c, sync.c:
|
||||
use getpass() to get the user's password
|
||||
|
||||
unlink the temp file if we are unable to fetch a new message from the
|
||||
server.
|
||||
|
||||
update version to 0.3
|
||||
|
||||
* isync.1: fixed typo in man page for --verbose option
|
||||
|
||||
* Makefile.am, README, TODO, imap.c, isync.h, list.c:
|
||||
added generic IMAP list parser and rewrote imap_exec() to handle
|
||||
arbitrary data instead of hardcoded
|
||||
|
||||
* Makefile.am, README, configure.in, main.c:
|
||||
fixes to compile cleanly under Solaris 2.7
|
||||
|
||||
* configure.in, imap.c, isync.1, isync.h, main.c:
|
||||
added OpenSSL support
|
||||
|
||||
* ChangeLog, configure.in, main.c:
|
||||
config options were not case insensitive
|
||||
|
||||
* imap.c, isync.h, maildir.c, main.c, sync.c:
|
||||
don't fetch deleted messages when expunging
|
||||
|
||||
display number of messages that are to be deleted
|
||||
|
||||
flags for \Recent messages were not properly fetched
|
||||
|
||||
local messages with updated flags were not corrected renamed
|
||||
|
||||
2000-12-20 Michael Elkins <me@sigipe.org>
|
||||
|
||||
* ChangeLog, Makefile.am:
|
||||
updated ChangeLog
|
||||
|
||||
added log: rule in Makefile.am
|
||||
|
||||
* configure: forgot to remove configure script
|
||||
|
||||
* INSTALL, Makefile.in, aclocal.m4, autogen.sh, install-sh, missing, mkinstalldirs:
|
||||
added autogen.sh to regenerate the build environment
|
||||
|
||||
* COPYING, INSTALL, install-sh, missing, mkinstalldirs:
|
||||
added missing files
|
||||
|
||||
* isync.1, sample.isyncrc: New file.
|
||||
|
||||
* isync.1, sample.isyncrc:
|
||||
initial import
|
||||
|
||||
* TODO, configure, imap.c, maildir.c, sync.c:
|
||||
New file.
|
||||
|
||||
* TODO, configure, imap.c, maildir.c, sync.c:
|
||||
initial import
|
||||
|
||||
* AUTHORS, ChangeLog, INSTALL, Makefile.am, Makefile.in, NEWS, README, aclocal.m4, configure.in, isync.h, main.c:
|
||||
New file.
|
||||
|
||||
* AUTHORS, ChangeLog, INSTALL, Makefile.am, Makefile.in, NEWS, README, aclocal.m4, configure.in, isync.h, main.c:
|
||||
initial import
|
||||
|
41
Dockerfile
Normal file
41
Dockerfile
Normal 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
|
88
Makefile.am
88
Makefile.am
|
@ -1,16 +1,80 @@
|
|||
bin_PROGRAMS=isync
|
||||
isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c cram.c
|
||||
man_MANS=isync.1
|
||||
EXTRA_DIST=sample.isyncrc $(man_MANS)
|
||||
CPPFLAGS=$(RPM_OPT_FLAGS)
|
||||
SUBDIRS = src
|
||||
bin_SCRIPTS = mbsync-get-cert
|
||||
EXTRA_DIST = debian isync.spec $(bin_SCRIPTS)
|
||||
|
||||
LOG_PL = \
|
||||
use POSIX qw(strftime); \
|
||||
use Date::Parse; \
|
||||
use Text::Wrap; \
|
||||
$$Text::Wrap::columns = 72; \
|
||||
while (defined($$_ = <>)) { \
|
||||
/^commit / or die "commit missing: $$_"; \
|
||||
<> =~ /^log size (\d+)$$/ or die "wrong size"; \
|
||||
$$len = $$1; \
|
||||
read(STDIN, $$log, $$len) == $$len or die "unexpected EOF"; \
|
||||
$$log =~ s/^Author: ([^>]+>)\nDate: (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d [-+]\d{4})\n(.*)$$/$$3/s or die "unexpected log format"; \
|
||||
$$author = $$1; $$date = str2time($$2); \
|
||||
scalar(<>); \
|
||||
@files = (); \
|
||||
$$pfx = ""; \
|
||||
while (defined($$l = <>) and $$l ne "\n") { \
|
||||
chomp $$l; \
|
||||
next if ($$l =~ m,^(ChangeLog$$|NEWS$$|TODO$$|debian/),); \
|
||||
if (!@files) { \
|
||||
$$pfx = $$l; \
|
||||
$$pfx =~ s,/?[^/]+$$,,; \
|
||||
} else { \
|
||||
while (length($$pfx)) { \
|
||||
$$l =~ m,^\Q$$pfx/\E, and last; \
|
||||
$$pfx =~ s,/?[^/]+$$,,; \
|
||||
} \
|
||||
} \
|
||||
push @files, $$l; \
|
||||
} \
|
||||
next if (!@files); \
|
||||
print strftime("%F %H:%M", gmtime($$date))." ".$$author."\n\n"; \
|
||||
if (@files > 1 and ($$len = length($$pfx))) { \
|
||||
@efiles = (); \
|
||||
for $$f (@files) { push @efiles, substr($$f, $$len + 1); } \
|
||||
$$fstr = $$pfx."/: "; \
|
||||
} else { \
|
||||
@efiles = @files; \
|
||||
$$fstr = ""; \
|
||||
} \
|
||||
print wrap("\t* ", "\t ", $$fstr.join(", ", @efiles).":")."\n"; \
|
||||
$$log =~ s, +$$,,gm; \
|
||||
$$log =~ s,^ ,\t,gm; \
|
||||
print $$log."\n"; \
|
||||
}
|
||||
|
||||
$(srcdir)/.git/index:
|
||||
$(srcdir)/ChangeLog: $(srcdir)/.git/index
|
||||
$(MAKE) log
|
||||
|
||||
log:
|
||||
rcs2log -h sigipe.org | sed 's;/home/cvs/isync/;;g' > ChangeLog
|
||||
@test -z "$(srcdir)" || cd $(srcdir) && \
|
||||
( ! test -d .git || \
|
||||
git log --pretty=medium --date=iso --log-size --name-only --no-merges | \
|
||||
perl -e '$(LOG_PL)' > ChangeLog )
|
||||
|
||||
isync.html: isync.1
|
||||
groff -Thtml -man isync.1 > isync.html
|
||||
cov-scan: clean
|
||||
/opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE)
|
||||
tar cavf isync-cov.tar.xz cov-int
|
||||
|
||||
rpm:
|
||||
make dist
|
||||
cp isync-$(VERSION).tar.gz /usr/src/RPM/SOURCES
|
||||
rpm -ba --target=i586 --clean isync.spec
|
||||
deb:
|
||||
CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign
|
||||
|
||||
dist-hook:
|
||||
find $(distdir)/debian \( -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf
|
||||
-cd $(distdir)/debian && test -f .gitignore && rm -rf `cut -c2- .gitignore` .gitignore
|
||||
|
||||
dist-sign: dist
|
||||
gpg -b -a $(PACKAGE)-$(VERSION).tar.gz
|
||||
|
||||
rpm: dist
|
||||
CFLAGS="-O2 -mtune=core2" rpmbuild --clean -ta $(PACKAGE)-$(VERSION).tar.gz
|
||||
|
||||
rpm-ia32: dist
|
||||
CFLAGS="-O2 -m32 -march=i686" rpmbuild --target i686-unknown-linux --clean -ta $(PACKAGE)-$(VERSION).tar.gz
|
||||
|
||||
doc_DATA = README TODO NEWS ChangeLog AUTHORS
|
||||
|
|
162
NEWS
162
NEWS
|
@ -1,3 +1,165 @@
|
|||
[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.
|
||||
|
||||
Support for proper Maildir++ and a Maildir sub-folder naming style
|
||||
without extra dots have been added.
|
||||
|
||||
Support for TLS client certificates was added.
|
||||
|
||||
Support for recovering from baseless UID validity changes was added.
|
||||
|
||||
The get-cert script was renamed to mbsync-get-cert.
|
||||
|
||||
[1.2.0]
|
||||
|
||||
The 'isync' compatibility wrapper is now deprecated.
|
||||
|
||||
An IMAP Path/NAMESPACE rooted in INBOX won't be handled specially any more.
|
||||
This means that some Patterns may need adjustment.
|
||||
|
||||
The default output is a lot less verbose now.
|
||||
The meanings of the -V and -D options changed significantly.
|
||||
|
||||
The SSL/TLS configuration has been re-designed.
|
||||
SSL is now explicitly enabled or disabled - "use SSL if available" is gone.
|
||||
Notice: Tunnels are assumed to be secure and thus default to no SSL.
|
||||
|
||||
Support for SASL (flexible authentication) has been added.
|
||||
|
||||
Support for Windows file systems has been added.
|
||||
|
||||
Support for compressed data transfer has been added.
|
||||
|
||||
Folder deletions can be propagated now.
|
||||
|
||||
[1.1.0]
|
||||
|
||||
Support for hierarchical mailboxes in Patterns.
|
||||
|
||||
Full support for IMAP pipelining (streaming, parallelization) added.
|
||||
This is considerably faster especially with high-latency networks.
|
||||
|
||||
Faster and hopefully more reliable support for IMAP servers without the
|
||||
UIDPLUS extension (e.g., M$ Exchange).
|
||||
|
||||
More automatic handling of SSL certificates.
|
||||
|
||||
IPv6 support.
|
||||
|
||||
IMAP password query can be scripted.
|
||||
|
||||
Message arrival dates can be propagated.
|
||||
|
||||
Data safety in case of system crashes was improved.
|
||||
|
||||
MaxMessages was made vastly more useful.
|
||||
|
||||
[1.0.0]
|
||||
|
||||
Essentially a rewrite. Synchronization state storage concept, configuration
|
||||
and command line changed entirely.
|
||||
But you needn't to worry about the upgrade, as a fully automated migration
|
||||
path is provided, even for users of isync 0.7 and below.
|
||||
Still, you should re-read the manual to be able to take full advantage of the
|
||||
new features:
|
||||
|
||||
The supported mailbox types can be freely paired.
|
||||
A possible application of this is using a local IMAP server to access
|
||||
mailboxes that are not natively supported yet.
|
||||
|
||||
Message deletions (expunges) are now propagated both ways, so there is no need
|
||||
for using mutt with maildir_trash any more.
|
||||
|
||||
Additional trash options added.
|
||||
|
||||
`OneToOne' replaced by something more flexible.
|
||||
|
||||
Partial support for IMAP pipelining (streaming, parallelization) added.
|
||||
Makes flag change propagation much faster - this affects every message that
|
||||
becomes Seen/Read.
|
||||
|
||||
[0.9]
|
||||
|
||||
Added Tunnel directive to allow the user to specify a shell command to run
|
||||
to set up an IMAP connection in place of a TCP socket (eg., to run over
|
||||
an SSH session).
|
||||
|
||||
Added PREAUTH support (useful mostly in conjunction with Tunnel).
|
||||
|
||||
Messages marked deleted are not uploaded when we are going to expunge.
|
||||
|
||||
Locally generated messages are not re-fetched after uploading even if the
|
||||
UIDPLUS extension is not supported by the server.
|
||||
|
||||
Added `OneToOne' configuration option: ignore any Mailbox specifications
|
||||
and instead pick up all mailboxes from the local MailDir and remote Folder
|
||||
and map them 1:1 onto each other according to their names.
|
||||
|
||||
-C now creates both local and remote boxes; -L and -R create only local/remote.
|
||||
|
||||
--quiet is now really quiet.
|
||||
|
||||
[0.8]
|
||||
|
||||
!!! IMPORTANT !!!
|
||||
|
||||
In order to fix the problem where messages copied from one mailbox to
|
||||
another were not uploaded to the new mailbox, the way Isync stores the UID
|
||||
for each message needed to be changed. As a result, you _MUST_ delete all
|
||||
the messages in the local maildir box before using this version. Otherwise
|
||||
it will upload every message to the server thinking its a new mail.
|
||||
|
||||
[0.7]
|
||||
|
||||
Added `MaxMessages' configuration option to allow tracking of only the most
|
||||
recently added message in the local mailbox.
|
||||
|
||||
Added --create (-C) command line option to force creation of the local
|
||||
maildir-style mailbox if it doesn't already exist.
|
||||
|
||||
[0.6]
|
||||
|
||||
Added `Delete' configuration option to correspond to the -d command line
|
||||
option.
|
||||
|
||||
Added -a (--all) command line option to synchronize all mailboxes.
|
||||
|
||||
[0.5]
|
||||
|
||||
Updated SSL support.
|
||||
|
||||
Added CRAM authentication support.
|
||||
|
||||
Added MailDir configuration option to specify the default location of local
|
||||
mailboxes when relative paths are used.
|
||||
|
||||
Added support for uploading local messages to the IMAP server.
|
||||
|
||||
Added CopyDeletedTo configuration option to cause isync to move deleted
|
||||
messages to a particular mailbox on the server when they are expunged.
|
||||
|
||||
[0.4]
|
||||
|
||||
Added MaxSize configuration option to limit downloading of new messages from
|
||||
|
|
82
README
82
README
|
@ -4,49 +4,75 @@
|
|||
| \__ \ |_| | | | | (__
|
||||
|_|___/\__, |_| |_|\___|
|
||||
|___/
|
||||
isync - IMAP4 to maildir mailbox synchronization program
|
||||
http://www.sigpipe.org/isync/
|
||||
isync/mbsync - free (GPL) mailbox synchronization program
|
||||
http://isync.sf.net/
|
||||
|
||||
Author: Michael Elkins <me@mutt.org>
|
||||
See AUTHORS for contact information.
|
||||
|
||||
``isync'' is a command line application which synchronizes a local
|
||||
maildir-style mailbox with a remote IMAP4 mailbox, suitable for use in
|
||||
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailbox can be
|
||||
maintained, and all flags are synchronized.
|
||||
``mbsync'' is a command line application which synchronizes mailboxes;
|
||||
currently Maildir and IMAP4 mailboxes are supported. New messages, message
|
||||
deletions and flag changes can be propagated both ways.
|
||||
``mbsync'' is suitable for use in IMAP-disconnected mode.
|
||||
|
||||
* Features:
|
||||
Synchronization is based on unique message identifiers (UIDs), so
|
||||
no identification conflicts can occur (unlike with some other mail
|
||||
synchronizers).
|
||||
Synchronization state is kept in one local text file per mailbox pair;
|
||||
these files are protected against concurrent ``mbsync'' processes.
|
||||
Mailboxes can be safely modified while ``mbsync'' operates.
|
||||
Multiple replicas of each mailbox can be maintained.
|
||||
|
||||
* Fast mode for fetching new mail only
|
||||
* Supports imaps: (port 993) TLS/SSL connections
|
||||
* Supports STARTTLS (RFC2595) for confidentiality
|
||||
* Supports NAMESPACE (RFC2342)
|
||||
* Supports CRAM-MD5 (RFC2095) for authentication
|
||||
isync is the project name, while mbsync is the current executable name; this
|
||||
change was necessary because of massive changes in the user interface.
|
||||
|
||||
* Features
|
||||
|
||||
* Fine-grained selection of synchronization operations to perform
|
||||
* Synchronizes single mailboxes or entire mailbox collections
|
||||
* Partial mirrors possible: keep only the latest messages locally
|
||||
* Trash functionality: backup messages before removing them
|
||||
* IMAP features:
|
||||
* Supports TLS/SSL via imaps: (port 993) and STARTTLS
|
||||
* Supports SASL for authentication
|
||||
* Pipelining for maximum speed
|
||||
|
||||
* Compatibility
|
||||
|
||||
``isync'' has been tested with the following IMAP servers:
|
||||
isync should work fairly well with any IMAP4 compliant server;
|
||||
servers that support the UIDPLUS and LITERAL+ extensions are most
|
||||
efficient.
|
||||
|
||||
* Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0
|
||||
* Courier-IMAP 1.2.3
|
||||
* WU-IMAP 2000
|
||||
Courier 1.4.3 is known to be buggy, version 1.7.3 works fine.
|
||||
|
||||
M$ Exchange (2013 at least) needs DisableExtension MOVE to be compatible
|
||||
with the Trash functionality.
|
||||
|
||||
* Platforms
|
||||
|
||||
``isync'' has successfully be compiled under:
|
||||
|
||||
* Linux 2.2.18
|
||||
* Solaris 2.7
|
||||
* OpenBSD 2.8
|
||||
At some point, ``isync'' has successfully run on:
|
||||
Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3.
|
||||
|
||||
* Requirements
|
||||
|
||||
OpenSSL for TLS/SSL support (optional)
|
||||
perl v5.14+
|
||||
Berkeley DB 4.1+ (optional)
|
||||
OpenSSL for TLS/SSL support (optional)
|
||||
Cyrus SASL (optional)
|
||||
zlib (optional)
|
||||
|
||||
* INSTALLING
|
||||
The build from git also requires:
|
||||
|
||||
./configure
|
||||
make install
|
||||
GNU autotools (autoconf & automake)
|
||||
perl module Date::Parse (libtimedate-perl on Debian, perl-TimeDate on
|
||||
Fedora and Suse)
|
||||
|
||||
* HELP
|
||||
* Installation
|
||||
|
||||
Please see the man page for complete documentation.
|
||||
./autogen.sh (only when building from git)
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
* Help
|
||||
|
||||
Please see the man page for complete documentation.
|
||||
|
|
87
TODO
87
TODO
|
@ -1,3 +1,86 @@
|
|||
add upload support to mirror local msgs on the server
|
||||
f{,data}sync() usage could be optimized by batching the calls.
|
||||
|
||||
add support for syncing with other: and shared: via NAMESPACE
|
||||
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
|
||||
|
||||
automatically resume upon transient errors, e.g. "connection reset by peer"
|
||||
or timeout after some data was already transmitted.
|
||||
possibly also try to handle Exchange's "glitches" somehow.
|
||||
|
||||
add support for IMAP UTF-7 (for internationalized mailbox names).
|
||||
|
||||
uidvalidity lock timeout handling would be a good idea.
|
||||
|
||||
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 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.
|
||||
|
||||
add alternative treatments of expired messages. ExpiredMessageMode: Prune
|
||||
(delete messages like now), Keep (just don't sync) and Archive (move to
|
||||
separate folder - ArchiveSuffix, default .archive).
|
||||
|
||||
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 far and near side in
|
||||
Patterns.
|
||||
- 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
|
||||
|
||||
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
|
||||
special cases of this.
|
||||
|
||||
add daemon mode. primary goal: keep imap password in memory.
|
||||
also: idling mode.
|
||||
|
||||
parallel fetching of multiple mailboxes.
|
||||
TLS session resumption becomes interesting then as well.
|
||||
|
||||
imap_set_flags(): group commands for efficiency, don't call back until
|
||||
imap_commit().
|
||||
|
||||
add streaming from fetching to storing.
|
||||
|
||||
handle custom flags (keywords).
|
||||
|
||||
make use of IMAP CONDSTORE extension (rfc4551; CHANGEDSINCE FETCH Modifier);
|
||||
make use of IMAP QRESYNC extension (rfc5162) to avoid SEARCH to find vanished
|
||||
messages.
|
||||
|
||||
use MULTIAPPEND and FETCH with multiple messages.
|
||||
|
||||
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.
|
||||
problem: UIDVALIDITY change detection is delayed, significantly complicating
|
||||
matters.
|
||||
|
||||
some error messages are unhelpful in non-verbose mode due to missing context.
|
||||
|
||||
possibly use ^[[1m to highlight error messages.
|
||||
|
||||
consider alternative approach to trashing: instead of the current trash-before-
|
||||
expunge done by mbsync, let MUAs do the trashing (as modern ones typically do).
|
||||
mbsync wouldn't do any trashing by itself, but should track the moves for
|
||||
optimization. additionally, there should be a mode to move trashed messages to
|
||||
the remote store. TrashMode Internal|External, AbsorbRemoteTrash.
|
||||
a yet different approach to trashing is treating the trash like a normal mailbox.
|
||||
however, this implies a huge working set.
|
||||
|
||||
consider optional use of messages-id (and X-GM-MSGID):
|
||||
- detection of message moves between folders
|
||||
- recovery from loss of sync state, migration from other tools
|
||||
|
|
35
acinclude.m4
Normal file
35
acinclude.m4
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Add --enable-maintainer-mode option to configure.
|
||||
# From Jim Meyering
|
||||
# Change it to enable maintainer mode by default by Nicolas Boullis.
|
||||
|
||||
# Copyright 1996, 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
# Copyright 2004 Nicolas Boullis.
|
||||
|
||||
# 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, 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, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
AC_DEFUN([AM_MAINTAINER_MODE],
|
||||
[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
|
||||
dnl maintainer-mode is enabled by default
|
||||
AC_ARG_ENABLE(maintainer-mode,
|
||||
[ --disable-maintainer-mode disable make rules and dependencies not useful
|
||||
(and sometimes confusing) to the casual installer],
|
||||
USE_MAINTAINER_MODE=$enableval,
|
||||
USE_MAINTAINER_MODE=yes)
|
||||
AC_MSG_RESULT([$USE_MAINTAINER_MODE])
|
||||
AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes])
|
||||
MAINT=$MAINTAINER_MODE_TRUE
|
||||
AC_SUBST(MAINT)dnl
|
||||
]
|
||||
)
|
19
autogen.sh
19
autogen.sh
|
@ -1,15 +1,4 @@
|
|||
#!/bin/sh
|
||||
# $Id$
|
||||
aclocal
|
||||
if test $? -ne 0; then
|
||||
exit
|
||||
fi
|
||||
automake --add-missing
|
||||
if test $? -ne 0; then
|
||||
exit
|
||||
fi
|
||||
autoconf
|
||||
if test $? -ne 0; then
|
||||
exit
|
||||
fi
|
||||
./configure $@
|
||||
#! /bin/sh
|
||||
set -e -v
|
||||
make -f Makefile.am log
|
||||
autoreconf -f -i
|
||||
|
|
3
build
Executable file
3
build
Executable 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
|
280
configure.ac
Normal file
280
configure.ac
Normal file
|
@ -0,0 +1,280 @@
|
|||
AC_INIT([isync], [1.4.4])
|
||||
AC_CONFIG_HEADERS([autodefs.h])
|
||||
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AC_PROG_CC
|
||||
if test "$GCC" = yes; then
|
||||
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)
|
||||
if test "x$PERL" = "x"; then
|
||||
AC_MSG_ERROR([perl not found])
|
||||
fi
|
||||
|
||||
need_perl=5.14
|
||||
AC_CACHE_CHECK([whether perl is recent enough], ob_cv_perl_ver, [
|
||||
if $PERL -e "use v$need_perl;" 2> /dev/null; then
|
||||
ob_cv_perl_ver=yes
|
||||
else
|
||||
ob_cv_perl_ver=no
|
||||
fi
|
||||
])
|
||||
if test "x$ob_cv_perl_ver" = "xno"; then
|
||||
AC_MSG_ERROR([perl is too old, need v$need_perl])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z,
|
||||
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
time_t t = 0;
|
||||
char buf[32];
|
||||
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)"])])
|
||||
if test "x$ob_cv_strftime_z" = x"no"; then
|
||||
AC_MSG_ERROR([libc lacks necessary feature])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADERS(poll.h sys/select.h)
|
||||
AC_CHECK_FUNCS(vasprintf strnlen memrchr timegm)
|
||||
|
||||
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
|
||||
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
|
||||
AC_SUBST(SOCK_LIBS)
|
||||
|
||||
have_ipv6=true
|
||||
sav_LIBS=$LIBS
|
||||
LIBS="$LIBS $SOCK_LIBS"
|
||||
AC_CHECK_FUNCS(getaddrinfo inet_ntop, , [have_ipv6=false])
|
||||
LIBS=$sav_LIBS
|
||||
if $have_ipv6; then
|
||||
AC_DEFINE(HAVE_IPV6, 1, [if your libc has IPv6 support])
|
||||
fi
|
||||
|
||||
have_ssl_paths=
|
||||
AC_ARG_WITH(ssl,
|
||||
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
|
||||
""|yes)
|
||||
dnl Detect the pkg-config tool, as it may have extra info about the openssl
|
||||
dnl installation we can use. I *believe* this is what we are expected to do
|
||||
dnl on really recent Redhat Linux hosts.
|
||||
PKG_PROG_PKG_CONFIG
|
||||
if test "x$PKG_CONFIG" != "x" ; then
|
||||
AC_MSG_CHECKING([OpenSSL presence with pkg-config])
|
||||
if $PKG_CONFIG --exists openssl; then
|
||||
SSL_LIBS=`$PKG_CONFIG --libs-only-l openssl`
|
||||
SSL_LDFLAGS=`$PKG_CONFIG --libs-only-L openssl`
|
||||
SSL_CPPFLAGS=`$PKG_CONFIG --cflags-only-I openssl`
|
||||
have_ssl_paths=yes
|
||||
AC_MSG_RESULT([found])
|
||||
else
|
||||
AC_MSG_RESULT([not found])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
SSL_LDFLAGS=-L$ob_cv_with_ssl/lib$libsuff
|
||||
SSL_CPPFLAGS=-I$ob_cv_with_ssl/include
|
||||
;;
|
||||
esac
|
||||
if test -z "$have_ssl_paths"; then
|
||||
sav_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
|
||||
AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl])
|
||||
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
|
||||
fi
|
||||
|
||||
sav_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
|
||||
AC_CHECK_HEADER(openssl/ssl.h, , [have_ssl_paths=])
|
||||
CPPFLAGS=$sav_CPPFLAGS
|
||||
|
||||
if test -z "$have_ssl_paths"; then
|
||||
if test -n "$ob_cv_with_ssl"; then
|
||||
AC_MSG_ERROR([OpenSSL libs and/or includes were not found where specified])
|
||||
fi
|
||||
else
|
||||
AC_DEFINE(HAVE_LIBSSL, 1, [if you have the OpenSSL libraries])
|
||||
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(SSL_LIBS)
|
||||
|
||||
have_sasl_paths=
|
||||
AC_ARG_WITH(sasl,
|
||||
AS_HELP_STRING([--with-sasl[=PATH]], [where to look for SASL [detect]]),
|
||||
[ob_cv_with_sasl=$withval])
|
||||
if test "x$ob_cv_with_sasl" != xno; then
|
||||
case $ob_cv_with_sasl in
|
||||
""|yes)
|
||||
dnl FIXME: Try various possible paths here...
|
||||
;;
|
||||
*)
|
||||
SASL_LDFLAGS=-L$ob_cv_with_sasl/lib$libsuff
|
||||
SASL_CPPFLAGS=-I$ob_cv_with_sasl/include
|
||||
;;
|
||||
esac
|
||||
if test -z "$have_sasl_paths"; then
|
||||
sav_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
|
||||
AC_CHECK_LIB(sasl2, sasl_client_init,
|
||||
[SASL_LIBS="-lsasl2" have_sasl_paths=yes])
|
||||
LDFLAGS=$sav_LDFLAGS
|
||||
fi
|
||||
|
||||
sav_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
|
||||
AC_CHECK_HEADER(sasl/sasl.h, , [have_sasl_paths=])
|
||||
CPPFLAGS=$sav_CPPFLAGS
|
||||
|
||||
if test -z "$have_sasl_paths"; then
|
||||
if test -n "$ob_cv_with_sasl"; then
|
||||
AC_MSG_ERROR([SASL libs and/or includes were not found where specified])
|
||||
fi
|
||||
else
|
||||
AC_DEFINE(HAVE_LIBSASL, 1, [if you have the SASL libraries])
|
||||
CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(SASL_LIBS)
|
||||
|
||||
AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4,
|
||||
[ac_cv_berkdb4=no
|
||||
sav_LIBS=$LIBS
|
||||
LIBS="$LIBS -ldb"
|
||||
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
|
||||
AC_SUBST([DB_LIBS], ["-ldb"])
|
||||
AC_DEFINE(USE_DB, 1, [if Berkeley DB should be used])
|
||||
fi
|
||||
|
||||
have_zlib=
|
||||
AC_ARG_WITH(zlib,
|
||||
AS_HELP_STRING([--with-zlib], [use zlib [detect]]),
|
||||
[ob_cv_with_zlib=$withval])
|
||||
if test "x$ob_cv_with_zlib" != xno; then
|
||||
AC_CHECK_LIB([z], [deflate],
|
||||
[AC_CHECK_HEADER(zlib.h,
|
||||
[have_zlib=1
|
||||
AC_SUBST([Z_LIBS], ["-lz"])
|
||||
AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])]
|
||||
)]
|
||||
)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes)
|
||||
|
||||
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()
|
||||
if test -n "$have_ssl_paths"; then
|
||||
AC_MSG_RESULT([Using SSL])
|
||||
else
|
||||
AC_MSG_RESULT([Not using SSL])
|
||||
fi
|
||||
if test -n "$have_sasl_paths"; then
|
||||
AC_MSG_RESULT([Using SASL])
|
||||
else
|
||||
AC_MSG_RESULT([Not using SASL])
|
||||
fi
|
||||
if test -n "$have_zlib"; then
|
||||
AC_MSG_RESULT([Using zlib])
|
||||
else
|
||||
AC_MSG_RESULT([Not using zlib])
|
||||
fi
|
||||
if test "x$ac_cv_berkdb4" = xyes; then
|
||||
AC_MSG_RESULT([Using Berkeley DB])
|
||||
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
configure.in
13
configure.in
|
@ -1,13 +0,0 @@
|
|||
AC_INIT(isync.h)
|
||||
AM_INIT_AUTOMAKE(isync,0.4)
|
||||
AM_PROG_CC_STDC
|
||||
if test $CC = gcc; then
|
||||
CFLAGS="$CFLAGS -pipe"
|
||||
fi
|
||||
AC_CHECK_FUNCS(getopt_long)
|
||||
AC_CHECK_LIB(socket,socket)
|
||||
AC_CHECK_LIB(nsl,inet_ntoa)
|
||||
AC_CHECK_LIB(crypto,ERR_error_string)
|
||||
AC_CHECK_LIB(ssl,SSL_library_init)
|
||||
CFLAGS="$CFLAGS -W -Wall -pedantic -Wmissing-prototypes -Wmissing-declarations"
|
||||
AC_OUTPUT(Makefile)
|
85
cram.c
85
cram.c
|
@ -1,85 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "isync.h"
|
||||
|
||||
#if HAVE_LIBSSL
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#define ENCODED_SIZE(n) (4*((n+2)/3))
|
||||
|
||||
static char
|
||||
hexchar (unsigned int b)
|
||||
{
|
||||
if (b < 10)
|
||||
return '0' + b;
|
||||
return 'a' + (b - 10);
|
||||
}
|
||||
|
||||
char *
|
||||
cram (const char *challenge, const char *user, const char *pass)
|
||||
{
|
||||
HMAC_CTX hmac;
|
||||
char hash[16];
|
||||
char hex[33];
|
||||
int i;
|
||||
unsigned int hashlen = sizeof (hash);
|
||||
char buf[256];
|
||||
int len = strlen (challenge);
|
||||
char *response = calloc (1, 1 + len);
|
||||
char *final;
|
||||
|
||||
/* response will always be smaller than challenge because we are
|
||||
* decoding.
|
||||
*/
|
||||
len = EVP_DecodeBlock ((unsigned char *) response, (unsigned char *) challenge, strlen (challenge));
|
||||
// printf ("CRAM-MD5 challege is %s\n", response);
|
||||
|
||||
HMAC_Init (&hmac, (unsigned char *) pass, strlen (pass), EVP_md5 ());
|
||||
HMAC_Update (&hmac, (unsigned char *) response, strlen(response));
|
||||
HMAC_Final (&hmac, (unsigned char *) hash, &hashlen);
|
||||
|
||||
assert (hashlen == sizeof (hash));
|
||||
|
||||
free (response);
|
||||
|
||||
hex[32] = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
hex[2 * i] = hexchar ((hash[i] >> 4) & 0xf);
|
||||
hex[2 * i + 1] = hexchar (hash[i] & 0xf);
|
||||
}
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s %s", user, hex);
|
||||
// printf ("Response: %s\n", buf);
|
||||
|
||||
len = strlen (buf);
|
||||
len = ENCODED_SIZE (len) + 1;
|
||||
final = malloc (len);
|
||||
final[len - 1] = 0;
|
||||
|
||||
assert (EVP_EncodeBlock ((unsigned char *) final, (unsigned char *) buf, strlen (buf)) == len - 1);
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
#endif
|
7
debian/.gitignore
vendored
Normal file
7
debian/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/.debhelper
|
||||
/autoreconf.after
|
||||
/autoreconf.before
|
||||
/files
|
||||
/isync
|
||||
/isync.debhelper.log
|
||||
/isync.substvars
|
13
debian/README.Debian
vendored
Normal file
13
debian/README.Debian
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
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>!mbsync the_channel:maildir\n"'
|
||||
|
||||
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).
|
349
debian/changelog
vendored
Normal file
349
debian/changelog
vendored
Normal file
|
@ -0,0 +1,349 @@
|
|||
isync (1.2.3-0) unstable; urgency=low
|
||||
|
||||
* Upload to unstable (with urgency=low)
|
||||
|
||||
-- Oswald Buddenhagen <ossi@users.sf.net> Sun, 01 Oct 2017 12:12:12 +0000
|
||||
|
||||
isync (1.2.1-2) unstable; urgency=low
|
||||
|
||||
* Upload to unstable (with urgency=low)
|
||||
* Don't call uupdate after uscan
|
||||
* Import patch to fix build with OpenSSL 1.1 (Closes: #828357)
|
||||
* Bump Standards-Version to 3.9.8 (no changes needed)
|
||||
* Add pkg-config to Build-Depends
|
||||
* Update Vcs-* URLs
|
||||
* Fix spelling-error-in-binary
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Sat, 19 Nov 2016 17:14:42 +0000
|
||||
|
||||
isync (1.2.1-1) experimental; urgency=medium
|
||||
|
||||
[ Evgeni Golov ]
|
||||
* New upstream release.
|
||||
* Explicitly Build-Depend on zlib1g-dev
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Sat, 09 Jan 2016 12:56:39 +0000
|
||||
|
||||
isync (1.2.0-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
- Only show sync progress by default (Closes: #765052)
|
||||
* Enable libsasl support
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Mon, 06 Apr 2015 13:42:24 +0200
|
||||
|
||||
isync (1.1.2-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
* Bump Standards-Version to 3.9.6 (no changes needed)
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Sun, 01 Feb 2015 20:42:25 +0100
|
||||
|
||||
isync (1.1.1-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
- Don't lie about the default of User (Closes: #744389)
|
||||
- Don't forget to reset message counts when skipping scan (Closes: #744259)
|
||||
- Rework maildir store mapping (Closes: #737708)
|
||||
* Drop 01_fix-manpages.patch (merged upstream)
|
||||
* Drop 02_fix-empty-folder-sync.patch (merged upstream)
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Tue, 03 Jun 2014 21:00:44 +0200
|
||||
|
||||
isync (1.1.0-2) unstable; urgency=medium
|
||||
|
||||
* Drop 02_fix-duplicate-changelog.patch
|
||||
(rm the file after installation instead)
|
||||
* Update 01_fix-manpages.patch
|
||||
* Add 02_fix-empty-folder-sync.patch (Closes: #738873)
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Fri, 14 Feb 2014 20:41:49 +0100
|
||||
|
||||
isync (1.1.0-1) unstable; urgency=low
|
||||
|
||||
* New upstream release (Closes: #674403)
|
||||
- Fix overlapping memcpy (Closes: #650373)
|
||||
- Fix segfault while syncing mailboxes (Closes: #411120)
|
||||
- Fix segfault when invoked with arguments without configuration
|
||||
(Closes: #727239)
|
||||
* Bump debhelper compat level, update Build-Depends
|
||||
* Switch to short-form dh rules, remove useless files
|
||||
* Switch to 3.0 (quilt) source format
|
||||
* Remove empty patches/ directory
|
||||
* Drop local source modifications
|
||||
* Update short/long descriptions
|
||||
* Add 01_fix-manpages.patch to fix manpage errors and typos
|
||||
* Add Homepage field
|
||||
* Update copyright file to Copyright-Format 1.0
|
||||
* Add Vcs-* fields
|
||||
* Add 02_fix-duplicate-changelog.patch to avoid duplicate changelog install
|
||||
* Add myself to Uploaders
|
||||
* Bump Standards-Version to 3.9.5 (no changes needed)
|
||||
* Use dh-autoreconf instead of autotools-dev
|
||||
|
||||
-- Alessandro Ghedini <ghedo@debian.org> Sun, 12 Jan 2014 16:35:52 +0100
|
||||
|
||||
isync (1.0.4-2.2) unstable; urgency=low
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Apply upstream patch for CVE-2013-0289.
|
||||
Fix incorrect server's SSL x509.v3 certificate validation when
|
||||
performing IMAP synchronization. (Closes: #701052)
|
||||
|
||||
-- Salvatore Bonaccorso <carnil@debian.org> Sun, 24 Feb 2013 09:27:55 +0100
|
||||
|
||||
isync (1.0.4-2.1) unstable; urgency=low
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Drop debconf note that deals with a pre-Etch transition.
|
||||
Closes: #492194
|
||||
|
||||
-- Christian Perrier <bubulle@debian.org> Sat, 25 Oct 2008 08:40:52 +0200
|
||||
|
||||
isync (1.0.4-2) unstable; urgency=low
|
||||
|
||||
* Change the libdb4.4-dev build-dependency to libdb-dev. Thanks Luk for
|
||||
pointing this. (Closes: #499165)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Wed, 17 Sep 2008 23:58:58 +0200
|
||||
|
||||
isync (1.0.4-1) unstable; urgency=low
|
||||
|
||||
* The second "thanks Christian" release.
|
||||
* New upstream release.
|
||||
- Accept empty "* SEARCH" response. (Closes: #413336)
|
||||
- Quote user name in generated config. (Closes: #456783)
|
||||
* Explain the isync->mbsync change in the package description.
|
||||
(Closes: #430648)
|
||||
* Fix the debian/watch file that lacked the version and action fields.
|
||||
* Disable the upstream "deb-clean" stuff in the top-level Makefile, as
|
||||
in breaks cleaning the build directory.
|
||||
* Bump Standards-Version to 3.7.3. (No change required.)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Sat, 03 May 2008 01:42:55 +0200
|
||||
|
||||
isync (1.0.3-3.1) unstable; urgency=low
|
||||
|
||||
* Non-maintainer upload to fix pending l10n issues.
|
||||
* Debconf translations:
|
||||
- Portuguese. Closes: #418283
|
||||
- Italian. Closes: #418246
|
||||
- Dutch. Closes: #422244
|
||||
- Spanish. Closes: #426184
|
||||
- Finnish. Closes: #468214
|
||||
- Galician. Closes: #470529
|
||||
* [Lintian] Do not include debian revision in the build dependency for
|
||||
libssl-dev
|
||||
* [Lintian] No longer ignore errors from "make distclean"
|
||||
|
||||
-- Christian Perrier <bubulle@debian.org> Wed, 12 Mar 2008 07:24:01 +0100
|
||||
|
||||
isync (1.0.3-3) unstable; urgency=low
|
||||
|
||||
* The "thanks Christian" release.
|
||||
* Update German debconf templates translation. Thanks to Erik Schanze
|
||||
(for the translation) and Christian Perrier (for forwarding the
|
||||
translation). (Closes: #407615)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Mon, 5 Feb 2007 00:17:15 +0100
|
||||
|
||||
isync (1.0.3-2.1) unstable; urgency=low
|
||||
|
||||
* Non-maintainer upload with maintainer's permission
|
||||
* Debconf templates translations:
|
||||
- French updated by me
|
||||
- Brazilian Portuguese translation added
|
||||
- Czech translation added. Closes: #403473
|
||||
- Russian translation added. Closes: #403510
|
||||
- Vietnamese translation added
|
||||
- Norwegian Bokmål translation added. Closes: #403523
|
||||
|
||||
-- Christian Perrier <bubulle@debian.org> Sun, 17 Dec 2006 15:31:04 +0100
|
||||
|
||||
isync (1.0.3-2) unstable; urgency=low
|
||||
|
||||
* Back to unstable, with permission from Steve Langasek. (Message-ID:
|
||||
<20061121015225.GF28035@borges.dodds.net>)
|
||||
* Rewrite the debconf note, thanks to the debian-l10n-english team (and
|
||||
especially MJ Ray).
|
||||
* Also add some information about the new version into NEWS.Debian.
|
||||
* Remove the information about the need to set the T (trashed) flag from
|
||||
README.Debian.
|
||||
* Also install the isyncrc.sample sample configuration file.
|
||||
* Bump Standards-Version to 3.7.2. (No change required.)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Tue, 5 Dec 2006 00:34:54 +0100
|
||||
|
||||
isync (1.0.3-1) experimental; urgency=low
|
||||
|
||||
* New upstream release. (Closes: #315423)
|
||||
- Isync now supports breaking and linking threads. (Closes: #177280)
|
||||
- It also supports unflagging messages. (Closes: #111286)
|
||||
- IMAP commands are sent asynchronously. (Closes: #226222)
|
||||
* Kill the old debconf question about upgrades from pre-0.8 versions.
|
||||
* Use the (now obsolete) swedish and portugese translations anyway.
|
||||
(Closes: #337771, #378891)
|
||||
* New debconf note that warns about upgrades from pre-1.0 versions.
|
||||
* Add a build dependency on po-debconf.
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Sun, 19 Nov 2006 15:04:31 +0100
|
||||
|
||||
isync (0.9.2-4) unstable; urgency=low
|
||||
|
||||
* Add Czech debconf translation, thanks to Martin Šín. (Closes: #317571)
|
||||
* Build with the newest libssl-dev.
|
||||
* Load the debconf library in postinst to ensure that everything works
|
||||
as expected, thanks to lintian for noticing the problem and to
|
||||
Josselin Mouette for pointing to the right doc.
|
||||
* Fix a bashism in the config script, thanks to lintian.
|
||||
* Update the postal address of the FSF in the copyright file.
|
||||
* Bump Standards-Version to 3.6.2. (No change required.)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Mon, 10 Oct 2005 01:37:50 +0200
|
||||
|
||||
isync (0.9.2-3) unstable; urgency=low
|
||||
|
||||
* Bump build-dependency from libdb4.0-dev to libdb4.2-dev, thanks to
|
||||
Andreas Jochens. (Closes: #280268)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Tue, 9 Nov 2004 18:21:12 +0100
|
||||
|
||||
isync (0.9.2-2) unstable; urgency=low
|
||||
|
||||
* Add german debconf templates translation, thanks to Erik Schanze.
|
||||
(Closes: #267675)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Tue, 24 Aug 2004 00:32:32 +0200
|
||||
|
||||
isync (0.9.2-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
- Password prompt now includes the mailbox/server. (Closes: #92893)
|
||||
* Backported from CVS:
|
||||
- A few prinf converted to info (disabled with -q).
|
||||
- A few other printf converted to warn (disabled with -q -q) to be
|
||||
able to disable the warning when SSL is not available.
|
||||
(Closes: #228086)
|
||||
- Update the manpage accordingly (about -q).
|
||||
- Improve the manpage (about using isync with mutt).
|
||||
* Add Theodore Y. Ts'o as a co-maintainter.
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Tue, 13 Apr 2004 02:12:42 +0200
|
||||
|
||||
isync (0.9.1-4) unstable; urgency=low
|
||||
|
||||
* The "Why do I keep adding such stupid bugs?" release.
|
||||
* Remove the extra parenthesis that caused UID FETCH syntax errors,
|
||||
thanks to Niels den Otter for pointing the bug and giving the
|
||||
solution. (Closes: #224803)
|
||||
* Use configure's --build and --host options to prevent wrong
|
||||
optimizations (such as building for sparc64 rather than for sparc).
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Wed, 7 Jan 2004 01:06:53 +0100
|
||||
|
||||
isync (0.9.1-3) unstable; urgency=low
|
||||
|
||||
* Do not segfault when using both tunneled end non-tunneled connections,
|
||||
thanks to Nik A. Melchior for reporting and for his patch.
|
||||
(Closes: #220667)
|
||||
* Save uid of messages when interrupted, thanks to Theodore Y. Ts'o for
|
||||
reporting and for his patch. (Closes: #220346)
|
||||
* Do not get the sizes of the messages if unneeded (if MaxSize=0).
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Thu, 18 Dec 2003 00:55:04 +0100
|
||||
|
||||
isync (0.9.1-2) unstable; urgency=low
|
||||
|
||||
* Add french debconf templates translation, thanks to Christian
|
||||
Perrier. (Closes: #218118)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Mon, 3 Nov 2003 18:50:56 +0100
|
||||
|
||||
isync (0.9.1-1) unstable; urgency=low
|
||||
|
||||
* New maintainer. (Closes: #180050)
|
||||
* New upstream release.
|
||||
- With the new option -R, isync is now able to create non-existent
|
||||
remote mailboxes. (Closes: #170388)
|
||||
* Update debian/copyright to match the current copyright:
|
||||
- Add Oswald Buddenhagen as copyright owner.
|
||||
- Add special exception for OpenSSL.
|
||||
* Add support for noopt in $DEB_BUILD_OPTIONS in debian/rules.
|
||||
* Switch to po-debconf.
|
||||
* Remove sample.isyncrc from debian/docs: no need to have it both as a
|
||||
doc and as an example.
|
||||
* Move package from section non-US/main (?) to mail. (Closes: #154216)
|
||||
* Update versionned build-dependency on debhelper to >= 4.1.16.
|
||||
* Bump Standards-Version to 3.6.1. (No change required.)
|
||||
|
||||
-- Nicolas Boullis <nboullis@debian.org> Tue, 14 Oct 2003 22:02:20 +0200
|
||||
|
||||
isync (0.8-4) unstable; urgency=low
|
||||
|
||||
* Orphaned the package, as I no longer use it.
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Thu, 6 Feb 2003 15:46:38 -0500
|
||||
|
||||
isync (0.8-3) unstable; urgency=low
|
||||
|
||||
* New upstream maintainer; updated copyright file web site address, and
|
||||
watch file. NB: new upstream has not made any new releases yet.
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Sat, 1 Feb 2003 16:03:49 -0500
|
||||
|
||||
isync (0.8-2) unstable; urgency=low
|
||||
|
||||
* Only reset debconf question if user chooses to abort upgrade.
|
||||
Closes: #167363
|
||||
* Don't open lock files O_EXCL. As seen in upstream cvs.
|
||||
* Description and build-deps updates.
|
||||
* Added README.Debian with notes on mutt integration.
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Fri, 1 Nov 2002 18:02:44 -0500
|
||||
|
||||
isync (0.8-1) unstable; urgency=low
|
||||
|
||||
* New upstream release. Closes: #134080
|
||||
|
||||
**WARNING**
|
||||
You need to remove all the messages in your local folder if you were
|
||||
previously using another version of isync or else you will end up with
|
||||
duplicate messages on your IMAP server.
|
||||
|
||||
* Has better support for uploading locally added messages. Closes: #120272
|
||||
* Added a debconf queston with some info about this that lets you abort the
|
||||
upgrade.
|
||||
* Added NEWS.Debian with same info.
|
||||
* New maintainer.
|
||||
* Removed upstream debianization stuff.
|
||||
* Updated copyright file.
|
||||
* Updated to current policy throughout.
|
||||
* Added uscan watch file.
|
||||
* Updated build-deps.
|
||||
* Now that isync needs berkeley databases, go with db4, so I don't have to
|
||||
transition from db3 later.
|
||||
* Fix fd leak (forgot to close tmp dir in maildir). Closes: #150762
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Tue, 29 Oct 2002 17:02:14 -0500
|
||||
|
||||
isync (0.7-1) unstable; urgency=low
|
||||
|
||||
* New upstream version (Closes: #121312, #92051).
|
||||
* Rumors say this might fix bugs #102255 and #120272,
|
||||
but I have no test setup right now, so I'm leaving them open.
|
||||
* Updated standards-version.
|
||||
|
||||
-- Tommi Virtanen <tv@debian.org> Sat, 5 Jan 2002 16:13:35 +0200
|
||||
|
||||
isync (0.5-1) unstable; urgency=low
|
||||
|
||||
* New upstream version (Closes: #98642).
|
||||
* Install sample.isyncrc too (Closes: #90464).
|
||||
|
||||
-- Tommi Virtanen <tv@debian.org> Sat, 23 Jun 2001 01:19:07 +0300
|
||||
|
||||
isync (0.4-1) unstable; urgency=low
|
||||
|
||||
* Initial Release.
|
||||
|
||||
-- Tommi Virtanen <tv@debian.org> Sat, 10 Mar 2001 18:43:35 +0200
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
9
|
38
debian/control
vendored
Normal file
38
debian/control
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
Source: isync
|
||||
Section: mail
|
||||
Priority: optional
|
||||
Maintainer: Nicolas Boullis <nboullis@debian.org>
|
||||
Uploaders: Theodore Y. Ts'o <tytso@mit.edu>,
|
||||
Alessandro Ghedini <ghedo@debian.org>
|
||||
Standards-Version: 3.9.8
|
||||
Build-Depends: debhelper (>= 9),
|
||||
dh-autoreconf,
|
||||
libdb-dev,
|
||||
libsasl2-dev,
|
||||
libssl-dev,
|
||||
pkg-config,
|
||||
zlib1g-dev
|
||||
Vcs-Git: https://anonscm.debian.org/git/collab-maint/isync.git
|
||||
Vcs-Browser: https://anonscm.debian.org/gitweb/?p=collab-maint/isync.git
|
||||
Homepage: http://isync.sourceforge.net/
|
||||
|
||||
Package: isync
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Suggests: mutt
|
||||
Description: IMAP and MailDir mailbox synchronizer
|
||||
mbsync/isync is a command line application which synchronizes mailboxes;
|
||||
currently Maildir and IMAP4 mailboxes are supported. New messages, message
|
||||
deletions and flag changes can be propagated both ways. isync is suitable
|
||||
for use in IMAP-disconnected mode.
|
||||
.
|
||||
Features:
|
||||
* Fine-grained selection of synchronization operations to perform
|
||||
* Synchronizes single mailboxes or entire mailbox collections
|
||||
* Partial mirrors possible: keep only the latest messages locally
|
||||
* Trash functionality: backup messages before removing them
|
||||
IMAP features:
|
||||
* Security: supports TLS/SSL via imaps: (port 993) and STARTTLS; SASL
|
||||
for authentication
|
||||
* Supports NAMESPACE for simplified configuration
|
||||
* Pipelining for maximum speed
|
33
debian/copyright
vendored
Normal file
33
debian/copyright
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: isync
|
||||
Source: http://isync.sourceforge.net
|
||||
|
||||
Files: *
|
||||
Copyright: 2000-2002, Michael R. Elkins <me@mutt.org>
|
||||
2002-2017, Oswald Buddenhagen <ossi@users.sf.net>
|
||||
2004, Theodore Y. Ts'o <tytso@mit.edu>
|
||||
License: GPL-2+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2013, Alessandro Ghedini <ghedo@debian.org>
|
||||
License: GPL-2+
|
||||
|
||||
License: GPL-2+
|
||||
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 package 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.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License version
|
||||
2 can be found in "/usr/share/common-licenses/GPL-2".
|
8
debian/rules
vendored
Executable file
8
debian/rules
vendored
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --with=autoreconf
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install
|
||||
$(RM) $(CURDIR)/debian/isync/usr/share/doc/isync/ChangeLog
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
2
debian/watch
vendored
Normal file
2
debian/watch
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
version=3
|
||||
http://sf.net/isync/ isync-(.*)\.tar\.gz
|
905
imap.c
905
imap.c
|
@ -1,905 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#if HAVE_LIBSSL
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#include "isync.h"
|
||||
|
||||
const char *Flags[] = {
|
||||
"\\Seen",
|
||||
"\\Answered",
|
||||
"\\Deleted",
|
||||
"\\Flagged",
|
||||
"\\Recent",
|
||||
"\\Draft"
|
||||
};
|
||||
|
||||
void
|
||||
free_message (message_t * msg)
|
||||
{
|
||||
message_t *tmp;
|
||||
|
||||
while (msg)
|
||||
{
|
||||
tmp = msg;
|
||||
msg = msg->next;
|
||||
if (tmp->file)
|
||||
free (tmp->file);
|
||||
free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LIBSSL
|
||||
|
||||
#define MAX_DEPTH 1
|
||||
|
||||
SSL_CTX *SSLContext = 0;
|
||||
|
||||
/* this gets called when a certificate is to be verified */
|
||||
static int
|
||||
verify_cert (SSL * ssl)
|
||||
{
|
||||
X509 *cert;
|
||||
int err;
|
||||
char buf[256];
|
||||
int ret = -1;
|
||||
BIO *bio;
|
||||
|
||||
cert = SSL_get_peer_certificate (ssl);
|
||||
if (!cert)
|
||||
{
|
||||
puts ("Error, no server certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = SSL_get_verify_result (ssl);
|
||||
if (err == X509_V_OK)
|
||||
return 0;
|
||||
|
||||
printf ("Error, can't verify certificate: %s (%d)\n",
|
||||
X509_verify_cert_error_string (err), err);
|
||||
|
||||
X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf));
|
||||
printf ("\nSubject: %s\n", buf);
|
||||
X509_NAME_oneline (X509_get_issuer_name (cert), buf, sizeof (buf));
|
||||
printf ("Issuer: %s\n", buf);
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
ASN1_TIME_print (bio, X509_get_notBefore (cert));
|
||||
memset (buf, 0, sizeof (buf));
|
||||
BIO_read (bio, buf, sizeof (buf) - 1);
|
||||
printf ("Valid from: %s\n", buf);
|
||||
ASN1_TIME_print (bio, X509_get_notAfter (cert));
|
||||
memset (buf, 0, sizeof (buf));
|
||||
BIO_read (bio, buf, sizeof (buf) - 1);
|
||||
BIO_free (bio);
|
||||
printf (" to: %s\n", buf);
|
||||
|
||||
printf
|
||||
("\n*** WARNING *** There is no way to verify this certificate. It is\n"
|
||||
" possible that a hostile attacker has replaced the\n"
|
||||
" server certificate. Continue at your own risk!\n");
|
||||
printf ("\nAccept this certificate anyway? [no]: ");
|
||||
fflush (stdout);
|
||||
if (fgets (buf, sizeof (buf), stdin) && (buf[0] == 'y' || buf[0] == 'Y'))
|
||||
{
|
||||
ret = 0;
|
||||
puts ("\n*** Fine, but don't say I didn't warn you!\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
init_ssl (config_t * conf)
|
||||
{
|
||||
if (!conf->cert_file)
|
||||
{
|
||||
puts ("Error, CertificateFile not defined");
|
||||
return -1;
|
||||
}
|
||||
SSL_library_init ();
|
||||
SSL_load_error_strings ();
|
||||
SSLContext = SSL_CTX_new (SSLv23_client_method ());
|
||||
if (access (conf->cert_file, F_OK))
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
perror ("access");
|
||||
return -1;
|
||||
}
|
||||
puts
|
||||
("*** Warning, CertificateFile doesn't exist, can't verify server certificates");
|
||||
}
|
||||
else
|
||||
if (!SSL_CTX_load_verify_locations
|
||||
(SSLContext, conf->cert_file, NULL))
|
||||
{
|
||||
printf ("Error, SSL_CTX_load_verify_locations: %s\n",
|
||||
ERR_error_string (ERR_get_error (), 0));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!conf->use_sslv2)
|
||||
SSL_CTX_set_options (SSLContext, SSL_OP_NO_SSLv2);
|
||||
if (!conf->use_sslv3)
|
||||
SSL_CTX_set_options (SSLContext, SSL_OP_NO_SSLv3);
|
||||
if (!conf->use_tlsv1)
|
||||
SSL_CTX_set_options (SSLContext, SSL_OP_NO_TLSv1);
|
||||
|
||||
/* we check the result of the verification after SSL_connect() */
|
||||
SSL_CTX_set_verify (SSLContext, SSL_VERIFY_NONE, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_LIBSSL */
|
||||
|
||||
static int
|
||||
socket_read (Socket_t * sock, char *buf, size_t len)
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
if (sock->use_ssl)
|
||||
return SSL_read (sock->ssl, buf, len);
|
||||
#endif
|
||||
return read (sock->fd, buf, len);
|
||||
}
|
||||
|
||||
static int
|
||||
socket_write (Socket_t * sock, char *buf, size_t len)
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
if (sock->use_ssl)
|
||||
return SSL_write (sock->ssl, buf, len);
|
||||
#endif
|
||||
return write (sock->fd, buf, len);
|
||||
}
|
||||
|
||||
/* simple line buffering */
|
||||
static int
|
||||
buffer_gets (buffer_t * b, char **s)
|
||||
{
|
||||
int n;
|
||||
int start = b->offset;
|
||||
|
||||
*s = b->buf + start;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* make sure we have enough data to read the \r\n sequence */
|
||||
if (b->offset + 1 >= b->bytes)
|
||||
{
|
||||
if (start != 0)
|
||||
{
|
||||
/* shift down used bytes */
|
||||
*s = b->buf;
|
||||
|
||||
assert (start <= b->bytes);
|
||||
n = b->bytes - start;
|
||||
|
||||
if (n)
|
||||
memmove (b->buf, b->buf + start, n);
|
||||
b->offset -= start;
|
||||
b->bytes = n;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
n =
|
||||
socket_read (b->sock, b->buf + b->bytes,
|
||||
sizeof (b->buf) - b->bytes);
|
||||
|
||||
if (n <= 0)
|
||||
{
|
||||
if (n == -1)
|
||||
perror ("read");
|
||||
else
|
||||
puts ("EOF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->bytes += n;
|
||||
}
|
||||
|
||||
if (b->buf[b->offset] == '\r')
|
||||
{
|
||||
assert (b->offset + 1 < b->bytes);
|
||||
if (b->buf[b->offset + 1] == '\n')
|
||||
{
|
||||
b->buf[b->offset] = 0; /* terminate the string */
|
||||
b->offset += 2; /* next line */
|
||||
// assert (strchr (*s, '\r') == 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
b->offset++;
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
static int
|
||||
parse_fetch (imap_t * imap, list_t * list)
|
||||
{
|
||||
list_t *tmp;
|
||||
unsigned int uid = 0;
|
||||
unsigned int mask = 0;
|
||||
unsigned int size = 0;
|
||||
message_t *cur;
|
||||
|
||||
if (!is_list (list))
|
||||
return -1;
|
||||
|
||||
for (tmp = list->child; tmp; tmp = tmp->next)
|
||||
{
|
||||
if (is_atom (tmp))
|
||||
{
|
||||
if (!strcmp ("UID", tmp->val))
|
||||
{
|
||||
tmp = tmp->next;
|
||||
if (is_atom (tmp))
|
||||
{
|
||||
uid = atoi (tmp->val);
|
||||
if (uid < imap->minuid)
|
||||
{
|
||||
/* already saw this message */
|
||||
return 0;
|
||||
}
|
||||
else if (uid > imap->maxuid)
|
||||
imap->maxuid = uid;
|
||||
}
|
||||
else
|
||||
puts ("Error, unable to parse UID");
|
||||
}
|
||||
else if (!strcmp ("FLAGS", tmp->val))
|
||||
{
|
||||
tmp = tmp->next;
|
||||
if (is_list (tmp))
|
||||
{
|
||||
list_t *flags = tmp->child;
|
||||
|
||||
for (; flags; flags = flags->next)
|
||||
{
|
||||
if (is_atom (flags))
|
||||
{
|
||||
if (!strcmp ("\\Seen", flags->val))
|
||||
mask |= D_SEEN;
|
||||
else if (!strcmp ("\\Flagged", flags->val))
|
||||
mask |= D_FLAGGED;
|
||||
else if (!strcmp ("\\Deleted", flags->val))
|
||||
mask |= D_DELETED;
|
||||
else if (!strcmp ("\\Answered", flags->val))
|
||||
mask |= D_ANSWERED;
|
||||
else if (!strcmp ("\\Draft", flags->val))
|
||||
mask |= D_DRAFT;
|
||||
else if (!strcmp ("\\Recent", flags->val))
|
||||
mask |= D_RECENT;
|
||||
else
|
||||
printf ("Warning, unknown flag %s\n",
|
||||
flags->val);
|
||||
}
|
||||
else
|
||||
puts ("Error, unable to parse FLAGS list");
|
||||
}
|
||||
}
|
||||
else
|
||||
puts ("Error, unable to parse FLAGS");
|
||||
}
|
||||
else if (!strcmp ("RFC822.SIZE", tmp->val))
|
||||
{
|
||||
tmp = tmp->next;
|
||||
if (is_atom (tmp))
|
||||
size = atol (tmp->val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur = calloc (1, sizeof (message_t));
|
||||
cur->next = imap->msgs;
|
||||
imap->msgs = cur;
|
||||
|
||||
if (mask & D_DELETED)
|
||||
imap->deleted++;
|
||||
|
||||
cur->uid = uid;
|
||||
cur->flags = mask;
|
||||
cur->size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_response_code (imap_t * imap, char *s)
|
||||
{
|
||||
char *arg;
|
||||
|
||||
if (*s != '[')
|
||||
return; /* no response code */
|
||||
s++;
|
||||
|
||||
arg = next_arg (&s);
|
||||
|
||||
if (!strcmp ("UIDVALIDITY", arg))
|
||||
{
|
||||
arg = next_arg (&s);
|
||||
imap->uidvalidity = atol (arg);
|
||||
}
|
||||
else if (!strcmp ("ALERT", arg))
|
||||
{
|
||||
/* RFC2060 says that these messages MUST be displayed
|
||||
* to the user
|
||||
*/
|
||||
fputs ("***ALERT*** ", stdout);
|
||||
puts (s);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
imap_exec (imap_t * imap, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char tmp[256];
|
||||
char buf[256];
|
||||
char *cmd;
|
||||
char *arg;
|
||||
char *arg1;
|
||||
|
||||
va_start (ap, fmt);
|
||||
vsnprintf (tmp, sizeof (tmp), fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
|
||||
if (Verbose)
|
||||
fputs (buf, stdout);
|
||||
socket_write (imap->sock, buf, strlen (buf));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (buffer_gets (imap->buf, &cmd))
|
||||
return -1;
|
||||
if (Verbose)
|
||||
puts (cmd);
|
||||
|
||||
arg = next_arg (&cmd);
|
||||
if (*arg == '*')
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
if (!arg)
|
||||
{
|
||||
puts ("Error, unable to parse untagged command");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp ("NAMESPACE", arg))
|
||||
{
|
||||
imap->ns_personal = parse_list (cmd, &cmd);
|
||||
imap->ns_other = parse_list (cmd, &cmd);
|
||||
imap->ns_shared = parse_list (cmd, 0);
|
||||
}
|
||||
else if (!strcmp ("OK", arg) || !strcmp ("BAD", arg) ||
|
||||
!strcmp ("NO", arg) || !strcmp ("PREAUTH", arg) ||
|
||||
!strcmp ("BYE", arg))
|
||||
{
|
||||
parse_response_code (imap, cmd);
|
||||
}
|
||||
else if (!strcmp ("CAPABILITY", arg))
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
while ((arg = next_arg (&cmd)))
|
||||
{
|
||||
if (!strcmp ("STARTTLS", arg))
|
||||
imap->have_starttls = 1;
|
||||
else if (!strcmp ("AUTH=CRAM-MD5", arg))
|
||||
imap->have_cram = 1;
|
||||
else if (!strcmp ("NAMESPACE", arg))
|
||||
imap->have_namespace = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if ((arg1 = next_arg (&cmd)))
|
||||
{
|
||||
if (!strcmp ("EXISTS", arg1))
|
||||
imap->count = atoi (arg);
|
||||
else if (!strcmp ("RECENT", arg1))
|
||||
imap->recent = atoi (arg);
|
||||
else if (!strcmp ("FETCH", arg1))
|
||||
{
|
||||
list_t *list;
|
||||
|
||||
list = parse_list (cmd, 0);
|
||||
|
||||
if (parse_fetch (imap, list))
|
||||
{
|
||||
free_list (list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free_list (list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
puts ("Error, unable to parse untagged command");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#if HAVE_LIBSSL
|
||||
else if (*arg == '+')
|
||||
{
|
||||
char *resp;
|
||||
|
||||
if (!imap->cram)
|
||||
{
|
||||
puts ("Error, not doing CRAM-MD5 authentication");
|
||||
return -1;
|
||||
}
|
||||
resp = cram (cmd, imap->box->user, imap->box->pass);
|
||||
|
||||
socket_write (imap->sock, resp, strlen (resp));
|
||||
if (Verbose)
|
||||
puts (resp);
|
||||
socket_write (imap->sock, "\r\n", 2);
|
||||
free (resp);
|
||||
imap->cram = 0;
|
||||
}
|
||||
#endif
|
||||
else if ((size_t) atol (arg) != Tag)
|
||||
{
|
||||
puts ("wrong tag");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
parse_response_code (imap, cmd);
|
||||
if (!strcmp ("OK", arg))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
/* `box' is the config info for the maildrop to sync. `minuid' is the
|
||||
* minimum UID to consider. in normal mode this will be 1, but in --fast
|
||||
* mode we only fetch messages newer than the last one seen in the local
|
||||
* mailbox.
|
||||
*/
|
||||
imap_t *
|
||||
imap_open (config_t * box, unsigned int minuid, imap_t * imap)
|
||||
{
|
||||
int ret;
|
||||
int s;
|
||||
struct sockaddr_in sin;
|
||||
struct hostent *he;
|
||||
char *ns_prefix = "";
|
||||
int reuse = 0;
|
||||
#if HAVE_LIBSSL
|
||||
int use_ssl = 0;
|
||||
#endif
|
||||
|
||||
if (imap)
|
||||
{
|
||||
/* determine whether or not we can reuse the existing session */
|
||||
if (strcmp (box->host, imap->box->host) ||
|
||||
strcmp (box->user, imap->box->user) ||
|
||||
box->port != imap->box->port
|
||||
#if HAVE_LIBSSL
|
||||
/* ensure that security requirements are met */
|
||||
|| (box->require_ssl ^ imap->box->require_ssl)
|
||||
|| (box->require_cram ^ imap->box->require_cram)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
/* can't reuse */
|
||||
imap_close (imap);
|
||||
imap = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
reuse = 1;
|
||||
/* reset mailbox-specific state info */
|
||||
imap->recent = 0;
|
||||
imap->deleted = 0;
|
||||
imap->count = 0;
|
||||
imap->maxuid = 0;
|
||||
free_message (imap->msgs);
|
||||
imap->msgs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!imap)
|
||||
{
|
||||
imap = calloc (1, sizeof (imap_t));
|
||||
imap->sock = calloc (1, sizeof (Socket_t));
|
||||
imap->buf = calloc (1, sizeof (buffer_t));
|
||||
imap->buf->sock = imap->sock;
|
||||
}
|
||||
|
||||
imap->box = box;
|
||||
imap->minuid = minuid;
|
||||
|
||||
if (!reuse)
|
||||
{
|
||||
/* open connection to IMAP server */
|
||||
|
||||
memset (&sin, 0, sizeof (sin));
|
||||
sin.sin_port = htons (box->port);
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
printf ("Resolving %s... ", box->host);
|
||||
fflush (stdout);
|
||||
he = gethostbyname (box->host);
|
||||
if (!he)
|
||||
{
|
||||
perror ("gethostbyname");
|
||||
return 0;
|
||||
}
|
||||
puts ("ok");
|
||||
|
||||
sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
|
||||
|
||||
s = socket (PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr),
|
||||
ntohs (sin.sin_port));
|
||||
fflush (stdout);
|
||||
if (connect (s, (struct sockaddr *) &sin, sizeof (sin)))
|
||||
{
|
||||
perror ("connect");
|
||||
exit (1);
|
||||
}
|
||||
puts ("ok");
|
||||
|
||||
imap->sock->fd = s;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/* if we are reusing the existing connection, we can skip the
|
||||
* authentication steps.
|
||||
*/
|
||||
if (!reuse)
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
if (!box->use_imaps)
|
||||
{
|
||||
/* let's see what this puppy can do... */
|
||||
if ((ret = imap_exec (imap, "CAPABILITY")))
|
||||
break;
|
||||
|
||||
/* always try to select SSL support if available */
|
||||
if (imap->have_starttls)
|
||||
{
|
||||
if ((ret = imap_exec (imap, "STARTTLS")))
|
||||
break;
|
||||
use_ssl = 1;
|
||||
}
|
||||
|
||||
if (!use_ssl)
|
||||
{
|
||||
if (box->require_ssl)
|
||||
{
|
||||
puts ("Error, SSL support not available");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
puts ("Warning, SSL support not available");
|
||||
}
|
||||
}
|
||||
else
|
||||
use_ssl = 1;
|
||||
|
||||
if (use_ssl)
|
||||
{
|
||||
/* initialize SSL */
|
||||
if (init_ssl (box))
|
||||
{
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
imap->sock->ssl = SSL_new (SSLContext);
|
||||
SSL_set_fd (imap->sock->ssl, imap->sock->fd);
|
||||
ret = SSL_connect (imap->sock->ssl);
|
||||
if (ret <= 0)
|
||||
{
|
||||
ret = SSL_get_error (imap->sock->ssl, ret);
|
||||
printf ("Error, SSL_connect: %s\n",
|
||||
ERR_error_string (ret, 0));
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify the server certificate */
|
||||
if ((ret = verify_cert (imap->sock->ssl)))
|
||||
break;
|
||||
|
||||
imap->sock->use_ssl = 1;
|
||||
puts ("SSL support enabled");
|
||||
|
||||
if (box->use_imaps)
|
||||
if ((ret = imap_exec (imap, "CAPABILITY")))
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if ((ret = imap_exec (imap, "CAPABILITY")))
|
||||
break;
|
||||
#endif
|
||||
|
||||
puts ("Logging in...");
|
||||
#if HAVE_LIBSSL
|
||||
if (imap->have_cram)
|
||||
{
|
||||
puts ("Authenticating with CRAM-MD5");
|
||||
imap->cram = 1;
|
||||
if ((ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5")))
|
||||
break;
|
||||
}
|
||||
else if (imap->box->require_cram)
|
||||
{
|
||||
puts
|
||||
("Error, CRAM-MD5 authentication is not supported by server");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
if (!use_ssl)
|
||||
#endif
|
||||
puts
|
||||
("*** Warning *** Password is being sent in the clear");
|
||||
if (
|
||||
(ret =
|
||||
imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user,
|
||||
box->pass)))
|
||||
break;
|
||||
}
|
||||
|
||||
/* get NAMESPACE info */
|
||||
if (box->use_namespace && imap->have_namespace)
|
||||
{
|
||||
if ((ret = imap_exec (imap, "NAMESPACE")))
|
||||
break;
|
||||
}
|
||||
} /* !reuse */
|
||||
|
||||
/* XXX for now assume personal namespace */
|
||||
if (imap->box->use_namespace && is_list (imap->ns_personal) &&
|
||||
is_list (imap->ns_personal->child) &&
|
||||
is_atom (imap->ns_personal->child->child))
|
||||
{
|
||||
ns_prefix = imap->ns_personal->child->child->val;
|
||||
}
|
||||
|
||||
fputs ("Selecting mailbox... ", stdout);
|
||||
fflush (stdout);
|
||||
if ((ret = imap_exec (imap, "SELECT %s%s", ns_prefix, box->box)))
|
||||
break;
|
||||
printf ("%d messages, %d recent\n", imap->count, imap->recent);
|
||||
|
||||
puts ("Reading IMAP mailbox index");
|
||||
if (imap->count > 0)
|
||||
{
|
||||
if ((ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)",
|
||||
imap->minuid)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (0);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
imap_close (imap);
|
||||
imap = 0;
|
||||
}
|
||||
|
||||
return imap;
|
||||
}
|
||||
|
||||
void
|
||||
imap_close (imap_t * imap)
|
||||
{
|
||||
puts ("Closing IMAP connection");
|
||||
imap_exec (imap, "LOGOUT");
|
||||
close (imap->sock->fd);
|
||||
free (imap->sock);
|
||||
free (imap->buf);
|
||||
free_message (imap->msgs);
|
||||
memset (imap, 0xff, sizeof (imap_t));
|
||||
free (imap);
|
||||
}
|
||||
|
||||
/* write a buffer stripping all \r bytes */
|
||||
static int
|
||||
write_strip (int fd, char *buf, size_t len)
|
||||
{
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
|
||||
while (start < len)
|
||||
{
|
||||
while (end < len && buf[end] != '\r')
|
||||
end++;
|
||||
write (fd, buf + start, end - start);
|
||||
end++;
|
||||
start = end;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_server (Socket_t * sock, const char *fmt, ...)
|
||||
{
|
||||
char buf[128];
|
||||
char cmd[128];
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
vsnprintf (buf, sizeof (buf), fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
|
||||
socket_write (sock, cmd, strlen (cmd));
|
||||
|
||||
if (Verbose)
|
||||
fputs (cmd, stdout);
|
||||
}
|
||||
|
||||
int
|
||||
imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
|
||||
{
|
||||
char *cmd;
|
||||
char *arg;
|
||||
size_t bytes;
|
||||
size_t n;
|
||||
char buf[1024];
|
||||
|
||||
send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (buffer_gets (imap->buf, &cmd))
|
||||
return -1;
|
||||
|
||||
if (Verbose)
|
||||
puts (cmd);
|
||||
|
||||
if (*cmd == '*')
|
||||
{
|
||||
/* need to figure out how long the message is
|
||||
* * <msgno> FETCH (RFC822 {<size>}
|
||||
*/
|
||||
|
||||
next_arg (&cmd); /* * */
|
||||
next_arg (&cmd); /* <msgno> */
|
||||
next_arg (&cmd); /* FETCH */
|
||||
|
||||
while ((arg = next_arg (&cmd)) && *arg != '{')
|
||||
;
|
||||
if (!arg)
|
||||
{
|
||||
puts ("parse error getting size");
|
||||
return -1;
|
||||
}
|
||||
bytes = strtol (arg + 1, 0, 10);
|
||||
// printf ("receiving %d byte message\n", bytes);
|
||||
|
||||
/* dump whats left over in the input buffer */
|
||||
n = imap->buf->bytes - imap->buf->offset;
|
||||
|
||||
if (n > bytes)
|
||||
{
|
||||
/* the entire message fit in the buffer */
|
||||
n = bytes;
|
||||
}
|
||||
|
||||
/* ick. we have to strip out the \r\n line endings, so
|
||||
* i can't just dump the raw bytes to disk.
|
||||
*/
|
||||
write_strip (fd, imap->buf->buf + imap->buf->offset, n);
|
||||
|
||||
bytes -= n;
|
||||
|
||||
// printf ("wrote %d buffered bytes\n", n);
|
||||
|
||||
/* mark that we used part of the buffer */
|
||||
imap->buf->offset += n;
|
||||
|
||||
/* now read the rest of the message */
|
||||
while (bytes > 0)
|
||||
{
|
||||
n = bytes;
|
||||
if (n > sizeof (buf))
|
||||
n = sizeof (buf);
|
||||
n = socket_read (imap->sock, buf, n);
|
||||
if (n > 0)
|
||||
{
|
||||
// printf("imap_fetch_message:%d:read %d bytes\n", __LINE__, n);
|
||||
write_strip (fd, buf, n);
|
||||
bytes -= n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n == (size_t) - 1)
|
||||
perror ("read");
|
||||
else
|
||||
puts ("EOF");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// puts ("finished fetching msg");
|
||||
|
||||
buffer_gets (imap->buf, &cmd);
|
||||
if (Verbose)
|
||||
puts (cmd); /* last part of line */
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
if (!arg || (size_t) atoi (arg) != Tag)
|
||||
{
|
||||
puts ("wrong tag");
|
||||
return -1;
|
||||
}
|
||||
arg = next_arg (&cmd);
|
||||
if (!strcmp ("OK", arg))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
/* add flags to existing flags */
|
||||
int
|
||||
imap_set_flags (imap_t * imap, unsigned int uid, unsigned int flags)
|
||||
{
|
||||
char buf[256];
|
||||
int i;
|
||||
|
||||
buf[0] = 0;
|
||||
for (i = 0; i < D_MAX; i++)
|
||||
{
|
||||
if (flags & (1 << i))
|
||||
snprintf (buf + strlen (buf),
|
||||
sizeof (buf) - strlen (buf), "%s%s",
|
||||
(buf[0] != 0) ? " " : "", Flags[i]);
|
||||
}
|
||||
|
||||
return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
|
||||
}
|
||||
|
||||
int
|
||||
imap_expunge (imap_t * imap)
|
||||
{
|
||||
return imap_exec (imap, "EXPUNGE");
|
||||
}
|
264
isync.1
264
isync.1
|
@ -1,264 +0,0 @@
|
|||
.ig
|
||||
\" isync - IMAP4 to maildir mailbox synchronizer
|
||||
\" Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
\"
|
||||
\" 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, write to the Free Software
|
||||
\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
..
|
||||
.TH isync 1 "2000 Dec 27"
|
||||
..
|
||||
.SH NAME
|
||||
isync - synchronize IMAP4 and maildir mailboxes
|
||||
..
|
||||
.SH SYNOPSIS
|
||||
.B isync
|
||||
[
|
||||
.I options...
|
||||
]
|
||||
.I mailbox
|
||||
[
|
||||
.I mailbox ...
|
||||
]
|
||||
..
|
||||
.SH DESCRIPTION
|
||||
.B isync
|
||||
is a command line application which synchronizes a local maildir-style
|
||||
mailbox with a remote IMAP4 mailbox, suitable for use in IMAP-disconnected
|
||||
mode. Multiple copies of the remote IMAP4 mailbox can be maintained, and
|
||||
all flags are synchronized.
|
||||
..
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-c\fR, \fB--config\fR \fIfile\fR
|
||||
Read configuration from
|
||||
.I file
|
||||
By default, configuration is read from ~/.isyncrc if it exists.
|
||||
.TP
|
||||
.B -d, --delete
|
||||
Causes
|
||||
.B isync
|
||||
to delete messages from the local maildir mailbox which do not exist on the
|
||||
IMAP server. By default,
|
||||
.I dead
|
||||
messages are
|
||||
.B not
|
||||
deleted.
|
||||
.TP
|
||||
.B -e, --expunge
|
||||
Causes
|
||||
.B isync
|
||||
to permanently remove all messages marked for deletion in both the local
|
||||
maildir mailbox and the remote IMAP mailbox. By default, messages are
|
||||
.B not
|
||||
expunged.
|
||||
.TP
|
||||
.B -f, --fast
|
||||
Causes
|
||||
.B isync
|
||||
to skip the step of synchronzing message flags between the local maildir
|
||||
mailbox and the IMAP mailbox. Only new messages existing on the server will
|
||||
be fetched into the local mailbox.
|
||||
.TP
|
||||
.B -h, --help
|
||||
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)
|
||||
.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 \fB[\fRimaps:\fB]\fR\fIhost\fR
|
||||
.P
|
||||
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
|
||||
.B -v, --version
|
||||
Displays
|
||||
.B isync
|
||||
version information
|
||||
.TP
|
||||
.B -V, --verbose
|
||||
Enables
|
||||
.I verbose
|
||||
mode, which displays the IMAP4 network traffic.
|
||||
..
|
||||
.SH CONFIGURATION
|
||||
.B isync
|
||||
reads
|
||||
.I ~/.isyncrc
|
||||
to load default configuration data. Each line of the configuration file
|
||||
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
|
||||
.I Mailbox
|
||||
command, apply to this mailbox only.
|
||||
..
|
||||
.TP
|
||||
\fBHost\fR \fB[\fRimaps:\fB]\fR\fIname\fR
|
||||
Defines the DNS name or IP address of the IMAP server. If the hostname is
|
||||
prefixed with
|
||||
.I imaps:
|
||||
the connection is assumed to be a SSL connection to port 993 (though you can
|
||||
change this by placing a
|
||||
.B Port
|
||||
command
|
||||
.B after
|
||||
the
|
||||
.B Host
|
||||
command. Note that some servers support SSL on the default port 143.
|
||||
.B isync
|
||||
will always attempt to use SSL if available.
|
||||
..
|
||||
.TP
|
||||
\fBPort\fR \fIport\fR
|
||||
Defines the TCP port number on the IMAP server to use (Default: 143)
|
||||
..
|
||||
.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
|
||||
.I username
|
||||
on the IMAP server. Note that this option is
|
||||
.B NOT
|
||||
required. If no password is specified in the configuration file,
|
||||
.B isync
|
||||
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
|
||||
\fBMaxSize\fR \fIbytes\fR
|
||||
Sets a threshold for the maximum message size (in bytes) for which
|
||||
.B isync
|
||||
should fetch from the server. This is useful for weeding out messages with
|
||||
large attachments. If
|
||||
.I bytes
|
||||
is 0, the maximum file size is
|
||||
.B unlimited.
|
||||
..
|
||||
.TP
|
||||
\fBUseNamespace\fR \fIyes|no\fR
|
||||
Selects whether
|
||||
.B isync
|
||||
should select mailboxes using the namespace given by the NAMESPACE command.
|
||||
This is useful with broken IMAP servers. (Default:
|
||||
.I yes
|
||||
)
|
||||
..
|
||||
.TP
|
||||
\fBRequireCRAM\fR \fIyes|no\fR
|
||||
If set to
|
||||
.I yes
|
||||
,
|
||||
.B isync
|
||||
will require that the server accept CRAM-MD5 intead of PLAIN to authenticate
|
||||
the user.
|
||||
..
|
||||
.TP
|
||||
\fBRequireSSL\fR \fIyes|no\fR
|
||||
.B isync
|
||||
will abort the connection if a TLS/SSL session to the IMAP
|
||||
server can not be established. (Default:
|
||||
.I yes
|
||||
)
|
||||
..
|
||||
.TP
|
||||
\fBCertificateFile\fR \fIpath\fR
|
||||
File containing X.509 CA certificates used to verify server identities.
|
||||
..
|
||||
.TP
|
||||
\fBUseSSLv2\fR \fIyes|no\fR
|
||||
Should
|
||||
.B isync
|
||||
use SSLv2 for communication with the IMAP server over SSL? (Default:
|
||||
.I yes
|
||||
)
|
||||
..
|
||||
.TP
|
||||
\fBUseSSLv3\fR \fIyes|no\fR
|
||||
Should
|
||||
.B isync
|
||||
use SSLv3 for communication with the IMAP server over SSL? (Default:
|
||||
.I yes
|
||||
)
|
||||
..
|
||||
.TP
|
||||
\fBUseTLSv1\fR \fIyes|no\fR
|
||||
Should
|
||||
.B isync
|
||||
use TLSv1 for communication with the IMAP server over SSL? (Default:
|
||||
.I yes
|
||||
)
|
||||
..
|
||||
.P
|
||||
Configuration commands that appear prior to the first
|
||||
.B Mailbox
|
||||
command are considered to be
|
||||
.I global
|
||||
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
|
||||
.B User
|
||||
command before the first
|
||||
.B Mailbox
|
||||
command, and then leave out the
|
||||
.B User
|
||||
command in the sections for each mailbox.
|
||||
.B isync
|
||||
will then use the global value by default.
|
||||
..
|
||||
.SH FILES
|
||||
.TP
|
||||
.B ~/.isyncrc
|
||||
Default configuration file
|
||||
..
|
||||
.SH BUGS
|
||||
maildir(5) states that readers should not attempt to parse the filename of a
|
||||
a message other than the :info field. However, since
|
||||
.B isync
|
||||
relies on using the message UIDs that info must be inserted into the
|
||||
filename in a way which will be interoperable with existing readers. So
|
||||
the UID is placed in the filename of the messages in the local maildir
|
||||
mailbox rather than the :info field.
|
||||
.P
|
||||
When synchronizing multiple mailboxes on the same IMAP server, it is not
|
||||
possible to select different SSL options for each mailbox. Only the options
|
||||
from the first mailbox are applied since the SSL session is reused.
|
||||
.SH SEE ALSO
|
||||
mutt(1), maildir(5)
|
||||
.P
|
||||
Up to date information on
|
||||
.B isync
|
||||
can be found at
|
||||
http://www.sigpipe.org/isync/.
|
||||
..
|
||||
.SH AUTHOR
|
||||
Written by Michael R. Elkins <me@mutt.org>.
|
182
isync.h
182
isync.h
|
@ -1,182 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#if HAVE_LIBSSL
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
#if HAVE_LIBSSL
|
||||
SSL *ssl;
|
||||
unsigned int use_ssl:1;
|
||||
#endif
|
||||
} Socket_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Socket_t *sock;
|
||||
char buf[1024];
|
||||
int bytes;
|
||||
int offset;
|
||||
}
|
||||
buffer_t;
|
||||
|
||||
typedef struct config config_t;
|
||||
typedef struct mailbox mailbox_t;
|
||||
typedef struct message message_t;
|
||||
|
||||
struct config
|
||||
{
|
||||
char *path;
|
||||
char *host;
|
||||
int port;
|
||||
char *user;
|
||||
char *pass;
|
||||
char *box;
|
||||
char *alias;
|
||||
unsigned int max_size;
|
||||
config_t *next;
|
||||
#if HAVE_LIBSSL
|
||||
char *cert_file;
|
||||
unsigned int use_imaps:1;
|
||||
unsigned int require_ssl:1;
|
||||
unsigned int use_sslv2:1;
|
||||
unsigned int use_sslv3:1;
|
||||
unsigned int use_tlsv1:1;
|
||||
unsigned int require_cram:1;
|
||||
#endif
|
||||
unsigned int use_namespace:1;
|
||||
};
|
||||
|
||||
/* struct representing local mailbox file */
|
||||
struct mailbox
|
||||
{
|
||||
char *path;
|
||||
message_t *msgs;
|
||||
unsigned int deleted; /* # of deleted messages */
|
||||
unsigned int uidvalidity;
|
||||
unsigned int maxuid; /* largest uid we know about */
|
||||
unsigned int changed:1;
|
||||
unsigned int maxuidchanged:1;
|
||||
};
|
||||
|
||||
/* message dispositions */
|
||||
#define D_SEEN (1<<0)
|
||||
#define D_ANSWERED (1<<1)
|
||||
#define D_DELETED (1<<2)
|
||||
#define D_FLAGGED (1<<3)
|
||||
#define D_RECENT (1<<4)
|
||||
#define D_DRAFT (1<<5)
|
||||
#define D_MAX 6
|
||||
|
||||
struct message
|
||||
{
|
||||
char *file;
|
||||
unsigned int uid;
|
||||
unsigned int flags;
|
||||
unsigned int size;
|
||||
message_t *next;
|
||||
unsigned int processed:1; /* message has already been evaluated */
|
||||
unsigned int new:1; /* message is in the new/ subdir */
|
||||
unsigned int changed:1; /* flags changed */
|
||||
unsigned int dead:1; /* message doesn't exist on the server */
|
||||
};
|
||||
|
||||
/* struct used for parsing IMAP lists */
|
||||
typedef struct _list list_t;
|
||||
|
||||
#define NIL (void*)0x1
|
||||
#define LIST (void*)0x2
|
||||
|
||||
struct _list {
|
||||
char *val;
|
||||
list_t *next;
|
||||
list_t *child;
|
||||
};
|
||||
|
||||
/* imap connection info */
|
||||
typedef struct
|
||||
{
|
||||
Socket_t *sock;
|
||||
unsigned int count; /* # of msgs */
|
||||
unsigned int recent; /* # of recent messages */
|
||||
buffer_t *buf; /* input buffer for reading server output */
|
||||
message_t *msgs; /* list of messages on the server */
|
||||
config_t *box; /* mailbox to open */
|
||||
unsigned int deleted; /* # of deleted messages */
|
||||
unsigned int uidvalidity;
|
||||
unsigned int maxuid;
|
||||
unsigned int minuid;
|
||||
/* NAMESPACE info */
|
||||
list_t *ns_personal;
|
||||
list_t *ns_other;
|
||||
list_t *ns_shared;
|
||||
unsigned int have_namespace:1;
|
||||
#if HAVE_LIBSSL
|
||||
unsigned int have_cram:1;
|
||||
unsigned int have_starttls:1;
|
||||
unsigned int cram:1;
|
||||
#endif
|
||||
}
|
||||
imap_t;
|
||||
|
||||
/* flags for sync_mailbox */
|
||||
#define SYNC_DELETE (1<<0) /* delete local that don't exist on server */
|
||||
#define SYNC_EXPUNGE (1<<1) /* don't fetch deleted messages */
|
||||
|
||||
extern config_t global;
|
||||
extern unsigned int Tag;
|
||||
extern char Hostname[256];
|
||||
extern int Verbose;
|
||||
|
||||
#if HAVE_LIBSSL
|
||||
extern SSL_CTX *SSLContext;
|
||||
|
||||
char *cram (const char *, const char *, const char *);
|
||||
#endif
|
||||
|
||||
char *next_arg (char **);
|
||||
|
||||
int sync_mailbox (mailbox_t *, imap_t *, int, unsigned int);
|
||||
|
||||
void imap_close (imap_t *);
|
||||
int imap_fetch_message (imap_t *, unsigned int, int);
|
||||
int imap_set_flags (imap_t *, unsigned int, unsigned int);
|
||||
int imap_expunge (imap_t *);
|
||||
imap_t *imap_open (config_t *, unsigned int, imap_t *);
|
||||
|
||||
mailbox_t *maildir_open (const char *, int fast);
|
||||
int maildir_expunge (mailbox_t *, int);
|
||||
int maildir_sync (mailbox_t *);
|
||||
int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity);
|
||||
void maildir_close (mailbox_t *);
|
||||
|
||||
message_t * find_msg (message_t * list, unsigned int uid);
|
||||
void free_message (message_t *);
|
||||
|
||||
/* parse an IMAP list construct */
|
||||
list_t * parse_list (char *s, char **end);
|
||||
int is_atom (list_t *list);
|
||||
int is_list (list_t *list);
|
||||
int is_nil (list_t *list);
|
||||
void free_list (list_t *list);
|
|
@ -1,33 +1,35 @@
|
|||
Summary: Utility to synchronize IMAP mailboxes with local maildir folders
|
||||
Name: isync
|
||||
Version: 0.4
|
||||
Version: @VERSION@
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
License: GPL
|
||||
Group: Applications/Internet
|
||||
Source: http://www.sigpipe.org/isync/isync-0.4.tar.gz
|
||||
URL: http://www.sigpipe.org/isync/
|
||||
Packager: Michael Elkins <me@mutt.org>
|
||||
Source: @PACKAGE@-@VERSION@.tar.gz
|
||||
URL: http://@PACKAGE@.sf.net/
|
||||
Packager: Oswald Buddenhagen <ossi@users.sf.net>
|
||||
BuildRoot: /var/tmp/%{name}-buildroot
|
||||
|
||||
%description
|
||||
isync is a command line utility for synchronizing a remote IMAP mailbox with a
|
||||
local maildir-style mailbox. This is useful for working in disconnected mode,
|
||||
such as on a laptop. Modifications made locally and remotely are synchronized
|
||||
so that no message status flags are lost.
|
||||
isync is a command line utility which synchronizes mailboxes; currently
|
||||
Maildir and IMAP4 mailboxes are supported.
|
||||
New messages, message deletions and flag changes can be propagated both ways.
|
||||
It is useful for working in disconnected mode, such as on a laptop or with a
|
||||
non-permanent internet collection (dIMAP).
|
||||
|
||||
%prep
|
||||
%setup
|
||||
%build
|
||||
./configure --prefix=/usr
|
||||
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS"
|
||||
%configure
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make DESTDIR=$RPM_BUILD_ROOT install
|
||||
rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name}
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%doc COPYING README TODO ChangeLog
|
||||
/usr/bin/isync
|
||||
/usr/man/man1/isync.1.bz2
|
||||
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample
|
||||
%{_bindir}/*
|
||||
%{_mandir}/man1/*
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# Global configuration section
|
||||
# Values here are used as defaults for any following Mailbox section that
|
||||
# doesn't specify it.
|
||||
|
||||
# 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
|
||||
|
||||
###
|
||||
### personal mailbox
|
||||
###
|
||||
|
||||
Mailbox /home/me/Mail/personal
|
||||
Host host.play.com
|
||||
# use a non-default port for this connection
|
||||
Port 6789
|
||||
Alias personal
|
175
list.c
175
list.c
|
@ -1,175 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "isync.h"
|
||||
|
||||
static char *
|
||||
skip_string (char *s)
|
||||
{
|
||||
while (*s && *s != '"')
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
list_t *
|
||||
parse_list (char *s, char **end)
|
||||
{
|
||||
int level = 1;
|
||||
list_t *cur;
|
||||
list_t **list;
|
||||
char *b;
|
||||
|
||||
cur = calloc (1, sizeof (list_t));
|
||||
while (isspace ((unsigned char) *s))
|
||||
s++;
|
||||
if (*s == '(')
|
||||
{
|
||||
/* start of list. find the end of the list */
|
||||
s++;
|
||||
b = s; /* save beginning */
|
||||
cur->val = LIST;
|
||||
while (*s)
|
||||
{
|
||||
if (*s == '(')
|
||||
{
|
||||
level++;
|
||||
}
|
||||
else if (*s == ')')
|
||||
{
|
||||
level--;
|
||||
if (level == 0)
|
||||
break;
|
||||
}
|
||||
else if (*s == '"')
|
||||
{
|
||||
s = skip_string (s + 1);
|
||||
if (!*s)
|
||||
{
|
||||
/* parse error */
|
||||
free (cur);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (level != 0)
|
||||
{
|
||||
free (cur); /* parse error */
|
||||
return NULL;
|
||||
}
|
||||
*s++ = 0;
|
||||
|
||||
list = &cur->child;
|
||||
while (*b)
|
||||
{
|
||||
*list = parse_list (b, &b);
|
||||
if (*list == NULL)
|
||||
{
|
||||
/* parse error */
|
||||
free (cur);
|
||||
return NULL;
|
||||
}
|
||||
while (*list)
|
||||
list = &(*list)->next;
|
||||
}
|
||||
}
|
||||
else if (*s == '"')
|
||||
{
|
||||
/* quoted string */
|
||||
s++;
|
||||
cur->val = s;
|
||||
s = skip_string (s);
|
||||
if (!*s)
|
||||
{
|
||||
/* parse error */
|
||||
free (cur);
|
||||
return NULL;
|
||||
}
|
||||
*s++ = 0;
|
||||
cur->val = strdup (cur->val);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* atom */
|
||||
cur->val = s;
|
||||
while (*s && !isspace ((unsigned char) *s))
|
||||
s++;
|
||||
if (*s)
|
||||
*s++ = 0;
|
||||
if (strcmp ("NIL", cur->val))
|
||||
cur->val = strdup (cur->val);
|
||||
else
|
||||
cur->val = NIL;
|
||||
}
|
||||
if (end)
|
||||
*end = s;
|
||||
return cur;
|
||||
}
|
||||
|
||||
int
|
||||
is_atom (list_t * list)
|
||||
{
|
||||
return (list && list->val && list->val != NIL && list->val != LIST);
|
||||
}
|
||||
|
||||
int
|
||||
is_list (list_t * list)
|
||||
{
|
||||
return (list && list->val == LIST);
|
||||
}
|
||||
|
||||
int
|
||||
is_nil (list_t * list)
|
||||
{
|
||||
return (list && list->val == NIL);
|
||||
}
|
||||
|
||||
void
|
||||
free_list (list_t * list)
|
||||
{
|
||||
list_t *tmp;
|
||||
|
||||
while (list)
|
||||
{
|
||||
tmp = list;
|
||||
list = list->next;
|
||||
if (is_list (list))
|
||||
free_list (tmp->child);
|
||||
else if (is_atom (tmp))
|
||||
free (tmp->val);
|
||||
free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#if TEST
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
char buf[256];
|
||||
list_t *list;
|
||||
|
||||
strcpy (buf,
|
||||
"((compound list) atom NIL \"string with a (\" (another list))");
|
||||
list = parse_list (buf, 0);
|
||||
}
|
||||
#endif
|
421
maildir.c
421
maildir.c
|
@ -1,421 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#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 <errno.h>
|
||||
#include "isync.h"
|
||||
|
||||
static int
|
||||
do_lock (int fd, int flag)
|
||||
{
|
||||
struct flock lck;
|
||||
struct stat sb;
|
||||
|
||||
if (fstat (fd, &sb))
|
||||
{
|
||||
perror ("fstat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset (&lck, 0, sizeof (lck));
|
||||
lck.l_type = flag;
|
||||
lck.l_whence = SEEK_SET;
|
||||
lck.l_start = 0;
|
||||
lck.l_len = sb.st_size;
|
||||
|
||||
if (fcntl (fd, F_SETLK, &lck))
|
||||
{
|
||||
perror ("fcntl");
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2,<flags> */
|
||||
static void
|
||||
parse_info (message_t * m, char *s)
|
||||
{
|
||||
if (*s == '2' && *(s + 1) == ',')
|
||||
{
|
||||
s += 2;
|
||||
while (*s)
|
||||
{
|
||||
if (*s == 'F')
|
||||
m->flags |= D_FLAGGED;
|
||||
else if (*s == 'R')
|
||||
m->flags |= D_ANSWERED;
|
||||
else if (*s == 'T')
|
||||
m->flags |= D_DELETED;
|
||||
else if (*s == 'S')
|
||||
m->flags |= D_SEEN;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
read_uid (const char *path, const char *file)
|
||||
{
|
||||
char full[_POSIX_PATH_MAX];
|
||||
int fd;
|
||||
int ret;
|
||||
int len;
|
||||
char buf[64];
|
||||
unsigned int uid = 0;
|
||||
|
||||
snprintf (full, sizeof (full), "%s/%s", path, file);
|
||||
fd = open (full, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
perror ("open");
|
||||
return -1;
|
||||
}
|
||||
return 0; /* doesn't exist */
|
||||
}
|
||||
ret = do_lock (fd, F_RDLCK);
|
||||
if (!ret)
|
||||
{
|
||||
len = read (fd, buf, sizeof (buf) - 1);
|
||||
if (len == -1)
|
||||
ret = -1;
|
||||
else
|
||||
{
|
||||
buf[len] = 0;
|
||||
uid = atol (buf);
|
||||
}
|
||||
}
|
||||
ret |= do_lock (fd, F_UNLCK);
|
||||
close (fd);
|
||||
return ret ? ret : uid;
|
||||
|
||||
}
|
||||
|
||||
/* open a maildir mailbox. if `fast' is nonzero, we just check to make
|
||||
* sure its a valid mailbox and don't actually parse it. any IMAP messages
|
||||
* with the \Recent flag set are guaranteed not to be in the mailbox yet,
|
||||
* so we can save a lot of time when the user just wants to fetch new messages
|
||||
* without syncing the flags.
|
||||
*/
|
||||
mailbox_t *
|
||||
maildir_open (const char *path, int fast)
|
||||
{
|
||||
char buf[_POSIX_PATH_MAX];
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
message_t **cur;
|
||||
message_t *p;
|
||||
mailbox_t *m;
|
||||
char *s;
|
||||
int count = 0;
|
||||
|
||||
/* check to make sure this looks like a valid maildir box */
|
||||
snprintf (buf, sizeof (buf), "%s/new", path);
|
||||
if (access (buf, F_OK))
|
||||
{
|
||||
perror ("access");
|
||||
return 0;
|
||||
}
|
||||
snprintf (buf, sizeof (buf), "%s/cur", path);
|
||||
if (access (buf, F_OK))
|
||||
{
|
||||
perror ("access");
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = calloc (1, sizeof (mailbox_t));
|
||||
m->path = strdup (path);
|
||||
|
||||
/* check for the uidvalidity value */
|
||||
m->uidvalidity = read_uid (path, "isyncuidvalidity");
|
||||
if (m->uidvalidity == (unsigned int) -1)
|
||||
{
|
||||
free (m->path);
|
||||
free (m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load the current maxuid */
|
||||
if ((m->maxuid = read_uid (path, "isyncmaxuid")) == (unsigned int) -1)
|
||||
{
|
||||
free (m->path);
|
||||
free (m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fast)
|
||||
return m;
|
||||
|
||||
cur = &m->msgs;
|
||||
for (; count < 2; count++)
|
||||
{
|
||||
/* read the msgs from the new subdir */
|
||||
snprintf (buf, sizeof (buf), "%s/%s", path,
|
||||
(count == 0) ? "new" : "cur");
|
||||
d = opendir (buf);
|
||||
if (!d)
|
||||
{
|
||||
free (m->path);
|
||||
free (m);
|
||||
perror ("opendir");
|
||||
return 0;
|
||||
}
|
||||
while ((e = readdir (d)))
|
||||
{
|
||||
if (*e->d_name == '.')
|
||||
continue; /* skip dot-files */
|
||||
*cur = calloc (1, sizeof (message_t));
|
||||
p = *cur;
|
||||
p->file = strdup (e->d_name);
|
||||
p->uid = -1;
|
||||
p->flags = (count == 1) ? D_SEEN : 0;
|
||||
p->new = (count == 0);
|
||||
|
||||
/* filename format is something like:
|
||||
* <unique-prefix>.UID<n>:2,<flags>
|
||||
* This is completely non-standard, but in order for mail
|
||||
* clients to understand the flags, we have to use the
|
||||
* standard :info as described by the qmail spec
|
||||
*/
|
||||
s = strstr (p->file, "UID");
|
||||
if (!s)
|
||||
puts ("Warning, no uid for message");
|
||||
else
|
||||
{
|
||||
p->uid = strtol (s + 3, &s, 10);
|
||||
if (p->uid > m->maxuid)
|
||||
{
|
||||
m->maxuid = p->uid;
|
||||
m->maxuidchanged = 1;
|
||||
}
|
||||
if (*s && *s != ':')
|
||||
{
|
||||
puts ("warning, unable to parse uid");
|
||||
p->uid = -1; /* reset */
|
||||
}
|
||||
}
|
||||
|
||||
s = strchr (p->file, ':');
|
||||
if (s)
|
||||
parse_info (p, s + 1);
|
||||
if (p->flags & D_DELETED)
|
||||
m->deleted++;
|
||||
cur = &p->next;
|
||||
}
|
||||
closedir (d);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* permanently remove messages from a maildir mailbox. if `dead' is nonzero,
|
||||
* we only remove the messags marked dead.
|
||||
*/
|
||||
int
|
||||
maildir_expunge (mailbox_t * mbox, int dead)
|
||||
{
|
||||
message_t **cur = &mbox->msgs;
|
||||
message_t *tmp;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
|
||||
while (*cur)
|
||||
{
|
||||
if ((dead == 0 && (*cur)->flags & D_DELETED) ||
|
||||
(dead && (*cur)->dead))
|
||||
{
|
||||
tmp = *cur;
|
||||
*cur = (*cur)->next;
|
||||
snprintf (path, sizeof (path), "%s/%s/%s",
|
||||
mbox->path, tmp->new ? "new" : "cur", tmp->file);
|
||||
if (unlink (path))
|
||||
perror ("unlink");
|
||||
free (tmp->file);
|
||||
free (tmp);
|
||||
}
|
||||
else
|
||||
cur = &(*cur)->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
update_maxuid (mailbox_t * mbox)
|
||||
{
|
||||
int fd;
|
||||
char buf[64];
|
||||
size_t len;
|
||||
unsigned int uid;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
int ret = 0;
|
||||
|
||||
snprintf (path, sizeof (path), "%s/isyncmaxuid", mbox->path);
|
||||
fd = open (path, O_RDWR | O_CREAT, 0600);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror ("open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* lock the file */
|
||||
if (do_lock (fd, F_WRLCK))
|
||||
{
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read the file again just to make sure it wasn't updated while
|
||||
* we were doing something else
|
||||
*/
|
||||
len = read (fd, buf, sizeof (buf) - 1);
|
||||
buf[len] = 0;
|
||||
uid = atol (buf);
|
||||
if (uid > mbox->maxuid)
|
||||
{
|
||||
puts ("Error, maxuid is now higher (fatal)");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
/* rewind */
|
||||
lseek (fd, 0, SEEK_SET);
|
||||
|
||||
/* write out the file */
|
||||
snprintf (buf, sizeof (buf), "%u\n", mbox->maxuid);
|
||||
len = write (fd, buf, strlen (buf));
|
||||
if (len == (size_t) - 1)
|
||||
{
|
||||
perror ("write");
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ftruncate (fd, len);
|
||||
if (ret)
|
||||
perror ("ftruncate");
|
||||
}
|
||||
}
|
||||
|
||||
ret |= do_lock (fd, F_UNLCK);
|
||||
ret |= close (fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
maildir_sync (mailbox_t * mbox)
|
||||
{
|
||||
message_t *cur = mbox->msgs;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char oldpath[_POSIX_PATH_MAX];
|
||||
char *p;
|
||||
int ret = 0;
|
||||
|
||||
if (mbox->changed)
|
||||
{
|
||||
for (; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->changed)
|
||||
{
|
||||
/* generate old path */
|
||||
snprintf (oldpath, sizeof (oldpath), "%s/%s/%s",
|
||||
mbox->path, cur->new ? "new" : "cur", cur->file);
|
||||
|
||||
/* truncate old flags (if present) */
|
||||
p = strchr (cur->file, ':');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
/* generate new path */
|
||||
snprintf (path, sizeof (path), "%s/%s/%s:2,%s%s%s%s",
|
||||
mbox->path, (cur->flags & D_SEEN) ? "cur" : "new",
|
||||
cur->file, (cur->flags & D_FLAGGED) ? "F" : "",
|
||||
(cur->flags & D_ANSWERED) ? "R" : "",
|
||||
(cur->flags & D_SEEN) ? "S" : "",
|
||||
(cur->flags & D_DELETED) ? "T" : "");
|
||||
|
||||
if (rename (oldpath, path))
|
||||
perror ("rename");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mbox->maxuidchanged)
|
||||
ret = update_maxuid (mbox);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
maildir_set_uidvalidity (mailbox_t * mbox, unsigned int uidvalidity)
|
||||
{
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char buf[16];
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
snprintf (path, sizeof (path), "%s/isyncuidvalidity", mbox->path);
|
||||
fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror ("open");
|
||||
return -1;
|
||||
}
|
||||
snprintf (buf, sizeof (buf), "%u\n", uidvalidity);
|
||||
|
||||
ret = write (fd, buf, strlen (buf));
|
||||
|
||||
if (ret == -1)
|
||||
perror ("write");
|
||||
else if ((size_t) ret != strlen (buf))
|
||||
ret = -1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
if (close (fd))
|
||||
{
|
||||
perror ("close");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
if (unlink (path))
|
||||
perror ("unlink");
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
maildir_close (mailbox_t * mbox)
|
||||
{
|
||||
free (mbox->path);
|
||||
free_message (mbox->msgs);
|
||||
memset (mbox, 0xff, sizeof (mailbox_t));
|
||||
free (mbox);
|
||||
}
|
476
main.c
476
main.c
|
@ -1,476 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "isync.h"
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
struct option Opts[] = {
|
||||
{"config", 1, NULL, 'c'},
|
||||
{"delete", 0, NULL, 'd'},
|
||||
{"expunge", 0, NULL, 'e'},
|
||||
{"fast", 0, NULL, 'f'},
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"remote", 1, NULL, 'r'},
|
||||
{"host", 1, NULL, 's'},
|
||||
{"port", 1, NULL, 'p'},
|
||||
{"user", 1, NULL, 'u'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{"verbose", 0, NULL, 'V'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
#endif
|
||||
|
||||
config_t global;
|
||||
unsigned int Tag = 0;
|
||||
static config_t *box = 0;
|
||||
char Hostname[256];
|
||||
int Verbose = 0;
|
||||
|
||||
static void
|
||||
version (void)
|
||||
{
|
||||
printf ("%s %s\n", PACKAGE, VERSION);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage (void)
|
||||
{
|
||||
printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION);
|
||||
puts ("Copyright (C) 2000 Michael R. Elkins <me@mutt.org>");
|
||||
printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE);
|
||||
puts
|
||||
(" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)");
|
||||
puts
|
||||
(" -d, --delete delete local msgs that don't exist on the server");
|
||||
puts
|
||||
(" -e, --expunge expunge deleted messages from the server");
|
||||
puts (" -f, --fast only fetch new messages");
|
||||
puts (" -h, --help display this help message");
|
||||
puts (" -p, --port PORT server IMAP port");
|
||||
puts (" -r, --remote BOX remote mailbox");
|
||||
puts (" -s, --host HOST IMAP server address");
|
||||
puts (" -u, --user USER IMAP user name");
|
||||
puts (" -v, --version display version");
|
||||
puts
|
||||
(" -V, --verbose verbose mode (display network traffic)");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
/* set defaults from the global configuration section */
|
||||
static void
|
||||
config_defaults (config_t * conf)
|
||||
{
|
||||
conf->user = global.user;
|
||||
conf->pass = global.pass;
|
||||
conf->port = global.port;
|
||||
conf->box = global.box;
|
||||
conf->host = global.host;
|
||||
conf->max_size = global.max_size;
|
||||
conf->use_namespace = global.use_namespace;
|
||||
#if HAVE_LIBSSL
|
||||
conf->require_ssl = global.require_ssl;
|
||||
conf->use_imaps = global.use_imaps;
|
||||
conf->cert_file = global.cert_file;
|
||||
conf->use_sslv2 = global.use_sslv2;
|
||||
conf->use_sslv3 = global.use_sslv3;
|
||||
conf->use_tlsv1 = global.use_tlsv1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
load_config (char *where)
|
||||
{
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char buf[1024];
|
||||
struct passwd *pw;
|
||||
config_t **cur = &box;
|
||||
int line = 0;
|
||||
FILE *fp;
|
||||
char *p, *cmd, *val;
|
||||
|
||||
if (!where)
|
||||
{
|
||||
pw = getpwuid (getuid ());
|
||||
snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
|
||||
where = path;
|
||||
}
|
||||
printf ("Reading %s\n", where);
|
||||
|
||||
fp = fopen (where, "r");
|
||||
if (!fp)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
perror ("fopen");
|
||||
return;
|
||||
}
|
||||
}
|
||||
buf[sizeof buf - 1] = 0;
|
||||
while ((fgets (buf, sizeof (buf) - 1, fp)))
|
||||
{
|
||||
p = buf;
|
||||
cmd = next_arg (&p);
|
||||
val = next_arg (&p);
|
||||
line++;
|
||||
if (!cmd || *cmd == '#')
|
||||
continue;
|
||||
if (!strncasecmp ("mailbox", cmd, 7))
|
||||
{
|
||||
if (*cur)
|
||||
cur = &(*cur)->next;
|
||||
*cur = calloc (1, sizeof (config_t));
|
||||
config_defaults (*cur);
|
||||
(*cur)->path = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("host", cmd, 4))
|
||||
{
|
||||
#if HAVE_LIBSSL
|
||||
if (!strncasecmp ("imaps:", val, 6))
|
||||
{
|
||||
val += 6;
|
||||
if (*cur)
|
||||
{
|
||||
(*cur)->use_imaps = 1;
|
||||
(*cur)->port = 993;
|
||||
}
|
||||
else
|
||||
{
|
||||
global.use_imaps = 1;
|
||||
global.port = 993;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (*cur)
|
||||
(*cur)->host = strdup (val);
|
||||
else
|
||||
global.host = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("user", cmd, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->user = strdup (val);
|
||||
else
|
||||
global.user = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("pass", cmd, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->pass = strdup (val);
|
||||
else
|
||||
global.pass = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("port", cmd, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->port = atoi (val);
|
||||
else
|
||||
global.port = atoi (val);
|
||||
}
|
||||
else if (!strncasecmp ("box", cmd, 3))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->box = strdup (val);
|
||||
else
|
||||
global.box = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("alias", cmd, 5))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->alias = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("maxsize", cmd, 7))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->max_size = atol (val);
|
||||
else
|
||||
global.max_size = atol (val);
|
||||
}
|
||||
else if (!strncasecmp ("UseNamespace", cmd, 12))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->use_namespace = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.use_namespace = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
#if HAVE_LIBSSL
|
||||
else if (!strncasecmp ("CertificateFile", cmd, 15))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->cert_file = strdup (val);
|
||||
else
|
||||
global.cert_file = strdup (val);
|
||||
}
|
||||
else if (!strncasecmp ("RequireSSL", cmd, 10))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->require_ssl = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.require_ssl = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
else if (!strncasecmp ("UseSSLv2", cmd, 8))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->use_sslv2 = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.use_sslv2 = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
else if (!strncasecmp ("UseSSLv3", cmd, 8))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->use_sslv3 = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.use_sslv3 = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
else if (!strncasecmp ("UseTLSv1", cmd, 8))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->use_tlsv1 = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.use_tlsv1 = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
else if (!strncasecmp ("RequireCRAM", cmd, 11))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->require_cram = (strcasecmp (val, "yes") == 0);
|
||||
else
|
||||
global.require_cram = (strcasecmp (val, "yes") == 0);
|
||||
}
|
||||
#endif
|
||||
else if (buf[0])
|
||||
printf ("%s:%d:unknown command:%s", path, line, cmd);
|
||||
}
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
static config_t *
|
||||
find_box (const char *s)
|
||||
{
|
||||
config_t *p = box;
|
||||
|
||||
for (; p; p = p->next)
|
||||
if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
|
||||
return p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
next_arg (char **s)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (!s)
|
||||
return 0;
|
||||
if (!*s)
|
||||
return 0;
|
||||
while (isspace ((unsigned char) **s))
|
||||
(*s)++;
|
||||
if (!**s)
|
||||
{
|
||||
*s = 0;
|
||||
return 0;
|
||||
}
|
||||
ret = *s;
|
||||
while (**s && !isspace ((unsigned char) **s))
|
||||
(*s)++;
|
||||
if (**s)
|
||||
*(*s)++ = 0;
|
||||
if (!**s)
|
||||
*s = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
config_t *box;
|
||||
mailbox_t *mail;
|
||||
imap_t *imap = 0;
|
||||
int expunge = 0; /* by default, don't delete anything */
|
||||
int fast = 0;
|
||||
int delete = 0;
|
||||
char *config = 0;
|
||||
struct passwd *pw;
|
||||
|
||||
pw = getpwuid (getuid ());
|
||||
|
||||
/* defaults */
|
||||
memset (&global, 0, sizeof (global));
|
||||
global.port = 143;
|
||||
global.box = "INBOX";
|
||||
global.user = strdup (pw->pw_name);
|
||||
global.max_size = 0;
|
||||
global.use_namespace = 1;
|
||||
#if HAVE_LIBSSL
|
||||
/* this will probably annoy people, but its the best default just in
|
||||
* case people forget to turn it on
|
||||
*/
|
||||
global.require_ssl = 1;
|
||||
global.use_sslv2 = 1;
|
||||
global.use_sslv3 = 1;
|
||||
global.use_tlsv1 = 1;
|
||||
#endif
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
while ((i = getopt_long (argc, argv, "defhp:u:r:s:vV", Opts, NULL)) != -1)
|
||||
#else
|
||||
while ((i = getopt (argc, argv, "defhp:u:r:s:vV")) != -1)
|
||||
#endif
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 'c':
|
||||
config = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
delete = 1;
|
||||
break;
|
||||
case 'e':
|
||||
expunge = 1;
|
||||
break;
|
||||
case 'f':
|
||||
fast = 1;
|
||||
break;
|
||||
case 'p':
|
||||
global.port = atoi (optarg);
|
||||
break;
|
||||
case 'r':
|
||||
global.box = optarg;
|
||||
break;
|
||||
case 's':
|
||||
global.host = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
free (global.user);
|
||||
global.user = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
Verbose = 1;
|
||||
break;
|
||||
case 'v':
|
||||
version ();
|
||||
default:
|
||||
usage ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!argv[optind])
|
||||
{
|
||||
puts ("No box specified");
|
||||
usage ();
|
||||
}
|
||||
|
||||
gethostname (Hostname, sizeof (Hostname));
|
||||
|
||||
load_config (config);
|
||||
|
||||
for (; argv[optind]; optind++)
|
||||
{
|
||||
box = find_box (argv[optind]);
|
||||
if (!box)
|
||||
{
|
||||
/* if enough info is given on the command line, don't worry if
|
||||
* the mailbox isn't defined.
|
||||
*/
|
||||
if (!global.host)
|
||||
{
|
||||
puts ("No such mailbox");
|
||||
exit (1);
|
||||
}
|
||||
global.path = argv[optind];
|
||||
box = &global;
|
||||
}
|
||||
|
||||
if (!box->pass)
|
||||
{
|
||||
char *pass = getpass ("Password:");
|
||||
|
||||
if (!pass)
|
||||
{
|
||||
puts ("Aborting, no password");
|
||||
exit (1);
|
||||
}
|
||||
box->pass = strdup (pass);
|
||||
}
|
||||
|
||||
printf ("Reading %s\n", box->path);
|
||||
mail = maildir_open (box->path, fast);
|
||||
if (!mail)
|
||||
{
|
||||
puts ("Unable to load mailbox");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap);
|
||||
if (!imap)
|
||||
exit (1);
|
||||
|
||||
puts ("Synchronizing");
|
||||
i = delete ? SYNC_DELETE : 0;
|
||||
i |= expunge ? SYNC_EXPUNGE : 0;
|
||||
if (sync_mailbox (mail, imap, i, box->max_size))
|
||||
exit (1);
|
||||
|
||||
if (!fast)
|
||||
{
|
||||
if (expunge && (imap->deleted || mail->deleted))
|
||||
{
|
||||
/* remove messages marked for deletion */
|
||||
printf ("Expunging %d messages from server\n", imap->deleted);
|
||||
if (imap_expunge (imap))
|
||||
exit (1);
|
||||
printf ("Expunging %d messages from local mailbox\n",
|
||||
mail->deleted);
|
||||
if (maildir_expunge (mail, 0))
|
||||
exit (1);
|
||||
}
|
||||
/* remove messages deleted from server. this can safely be an
|
||||
* `else' clause since dead messages are marked as deleted by
|
||||
* sync_mailbox.
|
||||
*/
|
||||
else if (delete)
|
||||
maildir_expunge (mail, 1);
|
||||
|
||||
/* write changed flags back to the mailbox */
|
||||
printf ("Committing changes to %s\n", mail->path);
|
||||
if (maildir_sync (mail))
|
||||
exit (1);
|
||||
}
|
||||
|
||||
maildir_close (mail);
|
||||
}
|
||||
|
||||
/* gracefully close connection to the IMAP server */
|
||||
imap_close (imap);
|
||||
|
||||
exit (0);
|
||||
}
|
62
mbsync-get-cert
Executable file
62
mbsync-get-cert
Executable file
|
@ -0,0 +1,62 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This script will extract the necessary certificate from the IMAP server
|
||||
# It assumes that an attacker isn't trying to spoof you when you connect
|
||||
# to the IMAP server! You're better off downloading the certificate
|
||||
# from a trusted source.
|
||||
#
|
||||
# Copyright (C) 2003 Theodore Ts'o <tytso@alum.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, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
#
|
||||
|
||||
if [ $# != 1 ]; then
|
||||
echo "Usage: $0 <host>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HOST=$1
|
||||
|
||||
seed=`date '+%s'`
|
||||
try=0
|
||||
while :; do
|
||||
TMPDIR=/tmp/get-cert.$$.$seed
|
||||
mkdir $TMPDIR 2> /dev/null && break
|
||||
if [ $try = 1000 ]; then
|
||||
echo "Cannot create temporary directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
try=`expr $try + 1`
|
||||
seed=`expr \( \( $seed \* 1103515245 \) + 12345 \) % 2147483648`
|
||||
done
|
||||
|
||||
TMPFILE=$TMPDIR/get-cert
|
||||
ERRFILE=$TMPDIR/get-cert-err
|
||||
CERTFILE=$TMPDIR/cert
|
||||
|
||||
echo QUIT | openssl s_client -connect $HOST:993 -showcerts \
|
||||
> $TMPFILE 2> $ERRFILE
|
||||
sed -e '1,/^-----BEGIN CERTIFICATE-----/d' \
|
||||
-e '/^-----END CERTIFICATE-----/,$d' < $TMPFILE > $CERTFILE
|
||||
|
||||
if test -s $CERTFILE ; then
|
||||
echo -----BEGIN CERTIFICATE-----
|
||||
cat $CERTFILE
|
||||
echo -----END CERTIFICATE-----
|
||||
else
|
||||
echo "Couldn't retrieve certificate. openssl reported the following errors:"
|
||||
cat $ERRFILE
|
||||
fi
|
||||
|
||||
rm -r $TMPDIR
|
8
src/.gitignore
vendored
Normal file
8
src/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
/drv_proxy.inc
|
||||
/mbsync
|
||||
/mdconvert
|
||||
/tst_timers
|
||||
/tmp/
|
||||
|
||||
.deps/
|
||||
*.o
|
28
src/Makefile.am
Normal file
28
src/Makefile.am
Normal file
|
@ -0,0 +1,28 @@
|
|||
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) $(KEYCHAIN_LIBS)
|
||||
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
|
||||
|
||||
drv_proxy.$(OBJEXT): drv_proxy.inc
|
||||
drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl
|
||||
perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc
|
||||
|
||||
mdconvert_SOURCES = mdconvert.c
|
||||
mdconvert_LDADD = $(DB_LIBS)
|
||||
if with_mdconvert
|
||||
mdconvert_prog = mdconvert
|
||||
mdconvert_man = mdconvert.1
|
||||
endif
|
||||
|
||||
EXTRA_PROGRAMS = tst_timers
|
||||
|
||||
tst_timers_SOURCES = tst_timers.c util.c
|
||||
|
||||
bin_PROGRAMS = mbsync $(mdconvert_prog)
|
||||
man_MANS = mbsync.1 $(mdconvert_man)
|
||||
|
||||
exampledir = $(docdir)/examples
|
||||
example_DATA = mbsyncrc.sample
|
||||
|
||||
EXTRA_DIST = drv_proxy_gen.pl run-tests.pl $(example_DATA) $(man_MANS)
|
||||
|
||||
CLEANFILES = drv_proxy.inc
|
264
src/common.h
Normal file
264
src/common.h
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <autodefs.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
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)
|
||||
|
||||
// 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) \
|
||||
((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)))
|
||||
#else
|
||||
# define ATTR_UNUSED
|
||||
# define ATTR_NORETURN
|
||||
# define ATTR_PRINTFLIKE(fmt,var)
|
||||
#endif
|
||||
|
||||
#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
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define INLINE __inline__
|
||||
#else
|
||||
# define INLINE
|
||||
#endif
|
||||
|
||||
#define EXE "mbsync"
|
||||
|
||||
/* main.c */
|
||||
|
||||
#define DEBUG_CRASH 0x01
|
||||
#define DEBUG_MAILDIR 0x02
|
||||
#define DEBUG_NET 0x04
|
||||
#define DEBUG_NET_ALL 0x08
|
||||
#define DEBUG_SYNC 0x10
|
||||
#define DEBUG_MAIN 0x20
|
||||
#define DEBUG_DRV 0x40
|
||||
#define DEBUG_DRV_ALL 0x80
|
||||
#define DEBUG_ALL (0xFF & ~(DEBUG_NET_ALL | DEBUG_DRV_ALL))
|
||||
#define QUIET 0x100
|
||||
#define VERYQUIET 0x200
|
||||
#define PROGRESS 0x400
|
||||
#define VERBOSE 0x800
|
||||
#define KEEPJOURNAL 0x1000
|
||||
#define ZERODELAY 0x2000
|
||||
#define FORCEASYNC 0x4000
|
||||
|
||||
extern int DFlags;
|
||||
extern int JLimit;
|
||||
extern int UseFSync;
|
||||
extern char FieldDelimiter;
|
||||
|
||||
extern int Pid;
|
||||
extern char Hostname[256];
|
||||
extern const char *Home;
|
||||
|
||||
extern uint BufferLimit;
|
||||
|
||||
extern int new_total[2], new_done[2];
|
||||
extern int flags_total[2], flags_done[2];
|
||||
extern int trash_total[2], trash_done[2];
|
||||
|
||||
void stats( void );
|
||||
|
||||
/* util.c */
|
||||
|
||||
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];
|
||||
} string_list_t;
|
||||
|
||||
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 );
|
||||
|
||||
#ifndef HAVE_MEMRCHR
|
||||
void *memrchr( const void *s, int c, size_t n );
|
||||
#endif
|
||||
#ifndef HAVE_STRNLEN
|
||||
size_t strnlen( const char *str, size_t maxlen );
|
||||
#endif
|
||||
|
||||
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 );
|
||||
#endif
|
||||
|
||||
void *nfmalloc( size_t sz );
|
||||
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 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, uint reserve, const char *in, const char *out );
|
||||
|
||||
#define DEFINE_ARRAY_TYPE(T) \
|
||||
typedef struct { \
|
||||
T *data; \
|
||||
uint size; \
|
||||
} T##_array_t; \
|
||||
typedef union { \
|
||||
T##_array_t array; \
|
||||
struct { \
|
||||
T *data; \
|
||||
uint size; \
|
||||
uint alloc; \
|
||||
}; \
|
||||
} T##_array_alloc_t; \
|
||||
static INLINE T *T##_array_append( T##_array_alloc_t *arr ) \
|
||||
{ \
|
||||
if (arr->size == arr->alloc) { \
|
||||
arr->alloc = arr->alloc * 2 + 100; \
|
||||
arr->data = nfrealloc( arr->data, arr->alloc * sizeof(T) ); \
|
||||
} \
|
||||
return &arr->data[arr->size++]; \
|
||||
}
|
||||
|
||||
#define ARRAY_INIT(arr) \
|
||||
do { (arr)->data = NULL; (arr)->size = (arr)->alloc = 0; } while (0)
|
||||
|
||||
#define ARRAY_SQUEEZE(arr) \
|
||||
do { \
|
||||
(arr)->data = nfrealloc( (arr)->data, (arr)->size * sizeof((arr)->data[0]) ); \
|
||||
} while (0)
|
||||
|
||||
DEFINE_ARRAY_TYPE(uint)
|
||||
void sort_uint_array( uint_array_t array );
|
||||
int find_uint_array( const uint_array_t array, uint value );
|
||||
|
||||
void arc4_init( void );
|
||||
uchar arc4_getbyte( void );
|
||||
|
||||
uint bucketsForSize( uint size );
|
||||
|
||||
typedef struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
} list_head_t;
|
||||
|
||||
typedef struct notifier {
|
||||
struct notifier *next;
|
||||
void (*cb)( int what, void *aux );
|
||||
void *aux;
|
||||
#ifdef HAVE_POLL_H
|
||||
uint index;
|
||||
#else
|
||||
int fd;
|
||||
short events;
|
||||
#endif
|
||||
} notifier_t;
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
# include <poll.h>
|
||||
#else
|
||||
# define POLLIN 1
|
||||
# define POLLOUT 4
|
||||
# define POLLERR 8
|
||||
#endif
|
||||
|
||||
void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux );
|
||||
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 {
|
||||
list_head_t links;
|
||||
void (*cb)( void *aux );
|
||||
void *aux;
|
||||
time_t timeout;
|
||||
} wakeup_t;
|
||||
|
||||
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 ATTR_UNUSED pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != NULL; }
|
||||
|
||||
void main_loop( void );
|
||||
|
||||
#endif
|
536
src/config.c
Normal file
536
src/config.c
Normal file
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2011 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sync.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static store_conf_t *stores;
|
||||
|
||||
char *
|
||||
get_arg( conffile_t *cfile, int required, int *comment )
|
||||
{
|
||||
char *ret, *p, *t;
|
||||
int escaped, quoted;
|
||||
char c;
|
||||
|
||||
p = cfile->rest;
|
||||
assert( p );
|
||||
while ((c = *p) && isspace( (uchar)c ))
|
||||
p++;
|
||||
if (!c || c == '#') {
|
||||
if (comment)
|
||||
*comment = (c == '#');
|
||||
if (required) {
|
||||
error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
|
||||
cfile->err = 1;
|
||||
}
|
||||
ret = NULL;
|
||||
} else {
|
||||
for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
|
||||
p++;
|
||||
if (escaped && c >= 32) {
|
||||
escaped = 0;
|
||||
*t++ = c;
|
||||
} else if (c == '\\')
|
||||
escaped = 1;
|
||||
else if (c == '"')
|
||||
quoted ^= 1;
|
||||
else if (!quoted && isspace( (uchar)c ))
|
||||
break;
|
||||
else
|
||||
*t++ = c;
|
||||
}
|
||||
*t = 0;
|
||||
if (escaped) {
|
||||
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
|
||||
cfile->err = 1;
|
||||
ret = NULL;
|
||||
}
|
||||
if (quoted) {
|
||||
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
|
||||
cfile->err = 1;
|
||||
ret = NULL;
|
||||
}
|
||||
}
|
||||
cfile->rest = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char
|
||||
parse_bool( conffile_t *cfile )
|
||||
{
|
||||
if (!strcasecmp( cfile->val, "yes" ) ||
|
||||
!strcasecmp( cfile->val, "true" ) ||
|
||||
!strcasecmp( cfile->val, "on" ) ||
|
||||
!strcmp( cfile->val, "1" ))
|
||||
return 1;
|
||||
if (strcasecmp( cfile->val, "no" ) &&
|
||||
strcasecmp( cfile->val, "false" ) &&
|
||||
strcasecmp( cfile->val, "off" ) &&
|
||||
strcmp( cfile->val, "0" )) {
|
||||
error( "%s:%d: invalid boolean value '%s'\n",
|
||||
cfile->file, cfile->line, cfile->val );
|
||||
cfile->err = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_int( conffile_t *cfile )
|
||||
{
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
ret = strtol( cfile->val, &p, 10 );
|
||||
if (*p) {
|
||||
error( "%s:%d: invalid integer value '%s'\n",
|
||||
cfile->file, cfile->line, cfile->val );
|
||||
cfile->err = 1;
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint
|
||||
parse_size( conffile_t *cfile )
|
||||
{
|
||||
char *p;
|
||||
uint ret;
|
||||
|
||||
ret = strtoul( cfile->val, &p, 10 );
|
||||
if (*p == 'k' || *p == 'K')
|
||||
ret *= 1024, p++;
|
||||
else if (*p == 'm' || *p == 'M')
|
||||
ret *= 1024 * 1024, p++;
|
||||
if (*p == 'b' || *p == 'B')
|
||||
p++;
|
||||
if (*p) {
|
||||
fprintf (stderr, "%s:%d: invalid size '%s'\n",
|
||||
cfile->file, cfile->line, cfile->val);
|
||||
cfile->err = 1;
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int op;
|
||||
const char *name;
|
||||
} boxOps[] = {
|
||||
{ OP_EXPUNGE, "Expunge" },
|
||||
{ OP_CREATE, "Create" },
|
||||
{ OP_REMOVE, "Remove" },
|
||||
};
|
||||
|
||||
static int
|
||||
getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
|
||||
{
|
||||
char *arg;
|
||||
uint i;
|
||||
|
||||
if (!strcasecmp( "Sync", cfile->cmd )) {
|
||||
arg = cfile->val;
|
||||
do
|
||||
if (!strcasecmp( "Push", arg ))
|
||||
*cops |= XOP_PUSH;
|
||||
else if (!strcasecmp( "Pull", arg ))
|
||||
*cops |= XOP_PULL;
|
||||
else if (!strcasecmp( "ReNew", arg ))
|
||||
*cops |= OP_RENEW;
|
||||
else if (!strcasecmp( "New", arg ))
|
||||
*cops |= OP_NEW;
|
||||
else if (!strcasecmp( "Delete", arg ))
|
||||
*cops |= OP_DELETE;
|
||||
else if (!strcasecmp( "Flags", arg ))
|
||||
*cops |= OP_FLAGS;
|
||||
else if (!strcasecmp( "PullReNew", arg ))
|
||||
conf->ops[N] |= OP_RENEW;
|
||||
else if (!strcasecmp( "PullNew", arg ))
|
||||
conf->ops[N] |= OP_NEW;
|
||||
else if (!strcasecmp( "PullDelete", arg ))
|
||||
conf->ops[N] |= OP_DELETE;
|
||||
else if (!strcasecmp( "PullFlags", arg ))
|
||||
conf->ops[N] |= OP_FLAGS;
|
||||
else if (!strcasecmp( "PushReNew", arg ))
|
||||
conf->ops[F] |= OP_RENEW;
|
||||
else if (!strcasecmp( "PushNew", arg ))
|
||||
conf->ops[F] |= OP_NEW;
|
||||
else if (!strcasecmp( "PushDelete", arg ))
|
||||
conf->ops[F] |= OP_DELETE;
|
||||
else if (!strcasecmp( "PushFlags", arg ))
|
||||
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 )) {
|
||||
error( "%s:%d: invalid Sync arg '%s'\n",
|
||||
cfile->file, cfile->line, arg );
|
||||
cfile->err = 1;
|
||||
}
|
||||
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 ))
|
||||
conf->use_internal_date = parse_bool( cfile );
|
||||
else if (!strcasecmp( "MaxMessages", cfile->cmd ))
|
||||
conf->max_messages = parse_int( cfile );
|
||||
else if (!strcasecmp( "ExpireUnread", cfile->cmd ))
|
||||
conf->expire_unread = parse_bool( cfile );
|
||||
else {
|
||||
for (i = 0; i < as(boxOps); i++) {
|
||||
if (!strcasecmp( boxOps[i].name, cfile->cmd )) {
|
||||
int op = boxOps[i].op;
|
||||
arg = cfile->val;
|
||||
do {
|
||||
if (!strcasecmp( "Both", arg )) {
|
||||
*cops |= 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, NULL )));
|
||||
conf->ops[F] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
getcline( conffile_t *cfile )
|
||||
{
|
||||
char *arg;
|
||||
int comment;
|
||||
|
||||
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;
|
||||
}
|
||||
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
|
||||
cfile->line++;
|
||||
cfile->rest = cfile->buf;
|
||||
if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) {
|
||||
if (comment)
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX - this does not detect None conflicts ... */
|
||||
int
|
||||
merge_ops( int cops, int ops[] )
|
||||
{
|
||||
int aops, op;
|
||||
uint i;
|
||||
|
||||
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[F] |= cops & OP_MASK_TYPE;
|
||||
ops[N] |= cops & OP_MASK_TYPE;
|
||||
if (cops & XOP_PULL) {
|
||||
if (ops[N] & OP_MASK_TYPE)
|
||||
goto cfl;
|
||||
ops[N] |= OP_MASK_TYPE;
|
||||
}
|
||||
if (cops & XOP_PUSH) {
|
||||
if (ops[F] & OP_MASK_TYPE)
|
||||
goto cfl;
|
||||
ops[F] |= OP_MASK_TYPE;
|
||||
}
|
||||
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
|
||||
if (!(cops & OP_MASK_TYPE))
|
||||
cops |= OP_MASK_TYPE;
|
||||
else if (!(cops & XOP_MASK_DIR))
|
||||
cops |= XOP_PULL|XOP_PUSH;
|
||||
if (cops & XOP_PULL)
|
||||
ops[N] |= cops & OP_MASK_TYPE;
|
||||
if (cops & XOP_PUSH)
|
||||
ops[F] |= cops & OP_MASK_TYPE;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < as(boxOps); i++) {
|
||||
op = boxOps[i].op;
|
||||
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[F] |= cops & op;
|
||||
ops[N] |= cops & op;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
load_config( const char *where )
|
||||
{
|
||||
conffile_t cfile;
|
||||
store_conf_t *store, **storeapp = &stores;
|
||||
channel_conf_t *channel, **channelapp = &channels;
|
||||
group_conf_t *group, **groupapp = &groups;
|
||||
string_list_t *chanlist, **chanlistapp;
|
||||
char *arg, *p;
|
||||
uint len, max_size;
|
||||
int cops, gcops, glob_ok, fn, i;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char buf[1024];
|
||||
|
||||
if (!where) {
|
||||
nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home );
|
||||
cfile.file = path;
|
||||
} else
|
||||
cfile.file = where;
|
||||
|
||||
info( "Reading configuration file %s\n", cfile.file );
|
||||
|
||||
if (!(cfile.fp = fopen( cfile.file, "r" ))) {
|
||||
sys_error( "Cannot open config file '%s'", cfile.file );
|
||||
return 1;
|
||||
}
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
cfile.buf = buf;
|
||||
cfile.bufl = sizeof(buf) - 1;
|
||||
cfile.line = 0;
|
||||
cfile.err = 0;
|
||||
cfile.ms_warn = 0;
|
||||
cfile.rest = NULL;
|
||||
|
||||
gcops = 0;
|
||||
glob_ok = 1;
|
||||
global_conf.expire_unread = -1;
|
||||
reloop:
|
||||
while (getcline( &cfile )) {
|
||||
if (!cfile.cmd)
|
||||
continue;
|
||||
for (i = 0; i < N_DRIVERS; i++)
|
||||
if (drivers[i]->parse_store( &cfile, &store )) {
|
||||
if (store) {
|
||||
if (!store->max_size)
|
||||
store->max_size = UINT_MAX;
|
||||
if (!store->flat_delim)
|
||||
store->flat_delim = "";
|
||||
*storeapp = store;
|
||||
storeapp = &store->next;
|
||||
*storeapp = NULL;
|
||||
}
|
||||
glob_ok = 0;
|
||||
goto reloop;
|
||||
}
|
||||
if (!strcasecmp( "Channel", cfile.cmd ))
|
||||
{
|
||||
channel = nfcalloc( sizeof(*channel) );
|
||||
channel->name = nfstrdup( cfile.val );
|
||||
channel->max_messages = global_conf.max_messages;
|
||||
channel->expire_unread = global_conf.expire_unread;
|
||||
channel->use_internal_date = global_conf.use_internal_date;
|
||||
cops = 0;
|
||||
max_size = UINT_MAX;
|
||||
while (getcline( &cfile ) && cfile.cmd) {
|
||||
if (!strcasecmp( "MaxSize", cfile.cmd ))
|
||||
max_size = parse_size( &cfile );
|
||||
else if (!strcasecmp( "Pattern", cfile.cmd ) ||
|
||||
!strcasecmp( "Patterns", cfile.cmd ))
|
||||
{
|
||||
arg = cfile.val;
|
||||
do
|
||||
add_string_list( &channel->patterns, arg );
|
||||
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
|
||||
}
|
||||
else if (!strcasecmp( "Far", cfile.cmd )) {
|
||||
fn = F;
|
||||
goto linkst;
|
||||
} 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",
|
||||
cfile.file, cfile.line );
|
||||
cfile.err = 1;
|
||||
continue;
|
||||
}
|
||||
*p = 0;
|
||||
for (store = stores; store; store = store->next)
|
||||
if (!strcmp( store->name, cfile.val + 1 )) {
|
||||
channel->stores[fn] = store;
|
||||
goto stpcom;
|
||||
}
|
||||
error( "%s:%d: unknown store '%s'\n",
|
||||
cfile.file, cfile.line, cfile.val + 1 );
|
||||
cfile.err = 1;
|
||||
continue;
|
||||
stpcom:
|
||||
if (*++p)
|
||||
channel->boxes[fn] = nfstrdup( p );
|
||||
} else if (!getopt_helper( &cfile, &cops, channel )) {
|
||||
error( "%s:%d: keyword '%s' is not recognized in Channel sections\n",
|
||||
cfile.file, cfile.line, cfile.cmd );
|
||||
cfile.err = 1;
|
||||
}
|
||||
}
|
||||
if (!channel->stores[F]) {
|
||||
error( "channel '%s' refers to no far side store\n", channel->name );
|
||||
cfile.err = 1;
|
||||
} 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 != UINT_MAX) {
|
||||
if (!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 ))
|
||||
{
|
||||
group = nfmalloc( sizeof(*group) );
|
||||
group->name = nfstrdup( cfile.val );
|
||||
*groupapp = group;
|
||||
groupapp = &group->next;
|
||||
*groupapp = NULL;
|
||||
chanlistapp = &group->channels;
|
||||
*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 = NULL;
|
||||
}
|
||||
while (getcline( &cfile ) && cfile.cmd) {
|
||||
if (!strcasecmp( "Channel", cfile.cmd ) ||
|
||||
!strcasecmp( "Channels", cfile.cmd ))
|
||||
{
|
||||
arg = cfile.val;
|
||||
goto addone;
|
||||
}
|
||||
else
|
||||
{
|
||||
error( "%s:%d: keyword '%s' is not recognized in Group sections\n",
|
||||
cfile.file, cfile.line, cfile.cmd );
|
||||
cfile.err = 1;
|
||||
}
|
||||
}
|
||||
glob_ok = 0;
|
||||
goto reloop;
|
||||
}
|
||||
else if (!strcasecmp( "FSync", cfile.cmd ))
|
||||
{
|
||||
UseFSync = parse_bool( &cfile );
|
||||
}
|
||||
else if (!strcasecmp( "FieldDelimiter", cfile.cmd ))
|
||||
{
|
||||
if (strlen( cfile.val ) != 1) {
|
||||
error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line );
|
||||
cfile.err = 1;
|
||||
} else {
|
||||
FieldDelimiter = cfile.val[0];
|
||||
if (!ispunct( FieldDelimiter )) {
|
||||
error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line );
|
||||
cfile.err = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcasecmp( "BufferLimit", cfile.cmd ))
|
||||
{
|
||||
BufferLimit = parse_size( &cfile );
|
||||
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: '%s' is not a recognized section-starting or global keyword\n",
|
||||
cfile.file, cfile.line, cfile.cmd );
|
||||
cfile.err = 1;
|
||||
while (getcline( &cfile ))
|
||||
if (!cfile.cmd)
|
||||
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 "/" );
|
||||
return cfile.err;
|
||||
}
|
51
src/config.h
Normal file
51
src/config.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
FILE *fp;
|
||||
char *buf;
|
||||
int bufl;
|
||||
int line;
|
||||
int err;
|
||||
int ms_warn;
|
||||
char *cmd, *val, *rest;
|
||||
} conffile_t;
|
||||
|
||||
#define ARG_OPTIONAL 0
|
||||
#define ARG_REQUIRED 1
|
||||
|
||||
char *get_arg( conffile_t *cfile, int required, int *comment );
|
||||
|
||||
char parse_bool( conffile_t *cfile );
|
||||
int parse_int( 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 );
|
||||
|
||||
#endif
|
78
src/driver.c
Normal file
78
src/driver.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
driver_t *drivers[N_DRIVERS] = { &maildir_driver, &imap_driver };
|
||||
|
||||
uint
|
||||
count_generic_messages( message_t *msgs )
|
||||
{
|
||||
uint count = 0;
|
||||
for (; msgs; msgs = msgs->next)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
free_generic_messages( message_t *msgs )
|
||||
{
|
||||
message_t *tmsg;
|
||||
|
||||
for (; msgs; msgs = tmsg) {
|
||||
tmsg = msgs->next;
|
||||
free( msgs->msgid );
|
||||
free( msgs );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type )
|
||||
{
|
||||
if (!strcasecmp( "Trash", cfg->cmd )) {
|
||||
store->trash = nfstrdup( cfg->val );
|
||||
} else if (!strcasecmp( "TrashRemoteNew", cfg->cmd )) {
|
||||
store->trash_remote_new = parse_bool( cfg );
|
||||
} else if (!strcasecmp( "TrashNewOnly", cfg->cmd )) {
|
||||
store->trash_only_new = parse_bool( cfg );
|
||||
} else if (!strcasecmp( "MaxSize", cfg->cmd )) {
|
||||
store->max_size = parse_size( cfg );
|
||||
} else if (!strcasecmp( "MapInbox", cfg->cmd )) {
|
||||
store->map_inbox = nfstrdup( cfg->val );
|
||||
} else if (!strcasecmp( "Flatten", cfg->cmd )) {
|
||||
const char *p;
|
||||
for (p = cfg->val; *p; p++) {
|
||||
if (*p == '/') {
|
||||
error( "%s:%d: flattened hierarchy delimiter cannot contain the canonical delimiter '/'\n", cfg->file, cfg->line );
|
||||
cfg->err = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
store->flat_delim = nfstrdup( cfg->val );
|
||||
} else {
|
||||
error( "%s:%d: keyword '%s' is not recognized in %s sections\n", cfg->file, cfg->line, cfg->cmd, type );
|
||||
cfg->err = 1;
|
||||
}
|
||||
}
|
302
src/driver.h
Normal file
302
src/driver.h
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#ifndef DRIVER_H
|
||||
#define DRIVER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
typedef struct driver driver_t;
|
||||
|
||||
#define FAIL_TEMP 0 /* Retry immediately (also: no error) */
|
||||
#define FAIL_WAIT 1 /* Retry after some time (if at all) */
|
||||
#define FAIL_FINAL 2 /* Don't retry until store reconfiguration */
|
||||
|
||||
#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: */
|
||||
/* 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_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
|
||||
|
||||
#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 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)
|
||||
#define OPEN_APPEND (1<<7)
|
||||
#define OPEN_FIND (1<<8)
|
||||
#define OPEN_OLD_IDS (1<<9)
|
||||
|
||||
#define UIDVAL_BAD ((uint)-1)
|
||||
|
||||
#define STORE(store) \
|
||||
store *next; \
|
||||
driver_t *driver; \
|
||||
store##_conf *conf; /* foreign */
|
||||
|
||||
typedef struct store {
|
||||
STORE(struct store)
|
||||
} store_t;
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
uint len;
|
||||
time_t date;
|
||||
uchar flags;
|
||||
} msg_data_t;
|
||||
|
||||
#define DRV_OK 0
|
||||
/* Message went missing, or mailbox is full, etc. */
|
||||
#define DRV_MSG_BAD 1
|
||||
/* Something is wrong with the current mailbox - probably it is somehow inaccessible. */
|
||||
#define DRV_BOX_BAD 2
|
||||
/* Failed to connect store. */
|
||||
#define DRV_STORE_BAD 3
|
||||
/* The command has been cancel()ed or cancel_store()d. */
|
||||
#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,
|
||||
not that it must. The lack of it OTOH implies that it CANNOT,
|
||||
and as CRLF is the canonical format, we convert.
|
||||
*/
|
||||
#define DRV_CRLF 1
|
||||
/*
|
||||
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 uint // For auto-generation of appropriate printf() formats.
|
||||
|
||||
struct driver {
|
||||
/* Return driver capabilities. */
|
||||
xint (*get_caps)( store_t *ctx );
|
||||
|
||||
/* Parse configuration. */
|
||||
int (*parse_store)( conffile_t *cfg, store_conf_t **storep );
|
||||
|
||||
/* Close remaining server connections. All stores must be discarded first. */
|
||||
void (*cleanup)( void );
|
||||
|
||||
/* Allocate a store with the given configuration. This is expected to
|
||||
* return quickly, and must not fail. */
|
||||
store_t *(*alloc_store)( store_conf_t *conf, const char *label );
|
||||
|
||||
/* When this callback is invoked (at most once per store), the store is fubar;
|
||||
* call cancel_store() to dispose of it. */
|
||||
void (*set_bad_callback)( store_t *ctx, void (*cb)( void *aux ), void *aux );
|
||||
|
||||
/* Open/connect the store. This may recycle existing server connections. */
|
||||
void (*connect_store)( store_t *ctx,
|
||||
void (*cb)( int sts, void *aux ), void *aux );
|
||||
|
||||
/* Discard the store. Underlying server connection may be kept alive. */
|
||||
void (*free_store)( store_t *ctx );
|
||||
|
||||
/* Discard the store after a bad_callback. The server connections will be closed.
|
||||
* Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */
|
||||
void (*cancel_store)( store_t *ctx );
|
||||
|
||||
/* List the mailboxes in this store. Flags are ORed LIST_* values.
|
||||
* The returned box list remains owned by the driver. */
|
||||
void (*list_store)( store_t *ctx, int flags,
|
||||
void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux );
|
||||
|
||||
/* Invoked before open_box(), this informs the driver which box is to be opened. */
|
||||
int (*select_box)( store_t *ctx, const char *name );
|
||||
|
||||
/* Get the selected box' on-disk path, if applicable, null otherwise. */
|
||||
const char *(*get_box_path)( store_t *ctx );
|
||||
|
||||
/* Create the selected mailbox. */
|
||||
void (*create_box)( store_t *ctx,
|
||||
void (*cb)( int sts, void *aux ), void *aux );
|
||||
|
||||
/* 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, uint uidvalidity, void *aux ), void *aux );
|
||||
|
||||
/* Return the minimal UID the next stored message will have. */
|
||||
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 );
|
||||
|
||||
/* Delete the open mailbox. The mailbox is expected to be empty.
|
||||
* Subfolders of the mailbox are *not* deleted.
|
||||
* Some artifacts of the mailbox may remain, but they won't be
|
||||
* recognized as a mailbox any more. */
|
||||
void (*delete_box)( store_t *ctx,
|
||||
void (*cb)( int sts, void *aux ), void *aux );
|
||||
|
||||
/* Remove the last artifacts of the open mailbox, as far as possible. */
|
||||
int (*finish_delete_box)( store_t *ctx );
|
||||
|
||||
/* Invoked before load_box(), this informs the driver which operations (OP_*)
|
||||
* will be performed on the mailbox. The driver may extend the set by implicitly
|
||||
* needed or available operations. Returns this possibly extended set. */
|
||||
xint (*prepare_load_box)( store_t *ctx, xint opts );
|
||||
|
||||
/* Load the message attributes needed to perform the requested operations.
|
||||
* 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 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 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.
|
||||
* 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.
|
||||
* If the new copy's UID can be immediately determined, return it, otherwise 0. */
|
||||
void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash,
|
||||
void (*cb)( int sts, uint uid, void *aux ), void *aux );
|
||||
|
||||
/* Index the messages which have newly appeared in the mailbox, including their
|
||||
* temporary UID headers. This is needed if store_msg() does not guarantee returning
|
||||
* a UID; otherwise the driver needs to implement only the OPEN_FIND flag.
|
||||
* The returned message list remains owned by the driver. */
|
||||
void (*find_new_msgs)( store_t *ctx, uint newuid,
|
||||
void (*cb)( int sts, message_t *msgs, void *aux ), void *aux );
|
||||
|
||||
/* Add/remove the named flags to/from the given message. The message may be either
|
||||
* 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,
|
||||
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,
|
||||
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,
|
||||
void (*cb)( int sts, void *aux ), void *aux );
|
||||
|
||||
/* Cancel queued commands which are not in flight yet; they will have their
|
||||
* callbacks invoked with DRV_CANCELED. Afterwards, wait for the completion of
|
||||
* the in-flight commands. If the store is canceled before this command completes,
|
||||
* the callback will *not* be invoked. */
|
||||
void (*cancel_cmds)( store_t *ctx,
|
||||
void (*cb)( void *aux ), void *aux );
|
||||
|
||||
/* Commit any pending set_msg_flags() commands. */
|
||||
void (*commit_cmds)( store_t *ctx );
|
||||
|
||||
/* Get approximate amount of memory occupied by the driver. */
|
||||
uint (*get_memory_usage)( store_t *ctx );
|
||||
|
||||
/* Get the FAIL_* state of the driver. */
|
||||
int (*get_fail_state)( store_conf_t *conf );
|
||||
};
|
||||
|
||||
uint count_generic_messages( message_t * );
|
||||
void free_generic_messages( message_t * );
|
||||
|
||||
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 );
|
||||
|
||||
#define N_DRIVERS 2
|
||||
extern driver_t *drivers[N_DRIVERS];
|
||||
extern driver_t maildir_driver, imap_driver, proxy_driver;
|
||||
|
||||
#endif
|
3821
src/drv_imap.c
Normal file
3821
src/drv_imap.c
Normal file
File diff suppressed because it is too large
Load diff
2021
src/drv_maildir.c
Normal file
2021
src/drv_maildir.c
Normal file
File diff suppressed because it is too large
Load diff
447
src/drv_proxy.c
Normal file
447
src/drv_proxy.c
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2017 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct gen_cmd gen_cmd_t;
|
||||
|
||||
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)
|
||||
debug( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start( va, msg );
|
||||
vdebug( DEBUG_DRV, msg, va );
|
||||
va_end( va );
|
||||
}
|
||||
|
||||
static void ATTR_PRINTFLIKE(1, 2)
|
||||
debugn( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start( va, msg );
|
||||
vdebugn( DEBUG_DRV, msg, va );
|
||||
va_end( va );
|
||||
}
|
||||
|
||||
/* 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( uchar flags, char *buf )
|
||||
{
|
||||
uint i, d;
|
||||
|
||||
for (d = 0, i = 0; i < as(Flags); i++)
|
||||
if (flags & (1 << i))
|
||||
buf[d++] = Flags[i];
|
||||
buf[d] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
proxy_store_deref( proxy_store_t *ctx )
|
||||
{
|
||||
if (!--ctx->ref_count) {
|
||||
assert( !pending_wakeup( &ctx->wakeup ) );
|
||||
free( ctx );
|
||||
}
|
||||
}
|
||||
|
||||
static int curr_tag;
|
||||
|
||||
#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, uint sz )
|
||||
{
|
||||
gen_cmd_t *cmd = nfmalloc( sz );
|
||||
cmd->ref_count = 2;
|
||||
cmd->tag = ++curr_tag;
|
||||
cmd->ctx = ctx;
|
||||
ctx->ref_count++;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void
|
||||
proxy_cmd_done( gen_cmd_t *cmd )
|
||||
{
|
||||
if (!--cmd->ref_count) {
|
||||
proxy_store_deref( cmd->ctx );
|
||||
free( 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 )
|
||||
{
|
||||
proxy_store_t *ctx = (proxy_store_t *)gctx;
|
||||
|
||||
@type@rv = ctx->real_driver->@name@( ctx->real_store );
|
||||
debug( "%sCalled @name@, ret=@fmt@\n", ctx->label, rv );
|
||||
return rv;
|
||||
}
|
||||
//# END
|
||||
|
||||
//# TEMPLATE REGULAR
|
||||
static @type@proxy_@name@( store_t *gctx@decl_args@ )
|
||||
{
|
||||
proxy_store_t *ctx = (proxy_store_t *)gctx;
|
||||
|
||||
@pre_print_args@
|
||||
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
|
||||
@print_args@
|
||||
@type@rv = ctx->real_driver->@name@( ctx->real_store@pass_args@ );
|
||||
debug( "%sLeave @name@, ret=@fmt@\n", ctx->label, rv );
|
||||
return rv;
|
||||
}
|
||||
//# END
|
||||
|
||||
//# TEMPLATE REGULAR_VOID
|
||||
static @type@proxy_@name@( store_t *gctx@decl_args@ )
|
||||
{
|
||||
proxy_store_t *ctx = (proxy_store_t *)gctx;
|
||||
|
||||
@pre_print_args@
|
||||
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
|
||||
@print_args@
|
||||
ctx->real_driver->@name@( ctx->real_store@pass_args@ );
|
||||
debug( "%sLeave @name@\n", ctx->label );
|
||||
@action@
|
||||
}
|
||||
//# END
|
||||
|
||||
//# TEMPLATE CALLBACK
|
||||
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;
|
||||
|
||||
@save_cb_args@
|
||||
proxy_invoke_cb( @gen_cmd@, proxy_do_@name@_cb, @checked@, "@name@" );
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@name@_cmd_t *cmd = (@name@_cmd_t *)proxy_cmd_new( ctx, sizeof(@name@_cmd_t) );
|
||||
cmd->callback = cb;
|
||||
cmd->callback_aux = aux;
|
||||
@assign_state@
|
||||
@pre_print_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->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 (cmd->sts == DRV_OK) {
|
||||
for (string_list_t *box = cmd->boxes; box; box = box->next)
|
||||
debug( " %s\n", box->string );
|
||||
}
|
||||
//# END
|
||||
|
||||
//# DEFINE load_box_pre_print_args
|
||||
static char ubuf[12];
|
||||
//# END
|
||||
//# 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 (uint t = 0; t < excs.size; t++)
|
||||
debugn( " %u", excs.data[t] );
|
||||
debug( "\n" );
|
||||
}
|
||||
//# END
|
||||
//# DEFINE load_box_print_fmt_cb_args , sts=%d, total=%d, recent=%d
|
||||
//# DEFINE load_box_print_pass_cb_args , cmd->sts, cmd->total_msgs, cmd->recent_msgs
|
||||
//# DEFINE load_box_print_cb_args
|
||||
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 , cmd->sts
|
||||
//# DEFINE find_new_msgs_print_cb_args
|
||||
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
|
||||
|
||||
//# DEFINE fetch_msg_decl_state
|
||||
msg_data_t *data;
|
||||
//# END
|
||||
//# DEFINE fetch_msg_assign_state
|
||||
cmd->data = data;
|
||||
//# END
|
||||
//# DEFINE fetch_msg_print_fmt_args , uid=%u, want_flags=%s, want_date=%s
|
||||
//# DEFINE fetch_msg_print_pass_args , msg->uid, !(msg->status & M_FLAGS) ? "yes" : "no", data->date ? "yes" : "no"
|
||||
//# DEFINE fetch_msg_pre_print_cb_args
|
||||
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=%u
|
||||
//# DEFINE fetch_msg_print_pass_cb_args , fbuf, (long long)cmd->data->date, cmd->data->len
|
||||
//# DEFINE fetch_msg_print_cb_args
|
||||
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->ctx->label );
|
||||
fflush( stdout );
|
||||
}
|
||||
//# END
|
||||
|
||||
//# DEFINE store_msg_pre_print_args
|
||||
static char fbuf[as(Flags) + 1];
|
||||
proxy_make_flags( data->flags, fbuf );
|
||||
//# END
|
||||
//# 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) {
|
||||
printf( "%s>>>>>>>>>\n", ctx->label );
|
||||
fwrite( data->data, data->len, 1, stdout );
|
||||
printf( "%s>>>>>>>>>\n", ctx->label );
|
||||
fflush( stdout );
|
||||
}
|
||||
//# END
|
||||
|
||||
//# DEFINE set_msg_flags_pre_print_args
|
||||
static char fbuf1[as(Flags) + 1], fbuf2[as(Flags) + 1];
|
||||
proxy_make_flags( add, fbuf1 );
|
||||
proxy_make_flags( del, fbuf2 );
|
||||
//# 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 set_bad_callback
|
||||
static void
|
||||
proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
|
||||
{
|
||||
proxy_store_t *ctx = (proxy_store_t *)gctx;
|
||||
|
||||
ctx->bad_callback = cb;
|
||||
ctx->bad_callback_aux = 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 );
|
||||
proxy_store_deref( ctx );
|
||||
}
|
||||
|
||||
//# EXCLUDE alloc_store
|
||||
store_t *
|
||||
proxy_alloc_store( store_t *real_ctx, const char *label )
|
||||
{
|
||||
proxy_store_t *ctx;
|
||||
|
||||
ctx = nfcalloc( sizeof(*ctx) );
|
||||
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;
|
||||
}
|
||||
|
||||
//# EXCLUDE parse_store
|
||||
//# EXCLUDE cleanup
|
||||
//# EXCLUDE get_fail_state
|
||||
|
||||
#include "drv_proxy.inc"
|
198
src/drv_proxy_gen.pl
Executable file
198
src/drv_proxy_gen.pl
Executable file
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# mbsync - mailbox synchronizer
|
||||
# Copyright (C) 2017 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/>.
|
||||
#
|
||||
# As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
# despite that library's more restrictive license.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
die("Usage: $0 driver.h drv_proxy.c drv_proxy.inc\n")
|
||||
if ($#ARGV != 2);
|
||||
|
||||
my ($in_header, $in_source, $out_source) = @ARGV;
|
||||
|
||||
my %templates;
|
||||
my %defines;
|
||||
my %excluded;
|
||||
my %special;
|
||||
|
||||
open(my $ins, $in_source) or die("Cannot open $in_source: $!\n");
|
||||
my $template;
|
||||
my $define;
|
||||
my $conts;
|
||||
while (<$ins>) {
|
||||
if ($template) {
|
||||
if (/^\/\/\# END$/) {
|
||||
$templates{$template} = $conts;
|
||||
$template = undef;
|
||||
} else {
|
||||
$conts .= $_;
|
||||
}
|
||||
} elsif ($define) {
|
||||
if (/^\/\/\# END$/) {
|
||||
$defines{$define} = $conts;
|
||||
$define = undef;
|
||||
} else {
|
||||
($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_");
|
||||
$conts .= $_;
|
||||
}
|
||||
} else {
|
||||
if (/^\/\/\# TEMPLATE (\w+)$/) {
|
||||
$template = $1;
|
||||
$conts = "";
|
||||
} elsif (/^\/\/\# DEFINE (\w+)$/) {
|
||||
$define = $1;
|
||||
$conts = "";
|
||||
} elsif (/^\/\/\# DEFINE (\w+) (.*)$/) {
|
||||
$defines{$1} = $2;
|
||||
} elsif (/^\/\/\# UNDEFINE (\w+)$/) {
|
||||
$defines{$1} = "";
|
||||
} elsif (/^\/\/\# EXCLUDE (\w+)$/) {
|
||||
$excluded{$1} = 1;
|
||||
} elsif (/^\/\/\# SPECIAL (\w+)$/) {
|
||||
$special{$1} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
close($ins);
|
||||
|
||||
open(my $inh, $in_header) or die("Cannot open $in_header: $!\n");
|
||||
my $sts = 0;
|
||||
my $cont = "";
|
||||
while (<$inh>) {
|
||||
if ($sts == 0) {
|
||||
if (/^struct driver \{$/) {
|
||||
$sts = 1;
|
||||
}
|
||||
} elsif ($sts == 1) {
|
||||
if (/^\};$/) {
|
||||
$sts = 0;
|
||||
} else {
|
||||
$cont .= $_;
|
||||
}
|
||||
}
|
||||
}
|
||||
close($inh);
|
||||
|
||||
$cont =~ s,\n, ,g;
|
||||
$cont =~ s,/\*.*?\*/, ,g;
|
||||
$cont =~ s,\h+, ,g;
|
||||
my @ptypes = map { s,^ ,,r } split(/;/, $cont);
|
||||
pop @ptypes; # last one is empty
|
||||
|
||||
my @cmd_table;
|
||||
|
||||
sub make_args($)
|
||||
{
|
||||
$_ = shift;
|
||||
s/(?:^|(?<=, ))(?:const )?\w+ \*?//g;
|
||||
return $_;
|
||||
}
|
||||
|
||||
sub type_to_format($)
|
||||
{
|
||||
$_ = shift;
|
||||
s/xint /\%\#x/g;
|
||||
s/uint /\%u/g;
|
||||
s/int /\%d/g;
|
||||
s/const char \*/\%s/g;
|
||||
return $_;
|
||||
}
|
||||
|
||||
sub make_format($)
|
||||
{
|
||||
$_ = type_to_format(shift);
|
||||
s/, (\%\#?.)(\w+)/, $2=$1/g;
|
||||
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, "NULL";
|
||||
next;
|
||||
}
|
||||
push @cmd_table, "proxy_$cmd_name";
|
||||
next if (defined($special{$cmd_name}));
|
||||
my %replace;
|
||||
$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_name =~ /^get_/) {
|
||||
$template = "GETTER";
|
||||
$replace{'fmt'} = type_to_format($cmd_type);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
for (keys %defines) {
|
||||
$replace{$1} = delete $defines{$_} if (/^${cmd_name}_(.*)$/);
|
||||
}
|
||||
my %used;
|
||||
my $text = $templates{$template};
|
||||
$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;
|
1156
src/main.c
Normal file
1156
src/main.c
Normal file
File diff suppressed because it is too large
Load diff
823
src/mbsync.1
Normal file
823
src/mbsync.1
Normal file
|
@ -0,0 +1,823 @@
|
|||
.\" 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.
|
||||
New messages, message deletions and flag changes can be propagated both ways;
|
||||
the operation set can be selected in a fine-grained manner.
|
||||
.br
|
||||
Synchronization is based on unique message identifiers (UIDs), so no
|
||||
identification conflicts can occur (unlike with some other mail synchronizers).
|
||||
OTOH, \fBmbsync\fR is susceptible to UID validity changes (but will recover
|
||||
just fine if the change is unfounded).
|
||||
Synchronization state is kept in one local text file per mailbox pair;
|
||||
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
|
||||
Read configuration from \fIfile\fR.
|
||||
By default, the configuration is read from ~/.mbsyncrc.
|
||||
.TP
|
||||
\fB-a\fR, \fB--all\fR
|
||||
Select all configured channels. Any channel/group specifications on the command
|
||||
line are ignored.
|
||||
.TP
|
||||
\fB-l\fR, \fB--list\fR
|
||||
Don't synchronize anything, but list all mailboxes in the selected channels
|
||||
and exit.
|
||||
.TP
|
||||
\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[\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[\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},\
|
||||
{\fB--new\fR|\fB--renew\fR|\fB--delete\fR|\fB--flags\fR|\fB--noop\fR|\fB--full\fR}
|
||||
.TP
|
||||
\r{\fB-L\fR|\fB-H\fR}[\fBn\fR][\fBN\fR][\fBd\fR][\fBf\fR],\
|
||||
{\fB--pull\fR|\fB--push\fR}[\fB-new\fR|\fB-renew\fR|\fB-delete\fR|\fB-flags\fR]
|
||||
Override any \fBSync\fR options from the config file. See below.
|
||||
.TP
|
||||
\fB-h\fR, \fB--help\fR
|
||||
Display a summary of command line options.
|
||||
.TP
|
||||
\fB-v\fR, \fB--version\fR
|
||||
Display version information.
|
||||
.TP
|
||||
\fB-V\fR, \fB--verbose\fR
|
||||
Enable \fIverbose\fR mode, which displays what is currently happening.
|
||||
.TP
|
||||
\fB-D\fR[\fBC\fR][\fBd\fR|\fBD\fR][\fBm\fR][\fBM\fR][\fBn\fR|\fBN\fR][\fBs\fR]\fR]\fR,\
|
||||
\fB--debug\fR[\fB-crash\fR|\fB-driver\fR|\fB-driver-all\fR|\fB-maildir\fR|\fB-main\fR|\fB-net\fR|\fB-net-all\fR|\fB-sync\fR]
|
||||
Enable debugging categories:
|
||||
.in +4
|
||||
\fBC\fR, \fBcrash\fR - use built-in crash handler
|
||||
.br
|
||||
\fBd\fR, \fBdriver\fR - print driver calls (metadata only)
|
||||
.br
|
||||
\fBD\fR, \fBdriver-all\fR - print driver calls (including messages)
|
||||
.br
|
||||
\fBm\fR, \fBmaildir\fR - print maildir debug info
|
||||
.br
|
||||
\fBM\fR, \fBmain\fR - print main debug info
|
||||
.br
|
||||
\fBn\fR, \fBnet\fR - print network traffic (protocol only)
|
||||
.br
|
||||
\fBN\fR, \fBnet-all\fR - print network traffic (including payloads)
|
||||
.br
|
||||
\fBs\fR, \fBsync\fR - print synchronization debug info
|
||||
.in -4
|
||||
All categories except \fBcrash\fR implictly enable \fIverbose\fR mode.
|
||||
Without category specification, all categories except net-all are enabled.
|
||||
.TP
|
||||
\fB-q\fR, \fB--quiet\fR
|
||||
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.
|
||||
Configuration items are keywords followed by one or more arguments;
|
||||
arguments containing spaces must be enclosed in double quotes (\fB"\fR),
|
||||
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 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
|
||||
There are two basic object classes: Stores and Channels. A Store defines
|
||||
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 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
|
||||
Store's internal path separators, which may be slashes, periods, etc., or
|
||||
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
|
||||
In this context, the term "remote" describes the second Store within a Channel,
|
||||
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
|
||||
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 \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 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
|
||||
hierarchy delimiter \fB/\fR with \fIdelim\fR.
|
||||
This can be useful when the MUA used to access the Store provides
|
||||
suboptimal handling of hierarchical mailboxes, as is the case with
|
||||
\fBMutt\fR.
|
||||
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
|
||||
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
|
||||
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.
|
||||
.br
|
||||
The \fBnative\fR scheme is stolen from the latest Maildir patches to \fBc-client\fR
|
||||
and is therefore compatible with \fBpine\fR. The UID validity is stored in a
|
||||
file named .uidvalidity; the UIDs are encoded in the file names of the messages.
|
||||
.br
|
||||
The \fBalternative\fR scheme is based on the UID mapping used by \fBisync\fR
|
||||
versions 0.8 and 0.9.x. The invariant parts of the file names of the messages
|
||||
are used as keys into a Berkeley database named .isyncuidmap.db, which holds
|
||||
the UID validity as well.
|
||||
.br
|
||||
The \fBnative\fR scheme is faster, more space efficient, endianness independent
|
||||
and "human readable", but will be disrupted if a message is copied from another
|
||||
mailbox without getting a new file name; this would result in duplicated UIDs
|
||||
sooner or later, which in turn results in a UID validity change, making
|
||||
synchronization fail.
|
||||
The \fBalternative\fR scheme would fail if a MUA changed a message's file name
|
||||
in a part \fBmbsync\fR considers invariant; this would be interpreted as a
|
||||
message deletion and a new message, resulting in unnecessary traffic.
|
||||
.br
|
||||
\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.
|
||||
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 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:
|
||||
.br
|
||||
\fBVerbatim\fR - \fIPath\fB/top/sub/subsub\fR and \fIInbox\fB/sub/subsub\fR
|
||||
(this is the style you probably want to use)
|
||||
.br
|
||||
\fBMaildir++\fR - \fIInbox\fB/.top.sub.subsub\fR and \fIInbox\fB/..sub.subsub\fR
|
||||
(this style is compatible with Courier and Dovecot - but note that
|
||||
the mailbox metadata format is \fInot\fR compatible).
|
||||
Note that attempts to set \fBPath\fR are rejected in this mode.
|
||||
.br
|
||||
\fBLegacy\fR - \fIPath\fB/top/.sub/.subsub\fR and \fIInbox\fB/.sub/.subsub\fR
|
||||
(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.
|
||||
.br
|
||||
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.
|
||||
.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.
|
||||
In addition to the mechanisms listed in the SASL registry (link below),
|
||||
the legacy IMAP \fBLOGIN\fR mechanism is known.
|
||||
The wildcard \fB*\fR represents all mechanisms that are deemed secure
|
||||
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:
|
||||
.br
|
||||
\fBNone\fR - no security.
|
||||
This is the default when \fBTunnel\fR is set, as tunnels are usually secure.
|
||||
.br
|
||||
\fBSTARTTLS\fR - security is established via the STARTTLS extension
|
||||
after connecting the regular IMAP port 143. Most servers support this,
|
||||
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] [\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] [\fBTLSv1.3\fR]).
|
||||
.
|
||||
.TP
|
||||
\fBSystemCertificates\fR \fByes\fR|\fBno\fR
|
||||
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.
|
||||
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
|
||||
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.
|
||||
\fBClientKey\fR should also be specified.
|
||||
.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.
|
||||
Setting this to \fI1\fR disables pipelining.
|
||||
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
|
||||
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.
|
||||
(Default: taken from the server's first "personal" NAMESPACE)
|
||||
.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
|
||||
{\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 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
|
||||
times); later matches take precedence.
|
||||
.br
|
||||
Note that \fBINBOX\fR is not matched by wildcards, unless it lives under
|
||||
\fBPath\fR.
|
||||
.br
|
||||
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 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 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
|
||||
the actual date of the message) will be deleted first.
|
||||
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.
|
||||
Normally, unread messages are considered important and thus never expired.
|
||||
This ensures that you never miss new messages even after an extended absence.
|
||||
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 far to near side.
|
||||
.br
|
||||
\fBPush\fR - propagate changes from near to far side.
|
||||
.br
|
||||
\fBNew\fR - propagate newly appeared messages.
|
||||
.br
|
||||
\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
|
||||
Store are marked as deleted only, i.e., they won't be really deleted until
|
||||
that Store is expunged.
|
||||
.br
|
||||
\fBFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as
|
||||
well; this is particularly interesting if you use \fBmutt\fR with the
|
||||
maildir_trash option.
|
||||
.br
|
||||
\fBAll\fR (\fB--full\fR on the command line) - all of the above.
|
||||
This is the global default.
|
||||
.br
|
||||
\fBNone\fR (\fB--noop\fR on the command line) - don't propagate anything.
|
||||
Useful if you want to expunge only.
|
||||
.IP
|
||||
\fBPull\fR and \fBPush\fR are direction flags, while \fBNew\fR, \fBReNew\fR,
|
||||
\fBDelete\fR and \fBFlags\fR are type flags. The two flag classes make up a
|
||||
two-dimensional matrix (a table). Its cells are the individual actions to
|
||||
perform. There are two styles of asserting the cells:
|
||||
.br
|
||||
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 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 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
|
||||
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 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|\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|\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
|
||||
For MailDir mailboxes it is sufficient to delete the cur/ subdirectory to
|
||||
mark them as deleted. This ensures compatibility with \fBSyncState *\fR.
|
||||
.br
|
||||
Note that for safety, non-empty mailboxes are never deleted.
|
||||
.br
|
||||
(Global default: \fBNone\fR)
|
||||
.
|
||||
.TP
|
||||
\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
|
||||
the messages.
|
||||
Enabling this makes sense in order to keep the time stamp based message
|
||||
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 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:\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...]]] ...
|
||||
Define the Group \fIname\fR, opening a section for its parameters.
|
||||
Note that even though Groups have an own namespace, they will "hide" Channels
|
||||
with the same name on the command line.
|
||||
.br
|
||||
One or more Channels can be specified on the same line.
|
||||
.br
|
||||
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
|
||||
.br
|
||||
Selects whether \fBmbsync\fR performs forced flushing, which determines
|
||||
the level of data safety after system crashes and power outages.
|
||||
Disabling it is reasonably safe for file systems which are mounted with
|
||||
data=ordered mode.
|
||||
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
|
||||
\fBSyncState\fR.
|
||||
\fBmbsync\fR prefers to use the colon, but this is incompatible with
|
||||
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
|
||||
\fBmbsync\fR will refrain from using more memory. Note that this is no
|
||||
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 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 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
|
||||
be configured not to do it.
|
||||
.P
|
||||
By default, \fBmbsync\fR will not delete any messages - deletions are
|
||||
propagated by marking the messages as deleted on the remote store.
|
||||
Once you have verified that your setup works, you will typically want to
|
||||
set \fBExpunge\fR to \fBBoth\fR, so that deletions become effective.
|
||||
.P
|
||||
\fBmbsync\fR's built-in trash functionality relies on \fBmbsync\fR doing
|
||||
the expunging of deleted messages. This is the case when it propagates
|
||||
deletions of previously propagated messages, and the trash is on the target
|
||||
store (typically your IMAP server).
|
||||
.br
|
||||
However, when you intend \fBmbsync\fR to trash messages which were not
|
||||
propagated yet, the MUA must mark the messages as deleted without expunging
|
||||
them (e.g., \fBMutt\fR's \fBmaildir_trash\fR option). Note that most
|
||||
messages are propagated a long time before they are deleted, so this is a
|
||||
corner case you probably do not want to optimize for. This also implies
|
||||
that the \fBTrashNewOnly\fR and \fBTrashRemoteNew\fR options are typically
|
||||
not very useful.
|
||||
.P
|
||||
If your server supports auto-trashing (as Gmail does), it is probably a
|
||||
good idea to rely on that instead of \fBmbsync\fR's trash functionality.
|
||||
If you do that, and intend to synchronize the trash like other mailboxes,
|
||||
you should not use \fBmbsync\fR's \fBTrash\fR option at all.
|
||||
.P
|
||||
Use of the \fBTrash\fR option with M$ Exchange 2013 requires the use of
|
||||
\fBDisableExtension MOVE\fR due to a server bug.
|
||||
.P
|
||||
When using the more efficient default UID mapping scheme, it is important
|
||||
that the MUA renames files when moving them between Maildir folders.
|
||||
Mutt always does that, while mu4e needs to be configured to do it:
|
||||
.br
|
||||
.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.
|
||||
.P
|
||||
Using \fBTrash\fR on IMAP Stores without the UIDPLUS extension (notably,
|
||||
M$ Exchange up to at least 2010) bears a race condition: messages will be
|
||||
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
|
||||
Default configuration file
|
||||
.TP
|
||||
.B ~/.mbsync/
|
||||
Directory containing synchronization state files
|
||||
.
|
||||
.SH SEE ALSO
|
||||
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,
|
||||
contributions by Theodore Y. Ts'o.
|
97
src/mbsyncrc.sample
Normal file
97
src/mbsyncrc.sample
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Global configuration section
|
||||
# Values here are used as defaults for any following Channel section that
|
||||
# doesn't specify them.
|
||||
Expunge None
|
||||
Create Both
|
||||
|
||||
MaildirStore local
|
||||
Path ~/Mail/
|
||||
Trash Trash
|
||||
|
||||
|
||||
IMAPStore work
|
||||
Host work.host.com
|
||||
User tehuser
|
||||
Pass xxxxxxxx
|
||||
# Fetch password from gnome-keyring:
|
||||
#PassCmd "gnome-keyring-query get mail_pw"
|
||||
# Fetch password from .netrc:
|
||||
#PassCmd "sed -n -e 's,^machine work\\.host\\.com login tehuser password \\(.*\\),\\1,p' < $HOME/.netrc"
|
||||
# Fetch password from a gpg-encrypted file:
|
||||
#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"
|
||||
|
||||
Channel work
|
||||
Far :work:
|
||||
Near :local:work
|
||||
Expunge Near
|
||||
Sync PullNew Push
|
||||
|
||||
|
||||
IMAPStore personal
|
||||
Host host.play.com
|
||||
Port 6789
|
||||
RequireSSL no
|
||||
|
||||
Channel personal
|
||||
Far :personal:
|
||||
Near :local:personal
|
||||
Expunge Both
|
||||
MaxMessages 150
|
||||
MaxSize 200k
|
||||
|
||||
IMAPStore remote
|
||||
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
|
||||
|
||||
Channel remote
|
||||
Far :remote:
|
||||
Near :local:remote
|
||||
|
||||
|
||||
Group boxes
|
||||
Channels work personal remote
|
||||
|
||||
|
||||
IMAPStore st1
|
||||
Host st1.domain.com
|
||||
RequireCRAM yes
|
||||
CertificateFile ~/.st1-certificate.crt
|
||||
|
||||
IMAPStore st2
|
||||
Host imap.another-domain.com
|
||||
Path non-standard/
|
||||
RequireSSL no
|
||||
UseTLSv1 no
|
||||
|
||||
Channel rst
|
||||
Far :st1:somebox
|
||||
Near :st2:
|
||||
|
||||
|
||||
IMAPAccount server
|
||||
Host imaps:foo.bar.com
|
||||
CertificateFile ~/.server-certificate.crt
|
||||
|
||||
IMAPStore server
|
||||
Account server
|
||||
MapInbox inbox
|
||||
Trash ~/trash
|
||||
TrashRemoteNew yes
|
||||
|
||||
MaildirStore mirror
|
||||
Path ~/Maildir/
|
||||
SubFolders Verbatim
|
||||
|
||||
Channel o2o
|
||||
Far :server:
|
||||
Near :mirror:
|
||||
Patterns %
|
||||
|
||||
Group partial o2o:inbox,sent-mail,foobar
|
||||
|
||||
# INBOX => server, INBOX.foo => server.foo, etc.
|
||||
Channel inbox
|
||||
Far :server:INBOX
|
||||
Near :mirror:server
|
||||
Patterns *
|
50
src/mdconvert.1
Normal file
50
src/mdconvert.1
Normal file
|
@ -0,0 +1,50 @@
|
|||
.ig
|
||||
\" mdconvert - Maildir mailbox UID storage scheme converter
|
||||
\" Copyright (C) 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 mdconvert 1 "2004 Mar 27"
|
||||
..
|
||||
.SH NAME
|
||||
mdconvert - Maildir mailbox UID storage scheme converter
|
||||
..
|
||||
.SH SYNOPSIS
|
||||
\fBmdconvert\fR [\fIoptions\fR ...] \fImailbox\fR ...
|
||||
..
|
||||
.SH DESCRIPTION
|
||||
\fBmdconvert\fR converts Maildir mailboxes between the two UID storage schemes
|
||||
supported by \fBmbsync\fR. See \fBmbsync\fR's manual page for details on these
|
||||
schemes.
|
||||
..
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-a\fR, \fB--alt\fR
|
||||
Convert to the \fBalternative\fR (Berkeley DB based) UID storage scheme.
|
||||
.TP
|
||||
\fB-n\fR, \fB--native\fR
|
||||
Convert to the \fBnative\fR (file name based) UID storage scheme.
|
||||
This is the default.
|
||||
.TP
|
||||
\fB-h\fR, \fB--help\fR
|
||||
Displays a summary of command line options.
|
||||
.TP
|
||||
\fB-v\fR, \fB--version\fR
|
||||
Displays version information.
|
||||
..
|
||||
.SH SEE ALSO
|
||||
mbsync(1)
|
||||
..
|
||||
.SH AUTHOR
|
||||
Written and maintained by Oswald Buddenhagen <ossi@users.sf.net>.
|
284
src/mdconvert.c
Normal file
284
src/mdconvert.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* mdconvert - Maildir UID scheme converter
|
||||
* Copyright (C) 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <db.h>
|
||||
|
||||
#define EXE "mdconvert"
|
||||
|
||||
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
|
||||
# define ATTR_NORETURN __attribute__((noreturn))
|
||||
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
|
||||
#else
|
||||
# define ATTR_NORETURN
|
||||
# define ATTR_PRINTFLIKE(fmt,var)
|
||||
#endif
|
||||
|
||||
static void ATTR_NORETURN
|
||||
oob( void )
|
||||
{
|
||||
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
|
||||
abort();
|
||||
}
|
||||
|
||||
static void ATTR_PRINTFLIKE(1, 2)
|
||||
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 );
|
||||
}
|
||||
|
||||
static int ATTR_PRINTFLIKE(3, 4)
|
||||
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 const char *subdirs[] = { "cur", "new" };
|
||||
static struct flock lck;
|
||||
static DBT key, value;
|
||||
|
||||
static int
|
||||
convert( const char *box, int altmap )
|
||||
{
|
||||
DB *db;
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
const char *u, *ru;
|
||||
char *p, *s, *dpath, *spath, *dbpath;
|
||||
int i, n, ret, sfd, dfd, bl, ml, uv[2], uid;
|
||||
struct stat st;
|
||||
char buf[_POSIX_PATH_MAX], buf2[_POSIX_PATH_MAX];
|
||||
char umpath[_POSIX_PATH_MAX], uvpath[_POSIX_PATH_MAX], tdpath[_POSIX_PATH_MAX];
|
||||
|
||||
if (stat( box, &st ) || !S_ISDIR(st.st_mode)) {
|
||||
fprintf( stderr, "'%s' is no Maildir mailbox.\n", box );
|
||||
return 1;
|
||||
}
|
||||
|
||||
nfsnprintf( umpath, sizeof(umpath), "%s/.isyncuidmap.db", box );
|
||||
nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", box );
|
||||
if (altmap)
|
||||
dpath = umpath, spath = uvpath, dbpath = tdpath;
|
||||
else
|
||||
spath = umpath, dpath = uvpath, dbpath = umpath;
|
||||
nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath );
|
||||
if ((sfd = open( spath, O_RDWR )) < 0) {
|
||||
if (errno != ENOENT)
|
||||
sys_error( "Cannot open %s", spath );
|
||||
return 1;
|
||||
}
|
||||
if (fcntl( sfd, F_SETLKW, &lck )) {
|
||||
sys_error( "Cannot lock %s", spath );
|
||||
goto sbork;
|
||||
}
|
||||
if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) {
|
||||
sys_error( "Cannot create %s", tdpath );
|
||||
goto sbork;
|
||||
}
|
||||
if (db_create( &db, NULL, 0 )) {
|
||||
fputs( "Error: db_create() failed\n", stderr );
|
||||
goto tbork;
|
||||
}
|
||||
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 );
|
||||
tbork:
|
||||
unlink( tdpath );
|
||||
close( dfd );
|
||||
sbork:
|
||||
close( sfd );
|
||||
return 1;
|
||||
}
|
||||
key.data = (void *)"UIDVALIDITY";
|
||||
key.size = 11;
|
||||
if (altmap) {
|
||||
if ((n = read( sfd, buf, sizeof(buf) - 1 )) <= 0 ||
|
||||
(buf[n] = 0, sscanf( buf, "%d\n%d", &uv[0], &uv[1] ) != 2))
|
||||
{
|
||||
fprintf( stderr, "Error: cannot read UIDVALIDITY of '%s'.\n", box );
|
||||
goto dbork;
|
||||
}
|
||||
value.data = uv;
|
||||
value.size = sizeof(uv);
|
||||
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, NULL, &key, &value, 0 ))) {
|
||||
db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box );
|
||||
goto dbork;
|
||||
}
|
||||
n = sprintf( buf, "%d\n%d\n", ((int *)value.data)[0], ((int *)value.data)[1] );
|
||||
if (write( dfd, buf, n ) != n) {
|
||||
fprintf( stderr, "Error: cannot write UIDVALIDITY for '%s'.\n", box );
|
||||
goto dbork;
|
||||
}
|
||||
}
|
||||
|
||||
again:
|
||||
for (i = 0; i < 2; i++) {
|
||||
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] );
|
||||
if (!(d = opendir( buf ))) {
|
||||
sys_error( "Cannot list %s", buf );
|
||||
goto dbork;
|
||||
}
|
||||
while ((e = readdir( d ))) {
|
||||
if (*e->d_name == '.')
|
||||
continue;
|
||||
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", e->d_name );
|
||||
memcpy( buf2, buf, bl );
|
||||
p = strstr( e->d_name, ",U=" );
|
||||
if (p)
|
||||
for (u = p, ru = p + 3; isdigit( (unsigned char)*ru ); ru++);
|
||||
else
|
||||
u = ru = strchr( e->d_name, ':' );
|
||||
if (u)
|
||||
ml = u - e->d_name;
|
||||
else
|
||||
ru = "", ml = sizeof(buf);
|
||||
if (altmap) {
|
||||
if (!p)
|
||||
continue;
|
||||
key.data = e->d_name;
|
||||
key.size = (size_t)(strchr( e->d_name, ',' ) - e->d_name);
|
||||
uid = atoi( p + 3 );
|
||||
value.data = &uid;
|
||||
value.size = sizeof(uid);
|
||||
if ((ret = db->put( db, NULL, &key, &value, 0 ))) {
|
||||
db->err( db, ret, "Error: cannot write UID for '%s'", box );
|
||||
goto ebork;
|
||||
}
|
||||
nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s%s", ml, e->d_name, ru );
|
||||
} else {
|
||||
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, NULL, &key, &value, 0 ))) {
|
||||
if (ret != DB_NOTFOUND) {
|
||||
db->err( db, ret, "Error: cannot read UID for '%s'", box );
|
||||
goto ebork;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
uid = *(int *)value.data;
|
||||
nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s,U=%d%s", ml, e->d_name, uid, ru );
|
||||
}
|
||||
if (rename( buf, buf2 )) {
|
||||
if (errno == ENOENT) {
|
||||
closedir( d );
|
||||
goto again;
|
||||
}
|
||||
sys_error( "Cannot rename %s to %s", buf, buf2 );
|
||||
ebork:
|
||||
closedir( d );
|
||||
goto dbork;
|
||||
}
|
||||
|
||||
}
|
||||
closedir( d );
|
||||
}
|
||||
|
||||
db->close( db, 0 );
|
||||
close( dfd );
|
||||
if (rename( tdpath, dpath )) {
|
||||
sys_error( "Cannot rename %s to %s", tdpath, dpath );
|
||||
close( sfd );
|
||||
return 1;
|
||||
}
|
||||
if (unlink( spath ))
|
||||
sys_error( "Cannot remove %s", spath );
|
||||
close( sfd );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main( int argc, char **argv )
|
||||
{
|
||||
int oint, ret, altmap = 0;
|
||||
|
||||
for (oint = 1; oint < argc; oint++) {
|
||||
if (!strcmp( argv[oint], "-h" ) || !strcmp( argv[oint], "--help" )) {
|
||||
puts(
|
||||
"Usage: " EXE " [-a] mailbox...\n"
|
||||
" -a, --alt convert to alternative (DB based) UID scheme\n"
|
||||
" -n, --native convert to native (file name based) UID scheme (default)\n"
|
||||
" -h, --help show this help message\n"
|
||||
" -v, --version display version"
|
||||
);
|
||||
return 0;
|
||||
} else if (!strcmp( argv[oint], "-v" ) || !strcmp( argv[oint], "--version" )) {
|
||||
puts( EXE " " VERSION " - Maildir UID scheme converter" );
|
||||
return 0;
|
||||
} else if (!strcmp( argv[oint], "-a" ) || !strcmp( argv[oint], "--alt" )) {
|
||||
altmap = 1;
|
||||
} else if (!strcmp( argv[oint], "-n" ) || !strcmp( argv[oint], "--native" )) {
|
||||
altmap = 0;
|
||||
} else if (!strcmp( argv[oint], "--" )) {
|
||||
oint++;
|
||||
break;
|
||||
} else if (argv[oint][0] == '-') {
|
||||
fprintf( stderr, "Unrecognized option '%s'. Try " EXE " -h\n", argv[oint] );
|
||||
return 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (oint == argc) {
|
||||
fprintf( stderr, "Mailbox specification missing. Try " EXE " -h\n" );
|
||||
return 1;
|
||||
}
|
||||
#if SEEK_SET != 0
|
||||
lck.l_whence = SEEK_SET;
|
||||
#endif
|
||||
#if F_WRLCK != 0
|
||||
lck.l_type = F_WRLCK;
|
||||
#endif
|
||||
ret = 0;
|
||||
for (; oint < argc; oint++)
|
||||
ret |= convert( argv[oint], altmap );
|
||||
return ret;
|
||||
}
|
||||
|
806
src/run-tests.pl
Executable file
806
src/run-tests.pl
Executable file
|
@ -0,0 +1,806 @@
|
|||
#! /usr/bin/perl -w
|
||||
#
|
||||
# Copyright (C) 2006,2013 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/>.
|
||||
#
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Path;
|
||||
use File::Temp 'tempdir';
|
||||
|
||||
my $use_vg = $ENV{USE_VALGRIND};
|
||||
my $mbsync = getcwd()."/mbsync";
|
||||
|
||||
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($$$$);
|
||||
|
||||
################################################################################
|
||||
|
||||
# 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,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 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, "" ],
|
||||
);
|
||||
|
||||
my @O01 = ("", "", "");
|
||||
#show("01", "01", "01");
|
||||
my @X01 = (
|
||||
[ 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,
|
||||
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);
|
||||
|
||||
my @O02 = ("", "", "Expunge Both\n");
|
||||
#show("01", "02", "02");
|
||||
my @X02 = (
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", I, 9, "", J, 10, "" ],
|
||||
[ 10,
|
||||
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);
|
||||
|
||||
my @O03 = ("", "", "Expunge Near\n");
|
||||
#show("01", "03", "03");
|
||||
my @X03 = (
|
||||
[ 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,
|
||||
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 near side", \@x01, \@X03, \@O03);
|
||||
|
||||
my @O04 = ("", "", "Sync Pull\n");
|
||||
#show("01", "04", "04");
|
||||
my @X04 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 10,
|
||||
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);
|
||||
|
||||
my @O05 = ("", "", "Sync Flags\n");
|
||||
#show("01", "05", "05");
|
||||
my @X05 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 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);
|
||||
|
||||
my @O06 = ("", "", "Sync Delete\n");
|
||||
#show("01", "06", "06");
|
||||
my @X06 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "" ],
|
||||
[ 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);
|
||||
|
||||
my @O07 = ("", "", "Sync New\n");
|
||||
#show("01", "07", "07");
|
||||
my @X07 = (
|
||||
[ 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,
|
||||
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);
|
||||
|
||||
my @O08 = ("", "", "Sync PushFlags PullDelete\n");
|
||||
#show("01", "08", "08");
|
||||
my @X08 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 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);
|
||||
|
||||
# size restriction tests
|
||||
|
||||
my @x10 = (
|
||||
[ 2,
|
||||
A, 1, "", B, 2, "*" ],
|
||||
[ 1,
|
||||
C, 1, "*" ],
|
||||
[ 0, 0, 0,
|
||||
],
|
||||
);
|
||||
|
||||
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "Expunge Near");
|
||||
#show("10", "11", "11");
|
||||
my @X11 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "*", C, 3, "?" ],
|
||||
[ 3,
|
||||
C, 1, "*", A, 2, "", B, 3, "?" ],
|
||||
[ 3, 0, 3,
|
||||
3, 1, "<", 1, 2, "", 2, 3, ">" ],
|
||||
);
|
||||
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,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
[ 0,
|
||||
],
|
||||
[ 0, 0, 0,
|
||||
],
|
||||
);
|
||||
|
||||
my @O31 = ("", "", "MaxMessages 3\n");
|
||||
#show("30", "31", "31");
|
||||
my @X31 = (
|
||||
[ 6,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
[ 5,
|
||||
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);
|
||||
|
||||
my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n");
|
||||
#show("30", "32", "32");
|
||||
my @X32 = (
|
||||
[ 6,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
[ 4,
|
||||
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);
|
||||
|
||||
my @x50 = (
|
||||
[ 6,
|
||||
A, 1, "FS", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 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, "" ],
|
||||
);
|
||||
|
||||
my @O51 = ("", "", "MaxMessages 3\nExpunge Both\n");
|
||||
#show("50", "51", "51");
|
||||
my @X51 = (
|
||||
[ 6,
|
||||
A, 1, "S", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
|
||||
[ 6,
|
||||
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);
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
print "OK.\n";
|
||||
exit 0;
|
||||
|
||||
|
||||
sub qm($)
|
||||
{
|
||||
shift;
|
||||
s/\\/\\\\/g;
|
||||
s/\"/\\"/g;
|
||||
s/\"/\\"/g;
|
||||
s/\n/\\n/g;
|
||||
return $_;
|
||||
}
|
||||
|
||||
# [ $far, $near, $channel ]
|
||||
sub writecfg($)
|
||||
{
|
||||
my ($sfx) = @_;
|
||||
|
||||
open(FILE, ">", ".mbsyncrc") or
|
||||
die "Cannot open .mbsyncrc.\n";
|
||||
print FILE
|
||||
"FSync no
|
||||
|
||||
MaildirStore far
|
||||
Path ./
|
||||
Inbox ./far
|
||||
".$$sfx[0]."
|
||||
MaildirStore near
|
||||
Path ./
|
||||
Inbox ./near
|
||||
".$$sfx[1]."
|
||||
Channel test
|
||||
Far :far:
|
||||
Near :near:
|
||||
SyncState *
|
||||
".$$sfx[2];
|
||||
close FILE;
|
||||
}
|
||||
|
||||
sub killcfg()
|
||||
{
|
||||
unlink $_ for (glob("*.log"));
|
||||
unlink ".mbsyncrc";
|
||||
}
|
||||
|
||||
# $run_async, $mbsync_options, $log_file
|
||||
# Return: $exit_code, \@mbsync_output
|
||||
sub runsync($$$)
|
||||
{
|
||||
my ($async, $flags, $file) = @_;
|
||||
|
||||
my $cmd;
|
||||
if ($use_vg) {
|
||||
$cmd = "valgrind -q --error-exitcode=1 ";
|
||||
} else {
|
||||
$flags .= " -D";
|
||||
}
|
||||
$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");
|
||||
if ($file) {
|
||||
open FILE, ">$file" or die("Cannot create $file: $!\n");
|
||||
print FILE @out;
|
||||
close FILE;
|
||||
}
|
||||
return $?, \@out;
|
||||
}
|
||||
|
||||
|
||||
# $path
|
||||
# Return: $max_uid, { uid => [ seq, flags ] }
|
||||
sub readbox($)
|
||||
{
|
||||
my $bn = shift;
|
||||
|
||||
(-d $bn) or
|
||||
die "No mailbox '$bn'.\n";
|
||||
(-d $bn."/tmp" and -d $bn."/new" and -d $bn."/cur") or
|
||||
die "Invalid mailbox '$bn'.\n";
|
||||
open(FILE, "<", $bn."/.uidvalidity") or die "Cannot read UID validity of mailbox '$bn'.\n";
|
||||
my $dummy = <FILE>;
|
||||
chomp(my $mu = <FILE>);
|
||||
close FILE;
|
||||
my %ms = ();
|
||||
for my $d ("cur", "new") {
|
||||
opendir(DIR, $bn."/".$d) or next;
|
||||
for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
|
||||
my ($uid, $flg, $ph, $num);
|
||||
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
|
||||
($uid, $flg) = ($1, $2);
|
||||
} else {
|
||||
print STDERR "unrecognided file name '$f' in '$bn'.\n";
|
||||
exit 1;
|
||||
}
|
||||
open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n";
|
||||
my $sz = 0;
|
||||
while (<FILE>) {
|
||||
/^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = $2);
|
||||
$sz += length($_);
|
||||
}
|
||||
close FILE;
|
||||
if (!defined($num)) {
|
||||
print STDERR "message '$f' in '$bn' has no identifier.\n";
|
||||
exit 1;
|
||||
}
|
||||
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":""));
|
||||
}
|
||||
}
|
||||
return $mu, \%ms;
|
||||
}
|
||||
|
||||
# $boxname
|
||||
# Output:
|
||||
# [ maxuid,
|
||||
# serial, uid, "flags", ... ],
|
||||
sub showbox($)
|
||||
{
|
||||
my ($bn) = @_;
|
||||
|
||||
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(\@bc);
|
||||
}
|
||||
|
||||
# $filename
|
||||
# Output:
|
||||
# [ maxuid[F], maxxfuid, maxuid[N],
|
||||
# uid[F], uid[N], "flags", ... ],
|
||||
sub showstate($)
|
||||
{
|
||||
my ($fn) = @_;
|
||||
|
||||
if (!open(FILE, "<", $fn)) {
|
||||
print STDERR " Cannot read sync state $fn: $!\n";
|
||||
return;
|
||||
}
|
||||
chomp(my @ls = <FILE>);
|
||||
close FILE;
|
||||
my %hdr;
|
||||
OUTER: while (1) {
|
||||
while (@ls) {
|
||||
$_ = shift(@ls);
|
||||
last OUTER if (!length($_));
|
||||
if (!/^([^ ]+) (\d+)$/) {
|
||||
print STDERR "Malformed sync state header entry: $_\n";
|
||||
close FILE;
|
||||
return;
|
||||
}
|
||||
$hdr{$1} = $2;
|
||||
}
|
||||
print STDERR "Unterminated sync state header.\n";
|
||||
close FILE;
|
||||
return;
|
||||
}
|
||||
my @T = ($hdr{'MaxPulledUid'} // "missing",
|
||||
$hdr{'MaxExpiredFarUid'} // "0",
|
||||
$hdr{'MaxPushedUid'} // "missing");
|
||||
for (@ls) {
|
||||
/^(\d+) (\d+) (.*)$/;
|
||||
push @T, $1, $2, $3;
|
||||
}
|
||||
printstate(\@T);
|
||||
}
|
||||
|
||||
# $filename
|
||||
sub showchan($)
|
||||
{
|
||||
my ($fn) = @_;
|
||||
|
||||
showbox("far");
|
||||
showbox("near");
|
||||
showstate($fn);
|
||||
}
|
||||
|
||||
# $source_state_name, $target_state_name, $configs_name
|
||||
sub show($$$)
|
||||
{
|
||||
my ($sx, $tx, $sfxn) = @_;
|
||||
my ($sp, $sfx);
|
||||
eval "\$sp = \\\@x$sx";
|
||||
eval "\$sfx = \\\@O$sfxn";
|
||||
mkchan($$sp[0], $$sp[1], $$sp[2]);
|
||||
print "my \@x$sx = (\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
print ");\n";
|
||||
writecfg($sfx);
|
||||
runsync(0, "", "");
|
||||
killcfg();
|
||||
print "my \@X$tx = (\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
print ");\n";
|
||||
print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n";
|
||||
rmtree "near";
|
||||
rmtree "far";
|
||||
}
|
||||
|
||||
# $box_name, \@box_state
|
||||
sub mkbox($$)
|
||||
{
|
||||
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$$bs[0]\n";
|
||||
close FILE;
|
||||
for (my $i = 1; $i < @$bs; $i += 3) {
|
||||
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
|
||||
my $big = $flg =~ s/\*//;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
# \@far_state, \@near_state, \@sync_state
|
||||
sub mkchan($$$)
|
||||
{
|
||||
my ($f, $n, $t) = @_;
|
||||
mkbox("far", $f);
|
||||
mkbox("near", $n);
|
||||
open(FILE, ">", "near/.mbsyncstate") or
|
||||
die "Cannot create sync state.\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;
|
||||
}
|
||||
|
||||
# $box_name, \@box_state
|
||||
sub ckbox($$)
|
||||
{
|
||||
my ($bn, $bs) = @_;
|
||||
|
||||
my ($mu, $ms) = readbox($bn);
|
||||
if ($mu != $$bs[0]) {
|
||||
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\n";
|
||||
return 1;
|
||||
}
|
||||
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 ($$m[0] ne $num) {
|
||||
print STDERR "Subject mismatch for $bn:$uid.\n";
|
||||
return 1;
|
||||
}
|
||||
if ($$m[1] ne $flg) {
|
||||
print STDERR "Flag mismatch for $bn:$uid.\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (%$ms) {
|
||||
print STDERR "Excess messages in '$bn': ".join(", ", sort({ $a <=> $b } keys(%$ms))).".\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# $state_file, \@sync_state
|
||||
sub ckstate($$)
|
||||
{
|
||||
my ($fn, $t) = @_;
|
||||
my %hdr;
|
||||
$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;
|
||||
OUTER: while (1) {
|
||||
while (@ls) {
|
||||
my $l = shift(@ls);
|
||||
last OUTER if (!length($l));
|
||||
if ($l !~ /^([^ ]+) (\d+)$/) {
|
||||
print STDERR "Malformed sync state header entry: $l\n";
|
||||
return 1;
|
||||
}
|
||||
my $want = delete $hdr{$1};
|
||||
if (!defined($want)) {
|
||||
print STDERR "Unexpected sync state header entry: $1\n";
|
||||
return 1;
|
||||
}
|
||||
if ($2 != $want) {
|
||||
print STDERR "Sync state header entry $1 mismatch: got $2, wanted $want\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
print STDERR "Unterminated sync state header.\n";
|
||||
return 1;
|
||||
}
|
||||
my @ky = keys %hdr;
|
||||
if (@ky) {
|
||||
print STDERR "Keys missing from sync state header: @ky\n";
|
||||
return 1;
|
||||
}
|
||||
my $i = 3;
|
||||
for my $l (@ls) {
|
||||
if ($i == @$t) {
|
||||
print STDERR "Excess sync state entry: '$l'.\n";
|
||||
return 1;
|
||||
}
|
||||
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 ($i < @$t) {
|
||||
print STDERR "Missing sync state entry: '".$$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."'.\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# $state_file, \@chan_state
|
||||
sub ckchan($$)
|
||||
{
|
||||
my ($fn, $cs) = @_;
|
||||
my $rslt = ckstate($fn, $$cs[2]);
|
||||
$rslt |= ckbox("far", $$cs[0]);
|
||||
$rslt |= ckbox("near", $$cs[1]);
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
# \@box_state
|
||||
sub printbox($)
|
||||
{
|
||||
my ($bs) = @_;
|
||||
|
||||
print " [ $$bs[0],\n ";
|
||||
my $frst = 1;
|
||||
for (my $i = 1; $i < @$bs; $i += 3) {
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\"";
|
||||
}
|
||||
print " ],\n";
|
||||
}
|
||||
|
||||
# \@sync_state
|
||||
sub printstate($)
|
||||
{
|
||||
my ($t) = @_;
|
||||
|
||||
print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n ";
|
||||
my $frst = 1;
|
||||
for (my $i = 3; $i < @$t; $i += 3) {
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\"");
|
||||
}
|
||||
print " ],\n";
|
||||
}
|
||||
|
||||
# \@chan_state
|
||||
sub printchan($)
|
||||
{
|
||||
my ($cs) = @_;
|
||||
|
||||
printbox($$cs[0]);
|
||||
printbox($$cs[1]);
|
||||
printstate($$cs[2]);
|
||||
}
|
||||
|
||||
sub readfile($)
|
||||
{
|
||||
my ($file) = @_;
|
||||
|
||||
open(FILE, $file) or return;
|
||||
my @nj = <FILE>;
|
||||
close FILE;
|
||||
return \@nj;
|
||||
}
|
||||
|
||||
# $run_async, \@source_state, \@target_state, \@channel_configs
|
||||
sub test_impl($$$$)
|
||||
{
|
||||
my ($async, $sx, $tx, $sfx) = @_;
|
||||
|
||||
mkchan($$sx[0], $$sx[1], $$sx[2]);
|
||||
|
||||
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";
|
||||
if (!$xc) {
|
||||
print "Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Actual result:\n";
|
||||
showchan("near/.mbsyncstate.new");
|
||||
}
|
||||
print "Debug output:\n";
|
||||
print @$ret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
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 "Old State:\n";
|
||||
printstate($$sx[2]);
|
||||
print "Journal:\n".join("", @$nj)."\n";
|
||||
if (!$jxc) {
|
||||
print "Expected New State:\n";
|
||||
printstate($$tx[2]);
|
||||
print "New State:\n";
|
||||
showstate("near/.mbsyncstate");
|
||||
}
|
||||
print "Debug output:\n";
|
||||
print @$jret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
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";
|
||||
if (!$ixc) {
|
||||
print "Actual result:\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
}
|
||||
print "Debug output:\n";
|
||||
print @$iret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
rmtree "near";
|
||||
rmtree "far";
|
||||
|
||||
my $njl = (@$nj - 1) * 2;
|
||||
for (my $l = 1; $l <= $njl; $l++) {
|
||||
mkchan($$sx[0], $$sx[1], $$sx[2]);
|
||||
|
||||
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;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
($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("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("near/.mbsyncstate.new");
|
||||
}
|
||||
print "Debug output:\n";
|
||||
print @$nret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
1160
src/socket.c
Normal file
1160
src/socket.c
Normal file
File diff suppressed because it is too large
Load diff
155
src/socket.h
Normal file
155
src/socket.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSSL
|
||||
# include <openssl/ssl.h>
|
||||
# include <openssl/x509.h>
|
||||
|
||||
enum {
|
||||
TLSv1 = 4,
|
||||
TLSv1_1 = 8,
|
||||
TLSv1_2 = 16,
|
||||
TLSv1_3 = 32
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char *tunnel;
|
||||
char *host;
|
||||
ushort port;
|
||||
int timeout;
|
||||
#ifdef HAVE_LIBSSL
|
||||
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_OF(X509) *trusted_certs;
|
||||
SSL_CTX *SSLContext;
|
||||
#endif
|
||||
} server_conf_t;
|
||||
|
||||
typedef struct buff_chunk {
|
||||
struct buff_chunk *next;
|
||||
uint len;
|
||||
char data[1];
|
||||
} buff_chunk_t;
|
||||
|
||||
typedef struct {
|
||||
/* connection */
|
||||
int fd;
|
||||
int state;
|
||||
const server_conf_t *conf; /* needed during connect */
|
||||
#ifdef HAVE_IPV6
|
||||
struct addrinfo *addrs, *curr_addr; /* needed during connect */
|
||||
#else
|
||||
struct addr_info *addrs, *curr_addr; /* needed during connect */
|
||||
#endif
|
||||
char *name;
|
||||
#ifdef HAVE_LIBSSL
|
||||
SSL *ssl;
|
||||
wakeup_t ssl_fake;
|
||||
#endif
|
||||
#ifdef HAVE_LIBZ
|
||||
z_streamp in_z, out_z;
|
||||
wakeup_t z_fake;
|
||||
int z_written;
|
||||
#endif
|
||||
|
||||
void (*bad_callback)( void *aux ); /* async fail while sending or listening */
|
||||
void (*read_callback)( void *aux ); /* data available for reading */
|
||||
void (*write_callback)( void *aux ); /* all *queued* data was sent */
|
||||
union {
|
||||
void (*connect)( int ok, void *aux );
|
||||
void (*starttls)( int ok, void *aux );
|
||||
} callbacks;
|
||||
void *callback_aux;
|
||||
|
||||
notifier_t notify;
|
||||
wakeup_t fd_fake;
|
||||
wakeup_t fd_timeout;
|
||||
|
||||
/* writing */
|
||||
buff_chunk_t *append_buf; /* accumulating buffer */
|
||||
buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */
|
||||
#ifdef HAVE_LIBZ
|
||||
uint append_avail; /* space left in accumulating buffer */
|
||||
#endif
|
||||
uint write_offset; /* offset into buffer head */
|
||||
uint buffer_mem; /* memory currently occupied by buffers in the queue */
|
||||
|
||||
/* reading */
|
||||
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];
|
||||
#endif
|
||||
} conn_t;
|
||||
|
||||
/* call this before doing anything with the socket */
|
||||
static INLINE void socket_init( conn_t *conn,
|
||||
const server_conf_t *conf,
|
||||
void (*bad_callback)( void *aux ),
|
||||
void (*read_callback)( void *aux ),
|
||||
void (*write_callback)( void *aux ),
|
||||
void *aux )
|
||||
{
|
||||
conn->conf = conf;
|
||||
conn->bad_callback = bad_callback;
|
||||
conn->read_callback = read_callback;
|
||||
conn->write_callback = write_callback;
|
||||
conn->callback_aux = aux;
|
||||
conn->fd = -1;
|
||||
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_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;
|
||||
uint len;
|
||||
ownership_t takeOwn;
|
||||
} conn_iovec_t;
|
||||
void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt );
|
||||
|
||||
#endif
|
2467
src/sync.c
Normal file
2467
src/sync.c
Normal file
File diff suppressed because it is too large
Load diff
87
src/sync.h
Normal file
87
src/sync.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2010-2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#ifndef SYNC_H
|
||||
#define SYNC_H
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#define F 0 // far side
|
||||
#define N 1 // near side
|
||||
|
||||
#define OP_NEW (1<<0)
|
||||
#define OP_RENEW (1<<1)
|
||||
#define OP_DELETE (1<<2)
|
||||
#define OP_FLAGS (1<<3)
|
||||
#define OP_MASK_TYPE (OP_NEW|OP_RENEW|OP_DELETE|OP_FLAGS) /* asserted in the target ops */
|
||||
#define OP_EXPUNGE (1<<4)
|
||||
#define OP_CREATE (1<<5)
|
||||
#define OP_REMOVE (1<<6)
|
||||
#define XOP_PUSH (1<<8)
|
||||
#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)
|
||||
|
||||
typedef struct channel_conf {
|
||||
struct channel_conf *next;
|
||||
const char *name;
|
||||
store_conf_t *stores[2];
|
||||
const char *boxes[2];
|
||||
char *sync_state;
|
||||
string_list_t *patterns;
|
||||
int ops[2];
|
||||
int max_messages; // For near side only.
|
||||
signed char expire_unread;
|
||||
char use_internal_date;
|
||||
} channel_conf_t;
|
||||
|
||||
typedef struct group_conf {
|
||||
struct group_conf *next;
|
||||
const char *name;
|
||||
string_list_t *channels;
|
||||
} group_conf_t;
|
||||
|
||||
extern channel_conf_t global_conf;
|
||||
extern channel_conf_t *channels;
|
||||
extern group_conf_t *groups;
|
||||
|
||||
extern const char *str_fn[2], *str_hl[2];
|
||||
|
||||
#define SYNC_OK 0 /* assumed to be 0 */
|
||||
#define SYNC_FAIL 1
|
||||
#define SYNC_BAD(fn) (4<<(fn))
|
||||
#define SYNC_NOGOOD 16 /* internal */
|
||||
#define SYNC_CANCELED 32 /* internal */
|
||||
|
||||
#define BOX_POSSIBLE -1
|
||||
#define BOX_ABSENT 0
|
||||
#define BOX_PRESENT 1
|
||||
|
||||
/* All passed pointers must stay alive until cb is called. */
|
||||
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
|
116
src/tst_timers.c
Normal file
116
src/tst_timers.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2014 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Just to satisfy the references in util.c */
|
||||
int DFlags;
|
||||
const char *Home;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
int first, other, morph_at, morph_to;
|
||||
time_t start;
|
||||
wakeup_t timer;
|
||||
wakeup_t morph_timer;
|
||||
} tst_t;
|
||||
|
||||
static void
|
||||
timer_start( tst_t *timer, int to )
|
||||
{
|
||||
printf( "starting timer %d, should expire after %d\n", timer->id, to );
|
||||
time( &timer->start );
|
||||
conf_wakeup( &timer->timer, to );
|
||||
}
|
||||
|
||||
static void
|
||||
timed_out( void *aux )
|
||||
{
|
||||
tst_t *timer = (tst_t *)aux;
|
||||
|
||||
printf( "timer %d expired after %d, repeat %d\n",
|
||||
timer->id, (int)(time( 0 ) - timer->start), timer->other );
|
||||
if (timer->other >= 0) {
|
||||
timer_start( timer, timer->other );
|
||||
} else {
|
||||
wipe_wakeup( &timer->timer );
|
||||
wipe_wakeup( &timer->morph_timer );
|
||||
free( timer );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
morph_timed_out( void *aux )
|
||||
{
|
||||
tst_t *timer = (tst_t *)aux;
|
||||
|
||||
printf( "morphing timer %d after %d\n",
|
||||
timer->id, (int)(time( 0 ) - timer->start) );
|
||||
timer_start( timer, timer->morph_to );
|
||||
}
|
||||
|
||||
static int nextid;
|
||||
|
||||
int
|
||||
main( int argc, char **argv )
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
char *val = argv[i];
|
||||
tst_t *timer = nfmalloc( sizeof(*timer) );
|
||||
init_wakeup( &timer->timer, timed_out, timer );
|
||||
init_wakeup( &timer->morph_timer, morph_timed_out, timer );
|
||||
timer->id = ++nextid;
|
||||
timer->first = strtol( val, &val, 0 );
|
||||
if (*val == '@') {
|
||||
timer->other = timer->first;
|
||||
timer->first = strtol( ++val, &val, 0 );
|
||||
} else {
|
||||
timer->other = -1;
|
||||
}
|
||||
if (*val == ':') {
|
||||
timer->morph_to = strtol( ++val, &val, 0 );
|
||||
if (*val != '@')
|
||||
goto fail;
|
||||
timer->morph_at = strtol( ++val, &val, 0 );
|
||||
} else {
|
||||
timer->morph_at = -1;
|
||||
}
|
||||
if (*val) {
|
||||
fail:
|
||||
fprintf( stderr, "Fatal: syntax error in %s, use <timeout>[@<delay>][:<newtimeout>@<delay>]\n", argv[i] );
|
||||
return 1;
|
||||
}
|
||||
timer_start( timer, timer->first );
|
||||
if (timer->morph_at >= 0) {
|
||||
printf( "timer %d, should morph after %d\n", timer->id, timer->morph_at );
|
||||
conf_wakeup( &timer->morph_timer, timer->morph_at );
|
||||
}
|
||||
}
|
||||
|
||||
main_loop();
|
||||
return 0;
|
||||
}
|
904
src/util.c
Normal file
904
src/util.c
Normal file
|
@ -0,0 +1,904 @@
|
|||
/*
|
||||
* mbsync - mailbox synchronizer
|
||||
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
||||
* Copyright (C) 2002-2006,2011,2012 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/>.
|
||||
*
|
||||
* As a special exception, mbsync may be linked with the OpenSSL library,
|
||||
* despite that library's more restrictive license.
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
static int need_nl;
|
||||
|
||||
void
|
||||
flushn( void )
|
||||
{
|
||||
if (need_nl) {
|
||||
putchar( '\n' );
|
||||
fflush( stdout );
|
||||
need_nl = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ATTR_PRINTFLIKE(1, 0)
|
||||
printn( const char *msg, va_list va )
|
||||
{
|
||||
if (*msg == '\v')
|
||||
msg++;
|
||||
else
|
||||
flushn();
|
||||
vprintf( msg, va );
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
void
|
||||
vdebug( int cat, const char *msg, va_list va )
|
||||
{
|
||||
if (DFlags & cat) {
|
||||
vprintf( msg, va );
|
||||
fflush( stdout );
|
||||
need_nl = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vdebugn( int cat, const char *msg, va_list va )
|
||||
{
|
||||
if (DFlags & cat) {
|
||||
vprintf( msg, va );
|
||||
fflush( stdout );
|
||||
need_nl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
progress( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start( va, msg );
|
||||
vprintf( msg, va );
|
||||
va_end( va );
|
||||
fflush( stdout );
|
||||
need_nl = 1;
|
||||
}
|
||||
|
||||
void
|
||||
info( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (DFlags & VERBOSE) {
|
||||
va_start( va, msg );
|
||||
printn( msg, va );
|
||||
va_end( va );
|
||||
need_nl = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
infon( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (DFlags & VERBOSE) {
|
||||
va_start( va, msg );
|
||||
printn( msg, va );
|
||||
va_end( va );
|
||||
need_nl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
notice( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (!(DFlags & QUIET)) {
|
||||
va_start( va, msg );
|
||||
printn( msg, va );
|
||||
va_end( va );
|
||||
need_nl = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
warn( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (!(DFlags & VERYQUIET)) {
|
||||
flushn();
|
||||
va_start( va, msg );
|
||||
vfprintf( stderr, msg, va );
|
||||
va_end( va );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
error( const char *msg, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
flushn();
|
||||
va_start( va, msg );
|
||||
vfprintf( stderr, msg, va );
|
||||
va_end( va );
|
||||
}
|
||||
|
||||
void
|
||||
vsys_error( const char *msg, va_list va )
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
int errno_bak = errno;
|
||||
flushn();
|
||||
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
|
||||
oob();
|
||||
errno = errno_bak;
|
||||
perror( buf );
|
||||
}
|
||||
|
||||
void
|
||||
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( offsetof(string_list_t, string) + len + 1 );
|
||||
elem->next = *list;
|
||||
*list = elem;
|
||||
memcpy( elem->string, str, len );
|
||||
elem->string[len] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
add_string_list( string_list_t **list, const char *str )
|
||||
{
|
||||
add_string_list_n( list, str, strlen( str ) );
|
||||
}
|
||||
|
||||
void
|
||||
free_string_list( string_list_t *list )
|
||||
{
|
||||
string_list_t *tlist;
|
||||
|
||||
for (; list; list = tlist) {
|
||||
tlist = list->next;
|
||||
free( list );
|
||||
}
|
||||
}
|
||||
|
||||
#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, (size_t)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
|
||||
size_t
|
||||
strnlen( const char *str, size_t maxlen )
|
||||
{
|
||||
const char *estr = memchr( str, 0, maxlen );
|
||||
return estr ? (size_t)(estr - str) : maxlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
|
||||
{
|
||||
if (strl < 0)
|
||||
strl = strnlen( str, cmpl + 1 );
|
||||
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
|
||||
}
|
||||
|
||||
int
|
||||
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
|
||||
{
|
||||
if (strl < 0)
|
||||
strl = strnlen( str, cmpl + 1 );
|
||||
if ((uint)strl < cmpl)
|
||||
return 0;
|
||||
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, uint cmpl )
|
||||
{
|
||||
if (strl < 0)
|
||||
strl = strnlen( str, cmpl + 1 );
|
||||
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
|
||||
}
|
||||
|
||||
#ifndef HAVE_TIMEGM
|
||||
/*
|
||||
Converts struct tm to time_t, assuming the data in tm is UTC rather
|
||||
than local timezone.
|
||||
|
||||
mktime is similar but assumes struct tm, also known as the
|
||||
"broken-down" form of time, is in local time zone. timegm
|
||||
uses mktime to make the conversion understanding that an offset
|
||||
will be introduced by the local time assumption.
|
||||
|
||||
mktime_from_utc then measures the introduced offset by applying
|
||||
gmtime to the initial result and applying mktime to the resulting
|
||||
"broken-down" form. The difference between the two mktime results
|
||||
is the measured offset which is then subtracted from the initial
|
||||
mktime result to yield a calendar time which is the value returned.
|
||||
|
||||
tm_isdst in struct tm is set to 0 to force mktime to introduce a
|
||||
consistent offset (the non DST offset) since tm and tm+o might be
|
||||
on opposite sides of a DST change.
|
||||
|
||||
Some implementations of mktime return -1 for the nonexistent
|
||||
localtime hour at the beginning of DST. In this event, use
|
||||
mktime(tm - 1hr) + 3600.
|
||||
|
||||
Schematically
|
||||
mktime(tm) --> t+o
|
||||
gmtime(t+o) --> tm+o
|
||||
mktime(tm+o) --> t+2o
|
||||
t+o - (t+2o - t+o) = t
|
||||
|
||||
Contributed by Roger Beeman <beeman@cisco.com>, with the help of
|
||||
Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
|
||||
Further improved by Roger with assistance from Edward J. Sabol
|
||||
based on input by Jamie Zawinski.
|
||||
*/
|
||||
|
||||
static time_t
|
||||
my_mktime( struct tm *t )
|
||||
{
|
||||
time_t tl = mktime( t );
|
||||
if (tl == -1) {
|
||||
t->tm_hour--;
|
||||
tl = mktime( t );
|
||||
if (tl != -1)
|
||||
tl += 3600;
|
||||
}
|
||||
return tl;
|
||||
}
|
||||
|
||||
time_t
|
||||
timegm( struct tm *t )
|
||||
{
|
||||
time_t tl, tb;
|
||||
struct tm *tg;
|
||||
|
||||
if ((tl = my_mktime( t )) == -1)
|
||||
return tl;
|
||||
tg = gmtime( &tl );
|
||||
tg->tm_isdst = 0;
|
||||
if ((tb = my_mktime( tg )) == -1)
|
||||
return tb;
|
||||
return tl - (tb - tl);
|
||||
}
|
||||
#endif
|
||||
|
||||
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 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen)
|
||||
oob();
|
||||
va_end( va );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
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 *
|
||||
nfcalloc( size_t sz )
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (!(ret = calloc( sz, 1 )))
|
||||
oom();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *
|
||||
nfrealloc( void *mem, size_t sz )
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (!(ret = realloc( mem, sz )) && sz)
|
||||
oom();
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
nfstrndup( const char *str, size_t nchars )
|
||||
{
|
||||
char *ret = nfmalloc( nchars + 1 );
|
||||
memcpy( ret, str, nchars );
|
||||
ret[nchars] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
nfstrdup( const char *str )
|
||||
{
|
||||
return nfstrndup( str, strlen( str ) );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
static struct passwd *
|
||||
cur_user( void )
|
||||
{
|
||||
char *p;
|
||||
struct passwd *pw;
|
||||
uid_t uid;
|
||||
|
||||
uid = getuid();
|
||||
if ((!(p = getenv("LOGNAME")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
|
||||
(!(p = getenv("USER")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
|
||||
!(pw = getpwuid( uid )))
|
||||
{
|
||||
fputs ("Cannot determinate current user\n", stderr);
|
||||
return 0;
|
||||
}
|
||||
return pw;
|
||||
}
|
||||
*/
|
||||
|
||||
char *
|
||||
expand_strdup( const char *s )
|
||||
{
|
||||
struct passwd *pw;
|
||||
const char *p, *q;
|
||||
char *r;
|
||||
|
||||
if (*s == '~') {
|
||||
s++;
|
||||
if (!*s) {
|
||||
p = NULL;
|
||||
q = Home;
|
||||
} else if (*s == '/') {
|
||||
p = s;
|
||||
q = Home;
|
||||
} else {
|
||||
if ((p = strchr( s, '/' ))) {
|
||||
r = nfstrndup( s, (size_t)(p - s) );
|
||||
pw = getpwnam( r );
|
||||
free( r );
|
||||
} else
|
||||
pw = getpwnam( s );
|
||||
if (!pw)
|
||||
return NULL;
|
||||
q = pw->pw_dir;
|
||||
}
|
||||
nfasprintf( &r, "%s%s", q, p ? p : "" );
|
||||
return r;
|
||||
} else
|
||||
return nfstrdup( 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, uint reserve, const char *in, const char *out )
|
||||
{
|
||||
char *p;
|
||||
uint i, l, ll, num, inl, outl;
|
||||
|
||||
assert( arg );
|
||||
l = strlen( arg );
|
||||
assert( in );
|
||||
inl = strlen( in );
|
||||
if (!inl) {
|
||||
copy:
|
||||
*result = nfmalloc( reserve + l + 1 );
|
||||
memcpy( *result + reserve, arg, l + 1 );
|
||||
return 0;
|
||||
}
|
||||
assert( out );
|
||||
outl = strlen( out );
|
||||
if (equals( in, (int)inl, out, outl ))
|
||||
goto copy;
|
||||
for (num = 0, i = 0; i < l; ) {
|
||||
for (ll = 0; ll < inl; ll++)
|
||||
if (arg[i + ll] != in[ll])
|
||||
goto fout;
|
||||
num++;
|
||||
i += inl;
|
||||
continue;
|
||||
fout:
|
||||
if (outl) {
|
||||
for (ll = 0; ll < outl; ll++)
|
||||
if (arg[i + ll] != out[ll])
|
||||
goto fnexti;
|
||||
return -1;
|
||||
}
|
||||
fnexti:
|
||||
i++;
|
||||
}
|
||||
if (!num)
|
||||
goto copy;
|
||||
if (!outl)
|
||||
return -2;
|
||||
*result = nfmalloc( reserve + l + num * (outl - inl) + 1 );
|
||||
p = *result + reserve;
|
||||
for (i = 0; i < l; ) {
|
||||
for (ll = 0; ll < inl; ll++)
|
||||
if (arg[i + ll] != in[ll])
|
||||
goto rnexti;
|
||||
memcpy( p, out, outl );
|
||||
p += outl;
|
||||
i += inl;
|
||||
continue;
|
||||
rnexti:
|
||||
*p++ = arg[i++];
|
||||
}
|
||||
*p = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_uints( const void *l, const void *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
|
||||
sort_uint_array( uint_array_t array )
|
||||
{
|
||||
qsort( array.data, array.size, sizeof(uint), compare_uints );
|
||||
}
|
||||
|
||||
int
|
||||
find_uint_array( uint_array_t array, uint value )
|
||||
{
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct {
|
||||
uchar i, j, s[256];
|
||||
} rs;
|
||||
|
||||
void
|
||||
arc4_init( void )
|
||||
{
|
||||
int i, fd;
|
||||
uchar j, si, dat[128];
|
||||
|
||||
if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
|
||||
error( "Fatal: no random number source available.\n" );
|
||||
exit( 3 );
|
||||
}
|
||||
if (read( fd, dat, 128 ) != 128) {
|
||||
error( "Fatal: cannot read random number source.\n" );
|
||||
exit( 3 );
|
||||
}
|
||||
close( fd );
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
rs.s[i] = (uchar)i;
|
||||
for (i = j = 0; i < 256; i++) {
|
||||
si = rs.s[i];
|
||||
j += si + dat[i & 127];
|
||||
rs.s[i] = rs.s[j];
|
||||
rs.s[j] = si;
|
||||
}
|
||||
rs.i = rs.j = 0;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
arc4_getbyte();
|
||||
}
|
||||
|
||||
uchar
|
||||
arc4_getbyte( void )
|
||||
{
|
||||
uchar si, sj;
|
||||
|
||||
rs.i++;
|
||||
si = rs.s[rs.i];
|
||||
rs.j += si;
|
||||
sj = rs.s[rs.j];
|
||||
rs.s[rs.i] = sj;
|
||||
rs.s[rs.j] = si;
|
||||
return rs.s[(si + sj) & 0xff];
|
||||
}
|
||||
|
||||
static const uchar prime_deltas[] = {
|
||||
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3,
|
||||
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
uint
|
||||
bucketsForSize( uint size )
|
||||
{
|
||||
uint base = 4, bits = 2;
|
||||
|
||||
for (;;) {
|
||||
uint prime = base + prime_deltas[bits];
|
||||
if (prime >= size)
|
||||
return prime;
|
||||
base <<= 1;
|
||||
bits++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
list_prepend( list_head_t *head, list_head_t *to )
|
||||
{
|
||||
assert( !head->next );
|
||||
assert( to->next );
|
||||
assert( to->prev->next == to );
|
||||
head->next = to;
|
||||
head->prev = to->prev;
|
||||
head->prev->next = head;
|
||||
to->prev = head;
|
||||
}
|
||||
|
||||
static void
|
||||
list_unlink( list_head_t *head )
|
||||
{
|
||||
assert( head->next );
|
||||
assert( head->next->prev == head);
|
||||
assert( head->prev->next == head);
|
||||
head->next->prev = head->prev;
|
||||
head->prev->next = head->next;
|
||||
head->next = head->prev = NULL;
|
||||
}
|
||||
|
||||
static notifier_t *notifiers;
|
||||
static int changed; /* Iterator may be invalid now. */
|
||||
#ifdef HAVE_POLL_H
|
||||
static struct pollfd *pollfds;
|
||||
static uint npolls, rpolls;
|
||||
#else
|
||||
# ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
void
|
||||
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
|
||||
{
|
||||
#ifdef HAVE_POLL_H
|
||||
uint idx = npolls++;
|
||||
if (rpolls < npolls) {
|
||||
rpolls = npolls;
|
||||
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
|
||||
}
|
||||
pollfds[idx].fd = fd;
|
||||
pollfds[idx].events = 0; /* POLLERR & POLLHUP implicit */
|
||||
sn->index = idx;
|
||||
#else
|
||||
sn->fd = fd;
|
||||
sn->events = 0;
|
||||
#endif
|
||||
sn->cb = cb;
|
||||
sn->aux = aux;
|
||||
sn->next = notifiers;
|
||||
notifiers = sn;
|
||||
}
|
||||
|
||||
void
|
||||
conf_notifier( notifier_t *sn, short and_events, short or_events )
|
||||
{
|
||||
#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_POLL_H
|
||||
uint idx;
|
||||
#endif
|
||||
|
||||
for (snp = ¬ifiers; *snp != sn; snp = &(*snp)->next)
|
||||
assert( *snp );
|
||||
*snp = sn->next;
|
||||
sn->next = NULL;
|
||||
changed = 1;
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
idx = sn->index;
|
||||
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
|
||||
for (sn = notifiers; sn; sn = sn->next) {
|
||||
if (sn->index > idx)
|
||||
sn->index--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static time_t
|
||||
get_now( void )
|
||||
{
|
||||
return time( NULL );
|
||||
}
|
||||
|
||||
static list_head_t timers = { &timers, &timers };
|
||||
|
||||
void
|
||||
init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
|
||||
{
|
||||
tmr->cb = cb;
|
||||
tmr->aux = aux;
|
||||
tmr->links.next = tmr->links.prev = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wipe_wakeup( wakeup_t *tmr )
|
||||
{
|
||||
if (tmr->links.next)
|
||||
list_unlink( &tmr->links );
|
||||
}
|
||||
|
||||
void
|
||||
conf_wakeup( wakeup_t *tmr, int to )
|
||||
{
|
||||
list_head_t *head, *succ;
|
||||
|
||||
if (to < 0) {
|
||||
if (tmr->links.next)
|
||||
list_unlink( &tmr->links );
|
||||
} else {
|
||||
time_t timeout = to;
|
||||
if (!to) {
|
||||
/* We always prepend null timers, to cluster related events. */
|
||||
succ = timers.next;
|
||||
} else {
|
||||
timeout += get_now();
|
||||
/* We start at the end in the expectation that the newest timer is likely to fire last
|
||||
* (which will be true only if all timeouts are equal, but it's an as good guess as any). */
|
||||
for (succ = &timers; (head = succ->prev) != &timers; succ = head) {
|
||||
if (head != &tmr->links && timeout > ((wakeup_t *)head)->timeout)
|
||||
break;
|
||||
}
|
||||
assert( head != &tmr->links );
|
||||
}
|
||||
tmr->timeout = timeout;
|
||||
if (succ != &tmr->links) {
|
||||
if (tmr->links.next)
|
||||
list_unlink( &tmr->links );
|
||||
list_prepend( &tmr->links, succ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
event_wait( void )
|
||||
{
|
||||
list_head_t *head;
|
||||
notifier_t *sn;
|
||||
int m;
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
int timeout = -1;
|
||||
if ((head = timers.next) != &timers) {
|
||||
wakeup_t *tmr = (wakeup_t *)head;
|
||||
time_t delta = tmr->timeout;
|
||||
if (!delta || (delta -= get_now()) <= 0) {
|
||||
list_unlink( head );
|
||||
tmr->cb( tmr->aux );
|
||||
return;
|
||||
}
|
||||
timeout = (int)delta * 1000;
|
||||
}
|
||||
switch (poll( pollfds, npolls, timeout )) {
|
||||
case 0:
|
||||
return;
|
||||
case -1:
|
||||
perror( "poll() failed in event loop" );
|
||||
abort();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (sn = notifiers; sn; sn = sn->next) {
|
||||
uint n = sn->index;
|
||||
if ((m = pollfds[n].revents)) {
|
||||
assert( !(m & POLLNVAL) );
|
||||
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );
|
||||
if (changed) {
|
||||
changed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
struct timeval *timeout = 0;
|
||||
struct timeval to_tv;
|
||||
fd_set rfds, wfds, efds;
|
||||
int fd;
|
||||
|
||||
if ((head = timers.next) != &timers) {
|
||||
wakeup_t *tmr = (wakeup_t *)head;
|
||||
time_t delta = tmr->timeout;
|
||||
if (!delta || (delta -= get_now()) <= 0) {
|
||||
list_unlink( head );
|
||||
tmr->cb( tmr->aux );
|
||||
return;
|
||||
}
|
||||
to_tv.tv_sec = delta;
|
||||
to_tv.tv_usec = 0;
|
||||
timeout = &to_tv;
|
||||
}
|
||||
FD_ZERO( &rfds );
|
||||
FD_ZERO( &wfds );
|
||||
FD_ZERO( &efds );
|
||||
m = -1;
|
||||
for (sn = notifiers; sn; sn = sn->next) {
|
||||
fd = sn->fd;
|
||||
if (sn->events & POLLIN)
|
||||
FD_SET( fd, &rfds );
|
||||
if (sn->events & POLLOUT)
|
||||
FD_SET( fd, &wfds );
|
||||
FD_SET( fd, &efds );
|
||||
if (fd > m)
|
||||
m = fd;
|
||||
}
|
||||
switch (select( m + 1, &rfds, &wfds, &efds, timeout )) {
|
||||
case 0:
|
||||
return;
|
||||
case -1:
|
||||
perror( "select() failed in event loop" );
|
||||
abort();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (sn = notifiers; sn; sn = sn->next) {
|
||||
fd = sn->fd;
|
||||
m = 0;
|
||||
if (FD_ISSET( fd, &rfds ))
|
||||
m |= POLLIN;
|
||||
if (FD_ISSET( fd, &wfds ))
|
||||
m |= POLLOUT;
|
||||
if (FD_ISSET( fd, &efds ))
|
||||
m |= POLLERR;
|
||||
if (m) {
|
||||
sn->cb( m, sn->aux );
|
||||
if (changed) {
|
||||
changed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
main_loop( void )
|
||||
{
|
||||
while (notifiers || timers.next != &timers)
|
||||
event_wait();
|
||||
}
|
203
sync.c
203
sync.c
|
@ -1,203 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "isync.h"
|
||||
|
||||
static unsigned int MaildirCount = 0;
|
||||
|
||||
message_t *
|
||||
find_msg (message_t * list, unsigned int uid)
|
||||
{
|
||||
for (; list; list = list->next)
|
||||
if (list->uid == uid)
|
||||
return list;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
|
||||
{
|
||||
message_t *cur;
|
||||
message_t *tmp;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char newpath[_POSIX_PATH_MAX];
|
||||
char suffix[_POSIX_PATH_MAX];
|
||||
char *p;
|
||||
int fd;
|
||||
int ret;
|
||||
int fetched = 0;
|
||||
|
||||
if (mbox->uidvalidity > 0)
|
||||
{
|
||||
if (mbox->uidvalidity != imap->uidvalidity)
|
||||
{
|
||||
/* if the UIDVALIDITY value has changed, it means all our
|
||||
* local UIDs are invalid, so we can't sync.
|
||||
*/
|
||||
puts ("Error, UIDVALIDITY changed on server (fatal)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (maildir_set_uidvalidity (mbox, imap->uidvalidity))
|
||||
{
|
||||
puts ("Error, unable to store UIDVALIDITY");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mbox->maxuid == 0 || imap->maxuid > mbox->maxuid)
|
||||
{
|
||||
mbox->maxuid = imap->maxuid;
|
||||
mbox->maxuidchanged = 1;
|
||||
}
|
||||
|
||||
/* if we are --fast mode, the mailbox wont have been loaded, so
|
||||
* this next step is skipped.
|
||||
*/
|
||||
for (cur = mbox->msgs; cur; cur = cur->next)
|
||||
{
|
||||
tmp = find_msg (imap->msgs, cur->uid);
|
||||
if (!tmp)
|
||||
{
|
||||
printf ("Warning, uid %d doesn't exist on server\n", cur->uid);
|
||||
if (flags & SYNC_DELETE)
|
||||
{
|
||||
cur->flags |= D_DELETED;
|
||||
cur->dead = 1;
|
||||
mbox->deleted++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
tmp->processed = 1;
|
||||
|
||||
/* check if local flags are different from server flags.
|
||||
* ignore \Recent and \Draft
|
||||
*/
|
||||
if (cur->flags != (tmp->flags & ~(D_RECENT | D_DRAFT)))
|
||||
{
|
||||
/* set local flags that don't exist on the server */
|
||||
if (!(tmp->flags & D_DELETED) && (cur->flags & D_DELETED))
|
||||
imap->deleted++;
|
||||
imap_set_flags (imap, cur->uid, cur->flags & ~tmp->flags);
|
||||
|
||||
/* update local flags */
|
||||
if((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED))
|
||||
mbox->deleted++;
|
||||
cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT));
|
||||
cur->changed = 1;
|
||||
mbox->changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
fputs ("Fetching new messages", stdout);
|
||||
fflush (stdout);
|
||||
for (cur = imap->msgs; cur; cur = cur->next)
|
||||
{
|
||||
if (!cur->processed)
|
||||
{
|
||||
/* new message on server */
|
||||
|
||||
if ((flags & SYNC_EXPUNGE) && (cur->flags & D_DELETED))
|
||||
{
|
||||
/* this message has been marked for deletion and
|
||||
* we are currently expunging a mailbox. don't
|
||||
* bother downloading this message
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
if (max_size && cur->size > max_size)
|
||||
{
|
||||
printf ("Warning, message skipped because it is too big (%u)\n",
|
||||
cur->size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* construct the flags part of the file name. */
|
||||
|
||||
*suffix = 0;
|
||||
if (cur->flags)
|
||||
{
|
||||
snprintf (suffix, sizeof (suffix), ":2,%s%s%s%s",
|
||||
(cur->flags & D_FLAGGED) ? "F" : "",
|
||||
(cur->flags & D_ANSWERED) ? "R" : "",
|
||||
(cur->flags & D_SEEN) ? "S" : "",
|
||||
(cur->flags & D_DELETED) ? "T" : "");
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* create new file */
|
||||
snprintf (path, sizeof (path), "%s/tmp/%s.%ld_%d.%d.UID%d%s",
|
||||
mbox->path, Hostname, time (0), MaildirCount++,
|
||||
getpid (), cur->uid, suffix);
|
||||
|
||||
if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0)
|
||||
break;
|
||||
if (errno != EEXIST)
|
||||
{
|
||||
perror ("open");
|
||||
break;
|
||||
}
|
||||
|
||||
sleep (2);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
/* give some visual feedback that something is happening */
|
||||
fputs (".", stdout);
|
||||
fflush (stdout);
|
||||
fetched++;
|
||||
|
||||
ret = imap_fetch_message (imap, cur->uid, fd);
|
||||
|
||||
if (close (fd))
|
||||
perror ("close");
|
||||
else if (!ret)
|
||||
{
|
||||
p = strrchr (path, '/');
|
||||
|
||||
snprintf (newpath, sizeof (newpath), "%s/%s%s", mbox->path,
|
||||
(cur->flags & D_SEEN) ? "cur" : "new", p);
|
||||
|
||||
/* its ok if this fails, the next time we sync the message
|
||||
* will get pulled down
|
||||
*/
|
||||
if (link (path, newpath))
|
||||
perror ("link");
|
||||
}
|
||||
|
||||
/* always remove the temp file */
|
||||
unlink (path);
|
||||
}
|
||||
}
|
||||
printf (" %d messages\n", fetched);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue