pax_global_header00006660000000000000000000000064126230353660014520gustar00rootroot0000000000000052 comment=cb691bc9b4a80c12f58f4bf23057fce095f8e259 syncmaildir-1.2.6.2/000077500000000000000000000000001262303536600142045ustar00rootroot00000000000000syncmaildir-1.2.6.2/ChangeLog000066400000000000000000000600141262303536600157570ustar00rootroot0000000000000018/11/2015 - minor bugfix - version 1.2.6.2 * smd-common: - signal failure in creating a temp file (disk full?) * mddiff: - when acting as a sha1 slave, be resilient to empty files * smd-common: - consider an empty file as non-existing (no conflicts) 22/05/2015 - update vala code to 0.26 - version 1.2.6.1 * smd-applet: - compile with valac 0.26 - use libgee ABI 2 (gee-0.8.pc) 14/03/2014 - feature improvement and gui redesign - version 1.2.6 * mddiff, smd-client, DESIGN: - Emit MOVE command when appropriate. This optimization and its implementation was desined by Jesus Cea to make smd ZFS friendly - --no-move option for retrocompatibility with smd-uniform-names * test.d/: - new tests for the MOVE command * smd-applet: - Redesign the applet with GNOME3 in mind - Do not use Gtk.HBox (deprecated in favour of Gtk.Grid) - Switch from GConf to Gsettings - Compile with valac 0.22 * smd-common, README: - Suggest to use "User" and not "Username" for ssh configuration. Thanks Kim Christensen for spotting that. 11/06/2012 - minor fix release - version 1.2.5 * mddiff: - Hashtable from body sha1 to mail now holds a list of mails so that COPY actions are generated whenever possible. It used to be the case that on body collisions a COPYBODY action could have been generated instead. - Fix: --sha1sum works on empty files too - Better error messages for malformed db-file (including line number) * syncmaildir.lua: - During handshake handle the case in which mddiff is unable to sha1sum the db file - Code for calling external tools refactored - Optimize calls to external translator spawning only one process (translators must be line bufferd now) * smd-uniform-names, smd-client: - Fix escaping of ~ that was always performed on the local host - Handle server ERROR messages while computing renamings - Generate better smd-rename.sh script that calls cp instead of mv if the source message has to be used later on * smd-client: - Mode --dry-run prints the list of messages transferred over the network 28/05/2012 - minor feature improvement - version 1.2.4 * smd-uniform-names: - New tool to ease the migration from offlineimap. When the mailboxes have roughly the same content but emails are named differently this tool generates a shell script that renames local mails to conform to the remote mailbox * smd-applet: - XDG compliant autostart, with a button in the options window - Target GLib 2.32 and Gtk+ 3.0 for the .ui file * smd-push, smd-pull, smd-common, smd-client, smd-server: - New option -n --nodelete to not propagate deletions - Support for local synchronizations: -l option to smd-client. It is now possible to set SERVERNAME=localhost and pass -l to smd-client (via SMDCLIENTOPTS) to obtain a synchronization on the same host. (Thanks Miguel Fernandes for the idea and his time for testing/debugging my prototype implementation). - Do not use ssh if it is a local synchronization - Support for SMDCLIENTOPTS and SMDSERVEROPTS in config file - Code refactoring for external tools invocation - Put a minimal README in the hooks/ directory to tell the user what these directories are for - Fix inconsistency in escaping of the value of TRANSLATOR_LR/RL - Turn off grep buffered mode for progress reporting (Thanks Stefano Zacchiroli for enlightening me on the subject) * smd-translate: - Fix bug in removing trailing / or " or ' from the MAILBOX_REMOTE/LOCAL value - New trasnlator mode: 'move'. It just replaces MAILBOX_LOCAL with MAILBOX_REMOTE and vice versa, useful for local synchronizations * mddiff: - New option -n --no-delete to not generate DELETE actions * Makefile: - Fix templates: C/LDFLAGS are now correctly preserved * README: - Update copyright dates to include 2012 - Document smd-uniform-names 03/03/2012 - minor feature improvement - version 1.2.3 * smd-pull, smd-push: - print progress report if -v is passed * smd-client: - generate progress report if verbose mode is on - generate verbose progress report during first synchronization - log "translating:" only when meaningful (i.e. not a noop) * smd-common: - create a new fifo for progress reporters - standard progress reporter printing on stdout PROGRESS: lines - use mddiff as mkdir and mkfifo (no more calls to mkfifo and mkdir) * smd-applet: - ported to vala 0.16 * smd-loop: - fixed cleanup process (Thanks Ralf Schmitt for the patch) * smd-restricted-shell: - new utility to be used in .ssh/authorized_keys, see the command= option (Thanks Luca Capello for the idea and the documentation) * syncmaildir.lua: - When mddiff fails to sha1 a message print the file name (thanks Zhengdao Wang for the idea) 01/12/2011 - minor fix - version 1.2.2 * smd-config.5: - fixed typo and use `quotes' to delimit strings * smd-common: - fixed temp file leak - fixed stale lock file detection * mddiff: - add --sha1sum to avoid depending on the sha1sum external utility - add --mkdir-p to avoid depending on the mkdir external utility - add --mkfifo to avoid depending on the mkfifo external utility - print OK on successful symlink creation - updated copyright * syncmaildir.lua: - use mddiff to create symlinks to remove dependency over ln 10/09/2011 - minor feature improvement - version 1.2.1 * mddiff: - support for --exclude via fnmatch(3) to filter out unwanted directories * smd-server, smd-push, smd-common: - thread around --exclude * smd-check-conf: - many bug fixes * README: - document EXCLUDE - reorganized * smd-applet: - ported to vala 0.13.3 - removed embedded copy of libnotify.vapi/deps 07/09/2011 - major feature improvement - version 1.2.0 * all tools: - support for renaming mailboxes thanks to translator programs: useful to switch from offlineimap to smd - -d option now implies -v * smd-translate: - common translator utility to handle renaming typical imap servers - support dovecot imap server (removal of leading dots) * smd-check-conf: - utility to test for round trip of translators on the current list of mailboxes and perform some other sanity checks * mddiff: - accept -l to list all mailboxes rooted in the given paths - accept -s to create symlinks reading input on a fifo - minor improvement: basename/g_path_basename now called only once per mail folder - directory handler leak fixed * smd-common: - accept -s to show tags, while -v is used for more verbose output - sanitize input of MAILBOX like variables * smd-client: - reworked to fix once and for all (hopefully ;-) the problems with names containing spaces * syncmaildir.lua: - better report a network error * smd-applet: - notification.set_hint "transient" called with a boolean GVariant to avoid crashing the notification-daemon * Makefile: - minor improvements w.r.t. test running - eye candy stats target * tests.d: - support tests for smd-push - test for renaming * smd-config.5: - new manpage for the configuration file 24/07/2011 - minor fix 4 Faidon - version 1.1.6 * smd-pull, smd-push: - mention --dry-run option in manpage * mddiff,syncmaildir,smd-client,smd-server: - properly handle file names with spaces * test.d: - new test for file names with spaces * mddiff: - if we get EPERM opening a mail we retry without O_NOATIME since that option is allowed only on files owned by the euid of the process (unless you have CAP_FOWNER). These files were previously skipped, but it may be the case one has old crap owned by a different user in his mailbox, like an old broken uid. Thanks Faidon for debugging this issue 03/07/2011 - minor fix - version 1.1.5 * smd-applet: - Ported to NetworkManager 0.9 APIs (GNOME 3) - Ported to libnotify 0.7 - Ported to GTK+-3.0 - Windows spacings conforming to the GNOME guidelines 04/03/2011 - minor fix - version 1.1.4 * Makefile: - add / in front PREFIX when generating smd-config.h 04/03/2011 - minor fix to the build system release - version 1.1.3 * Makefile: - compact compilation output: CC, VALAC call `make H=` to enable verbose output again - error message for too old glib version improved - automatic generation and update of smd-config.h based on Makefile variables, both the static and the command line ones - better warnings if there is no valac but any vala file changed - no more -DVERSION compile flag, since all compiled code includes the same smd-config.h file * mddiff.c: - include smd-config.h for SMD_CONF_VERSION * smd-config.vapi: - bind smd-config.h to vala * config.vala: - removed: smd-config.h is used by smd-applet.vala thanks to smd-config.vapi 03/03/2011 - maintenance release - version 1.1.2 * smd-applet.vala: - ported to valac 0.10 - ported to GDbus (package gio-2.0) - build dependency over vala removed: config.c and smd-applet.c are now included in the tarball * mddiff.c: - compile with additional -Wcast-align flag - use %zc to print size_t, if available - compute the default for --max-mailno using the number of lines in the db-file (if available) and adding 1000 (as the max amount of new mails). Overestimating the amount of memory needed can make malloc fail if the system is under heavy loads. --max-mailno still makes sense for the first run, since there is no db-file to look at. 14/10/2010 - minor fix - version 1.1.1 * syncmaildir.lua: - tmp_for fixed: the pid component is now a unique index instead of a fixed one. The old code Was causing troubles when receiving more then 10 mails during the same second and in the same mailbox and when the pid component of the mail name was not made just of digits. The regular expression to capture the pid component has also been relaxed accepting "_" too. 01/09/2010 - minor feature release - version 1.1.0 * smd-loop: - when run, immediately execute the first pull/push scheduled. it used to sleep for its interval * smd-applet: - connect to NetworkManager to query network status, if the connection is not present, we enter pause mode. 15/06/2010 - minor fix - version 1.0.0 * smd-applet: - recognize mailto: actions so that a nice label is used istead of the raw command that comprises subject and body - ported to vala 0.9.1, use .connect instead of deprecated += * smd-client, syncmaildir.lua: - generate as suggested action for internal errors: gnome-open "mailto:...." * smd-server: - do not es.execute(touch), use io.open instead * syncmaildir.lua: - place fifo for mddiff in HOME/.smd/fifo/ if HOME is set, /tmp otherwise - added upperbound to loop generating a fresh name for the fifo * sample-hooks/persistent-ssh: - set +e so that if ssh fails smd-pull/push does not fail too and generates proper tags * Makefile: - fixed upload target, used to overwrite the tarball with the changelog - substitute SMDVERSION, MKFIFO and MKDIR when installing scripts - new test/SUITE target to run only one testsuite * README: - added notes about the first synchronization * DESIGN: - better doc for mddiff * tests.d/: - new tests for hooks logging - new test for fifo creation (syncmaildir.lua function sha_file) - allow disabling valgrind by exporting the VALGRIND variable * Makefile, smd-common, syncmaildir.lua: - remove all explicit mentions of my username on sourceforge or my home directory layout 12/04/2010 - minor fix/speedup release - version 0.9.16 aka 1.0~rc5 * smd-client: - fixed a little bug preventing copybody to work properly if the target directory was not existing * syncmaildir.lua, smd-client: - call mddiff passing a pipe, and use the very same instance every time the sha1 of a file needs to be computed. This improves performances considerably when a large number of files needs to be hashed, for example when two very similar mailboxes are synced for the first time (here, 8x speedup) - implement cp in lua to avoid spawning a new process every time a file is copied/moved. The overhead, for small files, is too high and the pure lua implementation of cp is 10 times faster on small files (but could be way slower for big ones). * mddiff: - interface change: only a list of directories or a fifo can be passed as arguments - in case the fifo is passed, mddiff reads file names separated by \n and outputs their sha1 sums * smd-pull, smd-push: - report an error if a wrong flag is supplied 23/03/2010 - minor fix release - version 0.9.15 aka 1.0~rc4 * smd-applet: - the icon used to stay set to warning even after a successful synchronization * smd-client, smd-server, syncmaildir: - never use the lua error() function, all errors should generate a meaningful tag, only internal errors raise error and thus are tagged as internal-error by the parachute that also prints a backtrace - always use log_tags_and_fail, that calls error but with the exception {text=....} that is recognized by the parachute so that no backtrace is printed - parse mddiff opcodes with a function that is robust in face of malformed arguments * tests.d/: - new test for database mismatch (indirectly testing correct exception handling) and for malformed mddiff opcodes arguments * sample-hooks: - persistent-ssh: exploiting ssh connection sharing capability. This allows to save a little amount of time every synchronization. A single secure connection can be established once, at the very beginning, and then reused for evry subsequent synchronization. - documentation for hooks in markdown format generating hooks.html 12/03/2010 - major feature relase - version 0.9.14 aka too many changes to be rc * syncmaildir.lua: - mkdir_p creates always a maildir, if the last path component is in { "new", "tmp", "cur" } (used to do that only if it was "tmp". * smd-common: - major refactoring, almost all code is inside functions - new function run_hooks * smd-pull, smd-push: - adapt to the new smd-common interface: call by hand init functions instead of having them executed by side effect of sourcing smd-common * hooks support in smd-push and smd-pull: - executables contained in the .smd/hooks/{pre,post}-{pull,push}.d/ directories are executed pre/post the pull/push process. - hooks receive as argument: - when: pre/post - what: push/pull - endpoint: the endpoint name (usually default) - status: the current status, 0 for OK, 1 for error. pre-hooks always receive 0, post hooks receive the value smd-push/pull will return after hooks terminate - thanks to Bart Trojanowsk for the hooks idea * sample-hooks/: - new directory with sample hooks, for now just a simple mail-on-failure hook * tests.d/: - major redesign of the test framework, allowing multiple test suites - new test suite for smd-pull/push - new test suite for mddiff * smd-push/pull smd-client/server mddiff: - support -d --dry-run (i.e. perform no action) * manpages for mddiff, smd-client, smd-server, smd-pull, smd-push: - added -d --dry-run doc - mention hooks * copyright: - updated years in all files: 2008-2010 - added copyright banner where missing * git archive: - use git archive for the make dist target * README: - mention --dry-run - document hooks 02/03/2010 - major fix release - version 0.9.13 * smd-server mddiff: - big fat bug solved: the .mtime file was always updated. the .mtime file used to be updated even if the synchronization failed. This could have caused some changes (like in place eader modifications, but not like new messages or renamings/flagging) to be ignored in the following synchronization. Now .mtime works exactly as .db.txt, a .new one is created by mddiff, and is up to smd-server to rename both. * mddiff.1.txt: - fixed typo 27/12/2009 - minor fix release - version 0.9.12 aka 1.0rc3 * DESIGN: - mention the ERROR action that mddiff can now emit * smd-client: - understand the ERROR action * mddiff.c: - use only EXIT_SUCCESS and EXIT_FAILURE as exit codes - ERROR macro calls exit(EXIT_FAILURE) end emits the ERROR action - new macro WARNING, used for what used to be non fatal errors - improved argument check: stat is done in advance, and we bail out if they are not omogeneous * syncmaildir.lua: - handshake function emits 'network' as probable failure cause. Used to emit 'network-error', cheating smd-applet * smd-client, smd-server, smd-common: - check for the existence of external helper programs to fail early if it is not the case * smd-client, smd-server, syncmaildir.lua: - more comments in the code and better organization - littler refactoring for mkdir_p and tmp_for - added function to check for the existence of an external program using shell type command * Makefile: - added abspath/* template to install the software hardcoding absolute paths for helper programs * README, DESIGN: - fixed indentation so that markdown displays all blockquotes as such (kudos to Zack). 13/11/2009 - minor fix release - version 0.9.11 aka 1.0rc2 * smd-loop: - save pid in lockfile so that lockfile referring to dead instances can be ignored - on receiving SIGTERM, do the cleanup and exit - write log file ~/.smd/log/loop.log * smd-push/pull: - save pid in lockfile so that lockfile referring to dead instances can be ignored - print a tagged error message in case an instance is already running * smd-appled: - ported to vala 0.7.8 - accept error messages from smd-pushpull (that is used by both smd-push and smd-pull as the tool identifier) - added pause checkbox menu item to pause smd-loop (actually kill and restart) - menu for left and right mouse button differ: - right has preferences, about and quit - left has pause and logs - unrecoverable but non-network related errors are not silently hidden, a popup shows their possible cause but the application does not enter error handling mode 3/10/2009 - minor fix release - version 0.9.10 * smd-loop: - fixed a bug regarding the lock file name, that was clashing with the one of smd-push/pull * smd-push/pull: - print the date in the log, not on stdout 27/9/2009 - minor fix release - version 0.9.9 aka 1.0rc1 * mddiff: - --help and manpage improved - compile with -O2, why not? - add strict compile time dependency over glib >= 2.19.1, that fixes a bug concerning the checksum of data with length < 2 * smd-push/pull: - when -v is passed also print the date * smd-applet: - less noise when a network error occurrs, just notify the user once and change the icon - added manpage - added view log menu item and relative window - made vala 0.7.6 clean * smd-loop: - lockfile to not run two instances concurrently * Makefile: - sourceforge paths made variable, easier to change and more readable Makefile - added support for @variable@ substitution in installation phse for SED, SHA1SUM, XDELTA, CPN, SSH, LUA and PREFIX - added template for osx: make osx/FOO will make target FOO with some variables customized for osx * README: - added some notes about installation from sources 16/8/2009 - minor fix release * mddiff, smd-client: - REPLACE command implemented - tests added * smd-applet: - fixed a minor issue with Vala 0.7.5, GLib.Pid is not an int anymore - fixed a major issue with threading, all code regarding gtk is now run by the main thread - fixed a major issue with memory management, from List to Gee.ArrayList, that have non-week references and thus prevent their data from being collected 26/7/2009 - minor bugfix release * smd-applet: - when quit was clicked, it was killing every process in its process group, not in the one of its child smd-loop, thus it was killing all other applets started by gnome-applet - configuration window is not pupped up if no config files are found, an error icon and a notification are used instead, and the software wants the configuration files to be available before it actually does something. 26/7/2009 - major feature release * smd-applet, eye-candy applet for smd-loop: - attaches to the notification area - stores its configuration using GConf - notifies the user using libnotify - runs smd-loop, interpreting its output - shows the user possible errors and allows him to perform suggested actions by clicking on buttons - complains if smd-loop or smd-push/pull is not configured and forces the user to configure it before starting - automatically run by gnome-session (/etc/xdg/autostart) - shows an item in gnome-control-center * smd-loop, cron like push/pull iterator: - use a line based configuration file ~/.smd/loop in the style of cron, calling smd-push/pull with 1 minute granularity - catches some transient errors giving them a second chance; i.e. if the suggested solution is retry, it does so but only once * error report: - smd-client and smd-server output tags describing the error that caused a sync failure: - context: which part of the software failed, like ssh, message-deletion, copy-message, ... - probable-cause: something like network, bad file permissions, ... - human-intervention: avoidable or necessary - suggested-actions: run( command ), display-mail, retry, ... - smd-push/smd-pull catch early network errors, so that some tags are output even if smd-client/smd-server is not even run * statistics: - smd-client outputs tags describing its last run, comprising the number of added/deleted messages, so that higher level tools can notify the user or collect statistics 28/4/2009 - minor fix and minor feature release * support for multiple servers/clients: - config file is now ~/.smd/config.$FOO and smd-push/pull can get an extra argument for FOO, defulat value of FOO is `default`. - backward-compatibility code added to automatically migrate the old config file name to the new one * cleanup in ~/.smd/: - fifos are now in the fifo/ subdirectory - logs are now in the log/ subdirectory * bugfix to mddiff: - COPYBODY action used to be always followed by an ADD action, that was unnecessary (but not dangerous either). * better error reporting: - syncmaildir.lua improved to better report some errors 21/4/2009 - minor fix and minor feature release * track only mailmessages (skip other stuff like dovecott indexes): - scans only files in cur/ and new/ * add COPYBODY command to mddiff: - mails that are moved around and then flagged by MUA are not deteceted anymore as new messages, only the new header is transmitted - smd-client honors the command * smd-client better reports network errors: - if no data at all is readable from stdin, it means that the transport (ssh) is not working and this is reported to the user as such 19/4/2009 - major bugfix release * major bugfix: - the db file must be in sync too to grant all changes are detected - the mtime has not to be the same on client and server (thus has to be removed from the db file) - adopt xdelta to calculate a patch for the db file (should be better than diff and can scale to binary, compressed, db file) * new client/server protocol: - allows the server to ABORT - handshake checking protocol version and dbfile hash - at the end transmit and apply the xdelta patch * db file format changed: - no more mtime column - added db.txt.mtime auxiliary file to stores a global timestamp for the whole db * minor bugfixes in the code (mostly better error reporting) * more documentation 12/4/2009 - first public release * end-user tools: smd-pull, smd-push * stupid endpoint: smd-server * endpoint implementing politics: smd-client * low-level tool: mddiff syncmaildir-1.2.6.2/DESIGN000066400000000000000000000302451262303536600151040ustar00rootroot00000000000000DESIGN ====== What follows is my best effort in giving the big-ascii-picture of what happens when `smd-pull` is run. `smd-push` simply swaps `smd-server` and `smd-client`. Note that the sync direction is from `smd-server` to `smd-client`, so running them on the opposite hosts inverts the sync direction. Your mail server Your laptop ---------------- ----------- --- sync direction ---> smd-pull | | smd-server ------- ssh ----- smd-client | | | | mddiff (mddiff) `smd-client` uses `mddiff` only compute sha1 sums, not to compute a diff as `smd-server` does. Both endpoints hold a file (the `db-file` described below) that describes the status of the mailbox on which they previously agreed. The server will compute the difference between the current mailbox status and the previous one on which the client agreed. This diff is sent to the client, that tries to apply it, possibly requesting some data to the server. If the client succeeds, both the server and the client now agree on the new mailbox status. END USER tools ============== smd-pull and smd-push --------------------- The idea is quite simple. If `===` is a double pipe (a pair of pipes, one for `stdin` and one for `stdout`), `smd-pull` simply performs the following smd-client $CLIENTNAME $MAILBOX === tee log === \ ssh $SERVERNAME smd-server $CLIENTNAME $MAILBOX The `tee` command is used only for logging, and if $DEBUG is `false` it is replaced by `cat`. Viceversa `smd-push` performs what follows smd-server $CLIENTNAME $MAILBOX === tee log === \ ssh $SERVERNAME smd-client $CLIENTNAME $MAILBOX They are both implemented in `bash`, since their main activity is to redirect standard file descriptors and call other tools, check their exit status and eventually notify the user with an extract of their logs. smd-loop -------- The idea is to mimic cron, but retry a failed sync attempt if the given error is transient. `smd-client` and `smd-server` output TAGS that specify if the occurred error needs human intervention or not, and also suggest some actions, like retry. `smd-loop` understands these tags, and gives a second chance to a command that fails with an error that does not require human intervention and for which the suggested action is retry. It is implemented in `bash`, since it is mostly a while true loop. Arrays (non POSIX shell compliant) are used to record failures, and give only a second chance to every `smd-push` or `smd-pull` command. smd-applet ---------- To write an hopefully eye-candy applet for GNOME, the language Vala was an intriguing choice, since it is based on smart and sound ideas (that is to avoid the C++ non-standardized calling conventions) to provide a modern object oriented programming language built around gobject and glib. Bindings for GTK+, GConf, libnotify, etc... are available, and require no compiled glue code, just bare text `.vapi` files. If you are used to languages where writing bindings is not a trivial task, you'd better look at Vala, where bindings are simple by design. SERVER/CLIENT interaction ========================= A server software (`smd-server`) and a client software (`smd-client`) are respectively used to transmit the diff generated by `mddiff` and eventually mails header or body, and to apply a diff eventually requesting necessary data to the other endpoint. Since they mostly implement policies, like deciding if a diff can be applied or not, are implemented in an high level scripting language called [Lua](http://www.lua.org). The language choice is almost arbitrary, there are no strong reasons for adopting Lua instead of python or others, but its installation is pretty small and it executes quite fast. Moreover, its syntax is particularly simple, making it understandable to non Lua experts too. Finally, I find it elegant. They send and receive data on their standard input and output channels, delegating to external tools the transmission of data across a network, and optimizations like compressing the data, or encrypting it. [OpenSSH](http://www.openssh.com/) can do both, and is adopted by `smd-pull` and `smd-push` to connect `smd-client` to `smd-server`. A simple protocol defines how `smd-client` requests data to `smd-server` and how `smd-client` notifies `smd-server` that all changes have been applied correctly. The protocol ------------ The protocol is line oriented for commands, chunk oriented for data transmission. 1. Both client and server send the following two messages, and check that they are equal to the ones sent by the other endpoint protocol NUMBER dbfile SHA1 This part of the protocol is called handshake 2. The server sends the output of `mddiff` (that is line oriented) and then the following message to conclude the first phase of the protocol, now the client is expected to reply END 3. The client, from now on, can at any time send the following (alternative) messages ABORT COMMIT The former informs the server that the client was unable to apply the diff generated by `mddiff`, while the latter informs the server that all changes were applied successfully. 4. In response to a `COMMIT` message, se server will transmit an `xdelta` patch the client has to apply to its db-file. 5. The client replies with `DONE` to complete the synchronization 6. After point 2. and before point 3. the client can send the following commands to the server, that can reply transmitting data or with `ABORT`. NAME is not URL encoded. GET NAME GETHEADER NAME GETBODY NAME ### Transmission The server can transmit data or refuse. In the latter case it just sends `ABORT`. In the former case it sends chunk NUMBER ...DATA... First it declares with `chunk` the number of bytes to be sent, then its sends the data. MAILDIR DIFF ============ Maildir diff (`mddiff`) computes the delta from an old status of a maildir (previously recorded in the db-file) and the current status, generating a set of commands (a diff) that a third party software can apply to synchronize a (remote) copy of the maildir. How it works ------------ This software uses sha1 to compute snapshots of a maildir, and computes a set of actions a client should perform to sync with the mailbox status. This software alone is unable to synchronize two maildirs. It has to be supported but an higher level tool implementing the application of actions and data transfer over the network if the twin maildir is remote. To cache the expensive sha1 calculation, a cache file is used. Every run the program generates a new status file (appending .new) that must substitute the old one if generated actions are committed to the other maildir. Cache files are specific to the twin maildir, if you have more than one, you must use a different cache file for each of them. The db-file (say db.txt) is paired with a timestamp (db.txt.mtime) that is used to store the timestamp of the last run and files whose mtime does not exceed this timestamp will not be (re)processed next time mddiff is run. The .mtime companion file is updated only server side, since the mtime concept is local to the host running mddiff. The db-file format ------------------ The db-file is composed by two files, a real database file (extension .txt) and a timestamp (extension .txt.mtime). The latter contains just a number (date +%s). The former is line oriented, every line has 3, space separated, fields: - the sha1 sum of the header - the sha1 sum of the body - the name of the file, not URL encoded The commands ------------ From now on, name refers to a file name, hsha to the sha1 sum of its header and bsha to the sha1 sum of its body. - `ADD name hsha bsha` is generated whenever a new mail message is found, and there is no mail message with a different name but the same body. - `COPY name hsha bsha TO newname` is generated if a new message is found, and the mailbox contains a copy of it. - `MOVE name hsha bsha TO newname` is generated if a new message is found, and the mailbox does not contain a copy of it but it used do. - `COPYBODY name bsha TO newname newhsha` is generated when a new file is created, and that file has the same body of an already existent file. In case mail has been moved, this message is followed by a `DELETE` command. This happens when a new message is moved to another directory and marked in some way changing its header (for example when a new message is moved to the trash bin) - `DELETE name hsha bsha` is emitted when a message is no longer present. - `REPLACEHEADER name hsha bsha WITH newhsha` is emitted whenever a message that was already present has a different header but the same body. - `REPLACE name hsha bsha WITH newhsha newbsha` is emitted whenever the body (and eventually the header) of mailmassage change. This never happens in practice, since MUAs should do a copy of the edited message, not replace it. - `ERROR message` is emitted whenever an error is encountered; message is intended to be human readable. Messages should be processed in order, with the exception of `ADD` that can be safely postponed. In particular `DELETE` messages are always sent last, and `COPY` or `COPYBODY` messages preceeding them may refer to the same file `name`. Performing deletions in advance is still sound (since the client can always ask the servevr for the message) but clearly suboptimal, since a local copy does not involve any network traffic. File names are URL encoded escaping only `' '` (`%20`) and `'%'` (`%25`). `mddiff` as an hashing server ----------------------------- `mddiff` is also used by the client to compute the sha1 sums of header and body of local mails, for example to check that the source of a copy command holds the intended content. Since this operation may be really frequent, `mddiff` can operate in server mode. If the argument is a single file name and that file is a fifo, then `mddiff` reads file names not URL encoded, separated by `\n` from that fifo and outputs the sha1 sums of their header and body. `mddiff` as an `mkdir -p; ln` server ------------------------------------ `mddiff` is also used by the client to create the indirection layer needed to ranme mailbox folders. If the argument is a single file name and that file is a fifo and the `-s` flag is passed, then `mddiff` reads directories names not URL encoded, separated by `\n`, 2 at a time, from that fifo. The first one is the source path, the latter the target. Then it behaves like `mkdir -p $(dirname $target); ln -s $source $target`. For example if source is `~/Mail/foo/cur` and the target is `Maildir/.foo/cur` then `mddiff` will create the direcotries `Maildir` and `Maildir/.foo` and place in the latter a link named `cur` to `~/Mail/foo/cur`. Easy to parse output messages ============================= `smd-pull` and `smd-push` prefix all error messages with `ERROR:`, but what follows is meant to be read by a human being. To make other tools able to parse and react to error messages, a more formal output is given. A single line, prefixed with `TAGS:` is output if requested (`-v` option). It can be followed by `error::` or `stats::`, that denote an error message or a statistical one respectively. Then a list of improperly called tags is output. Their meaning should be easy to guess. ::= "error::" | "stats::" | "stats::" ::= "context(" ")" "probable-cause(" ")" "human-intervention(" ")" ::= | "suggested-actions(" ")" ::= `[^)]+` ::= "necessary" | "avoidable" ::= | ::= "run(" ")" | "display-mail(" ")" | "display-permissions(" ")" ::= "new-mails(" ")" "del-mails(" ")" "bytes-received(" ")" "xdelta-received(" ")" "xdelta-received(" ")" ::= "mail-transferred(" ")" ::= | " , " ::= `[0-9]+` ::= ` *,? *` syncmaildir-1.2.6.2/KNOWN_BUGS000066400000000000000000000004541262303536600156460ustar00rootroot00000000000000version-1.2.6 - & in EXCLUDE_* breaks shell scripts. smd-common builds a string to be called and the & breaks it. Extra quotes and eval could fix the problem, but the clean solution is to just rewrite the schellscripts in a decent language. workaround: use %26 instead of & in the conf file. syncmaildir-1.2.6.2/LICENSE000066400000000000000000001043741262303536600152220ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . syncmaildir-1.2.6.2/Makefile000066400000000000000000000231471262303536600156530ustar00rootroot00000000000000# ---------------------------------------------------------------------------- # These variables should not be changed by the regular user, only developers # should care about them PROJECTNAME=syncmaildir VERSION=1.2.6.2 COPYRIGHT=© 2008-2013 Enrico Tassi BINARIES=mddiff smd-applet MANPAGES1=mddiff.1 smd-server.1 smd-client.1 \ smd-pull.1 smd-push.1 smd-loop.1 smd-applet.1 smd-translate.1 \ smd-check-conf.1 smd-restricted-shell.1 smd-uniform-names.1 GSCHEMAS=xdg/glib-2.0/schemas/org.syncmaildir.applet.gschema.xml GSCHEMAS_COMPILED=xdg/glib-2.0/schemas/gschemas.compiled MANPAGES5=smd-config.5 HTML=index.html design.html hooks.html DESTDIR= SF_FRS=/home/frs/project/s/sy/syncmaildir/syncmaildir SF_LOGIN=$(SF_USER),syncmaildir SF_WEB=htdocs TEST_SIZE=100 TEST_MAILBOX=misc/Mail.TEST.tgz TEST_SUITES=mddiff client-server pull-push migration BENCH_SIZE=25000 BENCH_MAILBOX=misc/Mail.BENCH.tgz BENCH_SUITES=benchmarks PKG_GTK=gtk+-3.0 SMD_APPLET_C=smd-applet.c PKGS_VALA=glib-2.0 $(PKG_GTK) libnotify gconf-2.0 gee-0.8 gio-2.0 TARGET_GLIB=2.32 PKGCONFIG_CHECK_GLIB_VERSION=--atleast-version=$(TARGET_GLIB) glib-2.0 PKGCONFIG_GLIB_VERSION=--modversion glib-2.0 VALAC=valac-0.30 H=@ # ---------------------------------------------------------------------------- # These variables affect the programs behaviour and their installation; # they are meant to be overridden if necessary. See the end of this # file for some templates setting them. PREFIX=usr/local SED=sed XDELTA=xdelta SSH=ssh LUAV=5.1 LUA=lua$(LUAV) CFLAGS=-O2 -Wall -Wextra -Wcast-align -g PKG_FLAGS= # ---------------------------------------------------------------------------- # Rules follow... all: check-build update-smd-config $(BINARIES) $(GSCHEMAS_COMPILED) update-smd-config: $H echo "#define SMD_CONF_PREFIX \"/$(PREFIX)\"" > smd-config.h.new $H echo "#define SMD_CONF_VERSION \"$(VERSION)\"" >> smd-config.h.new $H echo "#define SMD_CONF_COPYRIGHT \"$(COPYRIGHT)\"" \ >> smd-config.h.new $H if diff -q smd-config.h smd-config.h.new > /dev/null 2>&1; then \ rm smd-config.h.new; \ else \ echo CONFIGURE smd-config.h; \ mv smd-config.h.new smd-config.h; \ fi smd-applet.c: smd-applet.vala smd-config.vapi $H if which $(VALAC) >/dev/null; then \ echo "VALAC $^"; \ $(VALAC) -C $^ --thread --vapidir=./ \ --target-glib=$(TARGET_GLIB) \ --pkg posix $(patsubst %,--pkg %,$(PKGS_VALA)); \ elif [ -e smd-applet.c ]; then \ echo "** No $(VALAC), reusing precompiled .c files"; \ echo "** Changes to the following files will not be"; \ echo "** taken into account: $^"; \ else \ echo "** No $(VALAC) and no precompiled .c files"; \ echo "** To compile smd-applet you need a vala compiler"; \ echo "** or the precompiled $@ file"; \ false; \ fi $H touch $@ mddiff: mddiff.c smd-config.h @echo CC $< $H $(CC) $(CFLAGS) $< -o $@ \ `pkg-config $(PKG_FLAGS) --cflags --libs glib-2.0` $(LDFLAGS) smd-applet: $(SMD_APPLET_C) smd-config.h @echo CC $< $H $(CC) $(CFLAGS) -w $< -o $@ \ `pkg-config $(PKG_FLAGS) --cflags --libs $(PKGS_VALA)` \ $(LDFLAGS) $(GSCHEMAS_COMPILED): $(GSCHEMAS) @echo GLIB-COMPILE-SCHEMAS $(dir $(GSCHEMAS)) $H glib-compile-schemas $(dir $(GSCHEMAS)) $H $(foreach p,$(subst :, ,$(XDG_DATA_DIRS)), \ test -e $(p)/glib-2.0/schemas/$(patsubst xdg/%, \ %,$(notdir $(GSCHEMAS))) ||) \ echo WARN: export XDG_DATA_DIRS=\$$XDG_DATA_DIRS:xdg/ check-build: check-w-gcc check-w-$(VALAC) check-w-glib-compile-schemas $H pkg-config $(PKGCONFIG_CHECK_GLIB_VERSION) || \ (echo glib version too old: \ `pkg-config $(PKGCONFIG_GLIB_VERSION)`; \ echo required version: $(TARGET_GLIB); \ false) check-run: check-w-$(LUA) check-w-bash check-w-%: $H which $* > /dev/null || echo $* not found test/%: text/all check-run $(TEST_MAILBOX) $H tests.d/test.sh $(TEST_MAILBOX) $* test: text/all check-run $(TEST_MAILBOX) $H SUITES="$(TEST_SUITES)" tests.d/test.sh $(TEST_MAILBOX) bench: text/all check-run $(BENCH_MAILBOX) $H SUITES="$(BENCH_SUITES)" tests.d/test.sh \ $(BENCH_MAILBOX) $(addprefix $(shell pwd)/,$T) misc/Mail.%.tgz: $(MAKE) check-w-polygen rm -rf Mail mkdir -p Mail/cur namea=Mail/cur/`date +%s`.$$$$;\ nameb=`hostname`;\ date=`date`;\ for i in `seq $$core $($*_SIZE) $$CORES`; do \ name=$${namea}_$$i.$$nameb;\ echo "Subject: foo-subject $$i" >> $$name; \ echo "Message-Id: $$i" >> $$name; \ echo "Date: $$date" >> $$name; \ echo "X-Foo: foo.foo.com" >> $$name; \ echo >> $$name;\ polygen -X 10 /usr/share/polygen/eng/manager.grm >> $$name;\ done tar -czf $@ Mail rm -rf Mail %.1:%.1.txt check-w-txt2man txt2man -t $* -v "Sync Mail Dir (smd) documentation" -s 1 $< > $@ %.5:%.5.txt check-w-txt2man txt2man -t $* -v "Sync Mail Dir (smd) documentation" -s 5 $< > $@ define install-replacing cat $(1) |\ $(SED) 's?@PREFIX@?/$(PREFIX)?' |\ $(SED) 's?@SED@?$(SED)?' |\ $(SED) 's?@XDELTA@?$(XDELTA)?' |\ $(SED) 's?@SSH@?$(SSH)?' |\ $(SED) 's?@SMDVERSION@?$(VERSION)?' |\ $(SED) 's?#! /usr/bin/env lua.*?#! /usr/bin/env $(LUA)?' |\ cat > $(DESTDIR)/$(PREFIX)/$(2)/$(1) if [ $(2) = "bin" ]; then chmod a+rx $(DESTDIR)/$(PREFIX)/$(2)/$(1); fi endef define install cp $(1) $(DESTDIR)/$(PREFIX)/$(2)/$(notdir $(1)) if [ $(2) = "bin" ]; then chmod a+rx $(DESTDIR)/$(PREFIX)/$(2)/$(1); fi endef define mkdir-p mkdir -p $(DESTDIR)/$(PREFIX)/$(1) endef install: install-bin install-misc install-bin: $(BINARIES) $(call mkdir-p,bin) $(call mkdir-p,share/$(PROJECTNAME)) $(call mkdir-p,share/$(PROJECTNAME)-applet) $(call mkdir-p,share/lua/$(LUAV)) cp $(BINARIES) $(DESTDIR)/$(PREFIX)/bin $(call install-replacing,smd-server,bin) $(call install-replacing,smd-client,bin) $(call install-replacing,smd-pull,bin) $(call install-replacing,smd-push,bin) $(call install-replacing,smd-translate,bin) $(call install-replacing,smd-check-conf,bin) $(call install-replacing,smd-uniform-names,bin) $(call install-replacing,smd-restricted-shell,bin) $(call install-replacing,smd-loop,bin) $(call install-replacing,smd-common,share/$(PROJECTNAME)) $(call install-replacing,syncmaildir.lua,share/lua/$(LUAV)) install-misc: $(MANPAGES1) $(MANPAGES5) $(call mkdir-p,$(DESTDIR)/etc/xdg/autostart) $(foreach d,\ $(filter-out xdg,$(shell find xdg -type d)),\ $(call mkdir-p,share/$(patsubst xdg/%,%,$(d)));) $(foreach f,\ $(filter-out $(GSCHEMAS_COMPILED),$(shell find xdg -type f)),\ $(call install,$(f),share/$(patsubst xdg/%,%,$(dir $(f))));) $(call install,smd-applet.ui,share/$(PROJECTNAME)-applet) $(call mkdir-p,share/man/man1) $(call mkdir-p,share/man/man5) cp $(MANPAGES1) $(DESTDIR)/$(PREFIX)/share/man/man1 cp $(MANPAGES5) $(DESTDIR)/$(PREFIX)/share/man/man5 $(call mkdir-p,share/doc/syncmaildir) cp -r sample-hooks/ $(DESTDIR)/$(PREFIX)/share/doc/syncmaildir $(call install,README,share/doc/syncmaildir) clean: $H rm -f $(BINARIES) $(MANPAGES1) $(MANPAGES5) $H rm -rf tests.d/run $H rm -f $(PROJECTNAME)-$(VERSION).tar.gz $H rm -f $(HTML) $(MANPAGES1:=.html) $(MANPAGES5:=.html) $H rm -f misc/smd-applet-1.0.0.c dist $(PROJECTNAME)-$(VERSION).tar.gz: smd-applet.c rm -f $(PROJECTNAME)-$(VERSION).tar.gz rm -f $(PROJECTNAME)-$(VERSION).tar git archive --format=tar \ --prefix=$(PROJECTNAME)-$(VERSION)/ HEAD \ > $(PROJECTNAME)-$(VERSION).tar tar --transform=s?^?$(PROJECTNAME)-$(VERSION)/? \ -r smd-applet.c \ --owner root --group root \ -f $(PROJECTNAME)-$(VERSION).tar gzip -9 -n -f $(PROJECTNAME)-$(VERSION).tar $(HTML): check-w-markdown cat misc/head.html > index.html markdown README >> index.html cat misc/tail.html >> index.html cat misc/head.html > design.html markdown DESIGN >> design.html cat misc/tail.html >> design.html cat misc/head.html > hooks.html grep -h '^##' sample-hooks/* | sed 's/^## \?//' | markdown >> hooks.html cat misc/tail.html >> hooks.html %.html:%.txt cat misc/head.html > $@ echo '
' >> $@
	cat $< | sed -e '/^AUTHOR/,$$d' >> $@
	echo '
' >> $@ cat misc/tail.html >> $@ upload-website: $(HTML) $(MANPAGES1:%=%.html) $(MANPAGES5:%=%.html) scp $? misc/style.css \ $(SF_LOGIN)@web.sourceforge.net:$(SF_WEB) upload-tarball-and-changelog: $(PROJECTNAME)-$(VERSION).tar.gz scp $(PROJECTNAME)-$(VERSION).tar.gz \ $(SF_LOGIN)@frs.sourceforge.net:$(SF_FRS)/$< scp ChangeLog $(SF_LOGIN)@frs.sourceforge.net:$(SF_FRS)/README stats: T=`git tag | sort -V | head -n 1`; \ for V in `git tag | sort -V | tail -n +2`; do \ echo $$V; \ git diff $$T $$V | diffstat -s; T=$$V; \ done;\ git diff --no-prefix $$V HEAD | diffstat -C # ---------------------------------------------------------------------------- # These templates collect standard values for known platforms, like osx. # To use a template run make TEMPLATE/TARGET, for example: # make osx/all && make osx/install # You may also combine templates. For example, to build only text mode # utilities on osx type: # make osx/text/all && make osx/text/install text/%: $H $(MAKE) $* \ BINARIES="$(subst smd-applet,,$(BINARIES))" \ MANPAGES1="$(subst smd-applet.1,,$(MANPAGES1))" \ PREFIX="$(PREFIX)" VALAC=ls H=$H \ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" static/%: $H $(MAKE) $* \ CFLAGS="$(CFLAGS) -static " \ PKG_FLAGS="$(PKG_FLAGS) --static " \ PREFIX="$(PREFIX)" H=$H LDFLAGS="$(LDFLAGS)" gnome2/%: $H gunzip -c misc/smd-applet-1.0.0.c.gz > misc/smd-applet-1.0.0.c $H $(MAKE) $* \ SMD_APPLET_C=misc/smd-applet-1.0.0.c PKG_GTK=gtk+-2.0 \ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" TARGET_GLIB="2.19.1" osx/%: $H $(MAKE) $* SED=sed PREFIX="$(PREFIX)" H=$H \ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" abspath/%: $H $(MAKE) $* SED=/bin/sed \ XDELTA=/usr/bin/xdelta SSH=/usr/bin/ssh \ PREFIX="$(PREFIX)" H=$H \ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" .PHONY : update-smd-config # eof syncmaildir-1.2.6.2/README000066400000000000000000000467361262303536600151040ustar00rootroot00000000000000SYNC MAIL DIR ============= Sync Mail Dir (`smd`) is a set of tools to synchronize a pair of mailboxes in Maildir format. It is Free Software, released under the terms of GPLv3, Copyright © 2008-2012 Enrico Tassi. It differs from other solutions in terms of performances and lower installation requirements. The widespread solution IMAP-server plus [OfflineIMAP](http://software.complete.org/software/projects/show/offlineimap) requires an IMAP server to be installed. Alternatively [Maildirsync](http://hacks.dlux.hu/maildirsync/) requires no IMAP server, just ssh, but it used to load my laptop CPU too much and it seems its development stopped in 2004. Other general purpose tools like rsync or unison may work too, but not benefit from the fact that they are synchronizing mail messages. Sync Mail Dir is similar to Maildirsync in its design and requirements, but is more efficient, having its mechanisms written in C (around 900 lines), while policies are written in scripting languages like Lua and shell script (other 1300 lines). Overview -------- Sync Mail Dir uses `ssh` to transmit and receive data, and to run commands on the remote host (but in principle it could use any bi-directional channel, like `nc` for example). Sync Mail Dir needs to be installed on both hosts: we call server the host we push to and pull from, we call client the host we push from and pull to. In the most common scenario, the server is our mail server, while the client is our laptop. The directory `~/.smd/` contains the configuration file(s), some `fifo` needed to short-circuit the softwares running on the client and on the server, and a cache file (called db-file from now on) that represents the status of the mailbox last time we successfully pushed. The configuration file is needed only on the client host (the one that will run `smd-pull` and `smd-push`). Sync Mail Dir is a layered set of tools, where low level tools are implemented in low level languages (to achieve decent performances) and high level tools are written in scripting languages (to achieve better flexibility). - [`mddiff`](mddiff.1.html) is a small and hopefully efficient C program that given a db-file (a snapshot of a previous mailbox status) generates a set of actions (a diff) a client should perform to synchronize its local mailbox copy. It is a low level tool, not meant to be used directly by the user. - [`smd-server`](smd-server.1.html) a simple script that calls `mddiff` to compute the diff, sends it to the client and then listens for client requests like getting a mail body or header. Even if this tool is simple to run, redirecting its input and output to `smd-client` over a network may not be straightforward, thus users should not call it directly. - [`smd-client`](smd-client.1.html) a quite complex script applying a diff locally, eventually requesting data to the server. In case the diff cannot be applied cleanly, it outputs an error message that higher level tools should display to the user. As `smd-server` it is a quite high level tool, but should not be called directly by the average user. - [`smd-pull`](smd-pull.1.html) thanks to ssh, it executes `smd-server` on the server host and `smd-client` on the client host. This allows to pull remote changes and apply them locally. The remote mailbox is left untouched. This tool is meant to be called by the user. - [`smd-push`](smd-push.1.html) thanks to ssh, it executes `smd-client` on the server host and `smd-serer` on the client host. This allows to push local changes and to apply them on the remote host. The local mailbox is left untouched. This tool is meant to be called by the user. - [`smd-translate`](smd-translate.1.html) handles common folder renaming scenarios. The tool is meant to be used as a translator program in the configuration file of `smd-pull` and `smd-push`. - [`smd-check-conf`](smd-check-conf.1.html) performs a quick check that a setup, especially when it involves some folders renaming, actually works as expected. This tool is meant to be manually called by the user to check a given configuration file. - [`smd-restricted-shell`](smd-restricted-shell.1.html) only meant to be used in conjunction with an SSH key and specifically in the remote OpenSSH's authorized_keys file to restrict the commands allowed. - [`smd-uniform-names`](smd-uniform-names.1.html) meant to be used before the first synchronization, when the content of the local and remote mailboxes is similar, but mails are named differently. This is often the case when migrating from offlineimap, that encodes some metadata in the local file names. - [`smd-loop`](smd-loop.1.html) runs runs smd-push and smd-pull at regular intervals as defined by the user in a configuration file, in the style of a crontab, but catches recoverable errors (like a, non recurrent, network problem), bailing out only in cases where human intervention is needed. This is the utility you may want to run if you are using a textual environment or a graphical one not based on GNOME. - [`smd-applet`](smd-applet.1.html) is an applet for the GNOME notification area, that runs `smd-loop`, eventually notifying the user for critical events and allowing him to solve them by clicking on buttons instead of running commands from the shell. HOW-TO ------ Four end-user tools are provided. You need to configure them bottom-up, starting from the simpler ones (`smd-pull` and `smd-push`), that already allow to synchronize two mailboxes. Anyway it is recommended to use higher level tools like `smd-loop` or `smd-applet`. ### smd-push and smd-pull - `smd-pull [name]` to obtain the changes made on the remote mailbox applied locally - `smd-push [name]` vice versa Both tools read a configuration file in `~/.smd/config.name`, that is a simple shell script sourced by both when called with argument `name`. If no argument is given, they source `~/.smd/config.default`. This file defines the following variables (see the [`smd-config`](smd-config.5.html) manpage for a complete documentation): - `SERVERNAME` is a valid alias for ssh. It should be defined in your `~/.ssh/config`, with compression enabled. For example: Host smd-server-foo BatchMode yes Compression yes Hostname your.real.server.name User you - `CLIENTNAME` a name for your client: the host name concatenated with the server name should be fine. Note that you can pull and push from different hosts, and each of them needs a unique CLIENTNAME in its configuration file. - `MAILBOX` a list of roots to be synchronized. There is no support for name mapping, thus they should be named the same on the remote host. Maildirs will be searched starting from these roots, traversing subdirectories recursively. - `DEBUG` can be set to true to log the traffic between client and server The first time you run `smd-pull` or `smd-push` a template file will be generated for you. The first synchronization can take a while, since all mail messages have to be inspected and their hash recorded in the db-file. While it is not strictly needed, you may want to copy a huge mailbox (hundreds of megabytes) to the other endpoint by hand (for example zipping it with a slow but space efficient compressor like `bzip2` and `lzma`) to save some bandwidth. `smd` is not optimized for such a (uncommon) situation: it uses regular ssh stream compression to transfer mails, that can be way less space efficient than running a compression utility over the whole mailbox. Moreover, you should not edit your mailboxes during the first synchronization, since edits may force the software to bail out without saving the db-file, and thus making the following run as slow as the first one. To check your setup you can run the `smd-check-conf` utility. The tools `smd-push` and `smd-push` can be run with the `-d` or `--dry-run` flag. In this way they will not modify in any way any maildir. Nevertheless it can be very handy to understand which changes smd would perform if not told otherwise. ### smd-loop `smd-loop` runs `smd-push` and `smd-pull` at regular intervals as defined by the user in the `~/.smd/loop` configuration file. On errors that are reported to be transient, its retries a second time before failing. The first time you run `smd-loop` a sample config file is created for you. The configuration file is line oriented. Each line is composed of three space separated fields: - `pull-frequency` - `push-frequency` - `endpoint-name` Frequencies are expressed in minutes, while endpoint name is a valid name for `smd-pull` and `smd-push`. Lines beginning with # are considered as comments. The following example calls the command `smd-pull default` every 3 minutes, and `smd-push default` every 10. # pull-frequency push-frequency endpoint-name 3 10 default ### smd-applet smd-applet just runs `smd-loop`, notifying the user if errors occur. It can be run with `--configure` to pop-up its configuration window, that allows to tune its notification behaviour and to edit the configuration files for `smd-loop` and `smd-push/pull`. Notes on performances --------------------- On my core duo 2 laptop equipped with a 5400rpm hard drive and with an hot cache, copying a 150MB mailbox with `tar cf - Mail | ssh -C localhost tar xf -` takes 17 seconds. Doing the same with `smd-pull` takes 19 seconds. This is clearly an uncommon workload, since you rarely get 150MB of new mails, but it shows the extra work the tool is doing (like computing `sha1` sums for every mail, or the client-server protocol overhead) makes `smd` not so inefficient. Once the mailbox has been copied, running `smd-pull` again to check for updates is almost instantaneous. As of September 2011, my mailbox is 1.3G and is on average pulled/pushed in less than 2s using a regular ADSL connection. Advanced Usage ============== ### restricted remote shell Version `1.2.3` comes with `smd-restricted-shell` to improve security, especially when using password-less SSH keys. This tool takes advantage of the OpenSSH command= option, which permits to restrict the command that is allowed to be executed on the remote host when the login is performed using a particular SSH key. Once you have identified in the ~/.ssh/authorized_keys on the remote host the SSH key you use together with Sync Mail Dir, prepend the line as in the following example: command="/usr/bin/smd-restricted-shell" ssh-rsa AAAABBBBCCCC.... ### multiple servers From verion `0.9.4` multiple configuration files are supported. This means you can push/pull from/to different remote mailboxes (one at a time). This turned out to be useful when migrating a mailbox: smd-pull oldserver smd-push newserver Note that you can run this for a while, not just one time. This can make the transition from a mail-address to another smooth, since simply forwarding mail from the old to the new one makes you believe you changed the subscription to all your mailing lists, that is obviously not always the case since nobody remembers all the mailing lists he is subscribed to. ### hooks From version 0.9.14, `smd-push` and `smd-pull` can run user defined hooks before and after doing their job. Hooks are regular programs (usually shell scripts) placed in the following directories: - `~/.smd/hooks/pre-push.d/` - `~/.smd/hooks/pre-pull.d/` - `~/.smd/hooks/post-push.d/` - `~/.smd/hooks/post-pull.d/` Hooks receive four arguments in the following order: 1. when: `pre` or `post` 2. what: `push` or `pull` 3. endpoint: the endpoint name, usually `default` 4. status: the current status, `0` for OK, `1` for error. pre-hooks always receive `0`, post hooks receive the value `smd-push/pull` will return after the hooks terminate Hooks should not fail, if they do so then `smd-push/pull` will fail too. Sample hooks are available in the source tarball under `sample-hooks/`. Some documentation about [available hooks](hooks.html) is also present. ### folder renaming In case your local and remote mailbox names or sub-folders structure differ, smd version 1.2.0 offers a translation functionality. The configuration file must be changed, replacing `MAILBOX` with `MAILBOX_LOCAL` and `MAILBOX_REMOTE`. Moreover two translator programs must be defined: - `TRANSLATOR_RL` to translate remote names to local ones - `TRANSLATOR_LR` to translate local names to remote ones To avoid common mistakes in writing translators, some recurrent renaming scenarios are handled by the `smd-translate` utility. Please refer to [`smd-translate`](smd-translate.1.html) manpage. What following describes how to write a translator by hand, that may be necessary is your translation schema is no supported by `smd-translate`. A translator is a program that receives in standard input one or more folder names, and must print on standard output a corresponding folder name on success, or prints the string `ERROR` followed by a new line and an optional following error message otherwise end exists returning 1. Note that the folder names will be complete of the `MAILBOX_LOCAL` or `MAILBOX_REMOTE` part and will always terminate with `cur`, `new` or `tmp`. For example, consider the following configuration file: MAILBOX_LOCAL=Mail MAILBOX_REMOTE=Maildir TRANSLATOR_LR=loc_to_remote.sh TRANSLATOR_RL=remote_to_loc.sh When `smd-pull` is called, `remote_to_loc.sh` is called to translate names like `Maildir/cur` or `Maildir/.sub.folder/new` to local names like `Mail/cur` or `Mail/sub.folder/new`. An example of `remote_to_loc.sh` could be: #!/bin/sh sed --unbuffered -e 's/^Maildir\(.*\)$/Mail\1/' -e 's?/\.?/?' Note the `--unbuffered`: translators should not work in buffered mode. I.e. when given a line in input (terminated by `\n`) they must output a line without expecting any additional input. Translating the way back is trickier, since the leading `.` must be added only to sub-folders: #!/bin/sh t() { if [ "$1" = Mail/cur -o "$1" = Mail/tmp -o "$1" = Mail/new ]; then echo $1 | sed --unbuffered 's?^Mail/\(.*\)?Maildir/\1?' else echo $1 | sed --unbuffered 's?^Mail/\(.*\)?Maildir/.\1?' fi } while read M; do t "$M"; done Last, translators are executed as external programs, thus they must be present in your path (for example in `~/bin/`) and have the executable bit set (`chmod +x`). To check your setup you can run the `smd-check-conf` utility. The test consists in listing local and remote mailboxes, calling the translators, displaying the result to the user and checking for round trip (i.e. that the translators programs behave as inverse functions on the current input). You can also test your setup using the dry-run mode with `smd-push -d` and `smd-pull -d` and examine their output. This anyway should be tried before the first pull/push, and thus may take a long time depending on the size of your mailboxes. To avoid common mistakes in writing translators, some recurrent renaming scenarios are handled by the `smd-translate` utility. Assuming the `MAILBOX_LOCAL` configuration variable is set to `Mail` and the `MAILBOX_REMOTE` is set to `Maildir`, One can use the following configuration file snippet as a reference, where default must be repaced with the endpoint name: TRANSLATOR_RL="smd-translate -m oimap-dovecot -d RL default" TRANSLATOR_LR="smd-translate -m oimap-dovecot -d LR default" ### excluding paths In case some paths need to be skipped, they can be specified as space separated glob(7) expressions in the following variable EXCLUDE="Mail/Spam Mail/Trash" Note that these glob expressions have to match real paths, no translation operation is applied to them, so it may be necessary to specify different expressions for the local and remote endpoint. In that case the following variables can be used: EXCLUDE_LOCAL EXCLUDE_REMOTE Matching is performed using fnmatch(3) with no special flags, thus '`*`' and '`?`' match any character including '`/`'. Note that spaces in glob expressions must be replaced by `%20`. For example, to exclude all paths matching the expression '`Mail/delayed [1-5] days/*`' the variable EXCLUDE must be set to '`Mail/delayed%20[1-5]%20days/*`'. Last, matching is performed every time a directory is entered, and if the matching succeeds the derectory and all its subdirectories are skipped. Thus there is no need to specify a trailing '`/*`' in every expression. ### local synchronization If the remote and local mailboxes are on the same filesystem, one has to specify the `-l` option to `smd-client`. This option can be specified adding to the configuration file `SMDCLIENTOPTS=-l` and set `SERVERNAME=localhost`. ### avoid deletions In some cases, usually unidirectional synchronizations, one may want to not propagate deletions. E.g. one keeps a slim working mailbox but pushes to a backup mailbox to save every email. For that scenario smd-pull and smd-push accept a -n, --no-delete, option. To avoid specifying this option every time one can put it in the configuration file: SMDSERVEROPTS=-n ### migration from offlineimap Migrating from offlineimap may require an extra step, since the local and remote mailboxes may not only differ in their names and sub folders, but also in the names of the single mail messages. Indeed offlineimap encodes some metadata in the file names local to the client. The `smd-translate` utility translates only folder names and not messages names. To uniform the names used on the client to the ones used on the server you can do as follows: 1. Remove `X-OfflineIMAP` from every mail that contains it. Often the same email has that extra header line on the server but not on the client. A not so dirty way of achieving that is the following snippet: `find Mail -type f -exec sed -i '/^X-OfflineIMAP/d' {} \;` 2. Run the `smd-uniform-names` utility. This utility has to be run before the first synchronization, but after smd is configured and `smd-check-conf` has reported no errors. `smd-uniform-names` does not modify the mailbox, but instead it generates a shell script that you can run to perform the renaming. Installation ============ Syncmaildir is part of the Debian archive. If you are running Debian or one of its derivatives, you can install the `syncmaildir` and `syncmaildir-applet` packages with your favourite package manager. If you want to install it from source you need a C compiler, the development files for GLib, GNU make and sed. For `smd-applet` you also need the Vala compiler, libgee, GTK+ 3, GConf, libnotify and dbus-glib. You may also want to customize few variables in the `Makefile`. Then typing `make && make install` should be enough to have syncmaildir installed. Some known platforms are supported by templates defined at the end of `Makefile`, for example you may want to run `make osx/text/all && make osx/text/install` to properly build and install text mode only syncmaildir utilities on an MacOSX platform. Runtime dependencies are: `ssh`, `xdelta`, `lua5.1` and `bash`. Design ====== The design of the software is detailed in the [design document](design.html). If you are interested in hacking `smd`, it may be helpful. Download ======== The software can be download from the Source Forge [download page](http://sourceforge.net/project/showfiles.php?group_id=259152) Author ====== The software is distributed as-is, with no warranties, so if your mailbox is irremediably lost due to Sync Mail Dir, you will get nothing back, but you can complain with me, of course. If you find the software useful, an happy-user report is also welcome. For patches, bug-reports, etc. refer to Enrico Tassi, login gares on fettunta dot org. syncmaildir-1.2.6.2/mddiff.1.txt000066400000000000000000000106101262303536600163330ustar00rootroot00000000000000NAME mddiff - computes diff for maildirs SYNOPSIS mddiff [--max-mailno mno] [--db-file dbf] [-l|--list] [-s|--symlink] [--exclude globexpr] [-v|--verbose] [-d|--dry-run] [-n|--no-delete] [--no-move] [--help] [--sha1sum] paths DESCRIPTION mddiff computes the delta from an old status of a maildir (previously recorded in a support file, called db file) and the current status, generating a set of commands (a diff) that a third party software can apply to synchronize a (eventually remote) copy of the maildir. If paths is a single file name, and that file is a fifo, mddiff reads from it file names separated by new line and outputs the sha1 of its header and body separated by space. $ mddiff /tmp/fifo_for_mddiff 806a0ffe4f29766effd764... 463e543da9dac8e298... 582cbb6a5cd3ce13965c8c... 8fa60a7458b1157193... ... If paths is a single file name, and that file is a fifo, and the -s option is passed, mddiff reads from that fifo two lines at a time, respectively a source name and a target name. It then creates a symlink named as as the target name pointing to the source name. If some dictories need to be created in order to create the symlink, this is also done. mddiff will print 'OK' on stdout to signal a success, 'ERROR' to signal an error. If paths is a list of directories, mddiff outputs a list of actions a client has to perform to synchronize a copy of the same maildirs. This set of actions is relative to a previous status of the maildir stored in the db file. The input directories are traversed recursively, and every file encountered inside directories named cur/ and new/ is a potential mail message (if it contains no \\n\\n it is skipped). $ mddiff ~/Mail/ ADD ~/Mail/cur/1239038050.14937_1.garfield:2,S 66532ebb05b252e... ... Every client (endpoint using mddiff for synchronization) must use a different db-file, and the db-file is strictly related with the set of directories given as arguments, and should not be used with a different directory set. Adding items to the directory set is safe, while removing them may not do what you want (delete actions are generated). mddiff does not alter the dbf file, it generates a new one called dbf.new. It is up to the higher level tool smd-server(1) to rename dbf.new to dbf in case the other endpoint successfully applied the diff. The --exclude option tells mddiff to ignore all paths matching the given glob(7) expression. This option can be passed multiple times. Matching is performed using fnmatch(3) with no special flags, thus '*' and '?' match any character including '/'. Matching is performed when a directory is entered. If the match is successful, the directory and all its subedirectories are skipped. The --no-delete option tells mddiff to not output a DELETE action for files that disappear. Note that a DELETE action is anyway generated for files that are moved (i.e. move is COPY plus DELETE). The result is that deletions are not propagated to the other endpoint. OPTIONS --max-mailno mno Estimation of max mail message number (defaults to the number of messages in the db-file + 1000 or 500000 if there is no db-file). You may want to decrease it for the first run on small systems. It is anyway increased automatically when needed --db-file dbf Name of the cache for the endpoint (default db.txt) --exclude globexpr Exclude paths maching the given expression --sha1sum Behaves like the sha1sum utility --mkdir-p Behaves like mkdir -p --mkfifo Behaves like mkfifo -l --list Only list the mailboxes recursively contained in paths -s --symlink Create symlinks for paths read on the input fifo -v --verbose Increase program verbosity (printed on stderr) -d --dry-run Do not generate a new db-file -n --no-delete Do not track deleted files --no-move Do not generate MOVE (only COPY + DELETE) --help This help screen NOTES mddiff is a low level utility, used by smd-server and smd-client. You should use higher level tools like smd-pull(1), smd-push(1) and smd-loop(1) SEE ALSO smd-client(1), smd-server(1), smd-pull(1), smd-push(1), smd-loop(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/mddiff.c000066400000000000000000000762701262303536600156150ustar00rootroot00000000000000// // maildir diff (mddiff) computes the delta from an old status of a maildir // (previously recorded in a support file) and the current status, generating // a set of commands (a diff) that a third party software can apply to // synchronize a (remote) copy of the maildir. // // Absolutely no warranties, released under GNU GPL version 3 or at your // option any later version. // Copyright Enrico Tassi #define _BSD_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smd-config.h" #ifndef O_NOATIME # define O_NOATIME 0 #endif // C99 has a printf length modifier for size_t #if __STDC_VERSION__ >= 199901L #define SIZE_T_FMT "%zu" #define SIZE_T_CAST(x) x #else #define SIZE_T_FMT "%lu" #define SIZE_T_CAST(x) ((unsigned long)x) #endif #define STATIC static #define SHA_DIGEST_LENGTH 20 #define __tostring(x) #x #define tostring(x) __tostring(x) #define ERROR(cause, msg...) { \ fprintf(stderr, "error [" tostring(cause) "]: " msg);\ fprintf(stdout, "ERROR " msg);\ exit(EXIT_FAILURE);\ } #define WARNING(cause, msg...) \ fprintf(stderr, "warning [" tostring(cause) "]: " msg) #define VERBOSE(cause,msg...) \ if (verbose) fprintf(stderr,"debug [" tostring(cause) "]: " msg) #define VERBOSE_NOH(msg...) \ if (verbose) fprintf(stderr,msg) // default numbers for static memory allocation #define DEFAULT_FILENAME_LEN 100 #define DEFAULT_MAIL_NUMBER 500000 #define MAX_EMAIL_NAME_LEN 1024 // int -> hex STATIC char hexalphabet[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; STATIC int hex2int(char c){ switch(c){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return c - 'a' + 10; } ERROR(hex2int,"Invalid hex character: %c\n",c); } // temporary buffers used to store sha1 sums in ASCII hex STATIC char tmpbuff_1[SHA_DIGEST_LENGTH * 2 + 1]; STATIC char tmpbuff_2[SHA_DIGEST_LENGTH * 2 + 1]; STATIC char tmpbuff_3[SHA_DIGEST_LENGTH * 2 + 1]; STATIC char tmpbuff_4[SHA_DIGEST_LENGTH * 2 + 1]; // temporary buffers used to URL encode mail names STATIC char tmpbuff_5[MAX_EMAIL_NAME_LEN]; STATIC char tmpbuff_6[MAX_EMAIL_NAME_LEN]; STATIC char* txtsha(unsigned char *sha1, char* outbuff){ int fd; for (fd = 0; fd < 20; fd++){ outbuff[fd*2] = hexalphabet[sha1[fd]>>4]; outbuff[fd*2+1] = hexalphabet[sha1[fd]&0x0f]; } outbuff[40] = '\0'; return outbuff; } STATIC void shatxt(const char string[41], unsigned char outbuff[]) { int i; for(i=0; i < SHA_DIGEST_LENGTH; i++){ outbuff[i] = hex2int(string[2*i]) * 16 + hex2int(string[2*i+1]); } } STATIC char* URLtxt(const char string[], char outbuff[]) { size_t i,j; size_t len = strlen(string); for(i=0, j=0; i < len && j + 4 < MAX_EMAIL_NAME_LEN; i++, j++) { if (string[i] == ' ' || string[i] == '%') { snprintf(&outbuff[j], 4, "%%%X", string[i]); j+=2; } else { outbuff[j] = string[i]; } } outbuff[j] = '\0'; return outbuff; } STATIC char* txtURL(const char* string, char* outbuff) { size_t i,j; size_t len = strlen(string); for(i=0, j=0; i < len && j + 4 < MAX_EMAIL_NAME_LEN; i++, j++) { if (string[i] == '%' && i + 2 < len) { unsigned int k; sscanf(&string[i+1],"%2x",&k); snprintf(&outbuff[j], 2, "%c", k); i+=2; } else { outbuff[j] = string[i]; } } outbuff[j] = '\0'; return outbuff; } #define PROMOTE(what,from,to) ((what) = ((what) == (from)) ? (to) : (what)) // flags used to mark struct mail so that at the end of the scanning // we output commands lookig that flag enum sight { SEEN=0, NOT_SEEN=1, MOVED=2, CHANGED=3 }; STATIC char* sightalphabet[]={"SEEN","NOT_SEEN","MOVED","CHANGED"}; STATIC const char* strsight(enum sight s){ return sightalphabet[s]; } // since the mails and names buffers may be reallocated, // hashtables cannot record pointers to a struct mail or char. // they record the offset w.r.t. the base pointer of the buffers. // we define a type for them, so that the compiler complains loudly typedef size_t name_t; typedef size_t mail_t; // mail metadata structure struct mail { unsigned char bsha[SHA_DIGEST_LENGTH]; // body hash value unsigned char hsha[SHA_DIGEST_LENGTH]; // header hash value name_t __name; // file name, do not use directly enum sight seen; // already seen? }; // memory pool for mail file names STATIC char *names; STATIC name_t curname, max_curname, old_curname; // memory pool for mail metadata STATIC struct mail* mails; STATIC mail_t mailno, max_mailno; // hash tables for fast comparison of mails given their name/body-hash STATIC GHashTable *bsha2mail; STATIC GHashTable *filename2mail; STATIC time_t lastcheck; // program options STATIC int verbose; STATIC int dry_run; STATIC int only_list_subfolders; STATIC int only_generate_symlinks; STATIC int only_sha1sum_args; STATIC int only_mkdirp; STATIC int only_mkfifo; STATIC int n_excludes; STATIC char **excludes; STATIC int no_delete; STATIC int no_move; // ============================ helpers ===================================== // mail da structure accessors STATIC struct mail* mail(mail_t mail_idx) { return &mails[mail_idx]; } STATIC char* mail_name(mail_t mail_idx) { return &names[mails[mail_idx].__name]; } STATIC void set_mail_name(mail_t mail_idx, name_t name) { mails[mail_idx].__name = name; } // predicates for assert_all_are STATIC int directory(struct stat sb){ return S_ISDIR(sb.st_mode); } STATIC int regular_file(struct stat sb){ return S_ISREG(sb.st_mode); } // stats and asserts pred on argv[optind] ... argv[argc-optind] STATIC void assert_all_are( int(*predicate)(struct stat), char* description, char*argv[], int argc) { struct stat sb; int c, rc; VERBOSE(input, "Asserting all input paths are: %s\n", description); for(c = 0; c < argc; c++) { const char * argv_c = txtURL(argv[c], tmpbuff_5); rc = stat(argv_c, &sb); if (rc != 0) { ERROR(stat,"unable to stat %s\n",argv_c); } else if ( ! predicate(sb) ) { ERROR(stat,"%s in not a %s\n", argv_c,description); } VERBOSE(input, "%s is a %s\n", argv_c, description); } } #define ASSERT_ALL_ARE(what,v,c) assert_all_are(what,tostring(what),v,c) // open a file in read only mode trying to use O_NOATIME STATIC int open_rdonly_noatime(const char *fname) { int fd = open(fname, O_RDONLY | O_NOATIME); // if the file is not owned by the euid of the process, then // it cannot be opened using the O_NOATIME flag (man 2 open) if (fd == -1 && errno == EPERM) { fd = open(fname, O_RDONLY); } return fd; } // looks for \n\n in a buffer starting at addr of size size STATIC unsigned char * find_endof_header(unsigned char *addr, size_t size) { unsigned char * next; unsigned char * end = addr + size; for(next = addr; next + 1 < end; next++){ if (*next == '\n' && *(next+1) == '\n') { next+=2; return next; } } return NULL; } // =========================== memory allocator ============================ STATIC mail_t alloc_mail(){ mail_t m = mailno; mailno++; if (mailno >= max_mailno) { mails = realloc(mails, sizeof(struct mail) * max_mailno * 2); if (mails == NULL){ ERROR(realloc,"allocation failed for " SIZE_T_FMT " mails\n", SIZE_T_CAST(max_mailno * 2)); } max_mailno *= 2; } return m; } STATIC void dealloc_mail(){ mailno--; } STATIC char *next_name(){ return &names[curname]; } STATIC name_t alloc_name(){ name_t name = curname; size_t len = strlen(&names[name]); old_curname = curname; curname += len + 1; if (curname + MAX_EMAIL_NAME_LEN > max_curname) { names = realloc(names, max_curname * 2); max_curname *= 2; } return name; } STATIC void dealloc_name(){ curname = old_curname; } // =========================== global variables setup ====================== // convenience casts to be used with glib hashtables #define MAIL(t) ((mail_t)(t)) #define GPTR(t) ((gpointer)(t)) STATIC guint bsha_hash(gconstpointer key){ mail_t m = MAIL(key); unsigned char * k = (unsigned char *) mail(m)->bsha; return k[0] + (k[1] << 8) + (k[2] << 16) + (k[3] << 24); } STATIC gboolean bsha_equal(gconstpointer k1, gconstpointer k2){ mail_t m1 = MAIL(k1); mail_t m2 = MAIL(k2); if(!memcmp(mail(m1)->bsha,mail(m2)->bsha,SHA_DIGEST_LENGTH)) return TRUE; else return FALSE; } STATIC gboolean hsha_equal(gconstpointer k1, gconstpointer k2){ mail_t m1 = MAIL(k1); mail_t m2 = MAIL(k2); if(!memcmp(mail(m1)->hsha,mail(m2)->hsha,SHA_DIGEST_LENGTH)) return TRUE; else return FALSE; } STATIC guint name_hash(gconstpointer key){ mail_t m = MAIL(key); return g_str_hash(mail_name(m)); } STATIC gboolean name_equal(gconstpointer k1, gconstpointer k2){ mail_t m1 = MAIL(k1); mail_t m2 = MAIL(k2); return g_str_equal(mail_name(m1), mail_name(m2)); } // wc -l, returning 0 on error STATIC unsigned long int wc_l(const char* dbfile){ int unsigned long mno = 0; struct stat sb; unsigned char *addr, *next; int fd; if ((fd = open(dbfile, O_RDONLY | O_NOATIME)) == -1) goto err_open; if (fstat(fd, &sb) == -1) goto err_mmap; if ((addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto err_mmap; for(next = addr; next < addr + sb.st_size; next++){ if (*next == '\n') mno++; } munmap(addr, sb.st_size); close(fd); return mno; err_mmap: close(fd); err_open: return 0; } // setup memory pools and hash tables STATIC void setup_globals( const char *dbfile, unsigned long int mno, unsigned int fnlen){ // we try to guess a reasonable number of email, to avoid asking the // allocator an unnnecessarily big chunk whose allocation may fail if there // is too few memory. We compute the number of entries in the db-file and // we add 1000 speculating not more than 1000 mails will be received. if (mno == 0){ if ((mno = wc_l(dbfile)) == 0) mno = DEFAULT_MAIL_NUMBER; else mno += 1000; VERBOSE(setup_globals, "guessing we need space for %lu mails\n", mno); } // allocate space for mail metadata mails = malloc(sizeof(struct mail) * mno); if (mails == NULL) ERROR(malloc,"allocation failed for %lu mails\n",mno); mailno=1; // 0 is reserved for NULL max_mailno = mno; // allocate space for mail filenames names = malloc(mno * fnlen); if (names == NULL) ERROR(malloc, "memory allocation failed for " SIZE_T_FMT " mails with an average filename length of %u\n", SIZE_T_CAST(mailno),fnlen); curname=0; max_curname=mno * fnlen; // allocate hashtables for detection of already available mails bsha2mail = g_hash_table_new(bsha_hash,bsha_equal); if (bsha2mail == NULL) ERROR(bsha2mail,"hashtable creation failure\n"); filename2mail = g_hash_table_new(name_hash,name_equal); if (filename2mail == NULL) ERROR(filename2mail,"hashtable creation failure\n"); } // =========================== cache (de)serialization ====================== // dump to file the mailbox status STATIC void save_db(const char* dbname, time_t timestamp){ mail_t m; FILE * fd; char new_dbname[PATH_MAX]; snprintf(new_dbname,PATH_MAX,"%s.new",dbname); fd = fopen(new_dbname,"w"); if (fd == NULL) ERROR(fopen,"unable to save db file '%s'\n",new_dbname); for(m=1; m < mailno; m++){ if (mail(m)->seen == SEEN) { fprintf(fd,"%s %s %s\n", txtsha(mail(m)->hsha,tmpbuff_1), txtsha(mail(m)->bsha,tmpbuff_2), mail_name(m)); } } fclose(fd); snprintf(new_dbname,PATH_MAX,"%s.mtime.new",dbname); fd = fopen(new_dbname,"w"); if (fd == NULL) ERROR(fopen,"unable to save db file '%s'\n",new_dbname); fprintf(fd,"%lu",timestamp); fclose(fd); } // load from disk a mailbox status and index mails with hashtables STATIC void load_db(const char* dbname){ FILE* fd; int fields; int line=0; char new_dbname[PATH_MAX]; snprintf(new_dbname,PATH_MAX,"%s.mtime",dbname); fd = fopen(new_dbname,"r"); if (fd == NULL){ WARNING(fopen,"unable to open db file '%s'\n",new_dbname); lastcheck = 0L; } else { fields = fscanf(fd,"%1$lu",&lastcheck); if (fields != 1) ERROR(fscanf,"malformed db file '%s', please remove it\n", new_dbname); fclose(fd); } fd = fopen(dbname,"r"); if (fd == NULL) { WARNING(fopen,"unable to open db file '%s'\n",dbname); return; } for(;;) { // allocate a mail entry mail_t m = alloc_mail(); // read one entry fields = fscanf(fd, "%1$40s %2$40s %3$" tostring(MAX_EMAIL_NAME_LEN) "[^\n]\n", tmpbuff_1, tmpbuff_2, next_name()); line++; if (fields == EOF) { // deallocate mail entry dealloc_mail(); break; } // sanity checks if (fields != 3) ERROR(fscanf, "%s: malformed line %d: %d != 3 fields." " Please remove this db file.\n", dbname, line, fields); shatxt(tmpbuff_1, mail(m)->hsha); shatxt(tmpbuff_2, mail(m)->bsha); // allocate a name string set_mail_name(m,alloc_name()); // not seen file, may be deleted mail(m)->seen=NOT_SEEN; // store it in the hash tables g_hash_table_insert(bsha2mail,GPTR(m), g_slist_prepend(g_hash_table_lookup(bsha2mail,GPTR(m)),GPTR(m))); g_hash_table_insert(filename2mail,GPTR(m),GPTR(m)); } fclose(fd); } // =============================== commands ================================ #define COMMAND_SKIP(m) \ VERBOSE(skip,"%s\n",mail_name(m)) #define COMMAND_ADD(m) \ fprintf(stdout,"ADD %s %s %s\n", URLtxt(mail_name(m),tmpbuff_5),\ txtsha(mail(m)->hsha,tmpbuff_1),\ txtsha(mail(m)->bsha, tmpbuff_2)) #define COMMAND_COPY(m,n) \ fprintf(stdout, "COPY %s %s %s TO %s\n", URLtxt(mail_name(m),tmpbuff_5),\ txtsha(mail(m)->hsha, tmpbuff_1),\ txtsha(mail(m)->bsha, tmpbuff_2),\ URLtxt(mail_name(n),tmpbuff_6)) #define COMMAND_MOVE(m,n) \ fprintf(stdout, "MOVE %s %s %s TO %s\n", URLtxt(mail_name(m),tmpbuff_5),\ txtsha(mail(m)->hsha, tmpbuff_1),\ txtsha(mail(m)->bsha, tmpbuff_2),\ URLtxt(mail_name(n),tmpbuff_6)) #define COMMAND_COPYBODY(m,n) \ fprintf(stdout, "COPYBODY %s %s TO %s %s\n",\ URLtxt(mail_name(m),tmpbuff_5),txtsha(mail(m)->bsha, tmpbuff_1),\ URLtxt(mail_name(n),tmpbuff_6),txtsha(mail(n)->hsha, tmpbuff_2)) #define COMMAND_DELETE(m) \ fprintf(stdout,"DELETE %s %s %s\n", URLtxt(mail_name(m),tmpbuff_5), \ txtsha(mail(m)->hsha, tmpbuff_1), txtsha(mail(m)->bsha, tmpbuff_2)) #define COMMAND_REPLACE(m,n) \ fprintf(stdout, "REPLACE %s %s %s WITH %s %s\n",\ URLtxt(mail_name(m),tmpbuff_5),txtsha(mail(m)->hsha,tmpbuff_1),\ txtsha(mail(m)->bsha,tmpbuff_2),\ txtsha(mail(n)->hsha,tmpbuff_3),txtsha(mail(n)->bsha,tmpbuff_4)) #define COMMAND_REPLACE_HEADER(m,n) \ fprintf(stdout, "REPLACEHEADER %s %s %s WITH %s\n",\ mail_name(m),txtsha(mail(m)->hsha,tmpbuff_1),\ txtsha(mail(m)->bsha,tmpbuff_2), \ txtsha(mail(n)->hsha,tmpbuff_3)) STATIC int is_old_file_still_there(const char* file){ int fd; struct stat sb; mail_t alias, m; m = alloc_mail(); snprintf(next_name(), MAX_EMAIL_NAME_LEN,"%s",file); set_mail_name(m,alloc_name()); fd = open_rdonly_noatime(mail_name(m)); if (fd == -1) { goto err_alloc_cleanup; } if (fstat(fd, &sb) == -1) { goto err_alloc_fd_cleanup; } alias = MAIL(g_hash_table_lookup(filename2mail,GPTR(m))); if (alias != 0 && lastcheck >= sb.st_mtime) { // we cache that it has been seen already mail(alias)->seen=SEEN; close(fd); dealloc_name(); dealloc_mail(); return 1; } err_alloc_fd_cleanup: close(fd); err_alloc_cleanup: dealloc_name(); dealloc_mail(); return 0; } // the heart STATIC void analyze_file(const char* dir,const char* file) { unsigned char *addr,*next; int fd; struct stat sb; mail_t alias, m; GChecksum* ctx; gsize ctx_len; GSList *bodyaliases = NULL, *bodyaliases_orig = NULL; m = alloc_mail(); snprintf(next_name(), MAX_EMAIL_NAME_LEN,"%s/%s",dir,file); set_mail_name(m,alloc_name()); fd = open_rdonly_noatime(mail_name(m)); if (fd == -1) { WARNING(open,"unable to open file '%s': %s\n", mail_name(m), strerror(errno)); WARNING(open,"ignoring '%s'\n", mail_name(m)); goto err_alloc_cleanup; } if (fstat(fd, &sb) == -1) { WARNING(fstat,"unable to stat file '%s'\n",mail_name(m)); goto err_alloc_cleanup; } alias = MAIL(g_hash_table_lookup(filename2mail,GPTR(m))); // check if the cache lists a file with the same name and the same // mtime. If so, this is an old, untouched, message we can skip if (alias != 0 && lastcheck > sb.st_mtime) { mail(alias)->seen=SEEN; COMMAND_SKIP(alias); goto err_alloc_fd_cleanup; } addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (addr == MAP_FAILED){ if (sb.st_size == 0) // empty file, we do not consider them emails goto err_alloc_fd_cleanup; else // mmap failed ERROR(mmap, "unable to load '%s'\n",mail_name(m)); } next = find_endof_header(addr, sb.st_size); if ( next == NULL ) { WARNING(parse, "malformed file '%s', no header\n",mail_name(m)); munmap(addr, sb.st_size); goto err_alloc_fd_cleanup; } // calculate sha1 ctx = g_checksum_new(G_CHECKSUM_SHA1); ctx_len = SHA_DIGEST_LENGTH; g_checksum_update(ctx, addr, next - addr); g_checksum_get_digest(ctx, mail(m)->hsha, &ctx_len); g_checksum_free(ctx); ctx = g_checksum_new(G_CHECKSUM_SHA1); ctx_len = SHA_DIGEST_LENGTH; g_checksum_update(ctx, next, sb.st_size - (next - addr)); g_checksum_get_digest(ctx, mail(m)->bsha, &ctx_len); g_checksum_free(ctx); munmap(addr, sb.st_size); close(fd); if (alias != 0) { if(bsha_equal(GPTR(alias),GPTR(m))) { if (hsha_equal(GPTR(alias), GPTR(m))) { mail(alias)->seen = SEEN; goto err_alloc_fd_cleanup; } else { COMMAND_REPLACE_HEADER(alias,m); mail(m)->seen=SEEN; mail(alias)->seen=CHANGED; return; } } else { COMMAND_REPLACE(alias,m); mail(m)->seen=SEEN; mail(alias)->seen=CHANGED; return; } } bodyaliases_orig = bodyaliases = g_hash_table_lookup(bsha2mail,GPTR(m)); // some messages with the same body are there if (bodyaliases != NULL) { mail_t firstalias = MAIL(bodyaliases->data); for(; bodyaliases != NULL; bodyaliases = g_slist_next(bodyaliases)) { mail_t bodyalias = MAIL(bodyaliases->data); if (hsha_equal(GPTR(bodyalias), GPTR(m))) { // this one has the same header too // absurd, see the else case g_assert(mail(bodyalias)->seen != MOVED || no_move); if (mail(bodyalias)->seen == SEEN || is_old_file_still_there(mail_name(bodyalias)) || no_move) { // a real copy COMMAND_COPY(bodyalias,m); PROMOTE(mail(bodyalias)->seen, NOT_SEEN, MOVED); mail(m)->seen=SEEN; } else { // a real move COMMAND_MOVE(bodyalias,m); // the new file is the source for such body so that if the // file was copied twice and then removed we generate a // MOVE x -> y and a COPY y -> z g_hash_table_insert(bsha2mail,GPTR(m), g_slist_prepend(bodyaliases_orig,GPTR(m))); mail(bodyalias)->seen=MOVED; mail(m)->seen=SEEN; } return; } } // no full alias, we just recycle the body COMMAND_COPYBODY(firstalias,m); mail(m)->seen=SEEN; return; } // we should add that file COMMAND_ADD(m); mail(m)->seen=SEEN; return; // error handlers, status cleanup err_alloc_fd_cleanup: close(fd); err_alloc_cleanup: dealloc_name(); dealloc_mail(); } // recursively analyze a directory and its sub-directories STATIC void analyze_dir(const char* path){ DIR* dir; struct dirent *dir_entry; int inside_cur_or_new = 0; int i, rc; // skip excluded paths for(i = 0; i < n_excludes; i++){ if ( (rc = fnmatch(excludes[i], path, 0)) == 0 ) { VERBOSE(analyze_dir, "skipping '%s' because excluded by pattern '%s'\n", path, excludes[i]); return; } if ( rc != FNM_NOMATCH ){ ERROR(fnmatch,"processing pattern '%s': %s",excludes[i], strerror(errno)) } } // detect if inside cur/ or new/ #ifdef __GLIBC__ const char* bname = basename(path); #else gchar* bname = g_path_get_basename(path); #endif if ( !strcmp(bname,"cur") || !strcmp(bname,"new") ) { inside_cur_or_new = 1; if ( only_list_subfolders ) { fprintf(stdout, "%s\n", path); return; } } #ifndef __GLIBC__ g_free(bname); #endif dir = opendir(path); if (dir == NULL) ERROR(opendir, "Unable to open directory '%s'\n", path); while ( (dir_entry = readdir(dir)) != NULL) { if (DT_REG == dir_entry->d_type) { if ( inside_cur_or_new && !only_list_subfolders ) { analyze_file(path,dir_entry->d_name); } else { VERBOSE(analyze_dir,"skipping '%s/%s', outside maildir\n", path,dir_entry->d_name); } } else if ((DT_DIR == dir_entry->d_type || DT_LNK == dir_entry->d_type) && strcmp(dir_entry->d_name,"tmp") && strcmp(dir_entry->d_name,".") && strcmp(dir_entry->d_name,"..")){ int len = strlen(path) + 1 + strlen(dir_entry->d_name) + 1; char * newdir = malloc(len); snprintf(newdir,len,"%s/%s",path,dir_entry->d_name); analyze_dir(newdir); free(newdir); } } closedir(dir); } STATIC void analyze_dirs(char* paths[], int no){ int i; for(i=0; iseen == NOT_SEEN || (no_move && mail(m)->seen == MOVED))) // normally moved or removed mails are deleted COMMAND_DELETE(m); else if (no_delete && no_move && mail(m)->seen == MOVED) // if --no-delete only moved mails should be deleted COMMAND_DELETE(m); else VERBOSE(seen,"STATUS OF %s %s %s IS %s\n", mail_name(m),txtsha(mail(m)->hsha,tmpbuff_1), txtsha(mail(m)->bsha,tmpbuff_2),strsight(mail(m)->seen)); } } // removes trailing '\n' modifying the string STATIC void rm_trailing_n(char *src_name){ size_t src_len = strlen(src_name); if (src_len > 0 && src_name[src_len-1] == '\n') src_name[src_len-1]='\0'; } STATIC void extra_sha_file(const char* file) { unsigned char *addr,*next; int fd; struct stat sb; gchar* sha1; fd = open_rdonly_noatime(file); if (fd == -1) ERROR(open,"unable to open file '%s'\n",file); if (fstat(fd, &sb) == -1) ERROR(fstat,"unable to stat file '%s'\n",file); if (! S_ISREG(sb.st_mode)) { ERROR(fstat,"not a regular file '%s'\n",file); } addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (addr == MAP_FAILED) { if (sb.st_size == 0) { next = addr; } else { ERROR(mmap, "unable to load '%s'\n",file); } } else { next = find_endof_header(addr, sb.st_size); if ( next == NULL ) ERROR(parse, "malformed file '%s', no header\n",file); } // calculate sha1 fprintf(stdout, "%s ", sha1 = g_compute_checksum_for_data(G_CHECKSUM_SHA1, addr, next - addr)); g_free(sha1); fprintf(stdout, "%s\n", sha1 = g_compute_checksum_for_data(G_CHECKSUM_SHA1, next, sb.st_size - (next - addr))); g_free(sha1); munmap(addr, sb.st_size); close(fd); } STATIC void extra_mkdir_ln(char* src_name, char* tgt_name) { gchar* dir_tgt = g_path_get_dirname(tgt_name); if ( g_mkdir_with_parents(dir_tgt, 0770) ){ ERROR(mkdir,"unable to create dir %s: %s\n", dir_tgt, strerror(errno)); exit(EXIT_FAILURE); } if ( symlink(src_name, tgt_name) != 0 ){ ERROR(symlink,"unable to symlink %s to %s: %s\n", src_name, tgt_name, strerror(errno)); exit(EXIT_FAILURE); } fprintf(stdout,"OK\n"); g_free(dir_tgt); } STATIC void extra_sha1sum_file(const char* file) { unsigned char *addr; int fd; struct stat sb; gchar* sha1; fd = open_rdonly_noatime(file); if (fd == -1) ERROR(open,"unable to open file '%s'\n",file); if (fstat(fd, &sb) == -1) ERROR(fstat,"unable to stat file '%s'\n",file); addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (addr == MAP_FAILED){ if (sb.st_size == 0) // empty file ; else // mmap failed ERROR(mmap, "unable to load '%s'\n",file); } // calculate sha1 fprintf(stdout, "%s %s\n", sha1 = g_compute_checksum_for_data(G_CHECKSUM_SHA1, addr, sb.st_size), file); g_free(sha1); if (addr != MAP_FAILED) munmap(addr, sb.st_size); close(fd); } // ============================ main ===================================== #define OPT_MAX_MAILNO 300 #define OPT_DB_FILE 301 #define OPT_EXCLUDE 302 #define OPT_SHA1SUM 303 #define OPT_MKDIRP 304 #define OPT_MKFIFO 305 #define OPT_NOMOVE 306 // command line options STATIC struct option long_options[] = { {"max-mailno", required_argument, NULL, OPT_MAX_MAILNO}, {"db-file" , required_argument, NULL, OPT_DB_FILE}, {"exclude" , required_argument, NULL, OPT_EXCLUDE}, {"sha1sum" , no_argument , NULL, OPT_SHA1SUM}, {"mkdir-p" , no_argument , NULL, OPT_MKDIRP}, {"mkfifo" , no_argument , NULL, OPT_MKFIFO}, {"list" , no_argument , NULL, 'l'}, {"symlink" , no_argument , NULL, 's'}, {"verbose" , no_argument , NULL, 'v'}, {"dry-run" , no_argument , NULL, 'd'}, {"no-delete" , no_argument , NULL, 'n'}, {"no-move" , no_argument , NULL, OPT_NOMOVE}, {"help" , no_argument , NULL, 'h'}, {NULL , no_argument , NULL, 0}, }; // command line options documentation STATIC const char* long_options_doc[] = { " number Estimation of max mail message number (defaults to the" "\n " "number of messages in the db-file + 1000 or " tostring(DEFAULT_MAIL_NUMBER) "\n " "if there is no db-file). You may want to decrease it" "\n " "for the first run on small systems. It is anyway" "\n " "increased automatically when needed", "path Name of the cache for the endpoint (default db.txt)", "glob Exclude paths matching the given glob expression", "behave as sha1sum", "behave as mkdir -p", "behave as mkfifo", "Only list subfolders (short -l)", "Symbolic Link generation mode (short -s)", "Increase program verbosity (printed on stderr, short -v)", "Do not generate a new db file (short -d)", "Do not track deletions (short -n)", "This help screen", NULL }; // print help and bail out STATIC void help(char* argv0){ int i; char *bname = g_path_get_basename(argv0); fprintf(stdout,"\nUsage: %s [options] (paths...|fifo)\n",bname); for (i=0;long_options[i].name != NULL;i++) { if ( long_options[i].has_arg == required_argument ) fprintf(stdout," --%-8s%s\n", long_options[i].name,long_options_doc[i]); else fprintf(stdout," --%-18s%s\n", long_options[i].name,long_options_doc[i]); } fprintf(stdout,"\n\ If paths is a single fifo, %s reads from it file names and outputs the\n\ sha1 of their header and body separated by space.\n\n\ If paths is a list of directories, %s outputs a list of actions a client\n\ has to perform to syncronize a copy of the same maildirs. This set of actions\n\ is relative to a previous status of the maildir stored in the db file.\n\ The input directories are traversed recursively, and every file encountered\n\ inside directories named cur/ and new/ is a potential mail message (if it\n\ contains no \\n\\n it is skipped).\n\n\ Every client must use a different db-file, and the db-file is strictly\n\ related with the set of directories given as arguments, and should not\n\ be used with a different directory set. Adding items to the directory\n\ set is safe, while removing them may not do what you want (delete actions\n\ are generated).\n\n", bname, bname); fprintf(stdout, "Copyright %s\n",SMD_CONF_COPYRIGHT); fprintf(stdout, "Version %s, ",SMD_CONF_VERSION); fprintf(stdout, "released under the terms of GPLv3, no waranties\n\n"); } int main(int argc, char *argv[]) { char *data; char *dbfile="db.txt"; unsigned long int mailno = 0; unsigned int filenamelen = DEFAULT_FILENAME_LEN; struct stat sb; int c = 0; int option_index = 0; time_t bigbang; glib_check_version(2,16,0); g_assert(MAIL(NULL) == 0); g_assert(GPTR(0) == NULL); g_assert(MAIL(GPTR(1)) == 1); for(;;) { c = getopt_long(argc, argv, "vhndls", long_options, &option_index); if (c == -1) break; // no more args switch (c) { case OPT_MAX_MAILNO: mailno = strtoul(optarg,NULL,10); break; case OPT_DB_FILE: dbfile = strdup(optarg); break; case OPT_EXCLUDE: excludes = realloc(excludes, sizeof(char*) * (n_excludes + 1)); excludes[n_excludes] = strdup(txtURL(optarg,tmpbuff_5)); n_excludes++; break; case OPT_SHA1SUM: only_sha1sum_args = 1; break; case OPT_MKDIRP: only_mkdirp = 1; break; case OPT_MKFIFO: only_mkfifo = 1; break; case OPT_NOMOVE: no_move = 1; break; case 'v': verbose = 1; break; case 'd': dry_run = 1; break; case 'n': no_delete = 1; break; case 'l': only_list_subfolders = 1; break; case 's': only_generate_symlinks = 1; break; case 'h': help(argv[0]); exit(EXIT_SUCCESS); break; default: help(argv[0]); exit(EXIT_FAILURE); break; } } if (optind >= argc) { help(argv[0]); exit(EXIT_FAILURE); } if ( only_mkdirp ) { int i; for (i = optind; i < argc; i++) { if ( g_mkdir_with_parents(argv[i], 0770) ) { ERROR(mkdir,"unable to create dir %s: %s\n", argv[i], strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } if ( only_sha1sum_args ) { int i; ASSERT_ALL_ARE(regular_file, &argv[optind], argc - optind); for (i = optind; i < argc; i++) { extra_sha1sum_file(argv[i]); } exit(EXIT_SUCCESS); } if ( only_mkfifo ) { int i; for (i = optind; i < argc; i++) { if ( mknod(argv[i], 0600 | S_IFIFO, 0) ) { ERROR(mknod,"Unable to create fifo %s: %s\n", argv[i], strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } // remaining args is the dirs containing the data or the files to hash data = strdup(txtURL(argv[optind],tmpbuff_5)); // check if data is a directory or a regular file c = stat(data, &sb); if (c != 0) ERROR(stat,"unable to stat %s\n",data); if ( S_ISFIFO(sb.st_mode) && argc - optind == 1){ FILE *in = fopen(data,"r"); if (in == NULL) { ERROR(fopen,"unable to open fifo %s\n",data); exit(EXIT_FAILURE); } if ( only_generate_symlinks ) { /* symlink */ char src_name[MAX_EMAIL_NAME_LEN]; char tgt_name[MAX_EMAIL_NAME_LEN]; while (!feof(in)) { if(fgets(src_name,MAX_EMAIL_NAME_LEN,in) != NULL && fgets(tgt_name,MAX_EMAIL_NAME_LEN,in) != NULL) { rm_trailing_n(src_name); rm_trailing_n(tgt_name); extra_mkdir_ln(src_name, tgt_name); fflush(stdout); } } } else { /* sha1 mail */ char name[MAX_EMAIL_NAME_LEN]; while (!feof(in)) { if(fgets(name,MAX_EMAIL_NAME_LEN,in) != NULL){ rm_trailing_n(name); extra_sha_file(name); fflush(stdout); } } } exit(EXIT_SUCCESS); } else if ( ! S_ISDIR(sb.st_mode) ) { ERROR(stat, "given path is not a fifo nor a directory: %s\n",data); } free(data); // regular case, hash the content of maildirs rooted in the // list of directories specified at command line ASSERT_ALL_ARE(directory, &argv[optind], argc - optind); if ( only_list_subfolders ) { analyze_dirs(&argv[optind], argc - optind); exit(EXIT_SUCCESS); } // allocate memory setup_globals(dbfile, mailno, filenamelen); load_db(dbfile); bigbang = time(NULL); analyze_dirs(&argv[optind], argc - optind); generate_deletions(); if (!dry_run) save_db(dbfile, bigbang); exit(EXIT_SUCCESS); } // vim:set ts=4: syncmaildir-1.2.6.2/misc/000077500000000000000000000000001262303536600151375ustar00rootroot00000000000000syncmaildir-1.2.6.2/misc/Mail.TEST.tgz000066400000000000000000000640231262303536600173320ustar00rootroot00000000000000‹¿ÞÁKí½[s\G’$ÜÏý+`¦Çö  —"û¢4lÈš2Ú²ÍØok X”°Q\Fûë·Î%3Ã#<"³XÐ.'Í>ëÕ7Pu.žî¯Î/¯þë/¿î:Ü®ÕÉI÷¿‹ÕÉ¡üß´þ²X¬–«Ó¿.‡‡Ë¿œüÊŸ«_ŸoïÎoþrw~{{ü»Úÿýwº^u÷ÿâóͯù ìvÿWÝý?YÏ÷Š•ïÿb¹:|öìôtuø·“£Ó§ÿûhõ·w—7ïÎàot7øôøØ»ÿGËÓSõþŸ.Nÿrpø»ºþä÷ÿÍçwÿg}q÷÷ƒ›Í“Ûá?ŽV}µ¾½=ÿaýäìýß»ÿüæüný÷ƒ7Ÿ¯žº9X,§?ZýýäðàÅ·oþu°<\þõßOþ{³éÑߺÿïbóñ¯}»>X_Þý¸¾9¸ß|¾zðn}w·ýç×Û_~°ý?¼_ß^þp}°ùpðþüîüàüâÿ~¾¼¹¼þáàn³¹ºýσÍÍÁç?oÿífûO^_m>l®Ÿ\]^¯n×7?¯on.·?~ó~ûk·ÿærûë·ŸuüÕý¿ÿ¸¾¾ë~ÿÙõ‡ÍÍÇó»ËíÏ߬ïn.×?Ÿ_ý­ûˆ÷—WW7ëËŸ®Öý¿~õêÍÁí/·wë·Ûïs³ý ݯ½¸ë>×ýöû¬?¾[¿¿~ðéfÓ]²Ûþ÷\¯·ÿ¶âbsýáò‡Ï7Û¸ùpw¾ý|ÚÜÜwßçîÇÏ·÷ù²ÜþØ_—÷ëwŸ8øôùÝÕåÅÁûÍÇóíwº½ë¾É—ëá*Œòj}~K¿žøSW—w—ÃÏuäþrû÷åãýpµyw~uùÿô 8ÿøîrû˶?v»ÙþŸÎïºÿûõð“ði7Ÿï.®¶ïû߯ï¶ÏÑ͇îRÝn®>w—{û¯·ßàâü®rþýeüùr{ Óº¼~ß}ØûñÞ¤ë?\üŸ/o?Ÿ_åÛÿ¡D®ß_õO |£_õÿêj=Þ ñk”ª»ê­ÊÁù§OÛÛs>~£tKÞ­Îß]õ¿ïn}{wðáf½îïþówŸïÄm¿À·á“þÜ/Ï>m_þW¼Û·}GÏŸl¿ÀöÓÞ–Kß¿ÛË÷CÿZl¿Ïûõöí{ŸHxnñ2_¦‹Ðß üPÁ›xÝ™íÝäߥ¯ñ§«Í/ì"ŸßÜ]~ØþÊóáþ^]]þ°¾¾XÏJ>n®/ï67Ýwýøùêîòãúýåv¸¹øqûž_Ümß0ü£é‹ˆŸ³×èÝöºÍzøsù øÛÇß¶OvO»ÿ‘¯°¾?â_m6·ë«_žl¿ÍöçÞ¬¯Øn\Ãm…Gàòz[†¶¿†üÝô‘ñkÛ›¾¢|awÒŸóýúC·‹^¯ïî77?mÞ‹ëÍÕ¦ÛoúŸ¯ìhäþx~ó¾â»7±§~èþøv—x²ùðdûOÞmþgØÏË)¿qýäÅæãÇõÍEÞ%ÃwóùÙßþúØUó³\ü·x:þÛþ§ÁÛ#ÁŒÿ&Xÿ-žþÛþçø—m!ê6†W¿ˆ˜•~¾±•/C‰~;ê‘f¿•?(Kß}ó.G1AJ¢¶oaNÞÖá;÷Õ²$—NmªeAú¨æÓú¦ÿ¼ÛŸV@6öáw…àÉ!lÿç}¾V»»1‚v‰¦%PM7êùY*ýÍéð’mß½y-nMÆšº”ï“Áy‡nÊo¢¸˜…d8JHg!d >ü€®ì¦8—êj*«Ä  }“ÐEy!Äá'@&¹þ v._¤ôm{”lQÉxÃó³Ü½ˆ—[„sþËÚ“ ù üÅ¢ä ¼.o>Ÿ6¿¨jÏÞ|óý“îê¿÷.ÜxŽÈ –\üá*Z¨]=j÷)ûÖÍÊÛ;~äîwe°?>ÖýÇ,/bÿ8/óöFË7ìãùÿ\~´[›: KwD¢ûþ`Œ`=Ý1 ó¥Ÿ¡ÔŸ|¹øïäd:þo©ùÿ-þ;žñߋ⿓ÀÛÿ|þO!–z&öEg}óänó¤û_€åjÐÇXêäö @hû¡?}ÞþgÏyúÊÔ¬‰y>ÿ¼ý@ÝÕ£‘\X%ÅP*]þ±÷”Ê«5¹@ß½z3–yTçKÒPo ˜Îdf‚¦Æ•+Néˆu”ýé“¿câr6xSÏ:ÄܦüëÕ? kšèÄ««ÌÀåáâZ¼W˜&|Ùû<ì«àåø25k9í Ž)?Y^…áCõWÀ~&q 8360ïœz-Ä[‚¹ÀöŸ¾ÿë ÌéƒUù©,·B§7˜"×keàH îÄU÷Ï-4‚Ëðô¦WÁy„9Ìaz›“Ç…Íè0£ÍrÚQè<Qç/–WVpꨞˆÜâøy-ލâoÞ¬·WaÍÎå=í0—·ÃƒÎv§¤øüØõð϶üþïáTøo¹:&øïhÆS,Þÿ=Äþïá>ø/ï,ŒriŠrïm0NID>ë!Y@„s dü|ys÷¹¥ý—~@àºt^Ç.\…ÞshÍYyXUìí!¡ @gD&‚Í´Ôd¡ÓöÍ+ã€Áû¿5 ÎRY|x`Ø\Hˆ’ÉœaÛ™Ø3†¥ñ’‰³@º`¹\ç3Cæ^6ÿdƒt£¿'x×Ý5ùtÓá–ŒqÓår­ ö§‡…gᤴÝ$^ð_Ç„8+´™DnT!á\ùùiW2è„æl¾š v@1PÀýT  Gà tMw‡ò }ƒ=½q¹.PîýZ\_Jþ}ûÝ×°1ÈÝ$Ò`|÷æõA¯¨ã)·þ?Xù¯ò?ÇÇFÿur4÷ÿ&Y´þCù?Þ§úãKÓ¿êX‚e5(@>BOÉPþÂv ìŽýÇÕu“qØF”ذº]JH v9pRÒ¤Û˜ý³<)”ªù1HæLÒÙsE„ªøò½àˆd,<·Û‚“nC¼ÚtÄ<þ«V%ëÕqÌ!ok‰mzüP숮äzðCÃÇA5Z)ã¥fº”‡z\›š¿!ÛP¥Õɬ:ßVºqKt«e ößÛGêGÝØ”uÞ‰›"ü©A–#¹·ÌWBçPŽ)ù@ï¾ÿæ,¿5€éó›~JÂiNLÁ[ä=Ç-´{žP :ðqf÷aÏÏÐä$è«ÿÕËä·Wô ¥„TóÞÃ…"'$|ÂMäc…uöÆB*)N¼š¥×Ì´˜ãù¤{×jœnBÏ]¥½å÷ÿŽ&ëÿ-NO¬þ9ã¿)ïÿaÿïh˜@ˆî…ä=Î*¨ÒöµŠn#\çL÷ï®Ô†€´`¸a7s袢¶!—–Ñ4@i IÁÜ™PÊÜ|v³±#í­¯ka°Ä•)âÆô¯¨–,R¯ì2wAä)€Î³tÞÑ/ç=Y?Á2΂04д}9ÄÌ–¶IKß‚BÀ 1r¿„€Çéˆ%tTüƒ2«”Ì*S8Ô\˜µØ|øPq]¢Ð¥:ï쫊\DI±¢×’;;€£¡ÂgCg@4å@³}"ÕiÂíûet#*÷çÕçñf&¨–Tµ°oòÎá”n(ù†Ëãà5z“GÛ¿ð¸ýØEo^y¹øo5Ýüçñ‰žÿç?§Zÿ­pþsµ×ü§ì7è‘ %jч2NjCãF‰¨›vˆfW.^¡¡Älû}œêÈ|0ÈŠ )@€56Ù'Ách™ˆßÃÑWG`bÐ ¥šª‚`¯Ä™`Õéà/y³ÊÖ´)ðÈø®,xs¹g+›¢ìòÁRåŸãŽêÞ‹©`^EÕ¹ôåœ À#ã@4¡8®ˆ^W›”kgê°6æxxc%S«d–Ñ(©nÙI<Ž6yzêA'¬RàËkê:s?p% Liæ<+:Iƒ5©•ÎIêë Ú.:‡5ȱš¶+}¡ÜÍOÞ=Æ"TƒòXžèö{E9áÙ¸w®*Ï<ïû«ë1’ \Èê÷ŸM¦ÿ:]þo9ó“,Þÿ}† àgЛ…ØEBùF•ûç½>õJ«º'‰G_„7Þ0 I)´VŒ@ðS´7¯G> ;F%¢¯ÿñÚ¢ÅÊþÍH§Àúô†»4žPŒÔÿY‡¨ÈLU©;©ß&TÌO$ú\ÎÓÃàʲW2¢éYF:ŤÙÓ0U+9²þëöä®+£ÑÁ4Íø|5tÄ]ʨ'H öî€wç²s‚ƒS‡#™K­˜Œ1=¥eÀaÄQ@bNøËžº"A:~ ¸jî„Júdÿ÷õß'«ú?Ûú?ÏÿM³¸þ`G{)ÀîeÓ£¨ƒÒI̺ ô#ó”¨qШYõ^KŽ"$»£hlÓÑ æùÙ(ðCŽ79ûè¡ÆšåÞ<5¼22K tÙz—Oº­Ç7²¿Q®ØZ™_y“Z]7²kýÝ™Ù#qk¹8ü±hLªZ¤Gh¡Çy)‹jgütgu0Òóæ–#tïªz'NôÈPÚ°eûé`ýäëÏ·Ý'¿µO€ì¯%d à/¹%º?0š&HœæwVÓk,ˆÝÜ€§y€²Ë€?Ã䯗_·¸…%Ys¤×YÎ@.ûrÀÐ@ºhÂÆŒ½{ûâ«Ä²!@-4Ÿ¢i‹³–§ÃÊê³¹[÷çY¾þk2ÿ¯å‰ñÿ=>]ÌúÿIסÿ×É^þ_\2‹œÃ&N_~ÍŠ$pfP#jæ«ì|4ðœîoøÙ0Jª^-gà´KR‘j@+ÍuåЀ§2ÊffX®ã]ÖGf~æãWàûNÆÿ,OOŽ­þ{1×ÿ)÷ÿDþg±ÿÚëhÖ÷òL—¾RnòÛ/£Laöà0váuRŠBîíÜ™Ò7ü±Ãd¼‘ìZZU|€üA{ÅUï¬\èbýfþd k‚·_3óvšdH‹ €˜Ñ(*L/ÓøÌQKÔÕçß¼ù§1·Ô™P÷-XGŸ3O€Z†CsŒPëäh&^1éíÙXxÇ}m¼Ù­¢'ïQIÎ^ ¢ÐEC ýÄSŒÒ¤^jîlo÷’ú}Xéï.}Ú¨‘³(I:Z=áØýÎÚ…X³O1Þ¤®©›I¢_cÿ÷õ¿“ù?ž®Ìù9×ÿI×ÿ¢ÿãj/ÿGÕoÒ+ƹN<¨h›Æaø=RâÚº—Q£ž«†8ÔI,‘± wÊ+PY‰]øèbÏ"Y_zd:O;içÓ뙳fVš5ïo3„Ôc!¡ð%Òô!¨”?s?“—òÿ,ÝÒίòÈÌ!Ÿà•ÍtgzØååú`sムl*ÆÖªðÒ#½ë#ÀË0±VuíKWn';˜ÜÃíâ•N-Kñ;êÚÃo4 =(:÷TÍA]= VóbDÔ5³™ÑTˆö©ˆ6ÖFã6DtGbŸ~3ál ½8ü¹Ë2y”8 `—ûKóFlÅ*:AL¦ ‹Qç¹+Û¸Rü© /¨¡ÂÓÚ.¯+vrçJÄ–H Ëbo›ëã•UkDzۦè¨ÿ¶Ìë–‹ÿ—Ëéú«Ùÿá±ÅÿË%àÿíîËÿÑÍÙÙ©”þ2+¬Ý‘}–²£ÁMA¤MA6Ȇñ´G#âePCÚ˜y;…Ù8ý²V,e¿$/@Jñl,ý­´©6­ï…@ΈƒÙm“¾f67°’x˧®žëçЍ”sio’.@5o~Ü\ütßýåÁ÷ª@uˆêkùüi´Ž‘TYë‘ qoðŸÐM”TIÒÝ>i5íÁ$þˆ¥~ ´ë˜wÔž±D$«Î+Ž%€…:=ø éœ`"“)þ&P”¯ÿ>Lÿ}´ úïyþ’Åõß§¨ÿ>}€ù¯ØÌIµÿè µóù#ræ¤ÝÞd Æ*xÃH‚©—Í»×ÇøífÔ³6ldd™ÆŽ^´ nƒo %n¸ N“ºC¡+6A÷ÆZ"ë‡^¡ô™‡Vä¢eSi4 É8ÁT°é(\ö{ÎÄãj~µË0|Ú„Pg¿Ä^ÛÁÌ›ŠTôÉ ã ÐÕõâèdcŸ !Æ».|DeŠ¢|ûöí“«ËŸ¤nŽŒRŒ.ÒÕ3‘Ìãì,$:k”Nç¸UdŸMÎgJÃMy0ņ ÎzƨF£ðl¹k@ûZ¦œ\´÷—7TØ€š ßx&"ƒÈ?ŒëQ–¯ÿš ÿ-»a¿ÿ=Îâú/Ä‹½ð;@0()íä`Äõ¾Ñ„G®2c ˆg(}¹«@ÖÈ!…CªõE‹ˆäo¬@•n¶åðÉmƒ(FÊÝ¡]õ±*%‘™ìø™¶ªkš“´xbXÍq¬5ýR †êžú<%ýh­áÌ/^ž Ø·tÎèd–ä4>a$cN3~Š”~³ÅCí7:&þB0‹ÆA§}Ž‘°téú­:-WE{•È·8™”ä ‚K=ë[¤€Ž-Ÿü9‰<Ê[Ž¿¢áPD}j×ã )Y@)Ef1î×d)-§Üÿ·+¥ †äp]úŸÐÀÄà¼ÉàçE6ß]³ÊZÞÊ3œ™R˜3hK7œšãº½‰½Û¯òyûwŸ|ܼ»¼ZCA8{“f C²˜à!JJ _4Ìò*6‰š6WÐDú¨Œñ•ûŸè»µX·B¡Î•pu¦¸#oa‡õƒßñïWÿ44˜+£N'’JtH²Ø)?¨ãWTc‹ H*¬/‘ tlürŸÿŸ,ÿ}yz¼²ù﫹þO±8ÿôÿ¨?ÛŠ@P×ÂÄ‚õa3‚>h$´ïÌS]G:µwÒ0#R‡CßÄñ˜ECé©Ú¾ê#µâ˶KǾÕÙ/”í–³)2›l`ßpÑf^ ¯ñùë·ææ"×—j•K>ìæ\}ŠvýJ4©È ŸfyDª ‹.ï›U$‹ÆÙæ M£ÜÖÏðò¬Anl1O)u¬Dœús ~¾EšÜ”Ó‘[jWüë2Ì¡ì/ÈÇ)ßÿKKæLƱäf«É!6ïzLjZKÇ)Gjf¼ZÂI}rÏ }\ÄË™meª[M¹ågÙ‚Š²¿0¨eè«"=V ÖZ·Õ°‹lLø„m† Väjâyr¾$‘o­bkÔK#%ÿ–WM—ý±+ækùùÓá¿øŒùóüï$‹ç?"þ;Ù ÿµåÚÖ6IßÔÉs}LáÂä^½z“Ømµ™Ê`0"©€›5•Ì(­¡–7Z’wV);9:IEdgt[t•ÕQU"“аˆ9®Ò²J¡Ð,Ä–PS¤pÃЈ¦J‡ÔNº0P`Ê qá›ù^=¡4Áˆ£«l8#zvLUЖ(„mšw”¥\ð4Ç „i)¦ÍWUó£€&MsÉ$Exƒ±BW xÚé'¹¼®¬óo7riû<§ÌúS †áñ•‘.˜×¡(Íz ,ì`HT ½ÂHðô¥þèà8Ï´L‘óN^KÞi X_é±ûm³ê JecûcjÞü‘+Ç~+›¸]ÒÓD§¢c}öCÄ>þ{8xUÿµ$ùßG3þ›bqü‡ð“½ààwà“zÎ4‘ÖÔøOB!NÄ Ô¨¨¨DDûPÓøT@¹ïðþu­Á8Ó£äÎl°thœ¹K‡ðHÔPI“K²@ÝiÂÉνܯéä9õæ+XvSÝ;AXÙ>$—3TšDeV¢÷^°8œ0=bp›‰´‚qïœÚËä|ÓáèÁX©ôWð¯-F¦×f«uf½½wFß[Ÿ†.°\BTuD2%ÝÕcI]\EPŒÝ-ÑC%Ù ‰{~1ÓA¢¸æöÀ÷™Ë»-A«@5ù‘Ö£ ̬Î3Üè¾™ö,U~ØG`lóö¡Õþqà/ Ä*ûÐÿΗŸÿ0þ[ý×áÉŒÿ¦X<ÿñßуà?åÁùõòëÀ¯¿ÕE¦Ä¼¸n¡RSöø6*e£íB]ŠþmõW×n§çTh[ÛÝUñq‹³‘ö-.WŽÑÛ +ÖÖ­„qð;Nð m:LÎÂnø#é(ó††ÒUE¨èIp½4¯¹q & àˆ$ Ÿá@ßðœZ´R±Tp£JæV}FTe;æLXþ·kÌÔdj‹Lû¸žŽˆMZ’+Æ-‹Ìa,œåáÁRÄÃu¨8GŽ,~‡–ÒæÐs®XÍ,Ž– †‹®Ik£Îp ÷óÇ®góÚmùþ“á¿åjµ°þ3þ›dqÿGÄ«=ñŸÒÄòÿÚ&[æ»ò„£˜Zâ*^ìq…EÖ×6 MØT)ÊÕ’b‘AFEĬ*rÄå(C„“)ÕâaÉs¼œË?“À¹#_Š‘nq\‡Ëld*ÞÕ"{$)Ú:Ï]QiË%sC ˆÃr91'•¦•üHpþQ²|^Qïo/…IpªuùôH¡EÚõ«–_±F©¾AõóЀ8PW÷›æ„?xÎhh· Ífî“âe¤©6×.TŒ"ÝZÊm£|Ðð÷¿:¿¸Ù ´†ÎÊV‰veÍ@Í­ÿO'óX®–Gsÿï‘­ÿOÑÿáé^þ, I&É*˜SõðŒ•^WaÎË,¥—¬pqáŠJ·%°oÙ¸B“¤ˆÖ9/‚çä¸T3$MµÜTÏÈÎæDeG}T1ñsdNO ÊXŽHÞý™>ƒ®µGg$ÝÆË'UÐÄËÃvèö+kµ¶‚b¨q­ø9îN©žSGP–á1y”t%k1B̤P :ãšï–ß~››{À-hsÈô8rÿMÀžù¶ hëŒ4…Òö‡ˆþ¡²5¸Ÿ=•hÍn } 1µ”\‡Àn|娣`×1\óHC%!Lºb‚ƒö·þŸN¦ÿ^nÿ¨=ÿÏúïI­ÿ§¨ÿ>ÝKÿô×W þ‘OØsżKYfÆŽXû2I€®©¾û¤ä2Ù Ì¡)+2N¿áíæf{‰ÞnkÆÁÛõ»¤” '„@ã(H²`ÉuCY ž&k3nÞÊš$Ãcá!+Û7‘|é¶ôlÉ ¤iÀô”`±tühø†®òñG´ÖÖ:QÚŠ3žÌá%ѳ°ƒ½j}êÁ)aÃcb¬~óMت¯tf‘bØÔù×?”­ƒKó`& ©Œ„@Qi I’J?šFåoÜ)àÛ‚²g’ʉÀà76ö€mßÿy:ýÇØú?û?O²¸ÿ³2€Þ‹ÿ×fˆ·_Þ| —·a`œµ€ YŸà°š‚NöÂ(L\2­ÛALÎ˳'ïºvµ¶÷‰žŸ•ŠP “dÁ3—ºÞ· §‘Å=ð?<5í±/¥°ˆM µq%¨„r’„‹RA¢Úô&ƒNÀKÇþ=¡ÑyôT“'È guã¡ ÒniÆ‘X²‹tñ|²‡ÁÂüÄ96Ûõ`’ Ü\‘Žò:8¢E³‚.ÀCC+á.$ÞÌž•`‡É§Cö<Œ•—Ø 71#+ìªùÇY.þ;ž.ÿódubýŸfþg’Eñß1æï•ÿéOÑ:£e~ÀñþÆzT£eÄë?Ë1¨ eÒw'Duâ™Tfu¥ù.ª@E¼XhEpáŸ>½?¤Ÿ‘‚ºxèG/ÓG b)D€aj*Çf‚+ò”¨ÝN5 ãbApèÍ4–ižÅÈ”0l‹FYQ³žDªÈjŸí2rôN•)tÏ0Êt „³¨çQC5Iß…Ù¦I¥îÇæ²ª˜bŒo“ÂŒ " » ¹w}ò©¡eìß‘œ=öæÿ—hþg9ÿs úÏÙÿqÊÅç–8ÿ³ÜËÿ¹ß…¼æwV*0ÂATdÕUá$¯GìVé´Ë–¶ŽF¶[Ú¸‹ÐVÐ0” ÿ¬Ûð»~·®JFmR™2Õçz)-Ñs-9ÊË©}wä¡™Z"0( ­™Gæé:¤Ê3×ÂÃÞÙÜ»©?DC',U@D%M­‘?ËØ—΢o>|ê/34äÃÞ- -nH’/`‘¬)‚Hg°ƒ(M¶ÆÃsVþœv…ëlð¿í×.&æ´°Œ…ðTãٓˇ*é~]KÏîhj' ÄKëYci5êÓ-_ó+gO”êfi‹pL<}j‰‡ÛlüK ÇJž™<+ÃÿàJqíi©ÿØ$$ÞÁí¿G\6üïy|˽ÜÓ×ÿM§ÿÿï±ÿ7çL²¸þ÷õ¿GÙÿ“³¼V`•3Ýr¢ÇÑzQ¦lt8Ù3mJUÄ!ªiññbFÃ6Ë öP£ ÉS/å5±–š3AŒŠ'h°äõ6´Íf†-\ÈîþNùCó®5ò—ªÁÇŠ•l\lö q0B{ÊiþÞî’·>[² y1õ‹Àpí­EJS~¬ÃcØ™á€(IUÔ¼Ü ªÖU;ã$S;¥Þ¾~ùºœRJ … rþžLVQ^ÞèµnêiÅ_Ú„êÈž•¸)¯P3 ¤{í~X̦~ýŸ®ÿ³ZYÿçåœÿ:Éâõû?O÷êÿ¨CK)Ë>^÷·zH‰ók:U·1ÆdÑ„Yô&µù Úû̉C·Ÿ•«‘8gþéOð@ëã£\[Zé׳m˜åžr*ŸlÏ”ÝÙý}¤â.sW»e‹ŽhMU8ͨ|Ðþ:ï<€;¬ÆR½£pQ «ž˜ÒR£c÷époº(ë€1ùÔ»Yð g¡¬"_TQf_Z”Q­š ¡ù¶¾Ó-QËŽÑkã§scš[º+H~²0´RÔYÒ"·§e(†²ãI÷¦óÍò«bÕnÿ÷õ¿Ó垬¬ÿïržÿdqý/æ>Û?ÿ¶—îipÁÅfçpg4®.Óù´8“;Ùˆg†êŒ™Ð‡àÈ•“¼XßN9Ù8'Ej§câèà!s#r¡,éÒL èp8{L­ÔáÝ;‹Wö!n÷‘¿ $ª‹ùáfª…0»Ô&P¾òñ¦ÂVÈ ßLxz& ik{9ä@¶ªÇ)Ú~œkøX$—QÄã1XI…o,Gõ"Êõ˜‰ÎÕ MÑ<»ÇA ª™$LÂÝà ÎhEµÃ‹:p”þ±÷ý´üüÏÓéôK«ÿ8œç&Y<ÿóó?O ÿ_´áŒ$3™7jŸðÀ¾mŒŠÖ¥eN·ç‰ûÍÍOP;QÅIÒZøh•!FÅaH„Ì…ò9Û²•ÓYxYbð=¶·þs‘{E¢ƒ´’%D9b{®PùöÂec‰éÜZÇåhµFA§èc!kÓŒD*åþK&’|Îl¬^bº›ÐL`•žÍ¹ÈÐZFqÏÐÐ7Çq i¬æ2“l¶€S<µŸágáïŠ[þæ†*›dÚ…ÈHZß,å9¾âÃØ8³‘á¡S¾¤ }I¬¯Ý¶:rEmÕ•bâ3˜o·Ì/fY¤eóŽk‘Üé¸Ô'TÞDCe¢1O]eÄ”a–´¨‚îœ{Ée힆ÃQ‚Þ„9}Áòý_§Ã‹ÅÉìÿöH‹û¿"þ[=þkàKö1Wxhž!ŽŠGmzw„Åw÷RÛhð8ïæ}@€N°rþÑ¢ÁÉBÊÊDÿØ!oµˆ%•ý~.o\…æØç8C&¾ÛL*x ¥.j>Ôï#§Ú¦ ØÔ¶6v1›×ÎË÷ÿ›ÎÿÿôˆøÿÍþÿ“,îÿ‡þ?§{ùÿð9‚æ#¨(¯’ÃjÙü¤é‡€̈ÞõOÌU`¥L”ú¤çM”¹h¥$ÎtÔèK-|Yˆ(hHÆþ¡¨$ú{Ûih3ˆ/WˆÙç„G|Ã`%®ƒÅ@oåQÈ"3Þe8”Wžjà~ËBΜ(ß !ʆÔ!ÙAI¯Ý¶læ>u››2ÙáÀ4r‘ò]é=Û•ûëó(À-ÞÁžÑdba %í3GdñF6ÑÂEH…Õ¶ ‰Cá¸ÀZ2C³ÔmPz{cÀ7|~ÊÇ_ný_L9ÿMògÿ—I­ÿ œÿ^ì5ÿm9êbjÔ%AS‚ ßû‰#1M­ §c²­achML؉ià—ð$¯Ý× Êˆóž"·Õß0aMÆ;dkP÷¹‡'ËÑÇJEZI‰3Cíê «Ò8þe×ÐEY žwß½zã§ßàX€ì„Sá5ÓµÿÛ÷Zsn¼èÚp‰ÁÁ•0 ‡QCExÔa"ör0-EŸŸIƺiµ;µí9¨4–“r¥>lMMvÒ¾ oç0êçšMËfŸþ Î“¨Yáο]¿sl˜ôˆœØíÌcoýËÃC_ÿ}2ÿó‘ÿ^Ìó_“,®ÿ>Aý÷Éƒë¿ R)ý9ªDYéüçNÅ4rß*÷uÑ<(5‡ý½,Ê\–°5)< jÈ aÚ,,j%3d°¯$:cP)QIêÎÜZ=7_ÔåJð-˜C wÄü„’ùÝUâŸq¾7Œ+O8A¦Ç­˜e7;l­Bž”¨óÚ­¥Q{†¤‡—A&“!2Q± óÊçÝ9Çm]“å°p Ù0š±D ¿ÖŠ'ñMµïŠ;F´ó’/“€ÆRÆúÐ'.³ ®¢´y¶‰¤w_hŸùŽ— î€ëqt¶¯bÅ+£k¹~YŒÕ¼¾pùþÏÓá¿ÅñræÿiqÿgÄÇ{á?Q*sûŠ˜ñ£æ¡Å¼8ôó£Y£Ô ¦p6¢’˼f%ÊÊŠ(7ØóÌRøPäCéÍŠäXXY5êÀih{£ü´(BHP»B m¸Z+,Eæ.|.ÅŒ£Ã{FCÛ²Q,äUJ´§cÂÖ¤Z·¤†jC»üÑ“sü¢$ûÅvTCM¤?4hÂñ]oÕ)½9aÏ£üŸŸyã@ ÚŒÚ#5OŠ¡X1n!¢_RW¿N^l?ôçë…j²|jûU…4±AÜ`¼ZñU/xÆÆø®ûgÖ $7¬ ãnï˧„ÑœqÜo{ùþß“ù?n™ÿœõÿ“,îÿþG{ù?ª=ª®FRKº¢¨qq'#ÔZà¹tàµHRG~‹*Äy­DǬ<­/T \ÄRPX_’ÄgQÖƒ­ûR(mrë1¦FßBÙ¦&eYÂr¤Ž“ j[äéGK^içáàýÖ€‚†dÈE¥5žJ<ÿhž¹ða3¿îpý¥_†™¦­;:‰8Ã3•]ü›=–Ôéq¾q•ì¸ãÓÎŒn¤Ø·÷Ö™ \¸3jÎ2Ý%`‡ò¬g¥äÅ‘œŸ# pQ¡É=ÄhbÖ L½ùåaóöØ`\ëñ¨æ¡ä(²¼løÈ‹üœ2 Q½2 ¡Sï¢/UzóÎxò«7BjMAeK9o­\ÿ}ÿéðßÑr5û<Òâþˆÿ–{á?Y4ü6à iŒ#5eD³ZgoÁÞ¤Ú8ÖHbèÈz("Bí<ßOL›¢ÎtI¿ƒ½‰§¤…’ëëõR¬Jv+zr 2ßü¸¹øé¾û*ÅB9w6uÇ{¬»Ú¬…qXôsùA²VNZQž}I;lüøÆ]V+~¢N?™\ 1òÝrÏœ…”ìÉá1Fbxi”eéŽôåó3¥¢oyœ“°Bβ²Åé×êÅ|zo V¸:š²s"Ô"2e>ñ\ ‰F—a^ta˜¬èpz~<­Ž ÚÌ„HóÀF@QNžѵ¢GšÈžŸ Zaöe(h²Iv$3ÆÍ¼Ëòý?'ôµþï‹yþs’Åý?Ñÿãðø¿¡ÂÙ_¶Ò$“™€G¥§G÷²r%cÆs=[ÜôF§iL¢AÒÔ­ ÓÓ„ÍYÚ¥âH7¯q†så ­58Ñ÷Ì”þ¦ &Lô‰y(›’ €N$yÛǪóë…ÝÊøb·Ùá;É©åÖj; TXh”|bz*¢‡nèbPǽºóé‹2B¢D‘C¬ÐŽ_µØ\ûlMå OsÌ·”œ1á (PÖðù™h ë˜ü&´–J:ým€¿þOæÿ~tÈü¿fýÿ$‹×ô_=°ÿ»kÀd%ieÆÁß„}’I; ²‰ƒPPã±Cº{£ŒúP”x!C‘§!]²= ‰ì I‹¯”ûÃV××zz©„YXW¨AêÑrênˆÞ¯3H¤yÏ;ÌiH¹CÃB©öŒHª ëQ¶ÈtsµJÉ/¢‘†¼‡áu¨F*—¯@ã_©Ìq˜0gôôÛ~3 ?úëk.f‹Ìañ¨˜¿—¡—i|¾Œo…€˜ùë;†ü÷LµVËFøÅ | [¤šc Ær¥„!óc£yM¾|ý×tøourlúËÅŒÿ¦X\ÿ…øïh/ü'v¼xÈ-m`ŽÈI\jzT ± ¦ßÂbÑ+gWÄgôÜD1Üj ¦`ÊM"Iòtz+¶ «šàh<ø’¸voF- ‚ŠS©÷yþÞ´ z]@&ñ }uqþõzíOï{ÊßKñ•ÚÇ=]¥ôú'ìǯmU¤ó€ôÚHIfºâþ6ðYCøc<Q«?5{kÂw‹†¸?÷,–S2ƒ™´î`èÝ“øsbNÓ_f«~µG¾UH¬ß”ª‰ ¼Ì®&Csz^R@ÉÓ¦ÎE0ˑϖf6º¹qHç¢I[£J±[h›]7Æxòsúö”—n¬þûþo‹éü_—Æÿíd5ë¿&YÜÿmþo‹½ù¿lßÕ$ ï5¤½½à±É쑾hdÙf¢ %÷ÒÒHÊꢜpÃ*q¢ÇaI‡dRD< [‚+ØmÑ`³ÓÆÎWªú:Ü@õ6 "S?§ÜôÚ”4l´wzÓmAƒÀiÙÎ56y‹êPèm (iLã̯³ªýk¨AýVâufzýòõ“›w—Wkˆ#;g[‡=Ïm^²’™³‘B”œÎü9!ÈmR/•ÎÁÒ“ã{¾"Ø¥°)ò Ötáôœïÿuî_éx’éPÍ‘=8„I~(0æÆéÌͨtûŒ™ä7 ¾ýîk8ÒBnü–œ={p‚ÀÏ›nþ{yrhêÿ<ÿ=Íâùo8ÿ}º×ü÷…<åFÕLásÌK1³q¶B,{‘m--²ÑQ'æ{K=ˆÝ&sQ:èã6PM ¨ýlr›Ç•”޾µ p„€¶Ñy¬»L¶ÏÁ1 5h)ÀÖÆ«ø]’’å¤)KåÝn†èElÅSÈBxd–¨Þ ÒÅ·Á8usîšY¤¯PI‡çôlœw©«f‘j JßÐlÔýF2iŸ°+bçÕ ƒ¦Ä¼ÇuF(§—±õ_Ícîì@È]®Ÿ½b ¡ŠŒñÒUhÈ€Üd*¢þ ÇO®@ÇÙô‡îzOQçýØuòº\üw2]þßâÐê—3þ›bQüw‚ù'{åÿÉC”öU‘ýœºYµŸxj QžŸ6yäÌå`2 Xš‡ÖùÌYG™â9Ä’EAjPãÛ·oŸ\]þD$ ôì9‡FѽLüŽ®ã0Ä ÈV]góp\"i°Ð>¡-)è(ŠÐóšsª"ÄÿPf¶&ß“5'\äæƒ¤Æ¬“Ÿ¬íæLd%-†ÊÀÔ9/¨PÊk9bÇ‚G{¹ŠUmWx»|Ñ¢A7>uô ‹¨u üüE6Á+XÉ=:n,dr‚$‹¥¦ê­;‚‘õùŽ&Ì©áØkEù;Ùm‚j^ ˜æsyš´ÿÍXÎkåó“Í-ˆþg1ç¿L²8ÿ‡ó_§{Íy 1ÔŽ{äò8hûŠåäk$2rÌ_Íë¦ “¤²ìl¼‹V;´G:+ý‘@›µ~KÂtùW’)g[ýä¸êɶ8&ð1üd1ÚN‰ñwPD€lÄ`fÁv¡|„7Èî­|æ,gãHHuH¸! §Eæb|SÕè½STå¸}ÓFü·< Óq¥ˆ?a)öŒˆ|ÈÔªu<úƒZ>'ê@ÇO±ˆø±7ÕßÑòùŸÉüÿŽ–‡ÄÿyÖÿL²8ÿƒþ'{ùÿúa’G]Œ’÷mFM±êÐ7È"Š«¼öyíh¦P]ƒ(ìÕNÝsWÇFƒ4♾)Hu4ŸÍ€Ó¡ö„C—ƒõU¡ÿ¹³Ä ètì§Ñ|Bb¤¹Êêd´º%m¶PF 7¯*랊´AüëƒfA#eS9µ¤)3œg!\ðAÐç2C§û¿Ù¨„¬óê³¥••7‰®QõX»ýy¯‚%_ä$8›Ô‘6Ð8“™½>Z<Üšá,iIþÞ+‘a¾ÒZ»cŸ‡Sd÷Žgçx+h¬ oÉX1¶Ëi#5sËÔ„ ¡œÜðPΙR/×wÖ̃٤ψíÑ–ïÿø`ð¯aþëÈÌÎøoŠÅýÑþñØ›s¢;šc)„ð4¢*E EBqàÙØ+T¹Á9ìMvî×D1 À3åñ“ڤ围t‘¤ IÅ×籪ºðûgÕ1q‰Ù%øÒQÄÒo—ƒ©äçK—\év€9#'ŽÃ!ô•Ý›´.àÙ.T,жPh¢ã–É”Vù*»FÖH~Kéµ\'î@æŽG¨ü´U½Ñ9îL ¹½ð2ÿF¥ˆ/^ž‰ñF6QŸ¨rrÃÃW±…DzÖlúºî4'\OÔÕ÷=É÷…í8Ÿÿ™lþûh¹˜ó_kqþç¿Oöšÿ÷.Ò_èO,ZþafÂr÷(W[ìtW†5ð…Õsž±u¾¤ãsw"ˆ5*!’^œ¦T;úæÉvïéX÷&=³&Xï&õ/¦ª{4¡óÁªÚt ˜Ó.~é3n»=øA0‘K£`sÊ&¿[_EªršŒÿ‘ãqGÙ™5¥îÑŒ5BÙŽ8&!ß-¿ýV'¨¸Ïót>DFXÉT nŠÔÜÂÔj£ô­ØT”ðMh6.t’? G¬“ÇgXž#b ßøÈ yŽå1ËüÞR¶­»Z«Iç²PË )iM-¶;¤¸8…ñ²6Ôž}´KtA_ˆÖšëz‰@ ç ”šð± áŸtùüÏdþGG‡dþoÖO²8ÿƒþ«½üÿȉœYõ©Ø+é“tèq)í9m棥BGéá'væ qœÛEnpì‡;?÷I±`PJu©óTGÛ|ñUAnµn“,Z®[E,ù~¾/çoή]žˆd÷N>pÝÏA;Q¡ÿ/,ÛVáÄþÊE²¨ä¢]å®T@BE;äFÒ,„}daç¤÷2|dxvˆÕµ°×Iâ´9s’Ìo™õ(q°SxÁ4o-ÅØˆÌÝ!8‰€qocC ±Ö]úùšO Ôv†•úh6Ÿ ‡ÉR‰)0W³âÍ6üÒö:ÆÄô®gظïòõßÓñ§'ÖÿiÎfqý7ò§{ñz£–ú¯8îzǨPúd… 2t4 ÅÐ}ÂöÓBcgÖσâ%½+%•S¤MqöÄjtuu¯åÞ‘%ùF êÀ¨·»ÐžwÉJÐZe‡äÝFR”Nà²Ú(“rI~U›Œ<²™tsZÅ&ÕQ6{];° û œïjªdšd.o$è&Ë0¤õÍl'" r´êv)•ÊŽ\òâ1ƒD£/³ÁmãÇ’¾-òØõÅ!q†©Ç]1rѪËÔ¤ôª nBWïÓ:.¼T>À]SÉÔÆVL-Ïl™Baž|&Œ„ GqªëÐÙ—ãuÓ¾:Ú*Ɇ>”¶ÇQÉi Ý•²,³±GO÷¿<²¿Rý÷ý¿'Óÿ/gÿ¯ÇZÜÿõÿÏöÒÿÞ5îo€® “ w6 jOНâåÀ½µó¾»ÃD¡´¶Tźޱö‹6ÅâpЈ:²SÖª­{ãì+LÆE:\ŒLèÚxÒ]ZvÈFl–³9»°Ø·‹uqn J’ʺ·_Pï`Üåê“Ü¹Ì Ù9ê¾xyöä]û+XJvdˆY¾àQ‡ì–FM(ÏèÉ 0Â@¡–Gá_/^ÿ×Ùk ‚Q™à¦fãæ83ë´Üú|:aÿïÄ迳þ{’Eëÿñ)Ôÿíîíÿ©vI–¼Þ‰åŽû:NúÐ nxÍØëU¯\Œ^Òâ)3)›SëÇ~ð#»•ÃÓ{6”^G…0]Ìdx…yJÅÌz§Hr¯°¢‡—èrŠ“)ó8‹ÙUÂŒâxK™5Õ†¸QÙ¶°;j I©JÓ†6µÆkÜvÍ‹U5nH,ÿDe×å§Øµßëe…v­ØƒÆ“¢0â_ôqŠËLá6ôæ×}ݹφtàÍ—«MÖ3ÖŽmîŽB- ’x–/:£².^ЋR¢ŒÍž”°…>–iÂãOŒò[iÿØEôw¼üü×éüŽíüßjîÿM²xþ+ò?‹½øŸ@ØVL²1”9™…E:¦óöÍf D…g݇íÄóAÎÂéÄË åS¯»¡ Iˆæþwæ¹]q‹ÐŸ$옛ãÐ*p¡L?Äi®‰’*Uv‚¯ËX:"*À~³îÝ`rO”‰’e=!¸}&0G3pòY¦‰2¹'œ{äªGÞ;ÇCņ‰sqÂiŽ.ßöEBbÎZ½‘YÿíÇ _è£C¥6Ó *W 5»ÞÖ-RLŸØÉeÇË)ê£ä Qø 2ýt ”öV°bÎÇj0é>˜ô±3~¸&Â1ÿþkÕÂ'õGÁãÕ.v ÿ±Kèïzùú¯ÉüŽNþ›óÿ¦Y\ÿ…ò¯½Ø?:´•NóBñ¢x¶lƒ «€1Ÿò3ü?Ý ×]zS–Îf@DÞÀšÖ‹Cµ"°ËÌãÊá%Y€YȄգ8díΟU§xêA½^‡á߯þ ÚÈÚqƒœÎ†éJ.¬Œ›ú]iÐA ±02µ æü<¦ÓKÿ=\üOCþ·ñÿ?9šù¿IÅÿóé?z®ÉgI™”M0iRKvkoÿÁ‹5“rÁ‚2<ðFŒÆæ/nÛº×ÄC5lUwÌ_3Ï&‰B2QñÔ¼¬«œòµnýX©5%°uÖ”½ÑÂkÙŽ®þByû@Pe „™¨Š(v/îŸÚV'a¡ð¡UÑÏL¬ûÌÐ,‰@# jFqõ±­j7L 9ôžý Li•zëž$V=¤è•?¯µË‡1Ъ´š÷¥£‘똜x}JÙúë] ýG0TT$xä2Ã1]ÂÈß¹A%RÜsè™ì±ËånùøoºùãÕ‘™ÿ\Îþ¿“,Žÿ°üt¯0ø ƒËª<€Ã±-Ã^ˆH+·n…"Ým”zj‰œï9Xw8ªÝ±–oì¹›]:{cn™z {à‰2`þ5ªŸ;€Jºâ™þ‹ÑÈ2ùÆùn-±T ìᱺ:V[©Ì…ŸD5Žú)êGQèÛðy.¢u;üX3Ýp–üBuøl„7eU£n„ݳùíãA¢À"Ï[½¨$E­,uuí LdÚÓ¬Ÿ¤vÝ\·›‘¦Ö<0Ò¶Àsúò²‘~…76µÞ¥ÑyÝXi™)8WßÏÊJ_çÎ6ÇZ‚»´øtþØ×d–“j ýIjdhË“]/‰EòûKõý—“ù,O,þ[,fü7Åâþ¿Kôÿ]>è˜ùÊW×TåC,ËÃûæ’|v!Ô®ÉX¦Š„I¦8•7§-'ëtïâh ÎîªåÉâ7½q²÷¥;òôUpkR踒.øs˜ëpâ‚äóöe£x€ØŽ‹Lk ßú¾€IêpEó(Z:2 ÎËíÇnžqq:®,¿7‚ð´™F³Nèú¦{ư”aÌñ 4cèäÓà :èùRN‰Ðœ¢HU…G &eüÉ#xô¯Zææ‡ñawXuõªCËÒ?v5üó-_ÿ?™ÿÿòtEôÿ³þk’Åõÿèÿ¿ÜËÿÒû¨¤Ó ©=§Ã’ öR§ÉgÉÑ@íZÞ¬ b]ZªÑ @÷mX– ë¿ø6QÊ%Çc©2é“4Ç«æ$U 6†nbøûåë—eT’ó/šî>w”+n¹ ê@ÙI+ý+O ã‹ÎZd‰RàÓÄ/_ÿ¯7›'XÔ8Ç*tÌ©áÐRºKiòÉÏPËÌ5TÈÜ nnxÄæ¥5JM[&Ð$ÜF)èÍ#wB Äm*b\XÆû|Þ @2xÄD¼CLóå9` Ñ^Ù€AuNp^>?á©X>P0 ”ß|j0\ÿìäëD30ÅÌWõцñ¦eB~W;VFEðÏ_/_Œ¶s™L¬vç½G7 c>XÝÃ+¬úØ(ˈ"¶¶<2£=’Ua8rÊ{GMÕ䟨„ð%¶3wgË=”ŒÀÅ{ì{' ;IBd'Vž4ýmiÐÎFÓ/Ÿÿ›.ÿéxEò?gþo’Åù?Ì:~ü'ëÛP&  ðCUf€YŽw6Àï+ݲ}=«ziôw$tFÚ*­‘¯ˆÀ‰f^åäí’¨ È´x«×˜-Ò U™“pƒ6ÞÊxðÃIß]k*Æý½œ„€Qåß¿Ì:~À>Ä uuDÓö=àjE‹ßÓãék[t%Ù8ÞnÇ¥0ÊTâŽ\±TÏÇ–t½ñ›7qC/ãó3Ín{”²V=:“ÿζ ”«Œ¤É)rír«n$ÅÄ–Dæñ£`Æwü4ÅùÅLÒV’U¢CµÖÄq¶Q9÷ ÷¾K¹¸>ïŸdæJ£ç­Ä,Rº®ÌxÏлîá'°'k:þAJ†ÄcÛ»x?ÀòõÓù¿Zÿÿ“åŒÿ&Y\ÿ‡ò¿½Õ°û Ò¼}:gl3ÐUÛUK”rÝe¹Àœ7”[á;0ñöí[FÃ4àä)i_„“ …‹gY0Dù4 ÈªYs»î6„BËÕéêç˳Ë#O·øÃ샀Y¸Ït%šG_â.™¦Û®ÒyFõX¡®Þžzòqóîr{÷:ÚÐBõR¸G¾™GpíÈb‘Ê.4› ‰ Èãsõ‡ÏÁäka‘øËì–½ÉI‰9º/ñô&GÔý£å»44®y­D„^"U!Œ»6Íë×_>ÿ·˜lþóø˜ôgü7Éâüßù¿Å¾  S0·ÖÊdox36 ÑxÍ;–_»T0ÑÔ Tį ”†qŒ$ ³ÀL¼ç¤d®•¯KIóP¨×¸ÆK |°ÍË[€TZ'¿6¸I´ ·b§¯pºL=š¿=±Ö¯°üüïéôÿG‡ 3ÿy8û¿O²xþ7êÿO÷Òÿ{ú:…Øì!Às­Õ¹ÜSÂfì'Ô~Éq3vÅÜNÙÕz™,äÎFì-1ˆÄH¬hd³òƒçédsh¡ö¥¬X}õÀ¬Ú©ùw:?G&€ŠÄí‹W¢힟”*1˜ý¥ÀÞ 2— œÁ‚?)ºq™Õl‘™¿S¯ÚYÈQQo¾”Þ^³\Ðbtf2ºrOŠ;‹³m÷5èR)WY:êm¹«³îúo?A¢¾¤å¶ô=­Ý—7ƒÊÀ@í6«‡ G¬ªˆÅ›üˆ WÀ–Í®dw}Ñov a]ùd)¡1<ìkÎ\qÁZJ,™½ZSþ± èï|ùù?ÓåžXÿYÿ?Éâù?Ø|ºWP(Œ*i ”艚SÇ8°O±.ðÌŽ|nÔXžt‹ØŒSä•Û¤Þ—¡|.Aãû:ihâÚ­fŒPÜg<3^êaFšx8Ks^ k29c"Qk£Â…ií–i¶ô ³¨ÃáAA¨£š8ùs3‹…ßë Y–0/ù>Ù'=á6œ†G=Á%#”sãÉm²”DAµ W9£©´å8HñY ‰÷h‹Ý¼~VÚ\y„¹öF䙢0Kޝ$êÇNЦP™úŒCÎNªÑi>3x¯ÄÑ ¼UO즲ô©w?Z>Mbç%Û¯ëxÿÔÒÅϦëÿ­WÿÎøoŠEñß3ìÿ=Û«ÿd^N Råu”`žXÝѳš}¦/ 2ÝìæäqÓîÑÀœH½* ° &´EdÐÔDí›ùzçsö&®÷„ö«­›iêÈb8굻Ȅ®ƒŸ‰ËÚÆà³¡0ЧÀ £ðŽ‘‡b!Õ¬fà)—RoH6\$³>5þé<Ë:ÞâyT+>©}á ”#·nŽÏ„—ÿ@©á®k@ÑL‹!?+e}ä,ñJ_•XÙ2H¢†^lïÿç-ÜRýgü_/^ÿ×ÙkxI™,ÝÌSD¬Z7âÜ„jœÊv?‹!s3ç0~ñþïëŽ&ãKëÿ5÷ÿ¦Y\ÿs„úŸ£ýúimÜ' Z+÷ ÷ÏÕÿ¨×P }‹:Ž¢݆ ºEÍA0@®˜™ÜÖeS«š²ȶ¼ÐsI(mN×_ì“ÅÀ‹©´6ÕØäb=\ÄÍ«“ýP¾^¾ð9ãÌ_^´}©D46HÙF Zîd“Gú½xyVú¥ôñMnÃE(‰±SŽ:—†8½·»Ô5sszòðh´Æ ÷àx‡Šh.W@0çðf)]gwÚ Ìw3º@ª3Cz @&Çr3ŸÌ`%<°Þm·s²?ÿæÍ?Óà·}­:LgÛ~OµMU›fÏ΋è)¦ØhK?¯‰–¯ÿšÎÿ±:´üÏŒÿ&Y\ÿ…þÿ§{ùÿK#h'WeŸ:ž¿/+,ÆÃž…¹ñÖ'´>4¸-Èx\ç¡?>k(EŽƒ+ãü¶È`Á*IL*N)$Ä%³$]‹ +YÐqÓî#äÁ·åg³l™ÓÜT5åP13›"IÖ–`¥›ž vlÁH× ÀHг$ ‘™È)+’ÒuÆ~M)¸‰°Á‘L·o\&Üô±·Z©¾Ú{jäÁLû¬ŠYÆÈO ËUj: 1BI. w%Æ Tƒ^­}‘Ë<Š‹+ÛG®­œ@á•.c XË6tÖI ”o=ëNèÛ+ê{ìº7¯aùþ¯Óé¿— ãÿ°šó¿'YÜÿõ_‹½ô_¸ ‰Œ tmNtmÿ˜]F¶Ä\‘Ô™c“ÅQ¤s«ï ¨ÙÚI3=„Š´Íô£Œ>ìAgCÁs^깼݌Cn0š±zÐë÷¨qbÔ"ȹ©‚·#¤©Ù·ô‘ö j¤¤2ú)ÇåÓgУ$Žš\8šXB·°”©óTwß[§a¿Oûª€Fùvó0üŃZz~GnÌ#Ž êÄ©"ÈÖZÃT,(S{L™ÝY”΂~·üö[1&"ÂYi«1z¨çcéJg8ЉV¶Ú!”Jîìu2Ù­v‚sä ÌþdËÏÿžÎÿëôÈø¿ž,gý×$‹çcü÷Þþ_´û7oÆ9D.È-y*”e¯ßOG&ZÍ8JÉ¢uÛ¤†éâ+ª8$ûû¦êhGÁ|m½¢Á|Àôß­ø\5Ò½±E'†ùE¤†ã€•x‹šPe„©SUAÉܼ”ĪÀq…]Ó™7‘!»Ø¢®¬ÁP2YI‚8ôÉìRK–¼“”JTëÌÊÇN(• £ZÓqS’ÖŸÏ2½£ NzrÒ-ÕÈáºÏÍvvtøØ".wH·Ñ‹E¾ß®oƒ” ÌÂØkï’pòü3¦\^¼5‘d•ê‡ *q³² È7îb®„Ëû&?·†ÓÞ­1Ç=9VPq®¥s€R…¤P—¼4–DËO¦bõñßtùO+’ÿ¹\ÌøoŠÅñæ?-÷ÊâC䥓EÐì¨÷| ¸B¥Â u-öþ·„T‰ïÂa7Vµãmœ%ãGà“JnÍÍ€c³Y]EÏœ°Û=Çäx }x—³Þ¬.® ¹››eÞJßY)yiR`œUM˜·X¶Zb(NÀ$Ð -4u¤X¿8 [¯Ç/F&Ê£šÕN]|y1Æg[wå(Òk¹[2«³´N ý í.žT¼‹#ܰ¨fºVHV ve@æÖ”R´CeéVõTÀ ²sM _¿|ýä§õ/x†P Ÿ d¸ÛDà&æûÎÛGèKÎ:Àˆ*æÑa‚‰Í` Èè!‚ȭʬiŽ”im,!«90]­˜›ßa\}<“ºJá8Ô]ö/ uÏ‹‡ÚØúívM4åIñ²e¼zõ&ù¼ B»–a‹¤e™¾n ‚£' oÈ7|”ùõh"£8UëÏz‚‰üt>vEš×”ËŸÿ}:þcùŸsÿw’ÅçŸâüïÓ½;À”ÈZg›2Ú¿Ah«J®ñ.!•;{x¸™Ä€$Çdç½<ܘ¬ÎLl|vî7÷r>#‘ ¬kZI9ñã¾;jñš¤q¿xÎRp$&ÊYmcO¦ ‘­IPG‹çx¨ANM*²[e„<ÝÞÇ#!–#ÏÃó.oqÜ’:ϕؾ¢¼»Rh‚Þ4öÝaiu S„_ å8Ë š]“3<“N! ¨X##å$ï΃ '• \M‚n‡™ŠQKªÞ¼;+’Pk髻ʹôO¶:GyžgÝc×§yýºËïÿM˜ÿ¹8žýiñþú¿îåÿó:ú8ì Ãª$«ÐñR"{½ûäGÕ"×{§Òó±5óFED³4)PÊC¹#§áœíýA¯-aW¯Ô°xS˜˜µ¦tBë£zL÷¦‘(œš&ä™ÅÑ ‡Zºw“ÏУÃó3xÚkÛÎ>Ïæ& Àw‚”v)'xò>«Âêz°Í<èD15@QK͸ 6]ê4ÕˆãøžÊ³ ±ÂñÆ!{à­]½ CŽ%á>š<,ŒÙµ„âÃZ˺>¢x먛ÏAŒ#=š‹%gªêX ŸÑ$3&r,V„—Ikjf9¦Û¡wE©d«±¤ G:€‚Ê"D§˜ª¿ål}å¿O§ÿ?^Yü7÷ÿ&Y<ÿõÿO÷Òÿ7å,æADyªÕCÿ¶$ëX:À(3ë*î¾ è³&1R(V¯MfƒÕ>L£ØYIv(¥= òµºûŽ5áíæfûÕßnÿ@¯‰ËƒêÖÅ–'h[WwÒî•(ŒX½dí}ÄIváR’Š«+IÒŽ-0ÎçøŒ^é™4”Si»• 5ð¶ÿÆ×6xxþzbÚ¸\Vi¢¡2NÍÍHŸÜdª—Z+†9ÆGR<)¿KEÇç•8†cÄLÌ%qÕóÍÑÙ«|T=#|ýËQp%·ìì…Y°nmæ;›ôäÓòŽ-upDÁv¹ Ö¹lÁw÷,ó>L=@š"ãfKRKNÙû žTSo¬åŸþùï“õ—«…Õÿ/çþï$‹ç¿cÿ÷Ù^ý_²ÐôwÖþ1e¦»˜±’#µÚy²­œªå¼Ùˆ!ÎÞ|ó}Uã Ã…ŸÙTÀ <ÆB-~5ÖÝQ·«&ëЗ‡žžÓÁ/èü‡lE3’Ƶͫ$yL¼s™Àµ,î’S¶À}ÔMÁ\á4$ƒ \G bE yw>?¸±r¹DQv¡jlèp«ØÜ~b®ÐqÁ ¦œ3Èæ†›ÜBJØÙe%G´ó™Ã(Rßþ&áÅAØ:È>4Hò…—ß%’ÒÓàãäsÕ_îï!isëÿÉb:ý×)Ñ-çú?Å¢õÿdõûŸûôÿüY!ªÔ5d²=Ä~*øÃÝõ¼{]7ÎTxœÈ$šÚJUÕmµëŽ*K)`¦²ÇÊèèÇÃ=“E“¥?óÁUö$bGò$‘ mÌú³ñ¦Ø]TQa"Ì6Ù k¶t-»3êÉ©96€pY`š<±YH†ò49ª–P0þ<éA9yG•Ž~ìr©ÈŒÒSLH€o¼2d‡Ä-©+ Ú>wéØ/Í„løŠì‰ØÄK$É+¿h琢³Ëú€i£~ô•¥ ‡WH!ê²Yj™–á±ëÕ¼vùý¿‡ €ªâ?Òÿ›õÿÓ,Þÿè§{%@ j¤l‘`u8Y~{MD89ŒPѵï'»ëÒÖV©ÎÑU qÏ&Hõ£p“ÁC•C2j/ß§¨Ô¦$Ò:À{‘ ŸM‹[Ãwœ°nµÐ S®¦@ätBÄHg}ÝOKúGR-Ç5þnB޼þÊ][E'O_ˆÔHfþb²¤Å=¹Ú©ÊÆ,Šaô<9ò¼IFï*9“yïE22´]WþÄÞ¡]„!Y]0p½­ëçxñ:g ž $Š•Ó?1µÌOø¾þ³Wßzùøo:ÿ¯cÒÿ›õÿÓ,ŽÿÐÿëé^þ_4O†NÓ£µA˜ù“êiéÎõh!ñVhºIZ.¶{”vÊ7?n.~ºïþÏÅ@5U0‚,^º[r êÃÿŽöL¸ŠÂU…j/^žÉ4õ’9OñDªÓ Qt‰+?ªŠqQ‹Þa¤Â\SoÐa#5·Éû‘j˜Ä–ÔúnÊQJ¨™}Œ†–÷"%[¸Zpø{”µÌÇ ÙèçŠ27ŠÕ® õTT³WÖ.ÿ!]íÁ–È&¿|EëA¬fù}$ÉI=³X$ƒ?zÁùbi4#â…Ú©ŽFL&Ù­# ÂÊ{›Óµð«KDÄ ôdq±f¿-{‡š­âøòœJš¥Ù¤6o¬O„ü 1°?ÿ9Yÿwyxr:û¿>ÒâóŸØÿ=Ý«ÿ«õªnñwAšËã²m RØ-ì9Õöö\.x׈á× À'™$k‚TÛ5Cûªçð±pó*3¾UÎOHŸºÇ¢®:I丘շ÷½šÆk©<žÚkøB¢Ÿç7ιüJø4xyBåöú¯¸Û´Dq,ªÌ•Hð¯™Ñ°Ä6dÔv ÿŸ¼ëN[å àÈæ²™%ÔQÛ¾×û.*¸¦?çê2Bç⨩ڎ·œÎÙL—–ô0åZžç&e8éä\³@òHy–Ø91Åt² I$÷Ò0ÏØjêñê.lfÌÎo·×p[¬aÌa›˜ÒÝL—\4Ï ¨æfýQ5:|ùl9©¼1Áý׌µF™4:êù÷Ñ:©ë[üFÈÔþ—›ùÛ1Fqû‹õ¾±è«:JUú‡:ƒO˜…ùEÛ\ØðT$dC¢¦¨N¸¥õ1EÙx@@]8-rØ`dáq ú‹hq‡€FŸÿ™ÐÿóøÔæ?ÏøoŠÅùôÿ<ÚËÿ7у†©b1‰ÅÞ†:±!³5jp …Ôê0•›˜Þqª¯ˆÔuFËL I:¡‚<ÎJ /5ÚNÓ¦5êÛûåQœñÅ•+G„C‡’XÕcÆTä£G˜+ðR9æEý0@jsðËk†Ak4óaåy" 6ãÀê8fÃYŒÕûbßFG2¦-Þj’rz„³Žµ\½ÄîØcŒt,Û}ܘ߉ۑ/=va#´¦Î–e¿Þ*÷‘]Μs 7ãä´êR¾]¿{ruù>JÂà >ÜhšOF°‡®ÏÒzØÌmYtwd“:!öàûGf}ü7¡ÿÃâÄú?ÌúŸIǨÿ9z8ÿ‡Tã/Ôy±«M2eÀf†­ëÌWç’èP¸q–ެ/C@,³º‹¬9‰Z6'ɳ„לé}ùn/‘­€:N `S‰!•:v×eS±vT,Éi3+¯oÁƒ D¦›Ý¦WmÁÞ¢”Ûì!)TN»Û©&©Ÿ—Ó-Ç8l§`Ú\>éð'À£³1?‰pFšR%Uc±Žq©ˆƒ{Ù/à°¨ ÊQGïz¹Äl:oeLÚå ’…sLÖˆ7×Ù=€k=±¼£$9‡K7™ôPÏò®?ùòû¿Óù?œœ,­þûtÆS,ÞÿEÿ‡g{ù? s΂q!¸â‚ÈpŒ[5WÎzJÜš¬ÌìøŽ¢˜ìòVÂ{´šô õLÖ‡’Ôeîí5ò¡-&äwGµ€³™0.7x(:i)ÊH»\Ù9BÙÅÁUwßä`”lìÚñÂ/èJcÄö8í/ ,’¨b x{­¿Å¨áá׿ÅL´ Y8¾°Zt¥ Ä+YIM£)Ö\\”RîÙÊoŒ¾Q_0?»ð{¿^~ÝC>2ÆÀ|r6´o¦«.G Nèð&NË>3¾0º{HÌf.³ëµb·{ß~¨åÖÿådþOÛ?¼œû´hý_¢ÿÓr/ÿ§û`Óõ²®óÎÌØ}[‘qØ™náét+N͆,òþ,\±ÃIÝFíÑ1¡‘”ï8(º‡]v©",’³e!Ðtì¶ãS6EeÆÐS,Št’ü‘oPj¤9éoX¹‹3Jí’ªÚ[ M*‘ߢüWEstpÃ:†0‘$A^¥È§©€8g¶SA=áu]5•’ 3>cwïT-¥’èp%º~pÞî_9S’ë×½Ïs/äU€eÚAÿBkâD.ŒjÒ’Ü>K>!‡‡’T5©”ûíÿ¾ÿãdùoG 6ÿ7×ÿI÷Äü·§{å¿¡JWíæŒvÔ–Ô¶¦-uœT~>×:k36“FÊTž“ÈáË%2ýÍg‹H@dOþ¸" Sync Mail Dir syncmaildir-1.2.6.2/misc/smd-applet-1.0.0.c.gz000066400000000000000000000502341262303536600204260ustar00rootroot00000000000000‹M_Nsmd-applet-1.0.0.cí}ëZI’èoüÕ:§Ý#lIÜŒY÷.Æ2Í.°»û›™­-I…¨E¨´ª˜Óãw?‘÷[UI€ÝŒ™o«òž‘‘‘‘‘/—ƒìj°M&£8цñ8žFy<z·Áu4ŠúÍ ¿ˆƒOägÐO¯&É(ž> –•‚çÓôJmj5ƒAŒÓ<¸JÉùm°üòÙ³—ËÁI<Š£ŒÔ™ñÎãéU¤çÁÞñÁõjNƒ(nÓÙ4H'y’Žƒh|ŒHGÓà:žf$åiŒ´u˜7Ñtó$ÎXÚn:¹&Ë<è´Z¯V:­v+莧I? ΢,K‚FÓ8û·ó8ÏgãwNIX¤©´Ú ºÐªÙT¸{°szZ¿Ä0›Ä,Þ-áhq3d³û§aù !Í1HG3ådmÙ´æº×åÍic£ù~øñÙ>ƒ•!¯gý<1—–ÙvgbU¥O±ãirM6z ~l‹‰„Ãð|Ç­úu4%3 ƒ7A&â'ôáǃeî¤ EÀp6žÆç¼*­û†–þÿ ^+Mª•ô¦•¶)ªÎÓ°RÃlÕ„Çq4®N'qŸWÿÜ6öÎé‡wáÎññA—`+¡Ã!¥Ãî]$Ëι•dÅ&ûìÃÙðbËÙ¾c‡Uœ€½ÍdE_ƒ‹m8g»J£ n½BxXø"JȲö&Ó›1Z-(Î÷¥™ íÍ«YÖvhé6“U¼›l^DÙE˜G½Q<ç&6+ôO§ét¾±Ë*íNã!™à|ÃVê´|åý‹0Ÿ§s‚\¯çíá¼?J³ªÍÒ£ib¾qŠ­’i8Ï0••ɯÜWv’†þTX ‰¨FpzÎÉ:êz§­Ð÷yðʬf¶/ gWAðdz¥îoûgáÎÛ£“³g_‚îçD™.d1I ÿwM/ y¦Ë¤úÞipœåѸ"uÖ—°ïígK×)‘éÉY"HHy2 2ûp¶%MX&äIí_DÓåà*βh›ß!‘ïÇ$qØKSr¾“IãB8Žó›tzɶ*9ˆÄvw^NNˆÛ<ßè—­”üŽf™ÌM’ÁI%“#’Q8Ž®b=¥—nIÊ^ï“ÌíÁzhýê*Hå/„Ó;)^úÅ`Û§Dœ6õe²PÑ(ù1A º&ËY<:oèmR´+’ì5SkÊ}q—µ¶8 _ogW M^ÔkÐz~ùv–ŒÈùt9èÑÛ˜úì À¹ñìÀL8¡ §y”ϲ}‚äTд_“ñ ½Yn’±ž@P+´GéP&îôÒYþ.‰HârÁ‡ÌÛ½ˆû8€ý<¾"ƒHŽB^zÕKߦŸ *õHÝÌÂ0’¸ˆ»äp¹‹gKr¦‡“&$ž]Lãh°Lèð¯¶50…PRì똀{’@½ÀÌɬ“²p”ö/­niž¶ Û¯xãÂ%rØäy{¿^¬^ìdÿlב·㟑OÇèÿ:žöRµ™BèìûƒÓî6é‘à{?NÒ¨ x¨âj@)õ6oÒùŽöNÃwû£ì´¢ZGÇáîû=½Mó×:>ßuß›õdª»¦ÂKàç(M'aÿjÀ›ðdWj‹³¨ÄӘȯÔÚdF$@ÿÈx¶hË\6¥¼sížíý'ò?•"D Ñ î͆A l–Ç"% ä@ž,Ò8 ò–´“£«”Äâe@ãè*ÌÈozFå#Ò!ÀÎozRÒ¿4’z£Ù´× Ôì< 2I®ޢaœÃ_>HÔñ„ëvï¤,ÓŒ¦„ë5;`ëóè2ž£¦LÃÚCÙ-‡Ö@gbê—ØrI©µ%ï>~øð{x|rtÜ=9ûÈ2HFÁˆ`q#jjØ3›º8³,79¢>2F¾4ñ"ÍŒ&}‰@þõ$EÑ3„0âH‰„¬±S 1§€*à¤ïš Ç,¯Ñu7a f•¡2‚¬FCÏØ÷ Ñï†B¢iëIÆx ÃÖ5 ØöW6—#DI8Ÿok‡| ²Aíe/¿8P»šVàøãé/J :5—šàã>)•‘ÉÅ/³Ûqf@Ž+L§þRQ¯Ï’Ú3‰*¦ ö`2"+}Z¢ö½A™³ºƒªØ“w“§æá¸G-t|²ÿiç¬[O=j!–Š¡Bµ“^É9N2¥'›š9†sßâ1Š !™D¦ ÜKHÓñè–ü¡û'sb04A6#`®£ziòÝZÓ¶[8Š®zƒ¨8èò«2KçF EÕp˜_ Lª"ö9$¨7Æ£A‚Rpó¤,¾£Me%Q[‚F}¼Žªã!H †<¡¿gŽ®¨)pk’L²mØhycecÄÍÝ £qöp—œä:ÚòŒ˜|2à›dèM¨lœK~ 'HWF­ôÿÌ ò ¡Q÷sµ­»]E×E׳t8Åb8U1¶C16ÇÚ|Hôkàhwž­Þi`«00kMàô{?«+[zˆ EDyŠÁ“çl Í–€’Br-Ôú¯¼káN$Él4š:ë NgýÎÓQG±AG!u•DZAÇ ›0!UmPí£78/FOcTòt÷³Q4 §3«Ý“ÙdO׿TžÑ&ãzP]™—ºY¦çô+28©“AÕ½¢ Ò 3!…/WdÉPu -BÆ´µðêoÝuõU‘g÷èð}ø©{rºtÔÚ/Z/ZN‰¦ÝZx¼íÖ¡ël2 _‡[–ŠˆÚn/>‘ö}N›ÊPÑŠk8I³©?¶/:Éç³q_tDÕµ÷–ƒÏü9òrŠI> Jƕб 2Àp˜LÉ©…_y'£Šb¢€>«Él‚3Á±«še &G¿´!@•)0iAS^Wúª=tmuÝ7G¥ô%ñÕ,‚‘]$ƒXí†H ]8‹,/S-²·™"¥Œ\öJíûy+¡øBNp 4ž¦–¦^’êÞ ¶¥«ÿ˜œE¥ ¢ÝZ9†£©W¦CC=T p™$úª &ó hXJ¥DÉp˜M¢"f\$£hÈ6¢$ Ê^XTbk?ŒÈG¹çWå’ñ\§@ŠŽ¡ Éìè¿å°tŸ¿š=¹-v’µ%_R¢9‡Øë< ÏQéê–²£(‡ÿ;Kr†•<®©Xm–ÇÕbèB2²uÒÙ´¯qª@Y®‡Ñô-ýž{¶„}‡l_LàµÁzpâÞˆGa__>b{ͱQ¤ž]©bëÚQè>>é¾ßÿ-¨½œeÓ—£´jÏð$‚Ý9N6¨#7á¼ Å"ò+ÅcBÅÚæê`=°$¿%ð ¢Bz«à>¦³³ -C[!yG ¢b3`Uqïô@{î«y9o’Û¿š´Ô«“`ì™Ú*‡¥IÜk{o/A}<%|.½iF&þ!í3¤æìA½W0¿f¤Å²[OhN…Ðv˜œ‡ç *®sÑàf½´ýl)%z«Ž•–Î'¤Óœ¤f9AK‚—µwÝ·÷^?fךʽêÒŠy—+MÁhÊ$nÊ”á;ø×@-NzÅB¯Ù½/´ÿèïòð¶“N‚œÁg£\\kódi«æµBí³­vôÏUýsMÿ\×?7BËR’7!Y` “cp9Æ4Þ¸‹Ø«–e R¹8,Uõ¶)ìíò`;¢Ü-cÒÊÏ|c¼ êtȯ!ì¿ÁŒ2j;Ì~&HûýÙ”È3€ué=kð:çœä_¥„â.Ek¦°¤ê²HÆ>ô¾é@Ûöîkk²VÛ¼UÓ莴wvò±K3),Y_½/zŸlô$±<ë‚/ kgÕh‡æZ-Éd¬ÃÚRvooMoO)a¶ida]\¾iy«ëz«"ß‚­šõÔa¿ó7-B¾³Ežõ²²7¡A“Š jÒ´|-\e>¶ ro ªKÓ¤µÔÆà>èØ\{{νJFÜrØ®4 ‚߯¯¹±ÇÖïokRìÊM4f=×õ¿Ê­:×ú¹l>îcÉ£˜ÀG3Âab´Á‘Y«rŸòËà³a^pîÕ°¦`™¸2£«Þ(ÖWTRøÚ0{ɇdó|(Ê„\˜¬ÞŽûÓt g›$¿ âäk”'Yמýâ©€NðsТBª˜–€äHÄ£,ÖËð©¡Í1ÔÂ2ª;[Ã39³°l\&²ñ Ã!o Éô8Pšê"Ëõ-ÜC•)y aJ‡®yt*ÎÃ=äâqR‰@¨) ºF´Z0¢B@V„Þ*.¼¶ƒe%Þhý¯ç0jÃ/.z»Î¿ÕœvùNÅC“ØzX–ïȲ?¹a°uFÑŒ}‚Yj}§a¸²Ú¡±Ýí¶d»~Ouy*£²…  Õ„ÎÁÿÀö†/Yƒà ûdýóXØ¡uÝŒT=©:9î}·†§ø o«fÅ~Ž¡×81Mç°aüµòó Ê£¿¶þþâ:äê7Ê)Ù×( zˆ ®¦ñôïiž.°áIë)j*¬aôÓÉ-†nÚ—Mû!3äI  õAŠRÞÊJnP)Æß…éH\Ö*ãÅnMˆ €I_ŠzNëF‡|jKÍL>u(£Û¿s½ùµåX/•1kÑ]VÖ̤•x*µå‹¢—u¬‰´å%Å€Ó_nU·££eÉéN÷y1ä¾ÜŽP£©dLÚO V! … €/œÙ)°Ðˆô¿ ŸÙ ?í|¤nÍáá·.C{ÂUj?Õ¤´Ã±†úÃ0d†°HTÒ« Y!„¸­éû“£Âà”O­¡wÆûiT™›‡5£gxÒ-îçq·‰“ÿªm4aì«n©*»‰OÞ¢o¤ùiËC >ÅiŽÔᄌ—ÿ,ÅkÄ^š.³ºÊšD'jw4݃ûﳟ‚ Eä(Cà­[C²ìŠiç²2¾»5ÏŸ‹vŽ‡Ë‚3ÂÝNí¶ütLEµ†‚<ÅÌÆÆoê˜Ñ}< g#?½rÛ°D¤Œ4©C‹-"7´…â²23„Æ8‰ ¼s²ó!¹­T° øUÙz9yg¤ ›_Hi‰ú>MåZÇÎ`·3zò’EKAHFÛ·$NïDA SËÀ"¬Q³ EÛ¤-QAÕR;òùå™+‹¹Zê嘫KqÏåËÄ++ši\;ée¢@÷/b/. T1Ì®´8•ˆa(rß Rö:éœï‰0¦‡èçžZìÅzbŒ ‰;/ ƒZ»ÐÚœPåõ–V¦éQ©4=:ަ÷èOÎR“šYM928òÕóÍ—mçÄöÇç©1%<„sø¦ç*­#Ôº¾÷6ÊHµ$V.L›HSß3d×s°¦¬`ÒRQÄU›þÕBÆÐ‚:<–‰Ù²F›Ásÿzy@B¬çÑÈs™+!%˜ÜÁÎ¥âÝwÁ?5•²¾³ý³·]#ó]œH]éÝc™Ù #V· Ã`)±Éu¦ñîÒ¦êÀÅB̈kŽìæš:˜­•h‚ƒ,ä¦ÅÑuìßtM}.ú!ÍSÇ8;(¡í1íUé*Ño†a”§W žŒÈ}2ZƒÈ+Ç%ö—51b±!PR¥ c@j$%ÁÄÓ“é‘#™”";Ô™U, ]1 ARm]×-7¸ C šÇö Á%ar¤¿±Ü  !ÅñŠÝ¢p¿§ššiõ5>Œ-¶k~ÅÀ=ÐÃ!¼lš\†ƒ¡v”[Zýç7ü>ŒßÏÖÔ@R iéœrjºØ†’QÈI¤/ mèu°“bÉh*ÕÔAðÀ>M\ÖœÝÒÎë0íÚ‡]Ú©—‘Jæõ¼QyÍ‘¶NÓ„½‹´§Qïd6èÌgP¡Æi"\…Ås:\NãŸkkÜhòÎW‘Ò+%/îĽ¢ZŠ]ܪ>TS¬#Öš^Ø.@N)Ú®¾yCÈôûýr&:99:á–†iža؇~íÁ ëb¨ÿ\Rd%ȧƒY²§DÓ‡ašT»†ø÷cö:¹íƒ×ÁŒí ÅbìŒ-ƒú¹~4 é0fáÁþ!ý¥Ï‰‰ŽMaHš§Â~Ô9ý†ÕDn Åx 2M¹-‰±¬´ Ýìøû‹X j)Å ¹w„ò¢b{‰ùåaRŸ’øf9ȯµÄ·³ós ¦¥î#s 44¡‡À?Z_´üdºËXàŠü¢YùµÃ†J1×BpŒ8†;€©Ó52Ãò +ͯR4)Ù;#§ëÓð¬ûÛYøi¿û+7ƒ0€õº:³7ñb¦¬ ´žcXèá¯IMNáÔók^O”¡Y¨ÃƒT½'6Y3Xi{Š£BŽCª<ç_¬‡^ÑðX[ìÎKCS5R·¦´Èo¥Í¡àì²þ”ˆ»€œ´~~Ý+G¤ŽD(„›dö“ü‘Ë8|Øø²zÞÞ- ,(Tv“aªm ýqŒ"'!è8Æ,¢…Í^5¢i‘ i¯GwüÇh £Û6ä9)ÒkÎa—Fž‡¦4&¿^òq`Ö„‚WÕAðZëyxG¬…-¼ e²6–,¢* ¥×ÏÊù‹l{¨v)Ñ&gn€ÿ§ Ù(¶$Ø6N88È{ Ë¡ˆ¢¢ivúñ]PQ]ŽXu}T_?Ž‹ê:Q‡•yA[œ4ËøA¹H¤—Ⓣu©rAÕºn¨…'.ð³ é£Èw¡ˆ.*`‘¦+ÈS3°¼¨çš^Ú·+P1UD]åôQìl!Ä«#Î=òû—é½°¼N2zÁ *KšÁÒ˜ÔÕÙá*¬¡ÑÉkº~:trAÄ Ø q7“Y¹™56”}ý¬ d‰É”óÁbü\ã~‚1)Ü£S…{̭顈ˢ”„Åzû ÄdÝØ´ÖÞ[—{oýO½÷ÖŸöÞÅSöÞêB’Ûãˆ9Å -ÎY5a£<ÅQ¦“¼ðýùC•@h0 ËâŸëCŒƒå,!¼©Ã?G5¶Và’ÀIºÑÔnpœ:>ã%ÙöÝus§Ç;¿(ç6B>kK;÷(Üà?SÛ^×á8½ŠWÒI<&«5µ 匜8Ð%¢øB]ˆ(ÇÇ«J¸l}¿xzzd >÷q½*}0ÎñW§½!¹¸ä?%/ßxâå÷¹RaßkÕEçòˆ”ßUºm~5vÉŸ7ø“³ËÍ'v9»”os<†éZá'†YN%ª1ÍMÉ47ÓÜ|bš÷YašëÕ™f•ÊîHMwTDòU(ét «\ÇøÁºÊl.0Ìb$dᣠøÌëtï¬{òný~„»Þ_o c+)ìí2‘yÕI©T¬û›hòîø&T«aTßêÞ1ê´²ÆÕ׸r\é ÖcŽ(Ó>K(öYƒ{ÀâzÖ¨yìMªÁX»&˜Q$±E¥C´¢Pš_²QœW,âZÚ¿´o!Ðjþ*$Ñ Â³f¸¤&¨iB¼ËëMˆêK`ؽ¦¾bâQ½‚ý{Ÿ!Í• Ã,´’Å“®÷¼Â^Vg‹­Ìb~ÈÜ1¢ºŒ­ê«_)¢zEH0$É.Ò›$)Â]ô+PhW29˜#{.^ž‚êb¯WA뎱àUhµ«@ë[E‹çtÈÛ7Ľ„¶I«¤=ÑJɪÏnž‚Mq.W§†§KuÅÀBk&‹¢ƒl€;TA{^ˆ®5æAhaÀð0}ª|­¨ø*N|yU£RÉÊT‹¢ïÞ‘™)n©à¤g 4‡nGx.˜Ù!D®Bwˆ"® Ë|¥¶+ͨMšýºñ¬*¿†“Lêö‹eÈwð­‘O½8xϨèå½™|З u †në ‰ÛŽÏбɸ³¿Âa@{OX¸ìÉŽ…'€f‡½æ(µi•Ú`¥´‡‘!cË*úÊÑ`»mk·ì¤«ú Ñ†íº·m¼¬Œ%W=Y³]9dé‰^ÈY%7\%­R›¬ ÚBÿÍB׋n9‹Ò÷øÌ¶Ñ{¯oé˜Eâ‘UfÕ*3™l+fc! :”R6t6ì¤WJE*AÀÁ\™¸"ZVÚëÞ±‘hµmµŠr±U®c•C3C«ÜªUŽ¿Ïm(La¡›sešA"ÚÞ©¶-ñ Â\uÏEu!Û-ÏJbFÕ rET“Ëý0?&É{'Ò™"×=}&ŸÞýðÖÄ<ð ÑD²â|i{ ³HÄr äh?¾¤-ŠsHÃðN±€z`–‰y¬7ùYÆèL.F´æc,®ÄéÙÉþឃWðËà€@ª+r >>Dê]+D¦:Ǽˆ4;Í)L(8Ø"7¯ž‚N›ùƒrÇN¡2P{˜ÆéðŸ’[¬Ñ^5Š "@@–Ù£YÀØÂ@Þ|9·Õ0Ò ½z(R_8¡{XÇPï®XŸWÀ¦Ø…†•ñöãÙÙÑaÀÛ{ƒÈPv‡ 1^˜;½(7ç>§Kž„…Ý[™¢› ‚úÏ­Ä,®I…‘ºà+ã]•øg—Ä”´uÙð­ËF¥u™cMJ¼û$z,f˜ ›óƒ¡ßƒ#¶žNƒÃ¦„ƒª4i"WžiµxéÔO™ýüæo—Ç›îl‘)7ù–ÃEƒª•š|nN+¤Ž<*w¾ÙQ¹ÊÑ´óަ6§cLÄ—-(Fa_fk[øPüíü]ÇÝÌÆ½ñj̲ÂÃå‹|BJ¾^Rºµ)¥Ó­BbºUBL;-AL;wsîŠw¤§kw§§t)š|‚NŠº&)êÚŸš¢®}wÕ‘IWô‘Ü’ù<Ñckã–Pä"V…"÷}ĸÓ^T¼÷Hö¶G²ÇŒBɾןC¬‹|h•™Ç#ïÔÐÂôºƒ$?HÓÉîùЀŽRÄ#?ŠÜžÔùN&Þ ¯.8áãYvq< &½ê›ôjù¤'“ÒY;íÖå¬-c¡Ë÷+¼; h¼¯âsUqú¡{ø1à­ àr#ÉUU‰8r~ƒ`k¼Ñ„JL̯ðî,pb¿:I]uÏlÃ=³yôÝ8t>3ªîFcvß Pj_%ÿIZ4fîŸu?¼Mmx'ÞDV!ŽÂ`IÜǃ¤^ó{¢Rãuq)Ôò¯è3kݘM°˜2³ ›­y–™™ã7¹áC¹z‘U)åsņàÀxßàƒãêÌî*1oK ®¶½ˆµZÊòʢɸí¶4n«ñ"e#6^ŽÏ^£k¥m’O‡ÊºÏИìꊆSõÍhïðèC7 ü_ö?B82 ’ip §¦ã †ÇªÝ£Ã÷á§îÉéþÑ!ôˆê<ï/ÀÒ ŠJs;þ.eé8ÜrPûM»%¬Ù•{â °ö«D¿Q2§½êŸv)Sgwe³öšh»)f–b)˜·j/ WRhãC¿ð-° Õ B®Lã~LʵR:–%œ„É{ ê»L#I_ yšŽòdÂ.Ñ,Ï"ñ®°’dÁt6¦ïú–RT¬ŒF°+Wè Mž‹ˆ‡ÕWÙ"¶º—™»_DÙ…Xpön9¤ÑÀ÷ô>pHÏÖ2šôú/ŒÉánDWY)8TºâkN%¯Â3rÉ ¹à|\p:.<;2ûžôxä˘LÜ9 Þ¸sh»³²ù!9Öë¡nãcºsÙ;Â@ŽJ•\ÓÑ$mÉ2±_6LÏЇNµr”†Ö´¼ù(µ?„x×ÿ&UZÏJ{lÓ¾ô뻹@£Føb–ûÞ÷vQC6ís|žýƒÛuÐÒYÍ}aMòx…´\4pɳ¹“`}7ðô-@Þ ‚ µ¤:9ý)àmÞ!}œWq&œEêÊ¥}¹[< EY¨ke_…àíˆ=üÓG’q¦ˆsruì@ͯP#¬Âé^4cÿõªGF~U%2òw¡|4é•ã™iþ– úAӆ٬LJJߘa¹¸÷†£”d¤ççDªá_£xŒ`¥åÌ^iÖ6):Ì/HªÖ2Љ9 ¥ÖJ ³üüÀ˜$qBACÄãëi8àà_¸ç7û~£+ø ›ÀÐ=VÞ߃Q¨fa­1úSZ Ã„±‹1‡9À9À/>€ñÞþBÆß)µr°£ÕSÅÓÍŒ4ƒ2y¶¶({ñ±5l…ˆƒõ!cÄß- Ãj©È*˜Ñhâ¨à wˆaâ">sic¯±™Œœ?xJq.šÂ¡EKéã+5zâEšåN¡D²xNª¼RÁÞI<§&ö°™šjåûbFUå›ê¾Ñ¡ãP[xäi9HÂ~þÙNãmi©¢E-5Â×Õ4jЬŠ0‚˜ÅZ Í6óÖ­&Ö\M¬4ñÊjbÓÕÄFAÔ1KŸIËÕÈ–Òˆ”M#nI”Y­tT±HyÊfÕî'®Ù´‹¼’E”ìDšxµNO§ê¶M?1þ>ݶx½ ÿJÄÚ¦ñ½¡…-+ ¿ôµ oaØUÕ Ó W¹<ìEGa!_„ֆܼ¿ÆÒÿö·zý¯ÿõ·¿5þþ—ù[Ã'ÚZ.Ùò.—Ð{áIw¯û›Äj®ÛhF¥þÏï_¢ž6Èÿùk™ú*öà ¿‚Ùd1C>¡)O\/uqÍï…Yˆ€©+(_½#„óŸì\þäé]¥¨Õï‰C·Ð(› ‡qFNí+¬`Ñ‹e@ `ùÿþóᕇûè!ñ«¨Çï÷P^— ÕÕNâDAFå4oŠk%ƒçȲb!šìdÓbÏ₼géòH³¨«#Ôá%T®YÁâÚ³„Ô’¢¨mæÇÕ{t}ˆŒS~ÎofÁÀ>âÈÄ#•bURà”•Åö5Ïôͺ([í×U@ëÝU€Œ@;äòÓÿŠs¦±¸ôÞ\_ÚU¶´ªaÁ:_ÜÕ‚ÅUîíו'&ç]^hæiqç[ÜMÇâbËæânÐÅåAºÒLïâÒaòê¤Ó霋‹Í<-n•ÅUôÂ¥±åXfVÆ\è-ºÐ”Ò«^±˜ç]gtCm _VÆ'ÏJ8DGøJ!!ùº¨ÚpŒƒd hmxV{÷sNok4jfÓx ¶-^¯m ú¡¨:Ö¥ÃRÇ7’uÿHÑc#ÉoÒé%‘›Zl¡Ô‘Ћ\ET±õ4l# Y(¥(Ih„œ%îû?²bdª®ÿXÒzK9gñ´ü9eÚP}^ûé^TNØëF4ÌÔßÎÚî¨ÿ¢¾ F\0aái•( VTLá3Ëçú´ñÝT½.Ýñ\ؽyOûlSì³W|Ÿ)˜ËœY”‡1‡Š¨ý0¹é—ð–î¶1¨‹®ušÿ¸6õæaΰ1Ì“>ߊ?R1½ñîæTcÒž­§ýtûIÑ}ËCP~I ýV•½Tí}¿/U¼ÊâH¨ÙÅ%Í4„ è)0T=…½í}Á| > %/V’LÀI„ö`é.I6E·+ üªëÀJoÊçÖ`̧ÂPö€A&¨ õ …Іâ=Qº+*ì‹ ;£to¨@QÕÊ{Ã[Dæ˜FjlÇè ÒÜB{.üiþ kô¨´dóØ)XG•eK”rz)dþÓ‘8JÝŸ6×ã%€O+øD¿yä{©Ôq:›ãîóŸ‚(R6ñ´áþYIæÓú>Ô¯BPá$0Å2¢¯,FñO 1¸ÖÆvÆ0«jhò?ÃX“j–lLª;7[[ =ÅW5zÕ´7$ü606*õhÓRÆ ÅQ/a¸Q,)Ñt“k(&×ÁÏAXrÂã‹1¸Žœô+¥Û“ó*ØŽëÖ”|qC¹péð{SlK@3ý÷¦øè)wØo Ÿ1` uØ)ÛjB:Ë'³\K"Xɾm¥gcá¿àZÍù^m3àRbˆÚ’–Òb@'χMa«:ùóemRFw›_Ò)5›žÈ)õmr•).wIwuÚ /抽§  +F·dÅ‘SV“}ûËQU–Ê$wEˆp0{.’3œ%VïÊWù¼+_8 $dŽ‚2ÖXÊ$))|,ËKE"ˆ©H!K‘¥Ççu·TM YrÈ$Ê–ÓoDÔ°*Ù£»ý\ÕÈŽÅjŠH¬'Ö‘î×·Å2ÝtáµÎ2 O¿%Ÿ³´çó÷SšsÜYžBß° £L\NšåÅÿ@9Ë/|Z|žl*Èëü‡á¥yÏÃKOö‰À~;ë$Cšøþ„ŸOøù'þTʈ¥û`ÿœ_9woϹvœ¾-'L®TÐu ôQ1g…ŽÁ½Å)œE&2Oá4>®~ _íX§pç‡eV8…CÀž¦ˆQ$¯u wíƒøb'ñyŽâò,ÎíM*׌“£S(RÈëBœR•<-Wêªu\W3_9«Xv5sÃÙ·<²kÆ:Z$>]Ä`U:•¤B ÈCÿW¦Xu¨à¯óü¿*Îÿ«•Ïÿ«âü¿Zpþ_UÏÿª`M(V} €µ–Sþc³“YUT ê4%@»åÑH5€W (|‚@%IÀ+ ”ËåÂ@©4PA(•J‚ A%‘ ¢LPQ(¨$”‰ÅrA±`PI2xŒºªÒS<[Qµ„¬3b<Ÿv@½É©¹yl6´í–8Ï»IÆk“{Ù*¿Ž PI ›¼ƒš @O füEÌPLšÏêt±nmÁüê‚'BüDˆÿT„ØM¬ %ÂÖ>aíŸ kfá~„ÁãÜÛÑ»[ÑØä¦Æ`M¨Œ PBÇ`W1• R˰æÖ2àa[W2¬ÙJ†5¡dX«¤dXJ†5MÉÀNÀ Ÿä¹˜~WÕ,¨t˜ÏÓŠãü<þUbŸ4ùO×1ÕÌ2i½-¥}$û°ŸǤ× k&=ÎL=o)iW,D ­ýËkêÌFFÓSBRŒ©c”º䠋*$ãÀšÜœ†u_6>—Òæ>E|§ˆãøy Ƭè¦÷gðÒ[{%½ôá÷hÈ=–Ì¿‘òÏÔy’Å3«âE¬0˜¦ŒªÖ”áÔšïRÀµW<`´¢©YÛt%n°*;Z‘‹D¿ÌàФ„bñ L.(Š%¯àáý¦_ÌíïX²ZHÉlÑÀîpwsÞsEÝó¥:kVˆä/P—=ᣘGCó1»L&ÛÏv‡ÔeÞŽå\,RøO↗ °ñå—„±rj ,¢‘$4—v°Hk¸ëÎᮩÃ)Ÿ>–õýÕÿú_¯ÿþ—ÆkŒv]§/ þ#ƒ°dÓýc2Ë.ÈŸÑÁÆ¿‰Jg;{§¯ë/jÁßžÕ–E§JUîÍF~SÆ´€a¿ª n *"Ä_*tûÈ´t‹EyZ¢[e.äP¡þ€Kï —¿)^Žéû@€¼uœd·} 0ùõk™ø¼kq¸ªÇ0¦™ 3Ó´¶ìôÏL=¿)@¾ÒÍ+X‚Nê;Þ®ÎH¾„"“„bšà# Nªà$ åt¡„0¸²,{S„RBËÝF¿kÀ>2м¸{ô’<;O¦×çTchÞï‹õhJÀ ÜpÄ¬Þ :(ªDÙÕ 3.M®—û9òñ¨ðD;¹(_­Ï™ß´XKðèµÅLÖÖx+‹f]8·\‡Ù-Ïaö•v˜•ñº·œ|€Y[ëú…ö+[ÁÐ~=$·ŠŠ¡Ó¼`K‘ZwÌ¡9Ø®ȧò™hÄ@[16¨Sv(·~w¡u¹Y"F–È‘^AÒ-IºEÉ ²d™0Y&M.äP.ÅÛŽ¢O ·@ÿØ$ùjF¾¶…ï&ã36§‘'‚|ß<‹ùèr›mÀZ|“Œv¬2>¢)k<Ûö3a$[—"¨ÁÄ <Ð(ƒE!$Šà¦ººC]›¤zükf®§yÐ9ç<Æ~ÔeÎFcƒ\èô£Ï×)~]úîácrª(§´™ýòÌÚÛ~ëRx^%GÑUoÙÌ4!Epeq>’bõú$„y¤Oñ³áhN´™‡üE2„¤Ù$<Ÿƒü¿º.zQi¥F°ÓÙXHŽÛ×ûy¥àei:ŠWI`׎‡N‘ ôýú¶^I½°bÉ4ª&­†p ÏìJZŠnþFbŒÎû˜¨D*”ÃêÍÎÏõqaŠ«©0itJ}?k›ó5h• ê®7ÄWç¶öl}ÅéÏu­!œRÉîÆN›A'ø‹°¤o…mý] Ù àƒ8EH÷R¾­—¯­\×D&¸È…#å:MemÉ·’+Ì¼ÈØ~š¹2—œxZµi\…>)…ÉMu™H¢ZFØ”‘>pAØj—[eܽ˜3yXpAFdH5ß'ñ8Ó™ÑAx‹žá 'É$&t‘®6ºÑŸ8ðf"Ðdõ¹ª#!d…¤ð}!~¦à#þ\ì‚…lU´£J‡óÆA–ßÅÌm6ØQØ ¨|O Pn/v¯.6Í„ 3…\q1Ñ×7›^÷â®ÞJµc–(j$“c|•‹À(þ÷gyøÑNxö×^C)ö+|’ç#òR{RÎñ\åŠs=uÓ™ºáLåþQærpÁô~£„ð% º¿íŸ5øî¼=:9C1>kÂ}„ŒÍ¢äüÁ¥L‡²$jŽéPP‚Ð9Fiê{ïbëôö0Í“óÛ{÷©!~"ÿãDhÈšo½„ƒ¥RAªS.Hª r‹¼}Ï*TÑ‹Ò_{,I2½Ã[Óm¦}Š0dpóaÁ¯˜³Ò¶Ê†i:F ?¦ÑÎébz+¼¡6¥›P5†jÊùÖ†®q¼-ýþCWá¶ôL審cÒc~/†fódŽ[*¡ñM×åã¬íPX·1ÜO¢4 aÁÑÐ|A3åR¯Á·ª­8pÍI¿ði‰ ›T>_Lãh’ØÝú–§ñØM(+*]dÙuŸtÚ¤ÃõÔñ;+´ow2T¨àB‡" Ó¨Ãñ^îf(ÖDz/kà½K 7‹Ý£dWžÜÈÐŒjoßoâ]D™µ{A™5e^ “|£3ºfrÂé'ÆiŒ.ÎXPAý‹( ˆ´G@€K:̸üÊ@fI†™¼‡ý‚gc&ZŽÒ,¡µºQË_&£‘<½ÖWêîæ.§û{gÝ“ì\ÍN‰œþ„çØ*xÐH¿î•a”ñ ëJ¹¬Ù0`]÷<À*Dƶ[lß› hzƆØp>dÊ„·‡Ýþį‚03g,¤¯/ø?}ó ÑÇ$ÔÜé©–/Ïî¼Å°õéñ?¨ïå—ogyžŽ—ƒîGK­çwøæ-h{Æ•F”RFMtEÓ«¨*"¡ê ÞâW ™ÏXŽÜò0d$BdéÅ…G]ñ‚Æsæ@ €.]¢Ia<š'k"M„£’º½†CÒ %ê¨Apµ÷o ÕJ—Õb†¸*NE½ÏÙr#š~HÄ( D"ž/ø´çÞJØ@ÉQ¿® s;ÉÉ£b„wq'ÀU<ÈoR2“üÊ›ugíÒ.qxÍ+¯±M©Ö(8FÒ¡â:%ë”ñĸ¡ªÿ×5K×Ù®ý¯ß)HªîÀ¿@}Ї DÍ•2̵ó”sa’iM¿D–¿Ã„ì~Måj/Êó¨AqX+¨“2MmY²„5BGïl+»HoXU·-l…ÈìyYÓÈ‹8Ô #kl›5_«ƒ¶ÂÜû‚Ü{ Š p_¨ø¯Ãµ )s¡ƒ®dÉoœDðyÛnêóÈèϬ $••ñ`ƒ‰”ˆiäÈ6<òÁÛì{쩳Ùþ êÅ£å`<·ÍR¯ìRÈÓ*·ÅË}z›~^®{V fZ.5ÁgãKQþ¬×ì˜51uÝQ’êúà£PÔB@´^ú…ÕušBv –^@¸ê÷b^"î˜5ñÛW-'?O&!Äbut ôÄc´;ާí˜)Ø5ºóè p\:  ªv¤Å<˜¨ië&"-¦ˆåƒëä.  Æy ÷Ø? vÞv„‰Oð¯T#A‘¢ðä×Ê-Ãß°^-2,hÐ=¨WîA½2Ž Ÿ..ʈŽa»Ëpûž’4B0”qî5µ.?aŒF~Õ.Öºîù ³5?d®{'³±˜Oo~ã^Ñ.¸3ÜûY •pñÜ(‡Äm-=¡BZ€G(+Z‚Lc¦ÝÚåy¤ïë¥yç@A¡AR_ë{ @IrÈÈÒ0Ž£ÐªDå NUÝHDy¤ úøéú¹FÞäj7¿âQ{®¦g\ÊiÕYjö•Ô§{uΪ·6ógTˆ1~|ˆDJæÜcÇr0î†Ö(Ž­†é*R¹·nëù¢#JòÁ¸"µŒºã|z»œ3$ÖJuD©32ªOI|ÃÉ’ÞÎÎÏc0ÎV÷Aß…ùGë‹•‘Ízÿ£d½ËÒžYâdö‡dòÊštÏN~vJbMè¼ÂòÈ^“J(kr>¦Â¢þf†­:ó?¿† (ƒ?ëþv~Úïþ*mƒøÄ’4Dp}.Ê Ãلפ&ÕBà"É™c š¡Ì¿'§O#»¯´ÝUPe–GSrDƒ‡ŠÏ“\ê+DyÈ ‘»‰¦`dMÁ—ø ‡ýºFAp8íîœìþBüfؤ7âVCºr”û@Z0Ôo}Jz˜ç~h”Yp7ÿ÷£·¡3¢1bõĈð³Â<ëO …!ûŠŒ w:í¥´^´¨˜Ä~’?RÏaì{ZGŠÆŽ"çãb¢áÈè™Ô„‡{7ˆ ƒqÖŽòî‚9ëfìoII€} *õjÂÐ5ššU‡mé~<„ Á/(g_ô¶ÕR㤇VQÎ-z#™&ÌÄ+§h³á#Ǹ;”é*oš¨Ïi0ŸbùžI_x«­´Ócµ/zþk½_¨ÈˆvÑKY<)<à€_‡xIS¶û¬ÇÚƒ­ž§üPÌãõÑT2‚ÏÛŽZŠ©â¾f‘*߬±ª¬Çö%Þ½‘,{¬ˆàÃqz¯€™í‹´ÜëÂàYKÕÞžóø-ÒDK‹>Q§êOÖ¾èÊ넇"o³\/Í{þ¢çª¨âJQm¦:)Žõζ]ÀzdÛ(r­Ô£,yt6 lÈw|×Z ıæÄ!7xŽE DZJÃqˆí¦XM²|o@†xM¾í5³\䊱ó‰d“gÉaŠÿ3(Wµå4÷Üt?J¤ZˆÎ¦ A8ëG“X, 3÷äïµ;ò÷&xÈÕ^¼xQs<ü©m™&Ÿ¯ks±qz³”gŠÍ÷Ÿøl•ÀgK·°VFV8ä-û¹ÒjdÇrÑ–h§ëOM]ð:?/ºÉàkƒG™JaÿÓ§ja×KàV1D;­… Ú1¤v«–Í'ê%t^KÁ£„Uùé~!Ù/¡úÕˆ~Í/'ù…ßKð™z¼"±wÐz†D<õ ‚\Ñ U (iË*õ`û•d}<áE£d8¾Âk4´#é¤ÒžBÎáïú9¯Ù›åþ!0‰¹(&2`厊å,Ç»ÿÉ[a–÷59 UAÈO^~»ÏB-Vm5í­*ûÊÈ„£¨´Æx ï¡G3æ ܽ]²ýzQÿ²Qbù#ÌwXAŸša<0¨ €~Ñ3ÌUÀôÄGÿÖ‡3ôòh-ÔÎÉý쟹¼6„J‡Çm/ÒªÐ5šPÔnêTDœŠi“ƒ¨ôFþÌžxtÓwÝdQ6nJà½7è, ¦:—d“QtëV"þºÿn¯{ðÖ…ÂG€Td˜*{Åšô:Éð©65‰ªI;N«Î.”¼3\@‘Ågh¨³´©u|SS5Yê>ç‹ô™3VæƒVH¯-zÀ÷À®HUØ«›¿V÷VzUÅ[é1qÙùE-:EÝ'UìöÎü騟ÍÝé¶lg:×AEhçÌsÊÌ'уä[*’‹¾¾"v“}ˆ з˜ÃŒœ‹»æm=!û=•+©£tè‘w*ˆ£•¥Ý@%•Jú¦B3ƒÈ23ùñˆÌÚï 1_݆kc7¡_eÕlžZ²RÖ`Ë׫÷ðëÕs•´Å«kÿ"¹æÎN4†z˜+n„X|i¾¨ñÐ],憮†@ÊÃ<†ðÀVŽŽŽÃÝ÷{dM(Í>ëžž…ÝßöOÏN¡–ר~Â@0˜žË}Ãã½a8o 10 kÜ…´qd^zRRZZA\%÷B¨1™e“Ùhô˜Ðãø8|×}ÿ5ä˜@é˜@é;E‡6a1 ‰Ô^5 J<‰‚7•Èã|=gq÷V`U`õ"C}Ò+בhÏEán¨Îeg“Dé$L\ãæTM9·¡tßʯeŠê½µ°Þ*¿¾Ç-—_¤C¿ûSËëýdl¹ù\Ÿò놸«y—€ Sµ  „º+¤GƒdÒ0Þ”[í†ïöO|w¼ˆAú潆é´BF3¨½ÿÉü¶Ý9=q`yLZ¦¹ÏW8%%J%XG‚Ói–§Ó˜6Ôå OÏŽNº_úéU/Es9ÀÐgšþÊ=­ÌËããÄ:ð:8Ç&8ïQO'ÛÏI»)T?Ôël†uEÊM)Ö¢~Œž$z,m¨Êm¿ÕhÚžpç¾IFƒÁœSlâæ’RØ„’s­ãÊ™‚[©ÈMx Ú%€­¾|l†:-öˆ”ê(+ªëa๋ìgŒt¨’Œ†ôžÔmÿ¹>H†£Á{OrS¡Ä!SÛ½HɉzF(Õ\ðE‹ß>5éj¤%`Rìá!ÄÜ¢KÅmÇÕg¢ïº)Õ½¿?³Zq™<Ñð¥Ê ¹-™.Á•tz;Ç¢ñç¿+Ý vZ•â4ºi¸„?&ýØÜÑ pCÂ%Öª·@"„b8ÌÒÙ´Ïa¤†_ô«ŠBKù¢Ü¯†Š@OxI ±Øi‹jÔRÑ#Á?®h’ö(]wº¨åÀʃvd_mȪåš5`ËöÊajå|>ïL¯ù½ŠÓY, X¼ï…Ç'ûG'ûg¿ÃuçãÁ™´•o·Zð€Z)6uÛ2 ³¡%#ª/xùû‡uÚ’7*fŒôð÷§öÓýiU’ãj©ÓÞ–WKø¾Ú “ïòÔlrþ·20h³h™·ËpÇëç’â)Ré6«Ó®r›õ@v¨ˆ¡º>&á`¡-ðESÇ”˜£°K Ik:ÙÜ€í>ò¿¤Ý¦‹Ø"l_zŽÍ1”˜ÇcŸo8¥ ]ÆŒÃl‘bgA@ÌlÇääÉ9àzƒŸº+ß‹›'F}„¦< ¾: GMDUßÎÐxK)HóEþT?ÁÆ j+ì6{<Ý’‚Ì1 åq€Q> y…ƒ$'tÓD‰zÑC DËŽ¢Ó±ì(,«£²¬ÎW²†¸kØáNçrƒ¢`£>NQTç{à"~(`¢J-°iˆøM“:Ž8ßó›&µ}¦I5xç±ÄïTŠþ$šÝ¿h& (ã4å vh1|*xçØÆNªÜã1V¹ÃÛ•,að<Ì© nq[©|­‹×<¦Tù"…÷Õ¤ë’C…QxA4”ÄS~vª)g°{¼yìíBƒÇ ³+÷ô%#ëòQĸp\=Ú’<Õ[b!Œ]èä0שa>¼TÆShààðcÐiƒ˜Î$š’S ðÐa0Èß»li”×Þ,ë+ÖÐE4Œâi’ŒÁˆF3‚}ÏÖf/<Ýß;Ü9?ìœíþî¿ þa¦½ÿx¸k§¾Û9Ûáwgx LGñ ¬AÄXEp€¥³(XŠQè ÕYZQÐâp™¦¨×Ñh«]&c0ÀÛûéË4iþ¢a‘ÿÚúû‹ëX.¸”¶ ÒIˆ©®ö%úú`N6†¸ ¼“3Zê§“[> úþ"V6íÓŒ¦H‚ÖB}¸¢”wȲ’r–¾»¨E]/kš? ¤±_žmbÇ—¢² 9iÆB}½ê¬ž^O:€ŽWÑND l¹àX/•Áî¾Ä»¬¬™I+ñÔóQ4ÌÄéeÝXÅù7Ý2 ¸1Nù&(¨¾ÍdiZ–‡hŠo²ÐEã~üDÅ(Ë@jV¤kBý1ædLÚ'J„]¨Bè_ƒN–8ÿ ô.ü´sð±KiÞá·.Ûžï'U Âp†*}èDBhnÃ'd©ðø³GÛyrô!Ü?<=Û9Üíò©5ôÎx?*3bó£Ò®) /ìçq·‰“ÿæØ{¬_}—UÙ`ôm‘Ÿð<Ä6ÎQ<œóŸ¥XŽ˜ÃKówèäšk¦tR£”jÉpùþûÇì§`BÑ:ÊJE«ÈŒ)°Ób⺬Ì@MÝø‡àùsÑãáÑîÑñïáîÑáY÷ðìÔnËOÞTÌPk˜¨TÌ—l|Ù;ަÑÕé$î/H‘ŸaF~+(ÃÑ„alˆcª&™ÉHêfÓþð°‹Hö÷!#ñD B=ÒÏmŸE*ÛÌIF|û°pU¥fGyÄ-ež›q4áxçdçCxôöß»»D£³¥ds¢ƒ†s+{zÜÝ%ì“´ÖXùYÒ8Ò“2®mzG5º‡Òk_clÆè†›Íî/ÝÝÿ° h”*1ZU¨1Æi’1+&t!HGç™sÞÀØI[Å›%=u esÞÀûfuÝÝ;›“뉱ùøÞv1càõ·u™µœ2ÌÉvPø |Â1d PÁ•<ºŒŸåOˆ,¹þ^ïC—ّO¬ð.Š­ËÁ%ükjý˜”ËD[.ÏÑvè! uZ› @Qðï윞ò¬•ŸQ- 6ª婈cJë`kGp´ËÁ&Èy—”KÏ•±Ó ÇIW;ãRÝœy èð䙟 O™ÃùïødÿÓÎYWºH84jr©ÔÌ«x<;ðg¸³²Ä~JyW»!wg2¿TwfÔ».oöUrÌÞêrdRãYo§hhìÎ¥^ÖÎ,aÏãÍ3âQipÏêz…¼@´„© êæ2l¯ÙÄÆ÷ÜHF»kÅv©ëgV>3fík¸‹Bš[=ä@àò‚ˆÌÕŠ”£7îÅe˜Â´¸P,ã½”Ú%Ŧ¨0YºAÊ 2¿‰*ƒîÅ%q±bô¥ivèÙ IÔ¦å”ÐÔÎùèïÖá±O ƒ8byp›â-Çõù—°Ù!:-(uèc†¢È› Å2xNÅØª®žUe* Ö'æa(¡ñÜ0Äd§1€‹L=ì÷·º´Y¤ëtdÊã¿#SÕÄ5ƒÚ¤æl@9¯û ©Z|iÌž6¼>cLÞ7À™Z¬Åxbïm”‘ªIþ~6î7„Rß3¥ç`MYÁ)ˆr®&è_kD Ô ×÷ö;wv¡ñúfðÜ¿¾ ‘†FxË\ ;!+ììQ¡§Kod*• ÏöwÎvÞtÌw]0\éÝc™Ù #¶vC{ãøÁR¥Ì6‡I†ÏÉ)ˆ3½:-úºSƒ†º¤¯Y€„¹[‰&p¸_ºoGqN8…û¶é˜ž®K)ªmœìÍ#™Èà˜bžwtvÊK¡xåéU‚ ò_f¡ótå,/ë¹.”™Ì¾øX(9TÆ3ˆûa„.dà@k ŒRBC®eò9JµŠx.Ä\.#uãÃ]%&ì›]˜ÉÃÇo¶ÉV‹Ø5'ê§–ƒh ®V ¿&Œ‡ùE›*T Ux½÷º1Pe×ñI÷ýþoâS\¼ÛVJ´ÀEz’©VáU¨}néŸm½=fšŽG·0º£ (<ÙS’òñ:õ4F_<‘@¾ÙdÝU×s 5›?Õð×#íÌNQæjQæš;“ˆ˜±Lbïý’Dt´w[0ѵÐ\0Ñv߇4ÛÀHoÍ´PŠC†r«@ëj&c÷Å3DŽpª“'3TPܳ' J–a£°ãã§¿”wl¬É†³c Á7ç$\•±¾—F„Ƽ 'wŒ‘BrÀ_lù™îûîòáÙÆ±æ½|Aú}IŽœ/]Ï»žÀ´1Ù \Ļ٪ÐM:ñwC[cÝl‰ndáìo3 ³'Ê«_Ñ4"…¯OÙ4çôÙ2°¶¡ŠÂ窜B¤]ø„ÌA€Rv(:`(‚‚¢Je°ÄTåÉÁ!8N1©\¸¬É7ÿÚú;Ô¸Š¯2Ðß?gRž†ø!I­ÜàUÛá xÅ^¿¿ jÂŒ±& dé4ç%~êÿ$sèM*éxˆ>-{áÑñÙþÑ!=š…;'{²0MðF–!y¤ÈaW+‚~›”z®€HÄYšLØãñ54¥£ƒ›dg³à²ƒÀbÔŒ†}@ì¨@ì8€¸Zˆ«…¨#óu(Ž~’9Š-™ä€ÕéÙÉþážVD@ËÃsdaz)ß"R ùˆ¿•Hï«Ý\uA²6™¦Ãit% )à\u€s­8×tp*«µ¦4O~ÉÃ)¶Ö”§¸<%µc´ oíè~:¡Á]虀¹{+A¦¸x)„Ъ7:hÚïÌ'qÔÓ>ŒGÞƒÓû>£ZÒ².‚üˆùC­…b˜v0"ÆcäÑGMe¼Š&S®@óÈ cÅ$€zæè`M¤}Zà‘¹zWb„çîG¡ûá¬mË +æ<Øû?¡®hóåryå|E|0â­ÙqXÖ*Åa±‡ëmÑ`ËÆú˜¼€ªFoA´ýB‘PUá F£&ô[p$¦š\tßTMsåRiÙzNò' öŒÙ$‹eå/îù\‹XªjÈkVà`Ø÷‚Γړ_ïéýÏO^„âÁ$ªôæÆ;âc^9ê=6ܛÑbŸô-¾Íg×>K—óÁ¸kÔ#B¾PZ™L¶Z€±õ'Æö›v?ã‚§œÐ,¦#~,ܦÐ;%vD ¡ èõÒ5³Îe†BTA#£­³›KqÛWoª &¶ë°(££é4º…]>Mo•+ÔÕ‚FJè$A…1¬\‚k$Á¿h=CÊòß_‚¶" ÔÅ—´xã¯ÉßMþ õí©ÓPÉŒÃ3Y]ü{€•s XszKZU\~::|muå}%(¯&-nùMQ+ ©mnl/“:Ò1Œäë~? µVh-–ßvœð鬡–ßÖn¼qd´¯&oæðìÙÿkš(÷syncmaildir-1.2.6.2/misc/spit-tags000077500000000000000000000023451262303536600170040ustar00rootroot00000000000000#!/usr/bin/env lua5.1 local f,g = io.open('smd-client'),io.open('syncmaildir.lua') local data = f:read("*a") .. g:read("*a") local errs = {} io.stderr = {} setmetatable(io.stderr,{ __index = { write = function(_,...) errs [#errs+1] = table.concat({...}) end } }) error = function() end require "syncmaildir" for k,v in pairs(syncmaildir) do _G[k] = v end set_translator('cat') local so_far = {} local so_far_counter = 0 setmetatable(_G,{ __index = function(_,name) if so_far[name] == nil then so_far[name] = "Mail/cur/foo"..so_far_counter so_far_counter = so_far_counter + 1 end return so_far[name] end }) for tag in string.gmatch(data,"log_internal_error_and_fail%b()") do --print('exec',tag) loadstring(tag)() end for tag in string.gmatch(data,"log_tags_and_fail%b()") do --print('exec',tag) loadstring(tag)() end for tag in string.gmatch(data,"log_tags%b()") do --print('exec',tag) loadstring(tag)() end -- for i,e in ipairs(errs) do -- print(i,e) -- end n = 1 f = io.open('/tmp/spit-tags','r') if f then n = f:read("*n") end print("default: smd-client@foo: "..errs[n]) io.open('/tmp/spit-tags','w'):write(n+1 % #errs) if n % #errs == 0 then print "STOPSTOPSTOPSTOPSTOPSTOPSTOP" os.remove('/tmp/spit-tags') end syncmaildir-1.2.6.2/misc/style.css000066400000000000000000000012371262303536600170140ustar00rootroot00000000000000body { background-color: #FFFFf2; font-family: sans-serif; max-width: 1050px; margin: auto; } h1 { text-align: center; } h2 { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #AAAAAA; } a:hover { color: #ffffff; background-color: #1030f0; text-decoration: none; } a.nohover:hover { background-color: transparent; } a code { font-family: inherit; } blockquote p { margin:0pt; padding-left:0pt; } pre, blockquote { display: block; padding-left:1em; padding-right:1em; padding-top:1em; padding-bottom:1em; margin-left:4em; margin-right:4em; border-style: solid; border-width: 1px; background-color: #DDDDDD; } syncmaildir-1.2.6.2/misc/tail.html000066400000000000000000000012561262303536600167620ustar00rootroot00000000000000 syncmaildir-1.2.6.2/sample-hooks/000077500000000000000000000000001262303536600166065ustar00rootroot00000000000000syncmaildir-1.2.6.2/sample-hooks/mail-on-failure000077500000000000000000000043561262303536600215250ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright 2008-2010 Enrico Tassi # # lines beginning with a double '#' are considered the documentation # of the hook, and should use the markdown syntax # ## Mail on Failure ## =============== ## ## `mail-on-failure` is a simple `post-*` hook that sends mails to the user ## in case the synchronization failed, in the style of fetchmail's messages ## about authentication failures. ## ## When synchronization fails, `$USER` receives a mail with the following ## subject containing the logs of the synchronization: ## ## [smd] sync with $endpoint failed ## ## When a subsequent synchronization attempt is successful, the `$USER` is ## notified with the following message: ## ## [smd] sync with $endpoint succeeded ## ## Note that the mail is sent with the `mail` command, thus local ## delivery has to be functional. when="$1" what="$2" endpoint="$3" status="$4" SMD_ROOT=$HOME/.smd HOOK_NAMESPACE=mail-on-failure # if the file exists, the failure was already reported by email HOOK_STATUS=$SMD_ROOT/hooks/$HOOK_NAMESPACE # on failure we send the mail, and create HOOK_STATUS if [ "$when" = "post" -a "$status" != 0 -a ! -f $HOOK_STATUS-$endpoint ]; then # something failed, we mail the $USER TMP=`mktemp` cat > $TMP <<-EOT There was an error while synchronizing with endpoint "$endpoint" using smd-$what on `date`. Logs follow: EOT echo "----------- client log --------------" >> $TMP cat $SMD_ROOT/log/client.$endpoint.log | tr -d '\015' >> $TMP echo >> $TMP echo "----------- server log --------------" >> $TMP cat $SMD_ROOT/log/server.$endpoint.log | tr -d '\015' >> $TMP echo >> $TMP echo "-- " >> $TMP echo "Sync Mail Dir - mail-on-failure hook" >> $TMP mail -s "[smd] sync with $endpoint failed" $USER < $TMP touch $HOOK_STATUS-$endpoint rm $TMP fi # on success, if HOOK_STATUS exists, we remove it and send an email # to say it is now OK if [ "$when" = "post" -a "$status" = 0 ]; then if [ -f $HOOK_STATUS-$endpoint ]; then mail -s "[smd] sync with $endpoint succeeded" \ $USER <<-EOT smd-$what succesfully completed for endpoint "$endpoint". EOT rm -f $HOOK_STATUS-$endpoint fi fi # vim:set ft=sh: syncmaildir-1.2.6.2/sample-hooks/persistent-ssh000077500000000000000000000055071262303536600215360ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright 2008-2010 Enrico Tassi # # lines beginning with a double '#' are considered the documentation # of the hook, and should use the markdown syntax # ## Persistent ssh connection ## ========================= ## ## Ssh can share multiple sessions over a single network connection. ## This feature allows to speedup connections. ## ## The `persistent-ssh` script is a `pre-*` hook that ## starts (if necessary) a mother connection the first time that ## it is needed. To make this hook work properly, you have to ## setup ssh as explained in the following. ## ## Your `.ssh` directory should have permission `700`, and your ## `.ssh/config` file should look like this, where `smd-server-foo` ## is the `SERVERNAME` specified in your smd config file: ## ## Host smd-server-foo ## ControlPath ~/.ssh/master-socket-%l-%r@%h:%p ## ControlMaster auto ## PermitLocalCommand yes ## LocalCommand ln -sf ~/.ssh/master-socket-%l-%r@%h:%p ~/.ssh/master-socket-smd-server-foo ## BatchMode yes ## Compression yes ## Hostname your.real.server.name ## User you ## ## The key ingredient is to obtain standard name for the master socket of a ## given endpoint, in that case `~/.ssh/master-socket-smd-server-foo` for ## the endpoint `smd-server-foo`. Refer the `ssh_config` man page for a ## detailed explanation of `ControlMaster` and `ControlPath`. ## ## Note that you may want to put the first four lines also in a more ## generic configuration entry, so that every ssh connection to your ## server can benefit from connection sharing. For example, a complete ## ssh configuration file for `your.real.server.name` may look like ## the following: ## ## Host smd-server-foo ## ControlPath ~/.ssh/master-socket-%l-%r@%h:%p ## ControlMaster auto ## PermitLocalCommand yes ## LocalCommand ln -sf ~/.ssh/master-socket-%l-%r@%h:%p ~/.ssh/master-socket-smd-server-foo ## BatchMode yes ## Compression yes ## Hostname your.real.server.name ## User you ## ## Host your.real.server.name ## ControlPath ~/.ssh/master-socket-%l-%r@%h:%p ## ControlMaster auto ## PermitLocalCommand yes ## LocalCommand ln -sf ~/.ssh/master-socket-%l-%r@%h:%p ~/.ssh/master-socket-smd-server-foo when="$1" what="$2" endpoint="$3" status="$4" SMD_ROOT=$HOME/.smd . $SMD_ROOT/config.$endpoint MASTER_SOCKET=~/.ssh/master-socket-$SERVERNAME # on failure we send the mail, and create HOOK_STATUS if [ "$when" = "pre" -a ! -e $MASTER_SOCKET ]; then # we spawn ssh and put it in the background # so that all subsequent connection attempts # reuse the same socket set +e ssh -fN $SERVERNAME set -e fi # vim:set ft=sh: syncmaildir-1.2.6.2/smd-applet.1.txt000066400000000000000000000011471262303536600171550ustar00rootroot00000000000000NAME smd-applet - graphical interface for smd-pull SYNOPSIS smd-applet [-vl] DESCRIPTION smd-applet is an applet for the GNOME notification area, that runs smd-loop, eventually notifying the user for critical events and allowing him to run suggested commands by clicking on buttons instead of using a terminal. OPTIONS -v, --verbose Increase program verbosity, debugging only -l, --smd-loop=command override smd-loop command name, debugging only SEE ALSO mddiff(1), smd-server(1), smd-client(1), smd-push(1), smd-pull(1), smd-loop(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-applet.ui000066400000000000000000002717611262303536600166270ustar00rootroot00000000000000 1 60 5 1 10 10 1 60 10 1 10 10 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> 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 3 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/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. False 12 True center dialog-error True False 5 True False True False 10 10 gtk-dialog-error 6 False True 0 True False 0 10 Syncmaildir encountered an error and is asking for your intervention. True True True 1 True True 0 True False 0 none True False 12 True False 0 label True True False <b>Error context</b> True True True 1 True False 0 none True False 12 True False 0 label True True False <b>Possible cause</b> True True True 2 True False 0 none True False 12 True False True False 0 none True False 12 True False 2 True False True False File: False False 0 True True False â— False True True 1 True True 0 True True in 200 True True False True True 1 True False <b>Show mail message</b> True True True 0 True False 0 none True False 12 True False 0 label True False <b>Check file permissions</b> True True True 1 True False 0 none True False 12 True False vertical True False <b>Run command</b> True True True 2 True False <b>Proposed actions</b> True True True 3 True False end gtk-close True True True True False False 0 False True 4 4 400 500 False Sync Mail Dir applet center mail-send-receive True True 5 True False 10 vertical 10 True False 10 10 10 True False vertical True True 0 True False 0 Synchronization False True 1 True True True False True 2 True False vertical True True 3 False False 0 True False 0 <b>Logs</b> True False True 1 True False vertical 14 True True True True etched-in 500 200 True True False 0 1 1 1 True True 2 True False Execution False True True never True False True False 10 vertical 4 True False vertical 5 True False 5 True False gtk-dialog-error False True 0 True False 0 smd-applet needs smd-loop to be properly configured True True True 1 False True 0 True False 5 True False 0.49000000953674316 gtk-dialog-error False True 0 True False 0 smd-applet needs smd-push/pull to be properly configured True True True 1 False True 1 False True 0 True False 0 <b>Applet</b> True False True 1 True False 5 5 10 0 in True False 10 3 True False True False 0 Notify when new mail is pulled True True 0 True True False True 1 True True 0 True False True False 0 Start automatically True True 0 True True False True 1 True True 1 False True 2 True False 0 <b>Synchronization Loop</b> True False True 3 True False 5 5 10 0 in True False 10 10 True False True False gtk-help 6 False False 0 True False 0 smd-loop controls the frequency of synchronizations, and is run by smd-applet. The configuration file is line oriented. Each line is composed of three space separated fields: pull-frequency, push-frequency and endpoint-name. True False True 1 True True 0 True False center gtk-edit True True True True False False 4 0 False True 1 False True 4 True False 0 <b>Pull and Push</b> True False True 5 True False 5 5 10 0 in True False 10 10 True False True False gtk-help 6 False False 0 True False 0 3 smd-push and smd-pull are run timely by smd-loop. Each endpoint you want to sync with has to be configured properly, defining its name and the mail forlder path. This interface allows you to edit only the default endpoint. True False True 1 True True 0 True False gtk-edit True True True True False False 4 0 False True 1 False True 6 1 True False Configuration 1 False True False 10 vertical 48 48 True False mail-send-receive 6 False False 0 True False <b>Sync Mail Dir</b> True False True 1 True False GNOME applet for syncmaildir False True 2 syncmaildir website True True True True none http://syncmaildir.sourceforge.net False True 3 True False label False True 4 True False This program comes with ABSOLUTELY NO WARRANTY and is distributed under the following terms: center False True 5 True True 10 in True False True True False word False tbLicense True True 6 2 True False About 2 False syncmaildir-1.2.6.2/smd-applet.vala000066400000000000000000000765731262303536600171410ustar00rootroot00000000000000// Released under the terms of GPLv3 or at your option any later version. // No warranties. // Copyright Enrico Tassi errordomain Exit { ABORT } bool verbose = false; void debug(string message) { if (verbose) stderr.printf("DEBUG: %s\n",message); } // minimalistic NetworkManager interface [DBus (name = "org.freedesktop.NetworkManager")] interface NetworkManager : Object { public signal void state_changed(uint state); // throws IOErrror; public abstract uint state { owned get; } } const string NM_SERVICE = "org.freedesktop.NetworkManager"; const string NM_PATH = "/org/freedesktop/NetworkManager"; // Checks if there is a connection // 70 is NM_STATE_CONNECTED_GLOBAL in NetworkManager >= 0.9 // 3 is NM_STATE_CONNECTED in NetworkManager < 0.9 // We may want to check for >= 50 or 60, since they are respectively LOCAL // and SITE connections. static bool is_nm_connected(uint code) { return code == 3 || code == 70; } // a simple class to pass data from the child process to the // notifier class Event { public string message = null; public string message_icon = "gtk-about"; public bool enter_network_error_mode = false; public bool enter_error_mode = false; public bool transient_error_message = false; // fields meaningful for the error mode only public string context = null; public string cause = null; public string permissions = null; public string mail_name = null; public string mail_body = null; public Gee.ArrayList commands = null; // constructors public static Event error(string account, string host, string context, string cause, string? permissions, string? mail_name, string? mail_body, Gee.ArrayList commands) { var e = new Event(); e.message = "An error occurred, click on the icon for more details"; e.message_icon = "error"; e.enter_error_mode = true; e.cause = cause; e.context = context; e.permissions = permissions; e.mail_name = mail_name; e.mail_body = mail_body; e.commands = commands; return e; } public static Event generic_error(string cause) { var e = new Event(); e.message = "A failure occurred: "+cause; e.message_icon = "dialog-warning"; e.transient_error_message = true; return e; } public static Event network_error() { var e = new Event(); e.message = "A persistent network failure occurred"; e.message_icon = "dialog-warning"; e.enter_network_error_mode = true; return e; } public static Event stats( string account,string host,int new_mails,int del_mails) { string preamble = "Synchronize with %s:\n".printf(account); var e = new Event(); if (new_mails > 0 && del_mails > 0) { e.message = "%s%d new messages\n%d deleted messages". printf(preamble,new_mails,del_mails); } else if (new_mails > 0) { e.message = "%s%d new messages".printf(preamble,new_mails); } else { e.message = "%s%d deleted messages".printf(preamble,del_mails); } return e; } public bool is_error_event() { return (this.enter_error_mode || this.enter_network_error_mode || this.transient_error_message); } } static const string SMD_LOOP = "/bin/smd-loop"; static const string SMD_PUSH = "/bin/smd-push"; static const string SMD_APPLET_UI = "/share/syncmaildir-applet/smd-applet.ui"; static const string SMD_APPLET_DESKTOP = "/share/applications/smd-applet.desktop"; static const string GNOME_AUTOSTART_DISABLED ="X-GNOME-Autostart-enabled=false"; static string SMD_LOGS_DIR; static string SMD_LOOP_CFG; static string SMD_PP_DEF_CFG; static string XDG_AUTORUN_FILE; // the main class containing all the data smd-applet will use class smdApplet : Gtk.Application { // =================== the constants =============================== // settings keys static const string key_newmail = "notify-new-mail"; // paths, set by main() to something that depends on the // installation path public static string smd_loop_cmd = null; public static string smd_applet_ui = null; public static string smd_push_cmd = null; public static string smd_applet_desktop = null; // =================== the data ===================================== // The builder Gtk.Builder builder = null; // main widgets Gtk.StatusIcon si = null; Gtk.Window win = null; Gtk.Window err_win = null; Gtk.Notebook notebook = null; Gtk.Switch sync_active = null; // Stuff for logs display Gtk.ComboBoxText cblogs = null; Gee.ArrayList lognames = null; // the gsettings entry point GLib.Settings settings = null; // the thread to manage the child smd-loop instance GLib.Thread thread = null; bool thread_die = false; GLib.Pid pid; // smd-loop pid, initially set to 0 // communication structure between the child process (managed by a thread // and the notifier timeout handler). Mutex events_lock = Mutex(); Gee.ArrayList events = null; // if the program is stuck bool error_mode = false; bool network_error_mode = false; bool config_wait_mode; GLib.HashTable command_hash = null; // dbus connection to NetworkManager NetworkManager net_manager = null; // last persistent notification, to avoid its garbage collection // so that the callback is called when the button is clicked Notify.Notification notification = null; bool notification_server_has_persistence = false; static string menu_ui = """
_Quit app.quit
"""; // ======================= constructor ================================ // initialize data structures and build gtk+ widgets public smdApplet() throws Exit { set_application_id("org.syncmaildir"); try { register(); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); }; // load the ui file builder = new Gtk.Builder (); try { builder.add_from_file (smd_applet_ui); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); throw new Exit.ABORT("Unable to load the ui file"); } try { builder.add_from_string (menu_ui, menu_ui.length); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); throw new Exit.ABORT("Unable to load the menu ui string"); } // events queue and mutex events = new Gee.ArrayList(); // gsettings settings = new Settings("org.syncmaildir.applet"); // connect to dbus try { net_manager=Bus.get_proxy_sync(BusType.SYSTEM,NM_SERVICE,NM_PATH); net_manager.state_changed.connect((s) => { if (is_nm_connected(s)) sync_active.set_active(true); else sync_active.set_active(false); }); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); net_manager=null; } // load widgets and attach callbacks var simple_mainwin = builder.get_object("wMain") as Gtk.Window; win = new Gtk.ApplicationWindow(this); simple_mainwin.get_child().reparent(win); var w = 0; var h = 0; simple_mainwin.get_size_request(out w, out h); win.set_size_request(w,h); win.set_title(simple_mainwin.get_title()); err_win = builder.get_object("wError") as Gtk.Window; var lcopyright = builder.get_object("lCopyright") as Gtk.Label; lcopyright.set_text("Copyright " + SMDConf.COPYRIGHT); var logs_vb = builder.get_object("vbLog") as Gtk.Grid; cblogs = new Gtk.ComboBoxText(); lognames = new Gee.ArrayList(); logs_vb.attach(cblogs,0,0,1,1); cblogs.show(); update_logcontents(); var bnotify = builder.get_object("sNotify") as Gtk.Switch; bnotify.set_active(settings.get_boolean(key_newmail)); bnotify.notify["active"].connect(() => { settings.set_boolean(key_newmail,bnotify.active); }); var bautostart = builder.get_object("sAutostart") as Gtk.Switch; try { string content; if (GLib.FileUtils.get_contents(XDG_AUTORUN_FILE,out content)){ if (GLib.Regex.match_simple(GNOME_AUTOSTART_DISABLED,content)) bautostart.set_active(false); else bautostart.set_active(true); } else bautostart.set_active(false); } catch (FileError e) { stderr.printf("%s\n",e.message); } bautostart.notify["active"].connect(() => { if (bautostart.active) { string content; try { GLib.FileUtils.get_contents(smd_applet_desktop,out content); GLib.FileUtils.set_contents(XDG_AUTORUN_FILE,content); } catch (FileError e) { stderr.printf("%s\n",e.message); } } else { GLib.FileUtils.remove(XDG_AUTORUN_FILE); } }); var bc = builder.get_object("bClose") as Gtk.Button; bc.clicked.connect(close_err_action); var bel = builder.get_object("bEditLoopCfg") as Gtk.Button; bel.clicked.connect((b) => { // if not existent, create the template first try { if (!is_smd_loop_configured()){ GLib.Process.spawn_command_line_sync( "%s -t".printf(smd_loop_cmd)); } string cmd = "gnome-open %s".printf(SMD_LOOP_CFG); GLib.Process.spawn_command_line_async(cmd); is_smd_loop_configured(); } catch (GLib.SpawnError e) { stderr.printf("%s\n",e.message); } }); var bepp = builder.get_object("bEditPushPullCfg") as Gtk.Button; bepp.clicked.connect((b) => { // if not existent, create the template first try { if (!is_smd_pushpull_configured()){ GLib.Process.spawn_command_line_sync( "%s -t".printf(smd_push_cmd)); } string cmd = "gnome-open %s".printf(SMD_PP_DEF_CFG); GLib.Process.spawn_command_line_async(cmd); is_smd_pushpull_configured(); } catch (GLib.SpawnError e) { stderr.printf("%s\n",e.message); } }); // menu popped up when the user clicks on the notification area sync_active = builder.get_object("sSyncActive") as Gtk.Switch; sync_active.notify["active"].connect(() => { if (sync_active.get_active()) unpause(); else pause(); }); notebook = builder.get_object("nMain") as Gtk.Notebook; update_loglist(); GLib.Timeout.add(2000,(() => { if (win.visible) { update_loglist(); update_logcontents(); } return true; })); // status icon si = new Gtk.StatusIcon.from_icon_name("mail-send-receive"); si.set_visible(true); si.set_tooltip_text("smd-applet is running"); si.activate.connect((s) => { if ( error_mode ) err_win.reshow_with_initial_size(); else { win.show(); if ( config_wait_mode ) notebook.page = 1; else notebook.page = 0; } }); add_window(win); activate.connect(() => { }); var quit = new SimpleAction("quit", null); var menu = builder.get_object("app-menu") as MenuModel; set_app_menu(menu); add_action(quit); quit.activate.connect(() => { thread_die = true; if ((int)pid != 0) { debug("sending SIGTERM to %d".printf(-(int)pid)); Posix.kill((Posix.pid_t)(-(int)pid),Posix.SIGTERM); } this.quit(); }); // notification system List l = Notify.get_server_caps(); notification_server_has_persistence = (0 <= l.index("persistence")); // error mode data command_hash = new GLib.HashTable( GLib.direct_hash,GLib.str_equal); } // ===================== smd-loop handling ============================ // This thread fills the event queue, parsing the // stdout of a child process private void *smdThread() { bool rc = true; while(rc && !thread_die){ debug("(re)starting smd-loop"); try { rc = run_smd_loop(); } catch (Exit e) { rc = false; } // unrecoverable error } return null; } // force starts the thread even if there is no connection private void start_smdThread(bool force = false) { // if no network, we do not start the thread and enter pause mode // immediately if (!force && net_manager != null && !is_nm_connected(net_manager.state)) { sync_active.set_active(false); si.set_from_stock("gtk-media-pause"); } else { // the thread fills the event queue thread = new GLib.Thread(null, smdThread); } } private bool eval_smd_loop_error_message( string args, string account, string host) throws GLib.RegexError{ var context = new GLib.Regex("context\\(([^\\)]+)\\)"); var cause = new GLib.Regex("probable-cause\\(([^\\)]+)\\)"); var human = new GLib.Regex("human-intervention\\(([^\\)]+)\\)"); var actions=new GLib.Regex("suggested-actions\\((.*)\\) *$"); GLib.MatchInfo i_ctx=null, i_cause=null, i_human=null, i_act=null; if (! context.match(args,0,out i_ctx)){ stderr.printf("smd-loop error with no context: %s\n",args); return true; } if (! cause.match(args,0,out i_cause)){ stderr.printf("smd-loop error with no cause: %s\n",args); return true; } if (! human.match(args,0,out i_human)){ stderr.printf("smd-loop error with no human: %s\n",args); return true; } var has_actions = actions.match(args,0,out i_act); if ( i_human.fetch(1) != "necessary" && i_cause.fetch(1) == "network"){ events_lock.lock(); events.insert(events.size, Event.network_error()); events_lock.unlock(); return true; } if ( i_human.fetch(1) != "necessary" ){ stderr.printf("smd-loop giving an avoidable error: %s\n", args); events_lock.lock(); events.insert(events.size, Event.generic_error(i_cause.fetch(1))); events_lock.unlock(); return true; } string permissions = null; string mail_name = null; string mail_body = null; var commands = new Gee.ArrayList( Gee.Functions.get_equal_func_for(typeof(string))); if (has_actions) { string acts = i_act.fetch(1); var r_perm = new GLib.Regex("display-permissions\\(([^\\)]+)\\)"); var r_mail = new GLib.Regex("display-mail\\(([^\\)]+)\\)"); var r_cmd = new GLib.Regex("run\\(([^\\)]+)\\)"); int from = 0; for (;acts != null && acts.length > 0;){ MatchInfo i_cmd = null; if ( r_perm.match(acts,0,out i_cmd) ){ i_cmd.fetch_pos(0,null,out from); string file = i_cmd.fetch(1); string output = null; string err = null; try { GLib.Process.spawn_command_line_sync( "ls -ld " + file, out output, out err); permissions = output + err; } catch (GLib.SpawnError e) { stderr.printf("Spawning ls: %s\n",e.message); } } else if ( r_mail.match(acts,0,out i_cmd) ){ i_cmd.fetch_pos(0,null,out from); string file = i_cmd.fetch(1); string output = ""; string err = null; try { mail_name = file; GLib.Process.spawn_command_line_sync( "cat " + file, out output, out err); mail_body = output + err; } catch (GLib.SpawnError e) { stderr.printf("Spawning ls: %s\n",e.message); } } else if ( r_cmd.match(acts,0,out i_cmd) ){ string command = i_cmd.fetch(1); i_cmd.fetch_pos(0,null,out from); commands.insert(commands.size,command); } else { stderr.printf("Unrecognized action: %s\n",acts); break; } acts = acts.substring(from); } } events_lock.lock(); events.insert(events.size, Event.error( account,host,i_ctx.fetch(1), i_cause.fetch(1), permissions, mail_name, mail_body, commands)); events_lock.unlock(); return false; } // return true if successful, false to stop due to an error private bool eval_smd_loop_message(string s){ try { GLib.MatchInfo info = null; var r_tags = new GLib.Regex( "^([^:]+): smd-(client|server|loop|push|pull|pushpull)@([^:]+): TAGS:(.*)$"); var r_skip = new GLib.Regex( "^([^:]+): smd-(client|server)@([^:]+): ERROR"); if (r_skip.match(s,0,null)) { return true; } if (!r_tags.match(s,0,out info)) { debug("unhandled smd-loop message: %s".printf(s)); return true; } var account = info.fetch(1); var host = info.fetch(3); var tags = info.fetch(4); GLib.MatchInfo i_args = null; var r_stats = new GLib.Regex(" stats::(.*)$"); var r_error = new GLib.Regex(" error::(.*)$"); if (r_stats.match(tags,0,out i_args)) { var r_neW = new GLib.Regex("new-mails\\(([0-9]+)\\)"); var r_del = new GLib.Regex("del-mails\\(([0-9]+)\\)"); GLib.MatchInfo i_new = null, i_del = null; var args = i_args.fetch(1); // check if matches var has_new = r_neW.match(args,0,out i_new); var has_del = r_del.match(args,0,out i_del); int new_mails = 0; if (has_new) { new_mails = int.parse(i_new.fetch(1)); } int del_mails = 0; if (has_del) { del_mails = int.parse(i_del.fetch(1)); } if (host == "localhost" && (new_mails > 0 || del_mails > 0)) { events_lock.lock(); events.insert(events.size, Event.stats(account,host,new_mails, del_mails)); events_lock.unlock(); } else { // we skip non-error messages from the remote host } return true; } else if (r_error.match(tags,0,out i_args)) { var args = i_args.fetch(1); return eval_smd_loop_error_message(args,account,host); } else { stderr.printf("unhandled smd-loop message: %s\n",s); return true; } } catch (GLib.RegexError e) { stderr.printf("%s\n",e.message); } return true; } // runs smd loop once, returns true if it stopped // with a recoverable error and thus should be restarted private bool run_smd_loop() throws Exit { string[] cmd = { smd_loop_cmd, "-v" }; int child_in; int child_out; int child_err; char[] buff = new char[10240]; GLib.SpawnFlags flags = 0; bool rc; debug("spawning %s\n".printf(smd_loop_cmd)); try { rc = GLib.Process.spawn_async_with_pipes( null,cmd,null,flags,() => { // create a new session Posix.setpgid(0,0); }, out pid, out child_in, out child_out, out child_err); } catch (GLib.Error e) { stderr.printf("Unable to execute "+ smd_loop_cmd+": "+e.message+"\n"); throw new Exit.ABORT("Unable to run smd-loop"); } if (rc) { var input = GLib.FileStream.fdopen(child_out,"r"); string s = null; bool goon = true; while ( goon && (s = input.gets(buff)) != null && !thread_die) { debug("smd-loop outputs: %s".printf(s)); goon = eval_smd_loop_message(s); debug("eval_smd_loop_message returned %d".printf((int)goon)); } if ( s != null ) { // smd-loop prints the error tag as its last action if ( (s = input.gets(buff)) != null ){ stderr.printf("smd-loop gave error tag but not died\n"); stderr.printf("smd-loop has pid %d and prints %s\n", (int)pid, s); } } GLib.Process.close_pid(pid); Posix.kill((Posix.pid_t) (-(int)pid),Posix.SIGTERM); return goon; // maybe true, if s == null } else { stderr.printf("Unable to execute "+smd_loop_cmd+"\n"); throw new Exit.ABORT("Unable to run smd-loop"); } } // process an event in the events queue by notifying the user // with its message bool eat_event() { Event e = null; // in error mode no events are processed if ( error_mode ) return true; // fetch the event events_lock.lock(); if ( events.size > 0) { e = events.first(); events.remove_at(0); } events_lock.unlock(); // notification if ( e != null && e.message != null) { bool notify_on_newail = false; notify_on_newail = settings.get_boolean(key_newmail); if (e.enter_network_error_mode && network_error_mode) { // we avoid notifying the network problem more than once } else if ((!e.is_error_event() && notify_on_newail) || (e.is_error_event() && e.enter_network_error_mode)) { notification = new Notify.Notification( "Syncmaildir",e.message,e.message_icon); notification.set_hint("transient",new Variant.boolean(true)); try { notification.show(); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); } } else if (e.is_error_event()) { notification = new Notify.Notification( "Syncmaildir",e.message,e.message_icon); notification.set_timeout(0); notification.add_action("clicked","Handle error", (not, action) => { err_win.reshow_with_initial_size(); }); try { notification.show(); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); } } } // behavioural changes, like entering error mode if ( e != null && e.enter_error_mode ) { // {{{ error notification and widget setup si.set_from_icon_name("error"); si.set_tooltip_text("smd-applet encountered an error"); error_mode = true; if (!notification_server_has_persistence) si.set_visible(true); var l_ctx = builder.get_object("lContext") as Gtk.Label; var l_cause = builder.get_object("lCause") as Gtk.Label; l_ctx.set_text(e.context); l_cause.set_text(e.cause); command_hash.remove_all(); var vb = builder.get_object("vbRun") as Gtk.Grid; foreach(Gtk.Widget w in vb.get_children()){ vb.remove(w); } if (e.permissions != null) { var l = builder.get_object("lPermissions") as Gtk.Label; l.set_text(e.permissions); } if (e.mail_name != null) { var fn = builder.get_object("eMailName") as Gtk.Entry; fn.set_text(e.mail_name); var l = builder.get_object("tvMail") as Gtk.TextView; Gtk.TextBuffer b = l.get_buffer(); b.set_text(e.mail_body,-1); Gtk.TextIter it,subj; b.get_start_iter(out it); if (it.forward_search("Subject:", Gtk.TextSearchFlags.TEXT_ONLY, out subj,null,null)){ var insert = b.get_insert(); b.select_range(subj,subj); l.scroll_to_mark(insert,0.0,true,0.0,0.0); } } if (e.commands != null) { foreach (string command in e.commands) { var hb = new Gtk.Grid(); hb.set_column_homogeneous(false); hb.set_column_spacing(10); string nice_command; try { GLib.MatchInfo i_mailto; var mailto_rex = new GLib.Regex("^gnome-open..mailto:"); if ( mailto_rex.match(command,0,out i_mailto)) { nice_command = GLib.Uri.unescape_string(command). substring(12,70) + "..."; } else { nice_command = command; } } catch (GLib.RegexError e) { nice_command = command; } var lbl = new Gtk.Label(nice_command); lbl.set_alignment(0.0f,0.5f); var but = new Gtk.Button.from_stock("gtk-execute"); command_hash.insert(but,command); but.clicked.connect((b) => { int cmd_status; string output; string error; debug("executing: %s\n".printf(command_hash.lookup(b))); //XXX take host into account try{ GLib.Process.spawn_command_line_sync( command_hash.lookup(b), out output,out error,out cmd_status); if (GLib.Process.if_exited(cmd_status) && 0==GLib.Process.exit_status(cmd_status)){ // OK! b.set_sensitive(false); } else { var w = new Gtk.MessageDialog(err_win, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "An error occurred:\n%s\n%s",output,error); w.run(); w.destroy(); } } catch (GLib.SpawnError e) { stderr.printf("Spawning: %s\n",e.message); } }); hb.attach(lbl,1,0,1,1); hb.attach(but,0,0,1,1); vb.add(hb); hb.show_all(); } } var x = builder.get_object("fDisplayPermissions") as Gtk.Widget; x.visible = (e.permissions != null); x = builder.get_object("fDisplayMail") as Gtk.Widget; x.visible = (e.mail_name != null); x = builder.get_object("fRun") as Gtk.Widget; x.visible = (e.commands.size > 0); // }}} } else if (e != null && e.enter_network_error_mode ) { // network error warning network_error_mode = true; si.set_from_icon_name("dialog-warning"); si.set_tooltip_text("Network error"); } else if (e != null) { // no error network_error_mode = false; si.set_from_icon_name("mail-send-receive"); si.set_tooltip_text("smd-applet is running"); } return true; // re-schedule me please } // ===================== named signal handlers ======================= // these are just wrappers for close_err private void close_err_action(Gtk.Button b){ reset_to_regular_run(); } private bool close_err_event(Gdk.EventAny e){ reset_to_regular_run(); return true; } private void reset_to_regular_run(bool force = false) { err_win.hide(); error_mode = false; si.set_tooltip_text("smd-applet is running"); si.set_from_icon_name("mail-send-receive"); debug("joining smdThread"); if (thread != null) thread.join(); thread_die = false; debug("starting smdThread"); start_smdThread(force); } // these are just wrappers for close_prefs private bool close_prefs_event(Gdk.EventAny e){ close_prefs(); return true; } // close the prefs button, eventually start the theread if exiting // config_wait_mode private void close_prefs(){ win.hide(); if (is_smd_stack_configured() && config_wait_mode) { config_wait_mode = false; // restore the default icon si.set_from_icon_name("mail-send-receive"); // start the thread (if connected) debug("starting smdThread since smd stack is configured"); start_smdThread(); } } // pause/unpause the program private void pause() { debug("enter pause mode"); if ((int)pid != 0) { debug("sending SIGTERM to %d".printf(-(int)pid)); Posix.kill((Posix.pid_t)(-(int)pid),Posix.SIGTERM); } thread_die = true; si.set_from_stock("gtk-media-pause"); si.set_tooltip_text("smd-applet is paused"); } private void unpause() { debug("exit pause mode"); reset_to_regular_run(true); } // ======================== config check =========================== private bool is_smd_loop_configured() { bool rc = GLib.FileUtils.test(SMD_LOOP_CFG,GLib.FileTest.EXISTS); var l = builder.get_object("bErrLoop") as Gtk.Box; if (!rc) l.show(); else l.hide(); return rc; } private bool is_smd_pushpull_configured() { bool rc = GLib.FileUtils.test(SMD_PP_DEF_CFG,GLib.FileTest.EXISTS); var l = builder.get_object("bErrPushPull") as Gtk.Box; if (!rc) l.show(); else l.hide(); return rc; } private bool is_smd_stack_configured() { var a = is_smd_loop_configured(); var b = is_smd_pushpull_configured(); return a && b; } // ======================== log window ================================ private void update_loglist(){ var tv = builder.get_object("tvLog") as Gtk.TextView; var b = tv.get_buffer(); try { Dir d = GLib.Dir.open(SMD_LOGS_DIR); string file; var new_lognames = new Gee.ArrayList( Gee.Functions.get_equal_func_for(typeof(string))); while ( (file = d.read_name()) != null ){ new_lognames.add(file); } if (!new_lognames.contains_all(lognames) || !lognames.contains_all(new_lognames)) { ((Gtk.ListStore)cblogs.get_model()).clear(); lognames.clear(); foreach (string f in new_lognames) { lognames.add(f); cblogs.append_text(f); } if (lognames.size == 0) { b.set_text("No logs in %s".printf(SMD_LOGS_DIR),-1); } else { cblogs.set_title("Choose log file"); cblogs.set_active(0); } } } catch (GLib.FileError e) { b.set_text("Unable to list directory %s".printf(SMD_LOGS_DIR),-1); } } void update_logcontents() { int selected = cblogs.get_active(); if (selected >= 0) { string file = lognames.get(selected); string content; try { if (GLib.FileUtils.get_contents( SMD_LOGS_DIR+file,out content)){ var tv = builder.get_object("tvLog") as Gtk.TextView; var b = tv.get_buffer(); Gtk.TextIter end_iter, start_iter; b.get_end_iter(out end_iter); b.get_start_iter(out start_iter); if (content != b.get_text(start_iter,end_iter,false)) { b.set_text(content,-1); b.get_end_iter(out end_iter); tv.scroll_to_iter(end_iter, 0.0, true, 0.0, 0.0); } } else { stderr.printf("Unable to read %s\n",SMD_LOGS_DIR+file); } } catch (GLib.FileError e) { stderr.printf("Unable to read %s: %s\n", SMD_LOGS_DIR+file, e.message); } } } // ====================== public methods ============================== // starts the thread and the timeout handler public void start() throws Exit { // the timout function that will eventually notify the user GLib.Timeout.add(1000, eat_event); // before running, we need the whole smd stack // to be configured if (is_smd_stack_configured()) { start_smdThread(); } else { config_wait_mode = true; } // windows will last for the whole execution, // so the (x) button should just hide them win.delete_event.connect(close_prefs_event); err_win.delete_event.connect(close_err_event); // we show the icon if we have to. // this is performed here and not in the constructor // since if we passed --configure the icon has not // to be shown if ( config_wait_mode ) { while ( Gtk.events_pending() ) Gtk.main_iteration(); si.set_visible(false); // we wait a bit, hopefully the gnome bar will be drawn in the // meanwhile Posix.sleep(5); // we draw the icon si.set_visible(true); si.set_from_icon_name("error"); // we process events to have the icon before the notification baloon while ( Gtk.events_pending() ) Gtk.main_iteration(); // we do the notification notification = new Notify.Notification( "Syncmaildir","Syncmaildir is not configured properly.", "dialog-error"); notification.set_hint("transient",new Variant.boolean(true)); notification.add_action("configure","Configure now",((n,a) => { si.activate(); })); try { notification.show(); } catch (GLib.Error e) { stderr.printf("%s\n",e.message); } } base.run(); if (thread != null) thread.join(); } } // class end // =================== main ===================================== static int main(string[] args){ string PREFIX = SMDConf.PREFIX; // handle prefix if (! GLib.FileUtils.test(PREFIX + SMD_APPLET_UI,GLib.FileTest.EXISTS)) { stderr.printf("error: file not found: %s + %s\n", PREFIX, SMD_APPLET_UI); smdApplet.smd_loop_cmd = "./smd-loop"; stderr.printf("smd-applet not installed, " + "assuming smd-loop is: %s\n", smdApplet.smd_loop_cmd); smdApplet.smd_applet_ui = "./smd-applet.ui"; stderr.printf("smd-applet not installed, " + "assuming smd-applet.ui is: %s\n", smdApplet.smd_applet_ui); smdApplet.smd_push_cmd = "./smd-push"; stderr.printf("smd-applet not installed, " + "assuming smd-push is: %s\n", smdApplet.smd_push_cmd); smdApplet.smd_applet_desktop = "./smd-applet.desktop"; stderr.printf("smd-applet not installed, " + "assuming smd-applet.desktop is: %s\n", smdApplet.smd_applet_desktop); } else { smdApplet.smd_loop_cmd = PREFIX + SMD_LOOP; smdApplet.smd_push_cmd = PREFIX + SMD_PUSH; smdApplet.smd_applet_ui = PREFIX + SMD_APPLET_UI; smdApplet.smd_applet_desktop = PREFIX + SMD_APPLET_DESKTOP; } var homedir = GLib.Environment.get_home_dir(); SMD_LOGS_DIR = homedir+"/.smd/log/"; SMD_LOOP_CFG = homedir+"/.smd/loop"; SMD_PP_DEF_CFG = homedir+"/.smd/config.default"; var conf_home = GLib.Environment.get_variable("XDG_CONFIG_HOME"); if (conf_home != null) XDG_AUTORUN_FILE = conf_home+"/autostart/smd-applet.desktop"; else XDG_AUTORUN_FILE = homedir + "/.config/autostart/smd-applet.desktop"; GLib.DirUtils.create_with_parents( GLib.Path.get_dirname(XDG_AUTORUN_FILE),0700); // we init gtk+ and notify Gtk.init (ref args); Notify.init("smd-applet"); GLib.OptionEntry[] oe = { GLib.OptionEntry () { long_name = "verbose", short_name = 'v', flags = 0, arg = GLib.OptionArg.NONE, arg_data = &verbose, description = "verbose output, for debugging only", arg_description = null }, GLib.OptionEntry () { long_name = "smd-loop", short_name = 'l', flags = 0, arg = GLib.OptionArg.STRING, arg_data = &smdApplet.smd_loop_cmd, description = "override smd-loop command name, debugging only", arg_description = "program" }, GLib.OptionEntry () { long_name = null } }; var oc = new GLib.OptionContext(" - syncmaildir applet"); oc.add_main_entries(oe,null); try { oc.parse(ref args); } catch (GLib.OptionError e) { stderr.printf("%s\n",e.message); return 1; } // go! try { var smd_applet = new smdApplet(); smd_applet.start(); } catch (Exit e) { stderr.printf("abort: %s\n",e.message); return 1; } return 0; } // vim:set ts=4 foldmethod=marker: syncmaildir-1.2.6.2/smd-check-conf000077500000000000000000000045721262303536600167230ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi #set -x SSH="@SSH@" if [ `echo "$SSH" | cut -c -1` = "@" ]; then SSH=ssh echo "`basename $0` not installed, assuming secure shell client is $SSH" fi PREFIX="@PREFIX@" MDDIFF=$PREFIX/bin/mddiff if [ `echo "$PREFIX" | cut -c -1` = "@" ]; then MDDIFF=./mddiff SMDROOT=. echo "`basename $0` not installed, assuming mddiff is $MDDIFF" else SMDROOT=$PREFIX/share/syncmaildir fi . $SMDROOT/smd-common init parse_args "$@" read_conffile check_lockfile setup_plumbing setup_logging setup_mailboxnames if [ -z "$SERVERNAME" -o -z "$CLIENTNAME" ]; then echo "ERROR: SERVERNAME or CLIENTNAME not set" exit 1 fi if [ ! -z "$MAILBOX" -a ! -z "$MAILBOX_LOCAL" -a "$MAILBOX" != "$MAILBOX_LOCAL" ]; then echo "ERROR: both MAILBOX and MAILBOX_LOCAL set" exit 1 fi if [ ! -z "$MAILBOX" -a ! -z "$MAILBOX_REMOTE" -a "$MAILBOX" != "$MAILBOX_REMOTE" ]; then echo "ERROR: both MAILBOX and MAILBOX_REMOTE set" exit 1 fi if [ -z "$TRANSLATOR_LR" -o -z "$TRANSLATOR_RL" ]; then echo ERROR: TRANSLATOR_LR and TRANSLATOR_RL not defined exit 1 fi cd TL=`mktemp` TR=`mktemp` echo Local mailboxes translated to remote and back: $MDDIFF $LOCALEXCLUDE -l $MAILBOX_LOCAL > $TL if [ ! $? -eq 0 ]; then echo Error while listing the content of $MAILBOX_LOCAL, skipping else while read M; do MM="`echo "$M" | $TRANSLATOR_LR | head -n 1`" MMM="`echo "$MM" | $TRANSLATOR_RL | head -n 1`" echo " " $M "->" $MM "->" $MMM if [ "$M" != "$MMM" ]; then echo Failed round trip check: $M "->" $MMM echo " echo "$M" | $TRANSLATOR_LR -> $MM" echo " echo "$MM" | $TRANSLATOR_RL -> $MMM" exit 1 fi done < $TL fi echo echo Remote mailboxes translated to local and back: $SSH $SERVERNAME $MDDIFF $REMOTEEXCLUDE -l $MAILBOX_REMOTE > $TR if [ ! $? -eq 0 ]; then echo Error while listing the content of $MAILBOX_REMOTE, skipping else while read M; do MM="`echo "$M" | $TRANSLATOR_RL | head -n 1`" MMM="`echo "$MM" | $TRANSLATOR_LR | head -n 1`" echo " " $M "->" $MM "->" $MMM if [ "$M" != "$MMM" ]; then echo Failed round trip check: $M "->" $MMM echo " echo $M | $TRANSLATOR_RL -> $MM" echo " echo $MM | $TRANSLATOR_LR -> $MMM" exit 1 fi done < $TR fi rm $TL $TR echo "Endpoint $REPNAME configuration file successfully checked" exit 0 # vim:ts=4: syncmaildir-1.2.6.2/smd-check-conf.1.txt000066400000000000000000000013421262303536600176650ustar00rootroot00000000000000NAME smd-check-conf - checks smd configuration file SYNOPSIS smd-check-conf [endpoint] DESCRIPTION smd-check-conf performs some sanity check on a configuration file. It mainly tests the translators for a configured endpoint. The test consists in listing local and remote mailboxes, calling the translators, displaying the result to the user and checking for round trip (i.e. that the translators programs behave as inverse functions on the current input). Refer to smd-config(5) for a longer discussion on translators. OPTIONS endpoint Is the suffix for the name of the configuration file to use FILES ~/.smd/config.* SEE ALSO smd-translate(1), smd-config(5) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-client000077500000000000000000000605541262303536600162030ustar00rootroot00000000000000#! /usr/bin/env lua5.1 -- -- Released under the terms of GPLv3 or at your option any later version. -- No warranties. -- Copyright Enrico Tassi require 'syncmaildir' -- export syncmaildir to the global namespace for k,v in pairs(syncmaildir) do _G[k] = v end -- globals counter for statistics local statistics = { added = 0, removed = 0, received = 0, xdelta = 0, files = {}, } -- ========================= get mail queue ================================= -- queue for fetching mails in blocks of queue_max_len messages -- to cope with latency local get_full_email_queue = {} local queue_max_len = 50 function process_get_full_email_queue() local command = {} for _,v in ipairs(get_full_email_queue) do command[#command+1] = 'GET ' .. v.name end command[#command+1] = '' io.write(table.concat(command,'\n')) command = nil io.flush() local tmp = {} for _,v in ipairs(get_full_email_queue) do tmp[#tmp+1] = tmp_for(v.name) v.tmp = tmp[#tmp] statistics.received = statistics.received + receive(io.stdin, tmp[#tmp]) end tmp = nil for _,v in ipairs(get_full_email_queue) do local hsha_l, bsha_l = sha_file(v.tmp) if hsha_l == v.hsha and bsha_l == v.bsha then local rc = os.rename(v.tmp, v.name) if rc then statistics.added = statistics.added + 1 else log_error('Failed to rename '..v.tmp..' to '..v.name) log_error('It may be caused by bad directory permissions, '.. 'please check.') os.remove(v.tmp) return (trace(false)) -- fail rename tmpfile to actual name end else log_error('The server sent a different email for '..v.name) log_error('This problem should be transient, please retry.') os.remove(v.tmp) return (trace(false)) -- get full email failed, received wrong mail end end get_full_email_queue = {} return (trace(true)) -- get full email OK end function process_pending_queue() local rc = process_get_full_email_queue() if not rc then io.write('ABORT\n') io.flush() os.exit(1) end end -- the function to fetch a mail message function get_full_email(name,hsha,bsha) if dry_run() then statistics.added = statistics.added + 1 statistics.files[#statistics.files + 1] = name return true end get_full_email_queue[#get_full_email_queue+1] = { name = name; hsha = hsha; bsha = bsha; } return true end -- ======================== header replacing ================================= function merge_mail(header,body,target) local h = io.open(header,"r") local b = io.open(body,"r") local t = io.open(target,"w") local l while true do l = h:read("*l") if l and l ~= "" then t:write(l,'\n') else break end end while true do l = b:read("*l") if not l or l == "" then break end end t:write('\n') while true do l = b:read("*l") if l then t:write(l,'\n') else break end end h:close() b:close() t:close() end function get_header_and_merge(name,hsha) local tmpfile = tmp_for(name) io.write('GETHEADER '..name..'\n') io.flush() receive(io.stdin, tmpfile) local hsha_l, _ = sha_file(tmpfile) if hsha_l == hsha then if not dry_run() then local tmpfile1 = tmp_for(name) merge_mail(tmpfile,name,tmpfile1) os.remove(tmpfile) os.rename(tmpfile1, name) else -- we delete the new piece without merging (--dry-run) os.remove(tmpfile) end return (trace(true)) -- get header OK else os.remove(tmpfile) log_error('The server sent a different email header for '..name) log_error('This problem should be transient, please retry.') log_tags("receive-header","modify-while-update",false,"retry") return (trace(false)) -- get header fails, got a different header end end -- ============================= renaming ==================================== function compute_renamings(actions) local copy = {} local delete = {} local script = {} for _, cmd in ipairs(actions) do local opcode = parse(cmd, '^(%S+)') if opcode == "COPY" then local name_src, hsha, bsha, name_tgt = parse(cmd, '^COPY (%S+) (%S+) (%S+) TO (%S+)$') name_src = url_decode(name_src) name_tgt = url_decode(name_tgt) copy[#copy + 1] = { src = name_src, tgt = name_tgt} elseif opcode == "DELETE" then local name, hsha, bsha = parse(cmd, '^DELETE (%S+) (%S+) (%S+)$') name = url_decode(name) delete[name] = 1 elseif opcode == 'ERROR' then local msg = parse(cmd, '^ERROR (.*)$') execute_error(msg) io.write('ABORT\n') io.flush() os.exit(6) end end for _, cp in ipairs(copy) do -- it is a real move if delete[cp.src] then local absolute1, t1, last1 = tokenize_path(cp.src) local absolute2, t2, last2 = tokenize_path(cp.tgt) local dir1 = table.concat(t1,'/') local dir2 = table.concat(t2,'/') if not absolute1 and not absolute2 and last1 ~= nil and last2 ~= nil and (t1[#t1] == "cur" or t1[#t1] == "new") and (t2[#t2] == "cur" or t2[#t2] == "new") and dir1 == dir2 then --and is_translator_set() then local t_dir = homefy(translate(dir1)) if delete[cp.src] > 1 then table.insert(script,1,string.format("cp %s %s", quote(t_dir..'/'..last1), quote(t_dir..'/'..last2))) else script[#script + 1] = string.format("mv %s %s", quote(t_dir..'/'..last1), quote(t_dir..'/'..last2)) end delete[cp.src] = delete[cp.src] + 1 end end end return script end -- ============================= actions ===================================== function execute_add(name, hsha, bsha) local ex, hsha_l, bsha_l = exists_and_sha(name) if ex then if hsha == hsha_l and bsha == bsha_l then return (trace(true)) -- skipping add since already there else log_error('Failed to add '..name.. ' since a file with the same name') log_error('exists but its content is different.') log_error('To fix this problem you should rename '..name) log_error('Executing `cd; mv -n '..quote(name)..' '.. quote(tmp_for(name,false))..'` should work.') log_tags("mail-addition","concurrent-mailbox-edit",true, mk_act("mv", name)) return (trace(false)) -- skipping add since already there but != end end return (get_full_email(name,hsha,bsha)) end function execute_delete(name, hsha, bsha) local ex, hsha_l, bsha_l = exists_and_sha(name) if ex then if hsha == hsha_l and bsha == bsha_l then local rc if not dry_run() then rc = os.remove(name) else rc = true -- we do not delete the message for real (--dry-run) end if rc then statistics.removed = statistics.removed + 1 return (trace(true)) -- removed successfully else log_error('Deletion of '..name..' failed.') log_error('It may be caused by bad directory permissions, '.. 'please check.') log_tags("delete-message","bad-directory-permission",true, mk_act("permission",name)) return (trace(false)) -- os.remove failed end else log_error('Failed to delete '..name.. ' since the local copy of it has') log_error('modifications.') log_error('To fix this problem you have two options:') log_error('- delete '..name..' by hand') log_error('- run @@INVERSECOMMAND@@ so that this file is added '.. 'to the other mailbox') log_tags("delete-message", "concurrent-mailbox-edit",true, mk_act('display',name), mk_act('rm',name), "run(@@INVERSECOMMAND@@ @@ENDPOINT@@)") return (trace(false)) -- remove fails since local file is != end end return (trace(true)) -- already removed end function execute_copy(name_src, hsha, bsha, name_tgt) local ex_src, hsha_src, bsha_src = exists_and_sha(name_src) local ex_tgt, hsha_tgt, bsha_tgt = exists_and_sha(name_tgt) if ex_src and ex_tgt then if hsha_src == hsha_tgt and bsha_src == bsha_tgt and hsha_src == hsha and bsha_src == bsha then return (trace(true)) -- skip copy, already there else log_error('Failed to copy '..name_src..' to '..name_tgt) log_error('The destination already exists but its content differs.') log_error('To fix this problem you have two options:') log_error('- rename '..name_tgt..' by hand so that '..name_src) log_error(' can be copied without replacing it.') log_error(' Executing `cd; mv -n '..quote(name_tgt)..' '.. quote(tmp_for(name_tgt,false))..'` should work.') log_error('- run @@INVERSECOMMAND@@ so that your changes to '.. name_tgt) log_error(' are propagated to the other mailbox') log_tags("copy-message","concurrent-mailbox-edit",true, mk_act('mv',name_tgt), "run(@@INVERSECOMMAND@@ @@ENDPOINT@@)") return (trace(false)) -- fail copy, already there but != end elseif ex_src and not ex_tgt then if hsha_src == hsha and bsha_src == bsha then local ok, err if not dry_run() then ok, err = cp(name_src,name_tgt) else ok = 0 -- we do not copy for real (--dry-run) end if ok == 0 then return (trace(true)) -- copy successful else log_error('Failed to copy '..name_src..' to '..name_tgt.. ' : '..(err or 'unknown error')) log_tags("delete-message","bad-directory-permission",true, mk_act('display', name_tgt)) return (trace(false)) -- copy failed (cp command failed) end else -- sub-optimal, we may reuse body or header return (get_full_email(name_tgt,hsha,bsha)) end elseif not ex_src and ex_tgt then if hsha == hsha_tgt and bsha == bsha_tgt then return (trace(true)) -- skip copy, already there (only the copy) else log_error('Failed to copy '..name_src..' to '..name_tgt) log_error('The source file has been locally removed.') log_error('The destination file already exists but its '.. 'content differs.') log_error('To fix this problem you have two options:') log_error('- rename '..name_tgt..' by hand so that '.. name_src..' can be') log_error(' copied without replacing it.') log_error(' Executing `cd; mv -n '..quote(name_tgt)..' '.. quote(tmp_for(name_tgt,false))..'` should work.') log_error('- run @@INVERSECOMMAND@@ so that your changes to '.. name_tgt..' are') log_error(' propagated to the other mailbox') log_tags("copy-message","concurrent-mailbox-edit",true, mk_act('mv', name_tgt), "run(@@INVERSECOMMAND@@ @@ENDPOINT@@)") return (trace(false)) -- skip copy, already there and !=, no source end else return (get_full_email(name_tgt,hsha,bsha)) end end function execute_move(name_src, hsha, bsha, name_tgt) local ex_src, hsha_src, bsha_src = exists_and_sha(name_src) local ex_tgt, hsha_tgt, bsha_tgt = exists_and_sha(name_tgt) if ex_src and ex_tgt then if hsha_tgt == hsha and bsha_tgt == bsha then -- the target is already in place if hsha_src == hsha and bsha_src == bsha then return (execute_delete(name_src,hsha,bsha)) else return (trace(true)) -- the source has changes, nothing to do end else log_error('Failed to move '..name_src..' to '..name_tgt) log_error('The destination already exists but its content differs.') log_error('To fix this problem you have two options:') log_error('- rename '..name_tgt..' by hand so that '..name_src) log_error(' can be copied without replacing it.') log_error(' Executing `cd; mv -n '..quote(name_tgt)..' '.. quote(tmp_for(name_tgt,false))..'` should work.') log_error('- run @@INVERSECOMMAND@@ so that your changes to '.. name_tgt) log_error(' are propagated to the other mailbox') log_tags("move-message","concurrent-mailbox-edit",true, mk_act('mv',name_tgt), "run(@@INVERSECOMMAND@@ @@ENDPOINT@@)") return (trace(false)) -- fail move, already there but != end elseif ex_src and not ex_tgt then if hsha_src == hsha and bsha_src == bsha then local ok, err if not dry_run() then ok, err = os.rename(name_src,name_tgt) else ok = true -- we do not move for real (--dry-run) end if ok then return (trace(true)) -- move successful else log_error('Failed to move '..name_src..' to '..name_tgt.. ' : '..(err or 'unknown error')) log_tags("move-message","bad-directory-permission",true, mk_act('display', name_tgt)) return (trace(false)) -- copy failed (cp command failed) end else -- sub-optimal, we may reuse body or header return (get_full_email(name_tgt,hsha,bsha)) end elseif not ex_src and ex_tgt then if hsha == hsha_tgt and bsha == bsha_tgt then return (trace(true)) -- skip move, already there (and no source) else log_error('Failed to move '..name_src..' to '..name_tgt) log_error('The source file has been locally removed.') log_error('The destination file already exists but its '.. 'content differs.') log_error('To fix this problem you have two options:') log_error('- rename '..name_tgt..' by hand so that '.. name_src..' can be') log_error(' copied without replacing it.') log_error(' Executing `cd; mv -n '..quote(name_tgt)..' '.. quote(tmp_for(name_tgt,false))..'` should work.') log_error('- run @@INVERSECOMMAND@@ so that your changes to '.. name_tgt..' are') log_error(' propagated to the other mailbox') log_tags("copy-message","concurrent-mailbox-edit",true, mk_act('mv', name_tgt), "run(@@INVERSECOMMAND@@ @@ENDPOINT@@)") return (trace(false)) -- skip copy, already there and !=, no source end else return (get_full_email(name_tgt,hsha,bsha)) end end function execute_replaceheader(name, hsha, bsha, hsha_new) if exists(name) then local hsha_l, bsha_l = sha_file(name) if hsha == hsha_l and bsha == bsha_l then return (get_header_and_merge(name,hsha_new)) elseif hsha_l == hsha_new and bsha == bsha_l then return (trace(true)) -- replace header ok, already changend else log_error('Failed to replace '..name..' header since it has local') log_error(' modifications.') log_error('To fix this problem you should rename '..name) log_error('Executing `cd; mv -n '..quote(name)..' '.. quote(tmp_for(name,false))..'` should work.') log_tags("header-replacement","concurrent-mailbox-edit",true, mk_act('mv', name)) return (trace(false)) -- replace header fails, local header != end else return (get_full_email(name,hsha_new,bsha)) end end function execute_copybody(name, bsha, newname, hsha) local exn, hsha_ln, bsha_ln = exists_and_sha(newname) if not exn then local ex, _, bsha_l = exists_and_sha(name) if ex and bsha_l == bsha then local ok, err if not dry_run() then ok, err = cp(name,newname) else ok = 0 -- we do not copy the body for merging (--dry-run) end if ok == 0 then ok = get_header_and_merge(newname,hsha) if ok then return (trace(true)) -- copybody OK else os.remove(newname) return (trace(false)) -- copybody failed, bad new header end else log_error('Failed to copy '..name..' to '..newname..' : '.. (err or 'unknown error')) log_tags("copy-message","bad-directory-permission",true, mk_act('display', newname)) return (trace(false)) -- copybody failed (cp command failed) end else return(get_full_email(newname,hsha,bsha)) end else if bsha == bsha_ln and hsha == hsha_ln then return (trace(true)) -- copybody OK (already there) else log_error('Failed to copy body of '..name..' to '..newname) log_error('To fix this problem you should rename '..newname) log_error('Executing `cd; mv -n '..quote(newname)..' '.. quote(tmp_for(newname,false))..'` should work.') log_tags("copy-body","concurrent-mailbox-edit",true, mk_act('mv', newname)) return (trace(false)) -- copybody failed (already there, != ) end end end function execute_replace(name1, hsha1, bsha1, hsha2, bsha2) local exn, hsha_ln, bsha_ln = exists_and_sha(name1) if not exn then return(get_full_email(name1,hsha2,bsha2)) else if bsha2 == bsha_ln and hsha2 == hsha_ln then return (trace(true)) -- replace OK (already there) elseif bsha1 == bsha_ln and hsha1 == hsha_ln then return(get_full_email(name1,hsha2,bsha2)) else log_error('Failed to replace '..name1) log_error('To fix this problem you should rename '..name1) log_error('Executing `cd; mv -n '..quote(name1)..' '.. quote(tmp_for(name1,false))..'` should work.') log_tags("replace","concurrent-mailbox-edit",true, mk_act('mv', name1)) return (trace(false)) -- replace failed (already there, != ) end end end function execute_error(msg) log_error('mddiff failed: '..msg) if msg:match('^Unable to open directory') then log_tags("mddiff","directory-disappeared",false) else log_tags("mddiff","unknown",true) end return (trace(false)) -- mddiff error end -- the main switch, dispatching actions. -- extra parentheses around execute_* calls make it a non tail call, -- thus we get the stack frame print in case of error. function execute(cmd) local opcode = parse(cmd, '^(%S+)') if opcode == "ADD" then local name, hsha, bsha = parse(cmd, '^ADD (%S+) (%S+) (%S+)$') name = url_decode(name) mkdir_p(name) return (execute_add(name, hsha, bsha)) end if opcode == "DELETE" then local name, hsha, bsha = parse(cmd, '^DELETE (%S+) (%S+) (%S+)$') name = url_decode(name) mkdir_p(name) return (execute_delete(name, hsha, bsha)) end if opcode == "COPY" then local name_src, hsha, bsha, name_tgt = parse(cmd, '^COPY (%S+) (%S+) (%S+) TO (%S+)$') name_src = url_decode(name_src) name_tgt = url_decode(name_tgt) mkdir_p(name_src) mkdir_p(name_tgt) return (execute_copy(name_src, hsha, bsha, name_tgt)) end if opcode == "MOVE" then local name_src, hsha, bsha, name_tgt = parse(cmd, '^MOVE (%S+) (%S+) (%S+) TO (%S+)$') name_src = url_decode(name_src) name_tgt = url_decode(name_tgt) mkdir_p(name_src) mkdir_p(name_tgt) return (execute_move(name_src, hsha, bsha, name_tgt)) end if opcode == "REPLACEHEADER" then local name, hsha, bsha, hsha_new = parse(cmd, '^REPLACEHEADER (%S+) (%S+) (%S+) WITH (%S+)$') name = url_decode(name) mkdir_p(name) return (execute_replaceheader(name, hsha, bsha, hsha_new)) end if opcode == "COPYBODY" then local name, bsha, newname, hsha = parse(cmd, '^COPYBODY (%S+) (%S+) TO (%S+) (%S+)$') name = url_decode(name) newname = url_decode(newname) mkdir_p(name) mkdir_p(newname) return (execute_copybody(name, bsha, newname, hsha)) end if opcode == "REPLACE" then local name1, hsha1, bsha1, hsha2, bsha2 = parse(cmd, '^REPLACE (%S+) (%S+) (%S+) WITH (%S+) (%S+)$') name1 = url_decode(name1) mkdir_p(name1) return (execute_replace(name1, hsha1, bsha1, hsha2, bsha2)) end if opcode == "ERROR" then local msg = parse(cmd, '^ERROR (.*)$') return (execute_error(msg)) end log_internal_error_and_fail('Unknown opcode '..opcode, "protocol") end -- ============================= MAIN ===================================== -- report every n mails local report_frequency = 5000 -- receive a list of commands function receive_delta(inf, firsttime) local cmds = {} local line = "" log_progress('Phase 1: changes detection') if firsttime then log_progress([[ This phase computes the SHA1 sum of all the emails in the remote mailbox. Depending on the size of the mailbox size and the speed of the hard drive, this operation may take a lot of time. After the first synchronization it will be much faster, since only new emails have to be scanned. On a cheap laptop it takes 10m to scan a 1G mailbox.]]) end repeat line = inf:read("*l") if line and line ~= "END" then cmds[#cmds+1] = line end if #cmds % report_frequency == 0 and #cmds > 0 then log_progress(string.format(' %3dK emails scanned', #cmds / 1000)) end until not line or line == "END" if line ~= "END" then log_error('Unable to receive a complete diff') log_tags_and_fail("network error while receiving delta", "receive-delta","network",false,"retry") end return cmds end function main() -- sanity checks for external softwares assert_exists(MDDIFF) assert_exists(XDELTA) -- argument parsing local usage = "Usage: "..arg[0]:match('[^/]+$').. " [-vd] [-t translatorRL] endpointname mailboxes...\n" local apply_xdelta = true local rename_only = false local override_db = nil while #arg > 2 do if arg[1] == '-v' or arg[1] == '--verbose' then set_verbose(true) table.remove(arg,1) elseif arg[1] == '-d' or arg[1] == '--dry-run' then set_dry_run(true) table.remove(arg,1) elseif arg[1] == '-l' or arg[1] == '--local-sync' then apply_xdelta = false table.remove(arg,1) elseif arg[1] == '-t' or arg[1] == '--translator' then set_translator(arg[2]) table.remove(arg,1) table.remove(arg,1) elseif arg[1] == '--rename-only' then rename_only = true table.remove(arg,1) elseif arg[1] == '--override-db' then override_db = arg[2] table.remove(arg,1) table.remove(arg,1) else break end end if #arg < 2 then io.stderr:write(usage) os.exit(2) end -- here we go local endpoint = arg[1] table.remove(arg,1) local dbfile = nil if override_db ~= nil then dbfile = override_db:gsub('^~',os.getenv('HOME')) else dbfile = dbfile_name(endpoint, arg) end local xdelta = dbfile .. '.xdelta' local newdb = dbfile .. '.new' -- sanity check, translator and absolute paths cannot work for _, v in ipairs(arg) do if v:byte(1) == string.byte('/',1) then log_error("Absolute paths are not supported: "..v) log_tags_and_fail("Absolute path detected", "main","mailbox-has--absolute-path",true) end end -- we check the protocol version and dbfile fingerprint local firsttime = not exists(dbfile) if firsttime then log_progress('This is the first synchronization, '.. 'verbose progress report enabled.') end log_progress('Phase 0: handshake') if firsttime then log_progress(' This phase opens the ssh connection.') end handshake(dbfile) -- receive and process commands local commands = receive_delta(io.stdin, firsttime) if rename_only then -- in renaming mode, we handle commands in a peculiar way log_progress('Phase 2: renaming script generation') local script = compute_renamings(commands) local fname = os.getenv('HOME')..'/smd-rename.sh' local f = io.open(fname,'w') f:write('#!/bin/sh\n\n') f:write(table.concat(script,'\n')) f:close() log('Please check and run: '..fname) -- and we exit os.exit(0) end log_progress('Phase 2: synchronization') if firsttime then log_progress([[ This phase propagates the chages occurred to the remote mailbox to the local one. In the first run of smd-pull all remote emails are considered as new, and if not already present in the local mailbox, they are transferred over the ssh link. It is thus recommended to run the first synchronization on mailboxes that are resonably similar (i.e. not on an empty local mailbox).]]) end for i,cmd in ipairs(commands) do local rc = execute(cmd) if not rc then io.write('ABORT\n') io.flush() os.exit(3) end -- some commands are delayed, we fire them in block if #get_full_email_queue > queue_max_len then process_pending_queue() end if firsttime and i % report_frequency == 0 then log_progress(string.format(' %3d%% complete', i / #commands * 100)) end end -- some commands may still be in the queue, we fire them now process_pending_queue() -- we commit and update the dbfile log_progress('Phase 3: agreement') if firsttime then log_progress([[ This last phase concludes the agreement between the remote and the local mailbox. In particular the s $tatus of the mailbox is sent from the remote host and stored locally. The status file size is circa 7M (3M compressed) for a 1G mailbox and it needs to be transferred completely only the first time.]]) end io.write('COMMIT\n') io.flush() statistics.xdelta = receive(io.stdin, xdelta) local rc if not dry_run() and apply_xdelta then rc = os.execute(XDELTA..' patch '..xdelta..' '..dbfile..' '..newdb) else rc = 0 -- the xdelta transmitted with --dry-run is dummy end if rc ~= 0 and rc ~= 256 then log_error('Unable to apply delta to dbfile.') io.write('ABORT\n') io.flush() os.exit(4) end if not dry_run() and apply_xdelta then rc = os.rename(newdb,dbfile) else rc = true -- with --dry-run there is no xdelta affair end if not rc then log_error('Unable to rename '..newdb..' to '..dbfile) io.write('ABORT\n') io.flush() os.exit(5) end os.remove(xdelta) io.write('DONE\n') io.flush() -- some machine understandable output before quitting log_tag('stats::new-mails('.. statistics.added.. '), del-mails('..statistics.removed.. '), bytes-received('..statistics.received.. '), xdelta-received('..statistics.xdelta.. ')') if dry_run() and #statistics.files > 0 then log_tag('stats::mail-transferred('.. table.concat(statistics.files,' , ')..')') end os.exit(0) end -- no more global variables set_strict() -- parachute for errors parachute(main, 6) -- vim:set ts=4: syncmaildir-1.2.6.2/smd-client.1.txt000066400000000000000000000026121262303536600171440ustar00rootroot00000000000000NAME smd-client - receives diffs and performs actions SYNOPSIS smd-client [-v|--verbose] [-d|--dry-run] [-t|--translator] [-l|--local-sync] [--rename-only] [--override-db dbf] endpoint mailboxes DESCRIPTION smd-client needs to know a name (endpoint) for the client (that must not be used by others) and a list of mailboxes (directories). smd-client waits on stdin a mail dir diff. It attempts to execute these actions, eventually asking data printing commands on stdout and expecting the data on stdin. OPTIONS -t --translator Specify a program to translate remote mailbox names to local ones. Note that the translator 'cat' (the identity program) is optimized away -l --local-sync Local synchronization, no changes is made to the db-file since smd-server takes care of it --rename-only Do not synchronize the mailboxes but generate the script '~/smd-rename.sh' to uniform the files names in the mailbox --override-db dbf Use dbf as the db-file -v --verbose Increase program verbosity (printed on stderr) -d --dry-run Do not perform any action for real NOTES smd-client is a low level utility. You should use higher level tools like smd-pull(1) and smd-push(1) SEE ALSO mddiff(1), smd-server(1), smd-pull(1), smd-push(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-common000066400000000000000000000363241262303536600162100ustar00rootroot00000000000000# Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi # Common stuff for smd-pull and smd-push # Convention: # - uppercase variables are global # - lowercase variables are local # - function arguments are documented assigning to local variable with # a decent name positional arguments ### Housekeeping ### __TOREMOVE="" __TOKILL="" atexit_rm() { local path="$1" __TOREMOVE="$__TOREMOVE $path" } atexit_kill() { local pid="$1" __TOKILL="$__TOKILL $pid" } gc_mktemp() { local tmp=`mktemp -q /tmp/smd.XXXXXXXXXX` if [ -z "$tmp" ]; then echo "Failed to create temp file in /tmp. Is the disk full?" exit 1 fi atexit_rm $tmp RC="$tmp" } __cleanup() { rm -f $__TOREMOVE for p in $__TOKILL; do kill $p 2>/dev/null || true done } trap __cleanup "EXIT" ### Variables setup and sanity checks ### assert_executable() { if type $1 >/dev/null; then : else echo $1 not found, please install it or fix the paths echo PATH=$PATH echo type $1: `type $1` exit 1 fi } init() { if [ `echo $PREFIX | cut -c -1` = "@" ]; then SMDSERVER=./smd-server SMDCLIENT=./smd-client MDDIFF=./mddiff # in development mode we assume that on the remote host # the software is installed such that binaries are in $PATH REMOTESMDSERVER=smd-server REMOTESMDCLIENT=smd-client else SMDSERVER=$PREFIX/bin/smd-server SMDCLIENT=$PREFIX/bin/smd-client MDDIFF=$PREFIX/bin/mddiff REMOTESMDSERVER="$SMDSERVER" REMOTESMDCLIENT="$SMDCLIENT" fi H=$HOME CONFDIR=$H/.smd LOCKFILE=$CONFDIR/lock SHOWTAGS=0 VERBOSE=0 DRYRUN=0 TEMPLATE_ONLY=0 CONFFILE="" WORKAREA=$CONFDIR/workarea REPNAME=default CHILDSARGS= REMOTEEXCLUDE= LOCALEXCLUDE= SMDCLIENTOPTS= SMDSERVEROPTS= RENAMEDB=.smd/rename-db # default values for the configuration file DEBUG=false # external programs SSH="@SSH@" if [ `echo "$SSH" | cut -c -1` = "@" ]; then SSH=ssh echo "`basename $0` not installed, assuming secure shell client is $SSH" fi SED="@SED@" if [ `echo "$SED" | cut -c -1` = "@" ]; then SED=sed echo "`basename $0` not installed, assuming stream editor is $SED" fi # sanity checks for required binaries assert_executable $SED assert_executable $SSH assert_executable $MDDIFF assert_executable $SMDSERVER assert_executable $SMDCLIENT # setup of confdir $MDDIFF --mkdir-p $CONFDIR/ $MDDIFF --mkdir-p $CONFDIR/log $MDDIFF --mkdir-p $CONFDIR/fifo $MDDIFF --mkdir-p $CONFDIR/hooks cat > $CONFDIR/hooks/README <<-EOT From version 0.9.14, smd-push and smd-pull can run user defined hooks before and after doing their job. Sample hooks are available in the source tarball under sample-hooks/. The invocation of hooks is documented in the main README file. EOT $MDDIFF --mkdir-p $CONFDIR/hooks/pre-pull.d $MDDIFF --mkdir-p $CONFDIR/hooks/pre-push.d $MDDIFF --mkdir-p $CONFDIR/hooks/post-pull.d $MDDIFF --mkdir-p $CONFDIR/hooks/post-push.d CRUFT=`find $CONFDIR/workarea ! -type d -a ! -type l 2> /dev/null || true` if [ ! -z "$CRUFT" ]; then echo "Some files are left in $CONFDIR/workarea" echo "This is an internal error. Please report this inconvenience" echo "and examine the content of these files, they may be of value." echo echo $CRUFT exit 1 fi rm -rf $CONFDIR/workarea $MDDIFF --mkdir-p $CONFDIR/workarea } is_absolute() { case "$1" in /*) return 0 ;; *) return 1 ;; esac } resolve_translator(){ local T=${1%% *} if [ ! -z "$T" ]; then if is_absolute "$1"; then RC="$1" elif `type "$HOME/$T" >/dev/null 2>&1`; then RC="$HOME/$1" elif `type "$T" >/dev/null 2>&1`; then RC="$1" else echo "Unable to find the given translator: $T" echo "It is not an absolute path" echo "It is not in \$HOME=$HOME" echo "It is not in \$PATH=$PATH" if [ $showtags = 1 ]; then echo "$REPNAME: $localprog@$localhost: TAGS: error::context(conf) probable-cause(translator-not-found) human-intervention(necessary)" fi exit 1 fi else RC="cat" fi } setup_workarea(){ gc_mktemp local TMP_FIND="$RC" gc_mktemp local TMP_FIND_ERR="$RC" $MDDIFF $LOCALEXCLUDE -l $MAILBOX_LOCAL >$TMP_FIND 2>$TMP_FIND_ERR if [ ! $? -eq 0 ]; then echo "$MDDIFF gave an error while scanning $MAILBOX_LOCAL:" cat $TMP_FIND_ERR exit 1 fi local FIFO_MKDIR="$CONFDIR/fifo/mkdir" [ -p "$FIFO_MKDIR" ] || $MDDIFF --mkfifo "$FIFO_MKDIR" local FIFO_MKDIRB="$CONFDIR/fifo/mkdir-back" [ -p "$FIFO_MKDIRB" ] || $MDDIFF --mkfifo "$FIFO_MKDIRB" $MDDIFF -s "$FIFO_MKDIR" > "$FIFO_MKDIRB" & local DIR_MAKER=$! atexit_kill $DIR_MAKER exec 6<$FIFO_MKDIRB exec 9>$FIFO_MKDIR gc_mktemp local TMP_T="$RC" local ERR=0 eval "$TRANSLATOR_LR" <$TMP_FIND >$TMP_T || ERR=$? exec 7<$TMP_FIND exec 8<$TMP_T while read M <&7 && read TM <&8; do if [ $ERR -eq 1 -o "$TM" = "ERROR" ]; then echo "Error: translating $M" cat $TMP_T if [ $showtags = 1 ]; then echo "$REPNAME: $localprog@localhost: TAGS: error::context(workarea) probable-cause(bad-translator) human-intervention(necessary)" fi exit 1 fi if [ $VERBOSE -eq 1 -a "$M" != "$TM" ]; then echo "translating:" $M "->" $TM fi if echo "$TM" | grep -q -e '\.\.'; then echo "Error: the translator returned a path containing .." exit 1 fi echo "$HOME/$M" 1>&9 echo ".smd/workarea/$TM" 1>&9 read R <&6 if [ "$R" != "OK" ]; then echo "$REPNAME: $localprog@localhost: TAGS: error::context(workarea) probable-cause(mddiff-s-error) human-intervention(necessary)" exit 1 fi done exec 6<&- exec 7<&- exec 8<&- exec 9>&- wait $DIR_MAKER || ERR=1 if [ $ERR -eq 1 ]; then echo "Error: creating symlinks" if [ $showtags = 1 ]; then echo "$REPNAME: $localprog@localhost: TAGS: error::context(workarea) probable-cause(fail-create-symlink) human-intervention(necessary)" fi exit 1 fi } ### Command line argument parsing ### parse_args() { for arg in "$@"; do case $arg in -v|--verbose) VERBOSE=1 SHOWTAGS=1 CHILDSARGS="$CHILDSARGS -v" ;; -s|--show-tags) SHOWTAGS=1 ;; -t|--template-only) TEMPLATE_ONLY=1 ;; -d|--dry-run) DRYRUN=1 CHILDSARGS="$CHILDSARGS -d" VERBOSE=1 SHOWTAGS=1 ;; -n|--no-delete) SMDSERVEROPTS="$SMDSERVEROPTS -n" ;; -*) cat <<-EOT usage: `basename $0` [options] [endpoint] Refer to the man page for `basename $0` EOT exit 1 ;; *) REPNAME="$arg" ;; esac done CONFFILE=$CONFDIR/config.$REPNAME } ### Confdir setup ### myfakessh() { shift cd "$@" } read_conffile() { # backward compatibility code if [ ! -f $CONFFILE ] && \ [ "$REPNAME" = "default" ] && \ [ -f $CONFDIR/config ]; then # we import the old conffile echo "From version 0.9.4, configuration files are named" echo "$CONFDIR/config.\$FOO, where FOO is an optional argument" echo "to smd-pull/smd-push. The default value of FOO is 'default'." echo "I'm renaming $CONFDIR/config to $CONFFILE." mv $CONFDIR/config $CONFFILE fi if [ ! -f $CONFFILE ]; then cat > $CONFFILE <<- EOT # No config file found, this is a template. You want to edit it. # Host name to be used with ssh as the server (use ~/.ssh/config # for extra options). smd-pull will pull from this host, smd-push # will push to this host and use it as the id of the remote mailbox. # # You should create an alias within your ~/.ssh/config like the # following on: # # Host smd-server-foo # Compression yes # Hostname your.real.server.name # User you # SERVERNAME=smd-server-$REPNAME # Host name to be used as the client. # smd-pull will use this just as an id for the client. If you # plan to sync with multiple endpoints, you must use a different # client id for any of them, thus a pair localhostname-remotehostname # should be used # CLIENTNAME=`hostname`-$REPNAME # The mailbox to sync, in case the path is the same on both hosts. # The path MUST be relative to the home directory, use a symlink if # the mailbox is not rooted there. If these paths contain spaces, # they must be substituted with %20. # MAILBOX="Mail/" # Use different paths on the local and remote hosts # # Local and remote mailbox may differ in name, as well as their # sub directory/folder structure. In that case a translator must be # provided. A translator is a program that takes in input, as it # first and only argument, a directory name (ending in /cur or /new # or /tmp) and prints on stdout its translation. Refer to the # smd-config (5) manpage for more infos. # # MAILBOX_LOCAL="Mail/" # MAILBOX_REMOTE="OtherMail/" # TRANSLATOR_RL=command # TRANSLATOR_LR=command # Ignore some paths # # To exclude some paths from the synchronization you can specify # a space separated list of glob(7) expressions. Spaces in these # expressions must be replaced with %20. # # EXCLUDE="Mail/spam Mail/trash Mail/with%20spaces" # # If the local and remote mailbox differ in name or their # sub directory/folder structure you can specify different # excluded paths for the two endpoints. # # EXCLUDE_LOCAL="Mail/spam Mail/trash" # EXCLUDE_REMOTE="OtherMail/with%20spaces" # Local synchronization # # If the local and remote mailboxes are on the same host # the following option must be added to the configuration file: # # SMDCLIENTOPTS=-l # Avoid deletions # # In some cases, usually unidirectional synchronizations, one may want # to not propagate deletions. E.g. one keeps a slim working mailbox but # pushes to a backup mailbox to save every email. # # SMDSERVEROPTS=-n # If the local and remote mailboxes are on the same host # the following option must be added to the configuration file: # # SMDCLIENTOPTS=-l # Log client to server and server to client communication. # # This is useful only for debugging, since all network traffic # is dumped, including transmitted mails. # # DEBUG=true EOT echo No config file found: created a default one echo Please edit it: $CONFFILE if [ "$TEMPLATE_ONLY" = 1 ]; then exit 0 else exit 1 fi fi if [ "$TEMPLATE_ONLY" = 1 ]; then exit 0 fi . $CONFFILE # sanityze MAILBOX="${MAILBOX%%/}" MAILBOX_LOCAL="${MAILBOX_LOCAL%%/}" MAILBOX_REMOTE="${MAILBOX_REMOTE%%/}" # default exclude if [ -z "$EXCLUDE_LOCAL" ]; then EXCLUDE_LOCAL="$EXCLUDE" fi if [ -z "$EXCLUDE_REMOTE" ]; then EXCLUDE_REMOTE="$EXCLUDE" fi for e in $EXCLUDE_LOCAL; do LOCALEXCLUDE="$LOCALEXCLUDE --exclude $e" done for e in $EXCLUDE_REMOTE; do REMOTEEXCLUDE="$REMOTEEXCLUDE --exclude $e" done # check for local synchronization if [ "$SERVERNAME" = "localhost" ]; then if echo "$SMDCLIENTOPTS" | grep -q -v -e "-l"; then echo "SERVERNAME is localhost but SMDCLIENTOPTS is not set." echo "Local synchronizations must set SMDCLIENTOPTS." else # no need to ssh SSH=myfakessh fi fi } ### Only one instance at a time please ### check_lockfile() { # could be relaxed to non related mailboxes/enpoints, but the test is # not straightforward if [ -f $LOCKFILE ]; then if ps -p `cat $LOCKFILE` 2> /dev/null | grep -E 'smd-(push|pull)'; then echo Already running. echo If this is not the case, remove $LOCKFILE by hand. echo "any: smd-pushpull@localhost: TAGS: error::context(locking) probable-cause(another-instance-is-running) human-intervention(necessary) suggested-actions(run(kill `cat $LOCKFILE`) run(rm $LOCKFILE))" exit 1 else echo Found lockfile of a dead instance. Ignored. fi fi echo $$ > $LOCKFILE atexit_rm $LOCKFILE } ### Create all the needed pipes ### setup_plumbing() { CtL=$CONFDIR/fifo/c2l.$REPNAME LtC=$CONFDIR/fifo/l2c.$REPNAME LtS=$CONFDIR/fifo/l2s.$REPNAME StL=$CONFDIR/fifo/s2l.$REPNAME PRp=$CONFDIR/fifo/pr.$REPNAME [ -p $CtL ] || $MDDIFF --mkfifo $CtL [ -p $LtC ] || $MDDIFF --mkfifo $LtC [ -p $LtS ] || $MDDIFF --mkfifo $LtS [ -p $StL ] || $MDDIFF --mkfifo $StL [ -p $PRp ] || $MDDIFF --mkfifo $PRp } ### Logging ### mycat() { # like cat, but ignores arguments cat } myreporter() { tee -a $1 | grep --line-buffered ^PROGRESS: | sed 's?^PROGRESS: ??' } mysilentreporter() { cat >> $1 } setup_logging() { CtS=$CONFDIR/log/c2s.$REPNAME.log StC=$CONFDIR/log/s2c.$REPNAME.log CL=$CONFDIR/log/client.$REPNAME.log SL=$CONFDIR/log/server.$REPNAME.log MITM=mycat if [ "$DEBUG" = "true" ]; then MITM=tee CHILDSARGS="$CHILDSARGS -v" fi PROGRESS_REPORTER=mysilentreporter if [ $VERBOSE -eq 1 ]; then PROGRESS_REPORTER=myreporter fi } setup_mailboxnames() { if [ -z "$MAILBOX" ]; then if [ -z "$MAILBOX_LOCAL" -o \ -z "$MAILBOX_REMOTE" -o \ -z "$TRANSLATOR_RL" -o \ -z "$TRANSLATOR_LR" ]; then echo "The config file must define MAILBOX xor MAILBOX_LOCAL, MAILBOX_REMOTE, TRANSLATOR_LR and TRANSLATOR_RL" exit 1 fi else if [ ! -z "$MAILBOX_LOCAL" -o ! -z "$MAILBOX_REMOTE" ]; then echo "The config file must define MAILBOX xor MAILBOX_LOCAL, MAILBOX_REMOTE, TRANSLATOR_LR and TRANSLATOR_RL" exit 1 fi MAILBOX_LOCAL="$MAILBOX" MAILBOX_REMOTE="$MAILBOX" fi if echo "$MAILBOX_LOCAL $MAILBOX_REMOTE" | grep -q -e '\.\.'; then echo "Mailbox names can't contain .." exit 1 fi resolve_translator "$TRANSLATOR_RL" TRANSLATOR_RL="$RC" resolve_translator "$TRANSLATOR_LR" TRANSLATOR_LR="$RC" } # this could be a system wide post-* hook report() { local exitcode="$1" local showtags="$2" local currcmd="$3" local inversecmd="$4" local localprog="$5" local remoteprog="$6" if [ $VERBOSE -eq 1 ]; then grep ^INFO: $SL | $SED 's/^INFO: //' grep ^INFO: $CL | $SED 's/^INFO: //' fi if [ $exitcode = 1 ]; then grep ^ERROR $SL \ | $SED "s/^/$remoteprog: /" \ | $SED "s/@@INVERSECOMMAND@@/$inversecmd/" \ | $SED "s/@@ENDPOINT@@/$REPNAME/" grep ^ERROR $CL \ | $SED "s/^/$localprog: /" \ | $SED "s/@@INVERSECOMMAND@@/$inversecmd/" \ | $SED "s/@@ENDPOINT@@/$REPNAME/" grep ^ssh: $SL \ | $SED "s/^/$remoteprog: ERROR: /" fi if [ $showtags = 1 ]; then #echo "`date`: $currcmd $SERVERNAME" >> $CL grep ^TAGS $SL \ | $SED "s/^/$REPNAME: $remoteprog@$SERVERNAME: /" \ | $SED "s/@@INVERSECOMMAND@@/$inversecmd/" \ | $SED "s/@@ENDPOINT@@/$REPNAME/" grep ^TAGS $CL \ | $SED "s/^/$REPNAME: $localprog@localhost: /" \ | $SED "s/@@INVERSECOMMAND@@/$inversecmd/" \ | $SED "s/@@ENDPOINT@@/$REPNAME/" if [ `grep ^TAGS $SL|wc -l` = 0 ] && \ [ `grep ^TAGS $CL|wc -l` = 0 ]; then # it may be that ssh failed to resolve the hostname # so we generate a fake tag for it cat $SL $CL echo "$REPNAME: $remoteprog@$SERVERNAME: TAGS: error::context(ssh) probable-cause(network) human-intervention(avoidable) suggested-actions(retry)" fi fi } ### Hooks ### run_hooks() { local dir="$1" local when="$2" local what="$3" local status="$4" for h in $dir/hooks/$when-$what.d/*; do if [ -x $h ]; then $h $when $what $REPNAME $status >> $CL 2>&1 fi done } # running server and client with appropriate parameters run_local_client() { cd $WORKAREA; $SMDCLIENT $CHILDSARGS $SMDCLIENTOPTS -t "$TRANSLATOR_RL" $CLIENTNAME $MAILBOX_REMOTE } run_local_server() { cd $WORKAREA; $SMDSERVER $LOCALEXCLUDE $CHILDSARGS $SMDSERVEROPTS $CLIENTNAME $MAILBOX_REMOTE } run_remote_server() { $SSH $SERVERNAME $REMOTESMDSERVER $REMOTEEXCLUDE $CHILDSARGS $SMDSERVEROPTS $CLIENTNAME $MAILBOX_REMOTE } run_remote_client() { $SSH $SERVERNAME $REMOTESMDCLIENT $CHILDSARGS $SMDCLIENTOPTS $CLIENTNAME $MAILBOX_REMOTE } # vim:ts=4 filetype=sh: syncmaildir-1.2.6.2/smd-config.5.txt000066400000000000000000000140411262303536600171360ustar00rootroot00000000000000NAME smd - configuration file for smd-pull and smd-push GENERAL SETUP To generate a template config file run smd-pull(1) with the -t option. If no endpoint is specified, the configuration file is named ~/.smd/config.default, otherwise it is named ~/.smd/config.endpoint. That file is composed by the following fields CLIENTNAME name of the client host SERVERNAME name of the server host MAILBOX list of directories, separated by space The field SERVERNAME must be a valid name for ssh, thus can be an alias like smd-server-foo. The default configuration file contains an example of how to set up an alias for ssh. The field CLIENTNAME is just an ID for the local host. If you plan to sync the same host with multiple remote hosts, you MUST use different values for CLIENTNAME for every configuration file. The field MAILBOX is a space separated list or roots that will be scanned for maildirs. Typically it is just one directory name, Mail or Maildir. The roots must be paths relative to the home directory. In the simplest case, the roots are named the same on both the local and the remote hosts. If the roots have different names on the local and remote hosts, but their internal structure is the same, the simplest solution is to just use a symlink on one of the two hosts so that a single name can be used to refer to both. If the internal sub folder structure differ, for example because on the remote hosts sub folders names are prefixed with a dot but it is not the case on the local one, refer to the MAIL FOLDER RENAMING section of this document. The configuration file is a regular shell script, refer to bash(1) for its syntax. HOOKS The content of the directories ~/.smd/hooks/{pre,post}-pull.d/ is executed respectively before and after smd-pull does it's job. They receive four arguments: "pre" or "post", "pull", the endpoint name and the status. The status is always 0 (meaning success) for pre hooks, while can be 1 (for failure) for post hooks. Hooks should not fail, if they do so then smd-pull will fail too. The content of the directories ~/.smd/hooks/{pre,post}-push.d/ is executed respectively before and after smd-push does it's job. They receive four arguments: "pre" or "post", "push", the endpoint name and the status. The status is always 0 (meaning success) for pre hooks, while can be 1 (for failure) for post hooks. Hooks should not fail, if they do so then smd-push will fail too. MAIL FOLDER RENAMING To make the transition from other synchronization tools smooth, the folders structure on the local and remote host are allowed to differ. For example, offlineimap usually removes trailing dots from the names of sub folders. To take advantage of folder renaming, the configuration file can contain the following fields: MAILBOX_LOCAL the local roots of maildirs MAILBOX_REMOTE the remote roots of maildirs TRANSLATOR_RL a program to translate remote mailbox names to local ones TRANSLATOR_LR a program to translate local mailbox names to remote ones The fields MAILBOX_LOCAL and MAILBOX_REMOTE must substitute the MAILBOX fields explained above. The fields TRANSLATOR_RL and TRANSLATOR_LR must define two translator programs that will be run to translate remote mailbox names to local ones (TRANSLATOR_RL) and vice versa (TRANSLATOR_LR). A translator program must fulfil the following requirements: - must be an absolute path or relative to the $HOME directory or in the user $PATH and must be executable - receives in standard input one or more paths starting with one of the roots listed in MAILBOX_LOCAL (for TRANSLATOR_LR) or MAILBOX_REMOTE (for TRANSLATOR_RL) and ending with cur, new or tmp - it can fail, returning 1 and writing on standard output the string ERROR followed by a new line and a human readable error message in the following lines - it can succeed, returning 0 and printing on standard output the corresponding translated paths PATHS EXCLUSION In case some paths need to be skipped, they can be specified as space separated glob(7) expressions in the variable: EXCLUDE glob expressions identifying paths to be excluded Note that these expressions must match real paths, no translation operation is applied to them, so it may be necessary to specify different expressions for the local and remote endpoint. In that case the following variables can be set: EXCLUDE_LOCAL glob expressions identifying local paths to be excluded EXCLUDE_REMOTE glob expressions identifying remote paths to be excluded Matching is performed using fnmatch(3) with no special flags, thus `*' and `?' match any character including `/'. Last, note that spaces in glob expressions must be replaced by %20. For example, to exclude all paths matching the expression `Mail/delayed [1-5] days/*' the variable EXCLUDE must be set to `Mail/delayed%20[1-5]%20days/*' Last, matching is performed every time a directory is entered, and if the matching succeeds the directory and all its subdirectories are skipped. Thus there is no need to specify a trailing '/*' in every expression. LOCAL SYNCHRONIZATION If the local and remote mailboxes are on the same host the following option must be added to the configuration file: SMDCLIENTOPTS=-l Note that this options has also the effect that ssh is not used. A a simple pair of pipes is used instead. DELETIONS In some cases, usually unidirectional synchronizations, one may want to not propagate deletions. E.g. one keeps a slim working mailbox but pushes to a backup mailbox to save every email. For that scenario smd-pull and smd-push accept a -n, --no-delete, option. To avoid specifying this option every time one can put it in the configuration file: SMDSERVEROPTS=-n FILES ~/.smd/config.* ~/.smd/hooks/pre-pull.d/ ~/.smd/hooks/post-pull.d/ ~/.smd/hooks/pre-push.d/ ~/.smd/hooks/post-push.d/ SEE ALSO mddiff(1), smd-server(1), smd-client(1), smd-push(1), smd-loop(1), smd-translate(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-config.vapi000066400000000000000000000002401262303536600171070ustar00rootroot00000000000000namespace SMDConf { [CCode (cheader_filename = "smd-config.h")] public const string PREFIX; public const string VERSION; public const string COPYRIGHT; } syncmaildir-1.2.6.2/smd-loop000077500000000000000000000104241262303536600156650ustar00rootroot00000000000000#!/bin/bash # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi # The config file name CONFFILE=~/.smd/loop # The lock file name LOCKFILE=~/.smd/loop.lock if [ -e $LOCKFILE ]; then if ps -p `cat $LOCKFILE` > /dev/null | grep smd-loop; then echo Another smd-loop instance is running. If it is not the case echo remove $LOCKFILE and retry echo "any: smd-loop@localhost: TAGS: error::context(locking) probable-cause(another-instance-is-running) human-intervention(necessary) suggested-actions(run(kill `cat $LOCKFILE`) run(rm $LOCKFILE))" exit 1 else echo "Found lock file of a dead instance. Ignored." fi fi [ -d ~/.smd ] || mkdir -p ~/.smd echo $$ > $LOCKFILE # The log file LOGFILE=~/.smd/log/loop.log mkdir -p `dirname $LOGFILE` > $LOGFILE log() { echo `date '+%x %X'`: $@ >> $LOGFILE } log_cat() { cat $1 | sed 's/^/output: /' >> $LOGFILE } # The length of a minute, decrease to debug MINUTE=60 # The clock, incremented every $MINUTE TIME=1 # Verbose VERBOSE=0 # Just create a template TEMPLATE_ONLY=0 # Temp file, used to store subprocesses' output OUTPUT=`mktemp -q /tmp/smd-loop.XXXXXXXXXX` # List of commands that failed STOP_TAG="__STOP__" declare -a FAILURES=("$STOP_TAG") # Prefix PREFIX="@PREFIX@" if [ `echo $PREFIX | cut -c -1` = "@" ]; then echo "smd-loop not installed, assuming smd-pull is ./smd-pull" echo "smd-loop not installed, assuming smd-push is ./smd-push" log "smd-loop not installed, assuming smd-pull is ./smd-pull" log "smd-loop not installed, assuming smd-push is ./smd-push" PULL="./smd-pull" PUSH="./smd-push" else PULL="$PREFIX/bin/smd-pull" PUSH="$PREFIX/bin/smd-push" fi remove_from_failures() { local item="$1" for ((i=0; ; i++)); do if [ "${FAILURES[$i]}" = "$item" ]; then unset FAILURES[$i] fi if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then break fi done } add_to_failures() { local item="$1" for ((i=0; ; i++)); do if [ "${FAILURES[$i]}" = "" ]; then FAILURES[$i]="$item" break fi if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then FAILURES[$i]="$item" FAILURES[`expr $i + 1`]="$STOP_TAG" break fi done } has_not_failed() { local item="$1" local found=0 for ((i=0; ; i++)); do if [ "${FAILURES[$i]}" = "$item" ]; then found=1 fi if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then break fi done return $found } perform() { local cmd=$1 local endpoint=$2 local cmd_line="$cmd -s $endpoint" if [ "$VERBOSE" = 1 ]; then echo smd-loop: $cmd_line 1>&2 fi log "$cmd_line" $cmd_line > $OUTPUT 2>&1 if [ $? = 0 ]; then cat $OUTPUT remove_from_failures "$cmd_line" log "completed successfully" else if grep -q 'human-intervention(avoidable)' $OUTPUT && grep -q 'suggested-actions(retry)' $OUTPUT && has_not_failed "$cmd_line"; then if [ "$VERBOSE" = 1 ]; then echo smd-loop: warning: failed: $cmd_line 1>&2 echo smd-loop: warning: will retry later 1>&2 fi add_to_failures "$cmd_line" log "avoidable failure, retry later" log_cat $OUTPUT else cat $OUTPUT log "persistent or non avoidable failure" log_cat $OUTPUT exit 1 fi fi } cleanup() { rm -f $OUTPUT rm -f $LOCKFILE log "exiting" } cleanup_killed() { cleanup log "killed" exit 1 } trap cleanup "EXIT" trap cleanup_killed "SIGTERM" if [ "$1" = "-v" ]; then VERBOSE=1 shift fi if [ "$1" = "-t" ]; then TEMPLATE_ONLY=1 shift fi if [ ! -f $CONFFILE ]; then mkdir -p `dirname $CONFFILE` cat > $CONFFILE <<-EOT # smd-loop configuration file # # Line starting with '#' are comments. # Frequences are in minutes. # # pull-frequency push-frequency endpoint-name 3 10 default EOT echo No config file found: created a default one echo Please edit it: $CONFFILE exit 1 fi if [ "$TEMPLATE_ONLY" = 1 ]; then exit 0 fi log "starting" first_run=1 while true; do while read pull push endpoint; do do_pull=1 do_push=1 if [ $pull -gt 0 ]; then do_pull=$((TIME % pull)); fi if [ $push -gt 0 ]; then do_push=$((TIME % push)); fi if [ $do_pull -eq 0 ]; then perform $PULL $endpoint first_run=0 fi if [ $do_push -eq 0 ]; then perform $PUSH $endpoint first_run=0 fi done < <(grep -v '^#' $CONFFILE) TIME=$((TIME+1)) if [ $first_run -eq 0 ]; then sleep $MINUTE; fi done syncmaildir-1.2.6.2/smd-loop.1.txt000066400000000000000000000021011262303536600166300ustar00rootroot00000000000000NAME smd-loop - iterates smd-pull and smd-push SYNOPSIS smd-loop [-vt] DESCRIPTION smd-loop runs smd-push and smd-pull at regular intervals as defined by the user in the ~/.smd/loop configuration file. On errors that are reported to be transient, it retries a second time before failing. The configuration file is line-oriented. Each line is composed of three space separated fields: pull-frequency, push-frequency and endpoint-name. Frequencies are expressed in minutes, while endpoint name is a valid name for smd-pull(1) and smd-push(1). Lines beginning with # are considered as comments. The following example calls the command smd-pull default every 3 minutes, and smd-push default every 10. Example: # pull-frequency push-frequency endpoint-name 3 10 default OPTIONS -v Increase program verbosity (printed on stderr) -t Just create a template configuration file if none FILES ~/.smd/loop SEE ALSO mddiff(1), smd-server(1), smd-client(1), smd-push(1), smd-pull(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-pull000077500000000000000000000024051262303536600156700ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi # the idea is (where == is a bi-directional pipe) : # # ssh foo smd-server == tee log == smd-client # set -e #set -x PREFIX="@PREFIX@" if [ `echo "$PREFIX" | cut -c -1` = "@" ]; then SMDROOT=. echo "smd-pull not installed, assuming smd-common is ./smd-common" else SMDROOT=$PREFIX/share/syncmaildir fi . $SMDROOT/smd-common init parse_args "$@" read_conffile # this could be a system wide pre-hook check_lockfile setup_plumbing setup_logging setup_mailboxnames # we move to the home, since Mail paths are relative # to the home cd # reset log file before caclling hooks > $CL run_hooks $CONFDIR pre pull 0 ($MITM $CtS > $LtS) < $CtL & LOGGER1=$! atexit_kill $LOGGER1 ($MITM $StC > $LtC) < $StL & LOGGER2=$! atexit_kill $LOGGER2 ($PROGRESS_REPORTER $CL) < $PRp & REPORTER=$! atexit_kill $REPORTER (run_local_client < $LtC 2> $PRp) > $CtL & CLIENT=$! atexit_kill $CLIENT EXITCODE=0 (run_remote_server < $LtS 2> $SL) > $StL || EXITCODE=1 wait $CLIENT || EXITCODE=1 wait $REPORTER || EXITCODE=1 run_hooks $CONFDIR post pull $EXITCODE report $EXITCODE $SHOWTAGS smd-pull smd-push smd-client smd-server exit $EXITCODE syncmaildir-1.2.6.2/smd-pull.1.txt000066400000000000000000000021351262303536600166420ustar00rootroot00000000000000NAME smd-pull - syncs the local mail dir letting the remote one untouched SYNOPSIS smd-pull [-d|--dry-run] [-v|--verbose] [-s|--show-tags] [-t|--template-only] [-n|--no-delete] [endpoint] DESCRIPTION smd-pull performs in the local maildir all the changes that were performed on remote one. No changes are made on the remote maildir. Use smd-push(1) for that. Refer to smd-config(5) for the configuration file format. OPTIONS -v --verbose Verbose output -s --show-tags Machine readable output -d --dry-run Do not perform any action for real -n --no-delete Do not propagate deletions -t --template-only Just create a template configuration file if none endpoint Is the suffix for the name of the configuration file to use. If it is omitted, the configuration file ~/.smd/config.default is used. FILES ~/.smd/config.* ~/.smd/hooks/pre-pull.d/ ~/.smd/hooks/post-pull.d/ SEE ALSO mddiff(1), smd-server(1), smd-client(1), smd-push(1), smd-loop(1), smd-config(5) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-push000077500000000000000000000024251262303536600156750ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi # the idea is (where == is a bi-directional pipe) : # # ssh foo smd-client == tee log == smd-server # set -e #set -x PREFIX="@PREFIX@" if [ `echo "$PREFIX" | cut -c -1` = "@" ]; then SMDROOT=. echo "smd-pull not installed, assuming smd-common is ./smd-common" else SMDROOT=$PREFIX/share/syncmaildir fi . $SMDROOT/smd-common init parse_args "$@" read_conffile # this could be a system wide pre-hook check_lockfile setup_plumbing setup_logging setup_mailboxnames # we move to the home, since Mail paths are relative # to the home cd # reset log file before caclling hooks > $CL run_hooks $CONFDIR pre push 0 ($MITM $CtS > $LtS) < $CtL & LOGGER1=$! atexit_kill $LOGGER1 ($MITM $StC > $LtC) < $StL & LOGGER2=$! atexit_kill $LOGGER2 ($PROGRESS_REPORTER $SL) < $PRp & REPORTER=$! atexit_kill $REPORTER setup_workarea (run_local_server < $LtC 2>> $CL) > $CtL & CLIENT=$! atexit_kill $CLIENT EXITCODE=0 (run_remote_client < $LtS 2> $PRp) > $StL || EXITCODE=1 wait $CLIENT || EXITCODE=1 wait $REPORTER || EXITCODE=1 run_hooks $CONFDIR post push $EXITCODE report $EXITCODE $SHOWTAGS smd-push smd-pull smd-server smd-client exit $EXITCODE syncmaildir-1.2.6.2/smd-push.1.txt000066400000000000000000000021551262303536600166470ustar00rootroot00000000000000NAME smd-push - syncs the remote mail dir letting the local one untouched SYNOPSIS smd-push [-d|--dry-run] [-v|--verbose] [-s|--show-tags] [-t|--template-only] [-n|--no-delete] [endpoint] DESCRIPTION smd-push performs in the remote maildir all the changes that were performed on the local one. No changes are made on the local maildir. Use smd-pull(1) for that. Refer to smd-config(5) for the configuration file format. OPTIONS -v --verbose Verbose output -s --show-tags Machine readable output -d --dry-run Do not perform any action for real -n --no-delete Do not propagate deletions -t --template-only Just create a template configuration file if none endpoint Is the suffix for the name of the configuration file to use. If it is omitted, the configuration file ~/.smd/config.default is used. FILES ~/.smd/config.* ~/.smd/hooks/pre-push.d/ ~/.smd/hooks/post-push.d/ SEE ALSO mddiff(1), smd-server(1), smd-client(1), smd-pull(1), smd-loop(1), smd-config(5) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-restricted-shell000077500000000000000000000011311262303536600201640ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi set -e #set -x PREFIX="@PREFIX@" if [ `echo "$PREFIX" | cut -c -1` = "@" ]; then echo "smd-restricted-shell not installed, assuming PREFIX=." WHERE="./" else WHERE="$PREFIX/bin" fi # check that SSH_ORIGINAL_COMMAND contains only # smd-client/smd-server C=`echo $SSH_ORIGINAL_COMMAND | cut -f 1 -d ' '` if [ "$C" != "$WHERE/smd-client" -a "$C" != "$WHERE/smd-server" ]; then exit 1 fi # we now run the smd command exec $SSH_ORIGINAL_COMMAND syncmaildir-1.2.6.2/smd-restricted-shell.1.txt000066400000000000000000000010111262303536600211330ustar00rootroot00000000000000NAME smd-restricted-shell - restricted shell for smd SYNOPSIS command="smd-restricted-shell" ssh-rsa ... DESCRIPTION This utility is meant to be used in conjunction with an ssh key. You can restrict the commands that are allowed to be executed on your remote host when the login is performed using a particular ssh key. Just prefix the line corresponding to that ssh key with command="smd-restricted-shell" FILES ~/.ssh/authorized_keys SEE ALSO ssd(8) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-server000077500000000000000000000123731262303536600162270ustar00rootroot00000000000000#! /usr/bin/env lua5.1 -- -- Released under the terms of GPLv3 or at your option any later version. -- No warranties. -- Copyright Enrico Tassi require 'syncmaildir' -- export to the global namespace all symbols for k,v in pairs(syncmaildir) do _G[k] = v end -- ============================= MAIN ===================================== function main() -- sanity checks assert_exists(MDDIFF) assert_exists(XDELTA) -- argument parsing local exclude = {} local no_delete = false local no_move = false local just_mddiff = false local stop_after_diff = false local override_db = nil local dump_stdin_to = nil local usage = "Usage: "..arg[0]:match('[^/]+$').. " [-vdn] endpointname mailboxes...\n" while #arg > 2 do if arg[1] == '-v' or arg[1] == '--verbose' then set_verbose(true) table.remove(arg,1) elseif arg[1] == '-d' or arg[1] == '--dry-run' then set_dry_run(true) table.remove(arg,1) elseif arg[1] == '-n' or arg[1] == '--no-delete' then no_delete = true table.remove(arg,1) elseif arg[1] == '--no-move' then no_move = true table.remove(arg,1) elseif arg[1] == '--exclude' then exclude[#exclude + 1] = arg[1] exclude[#exclude + 1] = arg[2] table.remove(arg,1) table.remove(arg,1) elseif arg[1] == '--get-mddiff-cmdline' then just_mddiff = true table.remove(arg,1) elseif arg[1] == '--override-db' then override_db = arg[2] table.remove(arg,1) table.remove(arg,1) elseif arg[1] == '--stop-after-diff' then stop_after_diff = true table.remove(arg,1) elseif arg[1] == '--dump-stdin' then dump_stdin_to = arg[2] table.remove(arg,1) table.remove(arg,1) else break end end if #arg < 2 then io.stderr:write(usage) os.exit(2) end if dump_stdin_to ~= nil then dump_stdin_to = dump_stdin_to:gsub('^~',os.getenv('HOME')) mkdir_p(dump_stdin_to) local f = io.open(dump_stdin_to,'w') local data = nil repeat data = io.read(4096) if data then f:write(data) end until data == nil f:close() os.exit(0) end local endpoint = arg[1] table.remove(arg,1) local dbfile = nil if override_db ~= nil then dbfile = override_db:gsub('^~',os.getenv('HOME')) else dbfile = dbfile_name(endpoint, arg) end local xdelta = dbfile .. '.xdelta' local dbfilemt = dbfile .. '.mtime' local newdb = dbfile .. '.new' local newdbmt = dbfilemt .. '.new' local database_opt = '--db-file '.. dbfile local mailbox_opt = table.concat(arg,' ') local no_delete_opt = '' if no_delete then no_delete_opt = '-n' end local no_move_opt = '' if no_move then no_move_opt = '--no-move' end local dry_opt = '' if dry_run() then dry_opt = '-d' end local exclude_opt = table.concat(exclude, ' ') local mddiff = MDDIFF..' '..dry_opt..' '..database_opt..' '.. exclude_opt..' '..no_delete_opt..' '..no_move_opt..' '.. mailbox_opt -- to call mddiff from another tool with the same cmdline if just_mddiff then print(mddiff) os.exit(0) end -- we check the protocol version and dbfile fingerprint handshake(dbfile) -- run mddiff and send the output to the client local r = io.popen(mddiff,"r") local sent = 0 while true do local l = r:read("*l") if l ~= nil then sent = sent + 1 io.write(l,'\n') else break end end r:close() -- end of the first phase, now the client should -- apply the diff eventually asking for the transmission -- of some data io.write('END\n') io.flush() -- if renaming mode, we exit if stop_after_diff then if override_db ~= nil then os.remove(dbfile) os.remove(dbfilemt) os.remove(newdb) os.remove(newdbmt) end os.exit(0) end -- process client commands while true do local l = io.read('*l') if l == nil then -- end of input stream, client is dead log_error('Communication with client died unexpectedly\n') os.exit(3) end if l:match('^COMMIT$') then -- the client applied the diff, the new mailbox -- fingerprint should be used for the next sync local rc if not dry_run() then rc = os.execute( XDELTA..' delta '..dbfile..' '..newdb..' '..xdelta) else local f = io.open(xdelta,'w') f:close() rc = 0 -- there is no newdb if --dry-run is given end if rc ~= 0 and rc ~= 256 then log_error('Failed running `xdelta delta` on db file: '..rc) os.exit(4) end transmit(io.stdout, xdelta, "all") os.remove(xdelta) elseif l:match('^DONE$') then if not dry_run() then os.rename(newdb, dbfile) end if not dry_run() then os.rename(newdbmt, dbfilemt) end os.exit(0) elseif l:match('^ABORT$') then -- the client failed in applying the diff log_error('Client aborted, removing '.. newdb..' and '..newdbmt..'\n') if not dry_run() then os.remove(newdb) end if not dry_run() then os.remove(newdbmt) end os.exit(5) elseif l:match('^GET ') then local path = parse(l, '^GET (%S.*)$') transmit(io.stdout, path, "all") elseif l:match('^GETHEADER ') then local path = parse(l, '^GETHEADER (%S.*)$') transmit(io.stdout, path, "header") elseif l:match('^GETBODY ') then local path = parse(l, '^GETBODY (%S.*)$') transmit(io.stdout, path, "body") else -- protocol error log_error('Invalid command '..l..'\n') os.exit(6) end end end -- no more globals set_strict() -- parachute for error parachute(main, 7) -- vim:set ts=4: syncmaildir-1.2.6.2/smd-server.1.txt000066400000000000000000000027511262303536600172000ustar00rootroot00000000000000NAME smd-server - sends diffs and mails to smd-client SYNOPSIS smd-server [--exclude glob] [-v|--verbose] [-d|--dry-run] [--get-mddiff-cmdline] [--stop-after-diff] [--override-db dbf] [--dump-stdin tgt] endpoint mailboxes DESCRIPTION smd-server needs to know a name (endpoint) for the client (that must not be used by others) and a list of mailboxes (directories). smd-server first calls mddiff(1), then prints on stdout the generated diff. It then accepts from stdin a small set of commands a client may issue to request a file (or parts of it, like the header). smd-server is in charge of committing the db file used by mddiff(1) in case the client communicates a successful sync. OPTIONS -v --verbose Increase program verbosity (printed on stderr) -d --dry-run Do not perform any action for real -n --no-delete Do not track deleted files --exclude glob Exclude paths matching glob --override-db dbf Use dbf as the db-file --get-mddiff-cmdline Print the command line used for mddiff and then exist --stop-after-diff Send the actions to the other endpoint and exit. If used in conjunction with --override-db, dbf is removed just before exiting --dump-stdin tgt Dump standard input to tgt and exit NOTES smd-server is a low level utility. You should use higher level tools like smd-pull(1) and smd-push(1) SEE ALSO mddiff(1), smd-client(1), smd-pull(1), smd-push(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-translate000077500000000000000000000075511262303536600167200ustar00rootroot00000000000000#!/usr/bin/env lua5.1 -- -- Released under the terms of GPLv3 or at your option any later version. -- No warranties. -- Copyright Enrico Tassi -- utils function normalize(x) return x:gsub("^['\"]",''):gsub("['\"]$",''):gsub("/$",'') end function escape(x) return x:gsub('[]%%%.-]',function(x) return '%'..x end) end local orig_print = print function print(...) orig_print(...) io.stdout:flush() end -- argument parsing local mode, dir, endpoint mode = 'default' local i = 1 while i <= select('#',...) do local cur = select(i,...) if cur == '-m' then i = i + 1 mode = select(i,...) elseif cur == '-d' then i = i + 1 dir = select(i,...) else if endpoint ~= nil then print 'ERROR' print 'smd-translate: too many arguments' os.exit(1) end endpoint = cur end i = i + 1 end -- argument checking if endpoint == nil then print 'ERROR' print('smd-translate: No endpoint specified') os.exit(1) end if dir ~= 'LR' and dir ~= 'RL' then print 'ERROR' print('smd-translate: Direction '..(dir or 'nil').. ' passed with -d is not LR nor R') os.exit(1) end -- config file parsing conf=os.getenv'HOME'..'/.smd/config.'..endpoint conf_f = io.open(conf) if conf_f == nil then print 'ERROR' print('smd-translate: No configuration file for endpoint ' .. (endpoint or 'nil')) os.exit(1) end local conf_tbl = {} for l in conf_f:lines() do -- this way we skip comments local k,v = l:match('^%s*(%S+)%s*=%s*(%S+)') if k ~= nil and v ~= nil then conf_tbl[k]=v end end conf_f:close() mailbox_local = conf_tbl['MAILBOX_LOCAL'] or conf_tbl['MAILBOX'] mailbox_local = normalize(mailbox_local) mailbox_remote = conf_tbl['MAILBOX_REMOTE'] or conf_tbl['MAILBOX'] mailbox_remote = normalize(mailbox_remote) -- modes modes = {} modes['oimap-dovecot'] = function(dir) if dir == 'LR' then local l_ml = '^'..escape(mailbox_local)..'/' local r_ml_dot = mailbox_remote..'/.' local r_ml = mailbox_remote..'/' local mc = mailbox_local..'/cur' local mn = mailbox_local..'/new' local mt = mailbox_local..'/tmp' for l in io.stdin:lines() do if l == mc or l == mn or l == mt then print((l:gsub(l_ml,r_ml))) else print((l:gsub(l_ml,r_ml_dot))) end end else local e_mr = '^'..escape(mailbox_remote) local m_ml = '^'..escape(mailbox_local)..'/%.' local dot_ml = mailbox_local..'/' for l in io.stdin:lines() do print((l:gsub(e_mr,mailbox_local):gsub(m_ml,dot_ml))) end end end modes['nodots'] = function(dir) if dir == 'LR' then local l_ml = '^'..escape(mailbox_local)..'/' local l_mlc = '^'..escape(mailbox_local)..'/(.*)/(cur)' local l_mln = '^'..escape(mailbox_local)..'/(.*)/(new)' local l_mlt = '^'..escape(mailbox_local)..'/(.*)/(tmp)' local r_ml_dot = mailbox_remote..'/.' local r_ml = mailbox_remote..'/' local mc = mailbox_local..'/cur' local mn = mailbox_local..'/new' local mt = mailbox_local..'/tmp' for l in io.stdin:lines() do if l == mc or l == mn or l == mt then print((l:gsub(l_ml,r_ml))) else local f = function(path, last) return r_ml_dot..(path:gsub('/','.'))..'/'..last end print((l:gsub(l_mlc,f):gsub(l_mln,f):gsub(l_mlt,f))) end end else local e_mr = '^'..escape(mailbox_remote)..'(/.*)' local dot_ml = mailbox_local for l in io.stdin:lines() do print((l:gsub(e_mr,function(cap) return dot_ml..(cap:gsub('%.','/'):gsub('^//','/')) end))) end end end modes['move'] = function(dir) if dir == 'LR' then local l_m = '^'..escape(mailbox_local)..'/' local r_m = mailbox_remote..'/' for l in io.stdin:lines() do print((l:gsub(l_m,r_m))) end else local r_m = '^'..escape(mailbox_remote)..'/' local l_m = mailbox_local..'/' for l in io.stdin:lines() do print((l:gsub(r_m,l_m))) end end end modes['cat'] = function(_) for l in io.stdin:lines() do print(l) end end modes['default'] = modes['oimap-dovecot'] -- run modes[mode](dir, f,s,init) -- vim:set ts=4: syncmaildir-1.2.6.2/smd-translate.1.txt000066400000000000000000000036371262303536600176730ustar00rootroot00000000000000NAME smd-translate - translates mailbox names according to predefined patterns SYNOPSIS smd-translate [-m mode] [-d direction] endpoint mailbox DESCRIPTION smd-translate translates mailbox names according to predefined patterns. It is designed to be used in the configuration file of smd-push(1) and smd-pull(1). Refer to smd-config(5) for a longer discussion on translators. Supported modes follow. oimap-dovecot This mode is designed to make the transition of offlineimap users to smd simpler. It assumes remote mailboxes are named like in the following: Maildir/cur Maildir/.sub.folder/new The corresponding local mailboxes are named like in the following: Mail/cur Mail/sub.folder/new nodots This mode simply gets rid of all dots. It assumes remote mailboxes are named like in the following: Maildir/cur Maildir/.sub.folder/new The corresponding local mailboxes are named like in the following: Mail/cur Mail/sub/folder/new move This mode just replaces the root of the mailbox, i.e. substitutes MAILBOX_LOCAL for MAILBOX_REMOTE and viceversa. USAGE IN CONFIGURATION FILE Assuming the MAILBOX_LOCAL configuration variable is set to Mail and the MAILBOX_REMOTE is set to Maildir, one can use the following configuration file snippet in ~/.smd/config.endpoint: TRANSLATOR_RL="smd-translate -m oimap-dovecot -d RL endpoint" TRANSLATOR_LR="smd-translate -m oimap-dovecot -d LR endpoint" To test your setup use the smd-check-conf(1) utility. OPTIONS -m mode One of the supported modes listed above -d direction One of RL or LR endpoint Is the suffix for the name of the configuration file to use mailbox The mailbox name to translate FILES ~/.smd/config.* SEE ALSO smd-push(1), smd-pull(1), smd-config(5), smd-check-conf(1) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/smd-uniform-names000077500000000000000000000045131262303536600174760ustar00rootroot00000000000000#!/bin/sh # # Released under the terms of GPLv3 or at your option any later version. # No warranties. # Copyright Enrico Tassi # the idea is: # 0) it is the first run (no db) # 1) we compute the local db with mddiff # 2) we push it to the remote host via ssh # 3) we start a special pull where smd-server uses the special db # and where smd-client just emits a shell script with all the renaming # 4) we remove the remote db set -e #set -x PREFIX="@PREFIX@" if [ `echo "$PREFIX" | cut -c -1` = "@" ]; then SMDROOT=. echo "smd-uniform-names not installed, assuming smd-common is ./smd-common" else SMDROOT=$PREFIX/share/syncmaildir fi . $SMDROOT/smd-common init parse_args "$@" read_conffile # this could be a system wide pre-hook check_lockfile setup_plumbing setup_logging setup_mailboxnames # we move to the home, since Mail paths are relative # to the home cd setup_workarea ORIG_CHILDARGS="$CHILDSARGS" CHILDSARGS="$ORIG_CHILDARGS --get-mddiff-cmdline" DB=`run_local_server | sed 's?^.*--db-file \([^ ][^ ]*\) .*$?\1?'` if [ -e "$DB" ]; then echo "Found db file: $DB" echo "This utility can be used only before any synchronization" echo "takes place. See the manpage for more details." exit 1 fi CHILDSARGS="$ORIG_CHILDARGS --get-mddiff-cmdline --override-db ~/$RENAMEDB" MDLINE=`run_local_server` EXITCODE=0 gc_mktemp TMPERR="$RC" gc_mktemp TMPOUT="$RC" (cd $WORKAREA; $MDLINE) > $TMPOUT 2> $TMPERR || EXITCODE=1 grep -v '^warning.*unable to open db' $TMPERR || true grep '^ERROR' $TMPOUT || true if [ $EXITCODE = 1 ]; then exit $EXITCODE; fi mv ~/$RENAMEDB.new ~/$RENAMEDB atexit_rm ~/$RENAMEDB.mtime.new atexit_rm ~/$RENAMEDB CHILDSARGS="$ORIG_CHILDARGS --dump-stdin ~/$RENAMEDB" cat ~/$RENAMEDB | run_remote_server ($MITM $CtS > $LtS) < $CtL & LOGGER1=$! atexit_kill $LOGGER1 ($MITM $StC > $LtC) < $StL & LOGGER2=$! atexit_kill $LOGGER2 ($PROGRESS_REPORTER $CL) < $PRp & REPORTER=$! atexit_kill $REPORTER CHILDSARGS="$ORIG_CHILDARGS --rename-only --override-db ~/$RENAMEDB" (run_local_client < $LtC 2> $PRp) > $CtL & CLIENT=$! atexit_kill $CLIENT CHILDSARGS="$ORIG_CHILDARGS --no-move --stop-after-diff --override-db ~/$RENAMEDB" (run_remote_server < $LtS 2> $SL) > $StL || EXITCODE=1 wait $CLIENT || EXITCODE=1 wait $REPORTER || EXITCODE=1 report $EXITCODE 0 smd-push smd-pull smd-server smd-client exit $EXITCODE syncmaildir-1.2.6.2/smd-uniform-names.1.txt000066400000000000000000000015351262303536600204510ustar00rootroot00000000000000NAME smd-uniform-names - renames local mails as on the remote host SYNOPSIS smd-uniform-names [-v|--verbose] [endpoint] DESCRIPTION smd-uniform-names is useful to migrate from offlineimap to smd. offlineimap may name the same email differently on the local and remote host. This utility generates a script to be run on the local host to uniform the names to the remote ones. The utility generates the script '~/smd-rename.sh' that has to be run by the user. OPTIONS -v --verbose Verbose output endpoint Is the suffix for the name of the configuration file to use. If it is omitted, the configuration file ~/.smd/config.default is used. FILES ~/.smd/config.* ~/smd-rename.sh SEE ALSO smd-pull(1), smd-push(1), smd-config(5) AUTHOR Enrico Tassi syncmaildir-1.2.6.2/syncmaildir.lua000066400000000000000000000470251262303536600172350ustar00rootroot00000000000000-- Released under the terms of GPLv3 or at your option any later version. -- No warranties. -- Copyright Enrico Tassi -- -- common code for smd-client/server local PROTOCOL_VERSION="1.2" local verbose = false local dryrun = false local translator = false local PREFIX = '@PREFIX@' local BUGREPORT_ADDRESS = 'syncmaildir-users@lists.sourceforge.net' local __G = _G local __error = _G.error module('syncmaildir',package.seeall) -- set mddiff path MDDIFF = "" if string.sub(PREFIX,1,1) == '@' then MDDIFF = './mddiff' io.stderr:write('smd-client not installed, assuming mddiff is: ', MDDIFF,'\n') else MDDIFF = PREFIX .. '/bin/mddiff' end -- set xdelta executable name XDELTA = '@XDELTA@' if string.sub(XDELTA,1,1) == '@' then XDELTA = 'xdelta' end -- set smd version SMDVERSION = '@SMDVERSION@' if string.sub(SMDVERSION,1,1) == '@' then SMDVERSION = '0.0.0' end -- to call external filter processes without too much pain function make_slave_filter_process(cmd, seed) seed = seed or "no seed" local init = function(filter) if filter.inf == nil then local rc local base_dir local home = os.getenv('HOME') local user = os.getenv('USER') or 'nobody' local mangled_name = string.gsub(seed,"[ %./]",'-') local attempt = 0 if home ~= nil then base_dir = home ..'/.smd/fifo/' else base_dir = '/tmp/' end rc = os.execute(MDDIFF..' --mkdir-p '..quote(base_dir)) if rc ~= 0 then log_internal_error_and_fail('unable to create directory', "make_slave_filter_process") end repeat pipe = base_dir..'smd-'..user..os.time()..mangled_name..attempt attempt = attempt + 1 rc = os.execute(MDDIFF..' --mkfifo '..quote(pipe)) until rc == 0 or attempt > 10 if rc ~= 0 then log_internal_error_and_fail('unable to create fifo', "make_slave_filter_process") end filter.inf = io.popen(cmd(quote(pipe)),'r') filter.outf = io.open(pipe,'w') filter.pipe = pipe end end return setmetatable({}, { __index = { read = function(filter,...) if filter.inf == nil then -- check already initialized log_internal_error_and_fail("read called before write", "make_slave_filter_process") end -- once we known the channel is open, we clean up the fifo if not filter.removed and filter.did_write then filter.removed = true local rc = { filter.inf:read(...) } os.remove(filter.pipe) return unpack(rc) else return filter.inf:read(...) end end, write = function(filter,...) init(filter) filter.did_write = true return filter.outf:write(...) end, flush = function(filter) return filter.outf:flush() end, lines = function(filter) return filter.inf:lines() end } }) end -- you should use logs_tags_and_fail function error(msg) local d = debug.getinfo(1,"nl") __error((d.name or '?')..': '..(d.currentline or '?').. ' :attempt to call error instead of log_tags_and_fail') end function log_tags_and_fail(msg,...) log_tags(...) __error({text=msg}) end function log_internal_error_and_fail(msg,...) log_internal_error_tags(msg,...) __error({text=msg}) end function set_verbose(v) verbose = v end function set_dry_run(v) dryrun = v if v then set_verbose(v) end end function dry_run() return dryrun end function set_translator(p) local translator_filter = make_slave_filter_process(function(pipe) return p .. ' < ' .. pipe end, "translate") if p == 'cat' then translator = function(x) return x end else translator = function(x) translator_filter:write(x..'\n') translator_filter:flush() local rc = translator_filter:read('*l') if rc == nil or rc == 'ERROR' then log_error("Translator "..p.." on input "..x.." gave an error") for l in translator_filter:lines() do log_error(l) end log_tags_and_fail('Unable to translate mailbox', 'translate','bad-translator',true) end if rc:match('%.%.') then log_error("Translator "..p.." on input "..x.. " returned a path containing ..") log_tags_and_fail('Translator returned a path containing ..', 'translate','bad-translator',true) end return rc end end end function is_translator_set() return translator ~= false end function translate(x) if is_translator_set() then return translator(x) else return x end end function log(msg) if verbose then io.stderr:write('INFO: ',msg,'\n') end end function log_error(msg) io.stderr:write('ERROR: ',msg,'\n') end function log_tag(tag) io.stderr:write('TAGS: ',tag,'\n') end function log_progress(msg) if verbose then for l in msg:gmatch('\t*([^\n]+)') do io.stderr:write('PROGRESS: ',l,'\n') end end end -- this function shoud be used only by smd-client leaves function log_tags(context, cause, human, ...) cause = cause or 'unknown' context = context or 'unknown' if human then human = "necessary" else human = "avoidable" end local suggestions = {} local suggestions_string = "" if select('#',...) > 0 then suggestions_string = "suggested-actions("..table.concat({...}," ")..")" else suggestions_string = "" end log_tag("error::context("..context..") ".. "probable-cause("..cause..") ".. "human-intervention("..human..") ".. suggestions_string) end -- ======================== data transmission protocol ====================== function transmit(out, path, what) what = what or "all" local f, err = io.open(path,"r") if not f then log_error("Unable to open "..path..": "..(err or "no error")) log_error("The problem should be transient, please retry.") log_tags_and_fail('Unable to open requested file.', "transmit", "simultaneous-mailbox-edit",false,"retry") end local size, err = f:seek("end") if not size then log_error("Unable to calculate the size of "..path) log_error("If it is not a regular file, please move it away.") log_error("If it is a regular file, please report the problem.") log_tags_and_fail('Unable to calculate the size of the requested file.', "transmit", "non-regular-file",true, mk_act('permission', path)) end f:seek("set") if what == "header" then local line local header = {} size = 0 while line ~= "" do line = assert(f:read("*l")) header[#header+1] = line header[#header+1] = "\n" size = size + 1 + string.len(line) end f:close() out:write("chunk " .. size .. "\n") out:write(unpack(header)) out:flush() return end if what == "body" then local line while line ~= "" do line = assert(f:read("*l")) size = size -1 -string.len(line) end end out:write("chunk " .. size .. "\n") while true do local data = f:read(16384) if data == nil then break end out:write(data) end out:flush() f:close() end function receive(inf,outfile) local outf = io.open(outfile,"w") if not outf then log_error("Unable to open "..outfile.." for writing.") log_error('It may be caused by bad directory permissions, '.. 'please check.') log_tags_and_fail("Unable to write incoming data", "receive", "non-writeable-file",true, mk_act('permission', outfile)) end local line = inf:read("*l") if line == nil or line == "ABORT" then log_error("Data transmission failed.") log_error("This problem is transient, please retry.") log_tags_and_fail('server sent ABORT or connection died', "receive","network",false,"retry") end local len = tonumber(line:match('^chunk (%d+)')) local total = len while len > 0 do local next_chunk = 16384 if len < next_chunk then next_chunk = len end local data = inf:read(next_chunk) if data == nil then log_error("Data transmission failed.") log_error("This problem is transient, please retry.") log_tags_and_fail('connection died', "receive","network",false,"retry") end len = len - data:len() outf:write(data) end outf:close() return total end function handshake(dbfile) -- send the protocol version and the dbfile sha1 sum io.write('protocol ',PROTOCOL_VERSION,'\n') -- if true the db file is deleted after SHA1 computation local kill_db_file_ASAP = false -- if the db file was not there and --dry-run, we schedule its deletion if dry_run() and not exists(dbfile) then kill_db_file_ASAP = true end -- we must have at least an empty file to compute its SHA1 sum touch(dbfile) local inf = io.popen(MDDIFF..' --sha1sum '.. quote(dbfile),'r') local db_sha, errmsg = inf:read('*a'):match('^(%S+)(.*)$') inf:close() if db_sha == 'ERROR' then log_internal_error_and_fail('unreadable db file: '.. quote(dbfile),'handshake') end io.write('dbfile ',db_sha,'\n') io.flush() -- but if the file was not there and --dry-run, we should not create it if kill_db_file_ASAP then os.remove(dbfile) end -- check protocol version and dbfile sha local line = io.read('*l') if line == nil then log_error("Network error.") log_error("Unable to get any data from the other endpoint.") log_error("This problem may be transient, please retry.") log_error("Hint: did you correctly setup the SERVERNAME variable") log_error("on your client? Did you add an entry for it in your ssh") log_error("configuration file?") log_tags_and_fail('Network error',"handshake", "network",false,"retry") end local protocol = line:match('^protocol (.+)$') if protocol ~= PROTOCOL_VERSION then log_error('Wrong protocol version.') log_error('The same version of syncmaildir must be used on '.. 'both endpoints') log_tags_and_fail('Protocol version mismatch', "handshake", "protocol-mismatch",true) end line = io.read('*l') if line == nil then log_error "The client disconnected during handshake" log_tags_and_fail('Network error',"handshake", "network",false,"retry") end local sha = line:match('^dbfile (%S+)$') if sha ~= db_sha then log_error('Local dbfile and remote db file differ.') log_error('Remove both files and push/pull again.') log_tags_and_fail('Database mismatch', "handshake", "db-mismatch",true, mk_act('rm',dbfile)) end end function dbfile_name(endpoint, mailboxes) local HOME = os.getenv('HOME') os.execute(MDDIFF..' --mkdir-p '..quote(HOME..'/.smd/')) local dbfile = HOME..'/.smd/' ..endpoint:gsub('/$',''):gsub('/','_').. '__' ..table.concat(mailboxes,'__'):gsub('/$',''):gsub('[/%%]','_').. '.db.txt' return dbfile end -- =================== fast/maildir aware mkdir -p ========================== local mddiff_mkdirln_handler = make_slave_filter_process(function(pipe) return MDDIFF .. ' -s ' .. pipe end, "mk_link_wa") -- create a link from the workarea to the real mailbox using mddiff function mk_link_wa(src, target) mddiff_mkdirln_handler:write(src,'\n',target,'\n') mddiff_mkdirln_handler:flush() local data = mddiff_mkdirln_handler:read('*l') if data:match('^ERROR') or not data:match('^OK') then log_tags_and_fail('Failed to mddiff -s', 'mddiff-s','wrong-permissions',true) end end local mkdir_p_cache = {} -- function to create the dir calling the real mkdir command -- pieces is a list components of the patch, they are concatenated -- separated by '/' and if absolute is true prefixed by '/' function make_dir_aux(absolute, pieces) local root = "" if absolute then root = '/' end local dir = root .. table.concat(pieces,'/') if not mkdir_p_cache[dir] then local rc = 0 local last = pieces[#pieces] if is_translator_set() and not absolute and (last == 'cur' or last == 'new' or last == 'tmp') then local lfn = translate(dir) local abs_lfn = homefy(lfn) if not dry_run() then rc = os.execute(MDDIFF..' --mkdir-p '..quote(abs_lfn)) end if dir ~= lfn then log('translating: '..dir..' -> '..lfn) end mk_link_wa(abs_lfn, dir) else if not dry_run() then rc = os.execute(MDDIFF..' --mkdir-p '..quote(dir)) end end if rc ~= 0 then log_error("Unable to create directory "..dir) log_error('It may be caused by bad directory permissions, '.. 'please check.') log_tags_and_fail("Directory creation failed", "mkdir", "wrong-permissions",true, mk_act('permission',dir)) end mkdir_p_cache[dir] = true end end function tokenize_path(path) local t = {} local absolute = false local file = "" if string.byte(path,1) == string.byte('/',1) then absolute = true end -- tokenization for m in path:gmatch('([^/]+)') do t[#t+1] = m end -- strip last component if not ending with '/' if string.byte(path,string.len(path)) ~= string.byte('/',1) then file=t[#t] table.remove(t,#t) end return absolute, t, file end -- creates a directory that can contains a path, should be equivalent -- to mkdir -p `dirname path`. moreover, if the last component is 'tmp', -- 'cur' or 'new', they are all are created too. exampels: -- mkdir_p('/foo/bar') creates /foo -- mkdir_p('/foo/bar/') creates /foo/bar/ -- mkdir_p('/foo/tmp/baz') creates /foo/tmp/, /foo/cur/ and /foo/new/ function mkdir_p(path) local absolute, t, _ = tokenize_path(path) make_dir_aux(absolute, t) -- ensure new, tmp and cur are there local todo = { ["new"] = true, ["cur"] = true, ["tmp"]=true } if todo[t[#t]] == true then todo[t[#t]] = nil for x, _ in pairs(todo) do t[#t] = x make_dir_aux(absolute, t) end end end -- ============== maildir aware tempfile name generator ===================== -- complex function to generate a valid tempfile name for path, possibly using -- the tmp directory if a subdir of path is new or cur and use_tmp is true -- -- we want something that changes, so we keep a local variable and increment it local smd_pid = 1 function tmp_for(path,use_tmp) if use_tmp == nil then use_tmp = true end local t = {} local absolute = "" if string.byte(path,1) == string.byte('/',1) then absolute = '/' end for m in path:gmatch('([^/]+)') do t[#t+1] = m end local fname = t[#t] local time, pid, host, tags = fname:match('^(%d+)%.([%d_]+)%.([^:]+)(.*)$') time = time or os.date("%s") pid = pid or smd_pid smd_pid = smd_pid + 1 host = host or "localhost" tags = tags or "" table.remove(t,#t) local i, found = 0, false if use_tmp then for i=#t,1,-1 do if t[i] == 'cur' or t[i] == 'new' then t[i] = 'tmp' found = true break end end end make_dir_aux(absolute == '/', t) local newpath if not found then time = os.date("%s") t[#t+1] = time..'.'..pid..'.'..host..tags else t[#t+1] = fname end newpath = absolute .. table.concat(t,'/') local attempts = 0 while exists(newpath) do if attempts > 10 then log_internal_error_and_fail('unable to generate a fresh tmp name: last attempt was '..newpath, "tmp_for") else time = os.date("%s") host = host .. 'x' t[#t] = time..'.'..pid..'.'..host..tags newpath = absolute .. table.concat(t,'/') attempts = attempts + 1 end end return newpath end -- =========================== misc helpers ================================= -- like s:match(spec) but chencks no captures are nil function parse(s,spec) local res = {s:match(spec)} local _,expected = spec:gsub('%b()','') if #res ~= expected then log_internal_error_and_fail('Error parsing "'..s..'"', "protocol") end return unpack(res) end local mddiff_sha_handler = make_slave_filter_process(function(pipe) return MDDIFF .. ' ' .. pipe end, "sha_file") function sha_file(name) mddiff_sha_handler:write(name,'\n') mddiff_sha_handler:flush() local data = mddiff_sha_handler:read('*l') if data:match('^ERROR') then log_tags_and_fail("Failed to sha1 message: "..(name or "nil"), 'sha_file','modify-while-update',false,'retry') end local hsha, bsha = data:match('(%S+) (%S+)') if hsha == nil or bsha == nil then log_internal_error_and_fail('mddiff incorrect behaviour', "mddiff") end return hsha, bsha end function exists(name) local f = io.open(name,'r') if f ~= nil then f:close() return true else return false end end local empty_file_sha = "da39a3ee5e6b4b0d3255bfef95601890afd80709" function exists_and_sha(name) if exists(name) then local h, b = sha_file(name) if h == empty_file_sha and b == empty_file_sha then return false else return true, h, b end else return false end end function cp(src,tgt) local s,err = io.open(src,'r') if not s then return 1, err end local t,err = io.open(tgt,'w+') if not t then return 1, err end local data repeat data = s:read(4096) if data then t:write(data) end until data == nil t:close() s:close() return 0 end function touch(f) local h = io.open(f,'r') if h == nil then h = io.open(f,'w') if h == nil then log_error('Unable to touch '..quote(f)) log_tags_and_fail("Unable to touch a file", "touch","bad-permissions",true, mk_act('permission', f)) else h:close() end else h:close() end end function quote(s) return '"' .. s:gsub('"','\\"'):gsub("%)","\\)").. '"' end function homefy(s) if string.byte(s,1) == string.byte('/',1) then return s else return os.getenv('HOME')..'/'..s end end function mk_act(kind, name) local homefy = function(x) if is_translator_set() and string.byte(x,1) ~= string.byte('/',1) then return homefy(".smd/workarea/"..x) else return homefy(x) end end if kind == "display" then return "display-mail("..quote(homefy(name))..")" elseif kind == "rm" then return "run(rm "..quote(homefy(name))..")" elseif kind == "mv" then return "run(mv -n "..quote(homefy(name)).." ".. quote(tmp_for(homefy(name),true)) ..")" elseif kind == "permission" then return "display-permissions("..quote(homefy(name))..")" else return kind .. (name or '') end end function assert_exists(name) local name = name:match('^([^ ]+)') local rc = os.execute('type '..name..' >/dev/null 2>&1') assert(rc == 0,'Not found: "'..name..'"') end -- a bit brutal, but correct function url_quote(txt) return string.gsub(txt,'.', function(x) return string.format("%%%02X",string.byte(x)) end) end -- the one used by mddiff function url_decode(s) return string.gsub(s,'%%([0-9A-Za-z][0-9A-Za-z])', function(x) return string.char(tonumber(x,16)) end) end function url_encode(s) return string.gsub(s,'[%% ]', function(x) return string.format("%%%2X",string.byte(x)) end) end function log_internal_error_tags(msg,ctx) log_tags("internal-error",ctx,true, 'run(gnome-open "mailto:'..BUGREPORT_ADDRESS..'?'.. 'subject='..url_quote("[smd-bug] internal error")..'&'.. 'body='..url_quote( 'This email reports an internal error, '.. 'something that should never happen.\n'.. 'To help the developers to find and solve the issue, please '.. 'send this email.\n'.. 'If you are able to reproduce the bug, please attach a '.. 'detailed description\n'.. 'of what you do to help the developers to experience the '.. 'same malfunctioning.'.. '\n\n'.. 'smd-version: '..SMDVERSION..'\n'.. 'error-message: '..tostring(msg)..'\n'.. 'backtrace:\n'..debug.traceback() )..'")') end -- parachute function parachute(f,rc) xpcall(f,function(msg) if type(msg) == "table" then log_error(tostring(msg.text)) else log_internal_error_tags("unknown","unknown") log_error(tostring(msg)) log_error(debug.traceback()) end os.exit(rc) end) end -- prints the stack trace. idiom is 'return(trance(x))' so that -- we have in the log the path for the leaf that computed x function trace(x) if verbose then local t = {} local n = 2 while true do local d = debug.getinfo(n,"nl") if not d or not d.name then break end t[#t+1] = d.name ..":".. (d.currentline or "?") n=n+1 end io.stderr:write('TRACE: ',table.concat(t," | "),'\n') end return x end -- strict access to the global environment function set_strict() setmetatable(__G,{ __newindex = function (t,k,v) local d = debug.getinfo(2,"nl") __error((d.name or '?')..': '..(d.currentline or '?').. ' :attempt to create new global '..k) end; __index = function(t,k) local d = debug.getinfo(2,"nl") __error((d.name or '?')..': '..(d.currentline or '?').. ' :attempt to read undefined global '..k) end; }) end -- vim:set ts=4: syncmaildir-1.2.6.2/tests.d/000077500000000000000000000000001262303536600155705ustar00rootroot00000000000000syncmaildir-1.2.6.2/tests.d/benchmarks/000077500000000000000000000000001262303536600177055ustar00rootroot00000000000000syncmaildir-1.2.6.2/tests.d/benchmarks/01-similar-mailboxes000066400000000000000000000004651262303536600234740ustar00rootroot00000000000000#!/bin/bash rm -rf target/Mail cp -r Mail/ target/ test_eq Mail target/Mail sync before=`date +%s` msync 1 after=`date +%s` test_eq Mail target/Mail echo -n "(`expr $after - $before`s," sync before=`date +%s` diff -ruN Mail target/Mail after=`date +%s` echo -n " diff takes `expr $after - $before`s) " syncmaildir-1.2.6.2/tests.d/benchmarks/02-many-cp000066400000000000000000000004361262303536600214160ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail mkdir -p Mail/foo/ sync cbefore=`date +%s` cp -r Mail/cur Mail/foo cafter=`date +%s` sync before=`date +%s` msync 2 after=`date +%s` test_eq Mail target/Mail echo -n "(`expr $after - $before`s, cp takes `expr $cafter - $cbefore`s) " syncmaildir-1.2.6.2/tests.d/benchmarks/03-many-mv000066400000000000000000000004341262303536600214350ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail mkdir -p Mail/foo/ sync mbefore=`date +%s` mv Mail/cur Mail/foo mafter=`date +%s` sync before=`date +%s` msync 2 after=`date +%s` test_eq Mail target/Mail echo -n "(`expr $after - $before`s, mv takes `expr $mafter - $mbefore`s) " syncmaildir-1.2.6.2/tests.d/benchmarks/04-mkdir000066400000000000000000000012731262303536600211620ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX_LOCAL=Mail >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo 'TRANSLATOR_RL="cat"'>> target/.smd/config.default echo 'TRANSLATOR_LR="cat"' >> target/.smd/config.default AMOUNT=3000 rm -rf Mail mkdir target/Mail/ TM=`mktemp` for i in `seq 1 $AMOUNT`; do echo target/Mail/$i/cur >> $TM done D=`date +%s` mkdir -p `cat $TM` D1=`date +%s` OT=`expr $D1 - $D` sync echo -n . D=`date +%s` mpush D1=`date +%s` echo -n "(`expr $D1 - $D`s, mkdir takes ${OT}s)." W=`ls target/.smd/workarea/Mail | wc -l` assert $W $AMOUNT "workarea not set up properly" syncmaildir-1.2.6.2/tests.d/client-server/000077500000000000000000000000001262303536600203525ustar00rootroot00000000000000syncmaildir-1.2.6.2/tests.d/client-server/01-copy-mailbox000066400000000000000000000002571262303536600231220ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail test -d target/Mail/tmp || (echo "no tmp/"; exit 1) X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/02-move-mail000066400000000000000000000004041262303536600224000ustar00rootroot00000000000000#!/bin/bash msync 1 mv Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/moved_here msync 2 test_eq Mail target/Mail X=`grep '^MOVE ' log.s2c | wc -l` assert $X 1 "missing MOVE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/03-copy-mail000066400000000000000000000005141262303536600224070ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here msync 2 test_eq Mail target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/04-replace-header000066400000000000000000000005651262303536600233650ustar00rootroot00000000000000#!/bin/sh msync 1 sed -i 's/^Subject:.*$/Subject: PIPPO/' Mail/cur/`ls Mail/cur/ | head -n 1` msync 2 test_eq Mail target/Mail X=`grep '^REPLACEHEADER ' log.s2c | wc -l` assert $X 1 "missing REPLACEHEADER in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 1 "missing GETHEADER in c2s" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/05-replace-header-fail000066400000000000000000000007771262303536600243040ustar00rootroot00000000000000#!/bin/sh msync 1 sed -i 's/^Subject:.*$/Subject: PIPPO/' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i 's/^Subject:.*$/Subject: PLUTO/' target/Mail/cur/`ls Mail/cur/ | head -n 1` cp -r target/Mail target/Mail.old msync 2 test_eq target/Mail.old target/Mail X=`grep '^REPLACEHEADER ' log.s2c | wc -l` assert $X 1 "missing REPLACEHEADER in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GETHEADER present in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/06-replace-header-already-ok000066400000000000000000000007551262303536600254160ustar00rootroot00000000000000#!/bin/sh msync 1 sed -i 's/^Subject:.*$/Subject: PIPPO/' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i 's/^Subject:.*$/Subject: PIPPO/' target/Mail/cur/`ls Mail/cur/ | head -n 1` test_eq Mail target/Mail msync 2 test_eq Mail target/Mail X=`grep '^REPLACEHEADER ' log.s2c | wc -l` assert $X 1 "missing REPLACEHEADER in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GETHEADER present in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/07-copy-mail-fail000066400000000000000000000006711262303536600233300ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | tail -n 1` target/Mail/cur/copied_here cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here cp -r target/Mail target/Mail.old msync 2 test_eq target/Mail.old target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/08-copy-mail-already-ok000066400000000000000000000006171262303536600244460ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here cp Mail/cur/`ls Mail/cur/ | head -n 1` target/Mail/cur/copied_here msync 2 test_eq Mail target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/09-copy-mail-cp-error000066400000000000000000000006651262303536600241530ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here cp -r target/Mail target/Mail.old chmod a-w target/Mail/cur msync 2 chmod u+w target/Mail/cur test_eq target/Mail.old target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET present in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/10-delete-already-done000066400000000000000000000004521262303536600243200ustar00rootroot00000000000000#!/bin/bash msync 1 rm Mail/cur/`ls Mail/cur/ | head -n 1` rm target/Mail/cur/`ls target/Mail/cur/ | head -n 1` msync 2 test_eq Mail target/Mail X=`grep '^DELETE ' log.s2c | wc -l` assert $X 1 "missing DELETE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/11-delete-fail-local-change000066400000000000000000000005631262303536600252060ustar00rootroot00000000000000#!/bin/bash msync 1 rm Mail/cur/`ls Mail/cur/ | head -n 1` sed -i 's/Subject/Subbbbbbject/' target/Mail/cur/`ls target/Mail/cur/ | head -n 1` cp -r target/Mail target/Mail.old msync 2 test_eq target/Mail.old target/Mail X=`grep '^DELETE ' log.s2c | wc -l` assert $X 1 "missing DELETE in s2c" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/12-skip-add-already-there000066400000000000000000000006021262303536600247330ustar00rootroot00000000000000#!/bin/bash msync 1 echo -en 'Subject: aa\n\nBody\n' > Mail/cur/new_mail echo -en 'Subject: aa\n\nBody\n' > target/Mail/cur/new_mail msync 2 test_eq Mail target/Mail X=`grep '^ADD ' log.s2c | wc -l` assert $X 1 "missing ADD in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/13-skip-add-already-there-and-changed000066400000000000000000000006541262303536600270720ustar00rootroot00000000000000#!/bin/bash msync 1 echo -en 'Subject: aa\n\nBody\n' > Mail/cur/new_mail echo -en 'Subject: bb\n\nBody\n' > target/Mail/cur/new_mail cp -r target/Mail target/Mail.old msync 2 test_eq target/Mail.old target/Mail X=`grep '^ADD ' log.s2c | wc -l` assert $X 1 "missing ADD in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/14-skip-copy-already-there-copy-only000066400000000000000000000006431262303536600271130ustar00rootroot00000000000000#!/bin/bash msync 1 C=`ls Mail/cur/ | head -n 1` cp Mail/cur/$C Mail/cur/copied_here cp Mail/cur/$C target/Mail/cur/copied_here rm target/Mail/cur/$C msync 2 rm Mail/cur/$C test_eq Mail target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/15-fail-copy-already-there-but-diff000066400000000000000000000010051262303536600266210ustar00rootroot00000000000000#!/bin/bash msync 1 C=`ls Mail/cur/ | head -n 1` cp Mail/cur/$C Mail/cur/copied_here cp Mail/cur/$C target/Mail/cur/copied_here sed -i 's/Subject/XXXX/' target/Mail/cur/copied_here rm target/Mail/cur/$C cp -r target/Mail target/Mail.old msync 2 rm Mail/cur/$C test_eq target/Mail.old target/Mail X=`grep '^COPY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/16-fail-delete000066400000000000000000000005271262303536600227000ustar00rootroot00000000000000#!/bin/bash msync 1 rm Mail/cur/`ls Mail/cur/ | head -n 1` cp -r target/Mail/ target/Mail.old chmod a-w target/Mail/cur msync 2 chmod u+w target/Mail/cur test_eq target/Mail.old target/Mail X=`grep '^DELETE ' log.s2c | wc -l` assert $X 1 "missing DELETE in s2c" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/17-fail-rename000066400000000000000000000005401262303536600227010ustar00rootroot00000000000000#!/bin/bash msync 1 echo -en 'Subject: foo\n\nBODY\n' > Mail/cur/new_mail cp -r target/Mail/ target/Mail.old chmod a-w target/Mail/cur msync 2 chmod u+w target/Mail/cur test_eq target/Mail.old target/Mail X=`grep '^ADD ' log.s2c | wc -l` assert $X 1 "missing ADD in s2c" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/18-copybody000066400000000000000000000010301262303536600223450ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here sed -i 's/^Subject.*$/Subject: foo/' Mail/cur/copied_here msync 2 test_eq Mail target/Mail X=`grep '^COPYBODY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^ADD ' log.s2c | wc -l` assert $X 0 "COPYBODY+ADD in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 1 "no GETHEADEr in c2s" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/19-copybody-already-ok000066400000000000000000000010211262303536600243740ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here sed -i 's/^Subject.*$/Subject: foo/' Mail/cur/copied_here cp Mail/cur/copied_here target/Mail/cur/copied_here msync 2 test_eq Mail target/Mail X=`grep '^COPYBODY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GETHEADEr in c2s, should not" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s, should not" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/20-copybody-cp-error000066400000000000000000000007721262303536600241010ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here sed -i 's/^Subject:.*$/Subject: foo/' Mail/cur/copied_here cp -r target/Mail target/Mail.old chmod a-w target/Mail/cur msync 2 chmod u+w target/Mail/cur test_eq target/Mail.old target/Mail X=`grep '^COPYBODY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GET present in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/21-copybody-already-there000066400000000000000000000011261262303536600250710ustar00rootroot00000000000000#!/bin/bash msync 1 cp Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/copied_here sed -i 's/^Subject:.*$/Subject: foo/' Mail/cur/copied_here cp Mail/cur/copied_here target/Mail/cur/copied_here echo foo >> target/Mail/cur/copied_here cp -r target/Mail target/Mail.old chmod a-w target/Mail/cur msync 2 chmod u+w target/Mail/cur test_eq target/Mail.old target/Mail X=`grep '^COPYBODY ' log.s2c | wc -l` assert $X 1 "missing COPY in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GET present in c2s, should not" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/22-replace000066400000000000000000000010411262303536600221250ustar00rootroot00000000000000#!/bin/bash msync 1 sed -i 's/^Subject.*$/Subject: foo/' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i '$ a foo' Mail/cur/`ls Mail/cur/ | head -n 1` msync 2 test_eq Mail target/Mail X=`grep '^REPLACE ' log.s2c | wc -l` assert $X 1 "missing REPLACE in s2c" X=`grep '^ADD ' log.s2c | wc -l` assert $X 0 "REPLACE+ADD in s2c" X=`grep '^GETHEADER ' log.c2s | wc -l` assert $X 0 "GETHEADEr in c2s, should not" X=`grep '^GET ' log.c2s | wc -l` assert $X 1 "no GET in c2s" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/23-replace-aready-ok000066400000000000000000000010241262303536600240010ustar00rootroot00000000000000#!/bin/bash msync 1 sed -i 's/^Subject.*$/Subject: foo/' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i '$ a foo' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i 's/^Subject.*$/Subject: foo/' target/Mail/cur/`ls Mail/cur/ | head -n 1` sed -i '$ a foo' target/Mail/cur/`ls Mail/cur/ | head -n 1` msync 2 test_eq Mail target/Mail X=`grep '^REPLACE ' log.s2c | wc -l` assert $X 1 "missing REPLACE in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/24-replace-aready-error000066400000000000000000000011001262303536600245150ustar00rootroot00000000000000#!/bin/bash msync 1 sed -i 's/^Subject.*$/Subject: foo/' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i '$ a foo' Mail/cur/`ls Mail/cur/ | head -n 1` sed -i 's/^Subject.*$/Subject: bar/' target/Mail/cur/`ls Mail/cur/ | head -n 1` sed -i '$ a bar' target/Mail/cur/`ls Mail/cur/ | head -n 1` cp -r target/Mail target/Mail.old msync 2 test_eq target/Mail.old target/Mail X=`grep '^REPLACE ' log.s2c | wc -l` assert $X 1 "missing REPLACE in s2c" X=`grep '^GET ' log.c2s | wc -l` assert $X 0 "GET in c2s" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/25-mddiff-error000066400000000000000000000003411262303536600230770ustar00rootroot00000000000000#!/bin/bash msync 1 mv Mail Mail.moved msync 2 test_eq Mail.moved target/Mail X=`grep '^ERROR ' log.s2c | wc -l` assert $X 1 "missing ERROR in s2c" X=`grep '^ABORT$' log.c2s | wc -l` assert $X 1 "missing ABORT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/26-copy-in-newdir000066400000000000000000000004131262303536600233640ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail mkdir -p Mail/foo/cur mkdir -p Mail/foo/tmp mkdir -p Mail/foo/new cp Mail/cur/`ls Mail/cur | head -n 1` Mail/foo/new msync 2 test_eq Mail target/Mail test -d target/Mail/foo/new assert $? 0 "not creating foo/new" syncmaildir-1.2.6.2/tests.d/client-server/27-db-mismatch000066400000000000000000000005741262303536600227210ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail cp -r target/Mail target/Mail.bak # altering the bd sed -i 's/52/35/' .smd/test__Mail.db.txt mv Mail/cur/`ls Mail/cur/ | head -n 1` Mail/cur/moved_here msync 2 test_eq target/Mail.bak target/Mail assert $? 0 "altering mailbox even if db mismatch" N=`grep TAGS log.client.* | wc -l` assert $N 1 "spitting tags more than once" syncmaildir-1.2.6.2/tests.d/client-server/28-mailmessage-crazy-filename000066400000000000000000000002461262303536600257210ustar00rootroot00000000000000#!/bin/bash msync 1 mv Mail/cur/`ls Mail/cur/ | head -n 1` "Mail/cur/moved_here " msync 2 X=`grep '^TAGS' log.client.* | wc -l` assert $X 1 "missing TAG output" syncmaildir-1.2.6.2/tests.d/client-server/29-copybody-in-newdir000066400000000000000000000004761262303536600242560ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail mkdir -p Mail/foo/cur mkdir -p Mail/foo/tmp mkdir -p Mail/foo/new cp Mail/cur/`ls Mail/cur | head -n 1` Mail/foo/new sed -i 's/^Subject:.*/Subject: XXX/' Mail/foo/new/* msync 2 test_eq Mail target/Mail test -d target/Mail/foo/new assert $? 0 "not creating foo/new" syncmaildir-1.2.6.2/tests.d/client-server/30-no-smd-fifo-dir-fail000066400000000000000000000002231262303536600243150ustar00rootroot00000000000000#!/bin/bash rmdir .smd/fifo rmdir target/.smd/fifo msync 1 X=`grep '^TAGS: error::.*internal-error' log.client.* | wc -l` assert $X 0 "failing" syncmaildir-1.2.6.2/tests.d/client-server/31-space-in-name000066400000000000000000000006441262303536600231370ustar00rootroot00000000000000#!/bin/bash msync 1 test_eq Mail target/Mail mkdir -p "Mail/very bad/cur" mkdir "Mail/very bad/new" mkdir "Mail/very bad/tmp" cp Mail/cur/`basename $(ls Mail/cur|head -1)` 'Mail/very bad/cur/foo %' cp Mail/cur/`basename $(ls Mail/cur|head -2)` 'Mail/very bad/cur/foo %11' sed -i 's/a/z/' 'Mail/very bad/cur/foo %11' mv Mail/cur/`basename $(ls Mail/cur|head -1)` 'Mail/cur/foo %' msync 2 test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/client-server/32-absolute-path000066400000000000000000000001601262303536600232640ustar00rootroot00000000000000#!/bin/bash bin/smd-client test /Foo > log 2>&1 if [ $? -eq 0 ]; then assert 0 1 "Accepting absolute path" fi syncmaildir-1.2.6.2/tests.d/client-server/33-move-fail1000066400000000000000000000005131262303536600224570ustar00rootroot00000000000000#!/bin/bash msync 1 M=Mail/cur/`ls Mail/cur/ | head -n 1` mv $M Mail/cur/copied_here echo -e 'foo\n\nbar' > target/Mail/cur/copied_here msync 2 test_eq target/$M Mail/cur/copied_here X=`grep '^MOVE ' log.s2c | wc -l` assert $X 1 "missing MOVE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 0 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/34-move-fail2000066400000000000000000000004631262303536600224650ustar00rootroot00000000000000#!/bin/bash msync 1 M=Mail/cur/`ls Mail/cur/ | head -n 1` mv $M Mail/cur/copied_here mv target/$M target/Mail/cur/copied_here msync 2 test_eq target/Mail Mail X=`grep '^MOVE ' log.s2c | wc -l` assert $X 1 "missing MOVE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/35-delete000066400000000000000000000003711262303536600217650ustar00rootroot00000000000000#!/bin/bash msync 1 M=Mail/cur/`ls Mail/cur/ | head -n 1` rm $M msync 2 test_eq target/Mail Mail X=`grep '^DELETE ' log.s2c | wc -l` assert $X 1 "missing DELETE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/36-move-fail3000066400000000000000000000004631262303536600224700ustar00rootroot00000000000000#!/bin/bash msync 1 M=Mail/cur/`ls Mail/cur/ | head -n 1` mv $M Mail/cur/copied_here cp target/$M target/Mail/cur/copied_here sed -i 's/a/z/' target/$M msync 2 X=`grep '^MOVE ' log.s2c | wc -l` assert $X 1 "missing MOVE in s2c" X=`grep '^COMMIT$' log.c2s | wc -l` assert $X 1 "missing COMMIT in c2s" syncmaildir-1.2.6.2/tests.d/client-server/summarize.sh000077500000000000000000000023211262303536600227230ustar00rootroot00000000000000#!/bin/sh echo echo --- client-server summary ----------------------------------------------- echo ROOT=$BASE/tests.d/run/client-server check_bin() { if which $1 >/dev/null; then return else echo $1 not installed exit 1 fi } check_bin combine check_bin grep check_bin cut check_bin sed check_bin awk check_bin sort PATHS=`grep TRACE $ROOT/[0-9]*/log.client* | cut -d '|' -f 1 | cut -d : -f 2- | sort -u | wc -l` echo echo "Tested $PATHS paths" echo grep TRACE $ROOT/[0-9]*/log.client* \ | sort -u | sed 's?^.*/\([^/]*\):?\1:?' | sed 's/TRACE://' echo echo "Surely missing leaves (there may be more paths for the same leaf):" echo tmpa=`mktemp` tmpb=`mktemp` grep -n 'return *( *trace' $BASE/smd-client \ | cut -d : -f 1 | sed 's/ //g' > $tmpa grep TRACE $ROOT/[0-9]*/log.client* \ | sort -u | cut -d : -f 4 \ | cut -d \| -f 1 | sed 's/ //g' > $tmpb for N in `combine $tmpa not $tmpb`; do awk \ "{L++} L==$N {\$1=\$2=\$3=\"\";print \"smd-client: \" L \":\" \$0 }" \ $BASE/smd-client done rm $tmpa $tmpb echo echo "Generated tags:" echo grep ^TAG $ROOT/[0-9]*/log.client* \ | cut -d : -f 2- | cut -d \( -f 1 | sort -u echo echo ------------------------------------------------------------------------- echo syncmaildir-1.2.6.2/tests.d/common000066400000000000000000000076541262303536600170170ustar00rootroot00000000000000#!/bin/bash # # BASE: where you can run make/make install # TESTSUITE: client-server, pull-push #set -x VALGRIND=${VALGRIND:-"valgrind --log-file=log.valgrind --leak-check=no --error-exitcode=1 --"} ROOT=$BASE/tests.d/run/$TESTSUITE/ TOKILL="" if [ ! -z "$VALGRIND" ]; then if which valgrind >/dev/null 2>&1; then : else echo 'VALGRIND set but not installed. Disabling it' VALGRIND= fi fi out(){ ( for P in $TOKILL; do kill $P done ) 2>/dev/null } trap out EXIT test_eq(){ if diff -ruN "$1" "$2" >/dev/null; then echo -n . else echo ERROR: diff exit 1 fi } run_server(){ HOME=$ROOT/$TESTSUITE/$__N__/ \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ smd-server -v test Mail < c2s 2> log.server.$1 \ | tee log.s2c > s2c & RC=$! TOKILL="$TOKILL $RC" } run_client(){ rm -rf target/.smd/workarea/ mkdir -p target/.smd/workarea/ cd target/.smd/workarea/ local DD=../../../ HOME=$ROOT/$TESTSUITE/$__N__/target \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ smd-client -t 'cat' -v \ test Mail < $DD/s2c 2> $DD/log.client.$1 \ | tee $DD/log.c2s > $DD/c2s & RC=$! TOKILL="$TOKILL $RC" cd $DD } msync(){ run_server $__N__ local SERVER=$RC run_client $__N__ local CLIENT=$RC wait $SERVER wait $CLIENT } mpull(){ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ HOME=$ROOT/$TESTSUITE/$__N__/target \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ HOMES=$ROOT/$TESTSUITE/$__N__/ \ $ROOT/$TESTSUITE/$__N__/bin/smd-pull "$@" > log.pull 2>&1 } mpush(){ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ HOME=$ROOT/$TESTSUITE/$__N__/target \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ HOMES=$ROOT/$TESTSUITE/$__N__/ \ $ROOT/$TESTSUITE/$__N__/bin/smd-push "$@" > log.push 2>&1 } mdiff(){ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ HOME=$ROOT/$TESTSUITE/$__N__/ \ $VALGRIND $ROOT/$TESTSUITE/$__N__/bin/mddiff "$@" > log.mddiff 2>&1 } mcheck(){ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ HOME=$ROOT/$TESTSUITE/$__N__/target/ \ HOMES=$ROOT/$TESTSUITE/$__N__/ \ $ROOT/$TESTSUITE/$__N__/bin/smd-check-conf "$@" > log.check 2>&1 } muniform(){ LUA_INIT="package.path='$ROOT/$TESTSUITE/$__N__/share/lua/5.1/?.lua;'" \ HOME=$ROOT/$TESTSUITE/$__N__/target \ PATH=$ROOT/$TESTSUITE/$__N__/bin:$PATH \ HOMES=$ROOT/$TESTSUITE/$__N__/ \ $ROOT/$TESTSUITE/$__N__/bin/smd-uniform-names "$@" \ > log.uniform 2>&1 } assert(){ if [ "$1" = "$2" ]; then echo -n '.' else echo 'ERROR: ' $3 exit 1 fi } prepare(){ local testcase=$1 local __N__=$2 cd $BASE rm -rf $ROOT/$TESTSUITE/$__N__ mkdir -p $ROOT/$TESTSUITE/$__N__/target make --no-print-directory --quiet text/all make --no-print-directory --quiet text/install-bin \ SSH=$ROOT/$TESTSUITE/$__N__/bin/fakessh \ PREFIX=$ROOT/$TESTSUITE/$__N__ cd $ROOT/$TESTSUITE/$__N__ cat > bin/fakessh <<-EOT #!/bin/sh shift cd \$HOMES HOME=\$HOMES "\$@" EOT chmod a+x bin/fakessh tar -xzf $BASE/$testcase mkfifo s2c mkfifo c2s mkdir -p .smd/fifo/ mkdir -p target/.smd/fifo/ } run_test() { local testcase="$1" local T="$2" echo -n "running $TESTSUITE/`basename $T`: " local __N__=`echo $T | sed 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/'` prepare $testcase $__N__ cd $ROOT/$TESTSUITE/$__N__ . $T echo OK } run_tests() { local testcase="$1" local testfile="$2" if [ ! -z "$testfile" ]; then if [ -f $BASE/tests.d/$testfile ] && (echo $testfile | grep ^$TESTSUITE > /dev/null); then run_test $testcase $BASE/tests.d/$testfile elif [ -d $BASE/tests.d/$testfile ] && (echo $testfile | grep ^$TESTSUITE > /dev/null); then for T in $BASE/tests.d/$testfile/[0-9]*; do run_test $testcase $T done fi else for T in $BASE/tests.d/$TESTSUITE/[0-9]*; do run_test $testcase $T done fi } syncmaildir-1.2.6.2/tests.d/mddiff/000077500000000000000000000000001262303536600170215ustar00rootroot00000000000000syncmaildir-1.2.6.2/tests.d/mddiff/01-dry-run000066400000000000000000000006341262303536600205650ustar00rootroot00000000000000#!/bin/sh mdiff Mail assert $? 0 "mdiff failed" test -f db.txt.new assert $? 0 "mdiff not generating .new file" mv db.txt.new db.txt mdiff -d Mail assert $? 0 "mdiff -d failed" test -f db.txt.new assert $? 1 "mdiff generating .new file even if -d is passed" mdiff --dry-run Mail assert $? 0 "mdiff --dry-run failed" test -f db.txt.new assert $? 1 "mdiff generating .new file even if --dry-run is passed" syncmaildir-1.2.6.2/tests.d/mddiff/02-first-run000066400000000000000000000004761262303536600211230ustar00rootroot00000000000000#!/bin/sh mdiff Mail assert $? 0 "mdiff failed" test -f db.txt.new assert $? 0 "mdiff not generating .new file" test -f db.txt.mtime.new assert $? 0 "mdiff not generating mtime.new file" grep -v ^warning log.mddiff > log.cleaned grep -v ^ADD log.cleaned > /dev/null assert $? 1 "mdiff generating non ADD commands" syncmaildir-1.2.6.2/tests.d/mddiff/03-non-existing-dir000066400000000000000000000001411262303536600223560ustar00rootroot00000000000000#!/bin/sh mdiff MailXX assert $? 1 "mdiff was successful even if called on a non-existing dir" syncmaildir-1.2.6.2/tests.d/mddiff/04-small-max-mailno000066400000000000000000000003371262303536600223400ustar00rootroot00000000000000#!/bin/sh mdiff --max-mailno 3 Mail assert $? 0 "mdiff failed" mdiff --max-mailno 4 Mail assert $? 0 "mdiff failed" mdiff --max-mailno 5 Mail assert $? 0 "mdiff failed" mdiff --max-mailno 7 Mail assert $? 0 "mdiff failed" syncmaildir-1.2.6.2/tests.d/mddiff/05-multiple-mail-dir000066400000000000000000000004031262303536600225120ustar00rootroot00000000000000#!/bin/sh mv Mail MailA cp -r MailA MailB mdiff MailA MailB assert $? 0 "mdiff failed" grep '^ADD.* MailA' log.mddiff > /dev/null assert $? 0 "mdiff not processing MailA" grep '^ADD.* MailB' log.mddiff > /dev/null assert $? 0 "mdiff not processing MailB" syncmaildir-1.2.6.2/tests.d/mddiff/06-listing-folders000066400000000000000000000001711262303536600222730ustar00rootroot00000000000000#!/bin/sh mdiff -l Mail assert $? 0 "mdiff failed" assert `cat log.mddiff` "Mail/cur" "mddiff listed non existing dirs" syncmaildir-1.2.6.2/tests.d/mddiff/07-mkdir000066400000000000000000000004141262303536600202750ustar00rootroot00000000000000#!/bin/sh rm -rf Mail mkdir -p a/b/c mkdir -p a/b/d mkfifo f mdiff -s f & MD=$! echo -e "$PWD/a/b/c\nd/e/f/g\n$PWD/a/b/d\nd/e/f/h" > f wait $MD assert $? 0 "mdiff failed making dirs" test -L d/e/f/g assert $? 0 "no d/e/f/g" test -L d/e/f/h assert $? 0 "no d/e/f/h" syncmaildir-1.2.6.2/tests.d/mddiff/08-exclude000066400000000000000000000007231262303536600206240ustar00rootroot00000000000000#!/bin/sh mkdir -p a/b/c/ mkdir -p a/b/d/new cp Mail/cur/* a/b/d/new mv Mail/cur a/b/c/ rm -rf Mail mdiff --exclude '*/new' -l a assert `wc -l log.mddiff | cut -d ' ' -f 1` 1 "not excluding new" mdiff --exclude '*/new' a assert `grep 'ADD a/b/c/cur' log.mddiff | wc -l` 100 "not 100 mails" mdiff --exclude 'b' a assert `grep 'ADD a/b/' log.mddiff | wc -l` 200 "not 200 mails" mdiff --exclude "*/b/*" -v a assert `grep 'ADD a/b/' log.mddiff | wc -l` 0 "not 0 mails" syncmaildir-1.2.6.2/tests.d/mddiff/09-no-delete000066400000000000000000000013741262303536600210530ustar00rootroot00000000000000#!/bin/sh mdiff Mail assert $? 0 "mdiff failed" mv db.txt.new db.txt mv db.txt.mtime.new db.txt.mtime ls Mail/cur | head -n 2 > mails exec 7 mails exec 7 mails exec 7 mails exec 7 mails exec 7 mails exec 7 mails exec 7> target/.smd/config.default cp -r Mail target/Mail cd target/Mail/cur for x in *; do mv $x $x`md5sum $x | cut -d ' ' -f 1`; done cd ../../.. muniform -v cd target sh smd-rename.sh cd .. test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/migration/02-unif-w-trans000066400000000000000000000014261262303536600222600ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo 'MAILBOX_LOCAL="MyMail/"' >> target/.smd/config.default echo 'MAILBOX_REMOTE="Mail/"' >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m default -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d LR default"' >> target/.smd/config.default echo DEBUG=true >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/* Mail/.foo.bar/ mkdir -p target/MyMail/foo.bar/cur cp Mail/.foo.bar/cur/* target/MyMail/foo.bar/cur cd target/MyMail/foo.bar/cur for x in *; do mv $x $x-`md5sum $x | cut -d ' ' -f 1`; done cd ../../../.. muniform -v cd target sh smd-rename.sh cd .. test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ syncmaildir-1.2.6.2/tests.d/migration/03-fail-not-first-sync000066400000000000000000000003121262303536600235300ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default mpull assert $? 0 "failed mpull" muniform -v assert $? 1 "muniform does not fail even if already sync" syncmaildir-1.2.6.2/tests.d/migration/04-noop000066400000000000000000000004271262303536600207030ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default cp -r Mail target/Mail muniform -v cd target N=`grep ^mv smd-rename.sh| wc -l` assert $N 0 "some renamings on identical mailboxes" sh smd-rename.sh cd .. test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/migration/05-unif-w-trans-w-exclude000066400000000000000000000027001262303536600241520ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo 'MAILBOX_LOCAL="MyMail/"' >> target/.smd/config.default echo 'MAILBOX_REMOTE="Mail/"' >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m default -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d LR default"' >> target/.smd/config.default echo 'EXCLUDE_LOCAL="MyMail/mygarbage"' >> target/.smd/config.default echo 'EXCLUDE_REMOTE="Mail/.garbage"' >> target/.smd/config.default echo DEBUG=true >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/* Mail/.foo.bar/ mkdir -p target/MyMail/foo.bar/cur cp Mail/.foo.bar/cur/* target/MyMail/foo.bar/cur cd target/MyMail/foo.bar/cur for x in *; do mv $x $x-`md5sum $x | cut -d ' ' -f 1`; done cd ../../../.. mkdir -p target/MyMail/mygarbage/cur echo -e "head: er\n\nbody" > target/MyMail/mygarbage/cur/mail1 mkdir -p Mail/.garbage/cur echo -e "head: er\n\nbody" > Mail/.garbage/cur/mail1 mcheck -v assert $? 0 "failed mcheck" muniform -v cd target sh smd-rename.sh cd .. test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ N=`grep garbage target/smd-rename.sh | wc -l` assert $N 0 "uniform ignores EXCLUDE" mpull -v assert $? 0 "failed mpull" mpush -v assert $? 0 "failed mpush" test_eq Mail/cur target/MyMail/cur test ! -d target/MyMail/garbage assert $? 0 "EXCLUDE_LOCAL ignored" test ! -d Mail/.mygarbage assert $? 0 "EXCLUDE_REMOTE ignored" syncmaildir-1.2.6.2/tests.d/migration/06-unif-bug-zack000066400000000000000000000016411262303536600223730ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo 'MAILBOX_LOCAL="MyMail/"' >> target/.smd/config.default echo 'MAILBOX_REMOTE="Mail/"' >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m oimap-dovecot -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m oimap-dovecot -d LR default"' >> target/.smd/config.default echo DEBUG=true >> target/.smd/config.default cp Mail/cur/`ls Mail/cur|head -n 1` Mail/cur/copy rm Mail/cur/[0-9]* cp Mail/cur/copy Mail/cur/copy1 cp -r Mail target/MyMail cd target/MyMail/cur for x in *; do mv $x $x`md5sum $x | cut -d ' ' -f 1`; done cd ../../.. muniform -v cd target sh smd-rename.sh cd .. N=`ls target/MyMail/cur | wc -l` assert $N 3 "not 3 mails" N=`grep '^cp ' target/smd-rename.sh | wc -l` assert $N 1 "not 1 cp" N=`grep '^mv ' target/smd-rename.sh | wc -l` assert $N 1 "not 1 mv" syncmaildir-1.2.6.2/tests.d/pull-push/000077500000000000000000000000001262303536600175215ustar00rootroot00000000000000syncmaildir-1.2.6.2/tests.d/pull-push/01-confdir000066400000000000000000000006321262303536600213070ustar00rootroot00000000000000#!/bin/sh rm -rf .smd target/.smd mpull -t assert $? 0 "mpull -t failed" for d in "" "log" "fifo" "hooks/pre-pull.d" "hooks/post-pull.d" "hooks/pre-push.d" "hooks/post-push.d" ; do test -d target/.smd/$d assert $? 0 "missing dir .smd/$d" done test -f target/.smd/config.default assert $? 0 "missing config.default" rm -rf target/.smd/ rmdir target assert $? 0 "target dir not empty, -t should not sync" syncmaildir-1.2.6.2/tests.d/pull-push/02-pull000066400000000000000000000002341262303536600206360ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default mpull assert $? 0 "failed mpull" test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/pull-push/03-confdir2000066400000000000000000000002011262303536600213630ustar00rootroot00000000000000#!/bin/sh mpull assert $? 1 "if not -t, it should fail" mpull -t assert $? 0 "if -t, it should not fail, even if .smd exists" syncmaildir-1.2.6.2/tests.d/pull-push/04-hooks000066400000000000000000000003571262303536600210150ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/pre-pull.d/01-test < /dev/null assert $? 0 "hook not called" syncmaildir-1.2.6.2/tests.d/pull-push/05-hooks-noexec-fail000066400000000000000000000002441262303536600232010ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/post-pull.d/01-test < /dev/null assert $? 1 "hook called" syncmaildir-1.2.6.2/tests.d/pull-push/06-hooks-post000066400000000000000000000003641262303536600220000ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/post-pull.d/01-test < /dev/null assert $? 0 "hook not called" syncmaildir-1.2.6.2/tests.d/pull-push/07-hooks-post-fail000066400000000000000000000004421262303536600227070ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/post-pull.d/01-test < /dev/null assert $? 0 "hook not called correctly" syncmaildir-1.2.6.2/tests.d/pull-push/08-confdir-longopts000066400000000000000000000002501262303536600231550ustar00rootroot00000000000000#!/bin/sh mpull assert $? 1 "if not --template-only, it should fail" mpull --template-only assert $? 0 "if --template-only, it should not fail, even if .smd exists" syncmaildir-1.2.6.2/tests.d/pull-push/09-pull-dry-run000066400000000000000000000007051262303536600222460ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default M=`ls Mail/cur/ | wc -l` mpull --dry-run assert $? 0 "failed mpull --dry-run" test -d target/Mail assert $? 1 "mpull --dry-run did something" N=`ls -l target/.smd/*.db.txt* 2>/dev/null | wc -l` assert $N 0 "mpull --dry-run created dbfile" N=`grep mail-transferred log.pull | sed 's? , ?\n?g' | wc -l` assert $N $M "dry run not printing the list of mails" syncmaildir-1.2.6.2/tests.d/pull-push/10-replace-header-dry-run000066400000000000000000000005221262303536600241200ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default mpull assert $? 0 "failed mpull" cp -r target/Mail target/Mail.backup sed -i 's/^Subject:.*$/Subject: PIPPO/' Mail/cur/`ls Mail/cur/ | head -n 1` mpull --dry-run assert $? 0 "failed mpull --dry-run" test_eq target/Mail.backup target/Mail syncmaildir-1.2.6.2/tests.d/pull-push/11-hook-logging000066400000000000000000000004201262303536600222430ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/post-pull.d/01-test <&2 EOT chmod a+x target/.smd/hooks/post-pull.d/01-test mpull grep 'post pull default 0' target/.smd/log/client.default.log > /dev/null assert $? 0 "hook output not redirected to log file" syncmaildir-1.2.6.2/tests.d/pull-push/12-hook-ssh-fail000066400000000000000000000003661262303536600223350ustar00rootroot00000000000000#!/bin/sh mpull -t cat > target/.smd/hooks/post-pull.d/01-test < /dev/null assert $? 0 "no tags generated" syncmaildir-1.2.6.2/tests.d/pull-push/13-symlink000066400000000000000000000002371262303536600213550ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" cd target mkdir MailDir ln -s MailDir Mail cd .. mpull assert $? 0 "failed mpull" test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/pull-push/14-translator000066400000000000000000000054211262303536600220610ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX_LOCAL=MyMail >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo TRANSLATOR_RL=../trl >> target/.smd/config.default echo TRANSLATOR_LR=../tlr >> target/.smd/config.default echo DEBUG=true >> target/.smd/config.default cat > tlr < trl < /dev/null assert $? 1 "smd-pull did not detect cruft in workarea/" rm target/.smd/workarea/cruft mpush test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur chmod a-rx target/MyMail/foo.bar/ mpush R=$? chmod a+rx target/MyMail/foo.bar/ assert $R 1 "smd-push did not detect a scanning error" mpush test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur rm -rf target/MyMail target/.smd/*.db.txt* .smd/*.db.txt* mpull -d W=`grep translating log.pull | wc -l` assert $W 9 "smd-pull -d not printing 'translating' lines" mpull rm -rf Mail target/.smd/*.db.txt* .smd/*.db.txt* mpush -d W=`grep translating log.push | wc -l` assert $W 6 "smd-push -d not printing 'translating' lines" mpush assert $? 0 "mpush failed" mkdir -p "target/MyMail/bad name/" mv target/MyMail/cur "target/MyMail/bad name/cur" mpush assert $? 0 "mpush failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ mv "Mail/.bad name" "Mail/. very bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/. very bad name" "target/MyMail/ very bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ syncmaildir-1.2.6.2/tests.d/pull-push/15-smd-translate000066400000000000000000000055401262303536600224510ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX_LOCAL=MyMail >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m default -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d LR default"' >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/cur Mail/.foo.bar/ mpull assert $? 0 "failed mpull" test_eq Mail/cur/ target/MyMail/cur/ test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ cp Mail/.foo.bar/cur/`ls Mail/.foo.bar/cur/|head -n 1` Mail/.foo.bar/cur/here sed -i s/a/z/ Mail/.foo.bar/cur/here mpull assert $? 0 "failed mpull" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ mkdir -p Mail/.foo.baz/cur cp Mail/.foo.bar/cur/here Mail/.foo.baz/cur/here mpull assert $? 0 "failed mpull" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ touch target/.smd/workarea/cruft mpull 2> /dev/null assert $? 1 "smd-pull did not detect cruft in workarea/" rm target/.smd/workarea/cruft mpush test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur chmod a-rx target/MyMail/foo.bar/ mpush R=$? chmod a+rx target/MyMail/foo.bar/ assert $R 1 "smd-push did not detect a scanning error" mpush test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur rm -rf target/MyMail target/.smd/*.db.txt* .smd/*.db.txt* mpull -d W=`grep translating log.pull | grep -v ^+ | wc -l` assert $W 9 "smd-pull -d not printing 'translating' lines" mpull rm -rf Mail target/.smd/*.db.txt* .smd/*.db.txt* mpush -d W=`grep translating log.push | grep -v ^+ | wc -l` assert $W 6 "smd-push -d not printing 'translating' lines" mpush assert $? 0 "mpush failed" mkdir -p "target/MyMail/bad name/" mv target/MyMail/cur "target/MyMail/bad name/cur" mpush assert $? 0 "mpush failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ mv "Mail/.bad name" "Mail/. very bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/. very bad name" "target/MyMail/ very bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ rm Mail/.foo.bar/cur/`ls Mail/.foo.bar/cur|head -n 1` rm target/MyMail/foo.baz/cur/`ls target/MyMail/foo.baz/cur|head -n 1` echo 'TRANSLATOR_RL="smd-translate -m default -d XX default"' >> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d XX default"' >> target/.smd/config.default mpull assert $? 1 "mpull suceeded with a bad tanslator" mpush assert $? 1 "mpush suceeded with a bad tanslator" syncmaildir-1.2.6.2/tests.d/pull-push/16-check-conf000066400000000000000000000025271262303536600216760ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX_LOCAL=MyMail >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m oimap-dovecot -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m oimap-dovecot -d LR default"' >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/cur Mail/.foo.bar/ mcheck W=`grep 'Error while listing the content of MyMail' log.check | wc -l` assert $W 1 "listing the content of a non existing dir" W=`grep 'Mail/cur -> MyMail/cur -> Mail/cur' log.check | wc -l` assert $W 1 "Not translating Mail/cur" W=`grep 'Mail/.foo.bar/cur -> MyMail/foo.bar/cur -> Mail/.foo.bar/cur' log.check | wc -l` assert $W 1 "Not translating Mail/.foo.bar/cur" mpull assert $? 0 "mpull failed" mcheck W=`grep 'MyMail/foo.bar/cur -> Mail/.foo.bar/cur -> MyMail/foo.bar/cur' log.check| wc -l` assert $W 1 "Not translating MyMail/foo.bar/cur" W=`grep 'MyMail/cur -> Mail/cur -> MyMail/cur' log.check| wc -l` assert $W 1 "Not translating MyMail/cur" W=`grep 'Mail/cur -> MyMail/cur -> Mail/cur' log.check | wc -l` assert $W 1 "Not translating Mail/cur" W=`grep 'Mail/.foo.bar/cur -> MyMail/foo.bar/cur -> Mail/.foo.bar/cur' log.check | wc -l` assert $W 1 "Not translating Mail/.foo.bar/cur" syncmaildir-1.2.6.2/tests.d/pull-push/17-check-trans-fail000066400000000000000000000015131262303536600230040ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX_LOCAL=Mail >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo 'TRANSLATOR_RL="../mytr1"'>> target/.smd/config.default echo 'TRANSLATOR_LR="../mytr2"' >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/cur Mail/.foo.bar/ cat > mytr1 < mytr2 < ERROR' log.check | wc -l` assert $W 1 "Not translating 2" syncmaildir-1.2.6.2/tests.d/pull-push/18-exclude000066400000000000000000000007261262303536600213300ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo 'EXCLUDE="Mail/cur Mail/w%20space"' >> target/.smd/config.default mkdir target/Mail mkdir -p 'Mail/w space/cur' mkdir -p 'Mail/x/cur' cp -r Mail/cur 'Mail/w space/cur' cp -r Mail/cur 'Mail/x/cur' mcheck assert $? 0 "check failed" mpull assert $? 0 "mpull failed" test ! -d target/Mail/cur assert $? 0 "mpull pulled excluded stuff 1" test ! -d 'target/Mail/w space' assert $? 0 "mpull pulled excluded stuff 2" syncmaildir-1.2.6.2/tests.d/pull-push/19-root-space000066400000000000000000000003651262303536600217530ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo 'MAILBOX="Ma%20il"' >> target/.smd/config.default mkdir "target/Ma il" mv Mail 'Ma il' mcheck assert $? 0 "check failed" mpull assert $? 0 "mpull failed" test_eq 'Ma il' 'target/Ma il' syncmaildir-1.2.6.2/tests.d/pull-push/20-push-verbose000066400000000000000000000005431262303536600223070ustar00rootroot00000000000000#!/bin/sh mv Mail target/ mpush -t assert $? 0 "failed mpush -t" mpush -v assert $? 0 "failed mpush -v" test_eq Mail target/Mail W=`grep '^Phase.[0-9]:' log.push | wc -l` assert $W 4 "bad log, not 4 Phases" mpush -s assert $? 0 "failed mpush -v" test_eq Mail target/Mail W=`grep '^Phase.[0-9]:' log.push | wc -l` assert $W 0 "bad log, not 0 Phases" syncmaildir-1.2.6.2/tests.d/pull-push/21-pull-verbose000066400000000000000000000005041262303536600223020ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" mpull -v assert $? 0 "failed mpull -v" test_eq Mail target/Mail W=`grep '^Phase.[0-9]:' log.pull | wc -l` assert $W 4 "bad log" mpull -s assert $? 0 "failed mpull -v" test_eq Mail target/Mail W=`grep '^Phase.[0-9]:' log.pull | wc -l` assert $W 0 "bad log, not 0 Phases" syncmaildir-1.2.6.2/tests.d/pull-push/22-local-pull000066400000000000000000000014071262303536600217330ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default echo SERVERNAME=localhost >> target/.smd/config.default echo SMDCLIENTOPTS=-l >> target/.smd/config.default echo MAILBOX= >> target/.smd/config.default echo MAILBOX_LOCAL=MailBK >> target/.smd/config.default echo MAILBOX_REMOTE=Mail >> target/.smd/config.default echo "TRANSLATOR_RL=\"smd-translate -m move -d RL default\"" >> target/.smd/config.default echo "TRANSLATOR_LR=\"smd-translate -m move -d LR default\"" >> target/.smd/config.default mv Mail target/ mcheck mpull assert $? 0 "failed mpull" test_eq target/Mail target/MailBK mpull assert $? 0 "failed 2nd mpull" test_eq target/Mail target/MailBK mpush assert $? 0 "failed mpush" test_eq target/Mail target/MailBK syncmaildir-1.2.6.2/tests.d/pull-push/23-smd-translate-bug000066400000000000000000000056341262303536600232270ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo 'MAILBOX_LOCAL="MyMail/"' >> target/.smd/config.default echo 'MAILBOX_REMOTE="Mail/"' >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m default -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d LR default"' >> target/.smd/config.default echo DEBUG=true >> target/.smd/config.default mkdir -p Mail/.foo.bar cp -r Mail/cur Mail/.foo.bar/ mpull assert $? 0 "failed mpull" test_eq Mail/cur/ target/MyMail/cur/ test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ cp Mail/.foo.bar/cur/`ls Mail/.foo.bar/cur/|head -n 1` Mail/.foo.bar/cur/here sed -i s/a/z/ Mail/.foo.bar/cur/here mpull assert $? 0 "failed mpull" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ mkdir -p Mail/.foo.baz/cur cp Mail/.foo.bar/cur/here Mail/.foo.baz/cur/here mpull assert $? 0 "failed mpull" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ touch target/.smd/workarea/cruft mpull 2> /dev/null assert $? 1 "smd-pull did not detect cruft in workarea/" rm target/.smd/workarea/cruft mpush -v test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur chmod a-rx target/MyMail/foo.bar/ mpush R=$? chmod a+rx target/MyMail/foo.bar/ assert $R 1 "smd-push did not detect a scanning error" mpush test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ test_eq Mail/cur target/MyMail/cur rm -rf target/MyMail target/.smd/*.db.txt* .smd/*.db.txt* mpull -d W=`grep translating log.pull | grep -v ^+ | wc -l` assert $W 9 "smd-pull -d not printing 'translating' lines" mpull rm -rf Mail target/.smd/*.db.txt* .smd/*.db.txt* mpush -d W=`grep translating log.push | grep -v ^+ | wc -l` assert $W 6 "smd-push -d not printing 'translating' lines" mpush assert $? 0 "mpush failed" mkdir -p "target/MyMail/bad name/" mv target/MyMail/cur "target/MyMail/bad name/cur" mpush assert $? 0 "mpush failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/.bad name" "target/MyMail/bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ mv "Mail/.bad name" "Mail/. very bad name" mpull assert $? 0 "mpull failed with space in dirname" test_eq "Mail/. very bad name" "target/MyMail/ very bad name" test_eq Mail/.foo.bar/ target/MyMail/foo.bar/ test_eq Mail/.foo.baz/ target/MyMail/foo.baz/ rm Mail/.foo.bar/cur/`ls Mail/.foo.bar/cur|head -n 1` rm target/MyMail/foo.baz/cur/`ls target/MyMail/foo.baz/cur|head -n 1` echo 'TRANSLATOR_RL="smd-translate -m default -d XX default"' >> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m default -d XX default"' >> target/.smd/config.default mpull assert $? 1 "mpull suceeded with a bad tanslator" mpush assert $? 1 "mpush suceeded with a bad tanslator" syncmaildir-1.2.6.2/tests.d/pull-push/24-local-translator000066400000000000000000000014121262303536600231460ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default echo SERVERNAME=localhost >> target/.smd/config.default echo SMDCLIENTOPTS=-l >> target/.smd/config.default echo MAILBOX= >> target/.smd/config.default echo MAILBOX_LOCAL=Mail >> target/.smd/config.default echo "MAILBOX_REMOTE='MyMail/'" >> target/.smd/config.default echo 'TRANSLATOR_RL="smd-translate -m move -d RL default"'>> target/.smd/config.default echo 'TRANSLATOR_LR="smd-translate -m move -d LR default"' >> target/.smd/config.default mv Mail target/MyMail mpull assert $? 0 "failed mpull" test_eq target/MyMail target/Mail mkdir -p target/Mail/.foo.bar cp -r target/Mail/cur target/Mail/.foo.bar/ mpush assert $? 0 "failed mpush" test_eq target/MyMail target/Mail syncmaildir-1.2.6.2/tests.d/pull-push/25-pull-nodel000066400000000000000000000006231262303536600217440ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default mpull assert $? 0 "failed mpull" test_eq Mail target/Mail cp -r target/Mail target/Mail.bak rm Mail/cur/`ls Mail/cur|head -n1` mpull -n assert $? 0 "failed mpull after deletion" test_eq target/Mail target/Mail.bak mpull -n assert $? 0 "failed mpull after deletion" test_eq target/Mail target/Mail.bak syncmaildir-1.2.6.2/tests.d/pull-push/26-check-dot-dot000066400000000000000000000003111262303536600223110ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" sed -i 's/MAILBOX=.*//' target/.smd/config.default echo MAILBOX=../Mail >> target/.smd/config.default mcheck assert $? 1 "mcheck does not detect .." syncmaildir-1.2.6.2/tests.d/pull-push/27-eamp-in-dirname000066400000000000000000000003221262303536600226320ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default mkdir 'Mail/foo & bar' mv Mail/cur 'Mail/foo & bar/' mpull assert $? 0 "failed mpull" test_eq Mail target/Mail syncmaildir-1.2.6.2/tests.d/pull-push/28-eamp-in-exclude000066400000000000000000000007611262303536600226540ustar00rootroot00000000000000#!/bin/sh mpull -t assert $? 0 "failed mpull -t" echo DEBUG=true >> target/.smd/config.default echo 'EXCLUDE_REMOTE="Mail/foo%20%26%20bar"' >> target/.smd/config.default mkdir -p 'Mail/foo & bar/' cp -r Mail/cur 'Mail/foo & bar/' mpull assert $? 0 "failed mpull" if [ -d 'target/Mail/foo & bar' ]; then assert 1 0 "EXCLUDE_REMOTE ignored" fi mpush assert $? 0 "failed mpush" mpull assert $? 0 "failed mpull2" if [ -d 'target/Mail/foo & bar' ]; then assert 1 0 "EXCLUDE_REMOTE ignored" fi syncmaildir-1.2.6.2/tests.d/test.sh000077500000000000000000000005151262303536600171070ustar00rootroot00000000000000#!/bin/bash # set -x SUITES=${SUITES:-"mddiff client-server pull-push benchmarks migration"} BASE=$PWD . $BASE/tests.d/common for TESTSUITE in $SUITES; do run_tests $@ done for TESTSUITE in $SUITES; do if [ -x $BASE/tests.d/$TESTSUITE/summarize.sh -a -z "$2" ]; then BASE=$BASE $BASE/tests.d/$TESTSUITE/summarize.sh fi done syncmaildir-1.2.6.2/xdg/000077500000000000000000000000001262303536600147665ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/GConf/000077500000000000000000000000001262303536600157625ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/GConf/gsettings/000077500000000000000000000000001262303536600177715ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/GConf/gsettings/smd-applet.convert000066400000000000000000000002071262303536600234400ustar00rootroot00000000000000[org.syncmaildir.applet] icon-only-on-errors = /apps/smd-applet/icon_only_on_errors notify-new-mail = /apps/smd-applet/notify_new_mail syncmaildir-1.2.6.2/xdg/applications/000077500000000000000000000000001262303536600174545ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/applications/smd-applet.desktop000066400000000000000000000004401262303536600231130ustar00rootroot00000000000000[Desktop Entry] Name=Sync Mail Dir Name[it]=Sincronizzazione Maildir Comment=Synchronizes mailboxes in Maildir format Comment[it]=Sincronizza due caselle di posta in formato Maildir Icon=mail-send-receive Exec=smd-applet Terminal=false Type=Application Categories=Network;GTK;GNOME;Email syncmaildir-1.2.6.2/xdg/glib-2.0/000077500000000000000000000000001262303536600162005ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/glib-2.0/schemas/000077500000000000000000000000001262303536600176235ustar00rootroot00000000000000syncmaildir-1.2.6.2/xdg/glib-2.0/schemas/org.syncmaildir.applet.gschema.xml000066400000000000000000000012201262303536600263360ustar00rootroot00000000000000 false Choose when the icon is shown in the notification area If set, this option makes the icon show up only in case an error occurs false Choose when a notification is produced If set, this option makes smd-applet produce a new notification every time new emails are received syncmaildir-1.2.6.2/smd-applet.c0000644000000000000000000054052412617176541016432 0ustar00rootroot00000000000000/* smd-applet.c generated by valac 0.30.0, the Vala compiler * generated from smd-applet.vala, do not modify */ /* Released under the terms of GPLv3 or at your option any later version.*/ /* No warranties.*/ /* Copyright Enrico Tassi */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smd-config.h" #include #define TYPE_NETWORK_MANAGER (network_manager_get_type ()) #define NETWORK_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NETWORK_MANAGER, NetworkManager)) #define IS_NETWORK_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NETWORK_MANAGER)) #define NETWORK_MANAGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TYPE_NETWORK_MANAGER, NetworkManagerIface)) typedef struct _NetworkManager NetworkManager; typedef struct _NetworkManagerIface NetworkManagerIface; #define TYPE_NETWORK_MANAGER_PROXY (network_manager_proxy_get_type ()) typedef GDBusProxy NetworkManagerProxy; typedef GDBusProxyClass NetworkManagerProxyClass; #define TYPE_EVENT (event_get_type ()) #define EVENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_EVENT, Event)) #define EVENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_EVENT, EventClass)) #define IS_EVENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_EVENT)) #define IS_EVENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_EVENT)) #define EVENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_EVENT, EventClass)) typedef struct _Event Event; typedef struct _EventClass EventClass; typedef struct _EventPrivate EventPrivate; #define _g_free0(var) (var = (g_free (var), NULL)) #define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) #define _event_unref0(var) ((var == NULL) ? NULL : (var = (event_unref (var), NULL))) typedef struct _ParamSpecEvent ParamSpecEvent; #define TYPE_SMD_APPLET (smd_applet_get_type ()) #define SMD_APPLET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SMD_APPLET, smdApplet)) #define SMD_APPLET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SMD_APPLET, smdAppletClass)) #define IS_SMD_APPLET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SMD_APPLET)) #define IS_SMD_APPLET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_SMD_APPLET)) #define SMD_APPLET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SMD_APPLET, smdAppletClass)) typedef struct _smdApplet smdApplet; typedef struct _smdAppletClass smdAppletClass; typedef struct _smdAppletPrivate smdAppletPrivate; #define _g_thread_unref0(var) ((var == NULL) ? NULL : (var = (g_thread_unref (var), NULL))) #define _g_hash_table_unref0(var) ((var == NULL) ? NULL : (var = (g_hash_table_unref (var), NULL))) typedef struct _Block1Data Block1Data; #define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL))) #define __g_list_free__g_free0_0(var) ((var == NULL) ? NULL : (var = (_g_list_free__g_free0_ (var), NULL))) #define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL))) #define _g_match_info_unref0(var) ((var == NULL) ? NULL : (var = (g_match_info_unref (var), NULL))) #define _fclose0(var) ((var == NULL) ? NULL : (var = (fclose (var), NULL))) #define _g_variant_unref0(var) ((var == NULL) ? NULL : (var = (g_variant_unref (var), NULL))) #define _g_list_free0(var) ((var == NULL) ? NULL : (var = (g_list_free (var), NULL))) #define _g_dir_close0(var) ((var == NULL) ? NULL : (var = (g_dir_close (var), NULL))) #define _g_option_context_free0(var) ((var == NULL) ? NULL : (var = (g_option_context_free (var), NULL))) typedef enum { EXIT_ABORT } Exit; #define EXIT exit_quark () struct _NetworkManagerIface { GTypeInterface parent_iface; guint (*get_state) (NetworkManager* self); }; struct _Event { GTypeInstance parent_instance; volatile int ref_count; EventPrivate * priv; gchar* message; gchar* message_icon; gboolean enter_network_error_mode; gboolean enter_error_mode; gboolean transient_error_message; gchar* context; gchar* cause; gchar* permissions; gchar* mail_name; gchar* mail_body; GeeArrayList* commands; }; struct _EventClass { GTypeClass parent_class; void (*finalize) (Event *self); }; struct _ParamSpecEvent { GParamSpec parent_instance; }; struct _smdApplet { GtkApplication parent_instance; smdAppletPrivate * priv; }; struct _smdAppletClass { GtkApplicationClass parent_class; }; struct _smdAppletPrivate { GtkBuilder* builder; GtkStatusIcon* si; GtkWindow* win; GtkWindow* err_win; GtkNotebook* notebook; GtkSwitch* sync_active; GtkComboBoxText* cblogs; GeeArrayList* lognames; GSettings* settings; GThread* thread; gboolean thread_die; GPid pid; GMutex events_lock; GeeArrayList* events; gboolean error_mode; gboolean network_error_mode; gboolean config_wait_mode; GHashTable* command_hash; NetworkManager* net_manager; NotifyNotification* notification; gboolean notification_server_has_persistence; }; struct _Block1Data { int _ref_count_; smdApplet* self; GtkSwitch* bnotify; GtkSwitch* bautostart; }; extern gboolean verbose; gboolean verbose = FALSE; static gpointer event_parent_class = NULL; extern gchar* SMD_LOGS_DIR; gchar* SMD_LOGS_DIR = NULL; extern gchar* SMD_LOOP_CFG; gchar* SMD_LOOP_CFG = NULL; extern gchar* SMD_PP_DEF_CFG; gchar* SMD_PP_DEF_CFG = NULL; extern gchar* XDG_AUTORUN_FILE; gchar* XDG_AUTORUN_FILE = NULL; static gpointer smd_applet_parent_class = NULL; extern gchar* smd_applet_smd_loop_cmd; gchar* smd_applet_smd_loop_cmd = NULL; extern gchar* smd_applet_smd_applet_ui; gchar* smd_applet_smd_applet_ui = NULL; extern gchar* smd_applet_smd_push_cmd; gchar* smd_applet_smd_push_cmd = NULL; extern gchar* smd_applet_smd_applet_desktop; gchar* smd_applet_smd_applet_desktop = NULL; static gchar* smd_applet_menu_ui; static gchar* smd_applet_menu_ui = NULL; GQuark exit_quark (void); void debug (const gchar* message); GType network_manager_get_type (void) G_GNUC_CONST; GType network_manager_proxy_get_type (void) G_GNUC_CONST; guint network_manager_register_object (void* object, GDBusConnection* connection, const gchar* path, GError** error); guint network_manager_get_state (NetworkManager* self); static void network_manager_proxy_g_signal (GDBusProxy* proxy, const gchar* sender_name, const gchar* signal_name, GVariant* parameters); static void _dbus_handle_network_manager_state_changed (NetworkManager* self, GVariant* parameters); static guint network_manager_dbus_proxy_get_state (NetworkManager* self); static void network_manager_proxy_network_manager_interface_init (NetworkManagerIface* iface); static void network_manager_dbus_interface_method_call (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* method_name, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer user_data); static GVariant* network_manager_dbus_interface_get_property (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GError** error, gpointer user_data); static GVariant* _dbus_network_manager_get_state (NetworkManager* self); static gboolean network_manager_dbus_interface_set_property (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GVariant* value, GError** error, gpointer user_data); static void _dbus_network_manager_state_changed (GObject* _sender, guint state, gpointer* _data); static void _network_manager_unregister_object (gpointer user_data); #define NM_SERVICE "org.freedesktop.NetworkManager" #define NM_PATH "/org/freedesktop/NetworkManager" gboolean is_nm_connected (guint code); gpointer event_ref (gpointer instance); void event_unref (gpointer instance); GParamSpec* param_spec_event (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags); void value_set_event (GValue* value, gpointer v_object); void value_take_event (GValue* value, gpointer v_object); gpointer value_get_event (const GValue* value); GType event_get_type (void) G_GNUC_CONST; enum { EVENT_DUMMY_PROPERTY }; Event* event_error (const gchar* account, const gchar* host, const gchar* context, const gchar* cause, const gchar* permissions, const gchar* mail_name, const gchar* mail_body, GeeArrayList* commands); Event* event_new (void); Event* event_construct (GType object_type); Event* event_generic_error (const gchar* cause); Event* event_network_error (void); Event* event_stats (const gchar* account, const gchar* host, gint new_mails, gint del_mails); gboolean event_is_error_event (Event* self); static void event_finalize (Event* obj); #define SMD_LOOP "/bin/smd-loop" #define SMD_PUSH "/bin/smd-push" #define SMD_APPLET_UI "/share/syncmaildir-applet/smd-applet.ui" #define SMD_APPLET_DESKTOP "/share/applications/smd-applet.desktop" #define GNOME_AUTOSTART_DISABLED "X-GNOME-Autostart-enabled=false" GType smd_applet_get_type (void) G_GNUC_CONST; #define SMD_APPLET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_SMD_APPLET, smdAppletPrivate)) enum { SMD_APPLET_DUMMY_PROPERTY }; #define SMD_APPLET_key_newmail "notify-new-mail" smdApplet* smd_applet_new (GError** error); smdApplet* smd_applet_construct (GType object_type, GError** error); static Block1Data* block1_data_ref (Block1Data* _data1_); static void block1_data_unref (void * _userdata_); static void ___lambda4_ (smdApplet* self, guint s); static void ____lambda4__network_manager_state_changed (NetworkManager* _sender, guint state, gpointer self); static void smd_applet_update_logcontents (smdApplet* self); static void __lambda5_ (Block1Data* _data1_); static void ___lambda5__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self); static void __lambda6_ (Block1Data* _data1_); static void ___lambda6__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self); static void smd_applet_close_err_action (smdApplet* self, GtkButton* b); static void _smd_applet_close_err_action_gtk_button_clicked (GtkButton* _sender, gpointer self); static void __lambda8_ (smdApplet* self, GtkButton* b); static gboolean smd_applet_is_smd_loop_configured (smdApplet* self); static void ___lambda8__gtk_button_clicked (GtkButton* _sender, gpointer self); static void __lambda9_ (smdApplet* self, GtkButton* b); static gboolean smd_applet_is_smd_pushpull_configured (smdApplet* self); static void ___lambda9__gtk_button_clicked (GtkButton* _sender, gpointer self); static void __lambda10_ (smdApplet* self); static void smd_applet_unpause (smdApplet* self); static void smd_applet_pause (smdApplet* self); static void ___lambda10__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self); static void smd_applet_update_loglist (smdApplet* self); static gboolean __lambda11_ (smdApplet* self); static gboolean ___lambda11__gsource_func (gpointer self); static void __lambda12_ (smdApplet* self, GtkStatusIcon* s); static void ___lambda12__gtk_status_icon_activate (GtkStatusIcon* _sender, gpointer self); static void __lambda13_ (smdApplet* self); static void ___lambda13__g_application_activate (GApplication* _sender, gpointer self); static void __lambda14_ (smdApplet* self); static void ___lambda14__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self); static void _g_object_unref0_ (gpointer var); static void _g_free0_ (gpointer var); static void _g_list_free__g_free0_ (GList* self); static void* smd_applet_smdThread (smdApplet* self); static gboolean smd_applet_run_smd_loop (smdApplet* self, GError** error); static void smd_applet_start_smdThread (smdApplet* self, gboolean force); static gpointer _smd_applet_smdThread_gthread_func (gpointer self); static gboolean smd_applet_eval_smd_loop_error_message (smdApplet* self, const gchar* args, const gchar* account, const gchar* host, GError** error); static gboolean smd_applet_eval_smd_loop_message (smdApplet* self, const gchar* s); static void ___lambda7_ (smdApplet* self); static void ____lambda7__gspawn_child_setup_func (gpointer self); static gboolean smd_applet_eat_event (smdApplet* self); static void ______lambda15_ (smdApplet* self, NotifyNotification* not, const gchar* action); static void _______lambda15__notify_action_callback (NotifyNotification* notification, const gchar* action, gpointer self); static void ______lambda16_ (smdApplet* self, GtkButton* b); static void _______lambda16__gtk_button_clicked (GtkButton* _sender, gpointer self); static void smd_applet_reset_to_regular_run (smdApplet* self, gboolean force); static gboolean smd_applet_close_err_event (smdApplet* self, GdkEventAny* e); static gboolean smd_applet_close_prefs_event (smdApplet* self, GdkEventAny* e); static void smd_applet_close_prefs (smdApplet* self); static gboolean smd_applet_is_smd_stack_configured (smdApplet* self); void smd_applet_start (smdApplet* self, GError** error); static gboolean _smd_applet_eat_event_gsource_func (gpointer self); static gboolean _smd_applet_close_prefs_event_gtk_widget_delete_event (GtkWidget* _sender, GdkEventAny* event, gpointer self); static gboolean _smd_applet_close_err_event_gtk_widget_delete_event (GtkWidget* _sender, GdkEventAny* event, gpointer self); static void ___lambda17_ (smdApplet* self, NotifyNotification* n, const gchar* a); static void ____lambda17__notify_action_callback (NotifyNotification* notification, const gchar* action, gpointer self); static void smd_applet_finalize (GObject* obj); gint _vala_main (gchar** args, int args_length1); static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func); static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func); static void _vala_clear_GMutex (GMutex * mutex); static void _vala_clear_GRecMutex (GRecMutex * mutex); static void _vala_clear_GRWLock (GRWLock * mutex); static void _vala_clear_GCond (GCond * mutex); static const GDBusMethodInfo * const _network_manager_dbus_method_info[] = {NULL}; static const GDBusArgInfo _network_manager_dbus_arg_info_state_changed_state = {-1, "state", "u"}; static const GDBusArgInfo * const _network_manager_dbus_arg_info_state_changed[] = {&_network_manager_dbus_arg_info_state_changed_state, NULL}; static const GDBusSignalInfo _network_manager_dbus_signal_info_state_changed = {-1, "StateChanged", (GDBusArgInfo **) (&_network_manager_dbus_arg_info_state_changed)}; static const GDBusSignalInfo * const _network_manager_dbus_signal_info[] = {&_network_manager_dbus_signal_info_state_changed, NULL}; static const GDBusPropertyInfo _network_manager_dbus_property_info_state = {-1, "State", "u", G_DBUS_PROPERTY_INFO_FLAGS_READABLE}; static const GDBusPropertyInfo * const _network_manager_dbus_property_info[] = {&_network_manager_dbus_property_info_state, NULL}; static const GDBusInterfaceInfo _network_manager_dbus_interface_info = {-1, "org.freedesktop.NetworkManager", (GDBusMethodInfo **) (&_network_manager_dbus_method_info), (GDBusSignalInfo **) (&_network_manager_dbus_signal_info), (GDBusPropertyInfo **) (&_network_manager_dbus_property_info)}; static const GDBusInterfaceVTable _network_manager_dbus_interface_vtable = {network_manager_dbus_interface_method_call, network_manager_dbus_interface_get_property, network_manager_dbus_interface_set_property}; GQuark exit_quark (void) { return g_quark_from_static_string ("exit-quark"); } void debug (const gchar* message) { gboolean _tmp0_ = FALSE; g_return_if_fail (message != NULL); _tmp0_ = verbose; if (_tmp0_) { FILE* _tmp1_ = NULL; const gchar* _tmp2_ = NULL; _tmp1_ = stderr; _tmp2_ = message; fprintf (_tmp1_, "DEBUG: %s\n", _tmp2_); } } guint network_manager_get_state (NetworkManager* self) { g_return_val_if_fail (self != NULL, 0U); return NETWORK_MANAGER_GET_INTERFACE (self)->get_state (self); } static void network_manager_base_init (NetworkManagerIface * iface) { static gboolean initialized = FALSE; if (!initialized) { initialized = TRUE; g_signal_new ("state_changed", TYPE_NETWORK_MANAGER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); } } GType network_manager_get_type (void) { static volatile gsize network_manager_type_id__volatile = 0; if (g_once_init_enter (&network_manager_type_id__volatile)) { static const GTypeInfo g_define_type_info = { sizeof (NetworkManagerIface), (GBaseInitFunc) network_manager_base_init, (GBaseFinalizeFunc) NULL, (GClassInitFunc) NULL, (GClassFinalizeFunc) NULL, NULL, 0, 0, (GInstanceInitFunc) NULL, NULL }; GType network_manager_type_id; network_manager_type_id = g_type_register_static (G_TYPE_INTERFACE, "NetworkManager", &g_define_type_info, 0); g_type_interface_add_prerequisite (network_manager_type_id, G_TYPE_OBJECT); g_type_set_qdata (network_manager_type_id, g_quark_from_static_string ("vala-dbus-proxy-type"), (void*) network_manager_proxy_get_type); g_type_set_qdata (network_manager_type_id, g_quark_from_static_string ("vala-dbus-interface-name"), "org.freedesktop.NetworkManager"); g_type_set_qdata (network_manager_type_id, g_quark_from_static_string ("vala-dbus-interface-info"), (void*) (&_network_manager_dbus_interface_info)); g_type_set_qdata (network_manager_type_id, g_quark_from_static_string ("vala-dbus-register-object"), (void*) network_manager_register_object); g_once_init_leave (&network_manager_type_id__volatile, network_manager_type_id); } return network_manager_type_id__volatile; } G_DEFINE_TYPE_EXTENDED (NetworkManagerProxy, network_manager_proxy, G_TYPE_DBUS_PROXY, 0, G_IMPLEMENT_INTERFACE (TYPE_NETWORK_MANAGER, network_manager_proxy_network_manager_interface_init) ) static void network_manager_proxy_class_init (NetworkManagerProxyClass* klass) { G_DBUS_PROXY_CLASS (klass)->g_signal = network_manager_proxy_g_signal; } static void _dbus_handle_network_manager_state_changed (NetworkManager* self, GVariant* parameters) { GVariantIter _arguments_iter; guint state = 0U; GVariant* _tmp0_; g_variant_iter_init (&_arguments_iter, parameters); _tmp0_ = g_variant_iter_next_value (&_arguments_iter); state = g_variant_get_uint32 (_tmp0_); g_variant_unref (_tmp0_); g_signal_emit_by_name (self, "state-changed", state); } static void network_manager_proxy_g_signal (GDBusProxy* proxy, const gchar* sender_name, const gchar* signal_name, GVariant* parameters) { if (strcmp (signal_name, "StateChanged") == 0) { _dbus_handle_network_manager_state_changed ((NetworkManager*) proxy, parameters); } } static void network_manager_proxy_init (NetworkManagerProxy* self) { } static guint network_manager_dbus_proxy_get_state (NetworkManager* self) { GVariant *_inner_reply; guint _result; _inner_reply = g_dbus_proxy_get_cached_property ((GDBusProxy *) self, "State"); if (!_inner_reply) { GVariant *_arguments; GVariant *_reply; GVariantBuilder _arguments_builder; g_variant_builder_init (&_arguments_builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value (&_arguments_builder, g_variant_new_string ("org.freedesktop.NetworkManager")); g_variant_builder_add_value (&_arguments_builder, g_variant_new_string ("State")); _arguments = g_variant_builder_end (&_arguments_builder); _reply = g_dbus_proxy_call_sync ((GDBusProxy *) self, "org.freedesktop.DBus.Properties.Get", _arguments, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); if (!_reply) { return 0U; } g_variant_get (_reply, "(v)", &_inner_reply); g_variant_unref (_reply); } _result = g_variant_get_uint32 (_inner_reply); g_variant_unref (_inner_reply); return _result; } static void network_manager_proxy_network_manager_interface_init (NetworkManagerIface* iface) { iface->get_state = network_manager_dbus_proxy_get_state; } static void network_manager_dbus_interface_method_call (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* method_name, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer user_data) { gpointer* data; gpointer object; data = user_data; object = data[0]; g_object_unref (invocation); } static GVariant* _dbus_network_manager_get_state (NetworkManager* self) { guint result; GVariant* _reply; result = network_manager_get_state (self); _reply = g_variant_new_uint32 (result); return _reply; } static GVariant* network_manager_dbus_interface_get_property (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GError** error, gpointer user_data) { gpointer* data; gpointer object; data = user_data; object = data[0]; if (strcmp (property_name, "State") == 0) { return _dbus_network_manager_get_state (object); } return NULL; } static gboolean network_manager_dbus_interface_set_property (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GVariant* value, GError** error, gpointer user_data) { gpointer* data; gpointer object; data = user_data; object = data[0]; return FALSE; } static void _dbus_network_manager_state_changed (GObject* _sender, guint state, gpointer* _data) { GDBusConnection * _connection; const gchar * _path; GVariant *_arguments; GVariantBuilder _arguments_builder; _connection = _data[1]; _path = _data[2]; g_variant_builder_init (&_arguments_builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value (&_arguments_builder, g_variant_new_uint32 (state)); _arguments = g_variant_builder_end (&_arguments_builder); g_dbus_connection_emit_signal (_connection, NULL, _path, "org.freedesktop.NetworkManager", "StateChanged", _arguments, NULL); } guint network_manager_register_object (gpointer object, GDBusConnection* connection, const gchar* path, GError** error) { guint result; gpointer *data; data = g_new (gpointer, 3); data[0] = g_object_ref (object); data[1] = g_object_ref (connection); data[2] = g_strdup (path); result = g_dbus_connection_register_object (connection, path, (GDBusInterfaceInfo *) (&_network_manager_dbus_interface_info), &_network_manager_dbus_interface_vtable, data, _network_manager_unregister_object, error); if (!result) { return 0; } g_signal_connect (object, "state-changed", (GCallback) _dbus_network_manager_state_changed, data); return result; } static void _network_manager_unregister_object (gpointer user_data) { gpointer* data; data = user_data; g_signal_handlers_disconnect_by_func (data[0], _dbus_network_manager_state_changed, data); g_object_unref (data[0]); g_object_unref (data[1]); g_free (data[2]); g_free (data); } gboolean is_nm_connected (guint code) { gboolean result = FALSE; gboolean _tmp0_ = FALSE; guint _tmp1_ = 0U; _tmp1_ = code; if (_tmp1_ == ((guint) 3)) { _tmp0_ = TRUE; } else { guint _tmp2_ = 0U; _tmp2_ = code; _tmp0_ = _tmp2_ == ((guint) 70); } result = _tmp0_; return result; } static gpointer _g_object_ref0 (gpointer self) { return self ? g_object_ref (self) : NULL; } Event* event_error (const gchar* account, const gchar* host, const gchar* context, const gchar* cause, const gchar* permissions, const gchar* mail_name, const gchar* mail_body, GeeArrayList* commands) { Event* result = NULL; Event* e = NULL; Event* _tmp0_ = NULL; gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; const gchar* _tmp3_ = NULL; gchar* _tmp4_ = NULL; const gchar* _tmp5_ = NULL; gchar* _tmp6_ = NULL; const gchar* _tmp7_ = NULL; gchar* _tmp8_ = NULL; const gchar* _tmp9_ = NULL; gchar* _tmp10_ = NULL; const gchar* _tmp11_ = NULL; gchar* _tmp12_ = NULL; GeeArrayList* _tmp13_ = NULL; GeeArrayList* _tmp14_ = NULL; g_return_val_if_fail (account != NULL, NULL); g_return_val_if_fail (host != NULL, NULL); g_return_val_if_fail (context != NULL, NULL); g_return_val_if_fail (cause != NULL, NULL); g_return_val_if_fail (commands != NULL, NULL); _tmp0_ = event_new (); e = _tmp0_; _tmp1_ = g_strdup ("An error occurred, click on the icon for more details"); _g_free0 (e->message); e->message = _tmp1_; _tmp2_ = g_strdup ("error"); _g_free0 (e->message_icon); e->message_icon = _tmp2_; e->enter_error_mode = TRUE; _tmp3_ = cause; _tmp4_ = g_strdup (_tmp3_); _g_free0 (e->cause); e->cause = _tmp4_; _tmp5_ = context; _tmp6_ = g_strdup (_tmp5_); _g_free0 (e->context); e->context = _tmp6_; _tmp7_ = permissions; _tmp8_ = g_strdup (_tmp7_); _g_free0 (e->permissions); e->permissions = _tmp8_; _tmp9_ = mail_name; _tmp10_ = g_strdup (_tmp9_); _g_free0 (e->mail_name); e->mail_name = _tmp10_; _tmp11_ = mail_body; _tmp12_ = g_strdup (_tmp11_); _g_free0 (e->mail_body); e->mail_body = _tmp12_; _tmp13_ = commands; _tmp14_ = _g_object_ref0 (_tmp13_); _g_object_unref0 (e->commands); e->commands = _tmp14_; result = e; return result; } Event* event_generic_error (const gchar* cause) { Event* result = NULL; Event* e = NULL; Event* _tmp0_ = NULL; const gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; g_return_val_if_fail (cause != NULL, NULL); _tmp0_ = event_new (); e = _tmp0_; _tmp1_ = cause; _tmp2_ = g_strconcat ("A failure occurred: ", _tmp1_, NULL); _g_free0 (e->message); e->message = _tmp2_; _tmp3_ = g_strdup ("dialog-warning"); _g_free0 (e->message_icon); e->message_icon = _tmp3_; e->transient_error_message = TRUE; result = e; return result; } Event* event_network_error (void) { Event* result = NULL; Event* e = NULL; Event* _tmp0_ = NULL; gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; _tmp0_ = event_new (); e = _tmp0_; _tmp1_ = g_strdup ("A persistent network failure occurred"); _g_free0 (e->message); e->message = _tmp1_; _tmp2_ = g_strdup ("dialog-warning"); _g_free0 (e->message_icon); e->message_icon = _tmp2_; e->enter_network_error_mode = TRUE; result = e; return result; } Event* event_stats (const gchar* account, const gchar* host, gint new_mails, gint del_mails) { Event* result = NULL; gchar* preamble = NULL; const gchar* _tmp0_ = NULL; gchar* _tmp1_ = NULL; Event* e = NULL; Event* _tmp2_ = NULL; gboolean _tmp3_ = FALSE; gint _tmp4_ = 0; g_return_val_if_fail (account != NULL, NULL); g_return_val_if_fail (host != NULL, NULL); _tmp0_ = account; _tmp1_ = g_strdup_printf ("Synchronize with %s:\n", _tmp0_); preamble = _tmp1_; _tmp2_ = event_new (); e = _tmp2_; _tmp4_ = new_mails; if (_tmp4_ > 0) { gint _tmp5_ = 0; _tmp5_ = del_mails; _tmp3_ = _tmp5_ > 0; } else { _tmp3_ = FALSE; } if (_tmp3_) { Event* _tmp6_ = NULL; const gchar* _tmp7_ = NULL; gint _tmp8_ = 0; gint _tmp9_ = 0; gchar* _tmp10_ = NULL; _tmp6_ = e; _tmp7_ = preamble; _tmp8_ = new_mails; _tmp9_ = del_mails; _tmp10_ = g_strdup_printf ("%s%d new messages\n%d deleted messages", _tmp7_, _tmp8_, _tmp9_); _g_free0 (_tmp6_->message); _tmp6_->message = _tmp10_; } else { gint _tmp11_ = 0; _tmp11_ = new_mails; if (_tmp11_ > 0) { Event* _tmp12_ = NULL; const gchar* _tmp13_ = NULL; gint _tmp14_ = 0; gchar* _tmp15_ = NULL; _tmp12_ = e; _tmp13_ = preamble; _tmp14_ = new_mails; _tmp15_ = g_strdup_printf ("%s%d new messages", _tmp13_, _tmp14_); _g_free0 (_tmp12_->message); _tmp12_->message = _tmp15_; } else { Event* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; gint _tmp18_ = 0; gchar* _tmp19_ = NULL; _tmp16_ = e; _tmp17_ = preamble; _tmp18_ = del_mails; _tmp19_ = g_strdup_printf ("%s%d deleted messages", _tmp17_, _tmp18_); _g_free0 (_tmp16_->message); _tmp16_->message = _tmp19_; } } result = e; _g_free0 (preamble); return result; } gboolean event_is_error_event (Event* self) { gboolean result = FALSE; gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; g_return_val_if_fail (self != NULL, FALSE); _tmp2_ = self->enter_error_mode; if (_tmp2_) { _tmp1_ = TRUE; } else { gboolean _tmp3_ = FALSE; _tmp3_ = self->enter_network_error_mode; _tmp1_ = _tmp3_; } if (_tmp1_) { _tmp0_ = TRUE; } else { gboolean _tmp4_ = FALSE; _tmp4_ = self->transient_error_message; _tmp0_ = _tmp4_; } result = _tmp0_; return result; } Event* event_construct (GType object_type) { Event* self = NULL; self = (Event*) g_type_create_instance (object_type); return self; } Event* event_new (void) { return event_construct (TYPE_EVENT); } static void value_event_init (GValue* value) { value->data[0].v_pointer = NULL; } static void value_event_free_value (GValue* value) { if (value->data[0].v_pointer) { event_unref (value->data[0].v_pointer); } } static void value_event_copy_value (const GValue* src_value, GValue* dest_value) { if (src_value->data[0].v_pointer) { dest_value->data[0].v_pointer = event_ref (src_value->data[0].v_pointer); } else { dest_value->data[0].v_pointer = NULL; } } static gpointer value_event_peek_pointer (const GValue* value) { return value->data[0].v_pointer; } static gchar* value_event_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) { if (collect_values[0].v_pointer) { Event* object; object = collect_values[0].v_pointer; if (object->parent_instance.g_class == NULL) { return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); } else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) { return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); } value->data[0].v_pointer = event_ref (object); } else { value->data[0].v_pointer = NULL; } return NULL; } static gchar* value_event_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) { Event** object_p; object_p = collect_values[0].v_pointer; if (!object_p) { return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); } if (!value->data[0].v_pointer) { *object_p = NULL; } else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) { *object_p = value->data[0].v_pointer; } else { *object_p = event_ref (value->data[0].v_pointer); } return NULL; } GParamSpec* param_spec_event (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags) { ParamSpecEvent* spec; g_return_val_if_fail (g_type_is_a (object_type, TYPE_EVENT), NULL); spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags); G_PARAM_SPEC (spec)->value_type = object_type; return G_PARAM_SPEC (spec); } gpointer value_get_event (const GValue* value) { g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_EVENT), NULL); return value->data[0].v_pointer; } void value_set_event (GValue* value, gpointer v_object) { Event* old; g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_EVENT)); old = value->data[0].v_pointer; if (v_object) { g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_EVENT)); g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); value->data[0].v_pointer = v_object; event_ref (value->data[0].v_pointer); } else { value->data[0].v_pointer = NULL; } if (old) { event_unref (old); } } void value_take_event (GValue* value, gpointer v_object) { Event* old; g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_EVENT)); old = value->data[0].v_pointer; if (v_object) { g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_EVENT)); g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); value->data[0].v_pointer = v_object; } else { value->data[0].v_pointer = NULL; } if (old) { event_unref (old); } } static void event_class_init (EventClass * klass) { event_parent_class = g_type_class_peek_parent (klass); ((EventClass *) klass)->finalize = event_finalize; } static void event_instance_init (Event * self) { gchar* _tmp0_ = NULL; self->message = NULL; _tmp0_ = g_strdup ("gtk-about"); self->message_icon = _tmp0_; self->enter_network_error_mode = FALSE; self->enter_error_mode = FALSE; self->transient_error_message = FALSE; self->context = NULL; self->cause = NULL; self->permissions = NULL; self->mail_name = NULL; self->mail_body = NULL; self->commands = NULL; self->ref_count = 1; } static void event_finalize (Event* obj) { Event * self; self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_EVENT, Event); g_signal_handlers_destroy (self); _g_free0 (self->message); _g_free0 (self->message_icon); _g_free0 (self->context); _g_free0 (self->cause); _g_free0 (self->permissions); _g_free0 (self->mail_name); _g_free0 (self->mail_body); _g_object_unref0 (self->commands); } GType event_get_type (void) { static volatile gsize event_type_id__volatile = 0; if (g_once_init_enter (&event_type_id__volatile)) { static const GTypeValueTable g_define_type_value_table = { value_event_init, value_event_free_value, value_event_copy_value, value_event_peek_pointer, "p", value_event_collect_value, "p", value_event_lcopy_value }; static const GTypeInfo g_define_type_info = { sizeof (EventClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) event_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Event), 0, (GInstanceInitFunc) event_instance_init, &g_define_type_value_table }; static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) }; GType event_type_id; event_type_id = g_type_register_fundamental (g_type_fundamental_next (), "Event", &g_define_type_info, &g_define_type_fundamental_info, 0); g_once_init_leave (&event_type_id__volatile, event_type_id); } return event_type_id__volatile; } gpointer event_ref (gpointer instance) { Event* self; self = instance; g_atomic_int_inc (&self->ref_count); return instance; } void event_unref (gpointer instance) { Event* self; self = instance; if (g_atomic_int_dec_and_test (&self->ref_count)) { EVENT_GET_CLASS (self)->finalize (self); g_type_free_instance ((GTypeInstance *) self); } } static Block1Data* block1_data_ref (Block1Data* _data1_) { g_atomic_int_inc (&_data1_->_ref_count_); return _data1_; } static void block1_data_unref (void * _userdata_) { Block1Data* _data1_; _data1_ = (Block1Data*) _userdata_; if (g_atomic_int_dec_and_test (&_data1_->_ref_count_)) { smdApplet* self; self = _data1_->self; _g_object_unref0 (_data1_->bautostart); _g_object_unref0 (_data1_->bnotify); _g_object_unref0 (self); g_slice_free (Block1Data, _data1_); } } static void ___lambda4_ (smdApplet* self, guint s) { guint _tmp0_ = 0U; gboolean _tmp1_ = FALSE; _tmp0_ = s; _tmp1_ = is_nm_connected (_tmp0_); if (_tmp1_) { GtkSwitch* _tmp2_ = NULL; _tmp2_ = self->priv->sync_active; gtk_switch_set_active (_tmp2_, TRUE); } else { GtkSwitch* _tmp3_ = NULL; _tmp3_ = self->priv->sync_active; gtk_switch_set_active (_tmp3_, FALSE); } } static void ____lambda4__network_manager_state_changed (NetworkManager* _sender, guint state, gpointer self) { ___lambda4_ ((smdApplet*) self, state); } static void __lambda5_ (Block1Data* _data1_) { smdApplet* self; GSettings* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; self = _data1_->self; _tmp0_ = self->priv->settings; _tmp1_ = gtk_switch_get_active (_data1_->bnotify); _tmp2_ = _tmp1_; g_settings_set_boolean (_tmp0_, SMD_APPLET_key_newmail, _tmp2_); } static void ___lambda5__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self) { __lambda5_ (self); } static void __lambda6_ (Block1Data* _data1_) { smdApplet* self; gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; GError * _inner_error_ = NULL; self = _data1_->self; _tmp0_ = gtk_switch_get_active (_data1_->bautostart); _tmp1_ = _tmp0_; if (_tmp1_) { gchar* content = NULL; { const gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; const gchar* _tmp4_ = NULL; const gchar* _tmp5_ = NULL; _tmp2_ = smd_applet_smd_applet_desktop; g_file_get_contents (_tmp2_, &_tmp3_, NULL, &_inner_error_); _g_free0 (content); content = _tmp3_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_FILE_ERROR) { goto __catch5_g_file_error; } _g_free0 (content); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _tmp4_ = XDG_AUTORUN_FILE; _tmp5_ = content; g_file_set_contents (_tmp4_, _tmp5_, (gssize) -1, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_FILE_ERROR) { goto __catch5_g_file_error; } _g_free0 (content); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } goto __finally5; __catch5_g_file_error: { GError* e = NULL; FILE* _tmp6_ = NULL; GError* _tmp7_ = NULL; const gchar* _tmp8_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp6_ = stderr; _tmp7_ = e; _tmp8_ = _tmp7_->message; fprintf (_tmp6_, "%s\n", _tmp8_); _g_error_free0 (e); } __finally5: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (content); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _g_free0 (content); } else { const gchar* _tmp9_ = NULL; _tmp9_ = XDG_AUTORUN_FILE; g_remove (_tmp9_); } } static void ___lambda6__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self) { __lambda6_ (self); } static void _smd_applet_close_err_action_gtk_button_clicked (GtkButton* _sender, gpointer self) { smd_applet_close_err_action ((smdApplet*) self, _sender); } static void __lambda8_ (smdApplet* self, GtkButton* b) { GError * _inner_error_ = NULL; g_return_if_fail (b != NULL); { gboolean _tmp0_ = FALSE; gchar* cmd = NULL; const gchar* _tmp4_ = NULL; gchar* _tmp5_ = NULL; const gchar* _tmp6_ = NULL; _tmp0_ = smd_applet_is_smd_loop_configured (self); if (!_tmp0_) { const gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; _tmp1_ = smd_applet_smd_loop_cmd; _tmp2_ = g_strdup_printf ("%s -t", _tmp1_); _tmp3_ = _tmp2_; g_spawn_command_line_sync (_tmp3_, NULL, NULL, NULL, &_inner_error_); _g_free0 (_tmp3_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch6_g_spawn_error; } g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } _tmp4_ = SMD_LOOP_CFG; _tmp5_ = g_strdup_printf ("gnome-open %s", _tmp4_); cmd = _tmp5_; _tmp6_ = cmd; g_spawn_command_line_async (_tmp6_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (cmd); if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch6_g_spawn_error; } _g_free0 (cmd); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } smd_applet_is_smd_loop_configured (self); _g_free0 (cmd); } goto __finally6; __catch6_g_spawn_error: { GError* e = NULL; FILE* _tmp7_ = NULL; GError* _tmp8_ = NULL; const gchar* _tmp9_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp7_ = stderr; _tmp8_ = e; _tmp9_ = _tmp8_->message; fprintf (_tmp7_, "%s\n", _tmp9_); _g_error_free0 (e); } __finally6: if (G_UNLIKELY (_inner_error_ != NULL)) { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } static void ___lambda8__gtk_button_clicked (GtkButton* _sender, gpointer self) { __lambda8_ ((smdApplet*) self, _sender); } static void __lambda9_ (smdApplet* self, GtkButton* b) { GError * _inner_error_ = NULL; g_return_if_fail (b != NULL); { gboolean _tmp0_ = FALSE; gchar* cmd = NULL; const gchar* _tmp4_ = NULL; gchar* _tmp5_ = NULL; const gchar* _tmp6_ = NULL; _tmp0_ = smd_applet_is_smd_pushpull_configured (self); if (!_tmp0_) { const gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; _tmp1_ = smd_applet_smd_push_cmd; _tmp2_ = g_strdup_printf ("%s -t", _tmp1_); _tmp3_ = _tmp2_; g_spawn_command_line_sync (_tmp3_, NULL, NULL, NULL, &_inner_error_); _g_free0 (_tmp3_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch7_g_spawn_error; } g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } _tmp4_ = SMD_PP_DEF_CFG; _tmp5_ = g_strdup_printf ("gnome-open %s", _tmp4_); cmd = _tmp5_; _tmp6_ = cmd; g_spawn_command_line_async (_tmp6_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (cmd); if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch7_g_spawn_error; } _g_free0 (cmd); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } smd_applet_is_smd_pushpull_configured (self); _g_free0 (cmd); } goto __finally7; __catch7_g_spawn_error: { GError* e = NULL; FILE* _tmp7_ = NULL; GError* _tmp8_ = NULL; const gchar* _tmp9_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp7_ = stderr; _tmp8_ = e; _tmp9_ = _tmp8_->message; fprintf (_tmp7_, "%s\n", _tmp9_); _g_error_free0 (e); } __finally7: if (G_UNLIKELY (_inner_error_ != NULL)) { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } static void ___lambda9__gtk_button_clicked (GtkButton* _sender, gpointer self) { __lambda9_ ((smdApplet*) self, _sender); } static void __lambda10_ (smdApplet* self) { GtkSwitch* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; _tmp0_ = self->priv->sync_active; _tmp1_ = gtk_switch_get_active (_tmp0_); if (_tmp1_) { smd_applet_unpause (self); } else { smd_applet_pause (self); } } static void ___lambda10__g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self) { __lambda10_ ((smdApplet*) self); } static gboolean __lambda11_ (smdApplet* self) { gboolean result = FALSE; GtkWindow* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; _tmp0_ = self->priv->win; _tmp1_ = gtk_widget_get_visible ((GtkWidget*) _tmp0_); _tmp2_ = _tmp1_; if (_tmp2_) { smd_applet_update_loglist (self); smd_applet_update_logcontents (self); } result = TRUE; return result; } static gboolean ___lambda11__gsource_func (gpointer self) { gboolean result; result = __lambda11_ ((smdApplet*) self); return result; } static void __lambda12_ (smdApplet* self, GtkStatusIcon* s) { gboolean _tmp0_ = FALSE; g_return_if_fail (s != NULL); _tmp0_ = self->priv->error_mode; if (_tmp0_) { GtkWindow* _tmp1_ = NULL; _tmp1_ = self->priv->err_win; gtk_window_reshow_with_initial_size (_tmp1_); } else { GtkWindow* _tmp2_ = NULL; gboolean _tmp3_ = FALSE; _tmp2_ = self->priv->win; gtk_widget_show ((GtkWidget*) _tmp2_); _tmp3_ = self->priv->config_wait_mode; if (_tmp3_) { GtkNotebook* _tmp4_ = NULL; _tmp4_ = self->priv->notebook; g_object_set (_tmp4_, "page", 1, NULL); } else { GtkNotebook* _tmp5_ = NULL; _tmp5_ = self->priv->notebook; g_object_set (_tmp5_, "page", 0, NULL); } } } static void ___lambda12__gtk_status_icon_activate (GtkStatusIcon* _sender, gpointer self) { __lambda12_ ((smdApplet*) self, _sender); } static void __lambda13_ (smdApplet* self) { } static void ___lambda13__g_application_activate (GApplication* _sender, gpointer self) { __lambda13_ ((smdApplet*) self); } static void __lambda14_ (smdApplet* self) { GPid _tmp0_ = 0; self->priv->thread_die = TRUE; _tmp0_ = self->priv->pid; if (((gint) _tmp0_) != 0) { GPid _tmp1_ = 0; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; GPid _tmp4_ = 0; _tmp1_ = self->priv->pid; _tmp2_ = g_strdup_printf ("sending SIGTERM to %d", -((gint) _tmp1_)); _tmp3_ = _tmp2_; debug (_tmp3_); _g_free0 (_tmp3_); _tmp4_ = self->priv->pid; kill ((pid_t) (-((gint) _tmp4_)), SIGTERM); } g_application_quit ((GApplication*) self); } static void ___lambda14__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self) { __lambda14_ ((smdApplet*) self); } static void _g_object_unref0_ (gpointer var) { (var == NULL) ? NULL : (var = (g_object_unref (var), NULL)); } static void _g_free0_ (gpointer var) { var = (g_free (var), NULL); } static void _g_list_free__g_free0_ (GList* self) { g_list_foreach (self, (GFunc) _g_free0_, NULL); g_list_free (self); } smdApplet* smd_applet_construct (GType object_type, GError** error) { smdApplet * self = NULL; Block1Data* _data1_; GtkBuilder* _tmp3_ = NULL; GeeArrayList* _tmp19_ = NULL; GSettings* _tmp20_ = NULL; GtkWindow* simple_mainwin = NULL; GtkBuilder* _tmp28_ = NULL; GObject* _tmp29_ = NULL; GtkWindow* _tmp30_ = NULL; GtkApplicationWindow* _tmp31_ = NULL; GtkWidget* _tmp32_ = NULL; GtkWindow* _tmp33_ = NULL; gint w = 0; gint h = 0; gint _tmp34_ = 0; gint _tmp35_ = 0; GtkWindow* _tmp36_ = NULL; gint _tmp37_ = 0; gint _tmp38_ = 0; GtkWindow* _tmp39_ = NULL; const gchar* _tmp40_ = NULL; GtkBuilder* _tmp41_ = NULL; GObject* _tmp42_ = NULL; GtkWindow* _tmp43_ = NULL; GtkLabel* lcopyright = NULL; GtkBuilder* _tmp44_ = NULL; GObject* _tmp45_ = NULL; GtkLabel* _tmp46_ = NULL; GtkGrid* logs_vb = NULL; GtkBuilder* _tmp47_ = NULL; GObject* _tmp48_ = NULL; GtkGrid* _tmp49_ = NULL; GtkComboBoxText* _tmp50_ = NULL; GeeArrayList* _tmp51_ = NULL; GtkComboBoxText* _tmp52_ = NULL; GtkComboBoxText* _tmp53_ = NULL; GtkBuilder* _tmp54_ = NULL; GObject* _tmp55_ = NULL; GtkSwitch* _tmp56_ = NULL; GSettings* _tmp57_ = NULL; gboolean _tmp58_ = FALSE; GtkBuilder* _tmp59_ = NULL; GObject* _tmp60_ = NULL; GtkSwitch* _tmp61_ = NULL; GtkButton* bc = NULL; GtkBuilder* _tmp70_ = NULL; GObject* _tmp71_ = NULL; GtkButton* _tmp72_ = NULL; GtkButton* _tmp73_ = NULL; GtkButton* bel = NULL; GtkBuilder* _tmp74_ = NULL; GObject* _tmp75_ = NULL; GtkButton* _tmp76_ = NULL; GtkButton* _tmp77_ = NULL; GtkButton* bepp = NULL; GtkBuilder* _tmp78_ = NULL; GObject* _tmp79_ = NULL; GtkButton* _tmp80_ = NULL; GtkButton* _tmp81_ = NULL; GtkBuilder* _tmp82_ = NULL; GObject* _tmp83_ = NULL; GtkSwitch* _tmp84_ = NULL; GtkSwitch* _tmp85_ = NULL; GtkBuilder* _tmp86_ = NULL; GObject* _tmp87_ = NULL; GtkNotebook* _tmp88_ = NULL; GtkStatusIcon* _tmp89_ = NULL; GtkStatusIcon* _tmp90_ = NULL; GtkStatusIcon* _tmp91_ = NULL; GtkStatusIcon* _tmp92_ = NULL; GtkWindow* _tmp93_ = NULL; GSimpleAction* quit = NULL; GSimpleAction* _tmp94_ = NULL; GMenuModel* menu = NULL; GtkBuilder* _tmp95_ = NULL; GObject* _tmp96_ = NULL; GMenuModel* _tmp97_ = NULL; GMenuModel* _tmp98_ = NULL; GSimpleAction* _tmp99_ = NULL; GSimpleAction* _tmp100_ = NULL; GList* l = NULL; GList* _tmp101_ = NULL; GList* _tmp102_ = NULL; gint _tmp103_ = 0; GHashFunc _tmp104_ = NULL; GEqualFunc _tmp105_ = NULL; GHashTable* _tmp106_ = NULL; GError * _inner_error_ = NULL; _data1_ = g_slice_new0 (Block1Data); _data1_->_ref_count_ = 1; self = (smdApplet*) g_object_new (object_type, NULL); _data1_->self = g_object_ref (self); g_application_set_application_id ((GApplication*) self, "org.syncmaildir"); { g_application_register ((GApplication*) self, NULL, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch0_g_error; } } goto __finally0; __catch0_g_error: { GError* e = NULL; FILE* _tmp0_ = NULL; GError* _tmp1_ = NULL; const gchar* _tmp2_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp0_ = stderr; _tmp1_ = e; _tmp2_ = _tmp1_->message; fprintf (_tmp0_, "%s\n", _tmp2_); _g_error_free0 (e); } __finally0: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); block1_data_unref (_data1_); _data1_ = NULL; _g_object_unref0 (self); return NULL; } else { block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } _tmp3_ = gtk_builder_new (); _g_object_unref0 (self->priv->builder); self->priv->builder = _tmp3_; { GtkBuilder* _tmp4_ = NULL; const gchar* _tmp5_ = NULL; _tmp4_ = self->priv->builder; _tmp5_ = smd_applet_smd_applet_ui; gtk_builder_add_from_file (_tmp4_, _tmp5_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch1_g_error; } } goto __finally1; __catch1_g_error: { GError* e = NULL; FILE* _tmp6_ = NULL; GError* _tmp7_ = NULL; const gchar* _tmp8_ = NULL; GError* _tmp9_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp6_ = stderr; _tmp7_ = e; _tmp8_ = _tmp7_->message; fprintf (_tmp6_, "%s\n", _tmp8_); _tmp9_ = g_error_new_literal (EXIT, EXIT_ABORT, "Unable to load the ui file"); _inner_error_ = _tmp9_; _g_error_free0 (e); goto __finally1; } __finally1: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); block1_data_unref (_data1_); _data1_ = NULL; _g_object_unref0 (self); return NULL; } else { block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } { GtkBuilder* _tmp10_ = NULL; const gchar* _tmp11_ = NULL; const gchar* _tmp12_ = NULL; gint _tmp13_ = 0; gint _tmp14_ = 0; _tmp10_ = self->priv->builder; _tmp11_ = smd_applet_menu_ui; _tmp12_ = smd_applet_menu_ui; _tmp13_ = strlen (_tmp12_); _tmp14_ = _tmp13_; gtk_builder_add_from_string (_tmp10_, _tmp11_, (gsize) _tmp14_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch2_g_error; } } goto __finally2; __catch2_g_error: { GError* e = NULL; FILE* _tmp15_ = NULL; GError* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; GError* _tmp18_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp15_ = stderr; _tmp16_ = e; _tmp17_ = _tmp16_->message; fprintf (_tmp15_, "%s\n", _tmp17_); _tmp18_ = g_error_new_literal (EXIT, EXIT_ABORT, "Unable to load the menu ui string"); _inner_error_ = _tmp18_; _g_error_free0 (e); goto __finally2; } __finally2: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); block1_data_unref (_data1_); _data1_ = NULL; _g_object_unref0 (self); return NULL; } else { block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } _tmp19_ = gee_array_list_new (TYPE_EVENT, (GBoxedCopyFunc) event_ref, event_unref, NULL, NULL, NULL); _g_object_unref0 (self->priv->events); self->priv->events = _tmp19_; _tmp20_ = g_settings_new ("org.syncmaildir.applet"); _g_object_unref0 (self->priv->settings); self->priv->settings = _tmp20_; { NetworkManager* _tmp21_ = NULL; NetworkManager* _tmp22_ = NULL; NetworkManager* _tmp23_ = NULL; NetworkManager* _tmp24_ = NULL; _tmp22_ = g_initable_new (TYPE_NETWORK_MANAGER_PROXY, NULL, &_inner_error_, "g-flags", 0, "g-name", NM_SERVICE, "g-bus-type", G_BUS_TYPE_SYSTEM, "g-object-path", NM_PATH, "g-interface-name", "org.freedesktop.NetworkManager", "g-interface-info", g_type_get_qdata (TYPE_NETWORK_MANAGER, g_quark_from_static_string ("vala-dbus-interface-info")), NULL); _tmp21_ = (NetworkManager*) _tmp22_; if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch3_g_error; } _tmp23_ = _tmp21_; _tmp21_ = NULL; _g_object_unref0 (self->priv->net_manager); self->priv->net_manager = _tmp23_; _tmp24_ = self->priv->net_manager; g_signal_connect_object (_tmp24_, "state-changed", (GCallback) ____lambda4__network_manager_state_changed, self, 0); _g_object_unref0 (_tmp21_); } goto __finally3; __catch3_g_error: { GError* e = NULL; FILE* _tmp25_ = NULL; GError* _tmp26_ = NULL; const gchar* _tmp27_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp25_ = stderr; _tmp26_ = e; _tmp27_ = _tmp26_->message; fprintf (_tmp25_, "%s\n", _tmp27_); _g_object_unref0 (self->priv->net_manager); self->priv->net_manager = NULL; _g_error_free0 (e); } __finally3: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); block1_data_unref (_data1_); _data1_ = NULL; _g_object_unref0 (self); return NULL; } else { block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } _tmp28_ = self->priv->builder; _tmp29_ = gtk_builder_get_object (_tmp28_, "wMain"); _tmp30_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp29_, gtk_window_get_type ()) ? ((GtkWindow*) _tmp29_) : NULL); simple_mainwin = _tmp30_; _tmp31_ = (GtkApplicationWindow*) gtk_application_window_new ((GtkApplication*) self); g_object_ref_sink (_tmp31_); _g_object_unref0 (self->priv->win); self->priv->win = (GtkWindow*) _tmp31_; _tmp32_ = gtk_bin_get_child ((GtkBin*) simple_mainwin); _tmp33_ = self->priv->win; gtk_widget_reparent (_tmp32_, (GtkWidget*) _tmp33_); w = 0; h = 0; gtk_widget_get_size_request ((GtkWidget*) simple_mainwin, &_tmp34_, &_tmp35_); w = _tmp34_; h = _tmp35_; _tmp36_ = self->priv->win; _tmp37_ = w; _tmp38_ = h; gtk_widget_set_size_request ((GtkWidget*) _tmp36_, _tmp37_, _tmp38_); _tmp39_ = self->priv->win; _tmp40_ = gtk_window_get_title (simple_mainwin); gtk_window_set_title (_tmp39_, _tmp40_); _tmp41_ = self->priv->builder; _tmp42_ = gtk_builder_get_object (_tmp41_, "wError"); _tmp43_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp42_, gtk_window_get_type ()) ? ((GtkWindow*) _tmp42_) : NULL); _g_object_unref0 (self->priv->err_win); self->priv->err_win = _tmp43_; _tmp44_ = self->priv->builder; _tmp45_ = gtk_builder_get_object (_tmp44_, "lCopyright"); _tmp46_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp45_, gtk_label_get_type ()) ? ((GtkLabel*) _tmp45_) : NULL); lcopyright = _tmp46_; gtk_label_set_text (lcopyright, "Copyright " SMD_CONF_COPYRIGHT); _tmp47_ = self->priv->builder; _tmp48_ = gtk_builder_get_object (_tmp47_, "vbLog"); _tmp49_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp48_, gtk_grid_get_type ()) ? ((GtkGrid*) _tmp48_) : NULL); logs_vb = _tmp49_; _tmp50_ = (GtkComboBoxText*) gtk_combo_box_text_new (); g_object_ref_sink (_tmp50_); _g_object_unref0 (self->priv->cblogs); self->priv->cblogs = _tmp50_; _tmp51_ = gee_array_list_new (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup, g_free, NULL, NULL, NULL); _g_object_unref0 (self->priv->lognames); self->priv->lognames = _tmp51_; _tmp52_ = self->priv->cblogs; gtk_grid_attach (logs_vb, (GtkWidget*) _tmp52_, 0, 0, 1, 1); _tmp53_ = self->priv->cblogs; gtk_widget_show ((GtkWidget*) _tmp53_); smd_applet_update_logcontents (self); _tmp54_ = self->priv->builder; _tmp55_ = gtk_builder_get_object (_tmp54_, "sNotify"); _tmp56_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp55_, gtk_switch_get_type ()) ? ((GtkSwitch*) _tmp55_) : NULL); _data1_->bnotify = _tmp56_; _tmp57_ = self->priv->settings; _tmp58_ = g_settings_get_boolean (_tmp57_, SMD_APPLET_key_newmail); gtk_switch_set_active (_data1_->bnotify, _tmp58_); g_signal_connect_data ((GObject*) _data1_->bnotify, "notify::active", (GCallback) ___lambda5__g_object_notify, block1_data_ref (_data1_), (GClosureNotify) block1_data_unref, 0); _tmp59_ = self->priv->builder; _tmp60_ = gtk_builder_get_object (_tmp59_, "sAutostart"); _tmp61_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp60_, gtk_switch_get_type ()) ? ((GtkSwitch*) _tmp60_) : NULL); _data1_->bautostart = _tmp61_; { gchar* content = NULL; gboolean _tmp62_ = FALSE; const gchar* _tmp63_ = NULL; gchar* _tmp64_ = NULL; gboolean _tmp65_ = FALSE; _tmp63_ = XDG_AUTORUN_FILE; _tmp65_ = g_file_get_contents (_tmp63_, &_tmp64_, NULL, &_inner_error_); _g_free0 (content); content = _tmp64_; _tmp62_ = _tmp65_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (content); if (_inner_error_->domain == G_FILE_ERROR) { goto __catch4_g_file_error; } _g_free0 (content); _g_object_unref0 (logs_vb); _g_object_unref0 (lcopyright); _g_object_unref0 (simple_mainwin); block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } if (_tmp62_) { gboolean _tmp66_ = FALSE; _tmp66_ = g_regex_match_simple (GNOME_AUTOSTART_DISABLED, content, 0, 0); if (_tmp66_) { gtk_switch_set_active (_data1_->bautostart, FALSE); } else { gtk_switch_set_active (_data1_->bautostart, TRUE); } } else { gtk_switch_set_active (_data1_->bautostart, FALSE); } _g_free0 (content); } goto __finally4; __catch4_g_file_error: { GError* e = NULL; FILE* _tmp67_ = NULL; GError* _tmp68_ = NULL; const gchar* _tmp69_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp67_ = stderr; _tmp68_ = e; _tmp69_ = _tmp68_->message; fprintf (_tmp67_, "%s\n", _tmp69_); _g_error_free0 (e); } __finally4: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); _g_object_unref0 (logs_vb); _g_object_unref0 (lcopyright); _g_object_unref0 (simple_mainwin); block1_data_unref (_data1_); _data1_ = NULL; _g_object_unref0 (self); return NULL; } else { _g_object_unref0 (logs_vb); _g_object_unref0 (lcopyright); _g_object_unref0 (simple_mainwin); block1_data_unref (_data1_); _data1_ = NULL; g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } g_signal_connect_data ((GObject*) _data1_->bautostart, "notify::active", (GCallback) ___lambda6__g_object_notify, block1_data_ref (_data1_), (GClosureNotify) block1_data_unref, 0); _tmp70_ = self->priv->builder; _tmp71_ = gtk_builder_get_object (_tmp70_, "bClose"); _tmp72_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp71_, gtk_button_get_type ()) ? ((GtkButton*) _tmp71_) : NULL); bc = _tmp72_; _tmp73_ = bc; g_signal_connect_object (_tmp73_, "clicked", (GCallback) _smd_applet_close_err_action_gtk_button_clicked, self, 0); _tmp74_ = self->priv->builder; _tmp75_ = gtk_builder_get_object (_tmp74_, "bEditLoopCfg"); _tmp76_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp75_, gtk_button_get_type ()) ? ((GtkButton*) _tmp75_) : NULL); bel = _tmp76_; _tmp77_ = bel; g_signal_connect_object (_tmp77_, "clicked", (GCallback) ___lambda8__gtk_button_clicked, self, 0); _tmp78_ = self->priv->builder; _tmp79_ = gtk_builder_get_object (_tmp78_, "bEditPushPullCfg"); _tmp80_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp79_, gtk_button_get_type ()) ? ((GtkButton*) _tmp79_) : NULL); bepp = _tmp80_; _tmp81_ = bepp; g_signal_connect_object (_tmp81_, "clicked", (GCallback) ___lambda9__gtk_button_clicked, self, 0); _tmp82_ = self->priv->builder; _tmp83_ = gtk_builder_get_object (_tmp82_, "sSyncActive"); _tmp84_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp83_, gtk_switch_get_type ()) ? ((GtkSwitch*) _tmp83_) : NULL); _g_object_unref0 (self->priv->sync_active); self->priv->sync_active = _tmp84_; _tmp85_ = self->priv->sync_active; g_signal_connect_object ((GObject*) _tmp85_, "notify::active", (GCallback) ___lambda10__g_object_notify, self, 0); _tmp86_ = self->priv->builder; _tmp87_ = gtk_builder_get_object (_tmp86_, "nMain"); _tmp88_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp87_, gtk_notebook_get_type ()) ? ((GtkNotebook*) _tmp87_) : NULL); _g_object_unref0 (self->priv->notebook); self->priv->notebook = _tmp88_; smd_applet_update_loglist (self); g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) 2000, ___lambda11__gsource_func, g_object_ref (self), g_object_unref); _tmp89_ = gtk_status_icon_new_from_icon_name ("mail-send-receive"); _g_object_unref0 (self->priv->si); self->priv->si = _tmp89_; _tmp90_ = self->priv->si; gtk_status_icon_set_visible (_tmp90_, TRUE); _tmp91_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp91_, "smd-applet is running"); _tmp92_ = self->priv->si; g_signal_connect_object (_tmp92_, "activate", (GCallback) ___lambda12__gtk_status_icon_activate, self, 0); _tmp93_ = self->priv->win; gtk_application_add_window ((GtkApplication*) self, _tmp93_); g_signal_connect_object ((GApplication*) self, "activate", (GCallback) ___lambda13__g_application_activate, self, 0); _tmp94_ = g_simple_action_new ("quit", NULL); quit = _tmp94_; _tmp95_ = self->priv->builder; _tmp96_ = gtk_builder_get_object (_tmp95_, "app-menu"); _tmp97_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp96_, g_menu_model_get_type ()) ? ((GMenuModel*) _tmp96_) : NULL); menu = _tmp97_; _tmp98_ = menu; gtk_application_set_app_menu ((GtkApplication*) self, _tmp98_); _tmp99_ = quit; g_action_map_add_action ((GActionMap*) self, (GAction*) _tmp99_); _tmp100_ = quit; g_signal_connect_object (_tmp100_, "activate", (GCallback) ___lambda14__g_simple_action_activate, self, 0); _tmp101_ = notify_get_server_caps (); l = _tmp101_; _tmp102_ = l; _tmp103_ = g_list_index (_tmp102_, "persistence"); self->priv->notification_server_has_persistence = 0 <= _tmp103_; _tmp104_ = g_direct_hash; _tmp105_ = g_str_equal; _tmp106_ = g_hash_table_new_full (_tmp104_, _tmp105_, _g_object_unref0_, _g_free0_); _g_hash_table_unref0 (self->priv->command_hash); self->priv->command_hash = _tmp106_; __g_list_free__g_free0_0 (l); _g_object_unref0 (menu); _g_object_unref0 (quit); _g_object_unref0 (bepp); _g_object_unref0 (bel); _g_object_unref0 (bc); _g_object_unref0 (logs_vb); _g_object_unref0 (lcopyright); _g_object_unref0 (simple_mainwin); block1_data_unref (_data1_); _data1_ = NULL; return self; } smdApplet* smd_applet_new (GError** error) { return smd_applet_construct (TYPE_SMD_APPLET, error); } static void* smd_applet_smdThread (smdApplet* self) { void* result = NULL; gboolean rc = FALSE; GError * _inner_error_ = NULL; g_return_val_if_fail (self != NULL, NULL); rc = TRUE; while (TRUE) { gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; _tmp1_ = rc; if (_tmp1_) { gboolean _tmp2_ = FALSE; _tmp2_ = self->priv->thread_die; _tmp0_ = !_tmp2_; } else { _tmp0_ = FALSE; } if (!_tmp0_) { break; } debug ("(re)starting smd-loop"); { gboolean _tmp3_ = FALSE; gboolean _tmp4_ = FALSE; _tmp4_ = smd_applet_run_smd_loop (self, &_inner_error_); _tmp3_ = _tmp4_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { goto __catch8_exit; } g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } rc = _tmp3_; } goto __finally8; __catch8_exit: { GError* e = NULL; e = _inner_error_; _inner_error_ = NULL; rc = FALSE; _g_error_free0 (e); } __finally8: if (G_UNLIKELY (_inner_error_ != NULL)) { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } } result = NULL; return result; } static gpointer _smd_applet_smdThread_gthread_func (gpointer self) { gpointer result; result = smd_applet_smdThread ((smdApplet*) self); g_object_unref (self); return result; } static void smd_applet_start_smdThread (smdApplet* self, gboolean force) { gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; g_return_if_fail (self != NULL); _tmp2_ = force; if (!_tmp2_) { NetworkManager* _tmp3_ = NULL; _tmp3_ = self->priv->net_manager; _tmp1_ = _tmp3_ != NULL; } else { _tmp1_ = FALSE; } if (_tmp1_) { NetworkManager* _tmp4_ = NULL; guint _tmp5_ = 0U; guint _tmp6_ = 0U; gboolean _tmp7_ = FALSE; _tmp4_ = self->priv->net_manager; _tmp5_ = network_manager_get_state (_tmp4_); _tmp6_ = _tmp5_; _tmp7_ = is_nm_connected (_tmp6_); _tmp0_ = !_tmp7_; } else { _tmp0_ = FALSE; } if (_tmp0_) { GtkSwitch* _tmp8_ = NULL; GtkStatusIcon* _tmp9_ = NULL; _tmp8_ = self->priv->sync_active; gtk_switch_set_active (_tmp8_, FALSE); _tmp9_ = self->priv->si; gtk_status_icon_set_from_stock (_tmp9_, "gtk-media-pause"); } else { GThread* _tmp10_ = NULL; _tmp10_ = g_thread_new (NULL, _smd_applet_smdThread_gthread_func, g_object_ref (self)); _g_thread_unref0 (self->priv->thread); self->priv->thread = _tmp10_; } } static glong string_strnlen (gchar* str, glong maxlen) { glong result = 0L; gchar* end = NULL; gchar* _tmp0_ = NULL; glong _tmp1_ = 0L; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; _tmp0_ = str; _tmp1_ = maxlen; _tmp2_ = memchr (_tmp0_, 0, (gsize) _tmp1_); end = _tmp2_; _tmp3_ = end; if (_tmp3_ == NULL) { glong _tmp4_ = 0L; _tmp4_ = maxlen; result = _tmp4_; return result; } else { gchar* _tmp5_ = NULL; gchar* _tmp6_ = NULL; _tmp5_ = end; _tmp6_ = str; result = (glong) (_tmp5_ - _tmp6_); return result; } } static gchar* string_substring (const gchar* self, glong offset, glong len) { gchar* result = NULL; glong string_length = 0L; gboolean _tmp0_ = FALSE; glong _tmp1_ = 0L; glong _tmp8_ = 0L; glong _tmp14_ = 0L; glong _tmp17_ = 0L; glong _tmp18_ = 0L; glong _tmp19_ = 0L; glong _tmp20_ = 0L; glong _tmp21_ = 0L; gchar* _tmp22_ = NULL; g_return_val_if_fail (self != NULL, NULL); _tmp1_ = offset; if (_tmp1_ >= ((glong) 0)) { glong _tmp2_ = 0L; _tmp2_ = len; _tmp0_ = _tmp2_ >= ((glong) 0); } else { _tmp0_ = FALSE; } if (_tmp0_) { glong _tmp3_ = 0L; glong _tmp4_ = 0L; glong _tmp5_ = 0L; _tmp3_ = offset; _tmp4_ = len; _tmp5_ = string_strnlen ((gchar*) self, _tmp3_ + _tmp4_); string_length = _tmp5_; } else { gint _tmp6_ = 0; gint _tmp7_ = 0; _tmp6_ = strlen (self); _tmp7_ = _tmp6_; string_length = (glong) _tmp7_; } _tmp8_ = offset; if (_tmp8_ < ((glong) 0)) { glong _tmp9_ = 0L; glong _tmp10_ = 0L; glong _tmp11_ = 0L; _tmp9_ = string_length; _tmp10_ = offset; offset = _tmp9_ + _tmp10_; _tmp11_ = offset; g_return_val_if_fail (_tmp11_ >= ((glong) 0), NULL); } else { glong _tmp12_ = 0L; glong _tmp13_ = 0L; _tmp12_ = offset; _tmp13_ = string_length; g_return_val_if_fail (_tmp12_ <= _tmp13_, NULL); } _tmp14_ = len; if (_tmp14_ < ((glong) 0)) { glong _tmp15_ = 0L; glong _tmp16_ = 0L; _tmp15_ = string_length; _tmp16_ = offset; len = _tmp15_ - _tmp16_; } _tmp17_ = offset; _tmp18_ = len; _tmp19_ = string_length; g_return_val_if_fail ((_tmp17_ + _tmp18_) <= _tmp19_, NULL); _tmp20_ = offset; _tmp21_ = len; _tmp22_ = g_strndup (((gchar*) self) + _tmp20_, (gsize) _tmp21_); result = _tmp22_; return result; } static gboolean smd_applet_eval_smd_loop_error_message (smdApplet* self, const gchar* args, const gchar* account, const gchar* host, GError** error) { gboolean result = FALSE; GRegex* context = NULL; GRegex* _tmp0_ = NULL; GRegex* cause = NULL; GRegex* _tmp1_ = NULL; GRegex* human = NULL; GRegex* _tmp2_ = NULL; GRegex* actions = NULL; GRegex* _tmp3_ = NULL; GMatchInfo* i_ctx = NULL; GMatchInfo* i_cause = NULL; GMatchInfo* i_human = NULL; GMatchInfo* i_act = NULL; GRegex* _tmp4_ = NULL; const gchar* _tmp5_ = NULL; GMatchInfo* _tmp6_ = NULL; gboolean _tmp7_ = FALSE; GRegex* _tmp10_ = NULL; const gchar* _tmp11_ = NULL; GMatchInfo* _tmp12_ = NULL; gboolean _tmp13_ = FALSE; GRegex* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; GMatchInfo* _tmp18_ = NULL; gboolean _tmp19_ = FALSE; gboolean has_actions = FALSE; GRegex* _tmp22_ = NULL; const gchar* _tmp23_ = NULL; GMatchInfo* _tmp24_ = NULL; gboolean _tmp25_ = FALSE; gboolean _tmp26_ = FALSE; GMatchInfo* _tmp27_ = NULL; gchar* _tmp28_ = NULL; gchar* _tmp29_ = NULL; gboolean _tmp30_ = FALSE; GMatchInfo* _tmp40_ = NULL; gchar* _tmp41_ = NULL; gchar* _tmp42_ = NULL; gboolean _tmp43_ = FALSE; gchar* permissions = NULL; gchar* mail_name = NULL; gchar* mail_body = NULL; GeeArrayList* commands = NULL; void* _tmp55_ = NULL; GDestroyNotify _tmp56_ = NULL; GeeEqualDataFunc _tmp57_ = NULL; GeeArrayList* _tmp58_ = NULL; gboolean _tmp59_ = FALSE; GeeArrayList* _tmp130_ = NULL; GeeArrayList* _tmp131_ = NULL; gint _tmp132_ = 0; gint _tmp133_ = 0; const gchar* _tmp134_ = NULL; const gchar* _tmp135_ = NULL; GMatchInfo* _tmp136_ = NULL; gchar* _tmp137_ = NULL; gchar* _tmp138_ = NULL; GMatchInfo* _tmp139_ = NULL; gchar* _tmp140_ = NULL; gchar* _tmp141_ = NULL; const gchar* _tmp142_ = NULL; const gchar* _tmp143_ = NULL; const gchar* _tmp144_ = NULL; GeeArrayList* _tmp145_ = NULL; Event* _tmp146_ = NULL; Event* _tmp147_ = NULL; GError * _inner_error_ = NULL; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (args != NULL, FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (host != NULL, FALSE); _tmp0_ = g_regex_new ("context\\(([^\\)]+)\\)", 0, 0, &_inner_error_); context = _tmp0_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); return FALSE; } else { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _tmp1_ = g_regex_new ("probable-cause\\(([^\\)]+)\\)", 0, 0, &_inner_error_); cause = _tmp1_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_regex_unref0 (context); return FALSE; } else { _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _tmp2_ = g_regex_new ("human-intervention\\(([^\\)]+)\\)", 0, 0, &_inner_error_); human = _tmp2_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _tmp3_ = g_regex_new ("suggested-actions\\((.*)\\) *$", 0, 0, &_inner_error_); actions = _tmp3_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } i_ctx = NULL; i_cause = NULL; i_human = NULL; i_act = NULL; _tmp4_ = context; _tmp5_ = args; _tmp7_ = g_regex_match (_tmp4_, _tmp5_, 0, &_tmp6_); _g_match_info_unref0 (i_ctx); i_ctx = _tmp6_; if (!_tmp7_) { FILE* _tmp8_ = NULL; const gchar* _tmp9_ = NULL; _tmp8_ = stderr; _tmp9_ = args; fprintf (_tmp8_, "smd-loop error with no context: %s\n", _tmp9_); result = TRUE; _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } _tmp10_ = cause; _tmp11_ = args; _tmp13_ = g_regex_match (_tmp10_, _tmp11_, 0, &_tmp12_); _g_match_info_unref0 (i_cause); i_cause = _tmp12_; if (!_tmp13_) { FILE* _tmp14_ = NULL; const gchar* _tmp15_ = NULL; _tmp14_ = stderr; _tmp15_ = args; fprintf (_tmp14_, "smd-loop error with no cause: %s\n", _tmp15_); result = TRUE; _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } _tmp16_ = human; _tmp17_ = args; _tmp19_ = g_regex_match (_tmp16_, _tmp17_, 0, &_tmp18_); _g_match_info_unref0 (i_human); i_human = _tmp18_; if (!_tmp19_) { FILE* _tmp20_ = NULL; const gchar* _tmp21_ = NULL; _tmp20_ = stderr; _tmp21_ = args; fprintf (_tmp20_, "smd-loop error with no human: %s\n", _tmp21_); result = TRUE; _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } _tmp22_ = actions; _tmp23_ = args; _tmp25_ = g_regex_match (_tmp22_, _tmp23_, 0, &_tmp24_); _g_match_info_unref0 (i_act); i_act = _tmp24_; has_actions = _tmp25_; _tmp27_ = i_human; _tmp28_ = g_match_info_fetch (_tmp27_, 1); _tmp29_ = _tmp28_; _tmp30_ = g_strcmp0 (_tmp29_, "necessary") != 0; _g_free0 (_tmp29_); if (_tmp30_) { GMatchInfo* _tmp31_ = NULL; gchar* _tmp32_ = NULL; gchar* _tmp33_ = NULL; _tmp31_ = i_cause; _tmp32_ = g_match_info_fetch (_tmp31_, 1); _tmp33_ = _tmp32_; _tmp26_ = g_strcmp0 (_tmp33_, "network") == 0; _g_free0 (_tmp33_); } else { _tmp26_ = FALSE; } if (_tmp26_) { GeeArrayList* _tmp34_ = NULL; GeeArrayList* _tmp35_ = NULL; gint _tmp36_ = 0; gint _tmp37_ = 0; Event* _tmp38_ = NULL; Event* _tmp39_ = NULL; g_mutex_lock (&self->priv->events_lock); _tmp34_ = self->priv->events; _tmp35_ = self->priv->events; _tmp36_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp35_); _tmp37_ = _tmp36_; _tmp38_ = event_network_error (); _tmp39_ = _tmp38_; gee_abstract_list_insert ((GeeAbstractList*) _tmp34_, _tmp37_, _tmp39_); _event_unref0 (_tmp39_); g_mutex_unlock (&self->priv->events_lock); result = TRUE; _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } _tmp40_ = i_human; _tmp41_ = g_match_info_fetch (_tmp40_, 1); _tmp42_ = _tmp41_; _tmp43_ = g_strcmp0 (_tmp42_, "necessary") != 0; _g_free0 (_tmp42_); if (_tmp43_) { FILE* _tmp44_ = NULL; const gchar* _tmp45_ = NULL; GeeArrayList* _tmp46_ = NULL; GeeArrayList* _tmp47_ = NULL; gint _tmp48_ = 0; gint _tmp49_ = 0; GMatchInfo* _tmp50_ = NULL; gchar* _tmp51_ = NULL; gchar* _tmp52_ = NULL; Event* _tmp53_ = NULL; Event* _tmp54_ = NULL; _tmp44_ = stderr; _tmp45_ = args; fprintf (_tmp44_, "smd-loop giving an avoidable error: %s\n", _tmp45_); g_mutex_lock (&self->priv->events_lock); _tmp46_ = self->priv->events; _tmp47_ = self->priv->events; _tmp48_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp47_); _tmp49_ = _tmp48_; _tmp50_ = i_cause; _tmp51_ = g_match_info_fetch (_tmp50_, 1); _tmp52_ = _tmp51_; _tmp53_ = event_generic_error (_tmp52_); _tmp54_ = _tmp53_; gee_abstract_list_insert ((GeeAbstractList*) _tmp46_, _tmp49_, _tmp54_); _event_unref0 (_tmp54_); _g_free0 (_tmp52_); g_mutex_unlock (&self->priv->events_lock); result = TRUE; _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } permissions = NULL; mail_name = NULL; mail_body = NULL; _tmp57_ = gee_functions_get_equal_func_for (G_TYPE_STRING, &_tmp55_, &_tmp56_); _tmp58_ = gee_array_list_new (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup, g_free, _tmp57_, _tmp55_, _tmp56_); commands = _tmp58_; _tmp59_ = has_actions; if (_tmp59_) { gchar* acts = NULL; GMatchInfo* _tmp60_ = NULL; gchar* _tmp61_ = NULL; GRegex* r_perm = NULL; GRegex* _tmp62_ = NULL; GRegex* r_mail = NULL; GRegex* _tmp63_ = NULL; GRegex* r_cmd = NULL; GRegex* _tmp64_ = NULL; gint from = 0; _tmp60_ = i_act; _tmp61_ = g_match_info_fetch (_tmp60_, 1); acts = _tmp61_; _tmp62_ = g_regex_new ("display-permissions\\(([^\\)]+)\\)", 0, 0, &_inner_error_); r_perm = _tmp62_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _tmp63_ = g_regex_new ("display-mail\\(([^\\)]+)\\)", 0, 0, &_inner_error_); r_mail = _tmp63_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _tmp64_ = g_regex_new ("run\\(([^\\)]+)\\)", 0, 0, &_inner_error_); r_cmd = _tmp64_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } from = 0; { gboolean _tmp65_ = FALSE; _tmp65_ = TRUE; while (TRUE) { gboolean _tmp66_ = FALSE; const gchar* _tmp67_ = NULL; GMatchInfo* i_cmd = NULL; GRegex* _tmp71_ = NULL; const gchar* _tmp72_ = NULL; GMatchInfo* _tmp73_ = NULL; gboolean _tmp74_ = FALSE; const gchar* _tmp127_ = NULL; gint _tmp128_ = 0; gchar* _tmp129_ = NULL; if (!_tmp65_) { } _tmp65_ = FALSE; _tmp67_ = acts; if (_tmp67_ != NULL) { const gchar* _tmp68_ = NULL; gint _tmp69_ = 0; gint _tmp70_ = 0; _tmp68_ = acts; _tmp69_ = strlen (_tmp68_); _tmp70_ = _tmp69_; _tmp66_ = _tmp70_ > 0; } else { _tmp66_ = FALSE; } if (!_tmp66_) { break; } i_cmd = NULL; _tmp71_ = r_perm; _tmp72_ = acts; _tmp74_ = g_regex_match (_tmp71_, _tmp72_, 0, &_tmp73_); _g_match_info_unref0 (i_cmd); i_cmd = _tmp73_; if (_tmp74_) { GMatchInfo* _tmp75_ = NULL; gint _tmp76_ = 0; gchar* file = NULL; GMatchInfo* _tmp77_ = NULL; gchar* _tmp78_ = NULL; gchar* output = NULL; gchar* err = NULL; _tmp75_ = i_cmd; g_match_info_fetch_pos (_tmp75_, 0, NULL, &_tmp76_); from = _tmp76_; _tmp77_ = i_cmd; _tmp78_ = g_match_info_fetch (_tmp77_, 1); file = _tmp78_; output = NULL; err = NULL; { const gchar* _tmp79_ = NULL; gchar* _tmp80_ = NULL; gchar* _tmp81_ = NULL; gchar* _tmp82_ = NULL; gchar* _tmp83_ = NULL; const gchar* _tmp84_ = NULL; const gchar* _tmp85_ = NULL; gchar* _tmp86_ = NULL; _tmp79_ = file; _tmp80_ = g_strconcat ("ls -ld ", _tmp79_, NULL); _tmp81_ = _tmp80_; g_spawn_command_line_sync (_tmp81_, &_tmp82_, &_tmp83_, NULL, &_inner_error_); _g_free0 (output); output = _tmp82_; _g_free0 (err); err = _tmp83_; _g_free0 (_tmp81_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch9_g_spawn_error; } _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp84_ = output; _tmp85_ = err; _tmp86_ = g_strconcat (_tmp84_, _tmp85_, NULL); _g_free0 (permissions); permissions = _tmp86_; } goto __finally9; __catch9_g_spawn_error: { GError* e = NULL; FILE* _tmp87_ = NULL; GError* _tmp88_ = NULL; const gchar* _tmp89_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp87_ = stderr; _tmp88_ = e; _tmp89_ = _tmp88_->message; fprintf (_tmp87_, "Spawning ls: %s\n", _tmp89_); _g_error_free0 (e); } __finally9: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _g_free0 (err); _g_free0 (output); _g_free0 (file); } else { GRegex* _tmp90_ = NULL; const gchar* _tmp91_ = NULL; GMatchInfo* _tmp92_ = NULL; gboolean _tmp93_ = FALSE; _tmp90_ = r_mail; _tmp91_ = acts; _tmp93_ = g_regex_match (_tmp90_, _tmp91_, 0, &_tmp92_); _g_match_info_unref0 (i_cmd); i_cmd = _tmp92_; if (_tmp93_) { GMatchInfo* _tmp94_ = NULL; gint _tmp95_ = 0; gchar* file = NULL; GMatchInfo* _tmp96_ = NULL; gchar* _tmp97_ = NULL; gchar* output = NULL; gchar* _tmp98_ = NULL; gchar* err = NULL; _tmp94_ = i_cmd; g_match_info_fetch_pos (_tmp94_, 0, NULL, &_tmp95_); from = _tmp95_; _tmp96_ = i_cmd; _tmp97_ = g_match_info_fetch (_tmp96_, 1); file = _tmp97_; _tmp98_ = g_strdup (""); output = _tmp98_; err = NULL; { const gchar* _tmp99_ = NULL; gchar* _tmp100_ = NULL; const gchar* _tmp101_ = NULL; gchar* _tmp102_ = NULL; gchar* _tmp103_ = NULL; gchar* _tmp104_ = NULL; gchar* _tmp105_ = NULL; const gchar* _tmp106_ = NULL; const gchar* _tmp107_ = NULL; gchar* _tmp108_ = NULL; _tmp99_ = file; _tmp100_ = g_strdup (_tmp99_); _g_free0 (mail_name); mail_name = _tmp100_; _tmp101_ = file; _tmp102_ = g_strconcat ("cat ", _tmp101_, NULL); _tmp103_ = _tmp102_; g_spawn_command_line_sync (_tmp103_, &_tmp104_, &_tmp105_, NULL, &_inner_error_); _g_free0 (output); output = _tmp104_; _g_free0 (err); err = _tmp105_; _g_free0 (_tmp103_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch10_g_spawn_error; } _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp106_ = output; _tmp107_ = err; _tmp108_ = g_strconcat (_tmp106_, _tmp107_, NULL); _g_free0 (mail_body); mail_body = _tmp108_; } goto __finally10; __catch10_g_spawn_error: { GError* e = NULL; FILE* _tmp109_ = NULL; GError* _tmp110_ = NULL; const gchar* _tmp111_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp109_ = stderr; _tmp110_ = e; _tmp111_ = _tmp110_->message; fprintf (_tmp109_, "Spawning ls: %s\n", _tmp111_); _g_error_free0 (e); } __finally10: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_REGEX_ERROR) { g_propagate_error (error, _inner_error_); _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return FALSE; } else { _g_free0 (err); _g_free0 (output); _g_free0 (file); _g_match_info_unref0 (i_cmd); _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } _g_free0 (err); _g_free0 (output); _g_free0 (file); } else { GRegex* _tmp112_ = NULL; const gchar* _tmp113_ = NULL; GMatchInfo* _tmp114_ = NULL; gboolean _tmp115_ = FALSE; _tmp112_ = r_cmd; _tmp113_ = acts; _tmp115_ = g_regex_match (_tmp112_, _tmp113_, 0, &_tmp114_); _g_match_info_unref0 (i_cmd); i_cmd = _tmp114_; if (_tmp115_) { gchar* command = NULL; GMatchInfo* _tmp116_ = NULL; gchar* _tmp117_ = NULL; GMatchInfo* _tmp118_ = NULL; gint _tmp119_ = 0; GeeArrayList* _tmp120_ = NULL; GeeArrayList* _tmp121_ = NULL; gint _tmp122_ = 0; gint _tmp123_ = 0; const gchar* _tmp124_ = NULL; _tmp116_ = i_cmd; _tmp117_ = g_match_info_fetch (_tmp116_, 1); command = _tmp117_; _tmp118_ = i_cmd; g_match_info_fetch_pos (_tmp118_, 0, NULL, &_tmp119_); from = _tmp119_; _tmp120_ = commands; _tmp121_ = commands; _tmp122_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp121_); _tmp123_ = _tmp122_; _tmp124_ = command; gee_abstract_list_insert ((GeeAbstractList*) _tmp120_, _tmp123_, _tmp124_); _g_free0 (command); } else { FILE* _tmp125_ = NULL; const gchar* _tmp126_ = NULL; _tmp125_ = stderr; _tmp126_ = acts; fprintf (_tmp125_, "Unrecognized action: %s\n", _tmp126_); _g_match_info_unref0 (i_cmd); break; } } } _tmp127_ = acts; _tmp128_ = from; _tmp129_ = string_substring (_tmp127_, (glong) _tmp128_, (glong) -1); _g_free0 (acts); acts = _tmp129_; _g_match_info_unref0 (i_cmd); } } _g_regex_unref0 (r_cmd); _g_regex_unref0 (r_mail); _g_regex_unref0 (r_perm); _g_free0 (acts); } g_mutex_lock (&self->priv->events_lock); _tmp130_ = self->priv->events; _tmp131_ = self->priv->events; _tmp132_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp131_); _tmp133_ = _tmp132_; _tmp134_ = account; _tmp135_ = host; _tmp136_ = i_ctx; _tmp137_ = g_match_info_fetch (_tmp136_, 1); _tmp138_ = _tmp137_; _tmp139_ = i_cause; _tmp140_ = g_match_info_fetch (_tmp139_, 1); _tmp141_ = _tmp140_; _tmp142_ = permissions; _tmp143_ = mail_name; _tmp144_ = mail_body; _tmp145_ = commands; _tmp146_ = event_error (_tmp134_, _tmp135_, _tmp138_, _tmp141_, _tmp142_, _tmp143_, _tmp144_, _tmp145_); _tmp147_ = _tmp146_; gee_abstract_list_insert ((GeeAbstractList*) _tmp130_, _tmp133_, _tmp147_); _event_unref0 (_tmp147_); _g_free0 (_tmp141_); _g_free0 (_tmp138_); g_mutex_unlock (&self->priv->events_lock); result = FALSE; _g_object_unref0 (commands); _g_free0 (mail_body); _g_free0 (mail_name); _g_free0 (permissions); _g_match_info_unref0 (i_act); _g_match_info_unref0 (i_human); _g_match_info_unref0 (i_cause); _g_match_info_unref0 (i_ctx); _g_regex_unref0 (actions); _g_regex_unref0 (human); _g_regex_unref0 (cause); _g_regex_unref0 (context); return result; } static gboolean smd_applet_eval_smd_loop_message (smdApplet* self, const gchar* s) { gboolean result = FALSE; GError * _inner_error_ = NULL; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (s != NULL, FALSE); { GMatchInfo* info = NULL; GRegex* r_tags = NULL; GRegex* _tmp0_ = NULL; GRegex* r_skip = NULL; GRegex* _tmp1_ = NULL; GRegex* _tmp2_ = NULL; const gchar* _tmp3_ = NULL; gboolean _tmp4_ = FALSE; GRegex* _tmp5_ = NULL; const gchar* _tmp6_ = NULL; GMatchInfo* _tmp7_ = NULL; gboolean _tmp8_ = FALSE; gchar* account = NULL; GMatchInfo* _tmp12_ = NULL; gchar* _tmp13_ = NULL; gchar* host = NULL; GMatchInfo* _tmp14_ = NULL; gchar* _tmp15_ = NULL; gchar* tags = NULL; GMatchInfo* _tmp16_ = NULL; gchar* _tmp17_ = NULL; GMatchInfo* i_args = NULL; GRegex* r_stats = NULL; GRegex* _tmp18_ = NULL; GRegex* r_error = NULL; GRegex* _tmp19_ = NULL; GRegex* _tmp20_ = NULL; const gchar* _tmp21_ = NULL; GMatchInfo* _tmp22_ = NULL; gboolean _tmp23_ = FALSE; info = NULL; _tmp0_ = g_regex_new ("^([^:]+): smd-(client|server|loop|push|pull|pushpull)@([^:]+): TAGS:(." \ "*)$", 0, 0, &_inner_error_); r_tags = _tmp0_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp1_ = g_regex_new ("^([^:]+): smd-(client|server)@([^:]+): ERROR", 0, 0, &_inner_error_); r_skip = _tmp1_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp2_ = r_skip; _tmp3_ = s; _tmp4_ = g_regex_match (_tmp2_, _tmp3_, 0, NULL); if (_tmp4_) { result = TRUE; _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); return result; } _tmp5_ = r_tags; _tmp6_ = s; _tmp8_ = g_regex_match (_tmp5_, _tmp6_, 0, &_tmp7_); _g_match_info_unref0 (info); info = _tmp7_; if (!_tmp8_) { const gchar* _tmp9_ = NULL; gchar* _tmp10_ = NULL; gchar* _tmp11_ = NULL; _tmp9_ = s; _tmp10_ = g_strdup_printf ("unhandled smd-loop message: %s", _tmp9_); _tmp11_ = _tmp10_; debug (_tmp11_); _g_free0 (_tmp11_); result = TRUE; _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); return result; } _tmp12_ = info; _tmp13_ = g_match_info_fetch (_tmp12_, 1); account = _tmp13_; _tmp14_ = info; _tmp15_ = g_match_info_fetch (_tmp14_, 3); host = _tmp15_; _tmp16_ = info; _tmp17_ = g_match_info_fetch (_tmp16_, 4); tags = _tmp17_; i_args = NULL; _tmp18_ = g_regex_new (" stats::(.*)$", 0, 0, &_inner_error_); r_stats = _tmp18_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp19_ = g_regex_new (" error::(.*)$", 0, 0, &_inner_error_); r_error = _tmp19_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp20_ = r_stats; _tmp21_ = tags; _tmp23_ = g_regex_match (_tmp20_, _tmp21_, 0, &_tmp22_); _g_match_info_unref0 (i_args); i_args = _tmp22_; if (_tmp23_) { GRegex* r_neW = NULL; GRegex* _tmp24_ = NULL; GRegex* r_del = NULL; GRegex* _tmp25_ = NULL; GMatchInfo* i_new = NULL; GMatchInfo* i_del = NULL; gchar* args = NULL; GMatchInfo* _tmp26_ = NULL; gchar* _tmp27_ = NULL; gboolean has_new = FALSE; GRegex* _tmp28_ = NULL; const gchar* _tmp29_ = NULL; GMatchInfo* _tmp30_ = NULL; gboolean _tmp31_ = FALSE; gboolean has_del = FALSE; GRegex* _tmp32_ = NULL; const gchar* _tmp33_ = NULL; GMatchInfo* _tmp34_ = NULL; gboolean _tmp35_ = FALSE; gint new_mails = 0; gboolean _tmp36_ = FALSE; gint del_mails = 0; gboolean _tmp41_ = FALSE; gboolean _tmp46_ = FALSE; const gchar* _tmp47_ = NULL; _tmp24_ = g_regex_new ("new-mails\\(([0-9]+)\\)", 0, 0, &_inner_error_); r_neW = _tmp24_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp25_ = g_regex_new ("del-mails\\(([0-9]+)\\)", 0, 0, &_inner_error_); r_del = _tmp25_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_regex_unref0 (r_neW); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_regex_unref0 (r_neW); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } i_new = NULL; i_del = NULL; _tmp26_ = i_args; _tmp27_ = g_match_info_fetch (_tmp26_, 1); args = _tmp27_; _tmp28_ = r_neW; _tmp29_ = args; _tmp31_ = g_regex_match (_tmp28_, _tmp29_, 0, &_tmp30_); _g_match_info_unref0 (i_new); i_new = _tmp30_; has_new = _tmp31_; _tmp32_ = r_del; _tmp33_ = args; _tmp35_ = g_regex_match (_tmp32_, _tmp33_, 0, &_tmp34_); _g_match_info_unref0 (i_del); i_del = _tmp34_; has_del = _tmp35_; new_mails = 0; _tmp36_ = has_new; if (_tmp36_) { GMatchInfo* _tmp37_ = NULL; gchar* _tmp38_ = NULL; gchar* _tmp39_ = NULL; gint _tmp40_ = 0; _tmp37_ = i_new; _tmp38_ = g_match_info_fetch (_tmp37_, 1); _tmp39_ = _tmp38_; _tmp40_ = atoi (_tmp39_); new_mails = _tmp40_; _g_free0 (_tmp39_); } del_mails = 0; _tmp41_ = has_del; if (_tmp41_) { GMatchInfo* _tmp42_ = NULL; gchar* _tmp43_ = NULL; gchar* _tmp44_ = NULL; gint _tmp45_ = 0; _tmp42_ = i_del; _tmp43_ = g_match_info_fetch (_tmp42_, 1); _tmp44_ = _tmp43_; _tmp45_ = atoi (_tmp44_); del_mails = _tmp45_; _g_free0 (_tmp44_); } _tmp47_ = host; if (g_strcmp0 (_tmp47_, "localhost") == 0) { gboolean _tmp48_ = FALSE; gint _tmp49_ = 0; _tmp49_ = new_mails; if (_tmp49_ > 0) { _tmp48_ = TRUE; } else { gint _tmp50_ = 0; _tmp50_ = del_mails; _tmp48_ = _tmp50_ > 0; } _tmp46_ = _tmp48_; } else { _tmp46_ = FALSE; } if (_tmp46_) { GeeArrayList* _tmp51_ = NULL; GeeArrayList* _tmp52_ = NULL; gint _tmp53_ = 0; gint _tmp54_ = 0; const gchar* _tmp55_ = NULL; const gchar* _tmp56_ = NULL; gint _tmp57_ = 0; gint _tmp58_ = 0; Event* _tmp59_ = NULL; Event* _tmp60_ = NULL; g_mutex_lock (&self->priv->events_lock); _tmp51_ = self->priv->events; _tmp52_ = self->priv->events; _tmp53_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp52_); _tmp54_ = _tmp53_; _tmp55_ = account; _tmp56_ = host; _tmp57_ = new_mails; _tmp58_ = del_mails; _tmp59_ = event_stats (_tmp55_, _tmp56_, _tmp57_, _tmp58_); _tmp60_ = _tmp59_; gee_abstract_list_insert ((GeeAbstractList*) _tmp51_, _tmp54_, _tmp60_); _event_unref0 (_tmp60_); g_mutex_unlock (&self->priv->events_lock); } else { } result = TRUE; _g_free0 (args); _g_match_info_unref0 (i_del); _g_match_info_unref0 (i_new); _g_regex_unref0 (r_del); _g_regex_unref0 (r_neW); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); return result; } else { GRegex* _tmp61_ = NULL; const gchar* _tmp62_ = NULL; GMatchInfo* _tmp63_ = NULL; gboolean _tmp64_ = FALSE; _tmp61_ = r_error; _tmp62_ = tags; _tmp64_ = g_regex_match (_tmp61_, _tmp62_, 0, &_tmp63_); _g_match_info_unref0 (i_args); i_args = _tmp63_; if (_tmp64_) { gchar* args = NULL; GMatchInfo* _tmp65_ = NULL; gchar* _tmp66_ = NULL; gboolean _tmp67_ = FALSE; const gchar* _tmp68_ = NULL; const gchar* _tmp69_ = NULL; const gchar* _tmp70_ = NULL; gboolean _tmp71_ = FALSE; _tmp65_ = i_args; _tmp66_ = g_match_info_fetch (_tmp65_, 1); args = _tmp66_; _tmp68_ = args; _tmp69_ = account; _tmp70_ = host; _tmp71_ = smd_applet_eval_smd_loop_error_message (self, _tmp68_, _tmp69_, _tmp70_, &_inner_error_); _tmp67_ = _tmp71_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (args); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch11_g_regex_error; } _g_free0 (args); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } result = _tmp67_; _g_free0 (args); _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); return result; } else { FILE* _tmp72_ = NULL; const gchar* _tmp73_ = NULL; _tmp72_ = stderr; _tmp73_ = s; fprintf (_tmp72_, "unhandled smd-loop message: %s\n", _tmp73_); result = TRUE; _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); return result; } } _g_regex_unref0 (r_error); _g_regex_unref0 (r_stats); _g_match_info_unref0 (i_args); _g_free0 (tags); _g_free0 (host); _g_free0 (account); _g_regex_unref0 (r_skip); _g_regex_unref0 (r_tags); _g_match_info_unref0 (info); } goto __finally11; __catch11_g_regex_error: { GError* e = NULL; FILE* _tmp74_ = NULL; GError* _tmp75_ = NULL; const gchar* _tmp76_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp74_ = stderr; _tmp75_ = e; _tmp76_ = _tmp75_->message; fprintf (_tmp74_, "%s\n", _tmp76_); _g_error_free0 (e); } __finally11: if (G_UNLIKELY (_inner_error_ != NULL)) { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } result = TRUE; return result; } static void ___lambda7_ (smdApplet* self) { setpgid ((pid_t) 0, (pid_t) 0); } static void ____lambda7__gspawn_child_setup_func (gpointer self) { ___lambda7_ ((smdApplet*) self); } static gboolean smd_applet_run_smd_loop (smdApplet* self, GError** error) { gboolean result = FALSE; gchar** cmd = NULL; const gchar* _tmp0_ = NULL; gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; gchar** _tmp3_ = NULL; gint cmd_length1 = 0; gint _cmd_size_ = 0; gint child_in = 0; gint child_out = 0; gint child_err = 0; gchar* buff = NULL; gchar* _tmp4_ = NULL; gint buff_length1 = 0; gint _buff_size_ = 0; GSpawnFlags flags = 0; gboolean rc = FALSE; const gchar* _tmp5_ = NULL; gchar* _tmp6_ = NULL; gchar* _tmp7_ = NULL; GError * _inner_error_ = NULL; g_return_val_if_fail (self != NULL, FALSE); _tmp0_ = smd_applet_smd_loop_cmd; _tmp1_ = g_strdup (_tmp0_); _tmp2_ = g_strdup ("-v"); _tmp3_ = g_new0 (gchar*, 2 + 1); _tmp3_[0] = _tmp1_; _tmp3_[1] = _tmp2_; cmd = _tmp3_; cmd_length1 = 2; _cmd_size_ = cmd_length1; _tmp4_ = g_new0 (gchar, 10240); buff = _tmp4_; buff_length1 = 10240; _buff_size_ = buff_length1; flags = 0; _tmp5_ = smd_applet_smd_loop_cmd; _tmp6_ = g_strdup_printf ("spawning %s\n", _tmp5_); _tmp7_ = _tmp6_; debug (_tmp7_); _g_free0 (_tmp7_); { gboolean _tmp8_ = FALSE; gchar** _tmp9_ = NULL; gint _tmp9__length1 = 0; GSpawnFlags _tmp10_ = 0; GPid _tmp11_ = 0; gint _tmp12_ = 0; gint _tmp13_ = 0; gint _tmp14_ = 0; gboolean _tmp15_ = FALSE; _tmp9_ = cmd; _tmp9__length1 = cmd_length1; _tmp10_ = flags; _tmp15_ = g_spawn_async_with_pipes (NULL, _tmp9_, NULL, _tmp10_, ____lambda7__gspawn_child_setup_func, self, &_tmp11_, &_tmp12_, &_tmp13_, &_tmp14_, &_inner_error_); self->priv->pid = _tmp11_; child_in = _tmp12_; child_out = _tmp13_; child_err = _tmp14_; _tmp8_ = _tmp15_; if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch12_g_error; } rc = _tmp8_; } goto __finally12; __catch12_g_error: { GError* e = NULL; FILE* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; gchar* _tmp18_ = NULL; gchar* _tmp19_ = NULL; gchar* _tmp20_ = NULL; gchar* _tmp21_ = NULL; GError* _tmp22_ = NULL; const gchar* _tmp23_ = NULL; gchar* _tmp24_ = NULL; gchar* _tmp25_ = NULL; gchar* _tmp26_ = NULL; gchar* _tmp27_ = NULL; GError* _tmp28_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp16_ = stderr; _tmp17_ = smd_applet_smd_loop_cmd; _tmp18_ = g_strconcat ("Unable to execute ", _tmp17_, NULL); _tmp19_ = _tmp18_; _tmp20_ = g_strconcat (_tmp19_, ": ", NULL); _tmp21_ = _tmp20_; _tmp22_ = e; _tmp23_ = _tmp22_->message; _tmp24_ = g_strconcat (_tmp21_, _tmp23_, NULL); _tmp25_ = _tmp24_; _tmp26_ = g_strconcat (_tmp25_, "\n", NULL); _tmp27_ = _tmp26_; fprintf (_tmp16_, "%s", _tmp27_); _g_free0 (_tmp27_); _g_free0 (_tmp25_); _g_free0 (_tmp21_); _g_free0 (_tmp19_); _tmp28_ = g_error_new_literal (EXIT, EXIT_ABORT, "Unable to run smd-loop"); _inner_error_ = _tmp28_; _g_error_free0 (e); goto __finally12; } __finally12: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); return FALSE; } else { buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } if (rc) { FILE* input = NULL; gint _tmp29_ = 0; FILE* _tmp30_ = NULL; gchar* s = NULL; gboolean goon = FALSE; const gchar* _tmp48_ = NULL; GPid _tmp58_ = 0; GPid _tmp59_ = 0; _tmp29_ = child_out; _tmp30_ = fdopen (_tmp29_, "r"); input = _tmp30_; s = NULL; goon = TRUE; while (TRUE) { gboolean _tmp31_ = FALSE; gboolean _tmp32_ = FALSE; gboolean _tmp33_ = FALSE; const gchar* _tmp40_ = NULL; gchar* _tmp41_ = NULL; gchar* _tmp42_ = NULL; const gchar* _tmp43_ = NULL; gboolean _tmp44_ = FALSE; gboolean _tmp45_ = FALSE; gchar* _tmp46_ = NULL; gchar* _tmp47_ = NULL; _tmp33_ = goon; if (_tmp33_) { FILE* _tmp34_ = NULL; gchar* _tmp35_ = NULL; gint _tmp35__length1 = 0; const gchar* _tmp36_ = NULL; gchar* _tmp37_ = NULL; const gchar* _tmp38_ = NULL; _tmp34_ = input; _tmp35_ = buff; _tmp35__length1 = buff_length1; _tmp36_ = fgets (_tmp35_, _tmp35__length1, _tmp34_); _tmp37_ = g_strdup (_tmp36_); _g_free0 (s); s = _tmp37_; _tmp38_ = s; _tmp32_ = _tmp38_ != NULL; } else { _tmp32_ = FALSE; } if (_tmp32_) { gboolean _tmp39_ = FALSE; _tmp39_ = self->priv->thread_die; _tmp31_ = !_tmp39_; } else { _tmp31_ = FALSE; } if (!_tmp31_) { break; } _tmp40_ = s; _tmp41_ = g_strdup_printf ("smd-loop outputs: %s", _tmp40_); _tmp42_ = _tmp41_; debug (_tmp42_); _g_free0 (_tmp42_); _tmp43_ = s; _tmp44_ = smd_applet_eval_smd_loop_message (self, _tmp43_); goon = _tmp44_; _tmp45_ = goon; _tmp46_ = g_strdup_printf ("eval_smd_loop_message returned %d", (gint) _tmp45_); _tmp47_ = _tmp46_; debug (_tmp47_); _g_free0 (_tmp47_); } _tmp48_ = s; if (_tmp48_ != NULL) { FILE* _tmp49_ = NULL; gchar* _tmp50_ = NULL; gint _tmp50__length1 = 0; const gchar* _tmp51_ = NULL; gchar* _tmp52_ = NULL; const gchar* _tmp53_ = NULL; _tmp49_ = input; _tmp50_ = buff; _tmp50__length1 = buff_length1; _tmp51_ = fgets (_tmp50_, _tmp50__length1, _tmp49_); _tmp52_ = g_strdup (_tmp51_); _g_free0 (s); s = _tmp52_; _tmp53_ = s; if (_tmp53_ != NULL) { FILE* _tmp54_ = NULL; FILE* _tmp55_ = NULL; GPid _tmp56_ = 0; const gchar* _tmp57_ = NULL; _tmp54_ = stderr; fprintf (_tmp54_, "smd-loop gave error tag but not died\n"); _tmp55_ = stderr; _tmp56_ = self->priv->pid; _tmp57_ = s; fprintf (_tmp55_, "smd-loop has pid %d and prints %s\n", (gint) _tmp56_, _tmp57_); } } _tmp58_ = self->priv->pid; g_spawn_close_pid (_tmp58_); _tmp59_ = self->priv->pid; kill ((pid_t) (-((gint) _tmp59_)), SIGTERM); result = goon; _g_free0 (s); _fclose0 (input); buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); return result; } else { FILE* _tmp60_ = NULL; const gchar* _tmp61_ = NULL; gchar* _tmp62_ = NULL; gchar* _tmp63_ = NULL; gchar* _tmp64_ = NULL; gchar* _tmp65_ = NULL; GError* _tmp66_ = NULL; _tmp60_ = stderr; _tmp61_ = smd_applet_smd_loop_cmd; _tmp62_ = g_strconcat ("Unable to execute ", _tmp61_, NULL); _tmp63_ = _tmp62_; _tmp64_ = g_strconcat (_tmp63_, "\n", NULL); _tmp65_ = _tmp64_; fprintf (_tmp60_, "%s", _tmp65_); _g_free0 (_tmp65_); _g_free0 (_tmp63_); _tmp66_ = g_error_new_literal (EXIT, EXIT_ABORT, "Unable to run smd-loop"); _inner_error_ = _tmp66_; if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); return FALSE; } else { buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } buff = (g_free (buff), NULL); cmd = (_vala_array_free (cmd, cmd_length1, (GDestroyNotify) g_free), NULL); } static void ______lambda15_ (smdApplet* self, NotifyNotification* not, const gchar* action) { GtkWindow* _tmp0_ = NULL; g_return_if_fail (not != NULL); g_return_if_fail (action != NULL); _tmp0_ = self->priv->err_win; gtk_window_reshow_with_initial_size (_tmp0_); } static void _______lambda15__notify_action_callback (NotifyNotification* notification, const gchar* action, gpointer self) { ______lambda15_ ((smdApplet*) self, notification, action); } static void ______lambda16_ (smdApplet* self, GtkButton* b) { gint cmd_status = 0; gchar* output = NULL; gchar* _error_ = NULL; GHashTable* _tmp0_ = NULL; GtkButton* _tmp1_ = NULL; gconstpointer _tmp2_ = NULL; gchar* _tmp3_ = NULL; gchar* _tmp4_ = NULL; GError * _inner_error_ = NULL; g_return_if_fail (b != NULL); _tmp0_ = self->priv->command_hash; _tmp1_ = b; _tmp2_ = g_hash_table_lookup (_tmp0_, (GtkWidget*) _tmp1_); _tmp3_ = g_strdup_printf ("executing: %s\n", (const gchar*) _tmp2_); _tmp4_ = _tmp3_; debug (_tmp4_); _g_free0 (_tmp4_); { GHashTable* _tmp5_ = NULL; GtkButton* _tmp6_ = NULL; gconstpointer _tmp7_ = NULL; gchar* _tmp8_ = NULL; gchar* _tmp9_ = NULL; gint _tmp10_ = 0; gboolean _tmp11_ = FALSE; gboolean _tmp12_ = FALSE; _tmp5_ = self->priv->command_hash; _tmp6_ = b; _tmp7_ = g_hash_table_lookup (_tmp5_, (GtkWidget*) _tmp6_); g_spawn_command_line_sync ((const gchar*) _tmp7_, &_tmp8_, &_tmp9_, &_tmp10_, &_inner_error_); _g_free0 (output); output = _tmp8_; _g_free0 (_error_); _error_ = _tmp9_; cmd_status = _tmp10_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_SPAWN_ERROR) { goto __catch16_g_spawn_error; } _g_free0 (_error_); _g_free0 (output); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _tmp12_ = WIFEXITED (cmd_status); if (_tmp12_) { gint _tmp13_ = 0; _tmp13_ = WEXITSTATUS (cmd_status); _tmp11_ = 0 == _tmp13_; } else { _tmp11_ = FALSE; } if (_tmp11_) { GtkButton* _tmp14_ = NULL; _tmp14_ = b; gtk_widget_set_sensitive ((GtkWidget*) _tmp14_, FALSE); } else { GtkMessageDialog* w = NULL; GtkWindow* _tmp15_ = NULL; GtkMessageDialog* _tmp16_ = NULL; GtkMessageDialog* _tmp17_ = NULL; GtkMessageDialog* _tmp18_ = NULL; _tmp15_ = self->priv->err_win; _tmp16_ = (GtkMessageDialog*) gtk_message_dialog_new (_tmp15_, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "An error occurred:\n%s\n%s", output, _error_); g_object_ref_sink (_tmp16_); w = _tmp16_; _tmp17_ = w; gtk_dialog_run ((GtkDialog*) _tmp17_); _tmp18_ = w; gtk_widget_destroy ((GtkWidget*) _tmp18_); _g_object_unref0 (w); } } goto __finally16; __catch16_g_spawn_error: { GError* e = NULL; FILE* _tmp19_ = NULL; GError* _tmp20_ = NULL; const gchar* _tmp21_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp19_ = stderr; _tmp20_ = e; _tmp21_ = _tmp20_->message; fprintf (_tmp19_, "Spawning: %s\n", _tmp21_); _g_error_free0 (e); } __finally16: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (_error_); _g_free0 (output); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _g_free0 (_error_); _g_free0 (output); } static void _______lambda16__gtk_button_clicked (GtkButton* _sender, gpointer self) { ______lambda16_ ((smdApplet*) self, _sender); } static gboolean smd_applet_eat_event (smdApplet* self) { gboolean result = FALSE; Event* e = NULL; gboolean _tmp0_ = FALSE; GeeArrayList* _tmp1_ = NULL; gint _tmp2_ = 0; gint _tmp3_ = 0; gboolean _tmp9_ = FALSE; Event* _tmp10_ = NULL; gboolean _tmp54_ = FALSE; Event* _tmp55_ = NULL; GError * _inner_error_ = NULL; g_return_val_if_fail (self != NULL, FALSE); e = NULL; _tmp0_ = self->priv->error_mode; if (_tmp0_) { result = TRUE; _event_unref0 (e); return result; } g_mutex_lock (&self->priv->events_lock); _tmp1_ = self->priv->events; _tmp2_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp1_); _tmp3_ = _tmp2_; if (_tmp3_ > 0) { GeeArrayList* _tmp4_ = NULL; gpointer _tmp5_ = NULL; GeeArrayList* _tmp6_ = NULL; gpointer _tmp7_ = NULL; Event* _tmp8_ = NULL; _tmp4_ = self->priv->events; _tmp5_ = gee_list_first ((GeeList*) _tmp4_); _event_unref0 (e); e = (Event*) _tmp5_; _tmp6_ = self->priv->events; _tmp7_ = gee_abstract_list_remove_at ((GeeAbstractList*) _tmp6_, 0); _tmp8_ = (Event*) _tmp7_; _event_unref0 (_tmp8_); } g_mutex_unlock (&self->priv->events_lock); _tmp10_ = e; if (_tmp10_ != NULL) { Event* _tmp11_ = NULL; const gchar* _tmp12_ = NULL; _tmp11_ = e; _tmp12_ = _tmp11_->message; _tmp9_ = _tmp12_ != NULL; } else { _tmp9_ = FALSE; } if (_tmp9_) { gboolean notify_on_newail = FALSE; GSettings* _tmp13_ = NULL; gboolean _tmp14_ = FALSE; gboolean _tmp15_ = FALSE; Event* _tmp16_ = NULL; gboolean _tmp17_ = FALSE; notify_on_newail = FALSE; _tmp13_ = self->priv->settings; _tmp14_ = g_settings_get_boolean (_tmp13_, SMD_APPLET_key_newmail); notify_on_newail = _tmp14_; _tmp16_ = e; _tmp17_ = _tmp16_->enter_network_error_mode; if (_tmp17_) { gboolean _tmp18_ = FALSE; _tmp18_ = self->priv->network_error_mode; _tmp15_ = _tmp18_; } else { _tmp15_ = FALSE; } if (_tmp15_) { } else { gboolean _tmp19_ = FALSE; gboolean _tmp20_ = FALSE; Event* _tmp21_ = NULL; gboolean _tmp22_ = FALSE; _tmp21_ = e; _tmp22_ = event_is_error_event (_tmp21_); if (!_tmp22_) { gboolean _tmp23_ = FALSE; _tmp23_ = notify_on_newail; _tmp20_ = _tmp23_; } else { _tmp20_ = FALSE; } if (_tmp20_) { _tmp19_ = TRUE; } else { gboolean _tmp24_ = FALSE; Event* _tmp25_ = NULL; gboolean _tmp26_ = FALSE; _tmp25_ = e; _tmp26_ = event_is_error_event (_tmp25_); if (_tmp26_) { Event* _tmp27_ = NULL; gboolean _tmp28_ = FALSE; _tmp27_ = e; _tmp28_ = _tmp27_->enter_network_error_mode; _tmp24_ = _tmp28_; } else { _tmp24_ = FALSE; } _tmp19_ = _tmp24_; } if (_tmp19_) { Event* _tmp29_ = NULL; const gchar* _tmp30_ = NULL; Event* _tmp31_ = NULL; const gchar* _tmp32_ = NULL; NotifyNotification* _tmp33_ = NULL; NotifyNotification* _tmp34_ = NULL; GVariant* _tmp35_ = NULL; GVariant* _tmp36_ = NULL; _tmp29_ = e; _tmp30_ = _tmp29_->message; _tmp31_ = e; _tmp32_ = _tmp31_->message_icon; _tmp33_ = notify_notification_new ("Syncmaildir", _tmp30_, _tmp32_); _g_object_unref0 (self->priv->notification); self->priv->notification = _tmp33_; _tmp34_ = self->priv->notification; _tmp35_ = g_variant_new_boolean (TRUE); g_variant_ref_sink (_tmp35_); _tmp36_ = _tmp35_; notify_notification_set_hint (_tmp34_, "transient", _tmp36_); _g_variant_unref0 (_tmp36_); { NotifyNotification* _tmp37_ = NULL; _tmp37_ = self->priv->notification; notify_notification_show (_tmp37_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch13_g_error; } } goto __finally13; __catch13_g_error: { GError* e = NULL; FILE* _tmp38_ = NULL; GError* _tmp39_ = NULL; const gchar* _tmp40_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp38_ = stderr; _tmp39_ = e; _tmp40_ = _tmp39_->message; fprintf (_tmp38_, "%s\n", _tmp40_); _g_error_free0 (e); } __finally13: if (G_UNLIKELY (_inner_error_ != NULL)) { _event_unref0 (e); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } else { Event* _tmp41_ = NULL; gboolean _tmp42_ = FALSE; _tmp41_ = e; _tmp42_ = event_is_error_event (_tmp41_); if (_tmp42_) { Event* _tmp43_ = NULL; const gchar* _tmp44_ = NULL; Event* _tmp45_ = NULL; const gchar* _tmp46_ = NULL; NotifyNotification* _tmp47_ = NULL; NotifyNotification* _tmp48_ = NULL; NotifyNotification* _tmp49_ = NULL; _tmp43_ = e; _tmp44_ = _tmp43_->message; _tmp45_ = e; _tmp46_ = _tmp45_->message_icon; _tmp47_ = notify_notification_new ("Syncmaildir", _tmp44_, _tmp46_); _g_object_unref0 (self->priv->notification); self->priv->notification = _tmp47_; _tmp48_ = self->priv->notification; notify_notification_set_timeout (_tmp48_, 0); _tmp49_ = self->priv->notification; notify_notification_add_action (_tmp49_, "clicked", "Handle error", _______lambda15__notify_action_callback, g_object_ref (self), g_object_unref); { NotifyNotification* _tmp50_ = NULL; _tmp50_ = self->priv->notification; notify_notification_show (_tmp50_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch14_g_error; } } goto __finally14; __catch14_g_error: { GError* e = NULL; FILE* _tmp51_ = NULL; GError* _tmp52_ = NULL; const gchar* _tmp53_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp51_ = stderr; _tmp52_ = e; _tmp53_ = _tmp52_->message; fprintf (_tmp51_, "%s\n", _tmp53_); _g_error_free0 (e); } __finally14: if (G_UNLIKELY (_inner_error_ != NULL)) { _event_unref0 (e); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } } } } } _tmp55_ = e; if (_tmp55_ != NULL) { Event* _tmp56_ = NULL; gboolean _tmp57_ = FALSE; _tmp56_ = e; _tmp57_ = _tmp56_->enter_error_mode; _tmp54_ = _tmp57_; } else { _tmp54_ = FALSE; } if (_tmp54_) { GtkStatusIcon* _tmp58_ = NULL; GtkStatusIcon* _tmp59_ = NULL; gboolean _tmp60_ = FALSE; GtkLabel* l_ctx = NULL; GtkBuilder* _tmp62_ = NULL; GObject* _tmp63_ = NULL; GtkLabel* _tmp64_ = NULL; GtkLabel* l_cause = NULL; GtkBuilder* _tmp65_ = NULL; GObject* _tmp66_ = NULL; GtkLabel* _tmp67_ = NULL; GtkLabel* _tmp68_ = NULL; Event* _tmp69_ = NULL; const gchar* _tmp70_ = NULL; GtkLabel* _tmp71_ = NULL; Event* _tmp72_ = NULL; const gchar* _tmp73_ = NULL; GHashTable* _tmp74_ = NULL; GtkGrid* vb = NULL; GtkBuilder* _tmp75_ = NULL; GObject* _tmp76_ = NULL; GtkGrid* _tmp77_ = NULL; GtkGrid* _tmp78_ = NULL; GList* _tmp79_ = NULL; Event* _tmp83_ = NULL; const gchar* _tmp84_ = NULL; Event* _tmp91_ = NULL; const gchar* _tmp92_ = NULL; Event* _tmp120_ = NULL; GeeArrayList* _tmp121_ = NULL; GtkWidget* x = NULL; GtkBuilder* _tmp169_ = NULL; GObject* _tmp170_ = NULL; GtkWidget* _tmp171_ = NULL; GtkWidget* _tmp172_ = NULL; Event* _tmp173_ = NULL; const gchar* _tmp174_ = NULL; GtkBuilder* _tmp175_ = NULL; GObject* _tmp176_ = NULL; GtkWidget* _tmp177_ = NULL; GtkWidget* _tmp178_ = NULL; Event* _tmp179_ = NULL; const gchar* _tmp180_ = NULL; GtkBuilder* _tmp181_ = NULL; GObject* _tmp182_ = NULL; GtkWidget* _tmp183_ = NULL; GtkWidget* _tmp184_ = NULL; Event* _tmp185_ = NULL; GeeArrayList* _tmp186_ = NULL; gint _tmp187_ = 0; gint _tmp188_ = 0; _tmp58_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp58_, "error"); _tmp59_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp59_, "smd-applet encountered an error"); self->priv->error_mode = TRUE; _tmp60_ = self->priv->notification_server_has_persistence; if (!_tmp60_) { GtkStatusIcon* _tmp61_ = NULL; _tmp61_ = self->priv->si; gtk_status_icon_set_visible (_tmp61_, TRUE); } _tmp62_ = self->priv->builder; _tmp63_ = gtk_builder_get_object (_tmp62_, "lContext"); _tmp64_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp63_, gtk_label_get_type ()) ? ((GtkLabel*) _tmp63_) : NULL); l_ctx = _tmp64_; _tmp65_ = self->priv->builder; _tmp66_ = gtk_builder_get_object (_tmp65_, "lCause"); _tmp67_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp66_, gtk_label_get_type ()) ? ((GtkLabel*) _tmp66_) : NULL); l_cause = _tmp67_; _tmp68_ = l_ctx; _tmp69_ = e; _tmp70_ = _tmp69_->context; gtk_label_set_text (_tmp68_, _tmp70_); _tmp71_ = l_cause; _tmp72_ = e; _tmp73_ = _tmp72_->cause; gtk_label_set_text (_tmp71_, _tmp73_); _tmp74_ = self->priv->command_hash; g_hash_table_remove_all (_tmp74_); _tmp75_ = self->priv->builder; _tmp76_ = gtk_builder_get_object (_tmp75_, "vbRun"); _tmp77_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp76_, gtk_grid_get_type ()) ? ((GtkGrid*) _tmp76_) : NULL); vb = _tmp77_; _tmp78_ = vb; _tmp79_ = gtk_container_get_children ((GtkContainer*) _tmp78_); { GList* w_collection = NULL; GList* w_it = NULL; w_collection = _tmp79_; for (w_it = w_collection; w_it != NULL; w_it = w_it->next) { GtkWidget* _tmp80_ = NULL; GtkWidget* w = NULL; _tmp80_ = _g_object_ref0 ((GtkWidget*) w_it->data); w = _tmp80_; { GtkGrid* _tmp81_ = NULL; GtkWidget* _tmp82_ = NULL; _tmp81_ = vb; _tmp82_ = w; gtk_container_remove ((GtkContainer*) _tmp81_, _tmp82_); _g_object_unref0 (w); } } _g_list_free0 (w_collection); } _tmp83_ = e; _tmp84_ = _tmp83_->permissions; if (_tmp84_ != NULL) { GtkLabel* l = NULL; GtkBuilder* _tmp85_ = NULL; GObject* _tmp86_ = NULL; GtkLabel* _tmp87_ = NULL; GtkLabel* _tmp88_ = NULL; Event* _tmp89_ = NULL; const gchar* _tmp90_ = NULL; _tmp85_ = self->priv->builder; _tmp86_ = gtk_builder_get_object (_tmp85_, "lPermissions"); _tmp87_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp86_, gtk_label_get_type ()) ? ((GtkLabel*) _tmp86_) : NULL); l = _tmp87_; _tmp88_ = l; _tmp89_ = e; _tmp90_ = _tmp89_->permissions; gtk_label_set_text (_tmp88_, _tmp90_); _g_object_unref0 (l); } _tmp91_ = e; _tmp92_ = _tmp91_->mail_name; if (_tmp92_ != NULL) { GtkEntry* fn = NULL; GtkBuilder* _tmp93_ = NULL; GObject* _tmp94_ = NULL; GtkEntry* _tmp95_ = NULL; GtkEntry* _tmp96_ = NULL; Event* _tmp97_ = NULL; const gchar* _tmp98_ = NULL; GtkTextView* l = NULL; GtkBuilder* _tmp99_ = NULL; GObject* _tmp100_ = NULL; GtkTextView* _tmp101_ = NULL; GtkTextBuffer* b = NULL; GtkTextView* _tmp102_ = NULL; GtkTextBuffer* _tmp103_ = NULL; GtkTextBuffer* _tmp104_ = NULL; GtkTextBuffer* _tmp105_ = NULL; Event* _tmp106_ = NULL; const gchar* _tmp107_ = NULL; GtkTextIter it = {0}; GtkTextIter subj = {0}; GtkTextBuffer* _tmp108_ = NULL; GtkTextIter _tmp109_ = {0}; GtkTextIter _tmp110_ = {0}; gboolean _tmp111_ = FALSE; _tmp93_ = self->priv->builder; _tmp94_ = gtk_builder_get_object (_tmp93_, "eMailName"); _tmp95_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp94_, gtk_entry_get_type ()) ? ((GtkEntry*) _tmp94_) : NULL); fn = _tmp95_; _tmp96_ = fn; _tmp97_ = e; _tmp98_ = _tmp97_->mail_name; gtk_entry_set_text (_tmp96_, _tmp98_); _tmp99_ = self->priv->builder; _tmp100_ = gtk_builder_get_object (_tmp99_, "tvMail"); _tmp101_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp100_, gtk_text_view_get_type ()) ? ((GtkTextView*) _tmp100_) : NULL); l = _tmp101_; _tmp102_ = l; _tmp103_ = gtk_text_view_get_buffer (_tmp102_); _tmp104_ = _g_object_ref0 (_tmp103_); b = _tmp104_; _tmp105_ = b; _tmp106_ = e; _tmp107_ = _tmp106_->mail_body; gtk_text_buffer_set_text (_tmp105_, _tmp107_, -1); _tmp108_ = b; gtk_text_buffer_get_start_iter (_tmp108_, &_tmp109_); it = _tmp109_; _tmp111_ = gtk_text_iter_forward_search (&it, "Subject:", GTK_TEXT_SEARCH_TEXT_ONLY, &_tmp110_, NULL, NULL); subj = _tmp110_; if (_tmp111_) { GtkTextMark* insert = NULL; GtkTextBuffer* _tmp112_ = NULL; GtkTextMark* _tmp113_ = NULL; GtkTextMark* _tmp114_ = NULL; GtkTextBuffer* _tmp115_ = NULL; GtkTextIter _tmp116_ = {0}; GtkTextIter _tmp117_ = {0}; GtkTextView* _tmp118_ = NULL; GtkTextMark* _tmp119_ = NULL; _tmp112_ = b; _tmp113_ = gtk_text_buffer_get_insert (_tmp112_); _tmp114_ = _g_object_ref0 (_tmp113_); insert = _tmp114_; _tmp115_ = b; _tmp116_ = subj; _tmp117_ = subj; gtk_text_buffer_select_range (_tmp115_, &_tmp116_, &_tmp117_); _tmp118_ = l; _tmp119_ = insert; gtk_text_view_scroll_to_mark (_tmp118_, _tmp119_, 0.0, TRUE, 0.0, 0.0); _g_object_unref0 (insert); } _g_object_unref0 (b); _g_object_unref0 (l); _g_object_unref0 (fn); } _tmp120_ = e; _tmp121_ = _tmp120_->commands; if (_tmp121_ != NULL) { { GeeArrayList* _command_list = NULL; Event* _tmp122_ = NULL; GeeArrayList* _tmp123_ = NULL; GeeArrayList* _tmp124_ = NULL; gint _command_size = 0; GeeArrayList* _tmp125_ = NULL; gint _tmp126_ = 0; gint _tmp127_ = 0; gint _command_index = 0; _tmp122_ = e; _tmp123_ = _tmp122_->commands; _tmp124_ = _g_object_ref0 (_tmp123_); _command_list = _tmp124_; _tmp125_ = _command_list; _tmp126_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp125_); _tmp127_ = _tmp126_; _command_size = _tmp127_; _command_index = -1; while (TRUE) { gint _tmp128_ = 0; gint _tmp129_ = 0; gint _tmp130_ = 0; gchar* command = NULL; GeeArrayList* _tmp131_ = NULL; gint _tmp132_ = 0; gpointer _tmp133_ = NULL; GtkGrid* hb = NULL; GtkGrid* _tmp134_ = NULL; GtkGrid* _tmp135_ = NULL; GtkGrid* _tmp136_ = NULL; gchar* nice_command = NULL; GtkLabel* lbl = NULL; const gchar* _tmp152_ = NULL; GtkLabel* _tmp153_ = NULL; GtkLabel* _tmp154_ = NULL; GtkButton* but = NULL; GtkButton* _tmp155_ = NULL; GHashTable* _tmp156_ = NULL; GtkButton* _tmp157_ = NULL; GtkWidget* _tmp158_ = NULL; const gchar* _tmp159_ = NULL; gchar* _tmp160_ = NULL; GtkButton* _tmp161_ = NULL; GtkGrid* _tmp162_ = NULL; GtkLabel* _tmp163_ = NULL; GtkGrid* _tmp164_ = NULL; GtkButton* _tmp165_ = NULL; GtkGrid* _tmp166_ = NULL; GtkGrid* _tmp167_ = NULL; GtkGrid* _tmp168_ = NULL; _tmp128_ = _command_index; _command_index = _tmp128_ + 1; _tmp129_ = _command_index; _tmp130_ = _command_size; if (!(_tmp129_ < _tmp130_)) { break; } _tmp131_ = _command_list; _tmp132_ = _command_index; _tmp133_ = gee_abstract_list_get ((GeeAbstractList*) _tmp131_, _tmp132_); command = (gchar*) _tmp133_; _tmp134_ = (GtkGrid*) gtk_grid_new (); g_object_ref_sink (_tmp134_); hb = _tmp134_; _tmp135_ = hb; gtk_grid_set_column_homogeneous (_tmp135_, FALSE); _tmp136_ = hb; gtk_grid_set_column_spacing (_tmp136_, (guint) 10); { GMatchInfo* i_mailto = NULL; GRegex* mailto_rex = NULL; GRegex* _tmp137_ = NULL; GRegex* _tmp138_ = NULL; const gchar* _tmp139_ = NULL; GMatchInfo* _tmp140_ = NULL; gboolean _tmp141_ = FALSE; _tmp137_ = g_regex_new ("^gnome-open..mailto:", 0, 0, &_inner_error_); mailto_rex = _tmp137_; if (G_UNLIKELY (_inner_error_ != NULL)) { _g_match_info_unref0 (i_mailto); if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch15_g_regex_error; } _g_match_info_unref0 (i_mailto); _g_free0 (nice_command); _g_object_unref0 (hb); _g_free0 (command); _g_object_unref0 (_command_list); _g_object_unref0 (vb); _g_object_unref0 (l_cause); _g_object_unref0 (l_ctx); _event_unref0 (e); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp138_ = mailto_rex; _tmp139_ = command; _tmp141_ = g_regex_match (_tmp138_, _tmp139_, 0, &_tmp140_); _g_match_info_unref0 (i_mailto); i_mailto = _tmp140_; if (_tmp141_) { const gchar* _tmp142_ = NULL; gchar* _tmp143_ = NULL; gchar* _tmp144_ = NULL; gchar* _tmp145_ = NULL; gchar* _tmp146_ = NULL; gchar* _tmp147_ = NULL; _tmp142_ = command; _tmp143_ = g_uri_unescape_string (_tmp142_, NULL); _tmp144_ = _tmp143_; _tmp145_ = string_substring (_tmp144_, (glong) 12, (glong) 70); _tmp146_ = _tmp145_; _tmp147_ = g_strconcat (_tmp146_, "...", NULL); _g_free0 (nice_command); nice_command = _tmp147_; _g_free0 (_tmp146_); _g_free0 (_tmp144_); } else { const gchar* _tmp148_ = NULL; gchar* _tmp149_ = NULL; _tmp148_ = command; _tmp149_ = g_strdup (_tmp148_); _g_free0 (nice_command); nice_command = _tmp149_; } _g_regex_unref0 (mailto_rex); _g_match_info_unref0 (i_mailto); } goto __finally15; __catch15_g_regex_error: { GError* e = NULL; const gchar* _tmp150_ = NULL; gchar* _tmp151_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp150_ = command; _tmp151_ = g_strdup (_tmp150_); _g_free0 (nice_command); nice_command = _tmp151_; _g_error_free0 (e); } __finally15: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (nice_command); _g_object_unref0 (hb); _g_free0 (command); _g_object_unref0 (_command_list); _g_object_unref0 (vb); _g_object_unref0 (l_cause); _g_object_unref0 (l_ctx); _event_unref0 (e); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return FALSE; } _tmp152_ = nice_command; _tmp153_ = (GtkLabel*) gtk_label_new (_tmp152_); g_object_ref_sink (_tmp153_); lbl = _tmp153_; _tmp154_ = lbl; gtk_misc_set_alignment ((GtkMisc*) _tmp154_, 0.0f, 0.5f); _tmp155_ = (GtkButton*) gtk_button_new_from_stock ("gtk-execute"); g_object_ref_sink (_tmp155_); but = _tmp155_; _tmp156_ = self->priv->command_hash; _tmp157_ = but; _tmp158_ = _g_object_ref0 ((GtkWidget*) _tmp157_); _tmp159_ = command; _tmp160_ = g_strdup (_tmp159_); g_hash_table_insert (_tmp156_, _tmp158_, _tmp160_); _tmp161_ = but; g_signal_connect_object (_tmp161_, "clicked", (GCallback) _______lambda16__gtk_button_clicked, self, 0); _tmp162_ = hb; _tmp163_ = lbl; gtk_grid_attach (_tmp162_, (GtkWidget*) _tmp163_, 1, 0, 1, 1); _tmp164_ = hb; _tmp165_ = but; gtk_grid_attach (_tmp164_, (GtkWidget*) _tmp165_, 0, 0, 1, 1); _tmp166_ = vb; _tmp167_ = hb; gtk_container_add ((GtkContainer*) _tmp166_, (GtkWidget*) _tmp167_); _tmp168_ = hb; gtk_widget_show_all ((GtkWidget*) _tmp168_); _g_object_unref0 (but); _g_object_unref0 (lbl); _g_free0 (nice_command); _g_object_unref0 (hb); _g_free0 (command); } _g_object_unref0 (_command_list); } } _tmp169_ = self->priv->builder; _tmp170_ = gtk_builder_get_object (_tmp169_, "fDisplayPermissions"); _tmp171_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp170_, gtk_widget_get_type ()) ? ((GtkWidget*) _tmp170_) : NULL); x = _tmp171_; _tmp172_ = x; _tmp173_ = e; _tmp174_ = _tmp173_->permissions; gtk_widget_set_visible (_tmp172_, _tmp174_ != NULL); _tmp175_ = self->priv->builder; _tmp176_ = gtk_builder_get_object (_tmp175_, "fDisplayMail"); _tmp177_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp176_, gtk_widget_get_type ()) ? ((GtkWidget*) _tmp176_) : NULL); _g_object_unref0 (x); x = _tmp177_; _tmp178_ = x; _tmp179_ = e; _tmp180_ = _tmp179_->mail_name; gtk_widget_set_visible (_tmp178_, _tmp180_ != NULL); _tmp181_ = self->priv->builder; _tmp182_ = gtk_builder_get_object (_tmp181_, "fRun"); _tmp183_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp182_, gtk_widget_get_type ()) ? ((GtkWidget*) _tmp182_) : NULL); _g_object_unref0 (x); x = _tmp183_; _tmp184_ = x; _tmp185_ = e; _tmp186_ = _tmp185_->commands; _tmp187_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp186_); _tmp188_ = _tmp187_; gtk_widget_set_visible (_tmp184_, _tmp188_ > 0); _g_object_unref0 (x); _g_object_unref0 (vb); _g_object_unref0 (l_cause); _g_object_unref0 (l_ctx); } else { gboolean _tmp189_ = FALSE; Event* _tmp190_ = NULL; _tmp190_ = e; if (_tmp190_ != NULL) { Event* _tmp191_ = NULL; gboolean _tmp192_ = FALSE; _tmp191_ = e; _tmp192_ = _tmp191_->enter_network_error_mode; _tmp189_ = _tmp192_; } else { _tmp189_ = FALSE; } if (_tmp189_) { GtkStatusIcon* _tmp193_ = NULL; GtkStatusIcon* _tmp194_ = NULL; self->priv->network_error_mode = TRUE; _tmp193_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp193_, "dialog-warning"); _tmp194_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp194_, "Network error"); } else { Event* _tmp195_ = NULL; _tmp195_ = e; if (_tmp195_ != NULL) { GtkStatusIcon* _tmp196_ = NULL; GtkStatusIcon* _tmp197_ = NULL; self->priv->network_error_mode = FALSE; _tmp196_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp196_, "mail-send-receive"); _tmp197_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp197_, "smd-applet is running"); } } } result = TRUE; _event_unref0 (e); return result; } static void smd_applet_close_err_action (smdApplet* self, GtkButton* b) { g_return_if_fail (self != NULL); g_return_if_fail (b != NULL); smd_applet_reset_to_regular_run (self, FALSE); } static gboolean smd_applet_close_err_event (smdApplet* self, GdkEventAny* e) { gboolean result = FALSE; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (e != NULL, FALSE); smd_applet_reset_to_regular_run (self, FALSE); result = TRUE; return result; } static gpointer _g_thread_ref0 (gpointer self) { return self ? g_thread_ref (self) : NULL; } static void smd_applet_reset_to_regular_run (smdApplet* self, gboolean force) { GtkWindow* _tmp0_ = NULL; GtkStatusIcon* _tmp1_ = NULL; GtkStatusIcon* _tmp2_ = NULL; GThread* _tmp3_ = NULL; gboolean _tmp6_ = FALSE; g_return_if_fail (self != NULL); _tmp0_ = self->priv->err_win; gtk_widget_hide ((GtkWidget*) _tmp0_); self->priv->error_mode = FALSE; _tmp1_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp1_, "smd-applet is running"); _tmp2_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp2_, "mail-send-receive"); debug ("joining smdThread"); _tmp3_ = self->priv->thread; if (_tmp3_ != NULL) { GThread* _tmp4_ = NULL; GThread* _tmp5_ = NULL; _tmp4_ = self->priv->thread; _tmp5_ = _g_thread_ref0 (_tmp4_); g_thread_join (_tmp5_); } self->priv->thread_die = FALSE; debug ("starting smdThread"); _tmp6_ = force; smd_applet_start_smdThread (self, _tmp6_); } static gboolean smd_applet_close_prefs_event (smdApplet* self, GdkEventAny* e) { gboolean result = FALSE; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (e != NULL, FALSE); smd_applet_close_prefs (self); result = TRUE; return result; } static void smd_applet_close_prefs (smdApplet* self) { GtkWindow* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; g_return_if_fail (self != NULL); _tmp0_ = self->priv->win; gtk_widget_hide ((GtkWidget*) _tmp0_); _tmp2_ = smd_applet_is_smd_stack_configured (self); if (_tmp2_) { gboolean _tmp3_ = FALSE; _tmp3_ = self->priv->config_wait_mode; _tmp1_ = _tmp3_; } else { _tmp1_ = FALSE; } if (_tmp1_) { GtkStatusIcon* _tmp4_ = NULL; self->priv->config_wait_mode = FALSE; _tmp4_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp4_, "mail-send-receive"); debug ("starting smdThread since smd stack is configured"); smd_applet_start_smdThread (self, FALSE); } } static void smd_applet_pause (smdApplet* self) { GPid _tmp0_ = 0; GtkStatusIcon* _tmp5_ = NULL; GtkStatusIcon* _tmp6_ = NULL; g_return_if_fail (self != NULL); debug ("enter pause mode"); _tmp0_ = self->priv->pid; if (((gint) _tmp0_) != 0) { GPid _tmp1_ = 0; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; GPid _tmp4_ = 0; _tmp1_ = self->priv->pid; _tmp2_ = g_strdup_printf ("sending SIGTERM to %d", -((gint) _tmp1_)); _tmp3_ = _tmp2_; debug (_tmp3_); _g_free0 (_tmp3_); _tmp4_ = self->priv->pid; kill ((pid_t) (-((gint) _tmp4_)), SIGTERM); } self->priv->thread_die = TRUE; _tmp5_ = self->priv->si; gtk_status_icon_set_from_stock (_tmp5_, "gtk-media-pause"); _tmp6_ = self->priv->si; gtk_status_icon_set_tooltip_text (_tmp6_, "smd-applet is paused"); } static void smd_applet_unpause (smdApplet* self) { g_return_if_fail (self != NULL); debug ("exit pause mode"); smd_applet_reset_to_regular_run (self, TRUE); } static gboolean smd_applet_is_smd_loop_configured (smdApplet* self) { gboolean result = FALSE; gboolean rc = FALSE; const gchar* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; GtkBox* l = NULL; GtkBuilder* _tmp2_ = NULL; GObject* _tmp3_ = NULL; GtkBox* _tmp4_ = NULL; gboolean _tmp5_ = FALSE; g_return_val_if_fail (self != NULL, FALSE); _tmp0_ = SMD_LOOP_CFG; _tmp1_ = g_file_test (_tmp0_, G_FILE_TEST_EXISTS); rc = _tmp1_; _tmp2_ = self->priv->builder; _tmp3_ = gtk_builder_get_object (_tmp2_, "bErrLoop"); _tmp4_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp3_, gtk_box_get_type ()) ? ((GtkBox*) _tmp3_) : NULL); l = _tmp4_; _tmp5_ = rc; if (!_tmp5_) { GtkBox* _tmp6_ = NULL; _tmp6_ = l; gtk_widget_show ((GtkWidget*) _tmp6_); } else { GtkBox* _tmp7_ = NULL; _tmp7_ = l; gtk_widget_hide ((GtkWidget*) _tmp7_); } result = rc; _g_object_unref0 (l); return result; } static gboolean smd_applet_is_smd_pushpull_configured (smdApplet* self) { gboolean result = FALSE; gboolean rc = FALSE; const gchar* _tmp0_ = NULL; gboolean _tmp1_ = FALSE; GtkBox* l = NULL; GtkBuilder* _tmp2_ = NULL; GObject* _tmp3_ = NULL; GtkBox* _tmp4_ = NULL; gboolean _tmp5_ = FALSE; g_return_val_if_fail (self != NULL, FALSE); _tmp0_ = SMD_PP_DEF_CFG; _tmp1_ = g_file_test (_tmp0_, G_FILE_TEST_EXISTS); rc = _tmp1_; _tmp2_ = self->priv->builder; _tmp3_ = gtk_builder_get_object (_tmp2_, "bErrPushPull"); _tmp4_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp3_, gtk_box_get_type ()) ? ((GtkBox*) _tmp3_) : NULL); l = _tmp4_; _tmp5_ = rc; if (!_tmp5_) { GtkBox* _tmp6_ = NULL; _tmp6_ = l; gtk_widget_show ((GtkWidget*) _tmp6_); } else { GtkBox* _tmp7_ = NULL; _tmp7_ = l; gtk_widget_hide ((GtkWidget*) _tmp7_); } result = rc; _g_object_unref0 (l); return result; } static gboolean smd_applet_is_smd_stack_configured (smdApplet* self) { gboolean result = FALSE; gboolean a = FALSE; gboolean _tmp0_ = FALSE; gboolean b = FALSE; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; gboolean _tmp3_ = FALSE; g_return_val_if_fail (self != NULL, FALSE); _tmp0_ = smd_applet_is_smd_loop_configured (self); a = _tmp0_; _tmp1_ = smd_applet_is_smd_pushpull_configured (self); b = _tmp1_; _tmp3_ = a; if (_tmp3_) { gboolean _tmp4_ = FALSE; _tmp4_ = b; _tmp2_ = _tmp4_; } else { _tmp2_ = FALSE; } result = _tmp2_; return result; } static void smd_applet_update_loglist (smdApplet* self) { GtkTextView* tv = NULL; GtkBuilder* _tmp0_ = NULL; GObject* _tmp1_ = NULL; GtkTextView* _tmp2_ = NULL; GtkTextBuffer* b = NULL; GtkTextView* _tmp3_ = NULL; GtkTextBuffer* _tmp4_ = NULL; GtkTextBuffer* _tmp5_ = NULL; GError * _inner_error_ = NULL; g_return_if_fail (self != NULL); _tmp0_ = self->priv->builder; _tmp1_ = gtk_builder_get_object (_tmp0_, "tvLog"); _tmp2_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp1_, gtk_text_view_get_type ()) ? ((GtkTextView*) _tmp1_) : NULL); tv = _tmp2_; _tmp3_ = tv; _tmp4_ = gtk_text_view_get_buffer (_tmp3_); _tmp5_ = _g_object_ref0 (_tmp4_); b = _tmp5_; { GDir* d = NULL; const gchar* _tmp6_ = NULL; GDir* _tmp7_ = NULL; gchar* file = NULL; GeeArrayList* new_lognames = NULL; void* _tmp8_ = NULL; GDestroyNotify _tmp9_ = NULL; GeeEqualDataFunc _tmp10_ = NULL; GeeArrayList* _tmp11_ = NULL; gboolean _tmp18_ = FALSE; GeeArrayList* _tmp19_ = NULL; GeeArrayList* _tmp20_ = NULL; gboolean _tmp21_ = FALSE; _tmp6_ = SMD_LOGS_DIR; _tmp7_ = g_dir_open (_tmp6_, (guint) 0, &_inner_error_); d = _tmp7_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_FILE_ERROR) { goto __catch17_g_file_error; } _g_object_unref0 (b); _g_object_unref0 (tv); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _tmp10_ = gee_functions_get_equal_func_for (G_TYPE_STRING, &_tmp8_, &_tmp9_); _tmp11_ = gee_array_list_new (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup, g_free, _tmp10_, _tmp8_, _tmp9_); new_lognames = _tmp11_; while (TRUE) { GDir* _tmp12_ = NULL; const gchar* _tmp13_ = NULL; gchar* _tmp14_ = NULL; const gchar* _tmp15_ = NULL; GeeArrayList* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; _tmp12_ = d; _tmp13_ = g_dir_read_name (_tmp12_); _tmp14_ = g_strdup (_tmp13_); _g_free0 (file); file = _tmp14_; _tmp15_ = file; if (!(_tmp15_ != NULL)) { break; } _tmp16_ = new_lognames; _tmp17_ = file; gee_abstract_collection_add ((GeeAbstractCollection*) _tmp16_, _tmp17_); } _tmp19_ = new_lognames; _tmp20_ = self->priv->lognames; _tmp21_ = gee_collection_contains_all ((GeeCollection*) _tmp19_, (GeeCollection*) _tmp20_); if (!_tmp21_) { _tmp18_ = TRUE; } else { GeeArrayList* _tmp22_ = NULL; GeeArrayList* _tmp23_ = NULL; gboolean _tmp24_ = FALSE; _tmp22_ = self->priv->lognames; _tmp23_ = new_lognames; _tmp24_ = gee_collection_contains_all ((GeeCollection*) _tmp22_, (GeeCollection*) _tmp23_); _tmp18_ = !_tmp24_; } if (_tmp18_) { GtkComboBoxText* _tmp25_ = NULL; GtkTreeModel* _tmp26_ = NULL; GeeArrayList* _tmp27_ = NULL; GeeArrayList* _tmp43_ = NULL; gint _tmp44_ = 0; gint _tmp45_ = 0; _tmp25_ = self->priv->cblogs; _tmp26_ = gtk_combo_box_get_model ((GtkComboBox*) _tmp25_); gtk_list_store_clear (G_TYPE_CHECK_INSTANCE_CAST (_tmp26_, gtk_list_store_get_type (), GtkListStore)); _tmp27_ = self->priv->lognames; gee_abstract_collection_clear ((GeeAbstractCollection*) _tmp27_); { GeeArrayList* _f_list = NULL; GeeArrayList* _tmp28_ = NULL; GeeArrayList* _tmp29_ = NULL; gint _f_size = 0; GeeArrayList* _tmp30_ = NULL; gint _tmp31_ = 0; gint _tmp32_ = 0; gint _f_index = 0; _tmp28_ = new_lognames; _tmp29_ = _g_object_ref0 (_tmp28_); _f_list = _tmp29_; _tmp30_ = _f_list; _tmp31_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp30_); _tmp32_ = _tmp31_; _f_size = _tmp32_; _f_index = -1; while (TRUE) { gint _tmp33_ = 0; gint _tmp34_ = 0; gint _tmp35_ = 0; gchar* f = NULL; GeeArrayList* _tmp36_ = NULL; gint _tmp37_ = 0; gpointer _tmp38_ = NULL; GeeArrayList* _tmp39_ = NULL; const gchar* _tmp40_ = NULL; GtkComboBoxText* _tmp41_ = NULL; const gchar* _tmp42_ = NULL; _tmp33_ = _f_index; _f_index = _tmp33_ + 1; _tmp34_ = _f_index; _tmp35_ = _f_size; if (!(_tmp34_ < _tmp35_)) { break; } _tmp36_ = _f_list; _tmp37_ = _f_index; _tmp38_ = gee_abstract_list_get ((GeeAbstractList*) _tmp36_, _tmp37_); f = (gchar*) _tmp38_; _tmp39_ = self->priv->lognames; _tmp40_ = f; gee_abstract_collection_add ((GeeAbstractCollection*) _tmp39_, _tmp40_); _tmp41_ = self->priv->cblogs; _tmp42_ = f; gtk_combo_box_text_append_text (_tmp41_, _tmp42_); _g_free0 (f); } _g_object_unref0 (_f_list); } _tmp43_ = self->priv->lognames; _tmp44_ = gee_abstract_collection_get_size ((GeeCollection*) _tmp43_); _tmp45_ = _tmp44_; if (_tmp45_ == 0) { GtkTextBuffer* _tmp46_ = NULL; const gchar* _tmp47_ = NULL; gchar* _tmp48_ = NULL; gchar* _tmp49_ = NULL; _tmp46_ = b; _tmp47_ = SMD_LOGS_DIR; _tmp48_ = g_strdup_printf ("No logs in %s", _tmp47_); _tmp49_ = _tmp48_; gtk_text_buffer_set_text (_tmp46_, _tmp49_, -1); _g_free0 (_tmp49_); } else { GtkComboBoxText* _tmp50_ = NULL; GtkComboBoxText* _tmp51_ = NULL; _tmp50_ = self->priv->cblogs; gtk_combo_box_set_title ((GtkComboBox*) _tmp50_, "Choose log file"); _tmp51_ = self->priv->cblogs; gtk_combo_box_set_active ((GtkComboBox*) _tmp51_, 0); } } _g_object_unref0 (new_lognames); _g_free0 (file); _g_dir_close0 (d); } goto __finally17; __catch17_g_file_error: { GError* e = NULL; GtkTextBuffer* _tmp52_ = NULL; const gchar* _tmp53_ = NULL; gchar* _tmp54_ = NULL; gchar* _tmp55_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp52_ = b; _tmp53_ = SMD_LOGS_DIR; _tmp54_ = g_strdup_printf ("Unable to list directory %s", _tmp53_); _tmp55_ = _tmp54_; gtk_text_buffer_set_text (_tmp52_, _tmp55_, -1); _g_free0 (_tmp55_); _g_error_free0 (e); } __finally17: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_object_unref0 (b); _g_object_unref0 (tv); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _g_object_unref0 (b); _g_object_unref0 (tv); } static void smd_applet_update_logcontents (smdApplet* self) { gint selected = 0; GtkComboBoxText* _tmp0_ = NULL; gint _tmp1_ = 0; gint _tmp2_ = 0; GError * _inner_error_ = NULL; g_return_if_fail (self != NULL); _tmp0_ = self->priv->cblogs; _tmp1_ = gtk_combo_box_get_active ((GtkComboBox*) _tmp0_); selected = _tmp1_; _tmp2_ = selected; if (_tmp2_ >= 0) { gchar* file = NULL; GeeArrayList* _tmp3_ = NULL; gint _tmp4_ = 0; gpointer _tmp5_ = NULL; gchar* content = NULL; _tmp3_ = self->priv->lognames; _tmp4_ = selected; _tmp5_ = gee_abstract_list_get ((GeeAbstractList*) _tmp3_, _tmp4_); file = (gchar*) _tmp5_; { gboolean _tmp6_ = FALSE; const gchar* _tmp7_ = NULL; const gchar* _tmp8_ = NULL; gchar* _tmp9_ = NULL; gchar* _tmp10_ = NULL; gchar* _tmp11_ = NULL; gboolean _tmp12_ = FALSE; gboolean _tmp13_ = FALSE; _tmp7_ = SMD_LOGS_DIR; _tmp8_ = file; _tmp9_ = g_strconcat (_tmp7_, _tmp8_, NULL); _tmp10_ = _tmp9_; _tmp12_ = g_file_get_contents (_tmp10_, &_tmp11_, NULL, &_inner_error_); _g_free0 (content); content = _tmp11_; _tmp13_ = _tmp12_; _g_free0 (_tmp10_); _tmp6_ = _tmp13_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_FILE_ERROR) { goto __catch18_g_file_error; } _g_free0 (content); _g_free0 (file); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } if (_tmp6_) { GtkTextView* tv = NULL; GtkBuilder* _tmp14_ = NULL; GObject* _tmp15_ = NULL; GtkTextView* _tmp16_ = NULL; GtkTextBuffer* b = NULL; GtkTextView* _tmp17_ = NULL; GtkTextBuffer* _tmp18_ = NULL; GtkTextBuffer* _tmp19_ = NULL; GtkTextIter end_iter = {0}; GtkTextIter start_iter = {0}; GtkTextBuffer* _tmp20_ = NULL; GtkTextIter _tmp21_ = {0}; GtkTextBuffer* _tmp22_ = NULL; GtkTextIter _tmp23_ = {0}; const gchar* _tmp24_ = NULL; GtkTextBuffer* _tmp25_ = NULL; GtkTextIter _tmp26_ = {0}; GtkTextIter _tmp27_ = {0}; gchar* _tmp28_ = NULL; gchar* _tmp29_ = NULL; gboolean _tmp30_ = FALSE; _tmp14_ = self->priv->builder; _tmp15_ = gtk_builder_get_object (_tmp14_, "tvLog"); _tmp16_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_TYPE (_tmp15_, gtk_text_view_get_type ()) ? ((GtkTextView*) _tmp15_) : NULL); tv = _tmp16_; _tmp17_ = tv; _tmp18_ = gtk_text_view_get_buffer (_tmp17_); _tmp19_ = _g_object_ref0 (_tmp18_); b = _tmp19_; _tmp20_ = b; gtk_text_buffer_get_end_iter (_tmp20_, &_tmp21_); end_iter = _tmp21_; _tmp22_ = b; gtk_text_buffer_get_start_iter (_tmp22_, &_tmp23_); start_iter = _tmp23_; _tmp24_ = content; _tmp25_ = b; _tmp26_ = start_iter; _tmp27_ = end_iter; _tmp28_ = gtk_text_buffer_get_text (_tmp25_, &_tmp26_, &_tmp27_, FALSE); _tmp29_ = _tmp28_; _tmp30_ = g_strcmp0 (_tmp24_, _tmp29_) != 0; _g_free0 (_tmp29_); if (_tmp30_) { GtkTextBuffer* _tmp31_ = NULL; const gchar* _tmp32_ = NULL; GtkTextBuffer* _tmp33_ = NULL; GtkTextIter _tmp34_ = {0}; GtkTextView* _tmp35_ = NULL; GtkTextIter _tmp36_ = {0}; _tmp31_ = b; _tmp32_ = content; gtk_text_buffer_set_text (_tmp31_, _tmp32_, -1); _tmp33_ = b; gtk_text_buffer_get_end_iter (_tmp33_, &_tmp34_); end_iter = _tmp34_; _tmp35_ = tv; _tmp36_ = end_iter; gtk_text_view_scroll_to_iter (_tmp35_, &_tmp36_, 0.0, TRUE, 0.0, 0.0); } _g_object_unref0 (b); _g_object_unref0 (tv); } else { FILE* _tmp37_ = NULL; const gchar* _tmp38_ = NULL; const gchar* _tmp39_ = NULL; gchar* _tmp40_ = NULL; gchar* _tmp41_ = NULL; _tmp37_ = stderr; _tmp38_ = SMD_LOGS_DIR; _tmp39_ = file; _tmp40_ = g_strconcat (_tmp38_, _tmp39_, NULL); _tmp41_ = _tmp40_; fprintf (_tmp37_, "Unable to read %s\n", _tmp41_); _g_free0 (_tmp41_); } } goto __finally18; __catch18_g_file_error: { GError* e = NULL; FILE* _tmp42_ = NULL; const gchar* _tmp43_ = NULL; const gchar* _tmp44_ = NULL; gchar* _tmp45_ = NULL; gchar* _tmp46_ = NULL; GError* _tmp47_ = NULL; const gchar* _tmp48_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp42_ = stderr; _tmp43_ = SMD_LOGS_DIR; _tmp44_ = file; _tmp45_ = g_strconcat (_tmp43_, _tmp44_, NULL); _tmp46_ = _tmp45_; _tmp47_ = e; _tmp48_ = _tmp47_->message; fprintf (_tmp42_, "Unable to read %s: %s\n", _tmp46_, _tmp48_); _g_free0 (_tmp46_); _g_error_free0 (e); } __finally18: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_free0 (content); _g_free0 (file); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _g_free0 (content); _g_free0 (file); } } static gboolean _smd_applet_eat_event_gsource_func (gpointer self) { gboolean result; result = smd_applet_eat_event ((smdApplet*) self); return result; } static gboolean _smd_applet_close_prefs_event_gtk_widget_delete_event (GtkWidget* _sender, GdkEventAny* event, gpointer self) { gboolean result; result = smd_applet_close_prefs_event ((smdApplet*) self, event); return result; } static gboolean _smd_applet_close_err_event_gtk_widget_delete_event (GtkWidget* _sender, GdkEventAny* event, gpointer self) { gboolean result; result = smd_applet_close_err_event ((smdApplet*) self, event); return result; } static void ___lambda17_ (smdApplet* self, NotifyNotification* n, const gchar* a) { GtkStatusIcon* _tmp0_ = NULL; g_return_if_fail (n != NULL); g_return_if_fail (a != NULL); _tmp0_ = self->priv->si; g_signal_emit_by_name (_tmp0_, "activate"); } static void ____lambda17__notify_action_callback (NotifyNotification* notification, const gchar* action, gpointer self) { ___lambda17_ ((smdApplet*) self, notification, action); } void smd_applet_start (smdApplet* self, GError** error) { gboolean _tmp0_ = FALSE; GtkWindow* _tmp1_ = NULL; GtkWindow* _tmp2_ = NULL; gboolean _tmp3_ = FALSE; GThread* _tmp18_ = NULL; GError * _inner_error_ = NULL; g_return_if_fail (self != NULL); g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) 1000, _smd_applet_eat_event_gsource_func, g_object_ref (self), g_object_unref); _tmp0_ = smd_applet_is_smd_stack_configured (self); if (_tmp0_) { smd_applet_start_smdThread (self, FALSE); } else { self->priv->config_wait_mode = TRUE; } _tmp1_ = self->priv->win; g_signal_connect_object ((GtkWidget*) _tmp1_, "delete-event", (GCallback) _smd_applet_close_prefs_event_gtk_widget_delete_event, self, 0); _tmp2_ = self->priv->err_win; g_signal_connect_object ((GtkWidget*) _tmp2_, "delete-event", (GCallback) _smd_applet_close_err_event_gtk_widget_delete_event, self, 0); _tmp3_ = self->priv->config_wait_mode; if (_tmp3_) { GtkStatusIcon* _tmp5_ = NULL; GtkStatusIcon* _tmp6_ = NULL; GtkStatusIcon* _tmp7_ = NULL; NotifyNotification* _tmp9_ = NULL; NotifyNotification* _tmp10_ = NULL; GVariant* _tmp11_ = NULL; GVariant* _tmp12_ = NULL; NotifyNotification* _tmp13_ = NULL; while (TRUE) { gboolean _tmp4_ = FALSE; _tmp4_ = gtk_events_pending (); if (!_tmp4_) { break; } gtk_main_iteration (); } _tmp5_ = self->priv->si; gtk_status_icon_set_visible (_tmp5_, FALSE); sleep ((guint) 5); _tmp6_ = self->priv->si; gtk_status_icon_set_visible (_tmp6_, TRUE); _tmp7_ = self->priv->si; gtk_status_icon_set_from_icon_name (_tmp7_, "error"); while (TRUE) { gboolean _tmp8_ = FALSE; _tmp8_ = gtk_events_pending (); if (!_tmp8_) { break; } gtk_main_iteration (); } _tmp9_ = notify_notification_new ("Syncmaildir", "Syncmaildir is not configured properly.", "dialog-error"); _g_object_unref0 (self->priv->notification); self->priv->notification = _tmp9_; _tmp10_ = self->priv->notification; _tmp11_ = g_variant_new_boolean (TRUE); g_variant_ref_sink (_tmp11_); _tmp12_ = _tmp11_; notify_notification_set_hint (_tmp10_, "transient", _tmp12_); _g_variant_unref0 (_tmp12_); _tmp13_ = self->priv->notification; notify_notification_add_action (_tmp13_, "configure", "Configure now", ____lambda17__notify_action_callback, g_object_ref (self), g_object_unref); { NotifyNotification* _tmp14_ = NULL; _tmp14_ = self->priv->notification; notify_notification_show (_tmp14_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { goto __catch19_g_error; } } goto __finally19; __catch19_g_error: { GError* e = NULL; FILE* _tmp15_ = NULL; GError* _tmp16_ = NULL; const gchar* _tmp17_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp15_ = stderr; _tmp16_ = e; _tmp17_ = _tmp16_->message; fprintf (_tmp15_, "%s\n", _tmp17_); _g_error_free0 (e); } __finally19: if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { g_propagate_error (error, _inner_error_); return; } else { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } } } g_application_run ((GApplication*) G_TYPE_CHECK_INSTANCE_CAST (self, gtk_application_get_type (), GtkApplication), 0, NULL); _tmp18_ = self->priv->thread; if (_tmp18_ != NULL) { GThread* _tmp19_ = NULL; GThread* _tmp20_ = NULL; _tmp19_ = self->priv->thread; _tmp20_ = _g_thread_ref0 (_tmp19_); g_thread_join (_tmp20_); } } static void smd_applet_class_init (smdAppletClass * klass) { gchar* _tmp0_ = NULL; smd_applet_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (smdAppletPrivate)); G_OBJECT_CLASS (klass)->finalize = smd_applet_finalize; _tmp0_ = g_strdup ("\n" \ "\t\n" \ "\t \n" \ "\t
\n" \ "\t \n" \ "\t _Quit" \ "\n" \ "\t app.quit\n" \ "\t \n" \ "\t
\n" \ "\t
\n" \ "\t
\n" \ "\t"); smd_applet_menu_ui = _tmp0_; } static void smd_applet_instance_init (smdApplet * self) { self->priv = SMD_APPLET_GET_PRIVATE (self); self->priv->builder = NULL; self->priv->si = NULL; self->priv->win = NULL; self->priv->err_win = NULL; self->priv->notebook = NULL; self->priv->sync_active = NULL; self->priv->cblogs = NULL; self->priv->lognames = NULL; self->priv->settings = NULL; self->priv->thread = NULL; self->priv->thread_die = FALSE; g_mutex_init (&self->priv->events_lock); self->priv->events = NULL; self->priv->error_mode = FALSE; self->priv->network_error_mode = FALSE; self->priv->command_hash = NULL; self->priv->net_manager = NULL; self->priv->notification = NULL; self->priv->notification_server_has_persistence = FALSE; } static void smd_applet_finalize (GObject* obj) { smdApplet * self; self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_SMD_APPLET, smdApplet); _g_object_unref0 (self->priv->builder); _g_object_unref0 (self->priv->si); _g_object_unref0 (self->priv->win); _g_object_unref0 (self->priv->err_win); _g_object_unref0 (self->priv->notebook); _g_object_unref0 (self->priv->sync_active); _g_object_unref0 (self->priv->cblogs); _g_object_unref0 (self->priv->lognames); _g_object_unref0 (self->priv->settings); _g_thread_unref0 (self->priv->thread); _vala_clear_GMutex (&self->priv->events_lock); _g_object_unref0 (self->priv->events); _g_hash_table_unref0 (self->priv->command_hash); _g_object_unref0 (self->priv->net_manager); _g_object_unref0 (self->priv->notification); G_OBJECT_CLASS (smd_applet_parent_class)->finalize (obj); } GType smd_applet_get_type (void) { static volatile gsize smd_applet_type_id__volatile = 0; if (g_once_init_enter (&smd_applet_type_id__volatile)) { static const GTypeInfo g_define_type_info = { sizeof (smdAppletClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) smd_applet_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (smdApplet), 0, (GInstanceInitFunc) smd_applet_instance_init, NULL }; GType smd_applet_type_id; smd_applet_type_id = g_type_register_static (gtk_application_get_type (), "smdApplet", &g_define_type_info, 0); g_once_init_leave (&smd_applet_type_id__volatile, smd_applet_type_id); } return smd_applet_type_id__volatile; } gint _vala_main (gchar** args, int args_length1) { gint result = 0; gchar* PREFIX = NULL; gchar* _tmp0_ = NULL; const gchar* _tmp1_ = NULL; gchar* _tmp2_ = NULL; gchar* _tmp3_ = NULL; gboolean _tmp4_ = FALSE; gboolean _tmp5_ = FALSE; gchar* homedir = NULL; const gchar* _tmp28_ = NULL; gchar* _tmp29_ = NULL; const gchar* _tmp30_ = NULL; gchar* _tmp31_ = NULL; const gchar* _tmp32_ = NULL; gchar* _tmp33_ = NULL; const gchar* _tmp34_ = NULL; gchar* _tmp35_ = NULL; gchar* conf_home = NULL; const gchar* _tmp36_ = NULL; gchar* _tmp37_ = NULL; const gchar* _tmp38_ = NULL; const gchar* _tmp43_ = NULL; gchar* _tmp44_ = NULL; gchar* _tmp45_ = NULL; GOptionEntry* oe = NULL; GOptionEntry _tmp46_ = {0}; GOptionEntry _tmp47_ = {0}; GOptionEntry _tmp48_ = {0}; GOptionEntry* _tmp49_ = NULL; gint oe_length1 = 0; gint _oe_size_ = 0; GOptionContext* oc = NULL; GOptionContext* _tmp50_ = NULL; GOptionContext* _tmp51_ = NULL; GOptionEntry* _tmp52_ = NULL; gint _tmp52__length1 = 0; GError * _inner_error_ = NULL; _tmp0_ = g_strdup (SMD_CONF_PREFIX); PREFIX = _tmp0_; _tmp1_ = PREFIX; _tmp2_ = g_strconcat (_tmp1_, SMD_APPLET_UI, NULL); _tmp3_ = _tmp2_; _tmp4_ = g_file_test (_tmp3_, G_FILE_TEST_EXISTS); _tmp5_ = !_tmp4_; _g_free0 (_tmp3_); if (_tmp5_) { FILE* _tmp6_ = NULL; const gchar* _tmp7_ = NULL; gchar* _tmp8_ = NULL; FILE* _tmp9_ = NULL; const gchar* _tmp10_ = NULL; gchar* _tmp11_ = NULL; FILE* _tmp12_ = NULL; const gchar* _tmp13_ = NULL; gchar* _tmp14_ = NULL; FILE* _tmp15_ = NULL; const gchar* _tmp16_ = NULL; gchar* _tmp17_ = NULL; FILE* _tmp18_ = NULL; const gchar* _tmp19_ = NULL; _tmp6_ = stderr; _tmp7_ = PREFIX; fprintf (_tmp6_, "error: file not found: %s + %s\n", _tmp7_, SMD_APPLET_UI); _tmp8_ = g_strdup ("./smd-loop"); _g_free0 (smd_applet_smd_loop_cmd); smd_applet_smd_loop_cmd = _tmp8_; _tmp9_ = stderr; _tmp10_ = smd_applet_smd_loop_cmd; fprintf (_tmp9_, "smd-applet not installed, " "assuming smd-loop is: %s\n", _tmp10_); _tmp11_ = g_strdup ("./smd-applet.ui"); _g_free0 (smd_applet_smd_applet_ui); smd_applet_smd_applet_ui = _tmp11_; _tmp12_ = stderr; _tmp13_ = smd_applet_smd_applet_ui; fprintf (_tmp12_, "smd-applet not installed, " "assuming smd-applet.ui is: %s\n", _tmp13_); _tmp14_ = g_strdup ("./smd-push"); _g_free0 (smd_applet_smd_push_cmd); smd_applet_smd_push_cmd = _tmp14_; _tmp15_ = stderr; _tmp16_ = smd_applet_smd_push_cmd; fprintf (_tmp15_, "smd-applet not installed, " "assuming smd-push is: %s\n", _tmp16_); _tmp17_ = g_strdup ("./smd-applet.desktop"); _g_free0 (smd_applet_smd_applet_desktop); smd_applet_smd_applet_desktop = _tmp17_; _tmp18_ = stderr; _tmp19_ = smd_applet_smd_applet_desktop; fprintf (_tmp18_, "smd-applet not installed, " "assuming smd-applet.desktop is: %s\n", _tmp19_); } else { const gchar* _tmp20_ = NULL; gchar* _tmp21_ = NULL; const gchar* _tmp22_ = NULL; gchar* _tmp23_ = NULL; const gchar* _tmp24_ = NULL; gchar* _tmp25_ = NULL; const gchar* _tmp26_ = NULL; gchar* _tmp27_ = NULL; _tmp20_ = PREFIX; _tmp21_ = g_strconcat (_tmp20_, SMD_LOOP, NULL); _g_free0 (smd_applet_smd_loop_cmd); smd_applet_smd_loop_cmd = _tmp21_; _tmp22_ = PREFIX; _tmp23_ = g_strconcat (_tmp22_, SMD_PUSH, NULL); _g_free0 (smd_applet_smd_push_cmd); smd_applet_smd_push_cmd = _tmp23_; _tmp24_ = PREFIX; _tmp25_ = g_strconcat (_tmp24_, SMD_APPLET_UI, NULL); _g_free0 (smd_applet_smd_applet_ui); smd_applet_smd_applet_ui = _tmp25_; _tmp26_ = PREFIX; _tmp27_ = g_strconcat (_tmp26_, SMD_APPLET_DESKTOP, NULL); _g_free0 (smd_applet_smd_applet_desktop); smd_applet_smd_applet_desktop = _tmp27_; } _tmp28_ = g_get_home_dir (); _tmp29_ = g_strdup (_tmp28_); homedir = _tmp29_; _tmp30_ = homedir; _tmp31_ = g_strconcat (_tmp30_, "/.smd/log/", NULL); _g_free0 (SMD_LOGS_DIR); SMD_LOGS_DIR = _tmp31_; _tmp32_ = homedir; _tmp33_ = g_strconcat (_tmp32_, "/.smd/loop", NULL); _g_free0 (SMD_LOOP_CFG); SMD_LOOP_CFG = _tmp33_; _tmp34_ = homedir; _tmp35_ = g_strconcat (_tmp34_, "/.smd/config.default", NULL); _g_free0 (SMD_PP_DEF_CFG); SMD_PP_DEF_CFG = _tmp35_; _tmp36_ = g_getenv ("XDG_CONFIG_HOME"); _tmp37_ = g_strdup (_tmp36_); conf_home = _tmp37_; _tmp38_ = conf_home; if (_tmp38_ != NULL) { const gchar* _tmp39_ = NULL; gchar* _tmp40_ = NULL; _tmp39_ = conf_home; _tmp40_ = g_strconcat (_tmp39_, "/autostart/smd-applet.desktop", NULL); _g_free0 (XDG_AUTORUN_FILE); XDG_AUTORUN_FILE = _tmp40_; } else { const gchar* _tmp41_ = NULL; gchar* _tmp42_ = NULL; _tmp41_ = homedir; _tmp42_ = g_strconcat (_tmp41_, "/.config/autostart/smd-applet.desktop", NULL); _g_free0 (XDG_AUTORUN_FILE); XDG_AUTORUN_FILE = _tmp42_; } _tmp43_ = XDG_AUTORUN_FILE; _tmp44_ = g_path_get_dirname (_tmp43_); _tmp45_ = _tmp44_; g_mkdir_with_parents (_tmp45_, 0700); _g_free0 (_tmp45_); gtk_init (&args_length1, &args); notify_init ("smd-applet"); memset (&_tmp46_, 0, sizeof (GOptionEntry)); _tmp46_.long_name = "verbose"; _tmp46_.short_name = 'v'; _tmp46_.flags = 0; _tmp46_.arg = G_OPTION_ARG_NONE; _tmp46_.arg_data = &verbose; _tmp46_.description = "verbose output, for debugging only"; _tmp46_.arg_description = NULL; memset (&_tmp47_, 0, sizeof (GOptionEntry)); _tmp47_.long_name = "smd-loop"; _tmp47_.short_name = 'l'; _tmp47_.flags = 0; _tmp47_.arg = G_OPTION_ARG_STRING; _tmp47_.arg_data = &smd_applet_smd_loop_cmd; _tmp47_.description = "override smd-loop command name, debugging only"; _tmp47_.arg_description = "program"; memset (&_tmp48_, 0, sizeof (GOptionEntry)); _tmp48_.long_name = NULL; _tmp49_ = g_new0 (GOptionEntry, 3); _tmp49_[0] = _tmp46_; _tmp49_[1] = _tmp47_; _tmp49_[2] = _tmp48_; oe = _tmp49_; oe_length1 = 3; _oe_size_ = oe_length1; _tmp50_ = g_option_context_new (" - syncmaildir applet"); oc = _tmp50_; _tmp51_ = oc; _tmp52_ = oe; _tmp52__length1 = oe_length1; g_option_context_add_main_entries (_tmp51_, _tmp52_, NULL); { GOptionContext* _tmp53_ = NULL; _tmp53_ = oc; g_option_context_parse (_tmp53_, &args_length1, &args, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == G_OPTION_ERROR) { goto __catch20_g_option_error; } _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return 0; } } goto __finally20; __catch20_g_option_error: { GError* e = NULL; FILE* _tmp54_ = NULL; GError* _tmp55_ = NULL; const gchar* _tmp56_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp54_ = stderr; _tmp55_ = e; _tmp56_ = _tmp55_->message; fprintf (_tmp54_, "%s\n", _tmp56_); result = 1; _g_error_free0 (e); _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); return result; } __finally20: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return 0; } { smdApplet* smd_applet = NULL; smdApplet* _tmp57_ = NULL; smdApplet* _tmp58_ = NULL; _tmp57_ = smd_applet_new (&_inner_error_); smd_applet = _tmp57_; if (G_UNLIKELY (_inner_error_ != NULL)) { if (_inner_error_->domain == EXIT) { goto __catch21_exit; } _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return 0; } _tmp58_ = smd_applet; smd_applet_start (_tmp58_, &_inner_error_); if (G_UNLIKELY (_inner_error_ != NULL)) { _g_object_unref0 (smd_applet); if (_inner_error_->domain == EXIT) { goto __catch21_exit; } _g_object_unref0 (smd_applet); _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return 0; } _g_object_unref0 (smd_applet); } goto __finally21; __catch21_exit: { GError* e = NULL; FILE* _tmp59_ = NULL; GError* _tmp60_ = NULL; const gchar* _tmp61_ = NULL; e = _inner_error_; _inner_error_ = NULL; _tmp59_ = stderr; _tmp60_ = e; _tmp61_ = _tmp60_->message; fprintf (_tmp59_, "abort: %s\n", _tmp61_); result = 1; _g_error_free0 (e); _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); return result; } __finally21: if (G_UNLIKELY (_inner_error_ != NULL)) { _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return 0; } result = 0; _g_option_context_free0 (oc); oe = (g_free (oe), NULL); _g_free0 (conf_home); _g_free0 (homedir); _g_free0 (PREFIX); return result; } int main (int argc, char ** argv) { #if !GLIB_CHECK_VERSION (2,32,0) g_thread_init (NULL); #endif #if !GLIB_CHECK_VERSION (2,35,0) g_type_init (); #endif return _vala_main (argv, argc); } static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func) { if ((array != NULL) && (destroy_func != NULL)) { int i; for (i = 0; i < array_length; i = i + 1) { if (((gpointer*) array)[i] != NULL) { destroy_func (((gpointer*) array)[i]); } } } } static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func) { _vala_array_destroy (array, array_length, destroy_func); g_free (array); } static void _vala_clear_GMutex (GMutex * mutex) { GMutex zero_mutex = { 0 }; if (memcmp (mutex, &zero_mutex, sizeof (GMutex))) { g_mutex_clear (mutex); memset (mutex, 0, sizeof (GMutex)); } } static void _vala_clear_GRecMutex (GRecMutex * mutex) { GRecMutex zero_mutex = { 0 }; if (memcmp (mutex, &zero_mutex, sizeof (GRecMutex))) { g_rec_mutex_clear (mutex); memset (mutex, 0, sizeof (GRecMutex)); } } static void _vala_clear_GRWLock (GRWLock * mutex) { GRWLock zero_mutex = { 0 }; if (memcmp (mutex, &zero_mutex, sizeof (GRWLock))) { g_rw_lock_clear (mutex); memset (mutex, 0, sizeof (GRWLock)); } } static void _vala_clear_GCond (GCond * mutex) { GCond zero_mutex = { 0 }; if (memcmp (mutex, &zero_mutex, sizeof (GCond))) { g_cond_clear (mutex); memset (mutex, 0, sizeof (GCond)); } }