Compare commits
	
		
			No commits in common. "filter" and "v0.8" have entirely different histories.
		
	
	
		
	
		
					 59 changed files with 4407 additions and 17118 deletions
				
			
		
							
								
								
									
										32
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,32 +0,0 @@ | |||
| /.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 | ||||
							
								
								
									
										16
									
								
								AUTHORS
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								AUTHORS
									
										
									
									
									
								
							|  | @ -1,17 +1 @@ | |||
| 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. | ||||
|  |  | |||
							
								
								
									
										545
									
								
								ChangeLog
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										545
									
								
								ChangeLog
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,545 @@ | |||
| 2002-01-28  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* TODO, configure.in: | ||||
| 	check for dbm_open() in libc and libdb | ||||
| 
 | ||||
| 2002-01-17  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* sync.c: | ||||
| 	don't bother renaming the message file if we are about to unlink() it | ||||
| 
 | ||||
| 2002-01-16  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* AUTHORS, Makefile.am, NEWS, sync.c: | ||||
| 	remove tilde backup files for distclean | ||||
| 
 | ||||
| 	fixed indentation | ||||
| 
 | ||||
| 	added full name to AUTHORS | ||||
| 
 | ||||
| 	reformated NEWS blurb for 0.8 | ||||
| 
 | ||||
| 	* sync.c, maildir.c: | ||||
| 	sync_mailbox() did not update the msg struct when flags were changed, | ||||
| 	causing the expunge command to fail | ||||
| 
 | ||||
| 	remove bogus strfcpy() line | ||||
| 
 | ||||
| 	* ChangeLog, Makefile.am, configure.in, debian/Makefile.am: | ||||
| 	added debian build files dist target so that people can use them to build | ||||
| 	their own .deb packages without having to use CVS | ||||
| 
 | ||||
| 	* debian/changelog, debian/control, debian/copyright, debian/dirs, debian/docs, debian/files, debian/rules, isync.1, maildir.c, sync.c: | ||||
| 	added debian build files | ||||
| 
 | ||||
| 	fixed indentation | ||||
| 
 | ||||
| 	added bug note to manpage about db file format not being architecture | ||||
| 	independent | ||||
| 
 | ||||
| 	* sync.c, maildir.c: | ||||
| 	remove the uid from the db when a message is deleted from the maildir | ||||
| 
 | ||||
| 	optimize db fetch/store to not copy the base filename | ||||
| 
 | ||||
| 	* NEWS, TODO, config.c, configure.in, cram.c, debug.c, debug.h, imap.c, isync.1, isync.h, list.c, maildir.c, main.c, sync.c, ChangeLog: | ||||
| 	updated year in copyright notice | ||||
| 
 | ||||
| 	the uid for each message in the maildir is now stored in a dbm database | ||||
| 	rather than the filename.  this change was necessary because isync became | ||||
| 	confused if you copied a message to another folder, in which case the uid | ||||
| 	was invalid. | ||||
| 
 | ||||
| 	as a result of the above change, isync now acquires a mutex on the mailbox | ||||
| 	to protect the dbm database from concurrent access. | ||||
| 
 | ||||
| 	main() was reworked to continue gracefully when an error is encountered, and | ||||
| 	to always call maildir_close() so that the lock can be disabled, and the | ||||
| 	database closed. | ||||
| 
 | ||||
| 2001-11-20  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* ChangeLog, Makefile.am, isync.spec: | ||||
| 	post 0.7-release commit | ||||
| 
 | ||||
| 	* Makefile.am, NEWS, isync.1, isync.h, maildir.c, main.c: | ||||
| 	added --create/-C command line option to force creation of the local | ||||
| 	maildir-style mailbox if nonexistent | ||||
| 
 | ||||
| 	debug.h was not included in isync_SOURCES in Makefile.am | ||||
| 
 | ||||
| 2001-11-19  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, configure.in, debug.c, debug.h, isync.h, list.c, main.c, Makefile.am, TODO: | ||||
| 	added memory debugging code | ||||
| 
 | ||||
| 	fixed memory leak in free_list() | ||||
| 
 | ||||
| 	free memory associated with global settings on exit | ||||
| 
 | ||||
| 2001-11-16  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* isync.h, sync.c, ChangeLog, cram.c, imap.c: | ||||
| 	remove c++ style comments | ||||
| 
 | ||||
| 	use %lu and cast off_t to unsigned long in printf() | ||||
| 
 | ||||
| 2001-11-15  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* NEWS, config.c, isync.1, isync.h, main.c, sync.c: | ||||
| 	Added MaxMessages patch from Eivind Eklund <eivind@FreeBSD.org>. | ||||
| 
 | ||||
| 	config_defaults() can just use memcpy() instead of assigning each struct | ||||
| 	member individually. | ||||
| 
 | ||||
| 	config_defaults() can be declared static | ||||
| 
 | ||||
| 2001-11-14  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, configure.in, strndup.c: | ||||
| 	move strndup() code into config.c for less complexity | ||||
| 
 | ||||
| 	change AC_REPLACE_FUNC(strndup) to AC_CHECK_FUNCS(strndup) | ||||
| 
 | ||||
| 	sed expression checking for gcc-3.0 should be quoted beccause it | ||||
| 	fails under Solaris 2.7 | ||||
| 
 | ||||
| 2001-11-13  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, strndup.c, sync.c: | ||||
| 	strndup() could return a non-NULL terminated string | ||||
| 
 | ||||
| 	size_t should be printed with %lu | ||||
| 
 | ||||
| 	when expending tildes (~), an extra slash was inserted after the user's home | ||||
| 	directory | ||||
| 
 | ||||
| 2001-11-12  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* isync.h, maildir.c, main.c: | ||||
| 	merge maildir_sync() and maildir_close().  the maxuid in a maildir still | ||||
| 	needs to be updated in --fast mode, and the sync code already checks to see | ||||
| 	if any changes were made to the mailbox. | ||||
| 
 | ||||
| 2001-11-09  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* README: add FreeBSD to the list of tested platforms | ||||
| 
 | ||||
| 	* config.c, configure.in, imap.c, maildir.c: | ||||
| 	update version to 0.7 | ||||
| 
 | ||||
| 	detect short write in write_strip() | ||||
| 
 | ||||
| 	fix compilation warnings with gcc-2.95.4 | ||||
| 
 | ||||
| 2001-10-31  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* configure.in, imap.c, isync.h, main.c, sync.c: | ||||
| 	set compiler warnings for gcc-3.0 as well | ||||
| 
 | ||||
| 	display message with count of uploaded messages | ||||
| 
 | ||||
| 	--quiet now supresses warnings in sync_mailbox() | ||||
| 
 | ||||
| 	fixed compiler warnings with -Wshadow | ||||
| 
 | ||||
| 	* ChangeLog, NEWS, isync.1: | ||||
| 	post 0.6 commit | ||||
| 
 | ||||
| 2001-10-30  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* README, configure.in, strndup.c: | ||||
| 	add strndup replacement function for systems which lack it | ||||
| 
 | ||||
| 2001-10-03  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* ChangeLog, Makefile.am, maildir.c: | ||||
| 	fixed broken code in maildir_clean_tmp() | ||||
| 
 | ||||
| 	* TODO, maildir.c: | ||||
| 	added code to clean the tmp directory in a maildir to comply with | ||||
| 	maildir(5) | ||||
| 
 | ||||
| 	* config.c: | ||||
| 	forgot to add code to parse the `Delete' option | ||||
| 
 | ||||
| 	* main.c: | ||||
| 	forgot conditional #if HAVE_LIBSSL around setting of .use_imaps in main() | ||||
| 	from command line arguments | ||||
| 
 | ||||
| 	* main.c: update Copyright printed by --help | ||||
| 
 | ||||
| 	add compile time option list to --help output | ||||
| 
 | ||||
| 	* NEWS, TODO, config.c, isync.1, isync.h, main.c, sample.isyncrc, sync.c: | ||||
| 	added `Delete' configuration option to force -d option | ||||
| 
 | ||||
| 	sync_mailbox() didn't consider MaxSize == 0 to mean "unlimited". | ||||
| 
 | ||||
| 	load_config() needs to print a newline in its error messages since | ||||
| 	next_arg() kills the newline of the line that was read out of the config | ||||
| 	file. | ||||
| 
 | ||||
| 	* TODO: update TODO list with action items | ||||
| 
 | ||||
| 	* imap.c, sync.c: | ||||
| 	fixed maildir message filenames to comply with the maildir(5) specification. | ||||
| 
 | ||||
| 	fixed write_strip() and imap_fetch_message() to check the return code of | ||||
| 	write() and fsync() to comply with maildir(5) spec. | ||||
| 
 | ||||
| 2001-10-02  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* main.c: | ||||
| 	the `Expunge' config directive didn't work since only the -e command line | ||||
| 	argument was consulted. | ||||
| 
 | ||||
| 	* config.c, imap.c, isync.h: | ||||
| 	we should issue a CAPABILITY even if we aren't going to use ssl/tls so that | ||||
| 	cram-md5 auth still works. | ||||
| 
 | ||||
| 2001-07-18  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c: | ||||
| 	find_box() should attempt to expand all filenames if none of the other | ||||
| 	methods found a match. | ||||
| 
 | ||||
| 	* isync.h, maildir.c, config.c: | ||||
| 	fixed to not expand filenames until they are used inside of maildir_open(), | ||||
| 	so that aliases are not required for simple filenames. | ||||
| 	[re: http://bugs.debian.org/102255] | ||||
| 
 | ||||
| 2001-06-22  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* main.c: --host option didn't check for imaps: prefix | ||||
| 
 | ||||
| 2001-06-21  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* main.c: | ||||
| 	fixed core when specifying multiple mailboxes on the command line | ||||
| 
 | ||||
| 2001-06-18  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* isync.1, TODO, configure.in, imap.c: | ||||
| 	handle untagged responses in imap_fetch_message() so that it doesn't bomb | ||||
| 	out if new mail arrives while in the process of downloading | ||||
| 
 | ||||
| 	noted in BUGS section of man page that if new mail arrives after the initial | ||||
| 	message list has been retrieved from the IMAP server, that new mail will not | ||||
| 	be fetched until the next invocation of isync. | ||||
| 
 | ||||
| 	* config.c, imap.c, isync.h, main.c: | ||||
| 	isync should continue to process additional mailboxes even if there is an | ||||
| 	error with a previous mailbox. | ||||
| 
 | ||||
| 	added -a (--all) flag to synchronize all mailboxes defined in ~/.isyncrc | ||||
| 
 | ||||
| 2001-06-13  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* NEWS: post 0.5-release commit | ||||
| 
 | ||||
| 	* ChangeLog, smtppush: | ||||
| 	updated ChangeLog.  removed smtppush binary. | ||||
| 
 | ||||
| 2001-06-12  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* Makefile, config.cache, config.log, config.status: | ||||
| 	auto generated files should not be part of the CVS tree | ||||
| 
 | ||||
| 	* Makefile, config.cache, config.status, smtppush: | ||||
| 	New file. | ||||
| 
 | ||||
| 	* Makefile, config.cache, config.status, smtppush: | ||||
| 	initial import | ||||
| 
 | ||||
| 	* config.log: New file. | ||||
| 
 | ||||
| 	* COPYING, INSTALL, Makefile.am, config.log, configure, configure.in, install-sh, main.c, missing, mkinstalldirs, AUTHORS, Makefile.in, README: | ||||
| 	initial import | ||||
| 
 | ||||
| 2001-02-28  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, imap.c: | ||||
| 	fixed compiler warnings under Solaris 2.7 | ||||
| 
 | ||||
| 2001-02-19  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* ChangeLog, cram.c, imap.c, maildir.c: | ||||
| 	rfc2595 compliance patch from Daniel Resare <noa@metamatrix.se> | ||||
| 		- CAPABILITY should be reissued after starting TLS since the | ||||
| 		  previous call was not protected | ||||
| 
 | ||||
| 2001-02-14  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, imap.c, isync.1, main.c, sync.c: | ||||
| 	patch from Daniel Resare <noa@metamatrix.se>: | ||||
| 	1 giving a path to a nonexistant rc-file with the -c argument dumps core | ||||
| 
 | ||||
| 	  The patch adds a check to ensure that the given rc-file is accessible | ||||
| 
 | ||||
| 	2 the error messages given from failed openssl calls are bogus | ||||
| 
 | ||||
| 	  The handles the error from SSL_connect () correctly. The bug is | ||||
| 	  understndable since the error handling in openssl is quite obfuscated. | ||||
| 	  Good news is that the documentation manapges has been greatly updated in | ||||
| 	  the latest version (0.9.6). See in particular err(3), ERR_get_error(3) | ||||
| 	  and SSL_get_error(3). | ||||
| 
 | ||||
| 	  Please note that possible SSL_ERROR_SSL type errors from SSL_read() and | ||||
| 	  SSL_write() is not handled. This should also be fixed. | ||||
| 
 | ||||
| 	3 connecting using the STARTTLS command with an imap server that is | ||||
| 	  configured only to accept the TLSv1 protocol gives an error because isync | ||||
| 	  sends an SSLv2 Hello message for backwards compability. (This is the case | ||||
| 	  with the uw-imap 2000 that ships with redhat-7.0) | ||||
| 	  I've read RFC2595 several times to see if it says something about | ||||
| 	  compability SSL2/SSL3 hello messages but can't find anything. IMHO the | ||||
| 	  correct thing to do is change the default to not use SSL2/3 compability | ||||
| 	  hello when using the STARTTLS command but use it if the imaps port is | ||||
| 	  used. The patch implements this change | ||||
| 
 | ||||
| 	4 repeated calls to SSL_CTX_set_options overwrites the old settings (the | ||||
| 	  values needs to be ORed together) | ||||
| 
 | ||||
| 	  fixed in the patch | ||||
| 
 | ||||
| 	patch from me@mutt.org: | ||||
| 		\Recent messages were put in the cur/ directory instead of new/ | ||||
| 
 | ||||
| 		give error message when the LOGIN command fails | ||||
| 
 | ||||
| 2001-02-01  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* imap.c: patch from Daniel Resare <noa@metamatrix.se> | ||||
| 		- don't initialize ssl support if none of use_sslv* is enabled | ||||
| 
 | ||||
| 2001-01-26  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* imap.c, isync.h: | ||||
| 	include <sys/types.h> for off_t | ||||
| 
 | ||||
| 	patch from "lorenzo martignoni" <lorenzo.martignoni@technologist.com> | ||||
| 		- fixed uploading of message to IMAP server | ||||
| 
 | ||||
| 2001-01-24  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* config.c, cram.c, imap.c, isync.1, list.c, maildir.c, main.c, sync.c: | ||||
| 	fixed cram compilation error under bsd | ||||
| 
 | ||||
| 	updated man page | ||||
| 
 | ||||
| 2001-01-16  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* TODO, config.c, imap.c, isync.1, isync.h, main.c: | ||||
| 	added support for tilde (~) expansion in the `Mailbox' and `CertificateFile' | ||||
| 	configuration directives | ||||
| 
 | ||||
| 	added `Maildir' configuration command to specify the default location of the | ||||
| 	user's mailboxes.  If a relative path is used in a `Mailbox' command, this | ||||
| 	path is used as a prefix. | ||||
| 
 | ||||
| 2001-01-11  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* configure.in, imap.c, isync.h: | ||||
| 	set imap->prefix to be the namespace prefix | ||||
| 
 | ||||
| 	update version to 0.5 | ||||
| 
 | ||||
| 	fixed compilation warnings in imap.c | ||||
| 
 | ||||
| 	* Makefile.am, config.c, imap.c, isync.1, isync.h, main.c, sample.isyncrc, sync.c: | ||||
| 	broke config code into config.c | ||||
| 
 | ||||
| 	added support for uploading local messages with no UID to the IMAP server | ||||
| 
 | ||||
| 	added Expunge configuration option | ||||
| 
 | ||||
| 	added CopyDeletedTo configuration option | ||||
| 
 | ||||
| 2001-01-09  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* maildir.c, sync.c: | ||||
| 	always put changed messages in the cur/ subdirectory since they are no | ||||
| 	longer new. | ||||
| 
 | ||||
| 	don't set \Seen implicitly for messages in the cur/ folder.  Require the S | ||||
| 	flag on the message since Mutt will move Old (unread, but not recent) | ||||
| 	messges into cur/. | ||||
| 
 | ||||
| 2001-01-08  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* Makefile.am, main.c: | ||||
| 	patch from Hugo Haas <hugo@larve.net> | ||||
| 		-c was not specified in the getopt*() calls | ||||
| 
 | ||||
| 		set global password to the one the user inputs and use that as the | ||||
| 		default for remaining mailboxes | ||||
| 
 | ||||
| 2001-01-05  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* configure.in: | ||||
| 	added --with-ssl-dir to specify an alternate installation of OpenSSL | ||||
| 
 | ||||
| 2000-12-31  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* ChangeLog, isync.spec: | ||||
| 	pre 0.4 commit. | ||||
| 
 | ||||
| 	updated rpm spec file | ||||
| 
 | ||||
| 	* 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  me  <me@sigpipe.org> | ||||
| 
 | ||||
| 	* main.c: | ||||
| 	fixed config parser to accept arbitrary whitespace | ||||
| 
 | ||||
| 2000-12-27  me  <me@sigpipe.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  me  <me@sigpipe.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  me  <me@sigpipe.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  me  <me@sigpipe.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  me  <me@sigpipe.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
									
										
									
									
									
								
							
							
						
						
									
										41
									
								
								Dockerfile
									
										
									
									
									
								
							|  | @ -1,41 +0,0 @@ | |||
| #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 | ||||
							
								
								
									
										94
									
								
								Makefile.am
									
										
									
									
									
								
							
							
						
						
									
										94
									
								
								Makefile.am
									
										
									
									
									
								
							|  | @ -1,80 +1,22 @@ | |||
| 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 | ||||
| SUBDIRS=debian | ||||
| bin_PROGRAMS=isync | ||||
| isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c cram.c config.c \ | ||||
| 	      debug.h | ||||
| isync_LDADD=@DEBUGOBJ@ | ||||
| isync_DEPENDENCIES=@DEBUGOBJ@ | ||||
| EXTRA_isync_SOURCES=debug.c | ||||
| man_MANS=isync.1 | ||||
| EXTRA_DIST=sample.isyncrc $(man_MANS) | ||||
| INCLUDES=$(RPM_OPT_FLAGS) | ||||
| DISTCLEANFILES=*~ | ||||
| 
 | ||||
| log: | ||||
| 	@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 ) | ||||
| 	rcs2log -h sigpipe.org | sed 's;/home/cvs/isync/;;g' > ChangeLog | ||||
| 
 | ||||
| cov-scan: clean | ||||
| 	/opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE) | ||||
| 	tar cavf isync-cov.tar.xz cov-int | ||||
| isync.html: isync.1 | ||||
| 	groff -Thtml -man isync.1 > isync.html | ||||
| 
 | ||||
| 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 | ||||
| rpm: | ||||
| 	make dist | ||||
| 	cp isync-$(VERSION).tar.gz /usr/src/rpm/SOURCES | ||||
| 	rpm -ba --target=i586 --clean isync.spec | ||||
|  |  | |||
							
								
								
									
										123
									
								
								NEWS
									
										
									
									
									
								
							
							
						
						
									
										123
									
								
								NEWS
									
										
									
									
									
								
							|  | @ -1,126 +1,3 @@ | |||
| [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 !!! | ||||
|  |  | |||
							
								
								
									
										83
									
								
								README
									
										
									
									
									
								
							
							
						
						
									
										83
									
								
								README
									
										
									
									
									
								
							|  | @ -4,75 +4,50 @@ | |||
| | \__ \ |_| | | | | (__  | ||||
| |_|___/\__, |_| |_|\___| | ||||
|        |___/             | ||||
| isync/mbsync - free (GPL) mailbox synchronization program | ||||
| http://isync.sf.net/ | ||||
| isync - IMAP4 to maildir mailbox synchronization program | ||||
| http://www.sigpipe.org:8080/isync/ | ||||
| 
 | ||||
| See AUTHORS for contact information. | ||||
| Author: Michael Elkins <me@mutt.org> | ||||
| 
 | ||||
| ``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. | ||||
| ``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. | ||||
| 
 | ||||
| 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. | ||||
| * Features: | ||||
| 
 | ||||
| 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 | ||||
| 	* 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 | ||||
| 
 | ||||
| * Compatibility | ||||
| 
 | ||||
|     isync should work fairly well with any IMAP4 compliant server; | ||||
|     servers that support the UIDPLUS and LITERAL+ extensions are most | ||||
|     efficient. | ||||
| 	``isync'' has been tested with the following IMAP servers: | ||||
| 
 | ||||
|     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. | ||||
| 	* Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0 | ||||
| 	* Courier-IMAP 1.2.3 | ||||
| 	* WU-IMAP 2000 | ||||
| 
 | ||||
| * Platforms | ||||
| 
 | ||||
|     At some point, ``isync'' has successfully run on: | ||||
|     Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3. | ||||
| 	``isync'' has successfully be compiled under: | ||||
| 
 | ||||
| 	* Linux 2.2.18 | ||||
| 	* Solaris 2.7 | ||||
| 	* OpenBSD 2.8 | ||||
| 	* FreeBSD 4.3 | ||||
| 
 | ||||
| * Requirements | ||||
| 
 | ||||
|     perl v5.14+ | ||||
|     Berkeley DB 4.1+ (optional) | ||||
|     OpenSSL for TLS/SSL support (optional) | ||||
|     Cyrus SASL (optional) | ||||
|     zlib (optional) | ||||
| 	OpenSSL for TLS/SSL support (optional) | ||||
| 
 | ||||
|   The build from git also requires: | ||||
| * INSTALLING | ||||
| 
 | ||||
|     GNU autotools (autoconf & automake) | ||||
|     perl module Date::Parse (libtimedate-perl on Debian, perl-TimeDate on | ||||
|                              Fedora and Suse) | ||||
| 	./configure | ||||
| 	make install | ||||
| 
 | ||||
| * Installation | ||||
| * HELP | ||||
| 
 | ||||
|     ./autogen.sh (only when building from git) | ||||
|     ./configure | ||||
|     make | ||||
|     sudo make install | ||||
| 
 | ||||
| * Help | ||||
| 
 | ||||
|     Please see the man page for complete documentation. | ||||
| 	Please see the man page for complete documentation. | ||||
|  |  | |||
							
								
								
									
										88
									
								
								TODO
									
										
									
									
									
								
							
							
						
						
									
										88
									
								
								TODO
									
										
									
									
									
								
							|  | @ -1,86 +1,10 @@ | |||
| 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". | ||||
| --fast downloads the last message again if no new messages have arrived | ||||
| 
 | ||||
| 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. | ||||
| isync gets confused when new mail is delivered while in the middle of an | ||||
| IMAP session.  need to handled those asynchronous notifications properly. | ||||
| 
 | ||||
| add support for IMAP UTF-7 (for internationalized mailbox names). | ||||
| add a way to automatically create and sync IMAP subfolders. | ||||
| 
 | ||||
| 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 | ||||
| add support for tunnelling over an ssh connection instead of ssl | ||||
|  |  | |||
							
								
								
									
										35
									
								
								acinclude.m4
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								acinclude.m4
									
										
									
									
									
								
							|  | @ -1,35 +0,0 @@ | |||
| # 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,4 +1,15 @@ | |||
| #! /bin/sh | ||||
| set -e -v | ||||
| make -f Makefile.am log | ||||
| autoreconf -f -i | ||||
| #!/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 $@ | ||||
|  |  | |||
							
								
								
									
										3
									
								
								build
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								build
									
										
									
									
									
								
							|  | @ -1,3 +0,0 @@ | |||
| #!/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  | ||||
							
								
								
									
										331
									
								
								config.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								config.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,331 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 <unistd.h> | ||||
| #include <limits.h> | ||||
| #include <errno.h> | ||||
| #include <pwd.h> | ||||
| #include <sys/types.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include "isync.h" | ||||
| 
 | ||||
| config_t *boxes = 0; | ||||
| 
 | ||||
| /* set defaults from the global configuration section */ | ||||
| static void | ||||
| config_defaults (config_t * conf) | ||||
| { | ||||
|     memcpy (conf, &global, sizeof (config_t)); | ||||
| } | ||||
| 
 | ||||
| static char * | ||||
| my_strndup (const char *s, size_t nchars) | ||||
| { | ||||
|     char *r = malloc (sizeof (char) * (nchars + 1)); | ||||
|     strncpy (r, s, nchars); | ||||
|     r[nchars] = 0; | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| char * | ||||
| expand_strdup (const char *s) | ||||
| { | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
|     struct passwd *pw; | ||||
|     const char *p; | ||||
| 
 | ||||
|     if (*s == '~') | ||||
|     { | ||||
| 	s++; | ||||
| 	if (*s == '/') | ||||
| 	{ | ||||
| 	    /* current user */ | ||||
| 	    pw = getpwuid (getuid ()); | ||||
| 	    p = s + 1; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	    char *user; | ||||
| 
 | ||||
| 	    p = strchr (s, '/'); | ||||
| 	    if (p) | ||||
| 	    { | ||||
| 		user = my_strndup (s, (int)(p - s)); | ||||
| 		p++; | ||||
| 	    } | ||||
| 	    else | ||||
| 		user = strdup (s); | ||||
| 	    pw = getpwnam (user); | ||||
| 	    free (user); | ||||
| 	} | ||||
| 	if (!pw) | ||||
| 	    return 0; | ||||
| 	snprintf (path, sizeof (path), "%s/%s", pw->pw_dir, p ? p : ""); | ||||
| 	s = path; | ||||
|     } | ||||
|     else if (*s != '/') | ||||
|     { | ||||
| 	snprintf (path, sizeof (path), "%s/%s", | ||||
| 		  global.maildir ? global.maildir : "", s); | ||||
| 	s = path; | ||||
|     } | ||||
|     return strdup (s); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| load_config (const char *where) | ||||
| { | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
|     char buf[1024]; | ||||
|     struct passwd *pw; | ||||
|     config_t **cur = &boxes; | ||||
|     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 (!strcasecmp ("mailbox", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		cur = &(*cur)->next; | ||||
| 	    *cur = calloc (1, sizeof (config_t)); | ||||
| 	    config_defaults (*cur); | ||||
| 	    /* not expanded at this point */ | ||||
| 	    (*cur)->path = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("maildir", cmd)) | ||||
| 	{ | ||||
| 	    /* this only affects the global setting */ | ||||
| 	    free (global.maildir); | ||||
| 	    global.maildir = expand_strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("host", cmd)) | ||||
| 	{ | ||||
| #if HAVE_LIBSSL | ||||
| 	    if (!strncasecmp ("imaps:", val, 6)) | ||||
| 	    { | ||||
| 		val += 6; | ||||
| 		if (*cur) | ||||
| 		{ | ||||
| 		    (*cur)->use_imaps = 1; | ||||
| 		    (*cur)->port = 993; | ||||
| 		    (*cur)->use_sslv2 = 1; | ||||
| 		    (*cur)->use_sslv3 = 1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 		    global.use_imaps = 1; | ||||
| 		    global.port = 993; | ||||
| 		    global.use_sslv2 = 1; | ||||
| 		    global.use_sslv3 = 1; | ||||
| 		} | ||||
| 	    } | ||||
| #endif | ||||
| 	    if (*cur) | ||||
| 		(*cur)->host = strdup (val); | ||||
| 	    else | ||||
| 		global.host = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("user", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->user = strdup (val); | ||||
| 	    else | ||||
| 		global.user = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("pass", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->pass = strdup (val); | ||||
| 	    else | ||||
| 		global.pass = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("port", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->port = atoi (val); | ||||
| 	    else | ||||
| 		global.port = atoi (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("box", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->box = strdup (val); | ||||
| 	    else | ||||
| 		global.box = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("alias", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->alias = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("maxsize", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->max_size = atol (val); | ||||
| 	    else | ||||
| 		global.max_size = atol (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("MaxMessages", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->max_messages = atol (val); | ||||
| 	    else | ||||
| 		global.max_messages = atol (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("UseNamespace", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->use_namespace = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.use_namespace = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("CopyDeletedTo", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->copy_deleted_to = strdup (val); | ||||
| 	    else | ||||
| 		global.copy_deleted_to = strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("Expunge", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->expunge = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.expunge = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("Delete", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->delete = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.delete = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| #if HAVE_LIBSSL | ||||
| 	else if (!strcasecmp ("CertificateFile", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->cert_file = expand_strdup (val); | ||||
| 	    else | ||||
| 		global.cert_file = expand_strdup (val); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("RequireSSL", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->require_ssl = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.require_ssl = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("UseSSLv2", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->use_sslv2 = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.use_sslv2 = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("UseSSLv3", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->use_sslv3 = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.use_sslv3 = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("UseTLSv1", cmd)) | ||||
| 	{ | ||||
| 	    if (*cur) | ||||
| 		(*cur)->use_tlsv1 = (strcasecmp (val, "yes") == 0); | ||||
| 	    else | ||||
| 		global.use_tlsv1 = (strcasecmp (val, "yes") == 0); | ||||
| 	} | ||||
| 	else if (!strcasecmp ("RequireCRAM", cmd)) | ||||
| 	{ | ||||
| 	    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 keyword:%s\n", path, line, cmd); | ||||
|     } | ||||
|     fclose (fp); | ||||
| } | ||||
| 
 | ||||
| config_t * | ||||
| find_box (const char *s) | ||||
| { | ||||
|     config_t *p = boxes; | ||||
| 
 | ||||
|     for (; p; p = p->next) | ||||
|     { | ||||
| 	if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias))) | ||||
| 	    return p; | ||||
| 	else | ||||
| 	{ | ||||
| 	    /* check to see if the full pathname was specified on the
 | ||||
| 	     * command line. | ||||
| 	     */ | ||||
| 	    char *t = expand_strdup (p->path); | ||||
| 
 | ||||
| 	    if (!strcmp (s, t)) | ||||
| 	    { | ||||
| 		free (t); | ||||
| 		return p; | ||||
| 	    } | ||||
| 	    free (t); | ||||
| 	} | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| free_config (void) | ||||
| { | ||||
|     free (global.user); | ||||
|     free (global.maildir); | ||||
|     free (global.host); | ||||
|     free (global.pass); | ||||
| } | ||||
							
								
								
									
										280
									
								
								configure.ac
									
										
									
									
									
								
							
							
						
						
									
										280
									
								
								configure.ac
									
										
									
									
									
								
							|  | @ -1,280 +0,0 @@ | |||
| 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() | ||||
							
								
								
									
										49
									
								
								configure.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								configure.in
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| AC_INIT(isync.h) | ||||
| AM_INIT_AUTOMAKE(isync,0.8) | ||||
| AM_PROG_CC_STDC | ||||
| AC_ARG_WITH(ssl-dir, [  --with-ssl-dir=DIR	location where openssl is insalled], | ||||
| 	[if test -d $withval/lib; then | ||||
| 		LIBS="$LIBS -L$withval/lib" | ||||
| 		CFLAGS="$CFLAGS -I$withval/include" | ||||
| 	else | ||||
| 		AC_MSG_ERROR(can't find OpenSSL in $withval) | ||||
| 	fi]) | ||||
| AC_ARG_ENABLE(debug, [  --enable-debug		enable memory debugging | ||||
| code], | ||||
| 	[AC_DEFINE(DEBUG), | ||||
| 	DEBUGOBJ=debug.o], | ||||
| 	[DEBUGOBJ='']) | ||||
| AC_SUBST(DEBUGOBJ) | ||||
| 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) | ||||
| 
 | ||||
| AC_CACHE_CHECK(for dbm_open, ac_cv_dbmopen, | ||||
| 	[ac_cv_dbmopen=no | ||||
| 	AC_TRY_LINK([#include <ndbm.h>], | ||||
| 		[dbm_open(0,0,0);],[ac_cv_dbmopen=yes])]) | ||||
| if test $ac_cv_dbmopen = no; then | ||||
| 	AC_CACHE_CHECK([for dbm_open in -ldb], ac_cv_libdb, | ||||
| 		[save_LIBS="$LIBS" | ||||
| 		LIBS="$LIBS -ldb" | ||||
| 		ac_cv_libdb=no | ||||
| 		AC_TRY_LINK([#define DB_DBM_HSEARCH 1 | ||||
| #include <db.h>], | ||||
| 			[dbm_open(0,0,0);], | ||||
| 			[ac_cv_libdb=yes]) | ||||
| 		LIBS="$save_LIBS"]) | ||||
| 
 | ||||
| 	if test $ac_cv_libdb = yes; then | ||||
| 		LIBS="$LIBS -ldb" | ||||
| 	else | ||||
| 		AC_MSG_ERROR([Could not find dbm_open(), you must install libdb]) | ||||
| 	fi | ||||
| fi | ||||
| 
 | ||||
| dnl test for gcc.  use the prefix so we know that gcc-3.0 is also gcc | ||||
| if test `echo $CC | sed 's/^gcc.*/gcc/'` = gcc; then | ||||
| 	CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wmissing-prototypes" | ||||
| fi | ||||
| AC_OUTPUT(Makefile debian/Makefile) | ||||
							
								
								
									
										84
									
								
								cram.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								cram.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 <string.h> | ||||
| #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)); | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|     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
									
									
								
							
							
						
						
									
										7
									
								
								debian/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,7 +0,0 @@ | |||
| /.debhelper | ||||
| /autoreconf.after | ||||
| /autoreconf.before | ||||
| /files | ||||
| /isync | ||||
| /isync.debhelper.log | ||||
| /isync.substvars | ||||
							
								
								
									
										1
									
								
								debian/Makefile.am
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/Makefile.am
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| EXTRA_DIST=files dirs docs rules control copyright changelog | ||||
							
								
								
									
										13
									
								
								debian/README.Debian
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								debian/README.Debian
									
										
									
									
										vendored
									
									
								
							|  | @ -1,13 +0,0 @@ | |||
| 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). | ||||
							
								
								
									
										347
									
								
								debian/changelog
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										347
									
								
								debian/changelog
									
										
									
									
										vendored
									
									
								
							|  | @ -1,349 +1,6 @@ | |||
| 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 | ||||
|  -- Michael Elkins <me@mutt.org>  Wed, 16 Jan 2002 13:36:52 -0800 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								debian/compat
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | |||
| 9 | ||||
							
								
								
									
										47
									
								
								debian/control
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								debian/control
									
										
									
									
										vendored
									
									
								
							|  | @ -1,38 +1,23 @@ | |||
| Source: isync | ||||
| Section: mail | ||||
| Section: unknown | ||||
| 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/ | ||||
| Maintainer: Michael Elkins <me@mutt.org> | ||||
| Build-Depends: debhelper (>> 3.0.0) | ||||
| Standards-Version: 3.5.2 | ||||
| 
 | ||||
| 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. | ||||
| Depends: ${shlibs:Depends} | ||||
| Description: Synchronize a local maildir with a remote IMAP4 mailbox | ||||
|  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. | ||||
|  . | ||||
|  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 | ||||
|   * 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 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										53
									
								
								debian/copyright
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										53
									
								
								debian/copyright
									
										
									
									
										vendored
									
									
								
							|  | @ -1,33 +1,26 @@ | |||
| Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | ||||
| Upstream-Name: isync | ||||
| Source: http://isync.sourceforge.net | ||||
| This package was debianized by Michael Elkins <melkins@debian> on | ||||
| Wed, 16 Jan 2002 13:36:52 -0800. | ||||
| 
 | ||||
| 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+ | ||||
| It was downloaded from http://www.sigpipe.org:8080/isync/ | ||||
| 
 | ||||
| Files: debian/* | ||||
| Copyright: 2013, Alessandro Ghedini <ghedo@debian.org> | ||||
| License: GPL-2+ | ||||
| Upstream Author(s): Michael Elkins <me@mutt.org> | ||||
| 
 | ||||
| Copyright: | ||||
| 
 | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 | ||||
| 
 | ||||
| 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". | ||||
|  |  | |||
							
								
								
									
										4
									
								
								debian/docs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/docs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| NEWS | ||||
| README | ||||
| TODO | ||||
| sample.isyncrc | ||||
							
								
								
									
										87
									
								
								debian/rules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										87
									
								
								debian/rules
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,85 @@ | |||
| #!/usr/bin/make -f | ||||
| # Sample debian/rules that uses debhelper. | ||||
| # GNU copyright 1997 to 1999 by Joey Hess. | ||||
| 
 | ||||
| %: | ||||
| 	dh $@ --with=autoreconf | ||||
| # Uncomment this to turn on verbose mode. | ||||
| #export DH_VERBOSE=1 | ||||
| 
 | ||||
| override_dh_auto_install: | ||||
| 	dh_auto_install | ||||
| 	$(RM) $(CURDIR)/debian/isync/usr/share/doc/isync/ChangeLog | ||||
| # This is the debhelper compatability version to use. | ||||
| export DH_COMPAT=3 | ||||
| 
 | ||||
| configure: configure-stamp | ||||
| configure-stamp: | ||||
| 	dh_testdir | ||||
| 	# Add here commands to configure the package. | ||||
| 	./configure --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info | ||||
| 
 | ||||
| 	touch configure-stamp | ||||
| 
 | ||||
| build: build-stamp | ||||
| 
 | ||||
| build-stamp: configure-stamp  | ||||
| 	dh_testdir | ||||
| 
 | ||||
| 	# Add here commands to compile the package. | ||||
| 	$(MAKE) | ||||
| 	#/usr/bin/docbook-to-man debian/isync.sgml > isync.1 | ||||
| 
 | ||||
| 	touch build-stamp | ||||
| 
 | ||||
| clean: | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
| 	rm -f build-stamp configure-stamp | ||||
| 
 | ||||
| 	# Add here commands to clean up after the build process. | ||||
| 	-$(MAKE) distclean | ||||
| 
 | ||||
| 	dh_clean | ||||
| 
 | ||||
| install: build | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
| 	dh_clean -k | ||||
| 	dh_installdirs | ||||
| 
 | ||||
| 	# Add here commands to install the package into debian/isync. | ||||
| 	$(MAKE) install prefix=$(CURDIR)/debian/isync | ||||
| 
 | ||||
| 
 | ||||
| # Build architecture-independent files here. | ||||
| binary-indep: build install | ||||
| # We have nothing to do by default. | ||||
| 
 | ||||
| # Build architecture-dependent files here. | ||||
| binary-arch: build install | ||||
| 	dh_testdir | ||||
| 	dh_testroot | ||||
| #	dh_installdebconf	 | ||||
| 	dh_installdocs | ||||
| 	dh_installexamples | ||||
| #	dh_installmenu | ||||
| #	dh_installlogrotate | ||||
| #	dh_installemacsen | ||||
| #	dh_installpam | ||||
| #	dh_installmime | ||||
| #	dh_installinit | ||||
| #	dh_installcron | ||||
| 	dh_installman isync.1 | ||||
| #	dh_installinfo | ||||
| #	dh_undocumented | ||||
| 	dh_installchangelogs ChangeLog | ||||
| 	dh_link | ||||
| 	dh_strip | ||||
| 	dh_compress | ||||
| 	dh_fixperms | ||||
| #	dh_makeshlibs | ||||
| 	dh_installdeb | ||||
| #	dh_perl | ||||
| 	dh_shlibdeps | ||||
| 	dh_gencontrol | ||||
| 	dh_md5sums | ||||
| 	dh_builddeb | ||||
| 
 | ||||
| binary: binary-indep binary-arch | ||||
| .PHONY: build clean binary-indep binary-arch binary install configure | ||||
|  |  | |||
							
								
								
									
										1
									
								
								debian/source/format
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | |||
| 3.0 (quilt) | ||||
							
								
								
									
										2
									
								
								debian/watch
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/watch
									
										
									
									
										vendored
									
									
								
							|  | @ -1,2 +0,0 @@ | |||
| version=3 | ||||
| http://sf.net/isync/ isync-(.*)\.tar\.gz | ||||
							
								
								
									
										338
									
								
								isync.1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								isync.1
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,338 @@ | |||
| .ig | ||||
| \" isync - IMAP4 to maildir mailbox synchronizer | ||||
| \" Copyright (C) 2000-2 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 "2002 Jan 16" | ||||
| .. | ||||
| .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-a\fR, \fB--all\fR | ||||
| Synchronize all mailboxes specified in the user's ~/.isyncrc. | ||||
| .TP | ||||
| \fB-C\fR, \fB--create\fR | ||||
| Automatically create the local maildir-style mailbox if it doesn't already | ||||
| exist. | ||||
| .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 | ||||
| \fB-e\fR, \fB--expunge\fR | ||||
| 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 | ||||
| \fB-f\fR, \fB--fast\fR | ||||
| 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 | ||||
| \fB-h\fR, \fB--help\fR | ||||
| Displays a summary of command line options | ||||
| .TP | ||||
| \fB-p\fR, \fB--port\fR \fIport\fR | ||||
| Specifies the port on the IMAP server to connect to (default: 143) | ||||
| .TP | ||||
| \fB-q\fR, \fB--quiet\fR | ||||
| Supress feedback messages. | ||||
| .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 | ||||
| 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 | ||||
| \fBCopyDeletedTo\fR \fIstring\fR | ||||
| Specifies the remote IMAP mailbox to copy deleted messages prior to | ||||
| expunging (Default: none). | ||||
| .. | ||||
| .TP | ||||
| \fBDelete\fR \fIyes|no\fR | ||||
| Specifies whether messages in the local copy of the mailbox which don't | ||||
| exist on the server are automatically deleted.  (Default: no). | ||||
| .. | ||||
| .TP | ||||
| \fBExpunge\fR \fIyes|no\fR | ||||
| Specifies whether deleted messages are expunged by default (Default: no). | ||||
| \fBNOTE:\fR  The | ||||
| .I -e | ||||
| command line option overrides this setting when set to | ||||
| \fIno\fR. | ||||
| .. | ||||
| .TP | ||||
| \fBMailDir\fR \fIstring\fR | ||||
| Specifies the location for your mailboxes if a relative path is | ||||
| specified in a | ||||
| .I Mailbox | ||||
| command (Default: \fI~\fR). | ||||
| .B NOTE: | ||||
| This directive is only meaningful the in | ||||
| .I global | ||||
| section (see below). | ||||
| .. | ||||
| .TP | ||||
| \fBMaxMessages\fR \fIcount\fR | ||||
| Sets the number of messages | ||||
| .B isync | ||||
| should keep in a mailbox. | ||||
| This is useful for mailboxes where you keep a complete archive on the | ||||
| server, but want to mirror only the last messages (for instance, for mailing | ||||
| lists.) | ||||
| The messages that were the first to arrive in the mailbox (independent of the | ||||
| actual date of the message) will automatically be deleted if you tell | ||||
| pass | ||||
| .B isync | ||||
| the delete (-d, --delete) flag. | ||||
| Messages that are flagged (marked as important) will not be automatically | ||||
| deleted. | ||||
| If | ||||
| .I count | ||||
| is 0, the maximum number of messages is | ||||
| .B unlimited | ||||
| (Default: 0). | ||||
| .. | ||||
| .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 | ||||
| if the imaps port is used, otherwise | ||||
| .I no | ||||
| ) | ||||
| .. | ||||
| .TP | ||||
| \fBUseSSLv3\fR \fIyes|no\fR | ||||
| Should | ||||
| .B isync | ||||
| use SSLv3 for communication with the IMAP server over SSL?  (Default: | ||||
| .I yes | ||||
| if the imaps port is used, otherwise | ||||
| .I no | ||||
| ) | ||||
| .. | ||||
| .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 | ||||
| .B isync | ||||
| does not use NFS-safe locking.  It will correctly prevent concurrent | ||||
| synchronization of a mailbox on the same host, but not across NFS. | ||||
| .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. | ||||
| .P | ||||
| If new mail arrives in the IMAP mailbox after | ||||
| .B isync | ||||
| has retrieved the initial message list, the new mail will not be fetched | ||||
| until the next time | ||||
| .B isync | ||||
| is invoked. | ||||
| .P | ||||
| It is currently impossible to unset the \\Flagged attribute of a message | ||||
| once it is set.  It has to be manually unset everywhere since isync | ||||
| doesn't have enough information to know which was the last status of the | ||||
| message. | ||||
| .P | ||||
| The ndbm database created for each mailbox is not portable across different | ||||
| architectures.  It currently stores the UID in host byte order. | ||||
| .SH SEE ALSO | ||||
| mutt(1), maildir(5) | ||||
| .P | ||||
| Up to date information on | ||||
| .B isync | ||||
| can be found at | ||||
| http://www.sigpipe.org:8080/isync/. | ||||
| .. | ||||
| .SH AUTHOR | ||||
| Written by Michael R. Elkins <me@mutt.org>. | ||||
							
								
								
									
										209
									
								
								isync.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								isync.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 | ||||
|  */ | ||||
| 
 | ||||
| #define DB_DBM_HSEARCH 1 | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <stdarg.h> | ||||
| #if HAVE_LIBSSL | ||||
| #include <openssl/ssl.h> | ||||
| #endif | ||||
| #include <db.h> | ||||
| #include "debug.h" | ||||
| 
 | ||||
| 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 *maildir; | ||||
|     char *path;	/* path relative to .maildir, or absolute path */ | ||||
|     char *host; | ||||
|     int port; | ||||
|     char *user; | ||||
|     char *pass; | ||||
|     char *box; | ||||
|     char *alias; | ||||
|     char *copy_deleted_to; | ||||
|     unsigned int max_messages; | ||||
|     off_t 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; | ||||
|     unsigned int expunge:1; | ||||
|     unsigned int delete:1; | ||||
|     unsigned int wanted:1; | ||||
| }; | ||||
| 
 | ||||
| /* struct representing local mailbox file */ | ||||
| struct mailbox | ||||
| { | ||||
|     DBM *db; | ||||
|     char *path; | ||||
|     message_t *msgs; | ||||
|     int lockfd; | ||||
|     unsigned int deleted;	/* # of deleted messages */ | ||||
|     unsigned int uidvalidity; | ||||
|     unsigned int maxuid;	/* largest uid we know about */ | ||||
| }; | ||||
| 
 | ||||
| /* 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; | ||||
|     size_t 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 dead:1;	/* message doesn't exist on the server */ | ||||
|     unsigned int wanted:1;	/* when using MaxMessages, keep this message */ | ||||
| }; | ||||
| 
 | ||||
| /* 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 */ | ||||
|     char *prefix;		/* namespace prefix */ | ||||
|     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 */ | ||||
| #define SYNC_QUIET	(1<<2)	/* only display critical errors */ | ||||
| 
 | ||||
| /* flags for maildir_open */ | ||||
| #define OPEN_FAST	(1<<0)	/* fast open - don't parse */ | ||||
| #define OPEN_CREATE	(1<<1)	/* create mailbox if nonexistent */ | ||||
| 
 | ||||
| extern config_t global; | ||||
| extern config_t *boxes; | ||||
| 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, unsigned int); | ||||
| 
 | ||||
| void load_config (const char *); | ||||
| char * expand_strdup (const char *s); | ||||
| config_t *find_box (const char *); | ||||
| void free_config (void); | ||||
| 
 | ||||
| void imap_close (imap_t *); | ||||
| int imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox); | ||||
| 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 *); | ||||
| int imap_append_message (imap_t *, int, message_t *); | ||||
| 
 | ||||
| mailbox_t *maildir_open (const char *, int flags); | ||||
| int maildir_expunge (mailbox_t *, int); | ||||
| int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity); | ||||
| void maildir_close (mailbox_t *); | ||||
| int maildir_update_maxuid (mailbox_t * mbox); | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| #define strfcpy(a,b,c) {strncpy(a,b,c);(a)[c-1]=0;} | ||||
|  | @ -1,35 +1,33 @@ | |||
| Summary: Utility to synchronize IMAP mailboxes with local maildir folders | ||||
| Name: isync | ||||
| Version: @VERSION@ | ||||
| Version: 0.8 | ||||
| Release: 1 | ||||
| License: GPL | ||||
| Copyright: GPL | ||||
| Group: Applications/Internet | ||||
| Source: @PACKAGE@-@VERSION@.tar.gz | ||||
| URL: http://@PACKAGE@.sf.net/ | ||||
| Packager: Oswald Buddenhagen <ossi@users.sf.net> | ||||
| Source: http://www.sigpipe.org:8080/isync/isync-0.8.tar.gz | ||||
| URL: http://www.sigpipe.org:8080/isync/ | ||||
| Packager: Michael Elkins <me@mutt.org> | ||||
| BuildRoot: /var/tmp/%{name}-buildroot | ||||
| 
 | ||||
| %description | ||||
| 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). | ||||
| 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. | ||||
| 
 | ||||
| %prep | ||||
| %setup | ||||
| %build | ||||
| %configure | ||||
| ./configure --prefix=/usr | ||||
| make RPM_OPT_FLAGS="$RPM_OPT_FLAGS" | ||||
| 
 | ||||
| %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 AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample | ||||
| %{_bindir}/* | ||||
| %{_mandir}/man1/* | ||||
| %doc COPYING README TODO ChangeLog | ||||
| /usr/bin/isync | ||||
| /usr/man/man1/isync.1.gz | ||||
|  |  | |||
							
								
								
									
										42
									
								
								isyncrc.sample
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								isyncrc.sample
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| # Global configuration section | ||||
| #   Values here are used as defaults for any following Mailbox section that | ||||
| #   doesn't specify it. | ||||
| 
 | ||||
| # by default, expunge deleted messages (same as -e on command line) | ||||
| Expunge yes | ||||
| 
 | ||||
| # by default delete messages in the local mailbox which no longer exist | ||||
| # on the server | ||||
| Delete yes | ||||
| 
 | ||||
| # copy deleted messages to the IMAP "Trash" folder | ||||
| CopyDeletedTo "Trash" | ||||
| 
 | ||||
| # my default username, if different from the local username | ||||
| User me | ||||
| #Port	143 | ||||
| #Box	INBOX | ||||
| # don't download messages larger than 200K bytes | ||||
| MaxSize	200000 | ||||
| 
 | ||||
| ### | ||||
| ### work mailbox | ||||
| ### | ||||
| 
 | ||||
| Mailbox /home/me/Mail/work | ||||
| Host	work.host.com | ||||
| Pass	xxxxxxxx | ||||
| # define a shortcut so I can just use "isync work" from the command line | ||||
| Alias	work | ||||
| # don't auto expunge messages in this box (overridden by -e on command line) | ||||
| Expunge no | ||||
| 
 | ||||
| ### | ||||
| ### personal mailbox | ||||
| ### | ||||
| 
 | ||||
| Mailbox /home/me/Mail/personal | ||||
| Host host.play.com | ||||
| # use a non-default port for this connection | ||||
| Port 6789 | ||||
| Alias personal | ||||
							
								
								
									
										175
									
								
								list.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								list.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,175 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 (tmp)) | ||||
| 	    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 | ||||
							
								
								
									
										483
									
								
								maildir.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								maildir.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,483 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 <time.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 = 0; | ||||
|     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 (full); | ||||
| 	    return -1; | ||||
| 	} | ||||
| 	return 0;		/* doesn't exist */ | ||||
|     } | ||||
|     len = read (fd, buf, sizeof (buf) - 1); | ||||
|     if (len == -1) | ||||
|     { | ||||
| 	perror ("read"); | ||||
| 	ret = -1; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| 	buf[len] = 0; | ||||
| 	uid = atol (buf); | ||||
|     } | ||||
|     close (fd); | ||||
|     return ret ? (unsigned int) ret : uid; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* NOTE: this is NOT NFS safe */ | ||||
| static int | ||||
| maildir_lock (mailbox_t * m) | ||||
| { | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
| 
 | ||||
|     snprintf (path, sizeof (path), "%s/isynclock", m->path); | ||||
|     m->lockfd = open (path, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR); | ||||
|     if (m->lockfd == -1) | ||||
|     { | ||||
| 	perror (path); | ||||
| 	return -1; | ||||
|     } | ||||
|     if (do_lock (m->lockfd, F_WRLCK)) | ||||
|     { | ||||
| 	close (m->lockfd); | ||||
| 	return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| maildir_unlock (mailbox_t * m) | ||||
| { | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
| 
 | ||||
|     snprintf (path, sizeof (path), "%s/isynclock", m->path); | ||||
|     unlink (path); | ||||
|     do_lock (m->lockfd, F_UNLCK); | ||||
|     close (m->lockfd); | ||||
| } | ||||
| 
 | ||||
| /* open a maildir mailbox.
 | ||||
|  * if OPEN_FAST is set, 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. | ||||
|  * if OPEN_CREATE is set, we create the mailbox if it doesn't already exist. | ||||
|  */ | ||||
| mailbox_t * | ||||
| maildir_open (const char *path, int flags) | ||||
| { | ||||
|     char buf[_POSIX_PATH_MAX]; | ||||
|     DIR *d; | ||||
|     struct dirent *e; | ||||
|     message_t **cur; | ||||
|     message_t *p; | ||||
|     mailbox_t *m; | ||||
|     char *s; | ||||
|     int count = 0; | ||||
|     struct stat sb; | ||||
|     const char *subdirs[] = { "cur", "new", "tmp" }; | ||||
|     int i; | ||||
|     datum key; | ||||
| 
 | ||||
|     m = calloc (1, sizeof (mailbox_t)); | ||||
|     m->lockfd = -1; | ||||
|     /* filename expansion happens here, not in the config parser */ | ||||
|     m->path = expand_strdup (path); | ||||
| 
 | ||||
|     if (stat (m->path, &sb)) | ||||
|     { | ||||
| 	if (errno == ENOENT && (flags & OPEN_CREATE)) | ||||
| 	{ | ||||
| 	    if (mkdir (m->path, S_IRUSR | S_IWUSR | S_IXUSR)) | ||||
| 	    { | ||||
| 		fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n", | ||||
| 			 m->path, strerror (errno), errno); | ||||
| 		goto err; | ||||
| 	    } | ||||
| 
 | ||||
| 	    for (i = 0; i < 3; i++) | ||||
| 	    { | ||||
| 		snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]); | ||||
| 		if (mkdir (buf, S_IRUSR | S_IWUSR | S_IXUSR)) | ||||
| 		{ | ||||
| 		    fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n", | ||||
| 			     buf, strerror (errno), errno); | ||||
| 		    goto err; | ||||
| 		} | ||||
| 	    } | ||||
| 
 | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	    fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", m->path, | ||||
| 		     strerror (errno), errno); | ||||
| 	    goto err; | ||||
| 	} | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| 	/* check to make sure this looks like a valid maildir box */ | ||||
| 	for (i = 0; i < 3; i++) | ||||
| 	{ | ||||
| 	    snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]); | ||||
| 	    if (stat (buf, &sb)) | ||||
| 	    { | ||||
| 		fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", buf, | ||||
| 			 strerror (errno), errno); | ||||
| 		fprintf (stderr, | ||||
| 			 "ERROR: %s does not appear to be a valid maildir style mailbox\n", | ||||
| 			 m->path); | ||||
| 		goto err; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     /* we need a mutex on the maildir because of the state files that isync
 | ||||
|      * uses. | ||||
|      */ | ||||
|     if (maildir_lock (m)) | ||||
| 	goto err; | ||||
| 
 | ||||
|     /* check for the uidvalidity value */ | ||||
|     m->uidvalidity = read_uid (m->path, "isyncuidvalidity"); | ||||
|     if (m->uidvalidity == (unsigned int) -1) | ||||
| 	goto err; | ||||
| 
 | ||||
|     /* load the current maxuid */ | ||||
|     if ((m->maxuid = read_uid (m->path, "isyncmaxuid")) == (unsigned int) -1) | ||||
| 	goto err; | ||||
| 
 | ||||
|     if (flags & OPEN_FAST) | ||||
| 	return m; | ||||
| 
 | ||||
|     snprintf (buf, sizeof (buf), "%s/isyncuidmap", m->path); | ||||
|     m->db = dbm_open (buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | ||||
|     if (m->db == NULL) | ||||
|     { | ||||
| 	fputs ("ERROR: unable to open UID db\n", stderr); | ||||
| 	goto err; | ||||
|     } | ||||
| 
 | ||||
|     cur = &m->msgs; | ||||
|     for (; count < 2; count++) | ||||
|     { | ||||
| 	/* read the msgs from the new subdir */ | ||||
| 	snprintf (buf, sizeof (buf), "%s/%s", m->path, | ||||
| 		  (count == 0) ? "new" : "cur"); | ||||
| 	d = opendir (buf); | ||||
| 	if (!d) | ||||
| 	{ | ||||
| 	    perror ("opendir"); | ||||
| 	    goto err; | ||||
| 	} | ||||
| 	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 = 0; | ||||
| 	    p->new = (count == 0); | ||||
| 
 | ||||
| 	    /* determine the UID for this message.  The basename (sans
 | ||||
| 	     * flags) is used as the key in the db | ||||
| 	     */ | ||||
| 	    key.dptr = p->file; | ||||
| 	    s = strchr (key.dptr, ':'); | ||||
| 	    key.dsize = s ? (size_t) (s - key.dptr) : strlen (key.dptr); | ||||
| 	    key = dbm_fetch (m->db, key); | ||||
| 	    if (key.dptr) | ||||
| 	    { | ||||
| 		p->uid = *(int *) key.dptr; | ||||
| 		if (p->uid > m->maxuid) | ||||
| 		    m->maxuid = p->uid; | ||||
| 	    } | ||||
| 	    else | ||||
| 		puts ("Warning, no UID for message"); | ||||
| 
 | ||||
| 	    if (s) | ||||
| 		parse_info (p, s + 1); | ||||
| 	    if (p->flags & D_DELETED) | ||||
| 		m->deleted++; | ||||
| 	    cur = &p->next; | ||||
| 	} | ||||
| 	closedir (d); | ||||
|     } | ||||
|     return m; | ||||
| 
 | ||||
|   err: | ||||
|     if (m->db) | ||||
| 	dbm_close (m->db); | ||||
|     if (m->lockfd != -1) | ||||
| 	maildir_unlock (m); | ||||
|     free (m->path); | ||||
|     free (m); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| /* 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 *s; | ||||
|     datum key; | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
| 
 | ||||
|     while (*cur) | ||||
|     { | ||||
| 	if ((dead == 0 && (*cur)->flags & D_DELETED) || | ||||
| 	    (dead && (*cur)->dead)) | ||||
| 	{ | ||||
| 	    tmp = *cur; | ||||
| 	    snprintf (path, sizeof (path), "%s/%s/%s", | ||||
| 		      mbox->path, tmp->new ? "new" : "cur", tmp->file); | ||||
| 	    if (unlink (path)) | ||||
| 		perror (path); | ||||
| 	    /* remove the message from the UID map */ | ||||
| 	    key.dptr = tmp->file; | ||||
| 	    s = strchr (key.dptr, ':'); | ||||
| 	    key.dsize = s ? (size_t) (s - key.dptr) : strlen (key.dptr); | ||||
| 	    dbm_delete (mbox->db, key); | ||||
| 	    *cur = (*cur)->next; | ||||
| 	    free (tmp->file); | ||||
| 	    free (tmp); | ||||
| 	} | ||||
| 	else | ||||
| 	    cur = &(*cur)->next; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| maildir_update_maxuid (mailbox_t * mbox) | ||||
| { | ||||
|     int fd; | ||||
|     char buf[64]; | ||||
|     size_t len; | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
|     int ret = 0; | ||||
| 
 | ||||
|     snprintf (path, sizeof (path), "%s/isyncmaxuid", mbox->path); | ||||
|     fd = open (path, O_WRONLY | O_CREAT, 0600); | ||||
|     if (fd == -1) | ||||
|     { | ||||
| 	perror ("open"); | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
|     /* 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; | ||||
|     } | ||||
| 
 | ||||
|     if (close (fd)) | ||||
| 	ret = -1; | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| #define _24_HOURS (3600 * 24) | ||||
| 
 | ||||
| static void | ||||
| maildir_clean_tmp (const char *mbox) | ||||
| { | ||||
|     char path[_POSIX_PATH_MAX]; | ||||
|     DIR *dirp; | ||||
|     struct dirent *entry; | ||||
|     struct stat info; | ||||
|     time_t now; | ||||
| 
 | ||||
|     snprintf (path, sizeof (path), "%s/tmp", mbox); | ||||
|     dirp = opendir (path); | ||||
|     if (dirp == NULL) | ||||
|     { | ||||
| 	fprintf (stderr, "maildir_clean_tmp: opendir: %s: %s (errno %d)\n", | ||||
| 		 path, strerror (errno), errno); | ||||
| 	return; | ||||
|     } | ||||
|     /* assuming this scan will take less than a second, we only need to
 | ||||
|      * check the time once before the following loop. | ||||
|      */ | ||||
|     time (&now); | ||||
|     while ((entry = readdir (dirp))) | ||||
|     { | ||||
| 	snprintf (path, sizeof (path), "%s/tmp/%s", mbox, entry->d_name); | ||||
| 	if (stat (path, &info)) | ||||
| 	    fprintf (stderr, "maildir_clean_tmp: stat: %s: %s (errno %d)\n", | ||||
| 		     path, strerror (errno), errno); | ||||
| 	else if (S_ISREG (info.st_mode) && now - info.st_ctime >= _24_HOURS) | ||||
| 	{ | ||||
| 	    /* this should happen infrequently enough that it won't be
 | ||||
| 	     * bothersome to the user to display when it occurs. | ||||
| 	     */ | ||||
| 	    printf ("Warning: removing stale file %s\n", path); | ||||
| 	    if (unlink (path)) | ||||
| 		fprintf (stderr, | ||||
| 			 "maildir_clean_tmp: unlink: %s: %s (errno %d)\n", | ||||
| 			 path, strerror (errno), errno); | ||||
| 	} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| maildir_close (mailbox_t * mbox) | ||||
| { | ||||
|     if (mbox->db) | ||||
| 	dbm_close (mbox->db); | ||||
| 
 | ||||
|     /* release the mutex on the mailbox */ | ||||
|     maildir_unlock (mbox); | ||||
| 
 | ||||
|     /* per the maildir(5) specification, delivery agents are supposed to
 | ||||
|      * set a 24-hour timer on items placed in the `tmp' directory. | ||||
|      */ | ||||
|     maildir_clean_tmp (mbox->path); | ||||
| 
 | ||||
|     free (mbox->path); | ||||
|     free_message (mbox->msgs); | ||||
|     memset (mbox, 0xff, sizeof (mailbox_t)); | ||||
|     free (mbox); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										370
									
								
								main.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								main.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,370 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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[] = { | ||||
|     {"all", 0, NULL, 'a'}, | ||||
|     {"config", 1, NULL, 'c'}, | ||||
|     {"create", 0, 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'}, | ||||
|     {"quiet", 0, NULL, 'q'}, | ||||
|     {"user", 1, NULL, 'u'}, | ||||
|     {"version", 0, NULL, 'v'}, | ||||
|     {"verbose", 0, NULL, 'V'}, | ||||
|     {0, 0, 0, 0} | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| config_t global; | ||||
| unsigned int Tag = 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-2 Michael R. Elkins <me@mutt.org>"); | ||||
|     printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE); | ||||
|     puts ("  -a, --all	Synchronize all defined mailboxes"); | ||||
|     puts ("  -c, --config CONFIG	read an alternate config file (default: ~/.isyncrc)"); | ||||
|     puts ("  -C, --create		create local maildir mailbox if nonexistent"); | ||||
|     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)"); | ||||
|     puts ("Compile time options:"); | ||||
| #if HAVE_LIBSSL | ||||
|     puts ("  +HAVE_LIBSSL"); | ||||
| #else | ||||
|     puts ("  -HAVE_LIBSSL"); | ||||
| #endif | ||||
|     exit (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; | ||||
|     } | ||||
|     if (**s == '"') | ||||
|     { | ||||
| 	++*s; | ||||
| 	ret = *s; | ||||
| 	*s = strchr (*s, '"'); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| 	ret = *s; | ||||
| 	while (**s && !isspace ((unsigned char) **s)) | ||||
| 	    (*s)++; | ||||
|     } | ||||
|     if (*s) | ||||
|     { | ||||
| 	if (**s) | ||||
| 	    *(*s)++ = 0; | ||||
| 	if (!**s) | ||||
| 	    *s = 0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| main (int argc, char **argv) | ||||
| { | ||||
|     int i; | ||||
|     config_t *box = 0; | ||||
|     mailbox_t *mail = 0; | ||||
|     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; | ||||
|     int quiet = 0; | ||||
|     int all = 0; | ||||
|     int create = 0; | ||||
| 
 | ||||
|     pw = getpwuid (getuid ()); | ||||
| 
 | ||||
|     /* defaults */ | ||||
|     memset (&global, 0, sizeof (global)); | ||||
|     global.port = 143; | ||||
|     global.box = "INBOX"; | ||||
|     global.user = strdup (pw->pw_name); | ||||
|     global.maildir = strdup (pw->pw_dir); | ||||
|     global.max_size = 0; | ||||
|     global.max_messages = 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 = 0; | ||||
|     global.use_sslv3 = 0; | ||||
|     global.use_tlsv1 = 1; | ||||
| #endif | ||||
| 
 | ||||
| #define FLAGS "aCc:defhp:qu:r:s:vV" | ||||
| 
 | ||||
| #if HAVE_GETOPT_LONG | ||||
|     while ((i = getopt_long (argc, argv, FLAGS, Opts, NULL)) != -1) | ||||
| #else | ||||
|     while ((i = getopt (argc, argv, FLAGS)) != -1) | ||||
| #endif | ||||
|     { | ||||
| 	switch (i) | ||||
| 	{ | ||||
| 	    case 'a': | ||||
| 		all = 1; | ||||
| 		break; | ||||
| 	    case 'C': | ||||
| 		create = 1; | ||||
| 		break; | ||||
| 	    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 'q': | ||||
| 		quiet = 1; | ||||
| 		Verbose = 0; | ||||
| 		break; | ||||
| 	    case 'r': | ||||
| 		global.box = optarg; | ||||
| 		break; | ||||
| 	    case 's': | ||||
| #if HAVE_LIBSSL | ||||
| 		if (!strncasecmp ("imaps:", optarg, 6)) | ||||
| 		{ | ||||
| 		    global.use_imaps = 1; | ||||
| 		    optarg += 6; | ||||
| 		} | ||||
| #endif | ||||
| 		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] && !all) | ||||
|     { | ||||
| 	puts ("No mailbox specified"); | ||||
| 	usage (); | ||||
|     } | ||||
| 
 | ||||
|     gethostname (Hostname, sizeof (Hostname)); | ||||
| 
 | ||||
|     load_config (config); | ||||
| 
 | ||||
|     for (box = boxes; (all && box) || (!all && argv[optind]); optind++) | ||||
|     { | ||||
| 	if (!all) | ||||
| 	{ | ||||
| 	    if (NULL == (box = find_box (argv[optind]))) | ||||
| 	    { | ||||
| 		/* if enough info is given on the command line, don't worry if
 | ||||
| 		 * the mailbox isn't defined. | ||||
| 		 */ | ||||
| 		if (!global.host) | ||||
| 		{ | ||||
| 		    fprintf (stderr, "%s: no such mailbox\n", argv[optind]); | ||||
| 		    /* continue is ok here because we are not handling the
 | ||||
| 		     * `all' case. | ||||
| 		     */ | ||||
| 		    continue; | ||||
| 		} | ||||
| 		global.path = argv[optind]; | ||||
| 		box = &global; | ||||
| 	    } | ||||
| 	} | ||||
| 
 | ||||
| 	do { | ||||
| 	    if (!box->pass) | ||||
| 	    { | ||||
| 		/* if we don't have a global password set, prompt the user for
 | ||||
| 		 * it now. | ||||
| 		 */ | ||||
| 		if (!global.pass) | ||||
| 		{ | ||||
| 		    global.pass = getpass ("Password:"); | ||||
| 		    if (!global.pass) | ||||
| 		    { | ||||
| 			fprintf (stderr, "Skipping %s, no password", box->path); | ||||
| 			break; | ||||
| 		    } | ||||
| 		} | ||||
| 		box->pass = strdup (global.pass); | ||||
| 	    } | ||||
| 
 | ||||
| 	    if (!quiet) | ||||
| 		printf ("Reading %s\n", box->path); | ||||
| 	    i = 0; | ||||
| 	    if (fast) | ||||
| 		i |= OPEN_FAST; | ||||
| 	    if (create) | ||||
| 		i |= OPEN_CREATE; | ||||
| 	    mail = maildir_open (box->path, i); | ||||
| 	    if (!mail) | ||||
| 	    { | ||||
| 		fprintf (stderr, "%s: unable to open mailbox\n", box->path); | ||||
| 		break; | ||||
| 	    } | ||||
| 
 | ||||
| 	    imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap); | ||||
| 	    if (!imap) | ||||
| 	    { | ||||
| 		fprintf (stderr, "%s: skipping mailbox due to IMAP error\n", | ||||
| 			 box->path); | ||||
| 		break; | ||||
| 	    } | ||||
| 
 | ||||
| 	    if (!quiet) | ||||
| 		puts ("Synchronizing"); | ||||
| 	    i = 0; | ||||
| 	    if (quiet) | ||||
| 		i |= SYNC_QUIET; | ||||
| 	    i |= (delete || box->delete) ? SYNC_DELETE : 0; | ||||
| 	    i |= (expunge || box->expunge) ? SYNC_EXPUNGE : 0; | ||||
| 	    if (sync_mailbox (mail, imap, i, box->max_size, box->max_messages)) | ||||
| 	    { | ||||
| 		imap_close (imap); /* Just to be safe.  Don't really know
 | ||||
| 				    * what the problem was. | ||||
| 				    */ | ||||
| 		break; | ||||
| 	    } | ||||
| 
 | ||||
| 	    if (!fast) | ||||
| 	    { | ||||
| 		if ((expunge || box->expunge) && | ||||
| 		    (imap->deleted || mail->deleted)) | ||||
| 		{ | ||||
| 		    /* remove messages marked for deletion */ | ||||
| 		    if (!quiet) | ||||
| 			printf ("Expunging %d messages from server\n", | ||||
| 				imap->deleted); | ||||
| 		    if (imap_expunge (imap)) | ||||
| 		    { | ||||
| 			imap_close (imap); | ||||
| 			imap = NULL; | ||||
| 			break; | ||||
| 		    } | ||||
| 		    if (!quiet) | ||||
| 			printf ("Expunging %d messages from local mailbox\n", | ||||
| 				mail->deleted); | ||||
| 		    if (maildir_expunge (mail, 0)) | ||||
| 			break; | ||||
| 		} | ||||
| 		/* 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); | ||||
| 	    } | ||||
| 
 | ||||
| 	} while (0); | ||||
| 
 | ||||
| 	/* we never sync the same mailbox twice, so close it now */ | ||||
| 	if (mail) | ||||
| 	    maildir_close (mail); | ||||
| 
 | ||||
| 	/* the imap connection is not closed so we can keep the connection
 | ||||
| 	 * open, and there is no IMAP command for un-SELECT-ing a mailbox. | ||||
| 	 */ | ||||
| 	if (all) | ||||
| 	    box = box->next; | ||||
|     } | ||||
| 
 | ||||
|     /* gracefully close connection to the IMAP server */ | ||||
|     imap_close (imap); | ||||
| 
 | ||||
|     free_config (); | ||||
| 
 | ||||
| #if DEBUG | ||||
|     debug_cleanup (); | ||||
| #endif | ||||
| 
 | ||||
|     exit (0); | ||||
| } | ||||
|  | @ -1,62 +0,0 @@ | |||
| #!/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
									
									
								
							
							
						
						
									
										8
									
								
								src/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +0,0 @@ | |||
| /drv_proxy.inc | ||||
| /mbsync | ||||
| /mdconvert | ||||
| /tst_timers | ||||
| /tmp/ | ||||
| 
 | ||||
| .deps/ | ||||
| *.o | ||||
|  | @ -1,28 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										264
									
								
								src/common.h
									
										
									
									
									
								
							|  | @ -1,264 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										536
									
								
								src/config.c
									
										
									
									
									
								
							|  | @ -1,536 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										51
									
								
								src/config.h
									
										
									
									
									
								
							|  | @ -1,51 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										78
									
								
								src/driver.c
									
										
									
									
									
								
							|  | @ -1,78 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										302
									
								
								src/driver.h
									
										
									
									
									
								
							|  | @ -1,302 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										3821
									
								
								src/drv_imap.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										2021
									
								
								src/drv_maildir.c
									
										
									
									
									
								
							
							
						
						
									
										2021
									
								
								src/drv_maildir.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										447
									
								
								src/drv_proxy.c
									
										
									
									
									
								
							
							
						
						
									
										447
									
								
								src/drv_proxy.c
									
										
									
									
									
								
							|  | @ -1,447 +0,0 @@ | |||
| /*
 | ||||
|  * 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" | ||||
|  | @ -1,198 +0,0 @@ | |||
| #!/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
									
										
									
									
									
								
							
							
						
						
									
										1156
									
								
								src/main.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										823
									
								
								src/mbsync.1
									
										
									
									
									
								
							
							
						
						
									
										823
									
								
								src/mbsync.1
									
										
									
									
									
								
							|  | @ -1,823 +0,0 @@ | |||
| .\" 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. | ||||
|  | @ -1,97 +0,0 @@ | |||
| # 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 * | ||||
|  | @ -1,50 +0,0 @@ | |||
| .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
									
										
									
									
									
								
							
							
						
						
									
										284
									
								
								src/mdconvert.c
									
										
									
									
									
								
							|  | @ -1,284 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										806
									
								
								src/run-tests.pl
									
										
									
									
									
								
							|  | @ -1,806 +0,0 @@ | |||
| #! /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
									
										
									
									
									
								
							
							
						
						
									
										1160
									
								
								src/socket.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										155
									
								
								src/socket.h
									
										
									
									
									
								
							
							
						
						
									
										155
									
								
								src/socket.h
									
										
									
									
									
								
							|  | @ -1,155 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										2467
									
								
								src/sync.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										87
									
								
								src/sync.h
									
										
									
									
									
								
							
							
						
						
									
										87
									
								
								src/sync.h
									
										
									
									
									
								
							|  | @ -1,87 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								src/tst_timers.c
									
										
									
									
									
								
							|  | @ -1,116 +0,0 @@ | |||
| /*
 | ||||
|  * 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
									
										
									
									
									
								
							
							
						
						
									
										904
									
								
								src/util.c
									
										
									
									
									
								
							|  | @ -1,904 +0,0 @@ | |||
| /*
 | ||||
|  * 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(); | ||||
| } | ||||
							
								
								
									
										383
									
								
								sync.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								sync.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,383 @@ | |||
| /* $Id$
 | ||||
|  * | ||||
|  * isync - IMAP4 to maildir mailbox synchronizer | ||||
|  * Copyright (C) 2000-2 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 <sys/stat.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; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| set_uid (DBM * db, const char *f, unsigned int uid) | ||||
| { | ||||
|     char *s; | ||||
|     datum key, val; | ||||
| 
 | ||||
|     key.dptr = (void *) f; | ||||
|     s = strchr (f, ':'); | ||||
|     key.dsize = s ? (size_t) (s - key.dptr) : strlen (f); | ||||
|     val.dptr = (void *) &uid; | ||||
|     val.dsize = sizeof (uid); | ||||
|     dbm_store (db, key, val, DBM_REPLACE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, | ||||
| 	      unsigned int max_size, unsigned int max_msgs) | ||||
| { | ||||
|     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; | ||||
|     int upload = 0; | ||||
|     unsigned int msg_count; | ||||
| 
 | ||||
|     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. | ||||
| 	     */ | ||||
| 	    fputs ("ERROR: UIDVALIDITY changed on server (fatal)\n", stderr); | ||||
| 	    return -1; | ||||
| 	} | ||||
|     } | ||||
|     else if (maildir_set_uidvalidity (mbox, imap->uidvalidity)) | ||||
|     { | ||||
| 	fputs ("ERROR: unable to store UIDVALIDITY\n", stderr); | ||||
| 	return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (mbox->maxuid == 0 || imap->maxuid > mbox->maxuid) | ||||
|     { | ||||
| 	mbox->maxuid = imap->maxuid; | ||||
| 	if (maildir_update_maxuid (mbox)) | ||||
| 	    return -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) | ||||
| 	{ | ||||
| 	    /* if this message wasn't fetched from the server, attempt to
 | ||||
| 	     * upload it | ||||
| 	     */ | ||||
| 	    if (cur->uid == (unsigned int) -1) | ||||
| 	    { | ||||
| 		struct stat sb; | ||||
| 
 | ||||
| 		if ((flags & SYNC_QUIET) == 0) | ||||
| 		{ | ||||
| 		    if (!upload) | ||||
| 			fputs ("Uploading messages", stdout); | ||||
| 		    fputc ('.', stdout); | ||||
| 		    fflush (stdout); | ||||
| 		    upload++; | ||||
| 		} | ||||
| 
 | ||||
| 		/* upload the message if its not too big */ | ||||
| 		snprintf (path, sizeof (path), "%s/%s/%s", mbox->path, | ||||
| 			  cur->new ? "new" : "cur", cur->file); | ||||
| 		if (stat (path, &sb)) | ||||
| 		{ | ||||
| 		    perror (path); | ||||
| 		    continue;	/* not fatal */ | ||||
| 		} | ||||
| 		if (imap->box->max_size > 0 | ||||
| 		    && sb.st_size > imap->box->max_size) | ||||
| 		{ | ||||
| 		    if ((flags & SYNC_QUIET) == 0) | ||||
| 			printf | ||||
| 			    ("Warning, local message is too large (%lu), skipping...\n", | ||||
| 			     (unsigned long) sb.st_size); | ||||
| 		    continue; | ||||
| 		} | ||||
| 		fd = open (path, O_RDONLY); | ||||
| 		if (fd == -1) | ||||
| 		{ | ||||
| 		    printf ("Error, unable to open %s: %s (errno %d)\n", | ||||
| 			    path, strerror (errno), errno); | ||||
| 		    continue; | ||||
| 		} | ||||
| 
 | ||||
| 		cur->size = sb.st_size; | ||||
| 		cur->uid = imap_append_message (imap, fd, cur); | ||||
| 		/* if the server gave us back a uid, update the db */ | ||||
| 		if (cur->uid != (unsigned int) -1) | ||||
| 		    set_uid (mbox->db, cur->file, cur->uid); | ||||
| 
 | ||||
| 		close (fd); | ||||
| 	    } | ||||
| 	    else if (flags & SYNC_DELETE) | ||||
| 	    { | ||||
| 		cur->flags |= D_DELETED; | ||||
| 		cur->dead = 1; | ||||
| 		mbox->deleted++; | ||||
| 	    } | ||||
| 	    /* if the user doesn't want local msgs deleted when they don't
 | ||||
| 	     * exist on the server, warn that such messages exist. | ||||
| 	     */ | ||||
| 	    else if ((flags & SYNC_QUIET) == 0) | ||||
| 		printf ("Warning, uid %u doesn't exist on server\n", | ||||
| 			cur->uid); | ||||
| 	    continue; | ||||
| 	} | ||||
| 	tmp->processed = 1; | ||||
| 
 | ||||
| 	/* if the message is deleted, and CopyDeletedTo is set, and we
 | ||||
| 	 * are expunging, make a copy of the message now. | ||||
| 	 */ | ||||
| 	if (((cur->flags | tmp->flags) & D_DELETED) != 0 && | ||||
| 	    (flags & SYNC_EXPUNGE) && imap->box->copy_deleted_to) | ||||
| 	{ | ||||
| 	    if (imap_copy_message (imap, cur->uid, | ||||
| 				   imap->box->copy_deleted_to)) | ||||
| 	    { | ||||
| 		fprintf (stderr, | ||||
| 			 "ERROR: unable to copy deleted message to \"%s\"\n", | ||||
| 			 imap->box->copy_deleted_to); | ||||
| 		return -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)); | ||||
| 
 | ||||
| 	    /* don't bother renaming the file if we are just going to
 | ||||
| 	     * remove it later. | ||||
| 	     */ | ||||
| 	    if ((cur->flags & D_DELETED) == 0 || (flags & SYNC_EXPUNGE) == 0) | ||||
| 	    { | ||||
| 		    /* generate old path */ | ||||
| 		    snprintf (path, sizeof (path), "%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 - always put this in the cur/ directory
 | ||||
| 		     * because its no longer new | ||||
| 		     */ | ||||
| 		    snprintf (newpath, sizeof (newpath), "%s/cur/%s:2,%s%s%s%s", | ||||
| 				    mbox->path, | ||||
| 				    cur->file, (cur->flags & D_FLAGGED) ? "F" : "", | ||||
| 				    (cur->flags & D_ANSWERED) ? "R" : "", | ||||
| 				    (cur->flags & D_SEEN) ? "S" : "", | ||||
| 				    (cur->flags & D_DELETED) ? "T" : ""); | ||||
| 
 | ||||
| 		    if (rename (path, newpath)) | ||||
| 		    { | ||||
| 			    perror ("rename"); | ||||
| 			    return -1; | ||||
| 		    } | ||||
| 		    else | ||||
| 		    { | ||||
| 			    /* update the filename in the msg struct */ | ||||
| 			    p = strrchr (newpath, '/'); | ||||
| 			    free (cur->file); | ||||
| 			    cur->file = strdup (p + 1); | ||||
| 			    cur->new = 0; /* not any more */ | ||||
| 		    } | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     if (upload) | ||||
| 	fprintf (stdout, " %d messages.\n", upload); | ||||
| 
 | ||||
|     if ((flags & SYNC_QUIET) == 0) | ||||
|     { | ||||
| 	fputs ("Fetching new messages", stdout); | ||||
| 	fflush (stdout); | ||||
|     } | ||||
| 
 | ||||
|     if (max_msgs == 0) | ||||
| 	max_msgs = UINT_MAX; | ||||
|     else | ||||
|     { | ||||
| 	/* expire messages in excess of the max-count for this mailbox.
 | ||||
| 	 * flagged mails are considered sacrosant and not deleted. | ||||
| 	 * we have already done the upload to the server, so messing with | ||||
| 	 * the flags variable do not have remote side effects. | ||||
| 	 */ | ||||
| 	for (cur = imap->msgs, msg_count = 0; | ||||
| 	     cur && msg_count < max_msgs; cur = cur->next, msg_count++) | ||||
| 	{ | ||||
| 	    tmp = find_msg (mbox->msgs, cur->uid); | ||||
| 	    if (tmp) | ||||
| 		tmp->wanted = 1; | ||||
| 	} | ||||
| 	for (cur = mbox->msgs; cur; cur = cur->next) | ||||
| 	{ | ||||
| 	    if (!cur->wanted && !(cur->flags & D_FLAGGED)) | ||||
| 	    { | ||||
| 		cur->flags |= D_DELETED; | ||||
| 		cur->dead = 1; | ||||
| 		mbox->deleted++; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     for (cur = imap->msgs, msg_count = 0; | ||||
| 	 cur && msg_count < max_msgs; cur = cur->next, msg_count++) | ||||
|     { | ||||
| 	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) | ||||
| 	    { | ||||
| 		if ((flags & SYNC_QUIET) == 0) | ||||
| 		    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 & ~D_RECENT) | ||||
| 	    { | ||||
| 		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/%ld_%d.%d.%s%s", | ||||
| 			  mbox->path, time (0), MaildirCount++, getpid (), | ||||
| 			  Hostname, suffix); | ||||
| 
 | ||||
| 		if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0) | ||||
| 		    break; | ||||
| 		if (errno != EEXIST) | ||||
| 		{ | ||||
| 		    perror (path); | ||||
| 		    break; | ||||
| 		} | ||||
| 
 | ||||
| 		sleep (2); | ||||
| 	    } | ||||
| 
 | ||||
| 	    if (fd < 0) | ||||
| 		continue; | ||||
| 
 | ||||
| 	    if ((flags & SYNC_QUIET) == 0) | ||||
| 	    { | ||||
| 		/* give some visual feedback that something is happening */ | ||||
| 		fputs (".", stdout); | ||||
| 		fflush (stdout); | ||||
| 	    } | ||||
| 	    fetched++; | ||||
| 
 | ||||
| 	    ret = imap_fetch_message (imap, cur->uid, fd); | ||||
| 
 | ||||
| 	    if (fsync (fd)) | ||||
| 	    { | ||||
| 		perror ("fsync"); | ||||
| 		close (fd); | ||||
| 	    } | ||||
| 	    else if (close (fd)) | ||||
| 		perror ("close"); | ||||
| 	    else if (!ret) | ||||
| 	    { | ||||
| 		p = strrchr (path, '/'); | ||||
| 
 | ||||
| 		snprintf (newpath, sizeof (newpath), "%s/%s%s", mbox->path, | ||||
| 			  (cur->flags & ~D_RECENT) ? "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"); | ||||
| 		else | ||||
| 		{ | ||||
| 		    /* update the db with the UID mapping for this file */ | ||||
| 		    set_uid (mbox->db, p + 1, cur->uid); | ||||
| 		} | ||||
| 	    } | ||||
| 
 | ||||
| 	    /* always remove the temp file */ | ||||
| 	    unlink (path); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|     if ((flags & SYNC_QUIET) == 0) | ||||
| 	printf ("  %d messages\n", fetched); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue