pax_global_header00006660000000000000000000000064147453647560014537gustar00rootroot0000000000000052 comment=572fbff7c5fdc8c96bd382e22bf4c83fd672ab3c getmail6-6.19.07/000077500000000000000000000000001474536475600134335ustar00rootroot00000000000000getmail6-6.19.07/.github/000077500000000000000000000000001474536475600147735ustar00rootroot00000000000000getmail6-6.19.07/.github/workflows/000077500000000000000000000000001474536475600170305ustar00rootroot00000000000000getmail6-6.19.07/.github/workflows/lint.yml000066400000000000000000000004751474536475600205270ustar00rootroot00000000000000# https://github.com/codespell-project/codespell # https://beta.ruff.rs name: lint on: push: branches: [main, master] pull_request: branches: [main, master] jobs: lint: runs-on: ubuntu-latest steps: - run: pip install --user codespell[toml] ruff - run: codespell - run: ruff check . getmail6-6.19.07/.github/workflows/publish.yml000066400000000000000000000016361474536475600212270ustar00rootroot00000000000000name: Publish on: push: tags: - '*.*.*' workflow_dispatch: permissions: contents: read jobs: build: name: Build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/project/getmail permissions: id-token: write steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - name: Make sure tag matches version run: "[[ $(git describe --tags --exact-match) = v$(python -c 'import getmailcore ; print(getmailcore.__version__)') ]]" - name: Build project run: | sed -i -e "s/name.*=.*getmail6./name = 'getmail'/g" setup.py make dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 getmail6-6.19.07/.github/workflows/publish6.yml000066400000000000000000000015141474536475600213100ustar00rootroot00000000000000name: Publish on: push: tags: - '*.*.*' workflow_dispatch: permissions: contents: read jobs: build: name: Build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/project/getmail6 permissions: id-token: write steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - name: Make sure tag matches version run: "[[ $(git describe --tags --exact-match) = v$(python -c 'import getmailcore ; print(getmailcore.__version__)') ]]" - name: Build project run: make dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 getmail6-6.19.07/.gitignore000066400000000000000000000040101474536475600154160ustar00rootroot00000000000000test/temp.* # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ .build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ getmail6-6.19.07/MANIFEST.in000077500000000000000000000005071474536475600151760ustar00rootroot00000000000000include README include MANIFEST.in include docs/*.txt include docs/*.html include docs/BUGS include docs/CHANGELOG include docs/COPYING include docs/THANKS include docs/getmaildocs.css include docs/getmailrc-examples include docs/*.1 include getmail getmail_fetch getmail_maildir getmail_mbox setup.py include getmailcore/*.py getmail6-6.19.07/Makefile000066400000000000000000000040471474536475600151000ustar00rootroot00000000000000# docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. # Needs Docker Daemon running # systemctl start docker .PHONY: doc doc: links -dump docs/documentation.html > docs/documentation.txt links -dump docs/configuration.html > docs/configuration.txt links -dump docs/faq.html > docs/faq.txt links -dump docs/troubleshooting.html > docs/troubleshooting.txt .PHONY: testclean testclean: [ -d /tmp/mailserver/ ] && (cd /tmp/mailserver && docker-compose down) || true [ -d /tmp/mailserver/ ] && sudo rm -rf /tmp/mailserver || true .PHONY: test3 test3: cd test && ./prepare_test.sh cd /tmp/mailserver && test/bats/bin/bats test/test_getmail_with_docker_mailserver.bats .PHONY: fortest fortest: pip install pytest .PHONY: testpython testpython: fortest pytest test/test.py .PHONY: test test: testpython testclean test3 cd /tmp/mailserver && docker-compose down .PHONY: lint lint: ruff check . .PHONY: check check: lint /usr/bin/man -l docs/getmail.1 /usr/bin/man -l docs/getmails.1 /usr/bin/man -l docs/getmail_fetch.1 /usr/bin/man -l docs/getmail_maildir.1 /usr/bin/man -l docs/getmail_mbox.1 restview --long-description --strict .PHONY: dist dist: doc echo "need sudo to create wheel" sudo python setup.py bdist_wheel sdist echo "note:" echo "use ./pypi.sh to upload to PYPI" # use ./pypi.sh to upload to PYPI .PHONY: up6 up6: dist twine upload dist/`ls dist -rt *.whl | tail -1` dist/`ls dist -rt *.tar.gz | tail -1` -u__token__ -p`pass show pypi.org/getmail6_api_token` .PHONY: up up: dist twine upload dist/`ls dist -rt *.whl | tail -1` dist/`ls dist -rt *.tar.gz | tail -1` -u__token__ -p`pass show pypi.org/getmail_api_token` .PHONY: tag tag: dist $(eval TAGMSG="v$(shell ./getmail --version | cut -d ' ' -f 2)") echo $(TAGMSG) git tag -s $(TAGMSG) -m"$(TAGMSG)" git verify-tag $(TAGMSG) git push origin $(TAGMSG) --follow-tags .PHONY: cleandocker cleandocker: docker network prune docker rm -vf $$(docker ps -aq) docker rmi -f $$(docker images -aq) getmail6-6.19.07/PKG-INFO000066400000000000000000000025521474536475600145340ustar00rootroot00000000000000Metadata-Version: 1.1 Name: getmail Version: 6.00 Summary: a mail retrieval, sorting, and delivering system Home-page: http://pyropus.ca/software/getmail/ Author: Charles Cazabon Author-email: charlesc-getmail@pyropus.ca License: GNU GPL version 2 Download-URL: http://pyropus.ca/software/getmail/#download Description: getmail is a multi-protocol mail retrieval system withsupport for simple and domain POP3 and IMAP4 mailboxes, domain SDPS mailboxes, POP3-over-SSL and IMAP-over-SSL, mail sorting, message filtering, and delivery to Maildirs, Mboxrd files, external MDAs, and other advanced features. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: End Users/Desktop Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Topic :: Communications :: Email Classifier: Topic :: Communications :: Email :: Filters Classifier: Topic :: Communications :: Email :: Post-Office :: IMAP Classifier: Topic :: Communications :: Email :: Post-Office :: POP3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities getmail6-6.19.07/README000066400000000000000000000070001474536475600143100ustar00rootroot00000000000000.. vim: syntax=rst .. docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 .. Please refer to the git history regarding who changed what and when in this file. Description =========== getmail6 is a flexible, extensible mail retrieval system with support for POP3, IMAP4, SSL variants of both, maildirs, mboxrd files, external MDAs, arbitrary message filtering, single-user and domain-mailboxes, and many other useful features. Licence ======= getmail is Copyright (C) 1998-2025 Charles Cazabon and others. getmail is licensed for use under the GNU General Public License version 2 (only). See ``docs/COPYING`` for specific terms and distribution information. Bugs ==== getmail6 has adaptations to work with Python 3. These changes might still contain some bugs. Please report them at https://github.com/getmail6/getmail6. See ``docs/BUGS`` for instructions on reporting bugs. Installation ============ To install:: pip install getmail6 To uninstall:: pip uninstall getmail6 You can install getmail6 in your home directory if you add ``--user``. If getmail6 is available via your Linux distribution, you better use that. Usage ===== getmail6 is not a python API. getmail6 provides command line tools: - getmail - getmail_maildir - getmail_mbox - getmail_fetch - getmail-gmail-xoauth-tokens Configuration ============= Before using ``getmail`` you must configure it. See ``docs/configuration.txt`` and ``docs/getmailrc-examples``. An example:: [retriever] type = SimpleIMAPSSLRetriever server = imap.gmail.com port = 993 username = #password = ... or password_command = ("pass", "") [destination] type = Maildir path = ~/Mail//INBOX/ [options] read_all = true delete = true Gmail ===== For **gmail**, after having enabled 2-Step Authentication, let google generate an "app password" for you. Then, for the above example, use ``pass edit `` and change to the generate one. - Go to https://mail.google.com - If you are signed in, on the left upper corner there is a cogwheel symbol for settings - Choose "See all Settings" - "Accounts and Imports" tab, then "Other Google Account Settings"/"Security" brings you to https://myaccount.google.com/u/0/security?hl=en - Turn on "2-Step Verification" (also known as 2-factor-authentication or 2FA) - In "App passwords", generate a password for your device - Update this in your password command. See also: https://support.google.com/accounts/answer/185833 getmail is a native app. See https://developers.google.com/identity/protocols/oauth2/native-app Still, to download your email from gmail to your computer using OAuth2 you need to grant getmail OAuth2 access to the scope ``https://mail.google.com/``, as you would to web apps. Unfortunately, the init step in example 12 in ``docs/getmailrc-examples`` has to be repeated regularly. This makes the *app password* method above a better alternative. Don't forget to remove the ``use_xoauth2`` line, if you switch from Oauth2 to *app password*. Tests ===== There is now a test folder that uses `docker-mailserver `__ for black box testing. Tests are work in progress. Documentation ============= See the HTML documentation for details on setting up and using ``getmail``. :: docs/documentation.html docs/configuration.html docs/faq.html docs/troubleshooting.html It is included in the ``docs`` subdirectory, and will be installed in ``/doc/getmail-/`` (by default). getmail6-6.19.07/docs/000077500000000000000000000000001474536475600143635ustar00rootroot00000000000000getmail6-6.19.07/docs/BUGS000066400000000000000000000015011474536475600150430ustar00rootroot00000000000000Known bugs ========== None, but we are very interested in hearing your experiences with using this version with Python version 3 or newer. Reporting bugs ============== Before reporting a problem -------------------------- If you want to report a getmail problem, please ensure - you are running the latest version of getmail from the website at http://getmail6.org/ - you have installed getmail from the .tar.gz file I provide Then follow the rest of the instructions below. How to report a problem with getmail ------------------------------------ IMPORTANT: Please include the output of getmail [your usual options] --dump and a copy of your getmail rc file(s) (with password masked) with any bug report. If you find a bug in getmail, please open an issue on GitHub at https://github.com/getmail6/getmail6/issues getmail6-6.19.07/docs/CHANGELOG000066400000000000000000002024211474536475600155760ustar00rootroot00000000000000Version 6.19.07 26.January.2025 - #225 Missing comma in getmailcore/message.py Version 6.19.06 18.January.2025 - #223 publish to pypi workflow - #219 #220 increase IMAP efficiency through uid_cache (needs to be configured) storing last fetched UID - updated tests - update copyright to 2025 Version 6.19.05 02.October.2024 - fix #211: --only-oldmail-file command line = only_oldmail_file config: regenerate oldmail file with mails currenlty on server - fix #212: oldmail-* reverts when --to-oldmail-on-each-mail is used - Pyp2 fixes (#210) - fix #209: using the suggested simpler fix as pathlib is not used so far - build and upload sdist - Fix invalid SPDX expression Version 6.19.04 25.August.2024 - Update getmailrc-examples (#204, #206, #207) - #205: pop3: Support use_xoauth2 Version 6.19.03 08 July 2024 - #199 fix: fails with "TypeError: IMAP4_SSL.__init__()" after changing to Python 3.12 (when keyfile/certfile in config) Version 6.19.02 01 July 2024 - #196 fix: handle KeyError (getmail-gmail-xoauth-tokens) - #197 fix: detect if --port is set to force the use of the local http server (getmail-gmail-xoauth-tokens) Version 6.19.01 7 June 2024 - #195 fix: make mark_read option act same as -ds, Version 6.19.00 31 May 2024 - #191 -m/--mark-read and mark_read in [options] of rcfile - #190 -o/--only-account to choose rcfile by username (=email) - #189 IMAP retrievers support imap_id_extension - #137 Add mailbox name to log line Version 6.18.14 20 March 2024 - fix make lint - fix #183: in case of TypeError keyfile and certfile are used later in wrap_socket (Python 3.12) - fix #182: make log to trace - Allow to customize 'redirect_uri' (pull request #179 from jerome-pouiller/master) - fix #176: Maildir Filename Generation Issue with Truncated Hostname (use full hostname) - remove "Labels" before deleting uid (pull request #175 from ewft/fix_gmail_deletion) - Redact real email in test data (pull request #170 from h3xx/remove-email) Version 6.18.13 22 August 2023 - not doing python2 tests any more - #169 failure to recover corrupt message - #168 LookupError: unknown encoding: unicode - #167 from cclauss/ruff (lint python code with ruff) - #166 help about write access to oauth2 token file - bring getmail and getmails rc file filtering in sync - #160 from MatthiasPetermann/master (Enable overriding the prompt parameter in the XOAUTH URL) - #158 from horrad/master (Ignore directories in getmailrcdir) Version 6.18.12 21 January 2023 - #156 Continue after error in child - #157 undo #148 getmails shell quoting Version 6.18.11 1 January 2023 - #149 (h3xx/fix-shell-quote) added ability to process token creation on a headless server (device without a browser) - #146 (ykasap/appperms) Fix shell quoting issue - #145 add "offline_access" permission in Example 13 docs/getmailrc-examples (Exchange Online) - #141 add documentation for --searchset/-s - #142 dont consider hidden files as config file (if no --rcfile) - #138 (ChrisTG742) partial fix for #135 (getmail-gmail-xoauth-tokens errors are confusing) HTTP exception handling for GET requests - #134 (ChrisTG742) fix #129 (MDA_external: screen output of called script silently dropped) new option "pipe_stdout" to the [destination] settings block - #148 getmails shell quoting Version 6.18.10 20 September 2022 - readme: mention "app password" for gmail - fix #122: Inappropriate error message for external password command - fix #125: exclude files ending in .json if no --rcfile - pull #131: Update getmailrc-examples (ChrisTG742/getmail6) - fix #132: forward to corrupted_message on encoding problems Version 6.18.9 11 May 2022 - pull #115: Add support for local webserver to receive oauth2 redirect - #119: NameError: name 'CertificateError' is not defined (ca_certs option) Version 6.18.8 09 May 2022 - #118: redirect_uri requirements in Microsoft's OAuth 2.0 Version 6.18.7 08 May 2022 - #116: Update to avoid use of depreciated Google OAuth2 flow - #114: Fails with "Killed" when retrieving large emails use timeout for socket also as wait timeout for child - #112: getmail keeps downloading the whole mailbox new to_oldmail_on_each_mail option. - #111: DeprecationWarnings from ssl Version 6.18.6 08 January 2022 - #104: add config parameter to MDA_lmtp to override recipient address (WIP) - #103: Fix random token generation - #101: Fix ignored netrc_file option - #94: bad message from server!' and empty messages Version 6.18.5 30 October 2021 - #93: IMAP: Deal better with untagged status updates - #95: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa9 in position 0: invalid start byte - #96: getmail_fetch: TypeError: method expected 2 arguments, got 3 Version 6.18.4 26 September 2021 - #91: UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 11: ordinal not in range(128) - #92: getmail_maildir: bugfix for python3 using bytes buffer instead of str - add new use_netrc and netrc_file configuration options to support reading secrets from a .netrc file (https://marc.info/?l=getmail&m=163241430111986&w=3). Version 6.18.3 06 July 2021 - #89: fetchmail feature: skip fetching read mail. New `imap_search` and `imap_on_delete` to fetch only `Unseen` messages and set them to `\Seen`. - add `-s/--searchset` to override imap search and setting of flags for all rcfile's in the command - if no `--rcfile` and no default `getmailrc`, then use all files from the `--getmaildir` folder - fix "SyntaxError: Non-ASCII in file, but no encoding declared" (only Python 2.7) Version 6.17 15 June 2021 - #79: UnicodeEncodeError: 'ascii' codec can't encode character in position - #78: Don't encode pure ASCII X-getmail-retrieved-from-mailbox - #69: skip_imap_fetch_size. IMAP requests size, making davmail download all the messages every time, making it slow - #81: Allow having IMAP message IDs recorded in headers (if received=True in config) - #82: Add branding to Received header - #85: Discuss content of oldmail files in FAQ Version 6.16 13 May 2021 - #73: Additional exception handling for IMAP IDLE with xoauth2 - #74: 'ascii' codec can't encode characters in position 4017-4018 - #75: Bring back "getmails" wrapper (was a Debian addition, but fits well to getmail itself) - #76, #77: Revert replaced strings (from "2020" to "2021") in docs/CHANGELOG Version 6.15 21 March 2021 - #67 Implement a destination that delivers to a local LMTP server - #71 allow "=" in ssl_ciphers to allow "ssl_ciphers = DEFAULT@SECLEVEL=1" - #72 non-compliant e-mail header (in multidrop configuration) causes getmail to stop working - Added test folder: run tests with `make test`. Tests and fixes. Version 6.14 14 January 2021 - #64 getmail6: sometimes dies on running external filter program in version 6.13 - #65 Wait much longer, and raise an exception when timeout (pull from ykasap/fix-timeout to solve #64) - #63 Trap exceptions caused by xoauth2 token expiration - #61 Refresh the OAuth access token when reconnecting (IMAP IDLE) Version 6.13 07 January 2021 - fix #59: Traceback while trying to reap child process - fix #60: Need to refresh the OAuth access token when reconnecting (IMAP IDLE) Version 6.12 31 December 2020 - fix #53: unclosed file warnings - fix #52: getmail seems to lose its child process and hangs forever (pull #54) - documentation improvements (#55, #56) Version 6.11 09 December 2020 - comply more closely with the XDG basedir specification: allow an unset XDG_CONFIG_HOME environment variable. - Simplify the documentation around the location of the configuration directory. - fix #49: "'utf-8' codec can't decode" for mbox Version 6.10 28 November 2020 - fix #48: handle Header in address_no_brackets using Header.__str__ Version 6.9 02 November 2020 - fix python 3.9 issues #42 #44 - fix regex warning #45 - add usage of Python keyring package in addition to gnomekeyring (#43) Version 6.8 07 October 2020 - fix #37: update copyright date and notices - fix #39: Slow mail retrieval - pull #40: fix UnboundLocalError when socket.error inside POP3_SSL_EXTENDED Version 6.7 01 September 2020 - use $XDG_CONFIG_HOME if defined - fix pip install (doc/man below site-packages) - fix #34: quote only for select Version 6.6 29 August 2020 - fix #34: quote mailbox if necessary - fix #36: use setuptools, suggest pip to install and uninstall Version 6.5 28 August 2020 - fix #23: port getmail-gmail-xoauth-tokens - fix #24: TypeError: 'gaierror' object is not subscriptable - documentation fixes Version 6.4 24 August 2020 - fix #20: mboxrd can't concat str to bytes - fix #21: version numbering from 6.0x to 6.x Version 6.03 17 August 2020 - fix #14: IDLE not working - documentation update Version 6.02 12 August 2020 - try to do as little own decoding as possible - issue #8 Version 6.01 - fixes of reported issues on github up to #7 Version 6.00 02 May 2020 - Make code compatible to Python 3 (losing some backward compatibility) Version 5.14 19 August 2019 -remove socket setblocking() calls which should be no longer needed, and which may have caused hangups. Thanks: Viacheslav Chimishuk. -correct version number string in error message. Thanks: Piet van Oostrum. Version 5.13 22 February 2019 -second attempt at fixing SNI change with Python < 2.7. Thanks: "Cimbal". Version 5.12 22 February 2019 -bugfix: the SNI change broke something else with Python 2.6 and earlier; fixed. Thanks: "Cimbal". Version 5.11 13 February 2019 -bugfix: the SNI change did not work correctly with Python < 2.7.13; should be fixed now. Thanks: Scott Robbins. Version 5.10 12 February 2019 -experimental: when SSL SNI support is present in the underlying Python (and OpenSSL), send SNI by default in the SSL setup. This should work around Gmail's brokenness with TLSv.1.3 connections when SNI is not sent. Version 5.9 -documentation-only update; add note with workaround for Gmail SSL connections with OpenSSL 1.1.1 and later. -update copyright dates. Version 5.8 9 November 2018 -fix problem handling IMAP errors introduced in 5.7. Thanks: Andreas Reuleaux. Version 5.7 30 October 2018 -when users specify an SSL version that no longer exists in the Python ssl module, do not result in an unhandled exception. Thanks: "nandre". -catch IMAP UNAVAILABLE temporary error during login. Thanks: Dario Corti. Version 5.6 02 April 2018 -fix references to version 4 in README. Thanks: Daniel Kahn Gillmor. -add Gmail-specific XOAUTH2 login support for IMAP. Thanks: Stefan Krah. Version 5.5 18 December 2017 -feature request: added record_mailbox configuration parameter, to allow turning off the header getmail adds with this information. Thanks: Daniel Kahn Gillmor, Osamu Aoki, Josh Triplett. Version 5.4 6 October 2017 -bugfix: fix another error in logging an error condition. Thanks: "ng0". Version 5.3 5 October 2017 -bugfix: another case where an error condition resulted in getmail not displaying the correct message. Thanks: "ng0". Version 5.2 4 October 2017 -bugfix: disconnection during IMAP IDLE could result in an error message rather than silently exiting. Thanks: David Gray. Version 5.1 15 July 2017 -bugfix: if password_command parameter was used with a non-existent program, getmail would error out during the handling of that condition and not report the problem correctly. Version 5.0 15 July 2017 -new release numbering scheme; previous version numbers were just getting too high. -catch and ignore/exit cleanly after reset connection in IMAP IDLE mode. Thanks: Stephan Schulz. -allow specifying an expected SSL certificate hostname, for when the server's certificate does not match the domain name used to connect to it. Thanks: "Andre". -fix error message not actually giving the header field name incorrectly specified as containing the envelope recipient address. Thanks: Hardy Braunsdorf. -add new password_command configuration parameter for retrievers, allowing getmail to retrieve the account password from any arbitrary external command. Suggestion: "ng0". Version 4.54.0 19 February 2017 -fix error running getmail_fetch introduced in 4.53.0. Thanks: "fsckd" of Arch Linux. Version 4.53.0 15 January 2017 -fix plaintext version of docs not generated correctly. Thanks: Elijah. -fix `getmail --fingerprint` not logging server TLS fingerprint correctly with SimplePOP3SSLRetriever. Thanks: Gabriel Kihlman. Version 4.52.0 22 October 2016 -add `ignore_header_shrinkage` parameter to Filter_external for users who know it is normal for their particular filter to result in a smaller message header than the source message, for example when the filter encapsulates the original message in a simpler wrapper message. Thanks: "RW", Tristan Miller, Francesco Ariis. -EXPERIMENTAL: when deleting a message from an IMAP mailbox, set the \Seen flag in addition to the the \Deleted flag. This apparently prevents the ever-innovatively-broken MSExchange from sending a spurious incorrect disposition-notification message to the sender of the message. I do not know if this will cause problems with other broken IMAP server implementations; please send a report to the getmail-user's mailing list if you see odd behaviour with this change. Thanks: John Hein. -enable socket timeouts for IMAP SSL classes by default. They were disabled in the code because they were incompatible with SSL in older versions of Python. If you see problems with Python 2.6 or 2.7 now, please let me know. Version 4.51.0 11 September 2016 -fix exception when using MDA_External and an IMAP mailbox whose name contains non-ASCII chars. Thanks: "drtmk". Version 4.50.0 11 July 2016 -maybe fix handling of OSX keychain passwords containing double-quote chars. Thanks: Teddy Wing, Patrick Asselman. -fix getmail erroring out on IMAP folders containing i18n chars. Thanks: Jan Stühler. Version 4.49.0 13 January 2016 -make IMAP class only issue EXPUNGE command on mailbox close if we have actually deleted any messages from the open mailbox. Makes use of read-only IMAP folders possible. Thanks: Zoltan Padrah. Version 4.48.0 31 May 2015 -work around brain-damaged change in Python's poplib which causes message retrieval errors if any line of a message has more than 2048 characters in it. -restore link to moved Marc mailing list archive. Thanks: David J. Weller-Fahy. Version 4.47.0 25 February 2015 -try to work around pathological breakage in one random POP server implementation. Thanks: Michael Thomas Kockmeyer. -remove dead marc.theaimsgroup.com list archive from docs. Thanks: Miroslav Rovis. -bugfix: if you combined IMAP IDLE mode with delete_after, getmail would, after remaining connected to the server for the number of days configured, begin deleting messages immediately after retrieval instead of after the configured delay. Now fixed. Thanks: Johannes Weißl. Version 4.46.0 6 April 2014 -fix --idle checking Python version incorrectly, resulting in incorrect warning about running with Python < 2.5. Thanks: "Voytek", Krzysztof Warzecha. -add missing support for SSL certificate checking in POP3 which broke POP retrieval in v4.45.0. Requires Python 2.6 or newer. Thanks: "mancha". Version 4.45.0 30 March 2014 -perform hostname-vs-certificate matching of SSL certificate if validating the certificate. Thanks: "mancha". -fix missing plaintext versions of documentation. Version 4.44.0 22 March 2014 -add extended SSL options for IMAP retrievers, allowing certificate verification and other features. Thanks: Steven Murdoch. -fix missing plaintext versions of documentation. Thanks: Osamu Aoki. -fix "Header instance has no attribute 'strip'" error which cropped up in some configurations. Thanks: Krzysztof Warzecha. Version 4.43.0 25 August 2013 -add IMAP IDLE support. Thanks: Jon Gjengset. Version 4.42.0 3 August 2013 -fix problem with non-ascii characters in newly-added message header fields with output from Filter_classifier. Thanks: "Gour". Version 4.41.0 26 May 2013 -messages retrieved with POP could have a blank trailing line removed when delivered; fixed. Thanks: Christoph Mitterer, Krzysztof Warzecha. -fix an ImportError when IMAP retriever used with getmail under Python 2.4. Version 4.40.3 10 May 2013 -increase system recursion limit when run with Python 2.3, to prevent recursion errors in parsing some pathologically complex MIME emails. Thanks: Kenneth Pronovici. Version 4.40.2 8 May 2013 -fix a backwards incompatibility with Python 2.3 and 2.4 introduced in getmail 4.38.0. Thanks: Massimo Zambelli, Krzysztof Warzecha. Version 4.40.1 22 April 2013 -again change protocol codes passed to OSX, as they're not only reserved codes, but also case-sensitive. Use the ones an OSX user reports work properly. Thanks: Tim Gray. Version 4.40.0 21 April 2013 -convert SIGTERM to SIGINT so getmail can cleanup and exit normally if killed with a default signal. Thanks: Carl Lei. -fix change which resulted in passing full protocol name to OSX keyring program, which can't handle it (restricted to 4-character code). Thanks: Tim Gray. Version 4.39.1 10 March 2013 -fix a bug that could crop up when retrieving mail via IMAP from Gmail, and the Gmail servers provided weird data in their header extensions. Thanks: Krzysztof Warzecha. Version 4.39.0 22 February 2013 -fix an oldmail filename issue that occurred with certain non-ASCII characters in an IMAP mailbox name. Thanks: Michael Kockmeyer. Version 4.38.0 16 February 2013 -fix retrieving mail from an IMAP mailbox with non-ASCII characters in its name causing difficult-to-understand unicode errors during delivery. Thanks: "fsLeg", Krzysztof Warzecha. Version 4.37.0 27 January 2013 -handle IMAP servers which include "/" in message IDs better, so those messages aren't always thought to be new. Thanks: Bradley Rintoul. -record (IMAP) mailbox retrieved from in X-getmail-retrieved-from-mailbox: header field, and make it available as %(mailbox) substitution in MDA_external arguments. Thanks: Les Barstow. -add delete_bigger_than option to allow removal of large messages after retrieval, even if not deleting messages otherwise. Thanks: Jan Vereecke. Version 4.36.0 15 December 2012 -add support for retrieving POP/IMAP passwords from, and storing them to, the Gnome keyring. Thanks: Krzysztof Warzecha, Scott Hepler. Version 4.35.0 24 October 2012 -fix From_ quoting in mbox delivery; use of the Python stdlib function meant getmail was incorrectly using mboxo-style quoting instead of mboxrd quoting, probably since early in the v4 series. Thanks: Christoph Mitterer. Version 4.34.0 8 September 2012 -retrieve Gmail metadata (labels, thread ID, message ID) via IMAP extension and record it in new message headers. Thanks: Krzysztof Warzecha. Version 4.33.0 7 August 2012 -allow normal exit on interrupt (ctrl-c) to allow the user to abort message retrieval while still remembering already-retrieved messages as successfully delivered. You may get a weird error message after you abort, since the server may be in the middle of delivering another message to getmail at the time. Thanks: Krzysztof Warzecha. Version 4.32.0 6 July 2012 -prevent some nuisance stack traces if getmail cannot connect to the POP/ IMAP server correctly. Thanks: Daniel Dumke. -restore use_peek IMAP retriever parameter which accidentally got removed in 4.30. Thanks: Andreas Amann. Version 4.31.0 5 July 2012 -improved backwards compatibility with pre-v.4.22.0 oldmail files, so IMAP mail is not re-retrieved if you upgrade from a 4.22 or earlier to this one; no user action necessary. Thanks: Osamu Aoki, Tim van der Molen. Version 4.30.2 27 June 2012 -fix a nuisance stack trace that would be dumped if a connection failed in particular states. Thanks: Gary Schmidt. Version 4.30.1 21 June 2012 -silence a nuisance deprecation warning about the sets module when running with Python >= 2.5 which was reintroduced in 4.29.0. Thanks: Stephan Schulz. Version 4.30.0 21 June 2012 -fix breakage introduced in 4.29.0 where BrokenUIDLPOP3Retriever would fail with a TypeError at logout time. Thanks: Scott Robbins, Stephan Schulz. -fix breakage introduced in 4.29.0 where deleted mail was not being expunged from the last (or only) folder retrieved from in an IMAP session. Thanks: Paul Howarth. Version 4.29.0 19 June 2012 -update old contact information for Free Software Foundation. Thanks: Ricky Zhou. -fix incorrect character encoding in plaintext documentation. Thanks: Ricky Zhou. -ensure getmail exits nonzero if a server refuses login due to a credential problem. Thanks: Stephan Schulz. Version 4.28.0 26 May 2012 -ensure getmail exits nonzero if various error conditions (like POP/IMAP authentication failure) occur. Thanks: Ryan J., Stephan Schulz. -python versions prior to 2.5.0 contain a bug when dealing with read-only IMAP mailboxes. Monkey-patch imaplib when running with Python<2.5.0. Thanks: Les Barstow. -do IMAP modified-utf7 conversion of mailbox names containing non-ASCII characters. Thanks: A. Lapraitis, Randall Mason. -add special ALL value for retrieving mail from all selectable IMAP mailboxes in the account. -change IMAP retrieval strategy to retrieve all messages from a mailbox, then move on to the next mailbox, etc. Should result in increased speed, but if you set `max_messages_per_session` too low, this could result in later mailboxes not being retrieved from. Version 4.27.0 20 May 2012 -make use of IMAP BODY.PEEK configurable; set the IMAP retriever parameter `use_peek` to False to disable use of PEEK to get getmail's historical IMAP behaviour. Version 4.26.0 14 April 2012 -switch to using BODY.PEEK in IMAP retrieval; I no longer see problems with this feature in my testing. If users experience incompatibility with any IMAP servers where 4.25.0 worked, please let me know. Version 4.25.0 1 February 2012 -add support for storing POP/IMAP password in OSX keychain. Thanks: Adam Lazur. Version 4.24.0 11 December 2011 -add an explicit expunge when closing an IMAP mailbox, for servers that incorrectly do not do this when the mailbox is closed. Thanks: Nicolas Pomarède. -fix incorrect section reference for `mailboxes` parameter in documentation. Thanks: Ross Boylan. -fix getmail_fetch broken in 4.21.0. Thanks: Chris Donoghue. Version 4.23.0 20 November 2011 -fix race if new message shows up in POP3 mailbox between running the UIDL and LIST commands. Thanks: Roland Koebler, Osamu Aoki. Version 4.22.2 12 November 2011 -fix an error when logging a bad response to an IMAP SELECT command, introduced in 4.21.0. Thanks: "kureshii". Version 4.22.1 30 September 2011 -fix BrokenUIDLPOP3Retriever breakage from 4.21.0. Thanks: Scott Robbins, "hgolden". Version 4.22.0 25 September 2011 -when retriever supports multiple mail folders (IMAP), store oldmail data for each folder separately, preventing problems in some cases when using multiple rc files with the same account. Thanks: Jesse Schobben. Version 4.21.0 23 September 2011 -automatically open IMAP folders read-only if neither the delete nor delete_after options are in use; necessary to access chatlogs via Gmail. Thanks: Daniel M. Drucker. -avoid sorting msgids on each retrieval, reducing overhead when dealing with folders containing thousands of messages. Thanks: Daniel Koukola. Version 4.20.4 16 July 2011 -include more info from server response in error message when IMAP command errors out. Thanks: W. Martin Borgert, Osamu Aoki. -switch to using Parser instead of HeaderParser to correct illegal formatting bogosities in the body parts of incoming messages. Thanks: Lauri Alanko. Version 4.20.3 30 May 2011 -fix logging errors in error paths. Thanks: Visa Putkinen. Version 4.20.2 9 Apr 2011 -further changes to the Received: header construction to handle IPv6 better. Thanks: Frédéric Perrin. Version 4.20.1 7 Apr 2011 -ensure correct remote address and port is included in the Received: trace headers generated by getmail. Previously the first address found for the server was used even if another address was actually used to connect to the server. Thanks: Frédéric Perrin. -add workaround for Python issue http://bugs.python.org/issue1389051 which affects Python < 2.5.3, causing message retrieval of large messages from IMAP4 SSL servers to consume pathologically large amounts of memory. Thanks: Bill Janssen. Version 4.20.0 29 June 2010 -fix crap code from bad svn merge that slipped into 4.18.0, triggering exceptions in MDA_external configs. Thanks: Paul Howarth. Version 4.19.0 29 June 2010 -fix missing import introduced in 4.18.0. Thanks: Paul Howarth. Version 4.18.0 26 June 2010 -update broken link in FAQ. Thanks: Stefan Kangas. -strip a few extra problematic (on non-Unix systems) characters when generating oldmail filenames; backslash was requested by Andy Ross. If upgrading and your current oldmail file contains any of these characters: \ ; < > | ... then rename it, replacing runs of one or more of those characters with a single "-". -improve clarity of message logged by getmail when an external program exits 0 but getmail considers it failed because it wrote to stderr. Thanks: Chris Dennis. Version 4.17.0 30 April 2010 -change to how getmail counts messages in an IMAP mailbox; prevents problems where getmail would only see the first 500 messages in a mailbox with some IMAP servers that return oddball responses to SELECT. Thanks: David Damerell. Version 4.16.0 5 January 2010 -additional fix for IMAP server that offers a message it then fails to return, to allow getmail to continue with the next message. Thanks: Yaw Anokwa. Version 4.15.0 2 December 2009 -default port for MultidropIMAPSSLRetriever was incorrect. Thanks: David Lehn. Version 4.14.0 23 November 2009 -handle additional cases where IMAP (and for good measure, POP3) server is broken and offers a message that it then fails to return. Thanks: Eric Waguespack. Version 4.13.0 13 October 2009 -fix new log message not having trailing newline.Thanks: Morty Abzug. Version 4.12.0 7 October 2009 -handle MSExchange failure to deliver message gracefully. Thanks: Morty Abzug. -documentation updates regarding Python IMAP memory bug. Thanks: "spig". -fix bad tag in documentation. Version 4.11.0 8 August 2009 -fix --dump not generating any output for rcfiles containing "verbose = 0" option. Thanks: Morty Abzug. -fix copy-and-paste error in documentation of MultiSorter destination. Thanks: Roland Hill. Version 4.10.0 6 August 2009 -add "message_log_verbose" option, defaulting to false. If true, the message_log file (and syslog, if used) will contain a little more information than the default of just messages retrieved. Thanks: Matthias Andree, Gregory Morozov. Version 4.9.2 15 July 2009 -use tempfile module function instead of os.tmpfile() so getmail obeys TMPDIR and similar environment variables. Thanks: Stefan Bähring. -fix encoding of CHANGELOG to utf-8. Version 4.9.1 1 June 2009 -if oldmail file is corrupted on disk (by system crash, perhaps), handle it gracefully. Some lines may be lost (and messages therefore re-retrieved), but operation will continue, with the corrupt lines logged. Thanks: Domen Puncer. Version 4.9.0 5 April 2009 -add Kerberos authentication support to IMAP retriever classes. Thanks: Guido Günther, Uli M. -clarify documentation regarding MDA_external. Thanks: AJ Weber. -eliminate deprecation warning for sets module with Python 2.6 by using a compatibility wrapper module. Version 4.8.4 26 September 2008 -Add missing fsync() in updatefile close method to ensure data actually hits disk before the rename takes place, to make writing of the oldmail file more resilient to system crashes. Thanks: Domen Puncer. Version 4.8.3 11 August 2008 -Improve information sent to logfile about messages not retrieved. Thanks: Scott. Version 4.8.2 2 August 2008 -for IMAP retrievers, keep message state around for 30 days even if the corresponding message isn't seen. This allows users to have multiple rc files with different IMAP folders to retrieve from without losing their "seen" mail state for the folders they're not currently retrieving from. You no longer need to work around this with different getmail state directories. Thanks: ?. Version 4.8.1 26 March 2008 -fix use of Python 2.4 set builtin in 4.8.0 preventing getmail from running on Python 2.3.x. Thanks: Björn Stenberg. -fix the RPM spec file not being included in the getmail distribution. Version 4.8.0 19 February 2008 -better diagnostic when user invalidly supplies timeout configuration parametmer for an IMAP-SSL retriever. Thanks: Dennis S. -code cleanups Version 4.7.8 5 February 2008 -explicitly close current IMAP mailbox when selecting a new one, so all servers expunge deleted mail. Thanks: Josh Triplett. -include experimental spec file for creating RPM with rpmbuild. Thanks: Dag Wieers, Rob Loos, Dries Verachtert. Version 4.7.7 8 November 2007 -convert changelog to utf-8 encoding. -update email addresses, etc. Domain for mailing lists has changed to lists.pyropus.ca. -add FAQ about memory errors on OS X. Thanks: Andres Gasson. -drop log message level for stderr output of destination if ignore_stderr is set, just like for filter. Thanks: Jeremy Chadwick. Version 4.7.6 8 August 2007 -fix exception when getmail is trying to report a POP3 server that completely missed generating a UIDL token for a message. Thanks: Hans Lammerts. Version 4.7.5 6 June 2007 -make updatefile honour symlinks, so users can alias one server's oldmail state file to another (for split-horizon DNS and other strange configurations). Thanks: Scott Hepler. Version 4.7.4 24 April 2007 -explicitly watch for broken POP3 servers that return a blank UIDL value (not permitted by the POP3 protocol) and print a fatal error message. Thanks: Florian Hackenberger and Cameron Rangeley. Version 4.7.3 18 March 2007 -fix wrong bytecount displayed in summary at end of run. Thanks: Andreas Jung. Version 4.7.2 20 February 2007 -add ignore_stderr option to filters, copying same from MDA_external destination. Thanks: Vittorio Beggi. Version 4.7.1 2 February 2007 -add max_bytes_per_session option. Thanks: Robert Spencer. -documentation additions. Version 4.7.0 24 January 2007 -make Mboxrd lock type configurable (lockf or flock). Change default to lockf. Thanks: Norman Carver. Version 4.6.7 3 January 2007 -better handling for some fatal violations of POP/IMAP protocols. Thanks: Paul ?. -formatting cleanups. Version 4.6.6 12 December 2006 -add ignore_stderr option to MDA_external destination. Thanks: Daniel Burrows. -documentation cleanups. Version 4.6.5 20 October 2006 -disable debug logging message which would show up in messages if --trace was used and the message was passed through an external filter. Thanks: Christian Bruemmer. -add additional sanity checks to ensure a recent-enough Python is used; trying to run getmail 4 with Python 2.2 would throw an exception. Thanks: fakhri ?. Version 4.6.4 28 September 2006 -add FAQ about BrokenUIDLPOP3 retrievers. -add better diagnostics for missing/invalid/unwritable state/data directory. Thanks: Christian Authmann. Version 4.6.3 22 June 2006 -fix regression: spurious errors thrown when required multidrop retriever parameter "envelope_recipient" not present in getmailrc file. Thanks: Elliot Schlegelmilch. -fix regression: getmail_fetch no longer properly supported external MDAs because of problems parsing the arguments to them. Thanks: ?. -change getmail's handling of regular expressions in the "locals" parameter of MultiSorter destination to allow it to handle backslashes correctly. Unfortunately this means complicating the configuration syntax slightly. Thanks: Tim van der Molen. -update filters to use new configuration code. Remove obsolete code. Version 4.6.2 8 June 2006 -fix regression introduced in 4.6.0 where atime on mbox files would get updated (i.e. not set back), making it difficult for other programs to tell if there was new mail or not without actually opening the mbox. Thanks: Tim van der Molen. Version 4.6.1 31 May 2006 -fix attribute deletion error with Filter_classifier when strip_delivered_to is set. Thanks: Darren Stevens. -do not consider qmail-local writing to stderr a delivery error. Thanks: Darren Stevens. Version 4.6.0 5 April 2006 -clean up logger API. -refactor configuration mechanism to use classes with embedded logic, rather than simply dictionaries. Version 4.5.4 14 February 2006 -added Maildir "filemode" parameter. Thanks: Martin Haag. Version 4.5.3 8 February 2006 -keyfile and certfile parameters to SSL retrievers are now expanded for leading "~/" and "~username/" as well as environment variables, just like other path parameters. Thanks: Fredrik Steen. Version 4.5.2 7 February 2006 -fix IMAP bug where no mail was retrieved introduced in 4.5.0. Thanks: Scott Robbins. Version 4.5.1 3 February 2006 -fix accidental use of sorted() builtin only present in Python 2.4 and up. Thanks: Jürgen Nagel. Version 4.5.0 1 February 2006 -add delete_dup_msgids option to non-multidrop POP3 retrievers. Thanks: Matthias Andree. -suppress stack trace in getmail_fetch error message when configuration error detected. Thanks: Dave Jarvis. -documentation updates. -build and development environment changes. The plaintext versions of the documentation are now generated with links instead of lynx. -explicitly log the reason for not retrieving a message (seen or oversized). Thanks: Payal Rathod. Version 4.4.4 3 January 2006 -improve parsing of flags in IMAP responses. Thanks: Gareth McCaughan. Version 4.4.3 1 December 2005 -fix harmless copy and paste error in IMAPRetrieverBase class. Thanks: Henry Miller. Version 4.4.2 13 November 2005 -fix logging import in corrupt messages with Python 2.3. Thanks: Marco Ferra. Version 4.4.1 10 November 2005 -work around bug in Python which meant POP3-over-SSL with Python >= 2.4 could "hang" (it's exactly the same bug that affected getmail's initial IMAP-over-SSL code). Version 4.4.0 9 November 2005 -add new POP3-over-SSL initialization class, which is automatically used when Python 2.4 or newer is in use. This reduces the overhead involved in retrieving messages from a POP3SSL server (reduces systime about 35%, user time about 50%, wall time 10-30%). Thanks: Jan Vereecke. -add getmail_fetch configuration-less commandline POP retriever, perfect for scripting use. Development of getmail_fetch was sponsored by Texas Communications. -future proofing: eliminate relative imports within getmailcore package. Version 4.3.14 2 November 2005 -Fix copy and paste error that caused BrokenUIDLPOP3SSLRetriever to default to port 110. Thanks: Daniel Burrows via Fredrik Steen. Version 4.3.13 15 October 2005 -add BrokenUIDLPOP3SSLRetriever as SSL version of BrokenUIDLPOP3Retriever, based on an idea by a user of the Debian package. Thanks: Daniel Burrows, Fredrik Steen. Version 4.3.12 13 October 2005 -remove deprecated "strict" argument from message/header parsing calls. While it was possible to suppress the warning that resulted when using Python 2.4, it appears many getmail users couldn't bother to do that and complained to me instead. Version 4.3.11 16 June 2005 -getmail could previously record an envelope return path header of "<<>>". Fixed. Thanks: Fredrik Steen. Version 4.3.10 19 May 2005 -bugfix for missing import. Thanks: Matthias Andree. Version 4.3.9 18 May 2005 -for multidrop retrievers, change the way the envelope recipient header field is parsed, to prevent odd values from being interpreted as multiple addresses when they look like an 822-style address group. Thanks: "aal". -try to avoid parsing message bodies, in case they're corrupt or invalid. Thanks: Michael Gold. Version 4.3.8 6 May 2005 -change (again) handling of deleted messages vs. connection that dies. Inability to send QUIT to POP3 server should be less problematic now. -improve housekeeping of old message state file. Thanks: Thomas Schwinge. Version 4.3.7 1 May 2005 -add error message for missing mboxrd file. Thanks: Marco Ferra. -change handling of connection that dies vs. deleted messages. Thanks: Thomas Schwinge. Version 4.3.6 8 April 2005 -broke BrokenUIDLPOP3Retriever when I added the forget_deleted parameter to the retrievers base class; now fixed. Thanks: Georg Neis. Version 4.3.5 3 April 2005 -make getmail less conservative about remembering messages as already-seen when unrelated errors occur after successfully delivering them. Thanks: Thomas Schwinge. Version 4.3.4 14 March 2005 -documentation changes; getmail v4 cannot run natively on Windows. Use Cygwin if you must run on a Windows platform; this works. -remove some Windows-specific code. Version 4.3.3 19 February 2005 -previously, for safety, getmail would re-retrieve messages after a session that encountered errors. However, getmail had enough information to safely remember those messages that had been successfully delivered. This behaviour has been changed, to avoid delivering duplicate messages where it isn't necessary. Thanks: Thomas Schwinge. -in output/log files, getmailrc files are now specified only by filename, instead of by complete paths. This will prevent some overly-long output lines. -add Windows versions of functions to lock/unlock files, so mbox delivery can be used on Windows. Thanks: Felix Schwarz. Version 4.3.2 5 February 2005 -previously, if an SSL POP3 or IMAP server abruptly closed the connection before getmail could finish logging in, getmail would exit instead of proceeding to the next configured mail account. Fixed. Thanks: Matthias Andree, Frank Benkstein, Thomas Schwinge. -eliminate duplicate Return-Path: header fields. Thanks: Angles Puglisi. Version 4.3.1 18 January 2005 -some IMAP errors would cause getmail to raise an exception, instead of gracefully proceeding with the next configured mail account. Fixed. Thanks: Matthias Andree. Version 4.3.0 10 January 2005 -add BrokenUIDLPOP3Retriever class to support servers that do not implement the UIDL command, or which fail to uniquely identify messages using it. Version 4.2.6 2 January 2005 -add new error message and documentation for POP3 servers that cannot uniquely identify messages in a mailbox. Thanks: Thomas Schwinge. Version 4.2.5 8 December 2004 -fix typo in getmailcore/exceptions.py that would raise a NameError exception in certain rare cases. Thanks: Gour ?. Version 4.2.4 22 November 2004 -one type of filter error would cause getmail to skip to the next configured mail account, rather than simply proceeding to the next message from the same account. Fixed. Thanks: Adrien Beau. -documentation updates. Version 4.2.3 18 November 2004 -documentation updates. -getmailrc examples file updated. Thanks: Scott Robbins. -clarify error message when user insecurely tries to deliver mail as GID 0. Version 4.2.2 11 October 2004 -in child delivery processes, change real as well as effective uid/gid. Thanks: David Watson. -handle corrupted oldmail file better. Thanks: Matthias Andree. Version 4.2.1 8 October 2004 -set message attributes on corrupt container objects to prevent problems with destinations that expect multidrop-retrieved messages. Thanks: Harry Wearne. -move tests for existence of file from mbox destination initialization to delivery method, and change error from configuration to delivery error. Thanks: David Watson. Version 4.2.0 18 September 2004 -SECURITY: previous versions of getmail contain a security vulnerability. A local attacker with a shell account could exploit a race condition (or a similar symlink attack) to cause getmail to create or overwrite files in a directory of the local user's choosing if the system administrator ran getmail as root and delivered messages to a maildir or mbox file under the control of the attacker, resulting in a local root exploit. Fixed in versions 4.2.0 and 3.2.5. This vulnerability is not exploitable if the administrator does not deliver mail to the maildirs/mbox files of untrusted local users, or if getmail is configured to use an external unprivileged MDA. This vulnerability is not remotely exploitable. Thanks: David Watson. My gratitude to David for his work on finding and analyzing this problem. -Now, on Unix-like systems when run as root, getmail forks a child process and drops privileges before delivering to maildirs or mbox files. getmail will absolutely refuse to deliver to such destinations as root; the uid to switch to must be configured in the getmailrc file. -revert behaviour regarding delivery to non-existent mbox files. Versions 4.0.0 through 4.1.5 would create the mbox file if it did not exist; in versions 4.2.0 and up, getmail reverts to the v.3 behaviour of refusing to do so. Version 4.1.5 13 September 2004 -getmail would not delete messages from the server if it was configured not to retrieve them and the delete_after directive was not in use (i.e. user normally left messages on server but occasionally wanted to force-delete them). Fixed. Thanks: Frankye Fattarelli. Version 4.1.4 1 September 2004 -change failure of a message filter to produce at least as many mail headers as it was provided from a non-fatal error to warning. If your filter strips headers, getmail will now warn you about it, but will not consider it an error. -documentation additions. Version 4.1.3 31 August 2004 -enhance warning diagnostics about non-accessible or non-writable maildirs. -change method of determining name of local host; only fall back to getfqdn() if the result of gethostname() does not contain a dot. -documentation enhancements. Version 4.1.2 28 August 2004 -dumping config would raise an exception since 4.1.0; fixed. Thanks: Ilya Krel. Version 4.1.1 27 August 2004 -getmail raised an exception after processing all accounts, while printing a summary, if verbose was set to 2. Fixed. Thanks: Matthias Andree. Version 4.1.0 24 August 2004 -biggest change is multiple verbosity levels. To support this from rc file, the verbose parameter had to change from a boolean to an integer. Update your getmail rc files: for quiet, set verbose to 0. For more output, set it to 1 or 2. 2 includes info about messages not retrieved, etc. -add rc filename to error messages -change fix from 4.0.11 for email module raising exception during .flatten(). Thanks: Jürgen Nagel. -some types of SSL error could cause getmail to exit instead of continuing to the next mail account; fixed. Thanks: Matthias Andree. Version 4.0.13 19 August 2004 -unrecognized parameters could trigger a traceback instead of a warning. Fixed. Thanks: Frankye Fattarelli. Version 4.0.12 19 August 2004 -forgot to add the new Message attribute (from 4.0.11) to its slots declaration. Fixed. Version 4.0.11 18 August 2004 -change oldmail file writing to save a few bytes of disk space -documentation additions. -Python's standard library email.Message().flatten() could barf on certain types of badly mis-formatted messages (instead of the during instantiation, like the /other/ buggy cases). Hope this is fixed in Python 2.4. Developed a work-around for getmail. Thanks: Jürgen Nagel. -changes to "normal" output of getmail to make it slightly less verbose. Version 4.0.10 12 August 2004 -cosmetic fix to output: add whitespace after timestamp when not retrieving message. Thanks: Matthias Andree. -include MANIFEST.in in source distribution, to assist users in building "built distributions" from it. -change a few output messages' level to make verbose mode slightly less verbose, and add --debug switch to get "more verbose" behaviour. Version 4.0.9 9 August 2004 -fix cosmetic error for IMAP mailboxes with 0 messages in them. -change method of obtaining uidvalidity from IMAP server and remove wrapper. -previously, connecting to an IMAP-SSL server could fail if a non-SSL IMAP connection were attempted immediately before. This was due to the Python bug discussed here: http://sourceforge.net/tracker/index.php?func=detail&aid=945642&group_id=5470&atid=305470 Implemented a workaround for getmail. Thanks: Payal Rathod. Version 4.0.8 6 August 2004 -add an extra error message if you ask getmail to deliver to a maildir, but getmail can't check the contents of it due to permissions. Thanks: Clemens Buschmann. -fix breakage introduced in 4.0.7: getmail would forget a message was "seen" after a cycle of not retrieving it. Thanks: Payal Rathod. Version 4.0.7 5 August 2004 -change failure of a message filter to produce at least as many mail headers as it was provided from a fatal to a non-fatal error. The message will be skipped. Thanks: Payal Rathod. -a few non-conformant IMAP4 servers don't implement SEARCH, so getmail couldn't get a list of UIDs. Changed to use FETCH instead. Thanks: Matthias Andree. -prevent traceback if IMAP SSL connection closed during connect(). Thanks: Payal Rathod. -add warning if unknown parameters are supplied to a retriever, filter, or destination. -write subclasses of Python imaplib classes to work around missing UIDVALIDITY select() response. Add it to the state getmail keeps. Thanks: Matthias Andree. -move message state saving to later, so getmail doesn't falsely remember having handled a message. Thanks: Matthias Andree. -change location of documentation/man pages to /share/{doc/getmail,man}/ to be more comformant with the FHS. -documentation updates Version 4.0.6 4 August 2004 -if the connection failed in a certain way, getmail could forget which messages it had already retrieved, and therefore retrieve them again when the next successful connection occurred. Fixed. Thanks: Wim Uyttebroek. -add win32 executable installer as third download option. Version 4.0.5 3 August 2004 -add additional error handler for certain network errors with POP3-over-SSL connections. Thanks: Frank Benkstein. -rename retriever class for Demon UK: it should have been SDPS; my transposition. Thanks: Paul Howarth. Version 4.0.4 2 August 2004 -corrupt-message handler introduced in 4.0.1 needed tweaking. Thanks: Bernhard Riedel. Version 4.0.3 2 August 2004 -some types of socket errors would raise an exception instead of letting getmail gracefully continue; fixed. Thanks: David. -documentatation updates; fix two typos and add to the section on using ClamAV with getmail. -changes to my release process; the current version's URL will not change when a new version is released. See, I try to help others, even when they don't do the same... Version 4.0.2 30 July 2004 -trying to use MDA_qmaillocal destination with a non-multidrop retriever would raise an exception instead of printing a configuration error message. Fixed. Thanks: Clemens Buschmann. Version 4.0.1 26 July 2004 -documentation fixes. Thanks: Roland Hill. -add handler for badly-misformatted messages as a workaround for Python 2.3.x, where the email module can raise exceptions while parsing messages. Thanks: Paul. Version 4.0.0 23 July 2004 -exit beta; final release of version 4.0.0, with approximately four thousand lines (~150 kbytes) of Python code and five thousand lines (~180 kbytes) of documentation. Version 4.0.0b10 21 July 2004 -documentation updates and cleanups -add MultiGuesser destination; it's like MultiSorter, but guesses at the message recipient based on addresses found in the message header, to be used for mail sorting if you don't have a proper domain/multidrop mailbox. -consolidate initialization code from retrievers to RetrieverSkeleton base class. -slight change to main script and retriever base classes to change the way the retriever objects are destroyed and garbage-collected -get rid of unneeded hostname variable in MultiDestinationBase -add "Summary:" header line to main script output in verbose mode -wrap code for 80 columns -fix error message when a filter doesn't return a message properly. Thanks: Shantanu Kulkarni. Version 4.0.0b9 19 July 2004 -change syntax of MultiSorter's locals parameter. The previous syntax was fragile and was mostly a holdover from previous versions; it is now a tuple of items, each of which is a 2-tuple of quoted strings. -add Python version to --dump output. -eliminate redundant _confstring() method in _retrieverbases.py. -small documentation updates. Version 4.0.0b8 18 July 2004 -documentation updates. -include RPM build. Rapidly approaching release state. Version 4.0.0b7 17 July 2004 -changes to an "impossible" error handler in _child_handler(), just in case. -move some code from destinations.MultiSorter() into new MultiDestinationBase base class. -fix MultiDestinationBase._get_destination() -add MultiDestination class to deliver messages to multiple destinations unconditionally. -cosmetic fixes to output Version 4.0.0b6 15 July 2004 -move common child-handling code out of filters and destinations into new baseclasses.ForkingBase() class. -add __all__ declarations to modules that were missing them, to help prevent namespace clutter for others Version 4.0.0b5 15 July 2004 -apply the child fix from b3 to Filter_classifier; I missed this in the update. Thanks: Dave Osbourn. -cosmetic fix for output describing filters and destination objects. Thanks: Dave Osbourn. -catch configuration error of non-multidrop retriever with multidrop-only destination. This broke when I cleaned up the message attribute interface in alpha 29. Thanks: Dominik Kacprzak. Version 4.0.0b4 14 July 2004 -remove unneeded code raising a ConfigurationError in Filter_TMDA -clean up output for Maildir, Mboxrd destinations and getmail_maildir script, messages dropped by filters, various other bits -add --show-default-install-dirs to setup.py -documentation updates, including more clarification and examples of installation options -tweak to getmail manpage. Thanks: Frankye Fattarelli. Version 4.0.0b3 13 July 2004 -different fix for the reaping child problem. This one tested by Paul and confirmed working on Mandrake 10's kernel. -add Filter_TMDA filter class. -changes to setup.py to account for --install-data specifying a non-default path for the documentation and man pages Version 4.0.0b2 13 July 2004 -documentation fixes regarding installation. Thanks: Emily Jackson. -do environment variable expansion in paths and arguments (before substitutions like %(sender), etc) in addition to expanding leading ~/ or ~user/ . Thanks: Paul. -try change to code which reaps child processes; the previous code failed on a few Linux systems (perhaps the ones where the kernel changed fork() behaviour to run the child first?) Reported by Paul. Version 4.0.0b1 10 July 2004 -first beta release -add missing os import to getmail_maildir -fix received_by in getmail_mbox and don't create Received: header -a handful of trivial changes to make the code idiomatically consistent -fix to oldmail file writing -documentation updates Version 4.0.0a30 8 July 2004 -documentation updates -change header-adding code from filters to a method in Message class. Thanks: Frankye Fattarelli. -make MultiSorter pass retriever info down to its sub-destinations. This fixes a problem reported by Frankye Fattarelli. Version 4.0.0a29 8 July 2004 -flesh out the getmailrc-examples file (note that it's been renamed from getmailrc-example) -clean up message attributes. Make Message() class. Move flatten_msg() out of utilities and make it into a Message method. Remove header-length checking when generating flattened representation of message. Change callers to use the new class. -move SENDER check from deliver_maildir() to getmail_maildir where it belongs. -update the miscellaneous documentation files -last alpha, I think Version 4.0.0a28 7 July 2004 -documentation additions and changes. -add manpages for getmail, getmail_maildir, getmail_box. These go in PREFIX/man/man1/. Version 4.0.0a27 4 July 2004 -documentation fixes and additions. Split documentation into reasonably-sized files. I need to add inter- and intra-document links to most of the documentation yet. -add auto-generated tables of contents to each doc file. -start flushing out the website with copies of the docs, download directories, etc. Version 4.0.0a26 4 July 2004 -add explicit checks for multidrop classes to prevent users from incorrectly specifying certain header fields as recording the envelope recipient address -documentation fixes and additions Version 4.0.0a25 2 July 2004 -add handler for KeyboardInterrupt. Thanks: Thomas Schwinge. -change setup script to not error if the specified directory prefix doesn't exist. Thanks: Thomas Schwinge. -pass retriever protocol info to filters. Thanks: Frankye Fattarelli. -first inclusion of "real" documentation. See docs/documentation.html or its plaintext counterpart docs/documentation.txt. Version 4.0.0a24 1 July 2004 -setup script/distutils changes. The setup.py script now looks for --prefix or --home specifying an alternate installation directory, and otherwise defaults to the hierarchy that Python was installed to. /usr/share/doc/ or similar is no longer hardcoded anywhere. Version 4.0.0a23 1 July 2004 -changes to MDA_qmaillocal: add "group" parameter, and make "user" parameter also have getmail change UID before invoking qmail-local. -change two instances of socket.gethostname() to socket.getfqdn() -change localparttranslate to localpart_translate -add explicit checks for socket.ssl(), which is optional. Raise getmailConfigurationError if they're not present and the user tries to use it. Thanks: Thomas Schwinge. -fix missing received_by in destinations. Thanks: Frankye Fattarelli and Andrew Raines. Version 4.0.0a22 30 June 2004 -MultiSorter default destination can now be a named destination section as well, so postmaster mail can go to an external MDA, etc. -deliver_maildir writes out a new Return-Path: header field if SENDER is set. -include getmail_maildir and getmail_mbox delivery scripts for use as MDAs with other programs. -add starting/finishing log lines for each retriever -change eval_bool to raise exception on unexpected values, rather than using Python's native idea of what is boolean True and False -ensure no message has partial final line regardless of delivery method. Thanks: Thomas Schwinge. -get rid of msg_lines(), mbox_timestamp() and their only callers. -add the Delivered-To: and Received: header creation like getmail v.3 had, and boolean options delivered_to and received to allow the user to disable them. This necessitates adding some data attributes to retriever classes and additional code to destination classes and utilities. Thanks: Thomas Schwinge. -drop .py suffix on the name of the main getmail script -declare the three scripts to be scripts instead of modules (for distutils) -some fixups to the distutils setup script. If files or directories are still installed with "wrong" permissions (like 0600/0700 instead of 0644/0755), please let me know. -a few miscellaneous fixups -heading for beta, working on documentation ... Version 4.0.0a21 27 June 2004 -leave Return-Path header field alone for program deliveries. Thanks: Andrew Raines. Version 4.0.0a20 25 June 2004 -fix typo in Filter_classifier -wrap additional exception check around executing subprocesses to ensure nonzero exit on error -finish MDA_qmaillocal address handling and add strip_delivered_to parameter. Document MDA_qmaillocal. -change email module import in utilities.py. Submit bug report to Python bug tracker. Thanks: Frankye Fattarelli. Version 4.0.0a19 24 June 2004 -bring some docstrings, comments, and documentation up to date. -add Filter_classifier filter module, to support adding information to the message header from programs that don't provide the original message back on stdout (like clamscan/clamdscan, apparently). Thanks: Frankye Fattarelli. -missed seek(0) in msg_flatten(). Add missing imports. Thanks: Andrew Raines. Version 4.0.0a18 24 June 2004 -add msg_flatten(), msg_lines() and use them exclusively to prevent .as_string() from changing header fields for spurious reasons. Thanks: Andrew Raines. -remove code doing From_-escaping in Mboxrd and let the email module do it. -fix copy & paste errors in change_uidgid(). Thanks: Andrew Raines. -catch timeouts Version 4.0.0a17 23 June 2004 -add exitcodes_keep and exitcodes_drop parameters to Filter_external. Thanks: Frankye Fattarelli. Version 4.0.0a16 23 June 2004 -add user and group, and allow_root_commands parameters to MDA_external destination and Filter_external filter classes. -make import pwd fail gracefully for Windows, etc. -add allow_root_commands parameter to MDA_external destination and Filter_external filter classes to force overriding of running external commands as root. -pass reference to configparser to retriever, destination, and filters -add MultiSorter destination of "[section]" which invokes another destination from the same getmail rc file. Use this to deliver to programs by including an MDA_external destination in a section named [foo] in the rc file, then specify that section name in the MultiSorter in the locals part, i.e. [destination] type = MultiSorter postmaster = /path/to/maildir/ locals = joe /path/to/maildir/ sam /path/to/mbox/ chris [myprogram] [myprogram] type = MDA_external path = /usr/local/bin/mymda arguments = ("--strip-attachments", "-f%(sender)", "--fast") Don't try to cause a loop using this to point to itself. You have been warned. -MultiSorter is now a little stricter about destination names. A maildir (after any tilde expansion, if applicable) must start with "." or "/" and end with "/". An mboxrd (after any tilde expansion, if applicable) must start with "." or "/". This means if you previous had something like: locals = sam@example.net subdir/maildir/ You'll need to change it to: locals = sam@example.net ./subdir/maildir/ Of course, locals = sam@example.net ~sam/maildir/ is already okay if sam is a valid user and has a valid home directory. -add message_log_syslog Version 4.0.0a15 22 June 2004 -add summary "Retrieved X messages for ..." for each rc file at program close. Thanks: Frankye Fattarelli. -add explicit delete of retriever after quit() to try to force writing oldmail file immediately instead of at garbage collection. Python makes no guarantees about when garbage collection takes place, so this won't be perfect. Thanks: Julian Gomez. -add message_log -add traceback formatting on exception -change --dump to include filters. Change order of operations. Version 4.0.0a14 22 June 2004 -fix IMAP-over-SSL. socket.ssl objects are apparently incompatible with socket timeouts, even in the IMAP_SSL class. -ensure --trace debugging output doesn't output passwords. Thanks: Julian Gomez. -IMAP classes would stop indexing mailboxes when they hit an empty one. Fixed. Thanks: Julian Gomez. Version 4.0.0a13 21 June 2004 -add missing default for max_messages_per_session. Thanks: Frankye Fattarelli. Version 4.0.0a12 21 June 2004 -slight change to quit() to ensure old connections are closed and garbage-collected before trying to make new ones. Thanks: Frankye Fattarelli. -move default documentation directory to /usr/local/share/doc/ . -filters.py now checks that the filtered message is at least basically sane in that it has a header no shorter than the message it was supplied. Let me know if this causes problems. -add and document max_messages_per_session Version 4.0.0a11 21 June 2004 -add MultidropIMAPRetriever and MultidropIMAPSSLRetriever -add move_on_delete parameter to all IMAP retrievers. -add and document delete_after and max_message_size options -document read_all and delete options -rename pop3ssl.py -fix --quiet. i.e., getmail should again operate truly silently if you want it to. -retrievers' quit() method wasn't getting called. Fix. Thanks: Frankye Fattarelli. Version 4.0.0a10 19 June 2004 -fix the retrieve-every-other-message bug when delete == True. -move base classes out of retrievers.py into _retrieverbases.py -fix exception when retrieving mail from an empty IMAP account Version 4.0.0a9 19 June 2004 -accidentally edited initialize() in the wrong class; it broke POP3. This update should fix it. Version 4.0.0a8 18 June 2004 -fix 6 instances of getmailConfirurationError typo in retrievers.py -finish basics of IMAP support -- i.e., it successfully retrieves mail. delete might not work yet. IMAP-over-SSL /might/, but the server I was testing against wasn't working with SSL (no connection). -handle email.Utils.getaddresses returning None. Thanks: Frankye Fattarelli. -fix default port for MultidropPOP3SSLRetriever. Version 4.0.0a7 17 June 2004 -debug and rewrite POP3-over-SSL functionality. Involved writing around some ugly limitations and kludges in Python's socket.ssl and poplib.POP3 code. But POP3-over-SSL actually works now, and is fairly clean. Let me know if it hangs; I might need to change the way blocking mode is handled on the underlying socket. Version 4.0.0a6 16 June 2004 -add pseudofile class to implement readline() and sslwrapper to implement sendall() on ssl objects. ssl objects in Python don't behave like a file, or like a socket; they're just ... broken. It's a deficiency in the Python standard library. This might make POP3-over-SSL work, or it might need more work yet. I've got it talking some SSL, but it hangs at the moment. Might be blocking in read(). -move Mboxrd file locking to _deliver_message(). Thanks: Frankye Fattarelli. -make --trace more useful and add extra debug info to main script. -eliminate noise from defaults in non-[options] section processing -found Python's ConfigParser .getboolean() method failed when the passed-in default was a non-string. Maybe I should have stayed with my own replacement configuration parser, as it properly handled this case ... submitted a patch to the Python bug tracker and added a workaround to getmail for the moment. -/really/ enable delete and read_all in [options] of rc file. Version 4.0.0a5 16 June 2004 -document tuple syntax. Thanks: Frankye Fattarelli. -fix "filters" name collision in script. Thanks: Frankye Fattarelli. Version 4.0.0a4 15 June 2004 -fix default values in three classes' parameters which previously relied on type conversion. Thanks: Andrew Raines. -fix default port for POP3-over-SSL and "username" parameter in documentation. Thanks: Frankye Fattarelli. -enable delete and read_all in [options] of rc file. Change handling of verbose. Version 4.0.0a3 15 June 2004 -fix typo in processing filters. Thanks: K. Shantanu. -fix typo in processing non-default parameters. Thanks: Andrew Raines. -document SSL retrievers -move retrievers.py configuration functionality into ConfigurableBase class -convert filters.py to use ConfigurableBase -convert destinations.py to use ConfigurableBase Version 4.0.0a2 14 June 2004 -fix default/'default' in filters module. Thanks: Andrew Raines. -add the mix-in classes for SSL support with POP3 classes. Meant small rewrites to the POP3 classes, but the design should be even cleaner now. Version 4.0.0a1 14 June 2004 -first alpha release of getmail version 4 Changes since getmail version 3 ------------------------------- -complete rewrite -switch to Python version 2.3.3: -increased code readability (augmented assignment, list comprehensions, string methods, etc) -eliminate external modules (ConfParser, timeoutsocket) -use standard library modules which have come up to reasonable levels of quality and functionality over old custom code (optparse, ConfigParser) -no longer require workarounds for older, broken standard library modules/ functions -modular, object-oriented framework for retrievers, destinations, filters allows extensibility -make more platform-agnostic (transparently support system EOL convention, etc) -support for multiple retriever/account types: -POP (single-user, multidrop, SDPS) -IMAPv4 (single-user, multidrop) -full native support for POP3-over-SSL and IMAP-over-SSL. -configuration (rc) file format changes: -support multiple rc files per invocation -one mail account per rc file getmail6-6.19.07/docs/COPYING000066400000000000000000000432541474536475600154260ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. getmail6-6.19.07/docs/HACKING000066400000000000000000000002431474536475600153510ustar00rootroot00000000000000Should you want to modify the documentation files here: - Install links - Modify the .html - Regenerate the .txt by running "make doc" from the toplevel directory getmail6-6.19.07/docs/THANKS000066400000000000000000000025741474536475600153060ustar00rootroot00000000000000We would like to thank the following individuals for their gracious contributions to the development of getmail/getmail6: First and foremost, Charles Cazabon for all his hard work through to the release of getmail version 5.14. Secondly, Roland Puntaier for porting getmail to Python 3. Thirdly, in alphabetical order, the following people for their patches, bug reports, suggestions, packaging efforts, and documentation updates: Michael Büker 'gswan' Dan Jacobson 'DaveF475' 'jborme' Dmytri Kleiner 'Kr1ss-XD' Remco Rijnders Johnny A. Solbu Boris Wachtmeister We would like to thank the following individuals and organizations for their gracious contributions to the development of getmail version 4 and 5. getmail 4 sponsors ------------------ Texas Communications getmail version 4 alpha testers ------------------------------- Andrew A. Raines Julian Gomez Frankye Fattarelli Thomas Schwinge Jody J. Hietpas Frank Benkstein Shantanu Kulkarni and others ... other thanks ------------ Clemens Hermann, for ... well, lots Fredrik Steen, maintainer of the Debian packages of getmail Earlier versions of getmail also benefitted from the contributions of others, including the following: getmail sponsors ---------------- Klinikum Landsberg of Germany Also see the CHANGELOG for thanks to individual contributors for bug reports, bug fixes, feature patches, and other contributions. getmail6-6.19.07/docs/configuration.html000066400000000000000000004014571474536475600201330ustar00rootroot00000000000000 getmail configuration

getmail documentation

This is the documentation for getmail version 6, a port of getmail version 5 to python 3.

getmail6 is Copyright © 1998-2025 by Charles Cazabon and others:
<charlesc-getmail @ pyropus.ca>
<roland.puntaier @ gmail.com>

getmail and getmail6 are licensed under the GNU General Public License version 2 (only).

Table of Contents

Configuring getmail

Once getmail is installed, you need to configure it before you can retrieve mail with it. Follow these steps:

  1. Create a data/configuration directory. getmail complies with the XDG basedir specification: $XDG_CONFIG_HOME/getmail/ defaulting to ~/.config/getmail/. For backward compatibility reasons, getmail also checks $HOME/.getmail/. If you want a different location, you will need to specify it on the getmail command line. In general, other users should not be able to read the contents of this directory, so you should set the permissions on it appropriately.
    mkdir -m 0700 $HOME/.getmail
            
  2. Create a configuration file in the configuration/data directory. The default name is getmailrc. If you choose a different filename, you will need to specify it on the getmail command line. If you want to retrieve mail from more than one mail account, you will need to create a separate rc file for each account getmail should retrieve mail from.

Creating a getmail rc file

The configuration file format is designed to be easy to understand (both for getmail, and for the user). It is broken down into small sections of related parameters by section headers which appear on lines by themselves, enclosed in square brackets, like this:

[section name]

Each section contains a series of parameters, declared as follows:

parameter_name = parameter_value

A parameter value, if necessary, can span multiple lines. To indicate that the second and subsequent lines form a continuation of the previous line, they need to begin with leading whitespace, like this:

first_parameter = value
    first parameter value continues here
second_parameter = value

You can annotate your configuration files with comments by putting them on lines which begin with a pound sign, like this:

first_parameter = value
# I chose this value because of etc.
second_parameter = value

Each rc file requires at least two specific sections. The first is retriever, which tells getmail about the mail account to retrieve messages from. The second is destination, which tells getmail what to do with the retrieved messages. There is also an optional section named options , which gives getmail general configuration information (such as whether to log its actions to a file), and other sections can be used to tell getmail to filter retrieved messages through other programs, or to deliver messages for particular users in a particular way.

Parameter types and formats

Several different types of parameters are used in getmail rc files:

Each parameter type has a specific format that must be used to represent it in the getmail rc file. They are explained below. Each parameter documented later specifies its type explicitly.

string

Specify a string parameter value with no special syntax:

parameter = my value

integer

Specify an integer parameter value with no special syntax:

parameter = 4150

boolean

A boolean parameter is true or false; you can specify its value with the (case-insensitive) words "true" and "false". The values "yes", "on" and 1 are accepted as equivalent to "true", while values "no", "off" and 0 are accepted as equivalent to "false". Some examples:

parameter = True
parameter = false
parameter = NO
parameter = 1

tuple of quoted strings

A tuple of quoted strings is essentially a list of strings, with each string surrounded by matching double- or single-quote characters to indicate where it begins and ends. The list must be surrounded by open- and close-parenthesis characters. A tuple may have to be a specific number of strings; for instance, a "2-tuple" must consist of two quoted strings, while a "4-tuple" must have exactly four. In most cases, the number of strings is not required to be a specific number, and it will not be specified in this fashion.

In general, a tuple of quoted strings parameter values should look like this:

parameter = ('first string', 'second string',
    "third string that contains a ' character")

However, tuples of 0 or 1 strings require special treatment. The empty tuple is specified with just the open- and close-parenthesis characters:

parameter = ()

A tuple containing a single quoted string requires a comma to indicate it is a tuple:

parameter = ("single string", )

tuple of integers

This is very similar to a tuple of quoted strings, above, minus the quotes. Some examples:

parameter = (1, 2, 3, 4, 5)
parameter = (37, )
parameter = ()

tuple of 2-tuples

This is a tuple of items, each of which is a 2-tuple of quoted strings. You can think of this as a list of pairs of quoted strings.

# Three pairs
parameter = (
    ("first-a", "first-b"),
    ("second-a", "second-b"),
    ("third-a", "third-b"),
    )
# One pair
parameter = (
    ("lone-a", "lone-b"),
    )

Creating the [retriever] section

The retriever section of the rc file tells getmail what mail account to retrieve mail from, and how to access that account. Begin with the section header line as follows:

[retriever]

Then, include a type string parameter to tell getmail what type of mail retriever to use to retrieve mail from this account. The possible values are:

What is a "multidrop" mailbox? How do I know if I have one?

Some ISPs, mailhosts, and other service providers provide a mail service they refer to as a "domain mailbox" or "multidrop mailbox". This is where they register a domain for you, and mail addressed to any local-part in that domain ends up in a single mailbox accessible via POP3, with the message envelope (envelope sender address and envelope recipient address) recorded properly in the message header, so that it can be re-constructed after you retrieve the messages with POP3 or IMAP. The primary benefit of this is that you can run your own MTA (qmail, Postfix, sendmail, Exchange, etc.) for your domain without having to have an SMTP daemon listening at a static IP address.

Unfortunately, a lot of what is advertised and sold as multidrop service really isn't. In many cases, the envelope recipient address of the message is not properly recorded, so the envelope information is lost and cannot be reconstructed. If the envelope isn't properly preserved, it isn't a domain mailbox, and you therefore can't use a multidrop retriever with that mailbox.

To determine if you have a multidrop mailbox, check the following list: if any of these items are not true, you do not have a multidrop mailbox.

  • the mailbox must receive one copy of the message for each envelope recipient in the domain; if the message was addressed to three local-parts in the domain, the mailbox must receive three separate copies of the message.
  • the envelope sender address must be recorded in a header field named Return-Path at the top of the message. If the message (incorrectly) already contained such a header field, it must be deleted before the envelope sender address is recorded.
  • the envelope recipient address must be recorded in a new header field. These may be named various things, but are commonly Delivered-To, X-Envelope-To, and similar values. In the case of messages which had multiple recipients in the domain, this must be a single address, reflecting the particular recipient of this copy of the message. Note that this field (and the envelope recipient address) are not related to informational header fields created by the originating MUA, like To or cc.

If you're not sure whether you have a multidrop mailbox, you probably don't. You probably want to use SimplePOP3Retriever (for POP3 mail accounts) or SimpleIMAPRetriever (for IMAP mail accounts) retrievers.

Specify the mail account type with one of the above values, like this:

type = typename

Then, include lines for any parameters and their values which are required by the retriever. The parameters and their types are documented below.

Common retriever parameters

All retriever types take several common required parameters:

  • server (string) — the name or IP address of the server to retrieve mail from
  • username (string) — username to provide when logging in to the mail server. If you enable use_netrc, the netrc file can supply a username for each retriever.

All retriever types also take several optional parameters:

  • port (integer) — the TCP port number to connect to. If not provided, the default is a port appropriate for the protocol (110 for POP3, etc.)
  • password (string) — password to use when logging in to the mail server. If not using Kerberos authentication -- see below -- getmail gets the password credential for the POP/IMAP server in one of the following ways:
    1. from the password configuration item in the getmailrc file
    2. by running an arbitrary command specified with the password_command parameter (see below)
    3. from Python keyring if available
    4. if use_netrc option is True, from a .netrc file
    5. if not found via any of the above methods, getmail will prompt for the password when run
    To store your POP/IMAP account password into the Python keyring, ensure the password is not provided in the getmailrc file, and run getmail with the special option --store-password-in-keyring; getmail will run, prompt you for the password, store it in the Python keyring, and exit without retrieving mail. If this option is not recognized, your Python installation does not have Python keyring.
  • password_command (tuple of quoted strings) — retrieve the account password by running an arbitrary external program. The program must write the password and nothing else to stdout, and must exit with a status of 0 on success. Note that the password parameter (above) overrides this parameter; specify one or the other, not both. This parameter is specified as the program to run as the first string in the tuple, and all remaining strings are arguments passed to that program.
    password_command = ("/path/to/password-retriever", "-p", "myaccount@example.org")
            

All POP3 retriever types also take the following optional parameters:

  • use_xoauth2 (boolean) — whether to use XOAUTH2 for login with the POP3 server. If not set, normal password-based authentication is used. This currently supports Gmail and Microsoft Office 365; if anyone extends this to support other POP3 providers, please let me know so I can include such support in getmail. Note that using XOAUTH2 is no more secure than a regular getmail configuration with a mode 0600 getmailrc file. You will need to set password_command as well to tell getmail to invoke the getmail-gmail-xoauth-tokens helper program; that script requires a positional argument to tell it json file where to read the initial tokens from and where it writes the access and refresh tokens to, and the file requires manual initial setup. Keep write access to the json file. This functionality was contributed by Stefan Krah, who has additional information about using it here: http://www.bytereef.org/howto/oauth2/getmail.html. See docs/getmailrc-examples.

All IMAP retriever types also take the following optional parameters:

  • mailboxes (tuple of quoted strings) — a list of mailbox paths to retrieve mail from, expressed as a Python tuple. If not specified, the default is to retrieve mail from the mail folder named INBOX. You might want to retrieve messages from several different mail folders, using a configuration like this:
    mailboxes = ("INBOX", "INBOX.spam",
        "mailing-lists.model-railroading")
            
    Note that the format for hierarchical folder names is determined by the IMAP server, not by getmail. Consult your server's documentation or postmaster if you're unsure what form your server uses. If your mailbox names contain non-ASCII characters, ensure that your getmailrc file is stored with UTF-8 encoding so that getmail can correctly determine the unicode character names that need to be quoted in IMAP's modified UTF-7 encoding; if you do not do this, the mailbox names will not match what the server expects them to be, or will cause UnicodeErrors when attempting to load your getmailrc file. As a special case, in getmail version 4.29.0 and later, the unquoted base (non-tuple) value ALL (case-sensitive) means to retrieve mail from all selectable IMAP mailboxes in the account. To retrieve messages from all mailboxes, you would use:
    mailboxes = ALL
            
  • use_peek (boolean) — whether to use PEEK to retrieve the message; the default is True. IMAP servers typically mark a message as seen if PEEK is not used to retrieve the message content. Versions of getmail prior to 4.26.0 did not use PEEK to retrieve messages.
  • move_on_delete (string) — if set, messages are moved to the named IMAP mail folder before being deleted from their original location. The specified mail folder must exist; getmail will not create it. Note that if you configure getmail not to delete retrieved messages (the default behaviour), they will not be moved at all.
  • cache_uid (string) — by setting the file for the cache one enables caching the highest imap UID seen to be used as the starting position when fetching the uid list the next time.
  • record_mailbox (boolean) — whether to add a X-getmail-retrieved-from-mailbox: header field to retrieved messages, containing the name of the selected mailbox that the message was retrieved from. This is on by default, but can be disabled.
  • use_kerberos (boolean) — whether to use Kerberos authentication with the IMAP server. If not set, normal password-based authentication is used. Note that when you use Kerberos authentication, it is up to you to ensure you have a valid Kerberos ticket (perhaps by running a ticket-renewing agent such as kstart or similar). This feature requires that a recent version of pykerberos with GSS support is installed; check your OS distribution or see http://honk.sigxcpu.org/projects/pykerberos/" for details.
  • use_xoauth2 (boolean) — whether to use XOAUTH2 for login with the IMAP server. If not set, normal password-based authentication is used. This currently supports Gmail and Microsoft Office 365; if anyone extends this to support other IMAP providers, please let me know so I can include such support in getmail. Note that using XOAUTH2 is no more secure than a regular getmail configuration with a mode 0600 getmailrc file. You will need to set password_command as well to tell getmail to invoke the getmail-gmail-xoauth-tokens helper program; that script requires a positional argument to tell it json file where to read the initial tokens from and where it writes the access and refresh tokens to, and the file requires manual initial setup. Keep write access to the json file. This functionality was contributed by Stefan Krah, who has additional information about using it here: http://www.bytereef.org/howto/oauth2/getmail.html. See docs/getmailrc-examples.
  • imap_search, imap_on_delete (string) — imap_search is the second parameter of Python's IMAP search. Set
    imap_search = UNSEEN
    to skip read messages. The value is case-insensitive. It may be in parentheses. As an example
    imap_search = Unseen
    imap_on_delete = \Seen
    fetches only new messages and sets the message flag to SEEN on "delete" (only \Delete in it will actually delete the email on the server). Setting the flag will also override the global delete = true (which is also made sure via -d in the command line). \Seen avoids further retrieval without deleting the message on the server. The default is
    imap_search = ALL
    imap_on_delete = (\Deleted \Seen)
    For more on IMAP SEARCH see rfc3501.

    The command line parameter

    --searchset/-s

    (search and set) overrides imap_search to select emails to retrieve and imap_on_delete to set flags on the server A starting , becomes IMAP flag char \. -s implies -d, ie is like -ds. -ds, alone is \SEEN. -ds, means "mark as read". Without , it is a search string to select mail to retrieve.

  • imap_id_extension (boolean). If set, getmail sends getmail 6.0.0 as client ID to the server. The default is False.

SSL Client Parameters

All SSL-enabled retriever types also take the following options, to allow specifying the use of a particular client key and client certificate in establishing a connection to the server.

  • keyfile (string) — use the specified PEM-formatted key file in the SSL negotiation.
  • certfile (string) — use the specified PEM-formatted certificate file in the SSL negotiation.

SSL Certificate Validation and Server Parameters

All SSL-enabled POP and IMAP retriever types also take the following options, allowing you to require validation of the server's SSL certificate, or to check the server's certificate fingerprint against a known good value, or to control the specific SSL cipher used during the connection.

Note: using these features, including server certificate validation, requires using Python 2.7 or higher with getmail. If you use an earlier version of Python, these features will not work, and no server certificate validation will be performed. Also note that these features are not currently implemented for SPDS retrievers; I would be interested in hearing from SPDS users who desire these features.

  • ca_certs (string) — advanced option to perform validation of the server's SSL certificate. Specify the path to a PEM-formatted list of 1 or more valid and trusted root certification authority (CA) certificates. Note: this option is only available with Python 2.7 or higher.
    To find out which root CA is used to sign the chain of certificates for a given server, you can run
    openssl s_client -showcerts -connect HOST:PORT < /dev/null 2>/dev/null \
      | grep '^[[:space:]]*i:' | tail -n 1
    
    If the server's certificate cannot be validated based upon the supplied trusted root certificates, getmail will abort the connection.
    Root certificates are not supplied with getmail; your OS probably installs a set by default for use by the system, or you may wish to use a specific set of trusted root certificates provided by your employer or a trusted third party. Common locations for OS-supplied SSL root certification authority certificates include:
    • Linux (Debian, Ubuntu, Arch, SuSE):
      /etc/ssl/certs/
    • Linux (RedHat, Fedora, CentOS):
      /etc/pki/tls/certs/
    • FreeBSD:
      /usr/local/share/certs/
    • OpenBSD:
      /etc/ssl/
    • OSX:
      /System/Library/OpenSSL/certs/
    • Windows: ask Microsoft
  • ssl_ciphers (string) — advanced option to control which SSL cipher algorithms will be allowed to proceed. See the Open SSL documentation for details. If the specified setting results in no possible ciphers available, getmail will abort the connection. E.g. on error DH_KEY_TOO_SMALL, one could downgrade the security level via
    ssl_ciphers = DEFAULT@SECLEVEL=1
    Note: this option is only available with Python 2.7 or higher.
  • ssl_version (string) — advanced option to control which SSL version getmail tries to use to connect to the server; the default is "sslv23". Another useful value is probably "sslv3". The possible values are:
    • sslv23
    • sslv3
    • tlsv1
    • tlsv1_1
    • tlsv1_2

    Note that this option exists only to help in connecting certain legacy, out-of-date, broken servers; most users should not specify this option at all. Using this option without knowing what you are doing can reduce the effectiveness of your encrypted connection.

    Note: this option is only available with Python 2.7 or higher.

    Note: see the FAQ for details on how to work around Gmail connection problems with OpenSSL v.1.1.1 and later.

  • ssl_fingerprints (tuple of quoted strings) — advanced option to notice when the server's SSL certificate changes. Supply a list of one or more SHA256 certificate fingerprints, and getmail will confirm whether the server's certificate fingerprint is in the list of allowed fingerprints; if it is not, getmail will abort the connection. Getmail will log the fingerprint of the server's certificate if you supply the --fingerprint commandline option. Note: this option is only available with Python 2.7 or higher.
  • ssl_cert_hostname (string) — advanced option to specify an alternate hostname which is expected in the server's SSL certificate hostname field. Specify this if the name used to connect to the server is known not to match the hostname in the server's certificate; otherwise, getmail will error out with a hostname mismatch.

SimplePOP3Retriever

The SimplePOP3Retriever class takes the common retriever parameters above, plus the following optional parameters:

  • use_apop (boolean) — if set to True, getmail will use APOP-style authentication to log in to the server instead of normal USER/PASS authentication. This is not supported by many POP3 servers. Note that APOP adds much less security than might be supposed; weaknesses in its hashing algorithm mean that an attacker can recover the first three characters of the password after snooping on only a few hundred authentications between a client and server — see http://www.securityfocus.com/archive/1/464477/30/0/threaded for details. The default is False.
  • timeout (integer) — how long (in seconds) to wait for socket operations to complete before considering them failed. If not specified, the default is 180 seconds. You may need to increase this value in particularly poor networking conditions.
  • delete_dup_msgids (boolean) — if set to True, and the POP3 server identifies multiple messages as having the same "unique" identifier, all but the first will be deleted without retrieving them.

BrokenUIDLPOP3Retriever

This retriever class is intended only for use with broken POP3 servers that either do not implement the UIDL command, or which do not properly assign unique identifiers to messages (preventing getmail from determining which messages it has seen before). It will identify every message in the mailbox as a new message, and therefore if you use this retriever class and opt not to delete messages after retrieval, it will retrieve those messages again the next time getmail is run. Use this retriever class only if your mailbox is hosted on such a broken POP3 server, and the server does not provide another means of getmail accessing it (i.e., IMAP).

The BrokenUIDLPOP3Retriever class takes the common retriever parameters above, plus the following optional parameters:

SimpleIMAPRetriever

The SimpleIMAPRetriever class takes the common retriever parameters above, plus the following optional parameters:

SimplePOP3SSLRetriever

The SimplePOP3SSLRetriever class takes the common retriever parameters above, plus the following optional parameters:

BrokenUIDLPOP3SSLRetriever

The BrokenUIDLPOP3SSLRetriever class takes the common retriever parameters above, plus the following optional parameters:

SimpleIMAPSSLRetriever

The SimpleIMAPSSLRetriever class takes the common retriever parameters above, plus the following optional parameters:

MultidropPOP3Retriever

The MultidropPOP3Retriever class takes the common retriever parameters above, plus the following required parameter:

  • envelope_recipient (string) — the name and position of the header field which records the envelope recipient address. This is set to a value of the form field_name : field_position . The first (topmost) Delivered-To: header field would be specified as:
    envelope_recipient = delivered-to:1
            

The MultidropPOP3Retriever also takes the following optional parameters:

MultidropPOP3SSLRetriever

The MultidropPOP3SSLRetriever class takes the common retriever parameters above, plus the following required parameter:

The MultidropPOP3SSLRetriever class also takes the following optional parameters:

MultidropSDPSRetriever

The MultidropSDPSRetriever class takes the common retriever parameters above, plus the following optional parameters:

MultidropIMAPRetriever

The MultidropIMAPRetriever class takes the common retriever parameters above, plus the following required parameter:

The MultidropIMAPRetriever class also takes the following optional parameters:

MultidropIMAPSSLRetriever

The MultidropIMAPSSLRetriever class takes the common retriever parameters above, plus the following required parameter:

The MultidropIMAPSSLRetriever class also takes following optional parameters:

Retriever examples

A typical POP3 mail account (the basic kind of mailbox provided by most internet service providers (ISPs)) would use a retriever configuration like this:

[retriever]
type = SimplePOP3Retriever
server = popmail.isp.example.net
username = account_name
password = my_mail_password

If your ISP provides POP3 access on a non-standard port number, you would need to include the port parameter:

[retriever]
type = SimplePOP3Retriever
server = popmail.isp.example.net
port = 8110
username = account_name
password = my_mail_password

If your ISP provides POP3-over-SSL and you wanted to use that, your retriever configuration might look like this:

[retriever]
type = SimplePOP3SSLRetriever
server = popmail.isp.example.net
username = account_name
password = my_mail_password

If you have an IMAP mail account and want to retrieve messages from several mail folders under that account, and you want to move messages to a special folder when deleting them, you would use a retriever configuration like this:

[retriever]
type = SimpleIMAPRetriever
server = imapmail.isp.example.net
username = account_name
password = my_mail_password
mailboxes = ("INBOX", "lists.unix", "lists.getmail")
move_on_delete = mail.deleted

If you are retrieving your company's mail from a domain POP3 mailbox for delivery to multiple local users, you might use a retriever configuration like this:

[retriever]
type = MultidropPOP3Retriever
server = imapmail.isp.example.net
username = account_name
password = company_maildrop_password
envelope_recipient = delivered-to:1

Creating the [destination] section

The destination section of the rc file tells getmail what to do with retrieved messages. Begin with the section header line as follows:

[destination]

Then, include a type string parameter to tell getmail what type of mail destination this is. The possible values are:

  • Maildir — deliver all messages to a local qmail-style maildir
  • Mboxrd — deliver all messages to a local mboxrd-format mbox file with fcntl-type locking.
  • MDA_external — use an external message delivery agent (MDA) to deliver messages. Typical MDAs include maildrop, procmail, and others.
  • MDA_lmtp — deliver messages to an external MDA via the LMTP protocol.
  • MultiDestination — unconditionally deliver messages to multiple destinations (maildirs, mbox files, external MDAs, or other destinations).
  • MultiSorter — sort messages according to the envelope recipient (requires a domain mailbox retriever) and deliver to a variety of maildirs, mbox files, external MDAs, or other destinations based on regular expressions matching the recipient address of each message. Messages not matching any of the regular expressions are delivered to a default "postmaster" destination.
  • MultiGuesser — sort messages according to getmail's best guess at what the envelope recipient of the message might have been, and deliver to a variety of maildirs, mbox files, external MDAs, or other destinations based on regular expressions matching those addresses. Messages not matching any of the regular expressions are delivered to a default "postmaster" destination.
  • MDA_qmaillocal — use qmail-local to deliver messages according to instructions in a .qmail file.

Maildir

The Maildir destination delivers to a qmail-style maildir. The maildir must already exist, and must contain all of the subdirectories required by the maildir format. getmail will not create the maildir if it does not exist. If you're not familiar with the maildir format, the requirements in a nutshell are: it must be a directory containing three writable subdirectories cur, new, and tmp, and they must all reside on the same filesystem.

The Maildir destination takes one required parameter:

  • path (string) — the path to the maildir, ending in slash (/). This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. You might want to deliver messages to a maildir named Maildir in your home directory; you could do this with a configuration like this:
    [destination]
    type = Maildir
    path = ~/Maildir/
            

The Maildir destination also takes two optional parameters:

  • user (string) — on Unix-like systems, if supplied, getmail will change the effective UID to that of the named user before delivering messages to the maildir. Note that this typically requires root privileges. getmail will not deliver to maildirs as root, so this "optional" parameter is required in that situation.
  • filemode (string) — if supplied, getmail will cause the delivered message files in the maildir to have at most these permissions (given in standard Unix octal notation). Note that the current umask is masked out of the given value at file creation time. The default value, which should be appropriate for most users, is "0600".

Mboxrd

The Mboxrd destination delivers to an mboxrd-format mbox file with either fcntl-type (lockf) or flock-type file locking. The file must already exist and appear to be a valid mboxrd file before getmail will try to deliver to it — getmail will not create the file if it does not exist. If you want to create a new mboxrd file for getmail to use, simply create a completely empty (0-byte) file.

You must ensure that all other programs accessing any the mbox file expect mboxrd-format mbox files and the same type of file locking that you configure getmail to use; failure to do so can cause mbox corruption. If you do not know what type of file locking your system expects, ask your system administrator. If you are the system administrator and don't know what type of file locking your system expects, do not use Mboxrd files; use Maildirs instead. Note that delivering to mbox files over NFS can be unreliable and should be avoided; this is the case with any MDA.

The Mboxrd destination takes one required parameter:

  • path (string) — the path to the mbox file. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. You might want to deliver messages to an mbox file named inbox in your home directory; you could do this with a configuration like this:
    [destination]
    type = Mboxrd
    path = ~/inbox
            

The Mboxrd destination also takes two optional parameters:

  • user (string) — on Unix-like systems, if supplied, getmail will change the effective UID to that of the named user before delivering messages to the mboxrd file. Note that this typically requires root privileges. getmail will not deliver to mbox files as root, so this "optional" parameter is required in that situation.
  • locktype (string) — which type of file locking to use; may be "lockf" (for fcntl locking) or "flock". The default in getmail 4.7.0 and later is lockf.

MDA_external

MDA_external delivers messages by running an external program (known as a message delivery agent, or MDA) and feeding it the message on its standard input. Some typical MDAs include maildrop and procmail.

The MDA_external destination takes one required parameter:

  • path (string) — the path to the command to run. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}.

The MDA_external destination also takes several optional parameters:

  • arguments (tuple of quoted strings) — arguments to be supplied to the command. The following substrings will be substituted with the equivalent values from the message:
    • %(sender) — envelope return-path address
    If the message is retrieved with a multidrop retriever class, the message recipient (and parts of it) are also available with the following replacement substrings:
    • %(recipient) — envelope recipient address
    • %(local) — local-part of the envelope recipient address
    • %(domain) — domain-part of the envelope recipient address
    • %(mailbox) — the IMAP mailbox name the message was retrieved from; for POP, this will be empty
    The default value of the arguments parameter is (), so no arguments are supplied to the command.
  • unixfrom (boolean) — whether to include a Unix-style mbox From_ line at the beginning of the message supplied to the command. Defaults to false. Some MDAs expect such a line to be present and will fail to operate if it is missing.
  • user (string) — if supplied, getmail will change the effective UID to that of the named user. Note that this typically requires root privileges.
  • group (string) — if supplied, getmail will change the effective GID to that of the named group. Note that this typically requires root privileges.
  • allow_root_commands (boolean) — if set, getmail will run external commands even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root.
  • ignore_stderr (boolean) — if set, getmail will not consider it an error if the program writes to stderr. The default is false, which causes getmail to consider the delivery failed and leave the message on the server, proceeding to the next message. This prevents loss of mail if the MDA writes to stderr but fails to exit nonzero when it encounters an error. Note that setting this option has serious implications; some MDAs can fail to deliver a message but still exit 0, which can cause loss of mail if this option is set. Only change this setting if you are confident your MDA always exits nonzero on error.
  • pipe_stdout (boolean) — if set, stdout generated by the external MDA will be piped to stdout of getmail. The default is true, which is the default with fetchmail. Settings it to false will retain the former behavior of getmail and suppress any data written to stdout.

A basic invocation of an external MDA might look like this:

[destination]
type = MDA_external
path = /path/to/mymda
arguments = ("--log-errors", )

Something more complex might look like this:

[destination]
type = MDA_external
path = /path/to/mymda
# Switch to fred's UID and the mail group GID before delivering his mail
user = fred
group = mail
arguments = ("--strip-forbidden-attachments", "--recipient=%(recipient)")

MDA_lmtp

MDA_lmtp delivers messages via LMTP by connecting to a Unix domain or TCP socket. It currently does not support any authentication.

The MDA_lmtp destination takes one required parameter:

  • host (string) — the host to connect to. Either a DNS-resolvable hostname, an IP address or absolute path to a Unix domain socket.

The MDA_lmtp destination also takes several optional parameters:

  • port (integer) — the remote port to connect to. Ignored if host is a Unix domain socket.
  • fallback (string) — an alternative recipient address to deliver to in case delivery to the intended recipient fails permanently (i.e. with a 5xx status code).
  • override (string) — deliver mail to an alternative recipient address instead of the one given by the envelope or mail headers. Behaviour of the fallback parameter still applies in case delivery fails.

A configuration connecting to a Dovecot LMTP server might look like this:

[destination]
type = MDA_lmtp
host = /run/dovecot/lmtp

Another example delivering to a remote host on a non-standard port:

[destination]
type = MDA_lmtp
host = mail.example.com
port = 3333

An example where each mail is delivered to user alice, but if this fails (i.e. their user quota has been reached) it is delivered to bob:

[destination]
type = MDA_lmtp
host = mail.example.com
override = alice
fallback = bob

MultiDestination

MultiDestination doesn't do any message deliveries itself; instead, it lets you specify a list of one or more other destinations which it will pass each message to. You can use this to deliver each message to several different destinations.

The MultiDestination destination takes one required parameter:

  • destinations (tuple of quoted strings) — the destinations which the messages will be passed to. A destination is a string that refers to another configuration file section by name (shortcuts for maildirs and mboxrd files are also provided; see below), like this:

    destinations = ('[other-destination-1]', '[other-destination-2]')
    
    [other-destination-1]
    type = Mboxrd
    path = /var/spool/mail/alice
    user = alice
    
    [other-destination-2]
    type = Maildir
    path = /home/joe/Maildir/
    user = joe
    

    Because Maildir and Mboxrd destinations are common, you can specify them directly as a shortcut if they do not require a user parameter. If the string (after expansion; see below) starts with a dot or slash and ends with a slash, it specifies the path of a Maildir destination, while if it starts with a dot or a slash and does not end with a slash, it specifies the path of a Mboxrd destination.

    For instance, you can deliver mail to two maildirs with the following:

    destinations = ('~/Mail/inbox/', '~/Mail/archive/current/')
    

    Each destination string is first expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}.

Some examples:

  • To deliver to a maildir named Maildir in the home directory of user jeff, when getmail is run as that user:
    [destination]
    type = MultiDestination
    destinations = ("~jeff/Maildir/", )
    
  • To deliver to an mboxrd file:
    [destination]
    type = MultiDestination
    destinations = ("/var/spool/mail/alice", )
    
  • To deliver with an external MDA:
    [destination]
    type = MultiDestination
    destinations = ("[procmail-as-bob]", )
    
    [procmail-as-bob]
    type = MDA_external
    path = /path/to/procmail
    arguments = ('~bob/.procmailrc', '-f', '%(sender)')
    user = bob
    

Of course, the whole point of MultiDestination is to allow you to specify multiple destinations, like this:

[destination]
type = MultiDestination
destinations = (
    "~jeff/Mail/inbox",
    "[procmail-as-jeff]",
    "/var/mail-archive/incoming"
    )

[procmail-as-jeff]
type = MDA_external
path = /path/to/procmail
arguments = ('~jeff/.procmailrc', '-f', '%(sender)')
user = jeff

MultiSorter

MultiSorter compares the envelope recipient address of messages against a list of user-supplied regular expressions and delivers the message to the destination (maildir, mboxrd file, or other) associated with any matching patterns. A message can match multiple patterns and therefore be delivered to multiple matching destinations. Any message which matches none of the patterns is delivered to a default destination for the postmaster.

Because MultiSorter requires the envelope recipient to operate, it must be used with a domain mailbox retriever. If you instead want to do some basic message sorting based on getmail's best guess as to the envelope recipient of the message, see the MultiGuesser destination class below.

The MultiSorter destination takes one required parameter:

  • default (string) — the destination for messages which aren't matched by any of the "locals" regular expressions. The destination can be a maildir, mboxrd file, or other destination. See MultiDestination for an explanation of how the type of destination is interpreted from this value.

The MultiSorter destination also takes one optional parameter:

  • locals (tuple of 2-tuples) — zero or more regular expression – destination pairs. Messages will be delivered to each destination for which the envelope recipient matches the given regular expression. The regular expression and destination are supplied as two quoted strings in a tuple; locals is then a tuple of such pairs of strings. Destinations are specified in the same manner as with the "default" parameter, above.

Important note: if your regular expression contains backslashes (by themselves, or as part of an escaped character or symbol like \n or \W ), you need to tell the parser that this expression must be parsed "raw" by prepending the string with an "r":

locals = (
    (r'jeff\?\?\?@.*', '[jeff]'),
    ('alice@', '[alice]')
    )

locals = (
    ('jeff@.*', '[jeff]'),
    (r'alice\D+@', '[alice]')
    )

Note that if you don't understand regular expressions, you don't need to worry about it. In general, an email address is a regular expression that matches itself. The only significant times this isn't the case is when the address contains odd punctuation characters like ^, $, \, or [. Handy hints:

  • the regular expression . (dot) matches anything
  • matches can occur anywhere in the address. If you want to only match at the beginning, start your expression with the ^ character. If you only want to match the whole address, also end your expression with a dollar sign $.

Using regular expressions:

  • The regular expression joe@example.org matches the addresses joe@example.org, joe@example.org.net, and heyjoe@example.org.
  • The regular expression ^jeff@ matches the addresses jeff@example.org and jeff@example.net, but not otherjeff@example.org.
  • The regular expression sam matches the addresses sam@example.org, samantha@example.org, asam@example.org, and chris@isam.example.net.

Some examples:

    • Deliver mail matching jeff@example.net to ~jeff/Maildir/
    • Deliver mail matching alice@anything to ~alice/inbox
    • Deliver all other mail to ~bob/Maildir/
    [destination]
    type = MultiSorter
    default = [bob-default]
    locals = (
        ('jeff@example.net', '[jeff]'),
        ('alice@', '[alice]')
        )
    
    [jeff]
    type = Maildir
    path = ~jeff/Maildir/
    user = jeff
    
    [alice]
    type = Mboxrd
    path = ~alice/inbox
    user = alice
    
    [bob-default]
    type = Maildir
    path = ~bob/Maildir/
    user = bob
    
    • Deliver mail for jeff, bob, and alice to maildirs in their home directories
    • Deliver copies of all messages to samantha's mail archive
    • Deliver copies of all messages to a program that logs certain information. This program should run as the user log, and command arguments should tell it to record the info to /var/log/mail/info
    [destination]
    type = MultiSorter
    default = doesn't matter, this won't be used, as locals will always match
    locals = (
        ('^jeff@', '[jeff]'),
        ('^bob@', '[bob]'),
        ('^alice@', '[alice]'),
        ('.', '[copies]'),
        ('.', '[info]')
        )
    
    [alice]
    type = Maildir
    path = ~alice/Maildir/
    user = alice
    
    [bob]
    type = Maildir
    path = ~bob/Maildir/
    user = bob
    
    [jeff]
    type = Maildir
    path = ~jeff/Maildir/
    user = jeff
    
    [copies]
    type = Maildir
    path = ~samantha/Mail/archive/copies/
    user = samantha
    
    [info]
    type = MDA_external
    path = /path/to/infologger
    arguments = ('--log=/var/log/mail/info', '--sender=%(sender)', '--recipient=%(recipient))
    user = log
    

MultiGuesser

MultiGuesser tries to guess what the envelope recipient address of the message might have been, by comparing addresses found in the message header against a list of user-supplied regular expressions, and delivers the message to the destination (maildir, mboxrd file, or other) associated with any matching patterns. A message can match multiple patterns and therefore be delivered to multiple matching destinations. Any message which matches none of the patterns is delivered to a default destination for the postmaster. In this fashion, you can do basic mail filtering and sorting with getmail without using an external filtering message delivery agent (MDA) (such as maildrop or procmail), if and only if the message recipient is the criteria you want to filter on.

If you want to filter based on arbitrary message criteria, like "What address is in the To: header field?" or "Who is the message from?", then use the filtering MDA of your choice, called from a getmail MDA_external destination.

MultiGuesser is similar to MultiSorter, except that it does not operate on the true envelope recipient address, and therefore does not require a domain mailbox retriever. Because it is "guessing" at the intended recipient of the message based on the contents of the message header, it is fallible — for instance, the address of a recipient of a mailing list message may not appear in the header of the message at all. If your locals regular expression patterns are only looking for that address, MultiGuesser will then have to deliver it to the destination specified as the default recipient.

This functionality is very similar to the guessing functionality of getmail version 2, which was removed in version 3. MultiGuesser extracts a list of addresses from the message header like this:

  1. it looks for addresses in any Delivered-To: header fields.
  2. if no addresses have been found, it looks for addresses in any Envelope-To: header fields.
  3. if no addresses have been found, it looks for addresses in any X-Envelope-To: header fields.
  4. if no addresses have been found, it looks for addresses in any Apparently-To: header fields.
  5. if no addresses have been found, it looks for addresses in any Resent-to: or Resent-cc: header fields (or Resent-bcc:, which shouldn't be present).
  6. if no addresses have been found, it looks for addresses in any To: or cc: header fields (or bcc:, which shouldn't be present).

The MultiGuesser destination takes one required parameter:

The MultiGuesser destination also takes one optional parameter:

Examples:

If you have a simple POP3 account (i.e. it's not a multidrop mailbox) and you want to deliver your personal mail to your regular maildir, but deliver mail from a couple of mailing lists (identified by the list address appearing in the message header) to separate maildirs, you could use a MultiGuesser configuration like this:

[destination]
type = MultiGuesser
default = ~/Maildir/
locals = (
    ("list-address-1@list-domain-1", "~/Mail/mailing-lists/list-1/"),
    ("list-address-2@list-domain-2", "~/Mail/mailing-lists/list-2/"),
    )

See MultiSorter above for other examples of getmail rc usage; the only difference is the type parameter specifying the MultiGuesser destination.

MDA_qmaillocal

MDA_qmaillocal delivers messages by running the qmail-local program as an external MDA. qmail-local uses .qmail files to tell it what to do with messages. If you're not already familiar with qmail, you don't need to use this destination class.

The MDA_qmaillocal destination takes several optional parameters:

  • qmaillocal (string) — path to the qmail-local program. The default value is /var/qmail/bin/qmail-local.
  • user (string) — supplied to qmail-local, and also tells getmail to change the current effective UID to that of the named user before running qmail-local. Note that this typically requires root privileges. The default value is the account name of the current effective UID.
  • group (string) — if supplied, getmail will change the effective GID to that of the named group before running qmail-local. Note that this typically requires root privileges.
  • homedir (string) — supplied to qmail-local. The default value is the home directory of the account with the current effective UID.
  • localdomain (string) — supplied to qmail-local as its domain argument. The default value is the fully-qualified domain name of the local host.
  • defaultdelivery (string) — supplied to qmail-local as its defaultdelivery argument. The default value is ./Maildir/.
  • conf-break (string) — supplied to qmail-local as its dash argument. The default value is -.
  • localpart_translate (2-tuple of quoted strings) — if supplied, the recipient address of the message (which is used to construct the local argument (among others) to qmail-local) will have any leading instance of the first string replaced with the second string. This can be used to remap recipient addresses, trim extraneous prefixes (such as the qmail virtualdomain prepend value), or perform other tasks. The default value is ('', '') (i.e., no translation).
  • strip_delivered_to (boolean) — if set, Delivered-To: header fields will be removed from the message before handing it to qmail-local. This may be necessary to prevent qmail-local falsely detecting a looping message if (for instance) the system retrieving messages otherwise believes it has the same domain name as the retrieval server. Inappropriate use of this option may cause message loops. The default value is False.
  • allow_root_commands (boolean) — if set, getmail will run qmail-local even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root.

A basic invocation of qmail-local might look like this:

[destination]
type = MDA_qmaillocal
user = joyce

Something more complex might look like this:

[destination]
type = MDA_qmaillocal
user = joyce
# The mail domain isn't the normal FQDN of the server running getmail
localdomain = host.example.net
# Trim the server's virtualdomain prepend value from message recipient before
# sending it to qmail-local
localpart_translate = ('mailhostaccount-', '')

Creating the [options] section

The optional options section of the rc file can be used to alter getmail's default behaviour. The parameters supported in this section are as follows:

  • verbose (integer) — controls getmail's verbosity. If set to 2, getmail prints messages about each of its actions. If set to 1, it prints messages about retrieving and deleting messages (only). If set to 0, getmail will only print warnings and errors. Default: 1.
  • read_all (boolean) — if set, getmail retrieves all available messages. If unset, getmail only retrieves messages it has not seen before. Default: True.
  • use_netrc (boolean) — if set, getmail will read a .netrc or .authinfo file to set the username and/or password parameter for matching retrievers. A retriever matches if its server parameter matches a machine entry in the netrc file. The location of the .netrc file to read can be set with the netrc_file option. Default: False.
  • netrc_file (string) — sets the file that use_netrc reads. Has no effect unless use_netrc is True. Default: unset, which means use_netrc will read the default netrc file for your system, typically ~/.netrc, unless NETRC or CURLOPT_NETRC_FILE is defined.
  • delete (boolean) — if set, getmail will delete messages after retrieving and successfully delivering them. If unset, getmail will leave messages on the server after retrieving them. Default: False.
  • delete_after (integer) — if set, getmail will delete messages this number of days after first seeing them, if they have been retrieved and delivered. This, in effect, leaves messages on the server for a configurable number of days after retrieving them. Note that the delete parameter has higher priority; if both are set, the messages will be deleted immediately. Default: 0, which means not to enable this feature.
  • to_oldmail_on_each_mail (boolean) — if set, getmail will update the oldmail file on each mail. This is slower, but avoids re-downloading mails, if something went wrong, like the server dying. --to-oldmail-on-each-mail in the command line. Default: False.
  • only_oldmail_file (boolean) — if set, getmail will not retrieve mails but only update the oldmail file with mails currently on the server. These mails will not be retrieved as long as the oldmail file exists. --only-oldmail-file in the command line. Default: False.
  • skip_imap_fetch_size (boolean) — will skip fetching RFC822.SIZE. This speeds up mail checks, particularly with a davmail proxies.
    skip_imap_fetch_size
    is only valid for IMAP and not valid with any of
    max_message_size
    ,
    max_bytes_per_session
    ,
    delete_bigger_than
    .
  • mark_read (boolean) — if set, getmail will mark messages as read after retrieving. It sets, the IMAP (\Seen) flag on the server. The command parameter -m/--mark-read can be used to override the option. -m/--mark-read is related to --searchset/-s, a synonym to -ds, = -ds,Seen. Default: False.
  • delete_bigger_than (integer) — if set, getmail will delete messages larger than this number of bytes after retrieving them, even if the delete and delete_after options are disabled. The purpose of this feature is to allow deleting only large messages, to help keep a mailbox under quota. Has no effect if delete is set, as that will unconditionally remove messages. If delete_after is also set, the message will be deleted immediately after retrieval if it is over this size, and otherwise will be deleted according to the setting of delete_after. Default: 0, which means not to enable this feature.
  • max_bytes_per_session (integer) — if set, getmail will retrieve messages totalling up to this number of bytes before closing the session with the server. This can be useful if you do not want large messages causing large bursts of network traffic. Default: 0, which means not to enable this feature. Note that message sizes reported by the server are used, and therefore may vary slightly from the actual size on disk after message retrieval.
  • max_message_size (integer) — if set, getmail will not retrieve messages larger than this number of bytes. Default: 0, which means not to enable this feature.
  • max_messages_per_session (integer) — if set, getmail will process a maximum of this number of messages before closing the session with the server. This can be useful if your network or the server is particularly unreliable. Default: 0, which means not to enable this feature.
  • delivered_to (boolean) — if set, getmail adds a Delivered-To: header field to the message. If unset, it will not do so. Default: True. Note that this field will contain the envelope recipient of the message if the retriever in use is a multidrop retriever; otherwise it will contain the string "unknown".
  • received (boolean) — if set, getmail adds a Received: header field to the message. If unset, it will not do so. Default: True.
  • message_log (string) — if set, getmail will record a log of its actions to the named file. The value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. Default: '' (the empty string), which means not to enable this feature.
  • message_log_syslog (boolean) — if set, getmail will record a log of its actions using the system logger. Note that syslog is inherently unreliable and can lose log messages. Default: False.
  • message_log_verbose (boolean) — if set, getmail will log to the message log file (or syslog) information about messages not retrieved and the reason for not retrieving them, as well as starting and ending information lines. By default, it will log only about messages actually retrieved, and about error conditions. Note that this has no effect if neither message_log nor message_log_syslog is in use. Default: False.

Most users will want to either enable the delete option (to delete mail after retrieving it), or disable the read_all option (to only retrieve previously-unread mail).

The verbose, read_all, delete, and mark_read parameters can be overridden at run time with commandline options.

[options] example

To configure getmail to operate quietly, to retrieve only new mail, to delete messages after retrieving them, and to log its actions to a file, you could provide the following in your getmail rc file(s):

[options]
verbose = 0
read_all = false
delete = true
message_log = ~/.getmail/log

Creating the [filter-something] sections

The filter-something section(s) of the rc file (which are not required) tell getmail to process messages in some way after retrieving them, but before delivering them to your destinations. Filters can tell getmail to drop a message (i.e. not deliver it at all), add information to the message header (i.e. for a spam- classification system or similar), or modify message content (like an antivirus system stripping suspected MIME parts from messages).

You can specify any number of filters; provide a separate rc file section for each, naming each of them filter-something. They will be run in collated order, so it's likely simplest to name them like this:

  • [filter-1]
  • [filter-2]
  • [filter-3]

Begin with the section header line as follows:

[filter-something]

Then, include a type string parameter to tell getmail what type of filter. The possible values are:

  • Filter_classifier — run the message through an external program, and insert the output of the program into X-getmail-filter-classifier: header fields in the message. Messages can be dropped by having the filter return specific exit codes.
  • Filter_external — supply the message to an external program, which can then modify the message in any fashion. The program must print the modified message to stdout. getmail reads the modified message from the program in this fashion before proceeding to the next filter or destination. Messages can be dropped by having the filter return specific exit codes.
  • Filter_TMDA — run the message through the tmda-filter program for use with the Tagged Message Delivery Agent (TMDA) package. If tmda-filter returns 0, the message will be passed to the next filter (or destination). If it returns 99, the message will be dropped, and TMDA is responsible for sending a challenge message, queuing the original, etc., as with normal TMDA operation in a .qmail, .courier, or .forward file.

By default, if a filter writes anything to stderr, getmail will consider the delivery to have encountered an error. getmail will leave the message on the server and proceed to the next message. You must configure any filter you use not to emit messages to stderr except on errors — please see the documentation for your filter program for details. Optionally, if you know your filter can emit warnings on stderr under non-error conditions, you can set the ignore_stderr option.

Filter_classifier

Filter_classifier runs the message through an external program, placing the output of that program into X-getmail-filter-classifier: header fields. It can also cause messages to be dropped by exiting with a return code listed in the exitcodes_drop parameter.

Filter_classifier has one required parameter:

  • path (string) — the path to the command to run. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}.

In addition, Filter_classifier takes the following optional parameters:

  • arguments (tuple of quoted strings) — arguments to be supplied to the command. The following substrings will be substituted with the equivalent values from the message:
    • %(sender) — envelope return-path address
    If the message is retrieved with a multidrop retriever class, the message recipient (and parts of it) are also available with the following replacement substrings:
    • %(recipient) — envelope recipient address
    • %(local) — local-part of the envelope recipient address
    • %(domain) — domain-part of the envelope recipient address
    The default value of the arguments parameter is (), so no arguments are supplied to the command.
  • unixfrom (boolean) — whether to include a Unix-style mbox From_ line at the beginning of the message supplied to the command. Default: False.
  • user (string) — if supplied, getmail will change the effective UID to that of the named user. Note that this typically requires root privileges.
  • group (string) — if supplied, getmail will change the effective GID to that of the named group. Note that this typically requires root privileges.
  • allow_root_commands (boolean) — if set, getmail will run external commands even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root.
  • ignore_stderr (boolean) — if set, getmail will not consider it an error if the filter writes to stderr. The default is false, which causes getmail to consider the delivery failed and leave the message on the server, proceeding to the next message. This prevents loss of mail if the filter writes to stderr but fails to exit nonzero when it encounters an error. Note that setting this option has serious implications; some poorly-written programs commonly used as mail filters can can mangle or drop mail but still exit 0, their only clue to failure being warnings emitted on stderr. Only change this setting if you are confident your filter always exits nonzero on error.
  • exitcodes_drop (tuple of integers) — if the filter returns an exit code in this list, the message will be dropped. The default is (99, 100).
  • exitcodes_keep (tuple of integers) — if the filter returns an exit code other than those in exitcodes_drop and exitcodes_keep, getmail assumes the filter encountered an error. getmail will then not proceed, so that the message is not lost. The default is (0, ).

Filter_external

Filter_external runs the message through an external program, and replaces the message with the output of that program, allowing the filter to make arbitrary changes to messages. It can also cause messages to be dropped by exiting with a return code listed in the exitcodes_drop parameter.

Filter_external has one required parameter:

In addition, Filter_external takes the following optional parameters:

Filter_TMDA

Filter_external runs the message through the external program tmda-filter, allowing the use of the Tagged Message Delivery Agent (TMDA) package. As TMDA relies on the message envelope, this filter requires the use of a multidrop retriever class to function. It sets the three environment variables SENDER, RECIPIENT, and EXT prior to running tmda-filter.

I've tested this filter, and it Works For Me™, but I'm not a regular TMDA user. I would appreciate any feedback about its use from TMDA users.

Filter_TMDA has no required parameters. It has the following optional parameters:

  • path (string) — the path to the tmda-filter binary. Default: /usr/local/bin/tmda-filter. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}.
  • user (string) — see Filter_classifier for definition.
  • group (string) — see Filter_classifier for definition.
  • allow_root_commands (boolean) — see Filter_classifier for definition.
  • ignore_stderr (boolean) — see Filter_classifier for definition.
  • conf-break (string) — this value will be used to split the local-part of the envelope recipient address to determine the value of the EXT environment variable. For example, if the envelope sender address is sender-something@host.example.org, and the envelope recipient address is user-ext-ext2@host.example.net, and conf-break is set to -, getmail will set the environment variables SENDER to "sender-something@host.example.org", RECIPIENT to "user-ext-ext2@host.example.net", and EXT to "ext-ext2". Default: "-".

[filter-something] examples

You might filter spam messages in your MUA based on information added to the message header by a spam-classification program. You could have that information added to the message header with a filter configuration like this:

[filter-3]
type = Filter_classifier
path = /path/to/my-classifier
arguments = ('--message-from-stdin', '--report-to-stdout')
user = nobody

You might use a program to prevent users from accidentally destroying their data by stripping suspected attachments from messages. You could have that information added to the message header with a filter configuration like this:

[filter-3]
type = Filter_external
path = /path/to/my-mime-filter
arguments = ('--message-from-stdin', '--remove-all-but-attachment-types=text/plain,text/rfc822')
user = nobody

You might use TMDA to challenge messages from unknown senders. If the default parameters are fine for your configuration, this is as simple as:

[filter-3]
type = Filter_TMDA

getmail rc file examples

Several examples of different getmail rc configuration are available in the included file getmailrc-examples.

Running getmail

To use getmail, simply run the script getmail, which is typically installed in /usr/local/bin/ by default. getmail will read the default getmail rc file (getmailrc) from the default configuration/data directory (~/.config/getmail/ or ~/.getmail/) and begin operating.

You can modify this behaviour by supplying commandline options to getmail.

Commandline options

getmail understands the following options:

  • --version — show getmail's version number and exit
  • --help or -h — show a brief usage summary and exit
  • --getmaildir=DIR or -gDIR — use DIR for configuration and data files
  • --rcfile=FILE or -rFILE — read getmail rc file FILE instead of the default. The file path is assumed to be relative to the getmaildir directory unless this value starts with a slash (/). This option can be given multiple times to have getmail retrieve mail from multiple accounts. This option can also be omitted. Then all files in getmaildir are used, apart from oldmail-*, *.json, .*
  • --only-account=email@address or -oemail@address choses the rc file based on the username/email.
  • --searchset=,flag or --searchset=search string or -s,flag. No flag -s, is -s,Seen. See imap_search and imap_on_delete.
  • -m/--mark-read is like -ds,. It overrides mark_read in [options]
  • --dump — read rc files, dump configuration, and exit (debugging)
  • --trace — print extended debugging information

If you are using a single getmailrc file with an IMAP server that understands the IDLE extension from RFC 2177, you can use the --idle=MAILBOX option to specify that getmail should wait on the server to notify getmail of new mail in the specified mailbox after getmail is finished retrieving mail.

In addition, the following commandline options can be used to override any values specified in the [options] section of the getmail rc files:

  • --verbose or -v — operate more verbosely. Can be given multiple times.
  • --quiet or -q — print only warnings or errors while running
  • --delete or -d — delete messages after retrieving
  • --dont-delete or -l — do not delete messages after retrieving
  • --all or -a — retrieve all messages
  • --new or -n — retrieve only new (unseen) messages

For instance, if you want to retrieve mail from two different mail accounts, create a getmail rc file for each of them (named, say, getmailrc-account1 and getmailrc-account2) and put them in directory ~/.config/getmail/. Then run getmail as follows:

$ getmail --rcfile getmailrc-account1 --rcfile getmailrc-account2

If those files were located in a directory other than the default, and you wanted to use that directory for storing the data files as well, you could run getmail as follows:

$ getmail --getmaildir /path/to/otherdir --rcfile getmailrc-account1 --rcfile getmailrc-account2

Using getmail as an MDA

getmail includes helper scripts which allow you to use it to deliver mail from other programs to maildirs or mboxrd files.

Using the getmail_maildir MDA

The getmail_maildir script can be used as an MDA from other programs to deliver mail to maildirs. It reads the mail message from stdin, and delivers it to a maildir path provided as an argument on the commandline. This path must (after expansion by the shell, if applicable) start with a dot or slash and end with a slash.

getmail_maildir uses the contents of the SENDER environment variable to construct a Return-Path: header field and the contents of the RECIPIENT environment variable to construct a Delivered-To: header field at the top of the message.

getmail_maildir also accepts the options --verbose or -v which tell it to print a status message on success. The default is to operate silently unless an error occurs.

Example

You could deliver a message to a maildir named Maildir located in your home directory by running the following command with the message on stdin:

$ getmail_maildir $HOME/Maildir/

Using the getmail_mbox MDA

The getmail_mbox script can be used as an MDA from other programs to deliver mail to mboxrd-format mbox files. It reads the mail message from stdin, and delivers it to an mbox path provided as an argument on the commandline. This path must (after expansion by the shell, if applicable) start with a dot or slash and not end with a slash.

getmail_maildir uses the contents of the SENDER environment variable to construct a Return-Path: header field and mbox From_ line and the contents of the RECIPIENT environment variable to construct a Delivered-To: header field at the top of the message.

getmail_mbox also accepts the options --verbose or -v which tell it to print a status message on success. The default is to operate silently unless an error occurs.

Example

You could deliver a message to an mboxrd-format mbox file named inbox located in a directory named mail in your home directory by running the following command with the message on stdin:

$ getmail_mbox $HOME/mail/inbox

Using getmail_fetch to retrieve mail from scripts

getmail includes the getmail_fetch helper script, which allows you to retrieve mail from a POP3 server without the use of a configuration file. It is primarily intended for use in automated or scripted environments, but can be used to retrieve mail normally.

See the getmail_fetch manual page for details on the use of getmail_fetch.

getmail6-6.19.07/docs/configuration.txt000066400000000000000000002503771474536475600200110ustar00rootroot00000000000000 Link: Contents Up Index: getmail6 getmail documentation This is the documentation for getmail version 6, a port of getmail version 5 to python 3. getmail6 is Copyright © 1998-2025 by Charles Cazabon and others: getmail and getmail6 are licensed under the GNU General Public License version 2 (only). Table of Contents * getmail documentation (version 6) * * getmail documentation * * Features * Requirements * Obtaining getmail * Installing getmail * getmail configuration (version 6) * * Configuring getmail * * Creating a getmail rc file * * Parameter types and formats * * string * integer * boolean * tuple of quoted strings * tuple of integers * tuple of 2-tuples * Creating the [retriever] section * * What is a "multidrop" mailbox? How do I know if I have one? * Common retriever parameters * SSL Client Parameters * SSL Certificate Validation and Server Parameters * SimplePOP3Retriever * BrokenUIDLPOP3Retriever * SimpleIMAPRetriever * SimplePOP3SSLRetriever * BrokenUIDLPOP3SSLRetriever * SimpleIMAPSSLRetriever * MultidropPOP3Retriever * MultidropPOP3SSLRetriever * MultidropSDPSRetriever * MultidropIMAPRetriever * MultidropIMAPSSLRetriever * Retriever examples * Creating the [destination] section * * Maildir * Mboxrd * MDA_external * MDA_lmtp * MultiDestination * MultiSorter * MultiGuesser * MDA_qmaillocal * Creating the [options] section * * [options] example * Creating the [filter-something] sections * * Filter_classifier * Filter_external * Filter_TMDA * [filter-something] examples * getmail rc file examples * Running getmail * * Commandline options * Using getmail as an MDA * * Using the getmail_maildir MDA * * Example * Using the getmail_mbox MDA * * Example * Using getmail_fetch to retrieve mail from scripts * getmail troubleshooting (version 6) * * Troubleshooting problems * * Error messages * Warning messages * Unexpected Behaviour * getmail frequently-asked questions (FAQs) (version 6) * * Frequently-Asked Questions (FAQs) * * About getmail * What is getmail6 and how does it relate to getmail? * Configuring getmail * How do I … * Using getmail with other software * I think I found this bug in getmail … Configuring getmail Once getmail is installed, you need to configure it before you can retrieve mail with it. Follow these steps: 1. Create a data/configuration directory. getmail complies with the XDG basedir specification: $XDG_CONFIG_HOME/getmail/ defaulting to ~/.config/getmail/. For backward compatibility reasons, getmail also checks $HOME/.getmail/. If you want a different location, you will need to specify it on the getmail command line. In general, other users should not be able to read the contents of this directory, so you should set the permissions on it appropriately. mkdir -m 0700 $HOME/.getmail 2. Create a configuration file in the configuration/data directory. The default name is getmailrc. If you choose a different filename, you will need to specify it on the getmail command line. If you want to retrieve mail from more than one mail account, you will need to create a separate rc file for each account getmail should retrieve mail from. Creating a getmail rc file The configuration file format is designed to be easy to understand (both for getmail, and for the user). It is broken down into small sections of related parameters by section headers which appear on lines by themselves, enclosed in square brackets, like this: [section name] Each section contains a series of parameters, declared as follows: parameter_name = parameter_value A parameter value, if necessary, can span multiple lines. To indicate that the second and subsequent lines form a continuation of the previous line, they need to begin with leading whitespace, like this: first_parameter = value first parameter value continues here second_parameter = value You can annotate your configuration files with comments by putting them on lines which begin with a pound sign, like this: first_parameter = value # I chose this value because of etc. second_parameter = value Each rc file requires at least two specific sections. The first is retriever, which tells getmail about the mail account to retrieve messages from. The second is destination, which tells getmail what to do with the retrieved messages. There is also an optional section named options , which gives getmail general configuration information (such as whether to log its actions to a file), and other sections can be used to tell getmail to filter retrieved messages through other programs, or to deliver messages for particular users in a particular way. Parameter types and formats Several different types of parameters are used in getmail rc files: * string * integer * boolean * tuple of quoted strings * tuple of integers * tuple of 2-tuples Each parameter type has a specific format that must be used to represent it in the getmail rc file. They are explained below. Each parameter documented later specifies its type explicitly. string Specify a string parameter value with no special syntax: parameter = my value integer Specify an integer parameter value with no special syntax: parameter = 4150 boolean A boolean parameter is true or false; you can specify its value with the (case-insensitive) words "true" and "false". The values "yes", "on" and 1 are accepted as equivalent to "true", while values "no", "off" and 0 are accepted as equivalent to "false". Some examples: parameter = True parameter = false parameter = NO parameter = 1 tuple of quoted strings A tuple of quoted strings is essentially a list of strings, with each string surrounded by matching double- or single-quote characters to indicate where it begins and ends. The list must be surrounded by open- and close-parenthesis characters. A tuple may have to be a specific number of strings; for instance, a "2-tuple" must consist of two quoted strings, while a "4-tuple" must have exactly four. In most cases, the number of strings is not required to be a specific number, and it will not be specified in this fashion. In general, a tuple of quoted strings parameter values should look like this: parameter = ('first string', 'second string', "third string that contains a ' character") However, tuples of 0 or 1 strings require special treatment. The empty tuple is specified with just the open- and close-parenthesis characters: parameter = () A tuple containing a single quoted string requires a comma to indicate it is a tuple: parameter = ("single string", ) tuple of integers This is very similar to a tuple of quoted strings, above, minus the quotes. Some examples: parameter = (1, 2, 3, 4, 5) parameter = (37, ) parameter = () tuple of 2-tuples This is a tuple of items, each of which is a 2-tuple of quoted strings. You can think of this as a list of pairs of quoted strings. # Three pairs parameter = ( ("first-a", "first-b"), ("second-a", "second-b"), ("third-a", "third-b"), ) # One pair parameter = ( ("lone-a", "lone-b"), ) Creating the [retriever] section The retriever section of the rc file tells getmail what mail account to retrieve mail from, and how to access that account. Begin with the section header line as follows: [retriever] Then, include a type string parameter to tell getmail what type of mail retriever to use to retrieve mail from this account. The possible values are: * SimplePOP3Retriever — for single-user POP3 mail accounts. * BrokenUIDLPOP3Retriever — for broken POP3 servers that do not support the UIDL command, or which do not uniquely identify messages; this provides basic support for single-user POP3 mail accounts on such servers. * SimpleIMAPRetriever — for single-user IMAP mail accounts. * SimplePOP3SSLRetriever — same as SimplePOP3Retriever, but uses SSL encryption. * BrokenUIDLPOP3SSLRetriever — same as BrokenUIDLPOP3Retriever, but uses SSL encryption. * SimpleIMAPSSLRetriever — same as SimpleIMAPRetriever, but uses SSL encryption. * MultidropPOP3Retriever — for domain mailbox (multidrop) POP3 mail accounts. * MultidropPOP3SSLRetriever — same as MultidropPOP3Retriever, but uses SSL encryption. * MultidropSDPSRetriever — for domain mailbox SDPS mail accounts, as provided by the UK ISP Demon. * MultidropIMAPRetriever — for domain mailbox (multidrop) IMAP mail accounts. * MultidropIMAPSSLRetriever — same as MultidropIMAPRetriever, but uses SSL encryption. What is a "multidrop" mailbox? How do I know if I have one? Some ISPs, mailhosts, and other service providers provide a mail service they refer to as a "domain mailbox" or "multidrop mailbox". This is where they register a domain for you, and mail addressed to any local-part in that domain ends up in a single mailbox accessible via POP3, with the message envelope (envelope sender address and envelope recipient address) recorded properly in the message header, so that it can be re-constructed after you retrieve the messages with POP3 or IMAP. The primary benefit of this is that you can run your own MTA (qmail, Postfix, sendmail, Exchange, etc.) for your domain without having to have an SMTP daemon listening at a static IP address. Unfortunately, a lot of what is advertised and sold as multidrop service really isn't. In many cases, the envelope recipient address of the message is not properly recorded, so the envelope information is lost and cannot be reconstructed. If the envelope isn't properly preserved, it isn't a domain mailbox, and you therefore can't use a multidrop retriever with that mailbox. To determine if you have a multidrop mailbox, check the following list: if any of these items are not true, you do not have a multidrop mailbox. * the mailbox must receive one copy of the message for each envelope recipient in the domain; if the message was addressed to three local-parts in the domain, the mailbox must receive three separate copies of the message. * the envelope sender address must be recorded in a header field named Return-Path at the top of the message. If the message (incorrectly) already contained such a header field, it must be deleted before the envelope sender address is recorded. * the envelope recipient address must be recorded in a new header field. These may be named various things, but are commonly Delivered-To, X-Envelope-To, and similar values. In the case of messages which had multiple recipients in the domain, this must be a single address, reflecting the particular recipient of this copy of the message. Note that this field (and the envelope recipient address) are not related to informational header fields created by the originating MUA, like To or cc. If you're not sure whether you have a multidrop mailbox, you probably don't. You probably want to use SimplePOP3Retriever (for POP3 mail accounts) or SimpleIMAPRetriever (for IMAP mail accounts) retrievers. Specify the mail account type with one of the above values, like this: type = typename Then, include lines for any parameters and their values which are required by the retriever. The parameters and their types are documented below. Common retriever parameters All retriever types take several common required parameters: * server (string) — the name or IP address of the server to retrieve mail from * username (string) — username to provide when logging in to the mail server. If you enable use_netrc, the netrc file can supply a username for each retriever. All retriever types also take several optional parameters: * port (integer) — the TCP port number to connect to. If not provided, the default is a port appropriate for the protocol (110 for POP3, etc.) * password (string) — password to use when logging in to the mail server. If not using Kerberos authentication -- see below -- getmail gets the password credential for the POP/IMAP server in one of the following ways: 1. from the password configuration item in the getmailrc file 2. by running an arbitrary command specified with the password_command parameter (see below) 3. from Python keyring if available 4. if use_netrc option is True, from a .netrc file 5. if not found via any of the above methods, getmail will prompt for the password when run To store your POP/IMAP account password into the Python keyring, ensure the password is not provided in the getmailrc file, and run getmail with the special option --store-password-in-keyring; getmail will run, prompt you for the password, store it in the Python keyring, and exit without retrieving mail. If this option is not recognized, your Python installation does not have Python keyring. * password_command (tuple of quoted strings) — retrieve the account password by running an arbitrary external program. The program must write the password and nothing else to stdout, and must exit with a status of 0 on success. Note that the password parameter (above) overrides this parameter; specify one or the other, not both. This parameter is specified as the program to run as the first string in the tuple, and all remaining strings are arguments passed to that program. password_command = ("/path/to/password-retriever", "-p", "myaccount@example.org") All POP3 retriever types also take the following optional parameters: * use_xoauth2 (boolean) — whether to use XOAUTH2 for login with the POP3 server. If not set, normal password-based authentication is used. This currently supports Gmail and Microsoft Office 365; if anyone extends this to support other POP3 providers, please let me know so I can include such support in getmail. Note that using XOAUTH2 is no more secure than a regular getmail configuration with a mode 0600 getmailrc file. You will need to set password_command as well to tell getmail to invoke the getmail-gmail-xoauth-tokens helper program; that script requires a positional argument to tell it json file where to read the initial tokens from and where it writes the access and refresh tokens to, and the file requires manual initial setup. Keep write access to the json file. This functionality was contributed by Stefan Krah, who has additional information about using it here: http://www.bytereef.org/howto/oauth2/getmail.html. See docs/getmailrc-examples. All IMAP retriever types also take the following optional parameters: * mailboxes (tuple of quoted strings) — a list of mailbox paths to retrieve mail from, expressed as a Python tuple. If not specified, the default is to retrieve mail from the mail folder named INBOX. You might want to retrieve messages from several different mail folders, using a configuration like this: mailboxes = ("INBOX", "INBOX.spam", "mailing-lists.model-railroading") Note that the format for hierarchical folder names is determined by the IMAP server, not by getmail. Consult your server's documentation or postmaster if you're unsure what form your server uses. If your mailbox names contain non-ASCII characters, ensure that your getmailrc file is stored with UTF-8 encoding so that getmail can correctly determine the unicode character names that need to be quoted in IMAP's modified UTF-7 encoding; if you do not do this, the mailbox names will not match what the server expects them to be, or will cause UnicodeErrors when attempting to load your getmailrc file. As a special case, in getmail version 4.29.0 and later, the unquoted base (non-tuple) value ALL (case-sensitive) means to retrieve mail from all selectable IMAP mailboxes in the account. To retrieve messages from all mailboxes, you would use: mailboxes = ALL * use_peek (boolean) — whether to use PEEK to retrieve the message; the default is True. IMAP servers typically mark a message as seen if PEEK is not used to retrieve the message content. Versions of getmail prior to 4.26.0 did not use PEEK to retrieve messages. * move_on_delete (string) — if set, messages are moved to the named IMAP mail folder before being deleted from their original location. The specified mail folder must exist; getmail will not create it. Note that if you configure getmail not to delete retrieved messages (the default behaviour), they will not be moved at all. * cache_uid (string) — by setting the file for the cache one enables caching the highest imap UID seen to be used as the starting position when fetching the uid list the next time. * record_mailbox (boolean) — whether to add a X-getmail-retrieved-from-mailbox: header field to retrieved messages, containing the name of the selected mailbox that the message was retrieved from. This is on by default, but can be disabled. * use_kerberos (boolean) — whether to use Kerberos authentication with the IMAP server. If not set, normal password-based authentication is used. Note that when you use Kerberos authentication, it is up to you to ensure you have a valid Kerberos ticket (perhaps by running a ticket-renewing agent such as kstart or similar). This feature requires that a recent version of pykerberos with GSS support is installed; check your OS distribution or see http://honk.sigxcpu.org/projects/pykerberos/" for details. * use_xoauth2 (boolean) — whether to use XOAUTH2 for login with the IMAP server. If not set, normal password-based authentication is used. This currently supports Gmail and Microsoft Office 365; if anyone extends this to support other IMAP providers, please let me know so I can include such support in getmail. Note that using XOAUTH2 is no more secure than a regular getmail configuration with a mode 0600 getmailrc file. You will need to set password_command as well to tell getmail to invoke the getmail-gmail-xoauth-tokens helper program; that script requires a positional argument to tell it json file where to read the initial tokens from and where it writes the access and refresh tokens to, and the file requires manual initial setup. Keep write access to the json file. This functionality was contributed by Stefan Krah, who has additional information about using it here: http://www.bytereef.org/howto/oauth2/getmail.html. See docs/getmailrc-examples. * imap_search, imap_on_delete (string) — imap_search is the second parameter of Python's IMAP search. Set imap_search = UNSEEN to skip read messages. The value is case-insensitive. It may be in parentheses. As an example imap_search = Unseen imap_on_delete = \Seen fetches only new messages and sets the message flag to SEEN on "delete" (only \Delete in it will actually delete the email on the server). Setting the flag will also override the global delete = true (which is also made sure via -d in the command line). \Seen avoids further retrieval without deleting the message on the server. The default is imap_search = ALL imap_on_delete = (\Deleted \Seen) For more on IMAP SEARCH see rfc3501. The command line parameter --searchset/-s (search and set) overrides imap_search to select emails to retrieve and imap_on_delete to set flags on the server A starting , becomes IMAP flag char \. -s implies -d, ie is like -ds. -ds, alone is \SEEN. -ds, means "mark as read". Without , it is a search string to select mail to retrieve. * imap_id_extension (boolean). If set, getmail sends getmail 6.0.0 as client ID to the server. The default is False. SSL Client Parameters All SSL-enabled retriever types also take the following options, to allow specifying the use of a particular client key and client certificate in establishing a connection to the server. * keyfile (string) — use the specified PEM-formatted key file in the SSL negotiation. * certfile (string) — use the specified PEM-formatted certificate file in the SSL negotiation. SSL Certificate Validation and Server Parameters All SSL-enabled POP and IMAP retriever types also take the following options, allowing you to require validation of the server's SSL certificate, or to check the server's certificate fingerprint against a known good value, or to control the specific SSL cipher used during the connection. Note: using these features, including server certificate validation, requires using Python 2.7 or higher with getmail. If you use an earlier version of Python, these features will not work, and no server certificate validation will be performed. Also note that these features are not currently implemented for SPDS retrievers; I would be interested in hearing from SPDS users who desire these features. * ca_certs (string) — advanced option to perform validation of the server's SSL certificate. Specify the path to a PEM-formatted list of 1 or more valid and trusted root certification authority (CA) certificates. Note: this option is only available with Python 2.7 or higher. To find out which root CA is used to sign the chain of certificates for a given server, you can run openssl s_client -showcerts -connect HOST:PORT < /dev/null 2>/dev/null \ | grep '^[[:space:]]*i:' | tail -n 1 If the server's certificate cannot be validated based upon the supplied trusted root certificates, getmail will abort the connection. Root certificates are not supplied with getmail; your OS probably installs a set by default for use by the system, or you may wish to use a specific set of trusted root certificates provided by your employer or a trusted third party. Common locations for OS-supplied SSL root certification authority certificates include: * Linux (Debian, Ubuntu, Arch, SuSE): /etc/ssl/certs/ * Linux (RedHat, Fedora, CentOS): /etc/pki/tls/certs/ * FreeBSD: /usr/local/share/certs/ * OpenBSD: /etc/ssl/ * OSX: /System/Library/OpenSSL/certs/ * Windows: ask Microsoft * ssl_ciphers (string) — advanced option to control which SSL cipher algorithms will be allowed to proceed. See the Open SSL documentation for details. If the specified setting results in no possible ciphers available, getmail will abort the connection. E.g. on error DH_KEY_TOO_SMALL, one could downgrade the security level via ssl_ciphers = DEFAULT@SECLEVEL=1 Note: this option is only available with Python 2.7 or higher. * ssl_version (string) — advanced option to control which SSL version getmail tries to use to connect to the server; the default is "sslv23". Another useful value is probably "sslv3". The possible values are: * sslv23 * sslv3 * tlsv1 * tlsv1_1 * tlsv1_2 Note that this option exists only to help in connecting certain legacy, out-of-date, broken servers; most users should not specify this option at all. Using this option without knowing what you are doing can reduce the effectiveness of your encrypted connection. Note: this option is only available with Python 2.7 or higher. Note: see the FAQ for details on how to work around Gmail connection problems with OpenSSL v.1.1.1 and later. * ssl_fingerprints (tuple of quoted strings) — advanced option to notice when the server's SSL certificate changes. Supply a list of one or more SHA256 certificate fingerprints, and getmail will confirm whether the server's certificate fingerprint is in the list of allowed fingerprints; if it is not, getmail will abort the connection. Getmail will log the fingerprint of the server's certificate if you supply the --fingerprint commandline option. Note: this option is only available with Python 2.7 or higher. * ssl_cert_hostname (string) — advanced option to specify an alternate hostname which is expected in the server's SSL certificate hostname field. Specify this if the name used to connect to the server is known not to match the hostname in the server's certificate; otherwise, getmail will error out with a hostname mismatch. SimplePOP3Retriever The SimplePOP3Retriever class takes the common retriever parameters above, plus the following optional parameters: * use_apop (boolean) — if set to True, getmail will use APOP-style authentication to log in to the server instead of normal USER/PASS authentication. This is not supported by many POP3 servers. Note that APOP adds much less security than might be supposed; weaknesses in its hashing algorithm mean that an attacker can recover the first three characters of the password after snooping on only a few hundred authentications between a client and server — see http://www.securityfocus.com/archive/1/464477/30/0/threaded for details. The default is False. * timeout (integer) — how long (in seconds) to wait for socket operations to complete before considering them failed. If not specified, the default is 180 seconds. You may need to increase this value in particularly poor networking conditions. * delete_dup_msgids (boolean) — if set to True, and the POP3 server identifies multiple messages as having the same "unique" identifier, all but the first will be deleted without retrieving them. BrokenUIDLPOP3Retriever This retriever class is intended only for use with broken POP3 servers that either do not implement the UIDL command, or which do not properly assign unique identifiers to messages (preventing getmail from determining which messages it has seen before). It will identify every message in the mailbox as a new message, and therefore if you use this retriever class and opt not to delete messages after retrieval, it will retrieve those messages again the next time getmail is run. Use this retriever class only if your mailbox is hosted on such a broken POP3 server, and the server does not provide another means of getmail accessing it (i.e., IMAP). The BrokenUIDLPOP3Retriever class takes the common retriever parameters above, plus the following optional parameters: * use_apop (boolean) — see SimplePOP3Retriever for definition. * timeout (integer) — see SimplePOP3Retriever for definition. SimpleIMAPRetriever The SimpleIMAPRetriever class takes the common retriever parameters above, plus the following optional parameters: * timeout (integer) — see SimplePOP3Retriever for definition. SimplePOP3SSLRetriever The SimplePOP3SSLRetriever class takes the common retriever parameters above, plus the following optional parameters: * use_apop (boolean) — see SimplePOP3Retriever for definition. * delete_dup_msgids (boolean) — see SimplePOP3Retriever for definition. * ca_certs (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_ciphers (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_version (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate Validation and Server Parameters for definition BrokenUIDLPOP3SSLRetriever The BrokenUIDLPOP3SSLRetriever class takes the common retriever parameters above, plus the following optional parameters: * use_apop (boolean) — see SimplePOP3Retriever for definition. * keyfile (string) — see SSL Client Parameters for definition. * certfile (string) — see SSL Client Parameters for definition. * ca_certs (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_ciphers (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_version (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate Validation and Server Parameters for definition SimpleIMAPSSLRetriever The SimpleIMAPSSLRetriever class takes the common retriever parameters above, plus the following optional parameters: * mailboxes (tuple of quoted strings) — see common retriever parameters for definition. * move_on_delete (string) — see SimpleIMAPRetriever for definition. * keyfile (string) — see SSL Client Parameters for definition. * certfile (string) — see SSL Client Parameters for definition. * ca_certs (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_ciphers (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_version (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate Validation and Server Parameters for definition * imap_search (string) — see SimpleIMAPRetriever for definition. MultidropPOP3Retriever The MultidropPOP3Retriever class takes the common retriever parameters above, plus the following required parameter: * envelope_recipient (string) — the name and position of the header field which records the envelope recipient address. This is set to a value of the form field_name : field_position . The first (topmost) Delivered-To: header field would be specified as: envelope_recipient = delivered-to:1 The MultidropPOP3Retriever also takes the following optional parameters: * use_apop (boolean) — see SimplePOP3Retriever for definition. * timeout (integer) — see SimplePOP3Retriever for definition. MultidropPOP3SSLRetriever The MultidropPOP3SSLRetriever class takes the common retriever parameters above, plus the following required parameter: * envelope_recipient (string) — see MultidropPOP3Retriever for definition. The MultidropPOP3SSLRetriever class also takes the following optional parameters: * use_apop (boolean) — see SimplePOP3Retriever for definition. * keyfile (string) — see SSL Client Parameters for definition. * certfile (string) — see SSL Client Parameters for definition. * ca_certs (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_ciphers (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_version (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate Validation and Server Parameters for definition MultidropSDPSRetriever The MultidropSDPSRetriever class takes the common retriever parameters above, plus the following optional parameters: * timeout (integer) — see SimplePOP3Retriever for definition. MultidropIMAPRetriever The MultidropIMAPRetriever class takes the common retriever parameters above, plus the following required parameter: * envelope_recipient (string) — see MultidropPOP3Retriever for definition. The MultidropIMAPRetriever class also takes the following optional parameters: * timeout (integer) — see SimplePOP3Retriever for definition. * mailboxes (tuple of quoted strings) — see common retriever parameters for definition. * move_on_delete (string) — see SimpleIMAPRetriever for definition. * imap_search (string) — see SimpleIMAPRetriever for definition. MultidropIMAPSSLRetriever The MultidropIMAPSSLRetriever class takes the common retriever parameters above, plus the following required parameter: * envelope_recipient (string) — see MultidropPOP3Retriever for definition. The MultidropIMAPSSLRetriever class also takes following optional parameters: * mailboxes (tuple of quoted strings) — see common retriever parameters for definition. * move_on_delete (string) — see SimpleIMAPRetriever for definition. * keyfile (string) — see SSL Client Parameters for definition. * certfile (string) — see SSL Client Parameters for definition. * ca_certs (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_ciphers (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_version (string) — see SSL Certificate Validation and Server Parameters for definition * ssl_fingerprints (tuple of quoted strings) — see SSL Certificate Validation and Server Parameters for definition * imap_search (string) — see SimpleIMAPRetriever for definition. Retriever examples A typical POP3 mail account (the basic kind of mailbox provided by most internet service providers (ISPs)) would use a retriever configuration like this: [retriever] type = SimplePOP3Retriever server = popmail.isp.example.net username = account_name password = my_mail_password If your ISP provides POP3 access on a non-standard port number, you would need to include the port parameter: [retriever] type = SimplePOP3Retriever server = popmail.isp.example.net port = 8110 username = account_name password = my_mail_password If your ISP provides POP3-over-SSL and you wanted to use that, your retriever configuration might look like this: [retriever] type = SimplePOP3SSLRetriever server = popmail.isp.example.net username = account_name password = my_mail_password If you have an IMAP mail account and want to retrieve messages from several mail folders under that account, and you want to move messages to a special folder when deleting them, you would use a retriever configuration like this: [retriever] type = SimpleIMAPRetriever server = imapmail.isp.example.net username = account_name password = my_mail_password mailboxes = ("INBOX", "lists.unix", "lists.getmail") move_on_delete = mail.deleted If you are retrieving your company's mail from a domain POP3 mailbox for delivery to multiple local users, you might use a retriever configuration like this: [retriever] type = MultidropPOP3Retriever server = imapmail.isp.example.net username = account_name password = company_maildrop_password envelope_recipient = delivered-to:1 Creating the [destination] section The destination section of the rc file tells getmail what to do with retrieved messages. Begin with the section header line as follows: [destination] Then, include a type string parameter to tell getmail what type of mail destination this is. The possible values are: * Maildir — deliver all messages to a local qmail-style maildir * Mboxrd — deliver all messages to a local mboxrd-format mbox file with fcntl-type locking. * MDA_external — use an external message delivery agent (MDA) to deliver messages. Typical MDAs include maildrop, procmail, and others. * MDA_lmtp — deliver messages to an external MDA via the LMTP protocol. * MultiDestination — unconditionally deliver messages to multiple destinations (maildirs, mbox files, external MDAs, or other destinations). * MultiSorter — sort messages according to the envelope recipient (requires a domain mailbox retriever) and deliver to a variety of maildirs, mbox files, external MDAs, or other destinations based on regular expressions matching the recipient address of each message. Messages not matching any of the regular expressions are delivered to a default "postmaster" destination. * MultiGuesser — sort messages according to getmail's best guess at what the envelope recipient of the message might have been, and deliver to a variety of maildirs, mbox files, external MDAs, or other destinations based on regular expressions matching those addresses. Messages not matching any of the regular expressions are delivered to a default "postmaster" destination. * MDA_qmaillocal — use qmail-local to deliver messages according to instructions in a .qmail file. Maildir The Maildir destination delivers to a qmail-style maildir. The maildir must already exist, and must contain all of the subdirectories required by the maildir format. getmail will not create the maildir if it does not exist. If you're not familiar with the maildir format, the requirements in a nutshell are: it must be a directory containing three writable subdirectories cur, new, and tmp, and they must all reside on the same filesystem. The Maildir destination takes one required parameter: * path (string) — the path to the maildir, ending in slash (/). This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. You might want to deliver messages to a maildir named Maildir in your home directory; you could do this with a configuration like this: [destination] type = Maildir path = ~/Maildir/ The Maildir destination also takes two optional parameters: * user (string) — on Unix-like systems, if supplied, getmail will change the effective UID to that of the named user before delivering messages to the maildir. Note that this typically requires root privileges. getmail will not deliver to maildirs as root, so this "optional" parameter is required in that situation. * filemode (string) — if supplied, getmail will cause the delivered message files in the maildir to have at most these permissions (given in standard Unix octal notation). Note that the current umask is masked out of the given value at file creation time. The default value, which should be appropriate for most users, is "0600". Mboxrd The Mboxrd destination delivers to an mboxrd-format mbox file with either fcntl-type (lockf) or flock-type file locking. The file must already exist and appear to be a valid mboxrd file before getmail will try to deliver to it — getmail will not create the file if it does not exist. If you want to create a new mboxrd file for getmail to use, simply create a completely empty (0-byte) file. You must ensure that all other programs accessing any the mbox file expect mboxrd-format mbox files and the same type of file locking that you configure getmail to use; failure to do so can cause mbox corruption. If you do not know what type of file locking your system expects, ask your system administrator. If you are the system administrator and don't know what type of file locking your system expects, do not use Mboxrd files; use Maildirs instead. Note that delivering to mbox files over NFS can be unreliable and should be avoided; this is the case with any MDA. The Mboxrd destination takes one required parameter: * path (string) — the path to the mbox file. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. You might want to deliver messages to an mbox file named inbox in your home directory; you could do this with a configuration like this: [destination] type = Mboxrd path = ~/inbox The Mboxrd destination also takes two optional parameters: * user (string) — on Unix-like systems, if supplied, getmail will change the effective UID to that of the named user before delivering messages to the mboxrd file. Note that this typically requires root privileges. getmail will not deliver to mbox files as root, so this "optional" parameter is required in that situation. * locktype (string) — which type of file locking to use; may be "lockf" (for fcntl locking) or "flock". The default in getmail 4.7.0 and later is lockf. MDA_external MDA_external delivers messages by running an external program (known as a message delivery agent, or MDA) and feeding it the message on its standard input. Some typical MDAs include maildrop and procmail. The MDA_external destination takes one required parameter: * path (string) — the path to the command to run. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. The MDA_external destination also takes several optional parameters: * arguments (tuple of quoted strings) — arguments to be supplied to the command. The following substrings will be substituted with the equivalent values from the message: * %(sender) — envelope return-path address If the message is retrieved with a multidrop retriever class, the message recipient (and parts of it) are also available with the following replacement substrings: * %(recipient) — envelope recipient address * %(local) — local-part of the envelope recipient address * %(domain) — domain-part of the envelope recipient address * %(mailbox) — the IMAP mailbox name the message was retrieved from; for POP, this will be empty The default value of the arguments parameter is (), so no arguments are supplied to the command. * unixfrom (boolean) — whether to include a Unix-style mbox From_ line at the beginning of the message supplied to the command. Defaults to false. Some MDAs expect such a line to be present and will fail to operate if it is missing. * user (string) — if supplied, getmail will change the effective UID to that of the named user. Note that this typically requires root privileges. * group (string) — if supplied, getmail will change the effective GID to that of the named group. Note that this typically requires root privileges. * allow_root_commands (boolean) — if set, getmail will run external commands even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root. * ignore_stderr (boolean) — if set, getmail will not consider it an error if the program writes to stderr. The default is false, which causes getmail to consider the delivery failed and leave the message on the server, proceeding to the next message. This prevents loss of mail if the MDA writes to stderr but fails to exit nonzero when it encounters an error. Note that setting this option has serious implications; some MDAs can fail to deliver a message but still exit 0, which can cause loss of mail if this option is set. Only change this setting if you are confident your MDA always exits nonzero on error. * pipe_stdout (boolean) — if set, stdout generated by the external MDA will be piped to stdout of getmail. The default is true, which is the default with fetchmail. Settings it to false will retain the former behavior of getmail and suppress any data written to stdout. A basic invocation of an external MDA might look like this: [destination] type = MDA_external path = /path/to/mymda arguments = ("--log-errors", ) Something more complex might look like this: [destination] type = MDA_external path = /path/to/mymda # Switch to fred's UID and the mail group GID before delivering his mail user = fred group = mail arguments = ("--strip-forbidden-attachments", "--recipient=%(recipient)") MDA_lmtp MDA_lmtp delivers messages via LMTP by connecting to a Unix domain or TCP socket. It currently does not support any authentication. The MDA_lmtp destination takes one required parameter: * host (string) — the host to connect to. Either a DNS-resolvable hostname, an IP address or absolute path to a Unix domain socket. The MDA_lmtp destination also takes several optional parameters: * port (integer) — the remote port to connect to. Ignored if host is a Unix domain socket. * fallback (string) — an alternative recipient address to deliver to in case delivery to the intended recipient fails permanently (i.e. with a 5xx status code). * override (string) — deliver mail to an alternative recipient address instead of the one given by the envelope or mail headers. Behaviour of the fallback parameter still applies in case delivery fails. A configuration connecting to a Dovecot LMTP server might look like this: [destination] type = MDA_lmtp host = /run/dovecot/lmtp Another example delivering to a remote host on a non-standard port: [destination] type = MDA_lmtp host = mail.example.com port = 3333 An example where each mail is delivered to user alice, but if this fails (i.e. their user quota has been reached) it is delivered to bob: [destination] type = MDA_lmtp host = mail.example.com override = alice fallback = bob MultiDestination MultiDestination doesn't do any message deliveries itself; instead, it lets you specify a list of one or more other destinations which it will pass each message to. You can use this to deliver each message to several different destinations. The MultiDestination destination takes one required parameter: * destinations (tuple of quoted strings) — the destinations which the messages will be passed to. A destination is a string that refers to another configuration file section by name (shortcuts for maildirs and mboxrd files are also provided; see below), like this: destinations = ('[other-destination-1]', '[other-destination-2]') [other-destination-1] type = Mboxrd path = /var/spool/mail/alice user = alice [other-destination-2] type = Maildir path = /home/joe/Maildir/ user = joe Because Maildir and Mboxrd destinations are common, you can specify them directly as a shortcut if they do not require a user parameter. If the string (after expansion; see below) starts with a dot or slash and ends with a slash, it specifies the path of a Maildir destination, while if it starts with a dot or a slash and does not end with a slash, it specifies the path of a Mboxrd destination. For instance, you can deliver mail to two maildirs with the following: destinations = ('~/Mail/inbox/', '~/Mail/archive/current/') Each destination string is first expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. Some examples: * To deliver to a maildir named Maildir in the home directory of user jeff, when getmail is run as that user: [destination] type = MultiDestination destinations = ("~jeff/Maildir/", ) * To deliver to an mboxrd file: [destination] type = MultiDestination destinations = ("/var/spool/mail/alice", ) * To deliver with an external MDA: [destination] type = MultiDestination destinations = ("[procmail-as-bob]", ) [procmail-as-bob] type = MDA_external path = /path/to/procmail arguments = ('~bob/.procmailrc', '-f', '%(sender)') user = bob Of course, the whole point of MultiDestination is to allow you to specify multiple destinations, like this: [destination] type = MultiDestination destinations = ( "~jeff/Mail/inbox", "[procmail-as-jeff]", "/var/mail-archive/incoming" ) [procmail-as-jeff] type = MDA_external path = /path/to/procmail arguments = ('~jeff/.procmailrc', '-f', '%(sender)') user = jeff MultiSorter MultiSorter compares the envelope recipient address of messages against a list of user-supplied regular expressions and delivers the message to the destination (maildir, mboxrd file, or other) associated with any matching patterns. A message can match multiple patterns and therefore be delivered to multiple matching destinations. Any message which matches none of the patterns is delivered to a default destination for the postmaster. Because MultiSorter requires the envelope recipient to operate, it must be used with a domain mailbox retriever. If you instead want to do some basic message sorting based on getmail's best guess as to the envelope recipient of the message, see the MultiGuesser destination class below. The MultiSorter destination takes one required parameter: * default (string) — the destination for messages which aren't matched by any of the "locals" regular expressions. The destination can be a maildir, mboxrd file, or other destination. See MultiDestination for an explanation of how the type of destination is interpreted from this value. The MultiSorter destination also takes one optional parameter: * locals (tuple of 2-tuples) — zero or more regular expression – destination pairs. Messages will be delivered to each destination for which the envelope recipient matches the given regular expression. The regular expression and destination are supplied as two quoted strings in a tuple; locals is then a tuple of such pairs of strings. Destinations are specified in the same manner as with the "default" parameter, above. Important note: if your regular expression contains backslashes (by themselves, or as part of an escaped character or symbol like \n or \W ), you need to tell the parser that this expression must be parsed "raw" by prepending the string with an "r": locals = ( (r'jeff\?\?\?@.*', '[jeff]'), ('alice@', '[alice]') ) locals = ( ('jeff@.*', '[jeff]'), (r'alice\D+@', '[alice]') ) Note that if you don't understand regular expressions, you don't need to worry about it. In general, an email address is a regular expression that matches itself. The only significant times this isn't the case is when the address contains odd punctuation characters like ^, $, \, or [. Handy hints: * the regular expression . (dot) matches anything * matches can occur anywhere in the address. If you want to only match at the beginning, start your expression with the ^ character. If you only want to match the whole address, also end your expression with a dollar sign $. Using regular expressions: * The regular expression joe@example.org matches the addresses joe@example.org, joe@example.org.net, and heyjoe@example.org. * The regular expression ^jeff@ matches the addresses jeff@example.org and jeff@example.net, but not otherjeff@example.org. * The regular expression sam matches the addresses sam@example.org, samantha@example.org, asam@example.org, and chris@isam.example.net. Some examples: * * Deliver mail matching jeff@example.net to ~jeff/Maildir/ * Deliver mail matching alice@anything to ~alice/inbox * Deliver all other mail to ~bob/Maildir/ [destination] type = MultiSorter default = [bob-default] locals = ( ('jeff@example.net', '[jeff]'), ('alice@', '[alice]') ) [jeff] type = Maildir path = ~jeff/Maildir/ user = jeff [alice] type = Mboxrd path = ~alice/inbox user = alice [bob-default] type = Maildir path = ~bob/Maildir/ user = bob * * Deliver mail for jeff, bob, and alice to maildirs in their home directories * Deliver copies of all messages to samantha's mail archive * Deliver copies of all messages to a program that logs certain information. This program should run as the user log, and command arguments should tell it to record the info to /var/log/mail/info [destination] type = MultiSorter default = doesn't matter, this won't be used, as locals will always match locals = ( ('^jeff@', '[jeff]'), ('^bob@', '[bob]'), ('^alice@', '[alice]'), ('.', '[copies]'), ('.', '[info]') ) [alice] type = Maildir path = ~alice/Maildir/ user = alice [bob] type = Maildir path = ~bob/Maildir/ user = bob [jeff] type = Maildir path = ~jeff/Maildir/ user = jeff [copies] type = Maildir path = ~samantha/Mail/archive/copies/ user = samantha [info] type = MDA_external path = /path/to/infologger arguments = ('--log=/var/log/mail/info', '--sender=%(sender)', '--recipient=%(recipient)) user = log MultiGuesser MultiGuesser tries to guess what the envelope recipient address of the message might have been, by comparing addresses found in the message header against a list of user-supplied regular expressions, and delivers the message to the destination (maildir, mboxrd file, or other) associated with any matching patterns. A message can match multiple patterns and therefore be delivered to multiple matching destinations. Any message which matches none of the patterns is delivered to a default destination for the postmaster. In this fashion, you can do basic mail filtering and sorting with getmail without using an external filtering message delivery agent (MDA) (such as maildrop or procmail), if and only if the message recipient is the criteria you want to filter on. If you want to filter based on arbitrary message criteria, like "What address is in the To: header field?" or "Who is the message from?", then use the filtering MDA of your choice, called from a getmail MDA_external destination. MultiGuesser is similar to MultiSorter, except that it does not operate on the true envelope recipient address, and therefore does not require a domain mailbox retriever. Because it is "guessing" at the intended recipient of the message based on the contents of the message header, it is fallible — for instance, the address of a recipient of a mailing list message may not appear in the header of the message at all. If your locals regular expression patterns are only looking for that address, MultiGuesser will then have to deliver it to the destination specified as the default recipient. This functionality is very similar to the guessing functionality of getmail version 2, which was removed in version 3. MultiGuesser extracts a list of addresses from the message header like this: 1. it looks for addresses in any Delivered-To: header fields. 2. if no addresses have been found, it looks for addresses in any Envelope-To: header fields. 3. if no addresses have been found, it looks for addresses in any X-Envelope-To: header fields. 4. if no addresses have been found, it looks for addresses in any Apparently-To: header fields. 5. if no addresses have been found, it looks for addresses in any Resent-to: or Resent-cc: header fields (or Resent-bcc:, which shouldn't be present). 6. if no addresses have been found, it looks for addresses in any To: or cc: header fields (or bcc:, which shouldn't be present). The MultiGuesser destination takes one required parameter: * default (string) — see MultiSorter for definition. The MultiGuesser destination also takes one optional parameter: * locals (tuple of 2-tuples) — see MultiSorter for definition. Examples: If you have a simple POP3 account (i.e. it's not a multidrop mailbox) and you want to deliver your personal mail to your regular maildir, but deliver mail from a couple of mailing lists (identified by the list address appearing in the message header) to separate maildirs, you could use a MultiGuesser configuration like this: [destination] type = MultiGuesser default = ~/Maildir/ locals = ( ("list-address-1@list-domain-1", "~/Mail/mailing-lists/list-1/"), ("list-address-2@list-domain-2", "~/Mail/mailing-lists/list-2/"), ) See MultiSorter above for other examples of getmail rc usage; the only difference is the type parameter specifying the MultiGuesser destination. MDA_qmaillocal MDA_qmaillocal delivers messages by running the qmail-local program as an external MDA. qmail-local uses .qmail files to tell it what to do with messages. If you're not already familiar with qmail, you don't need to use this destination class. The MDA_qmaillocal destination takes several optional parameters: * qmaillocal (string) — path to the qmail-local program. The default value is /var/qmail/bin/qmail-local. * user (string) — supplied to qmail-local, and also tells getmail to change the current effective UID to that of the named user before running qmail-local. Note that this typically requires root privileges. The default value is the account name of the current effective UID. * group (string) — if supplied, getmail will change the effective GID to that of the named group before running qmail-local. Note that this typically requires root privileges. * homedir (string) — supplied to qmail-local. The default value is the home directory of the account with the current effective UID. * localdomain (string) — supplied to qmail-local as its domain argument. The default value is the fully-qualified domain name of the local host. * defaultdelivery (string) — supplied to qmail-local as its defaultdelivery argument. The default value is ./Maildir/. * conf-break (string) — supplied to qmail-local as its dash argument. The default value is -. * localpart_translate (2-tuple of quoted strings) — if supplied, the recipient address of the message (which is used to construct the local argument (among others) to qmail-local) will have any leading instance of the first string replaced with the second string. This can be used to remap recipient addresses, trim extraneous prefixes (such as the qmail virtualdomain prepend value), or perform other tasks. The default value is ('', '') (i.e., no translation). * strip_delivered_to (boolean) — if set, Delivered-To: header fields will be removed from the message before handing it to qmail-local. This may be necessary to prevent qmail-local falsely detecting a looping message if (for instance) the system retrieving messages otherwise believes it has the same domain name as the retrieval server. Inappropriate use of this option may cause message loops. The default value is False. * allow_root_commands (boolean) — if set, getmail will run qmail-local even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root. A basic invocation of qmail-local might look like this: [destination] type = MDA_qmaillocal user = joyce Something more complex might look like this: [destination] type = MDA_qmaillocal user = joyce # The mail domain isn't the normal FQDN of the server running getmail localdomain = host.example.net # Trim the server's virtualdomain prepend value from message recipient before # sending it to qmail-local localpart_translate = ('mailhostaccount-', '') Creating the [options] section The optional options section of the rc file can be used to alter getmail's default behaviour. The parameters supported in this section are as follows: * verbose (integer) — controls getmail's verbosity. If set to 2, getmail prints messages about each of its actions. If set to 1, it prints messages about retrieving and deleting messages (only). If set to 0, getmail will only print warnings and errors. Default: 1. * read_all (boolean) — if set, getmail retrieves all available messages. If unset, getmail only retrieves messages it has not seen before. Default: True. * use_netrc (boolean) — if set, getmail will read a .netrc or .authinfo file to set the username and/or password parameter for matching retrievers. A retriever matches if its server parameter matches a machine entry in the netrc file. The location of the .netrc file to read can be set with the netrc_file option. Default: False. * netrc_file (string) — sets the file that use_netrc reads. Has no effect unless use_netrc is True. Default: unset, which means use_netrc will read the default netrc file for your system, typically ~/.netrc, unless NETRC or CURLOPT_NETRC_FILE is defined. * delete (boolean) — if set, getmail will delete messages after retrieving and successfully delivering them. If unset, getmail will leave messages on the server after retrieving them. Default: False. * delete_after (integer) — if set, getmail will delete messages this number of days after first seeing them, if they have been retrieved and delivered. This, in effect, leaves messages on the server for a configurable number of days after retrieving them. Note that the delete parameter has higher priority; if both are set, the messages will be deleted immediately. Default: 0, which means not to enable this feature. * to_oldmail_on_each_mail (boolean) — if set, getmail will update the oldmail file on each mail. This is slower, but avoids re-downloading mails, if something went wrong, like the server dying. --to-oldmail-on-each-mail in the command line. Default: False. * only_oldmail_file (boolean) — if set, getmail will not retrieve mails but only update the oldmail file with mails currently on the server. These mails will not be retrieved as long as the oldmail file exists. --only-oldmail-file in the command line. Default: False. * skip_imap_fetch_size (boolean) — will skip fetching RFC822.SIZE. This speeds up mail checks, particularly with a davmail proxies. skip_imap_fetch_size is only valid for IMAP and not valid with any of max_message_size , max_bytes_per_session , delete_bigger_than . * mark_read (boolean) — if set, getmail will mark messages as read after retrieving. It sets, the IMAP (\Seen) flag on the server. The command parameter -m/--mark-read can be used to override the option. -m/--mark-read is related to --searchset/-s, a synonym to -ds, = -ds,Seen. Default: False. * delete_bigger_than (integer) — if set, getmail will delete messages larger than this number of bytes after retrieving them, even if the delete and delete_after options are disabled. The purpose of this feature is to allow deleting only large messages, to help keep a mailbox under quota. Has no effect if delete is set, as that will unconditionally remove messages. If delete_after is also set, the message will be deleted immediately after retrieval if it is over this size, and otherwise will be deleted according to the setting of delete_after. Default: 0, which means not to enable this feature. * max_bytes_per_session (integer) — if set, getmail will retrieve messages totalling up to this number of bytes before closing the session with the server. This can be useful if you do not want large messages causing large bursts of network traffic. Default: 0, which means not to enable this feature. Note that message sizes reported by the server are used, and therefore may vary slightly from the actual size on disk after message retrieval. * max_message_size (integer) — if set, getmail will not retrieve messages larger than this number of bytes. Default: 0, which means not to enable this feature. * max_messages_per_session (integer) — if set, getmail will process a maximum of this number of messages before closing the session with the server. This can be useful if your network or the server is particularly unreliable. Default: 0, which means not to enable this feature. * delivered_to (boolean) — if set, getmail adds a Delivered-To: header field to the message. If unset, it will not do so. Default: True. Note that this field will contain the envelope recipient of the message if the retriever in use is a multidrop retriever; otherwise it will contain the string "unknown". * received (boolean) — if set, getmail adds a Received: header field to the message. If unset, it will not do so. Default: True. * message_log (string) — if set, getmail will record a log of its actions to the named file. The value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. Default: '' (the empty string), which means not to enable this feature. * message_log_syslog (boolean) — if set, getmail will record a log of its actions using the system logger. Note that syslog is inherently unreliable and can lose log messages. Default: False. * message_log_verbose (boolean) — if set, getmail will log to the message log file (or syslog) information about messages not retrieved and the reason for not retrieving them, as well as starting and ending information lines. By default, it will log only about messages actually retrieved, and about error conditions. Note that this has no effect if neither message_log nor message_log_syslog is in use. Default: False. Most users will want to either enable the delete option (to delete mail after retrieving it), or disable the read_all option (to only retrieve previously-unread mail). The verbose, read_all, delete, and mark_read parameters can be overridden at run time with commandline options. [options] example To configure getmail to operate quietly, to retrieve only new mail, to delete messages after retrieving them, and to log its actions to a file, you could provide the following in your getmail rc file(s): [options] verbose = 0 read_all = false delete = true message_log = ~/.getmail/log Creating the [filter-something] sections The filter-something section(s) of the rc file (which are not required) tell getmail to process messages in some way after retrieving them, but before delivering them to your destinations. Filters can tell getmail to drop a message (i.e. not deliver it at all), add information to the message header (i.e. for a spam- classification system or similar), or modify message content (like an antivirus system stripping suspected MIME parts from messages). You can specify any number of filters; provide a separate rc file section for each, naming each of them filter-something. They will be run in collated order, so it's likely simplest to name them like this: * [filter-1] * [filter-2] * [filter-3] Begin with the section header line as follows: [filter-something] Then, include a type string parameter to tell getmail what type of filter. The possible values are: * Filter_classifier — run the message through an external program, and insert the output of the program into X-getmail-filter-classifier: header fields in the message. Messages can be dropped by having the filter return specific exit codes. * Filter_external — supply the message to an external program, which can then modify the message in any fashion. The program must print the modified message to stdout. getmail reads the modified message from the program in this fashion before proceeding to the next filter or destination. Messages can be dropped by having the filter return specific exit codes. * Filter_TMDA — run the message through the tmda-filter program for use with the Tagged Message Delivery Agent (TMDA) package. If tmda-filter returns 0, the message will be passed to the next filter (or destination). If it returns 99, the message will be dropped, and TMDA is responsible for sending a challenge message, queuing the original, etc., as with normal TMDA operation in a .qmail, .courier, or .forward file. By default, if a filter writes anything to stderr, getmail will consider the delivery to have encountered an error. getmail will leave the message on the server and proceed to the next message. You must configure any filter you use not to emit messages to stderr except on errors — please see the documentation for your filter program for details. Optionally, if you know your filter can emit warnings on stderr under non-error conditions, you can set the ignore_stderr option. Filter_classifier Filter_classifier runs the message through an external program, placing the output of that program into X-getmail-filter-classifier: header fields. It can also cause messages to be dropped by exiting with a return code listed in the exitcodes_drop parameter. Filter_classifier has one required parameter: * path (string) — the path to the command to run. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. In addition, Filter_classifier takes the following optional parameters: * arguments (tuple of quoted strings) — arguments to be supplied to the command. The following substrings will be substituted with the equivalent values from the message: * %(sender) — envelope return-path address If the message is retrieved with a multidrop retriever class, the message recipient (and parts of it) are also available with the following replacement substrings: * %(recipient) — envelope recipient address * %(local) — local-part of the envelope recipient address * %(domain) — domain-part of the envelope recipient address The default value of the arguments parameter is (), so no arguments are supplied to the command. * unixfrom (boolean) — whether to include a Unix-style mbox From_ line at the beginning of the message supplied to the command. Default: False. * user (string) — if supplied, getmail will change the effective UID to that of the named user. Note that this typically requires root privileges. * group (string) — if supplied, getmail will change the effective GID to that of the named group. Note that this typically requires root privileges. * allow_root_commands (boolean) — if set, getmail will run external commands even if it is currently running with root privileges. The default is false, which causes getmail to raise an exception if it is asked to run an external command as root. Note that setting this option has serious security implications. Don't use it if you don't know what you're doing. I strongly recommend against running external processes as root. * ignore_stderr (boolean) — if set, getmail will not consider it an error if the filter writes to stderr. The default is false, which causes getmail to consider the delivery failed and leave the message on the server, proceeding to the next message. This prevents loss of mail if the filter writes to stderr but fails to exit nonzero when it encounters an error. Note that setting this option has serious implications; some poorly-written programs commonly used as mail filters can can mangle or drop mail but still exit 0, their only clue to failure being warnings emitted on stderr. Only change this setting if you are confident your filter always exits nonzero on error. * exitcodes_drop (tuple of integers) — if the filter returns an exit code in this list, the message will be dropped. The default is (99, 100). * exitcodes_keep (tuple of integers) — if the filter returns an exit code other than those in exitcodes_drop and exitcodes_keep, getmail assumes the filter encountered an error. getmail will then not proceed, so that the message is not lost. The default is (0, ). Filter_external Filter_external runs the message through an external program, and replaces the message with the output of that program, allowing the filter to make arbitrary changes to messages. It can also cause messages to be dropped by exiting with a return code listed in the exitcodes_drop parameter. Filter_external has one required parameter: * path (string) — see Filter_classifier for definition. In addition, Filter_external takes the following optional parameters: * arguments (tuple of quoted strings) — see Filter_classifier for definition. * unixfrom (boolean) — see Filter_classifier for definition. * user (string) — see Filter_classifier for definition. * group (string) — see Filter_classifier for definition. * allow_root_commands (boolean) — see Filter_classifier for definition. * ignore_header_shrinkage (boolean) — by default, getmail will warn if a filtered message's header contains fewer fields than the source message had, to warn you if your filter is unexpectedly deleting information from messages it handles. If you know your filter can legitimately produce a message with a shorter header (such as if it encapsulates the original message), set this option to disable the warning. Do not simply set this if you see the warning; you must understand whether your filter is operating correctly or not before you use this. * ignore_stderr (boolean) — see Filter_classifier for definition. * exitcodes_drop (tuple of integers) — see Filter_classifier for definition. * exitcodes_keep (tuple of integers) — see Filter_classifier for definition. Filter_TMDA Filter_external runs the message through the external program tmda-filter, allowing the use of the Tagged Message Delivery Agent (TMDA) package. As TMDA relies on the message envelope, this filter requires the use of a multidrop retriever class to function. It sets the three environment variables SENDER, RECIPIENT, and EXT prior to running tmda-filter. I've tested this filter, and it Works For Me™, but I'm not a regular TMDA user. I would appreciate any feedback about its use from TMDA users. Filter_TMDA has no required parameters. It has the following optional parameters: * path (string) — the path to the tmda-filter binary. Default: /usr/local/bin/tmda-filter. This value will be expanded for leading ~ or ~USER and environment variables in the form $VARNAME or ${VARNAME}. * user (string) — see Filter_classifier for definition. * group (string) — see Filter_classifier for definition. * allow_root_commands (boolean) — see Filter_classifier for definition. * ignore_stderr (boolean) — see Filter_classifier for definition. * conf-break (string) — this value will be used to split the local-part of the envelope recipient address to determine the value of the EXT environment variable. For example, if the envelope sender address is sender-something@host.example.org, and the envelope recipient address is user-ext-ext2@host.example.net, and conf-break is set to -, getmail will set the environment variables SENDER to "sender-something@host.example.org", RECIPIENT to "user-ext-ext2@host.example.net", and EXT to "ext-ext2". Default: "-". [filter-something] examples You might filter spam messages in your MUA based on information added to the message header by a spam-classification program. You could have that information added to the message header with a filter configuration like this: [filter-3] type = Filter_classifier path = /path/to/my-classifier arguments = ('--message-from-stdin', '--report-to-stdout') user = nobody You might use a program to prevent users from accidentally destroying their data by stripping suspected attachments from messages. You could have that information added to the message header with a filter configuration like this: [filter-3] type = Filter_external path = /path/to/my-mime-filter arguments = ('--message-from-stdin', '--remove-all-but-attachment-types=text/plain,text/rfc822') user = nobody You might use TMDA to challenge messages from unknown senders. If the default parameters are fine for your configuration, this is as simple as: [filter-3] type = Filter_TMDA getmail rc file examples Several examples of different getmail rc configuration are available in the included file getmailrc-examples. Running getmail To use getmail, simply run the script getmail, which is typically installed in /usr/local/bin/ by default. getmail will read the default getmail rc file (getmailrc) from the default configuration/data directory (~/.config/getmail/ or ~/.getmail/) and begin operating. You can modify this behaviour by supplying commandline options to getmail. Commandline options getmail understands the following options: * --version — show getmail's version number and exit * --help or -h — show a brief usage summary and exit * --getmaildir=DIR or -gDIR — use DIR for configuration and data files * --rcfile=FILE or -rFILE — read getmail rc file FILE instead of the default. The file path is assumed to be relative to the getmaildir directory unless this value starts with a slash (/). This option can be given multiple times to have getmail retrieve mail from multiple accounts. This option can also be omitted. Then all files in getmaildir are used, apart from oldmail-*, *.json, .* * --only-account=email@address or -oemail@address choses the rc file based on the username/email. * --searchset=,flag or --searchset=search string or -s,flag. No flag -s, is -s,Seen. See imap_search and imap_on_delete. * -m/--mark-read is like -ds,. It overrides mark_read in [options] * --dump — read rc files, dump configuration, and exit (debugging) * --trace — print extended debugging information If you are using a single getmailrc file with an IMAP server that understands the IDLE extension from RFC 2177, you can use the --idle=MAILBOX option to specify that getmail should wait on the server to notify getmail of new mail in the specified mailbox after getmail is finished retrieving mail. In addition, the following commandline options can be used to override any values specified in the [options] section of the getmail rc files: * --verbose or -v — operate more verbosely. Can be given multiple times. * --quiet or -q — print only warnings or errors while running * --delete or -d — delete messages after retrieving * --dont-delete or -l — do not delete messages after retrieving * --all or -a — retrieve all messages * --new or -n — retrieve only new (unseen) messages For instance, if you want to retrieve mail from two different mail accounts, create a getmail rc file for each of them (named, say, getmailrc-account1 and getmailrc-account2) and put them in directory ~/.config/getmail/. Then run getmail as follows: $ getmail --rcfile getmailrc-account1 --rcfile getmailrc-account2 If those files were located in a directory other than the default, and you wanted to use that directory for storing the data files as well, you could run getmail as follows: $ getmail --getmaildir /path/to/otherdir --rcfile getmailrc-account1 --rcfile getmailrc-account2 Using getmail as an MDA getmail includes helper scripts which allow you to use it to deliver mail from other programs to maildirs or mboxrd files. Using the getmail_maildir MDA The getmail_maildir script can be used as an MDA from other programs to deliver mail to maildirs. It reads the mail message from stdin, and delivers it to a maildir path provided as an argument on the commandline. This path must (after expansion by the shell, if applicable) start with a dot or slash and end with a slash. getmail_maildir uses the contents of the SENDER environment variable to construct a Return-Path: header field and the contents of the RECIPIENT environment variable to construct a Delivered-To: header field at the top of the message. getmail_maildir also accepts the options --verbose or -v which tell it to print a status message on success. The default is to operate silently unless an error occurs. Example You could deliver a message to a maildir named Maildir located in your home directory by running the following command with the message on stdin: $ getmail_maildir $HOME/Maildir/ Using the getmail_mbox MDA The getmail_mbox script can be used as an MDA from other programs to deliver mail to mboxrd-format mbox files. It reads the mail message from stdin, and delivers it to an mbox path provided as an argument on the commandline. This path must (after expansion by the shell, if applicable) start with a dot or slash and not end with a slash. getmail_maildir uses the contents of the SENDER environment variable to construct a Return-Path: header field and mbox From_ line and the contents of the RECIPIENT environment variable to construct a Delivered-To: header field at the top of the message. getmail_mbox also accepts the options --verbose or -v which tell it to print a status message on success. The default is to operate silently unless an error occurs. Example You could deliver a message to an mboxrd-format mbox file named inbox located in a directory named mail in your home directory by running the following command with the message on stdin: $ getmail_mbox $HOME/mail/inbox Using getmail_fetch to retrieve mail from scripts getmail includes the getmail_fetch helper script, which allows you to retrieve mail from a POP3 server without the use of a configuration file. It is primarily intended for use in automated or scripted environments, but can be used to retrieve mail normally. See the getmail_fetch manual page for details on the use of getmail_fetch. getmail6-6.19.07/docs/documentation.html000066400000000000000000000545421474536475600201340ustar00rootroot00000000000000 getmail documentation (version 6)

getmail documentation

This is the documentation for getmail version 6, a port of getmail version 5 to python 3 without adding features.

getmail6 is Copyright © 1998-2025 by Charles Cazabon and others:
<charlesc-getmail @ pyropus.ca>
<roland.puntaier @ gmail.com>

getmail and getmail6 are licensed under the GNU General Public License version 2 (only).

Table of Contents

Features

getmail is a mail retriever designed to allow you to get your mail from one or more mail accounts on various mail servers to your local machine for reading with a minimum of fuss. getmail is designed to be secure, flexible, reliable, and easy-to-use. getmail is designed to replace other mail retrievers such as fetchmail.

getmail includes the following features:

  • simple to install, configure, and use
  • retrieve virtually any mail
    • support for accessing mailboxes with the following protocols:
      • POP3
      • POP3-over-SSL
      • IMAP4
      • IMAP4-over-SSL
      • SDPS (Demon UK's extensions to POP3)
    • support for single-user and domain mailboxes
    • retrieve mail from an unlimited number of mailboxes and servers
    • can remember which mail it has already retrieved, and can be set to only download new messages
  • support for message filtering, classification, and annotation by external programs like spam filters and anti-virus programs
  • support for delivering messages to different destinations based on the message recipient
  • reliability
    • native safe and reliable delivery support for maildirs and mboxrd files, in addition to delivery through arbitrary external message delivery agents (MDAs)
    • does not destroy information by rewriting mail headers
    • does not cause mail loops by doing SMTP injection, and therefore does not require that you run an MTA (like qmail or sendmail) on your host
  • written in Python, and therefore easy to extend or customize
    • a flexible, extensible architecture so that support for new mail access protocols, message filtering operations, or destination types can be easily added
    • cross-platform operation; getmail 6 should work on Unix/Linux, Macintosh, and other platforms. Windows support available under the free Cygwin package.
  • winner of various software awards, including DaveCentral's "Best of Linux"

Requirements

getmail version 6 requires Python version 3 or later. Python 2.7.18 should also work. If you have only an earlier version of Python available, you can install the latest version without disturbing your current version, or use getmail version 5.* from the original getmail author.

getmail 6 requires that servers uniquely identify the messages they provide (via the UIDL command) to getmail for full functionality. Certain very old or broken POP3 servers may not be capable of this or may not implement the UIDL command at all, and limited support is available for such servers via the BrokenUIDLPOP3Retriever and BrokenUIDLPOP3SSLRetriever retriever classes.

Obtaining getmail

Download getmail 6 from the official website main page at http://getmail6.org/ .

Installing getmail

For the impatient

Installing getmail is very easy:

# sudo pip install getmail6

or

# pip install --user getmail6

To uninstalling do

# sudo pip uninstall getmail6

or

# pip uninstall getmail6

Alternatively download the tarball distribution, unpack it, change into the directory it unpacks into, and run this command:

# python setup.py install

That's all there is to it. 99.9% of users don't need a special package/port/etc. If you'd like more details on install options, keep reading.

Full installation instructions

Once you have downloaded or otherwise obtained getmail, unpack it. On GNU-ish Unix-like systems, this means:

# tar xzf getmail-version.tar.gz

On Macintosh systems, use a Zip-type archiver program to unpack the tarball.

On SystemV-like Unix systems, you may instead need to break this down into two steps:

# gunzip getmail-version.tar.gz
# tar xf getmail-version.tar

Then, change into the extracted getmail directory and start the build process. The easiest installation method is to use the included setup.py to build and install getmail directly.

Installing directly from the source

To build and install directly from the included source, follow these steps.

# cd getmail-version
# python setup.py build

When that completes in a few seconds, become root and then install the software. You can install in the default location, or specify an alternate location to install the software, or specify alternate directories for only part of the package.

Installing in the default location

To install in the default location, become user root and install with the following commands:

# su
enter root password
# python setup.py install

This will, by default, install files into subdirectories under the directory prefix, which is the directory that your Python installation was configured to install under (typically /usr/local/ or /usr/, but other values are sometimes used):

  • the scripts getmail, getmails, getmail_fetch, getmail_maildir, and getmail_mbox will be installed under prefix/bin/
  • the Python package getmailcore (which implements all the protocol–, filter–, and destination-specific code for getmail, plus various other bits) will be installed under the site-specific packages directory of your Python library directory. This directory is prefix/lib/python-python-version/site-packages/.
  • The documentation directory getmail-getmail-version will be installed under prefix/doc/
  • The manual pages for the four scripts will be installed under prefix/man/

You can see a list of the default installation locations by running:

# python setup.py --show-default-install-dirs

Installing under an alternate prefix directory

You can specify an alternate prefix directory by supplying the --prefix option to the install command, like this:

# python setup.py install --prefix=path

This will install the various parts of the package in subdirectories like in the default installation (see the section Installing in the default location above), but under your specified prefix directory. These alternate installations allow you to install the software without root privileges (say, by installing under $HOME/). Note, however, that the getmailcore package will not be in the default Python module search path if you do this; see the section Installing the getmailcore package in a non-standard location if you use this option.

Installing parts of the package to alternate directories

If you only want to change the directory for some of the components, use the following options:

  • --install-lib=path specifies the directory the getmailcore package is installed under (i.e., it will be installed as path/getmailcore ). See the section Installing the getmailcore package in a non-standard location if you use this option.
  • --install-scripts=path specifies the directory the four scripts are installed under (i.e., they will be installed directly in path/ ).
  • --install-data=path specifies the directory the documentation is installed under (i.e., the HTML and plaintext documentation will be installed in the directory path/doc/getmail-getmail-version/, and the man(1) pages will be installed in path/man/man1/.

For example, if your Python installation is located under /usr/ because it was installed as part of your OS, but you would like the getmail scripts installed into /usr/local/bin/ instead of /usr/bin/, while still letting the getmailcore package be installed under /usr/lib/python-python-version/site-packages/, and the documentation and man pages under /usr/doc/ and /usr/man/ you could use this command to install:

# python setup.py --install-scripts=/usr/local/bin/

If you also wanted to locate the documentation and man pages under /usr/local/ but still install the getmailcore package in the default /usr/lib/python-python-version/site-packages/, you would instead use this command to install:

# python setup.py --install-scripts=/usr/local/bin/ --install-data=/usr/local/

Installing the getmailcore package in a non-standard location

Note: if you use one of the above methods to install the getmailcore package into a directory other than the default, the four scripts (getmail, getmails, getmail_fetch, getmail_maildir, and getmail_mbox) will almost certainly be unable to locate the required files from the getmailcore package, because they will not be in a directory in the standard Python module search path. You will need to do one of the following to make those files available to the scripts:

  • set the environment variable PYTHONPATH to tell Python where to find the appropriate modules. See the documentation at the Python.org website for details.

    For example, if the directory getmailcore ends up living in $HOME/tmp/getmail/lib/python3.8/site-packages/getmailcore , then in the shell do:

    # export PYTHONPATH=$HOME/tmp/getmail/lib/python3.8/site-packages .
            

    Note that setting PYTHONPATH in $HOME/.profile (or equivalent) is not sufficient -- for instance, cron runs jobs in a simpler environment, ignoring $HOME/.profile, and getmail would therefore fail when run as a user cron job. It is strongly recommended that you install the Python library files in the site-packages directory which Python provides for exactly this reason.

  • modify the scripts to explicitly tell Python where you've installed them. Insert a line like this:
    sys.path.append('/path/to/installation-directory')
    
    containing the path to the directory you installed the getmailcore directory in, somewhere below the line which reads
    import sys
    
    and before the first line which references getmailcore .

Building a binary package from the source

To build a binary package from the included source, run the following command from inside the unpacked getmail source.

# cd getmail-version
# python setup.py bdist --format=package-format

The useful allowed values for package-format are:

  • rpm — build a .noarch.rpm file which can then be installed with the rpm package manager.
  • pkgtool — build a package for the Sun Solaris pkgtool package manager.
  • sdux — build a package for the HP/UX swinstall software installer.

Ideally, if you use this method, it will result in a "built distribution" binary package in a subdirectory named dist which can then be installed using the appropriate system-specific tool. If you have problems with this process, please do not ask me for assistance; ask your OS vendor or the comp.lang.python newsgroup. The install-directory-from-source process above is the only one I can support, and it should work on all platforms.

getmail6-6.19.07/docs/documentation.txt000066400000000000000000000315061474536475600200020ustar00rootroot00000000000000 getmail documentation This is the documentation for getmail version 6, a port of getmail version 5 to python 3 without adding features. getmail6 is Copyright © 1998-2025 by Charles Cazabon and others: getmail and getmail6 are licensed under the GNU General Public License version 2 (only). Table of Contents * getmail documentation * * getmail documentation * * Features * Requirements * Obtaining getmail * Installing getmail * * For the impatient * Full installation instructions * Installing directly from the source * * Installing in the default location * Installing under an alternate prefix directory * Installing parts of the package to alternate directories * Installing the getmailcore package in a non-standard location * Building a binary package from the source * getmail configuration (version 6) * * Configuring getmail * * Creating a getmail rc file * Running getmail * * Commandline options * Using getmail as an MDA * Using getmail_fetch to retrieve mail from scripts * getmail troubleshooting * * Troubleshooting problems * * Error messages * Warning messages * Unexpected Behaviour * getmail frequently-asked questions (FAQs) * * Frequently-Asked Questions (FAQs) * * About getmail * What is getmail6 and how does it relate to getmail? * Configuring getmail * How do I … * Using getmail with other software * I think I found this bug in getmail … Features getmail is a mail retriever designed to allow you to get your mail from one or more mail accounts on various mail servers to your local machine for reading with a minimum of fuss. getmail is designed to be secure, flexible, reliable, and easy-to-use. getmail is designed to replace other mail retrievers such as fetchmail. getmail includes the following features: * simple to install, configure, and use * retrieve virtually any mail * support for accessing mailboxes with the following protocols: * POP3 * POP3-over-SSL * IMAP4 * IMAP4-over-SSL * SDPS (Demon UK's extensions to POP3) * support for single-user and domain mailboxes * retrieve mail from an unlimited number of mailboxes and servers * can remember which mail it has already retrieved, and can be set to only download new messages * support for message filtering, classification, and annotation by external programs like spam filters and anti-virus programs * support for delivering messages to different destinations based on the message recipient * reliability * native safe and reliable delivery support for maildirs and mboxrd files, in addition to delivery through arbitrary external message delivery agents (MDAs) * does not destroy information by rewriting mail headers * does not cause mail loops by doing SMTP injection, and therefore does not require that you run an MTA (like qmail or sendmail) on your host * written in Python, and therefore easy to extend or customize * a flexible, extensible architecture so that support for new mail access protocols, message filtering operations, or destination types can be easily added * cross-platform operation; getmail 6 should work on Unix/Linux, Macintosh, and other platforms. Windows support available under the free Cygwin package. * winner of various software awards, including DaveCentral's "Best of Linux" Requirements getmail version 6 requires Python version 3 or later. Python 2.7.18 should also work. If you have only an earlier version of Python available, you can install the latest version without disturbing your current version, or use getmail version 5.* from the original getmail author. getmail 6 requires that servers uniquely identify the messages they provide (via the UIDL command) to getmail for full functionality. Certain very old or broken POP3 servers may not be capable of this or may not implement the UIDL command at all, and limited support is available for such servers via the BrokenUIDLPOP3Retriever and BrokenUIDLPOP3SSLRetriever retriever classes. Obtaining getmail Download getmail 6 from the official website main page at http://getmail6.org/ . Installing getmail For the impatient Installing getmail is very easy: # sudo pip install getmail6 or # pip install --user getmail6 To uninstalling do # sudo pip uninstall getmail6 or # pip uninstall getmail6 Alternatively download the tarball distribution, unpack it, change into the directory it unpacks into, and run this command: # python setup.py install That's all there is to it. 99.9% of users don't need a special package/port/etc. If you'd like more details on install options, keep reading. Full installation instructions Once you have downloaded or otherwise obtained getmail, unpack it. On GNU-ish Unix-like systems, this means: # tar xzf getmail-version.tar.gz On Macintosh systems, use a Zip-type archiver program to unpack the tarball. On SystemV-like Unix systems, you may instead need to break this down into two steps: # gunzip getmail-version.tar.gz # tar xf getmail-version.tar Then, change into the extracted getmail directory and start the build process. The easiest installation method is to use the included setup.py to build and install getmail directly. Installing directly from the source To build and install directly from the included source, follow these steps. # cd getmail-version # python setup.py build When that completes in a few seconds, become root and then install the software. You can install in the default location, or specify an alternate location to install the software, or specify alternate directories for only part of the package. Installing in the default location To install in the default location, become user root and install with the following commands: # su enter root password # python setup.py install This will, by default, install files into subdirectories under the directory prefix, which is the directory that your Python installation was configured to install under (typically /usr/local/ or /usr/, but other values are sometimes used): * the scripts getmail, getmails, getmail_fetch, getmail_maildir, and getmail_mbox will be installed under prefix/bin/ * the Python package getmailcore (which implements all the protocol–, filter–, and destination-specific code for getmail, plus various other bits) will be installed under the site-specific packages directory of your Python library directory. This directory is prefix/lib/python-python-version/site-packages/. * The documentation directory getmail-getmail-version will be installed under prefix/doc/ * The manual pages for the four scripts will be installed under prefix/man/ You can see a list of the default installation locations by running: # python setup.py --show-default-install-dirs Installing under an alternate prefix directory You can specify an alternate prefix directory by supplying the --prefix option to the install command, like this: # python setup.py install --prefix=path This will install the various parts of the package in subdirectories like in the default installation (see the section Installing in the default location above), but under your specified prefix directory. These alternate installations allow you to install the software without root privileges (say, by installing under $HOME/). Note, however, that the getmailcore package will not be in the default Python module search path if you do this; see the section Installing the getmailcore package in a non-standard location if you use this option. Installing parts of the package to alternate directories If you only want to change the directory for some of the components, use the following options: * --install-lib=path specifies the directory the getmailcore package is installed under (i.e., it will be installed as path/getmailcore ). See the section Installing the getmailcore package in a non-standard location if you use this option. * --install-scripts=path specifies the directory the four scripts are installed under (i.e., they will be installed directly in path/ ). * --install-data=path specifies the directory the documentation is installed under (i.e., the HTML and plaintext documentation will be installed in the directory path/doc/getmail-getmail-version/, and the man(1) pages will be installed in path/man/man1/. For example, if your Python installation is located under /usr/ because it was installed as part of your OS, but you would like the getmail scripts installed into /usr/local/bin/ instead of /usr/bin/, while still letting the getmailcore package be installed under /usr/lib/python-python-version/site-packages/, and the documentation and man pages under /usr/doc/ and /usr/man/ you could use this command to install: # python setup.py --install-scripts=/usr/local/bin/ If you also wanted to locate the documentation and man pages under /usr/local/ but still install the getmailcore package in the default /usr/lib/python-python-version/site-packages/, you would instead use this command to install: # python setup.py --install-scripts=/usr/local/bin/ --install-data=/usr/local/ Installing the getmailcore package in a non-standard location Note: if you use one of the above methods to install the getmailcore package into a directory other than the default, the four scripts (getmail, getmails, getmail_fetch, getmail_maildir, and getmail_mbox) will almost certainly be unable to locate the required files from the getmailcore package, because they will not be in a directory in the standard Python module search path. You will need to do one of the following to make those files available to the scripts: * set the environment variable PYTHONPATH to tell Python where to find the appropriate modules. See the documentation at the Python.org website for details. For example, if the directory getmailcore ends up living in $HOME/tmp/getmail/lib/python3.8/site-packages/getmailcore , then in the shell do: # export PYTHONPATH=$HOME/tmp/getmail/lib/python3.8/site-packages . Note that setting PYTHONPATH in $HOME/.profile (or equivalent) is not sufficient -- for instance, cron runs jobs in a simpler environment, ignoring $HOME/.profile, and getmail would therefore fail when run as a user cron job. It is strongly recommended that you install the Python library files in the site-packages directory which Python provides for exactly this reason. * modify the scripts to explicitly tell Python where you've installed them. Insert a line like this: sys.path.append('/path/to/installation-directory') containing the path to the directory you installed the getmailcore directory in, somewhere below the line which reads import sys and before the first line which references getmailcore . Building a binary package from the source To build a binary package from the included source, run the following command from inside the unpacked getmail source. # cd getmail-version # python setup.py bdist --format=package-format The useful allowed values for package-format are: * rpm — build a .noarch.rpm file which can then be installed with the rpm package manager. * pkgtool — build a package for the Sun Solaris pkgtool package manager. * sdux — build a package for the HP/UX swinstall software installer. Ideally, if you use this method, it will result in a "built distribution" binary package in a subdirectory named dist which can then be installed using the appropriate system-specific tool. If you have problems with this process, please do not ask me for assistance; ask your OS vendor or the comp.lang.python newsgroup. The install-directory-from-source process above is the only one I can support, and it should work on all platforms. getmail6-6.19.07/docs/faq.html000066400000000000000000002112041474536475600160200ustar00rootroot00000000000000 getmail frequently-asked questions (FAQs) (version 5)

getmail Frequently Asked Questions (FAQ)

getmail6 is Copyright © 1998-2025 by Charles Cazabon and others:
<charlesc-getmail @ pyropus.ca>
<roland.puntaier @ gmail.com>

getmail is licensed under the GNU General Public License version 2 (only).

Table of Contents

Frequently-Asked Questions (FAQs)

The following questions about getmail have been asked more-or-less frequently. Please also read the unexpected behaviour section of the troubleshooting document.

About getmail

What is getmail?

getmail is a mail retriever with support for POP3, POP3-over-SSL, IMAP4, IMAP4-over-SSL, and SDPS mail accounts. It supports normal single-user mail accounts and multidrop (domain) mailboxes. getmail is written in Python, and licensed under the GNU General Public License version 2.

What is getmail6 and how does it relate to getmail?

getmail is the name of this software and project as it was originally developed by Charles Cazabon. At the time of getmail version 5.14 getmail would not work with Python version 3 and newer. As support for Python 3 was not available for getmail at the time while many distributions were dropping support for all Python versions older than version 3, this fork was created.

To avoid confusion and conflicts with Charles' project, we decided to call our fork getmail6 and start our version numbering from 6.

As we intend getmail6 to be a drop-in replacement for getmail, names of files etc. have been kept the same as they are in getmail 5.14 and earlier.

What platforms/machines does getmail run on?

getmail runs on basically any platform. It's designed to, and written in a language that helps to maintain cross-platform compatibility. getmail is known to run on the following platforms:

  • Linux-based GNU systems (all distributions)
  • HURD-based GNU systems
  • FreeBSD
  • OpenBSD
  • NetBSD
  • HP/UX
  • Sun Solaris
  • IBM AIX
  • Digital/Compaq Tru64 (a.k.a OSF/1) UNIX
  • SGI Irix
  • other commercial Unices
  • Digital VMS / OpenVMS
  • BeOS
  • Amiga OS
  • OS/2
  • Cygwin on Windows
  • Macintosh OS X
  • Macintosh OS 9

But getmail will also run on other, less common platforms. The only real requirement is that Python run on that platform, and porting Python is generally very easy.

Does getmail run on MS Windows?

Yes, under the free Cygwin package. Running recent versions of Python under Cygwin requires a process known as "rebasing" your Cygwin installation; you can find details in this Python developers' mailing list message.

Does getmail run on Macintosh systems?

Yes.

Does getmail require Unix/Linux?

No.

How can I get support for getmail?

getmail is Free Software. As such, it comes with no warranty. However, we will do our best to support getmail on a voluntary basis through our GitHub repository.

If you have questions about getmail, the first step is to read the documentation, and the remainder of the Frequently Asked Questions. If your question isn't answered there, please open an issue on GitHub. If you post your question there, we will see it.

I think I found a bug! How do I report it?

First, make sure that you are running the latest version. You can always find what is the latest version by checking this page at the original web site:
http://getmail6.org/.
If you are running an older version of the software, chances are whatever bug you may have found has already been fixed.

After this, please check our repository on GitHub to see if this issue has already been reported. If not, feel free to open an issue to report your bug. You should include at least the following information:

  • getmail version
  • Python version
  • any error message which getmail displayed
  • the output from running getmail with your normal options plus --dump
  • if your problem is getmail not determining the proper local recipient, please include the output of running getmail with your normal options plus --trace, showing the retrieval of one problematic message.

If your bugreport contains confidential information, please exclude this from your report.

I have a neat idea for random feature "foo" … how do I get you to implement it?

Follow the same instructions as for reporting bugs above — yes, that means we would prefer you submit your idea as an issue in our repository allowing other users to also comment on it which may lead to a useful discussion if your feature has not been proposed before.

Why won't you implement random feature "foo"?

Every line of code added to getmail has a certain cost. Every feature added requires code, documentation, and support. Adding features increases the complexity of the software, confuses users, and leads to higher support costs. We therefore weigh features very carefully as a cost-versus-benefit tradeoff before deciding whether to add them.

Some users are confused by this. They think that a feature you don't use has no cost, and therefore if it has any value to anyone, it should be added. That simply isn't the case; the costs of an unused feature are simply borne by others, including us.

If you have asked me to add some feature, and we've said no, this may be the reason. Other possibilities include us simply not having had sufficient time to implement it yet.

Does getmail support virus scanning of retrieved messages?

Yes. You can use getmail message filtering options to do this with an external virus scanning program, or invoke your virus scanning program during delivery with getmail's support for external MDAs.

Also see the FAQ about using getmail with the ClamAV program.

Does getmail support spam filtering of retrieved messages?

Yes. You can use getmail message filtering options to do this with an external spam filtering program, or invoke your spam filtering program during delivery with getmail's support for external MDAs.

Also see the FAQ about using getmail with the SpamAssassin program.

Does getmail support SSL?

Yes. getmail has built in support for POP3-over-SSL and IMAP4-over-SSL.

Does getmail rewrite mail headers when it retrieves mail?

No. Rewriting message header fields is bad for many reasons; the biggest problem is that it causes a loss of critical technical information necessary to track down many mail problems. getmail will add a new Received: header field and a new Delivered-To: header field, but does not rewrite existing headers. You can disable the creation of these header fields.

What are these oldmail* files? Can I delete or trim them?

getmail stores "e;<msgid>\0<timestamp>"e; of messages it has seen in your POP/IMAP account in the oldmail files.

Do NOT delete or edit these files. You'll make getmail re-retrieve all your old mail, or even prevent getmail from running. The files are tiny by modern storage standards; you could have a million of these files and still not have to worry about the disk space they take up for a thousand years.

Why did you write getmail? Why not just use fetchmail?

The below text is by Charles Cazabon, getmail's original author:

Short answer: … well, the short answer is mostly unprintable. The long answer is … well, long:

I do not like some of the design choices which were made with fetchmail. getmail does things a little differently, and for my purposes, better. In addition, most people find getmail easier to configure and use than fetchmail. Perhaps most importantly, getmail goes to great lengths to ensure that mail is never lost, while fetchmail (in its default configuration) frequently loses mail, causes mail loops, bounces legitimate messages, and causes many other problems.

When people have pointed out problems in fetchmail's design and implementation, it's maintainer has frequently ignored them, or (worse yet) gone in the completely wrong direction in the name of "fixing" the problems. For instance, fetchmail's configuration file syntax has been criticized as being needlessly difficult to write; instead of cleaning up the syntax, the maintainer instead included a GUI configuration-file-writing program, leading to comments like:

The punchline is that fetchmail sucks, even if it does have giddily-engineered whizbang configurator apps.

As an example, Dan Bernstein, author of qmail and other software packages, once noted to the qmail list:

Last night, root@xxxxxxxxxxxxxxxxx reinjected thirty old messages from various authors to qmail@xxxxxxxxxxxxxx

This sort of idiocy happens much more often than most subscribers know, thanks to a broken piece of software by Eric Raymond called fetchmail. Fortunately, qmail and ezmlm have loop-prevention mechanisms that stop these messages before they are distributed to subscribers. The messages end up bouncing to the wrong place, thanks to another fetchmail bug, but at least the mailing list is protected.

--D. J. Bernstein

The maintainer also ignored dozens of complaints about fetchmail's behaviour, stating (by fiat) that fetchmail was bug-free and had entered "maintenance mode", allowing him to ignore further bug reports.

fetchmail's default configuration values frequently cause lost or misdirected mail, and seem to be chosen to cause maximum pain and inconvenience. From fetchmail's to-do file (emphasis mine):

Maybe refuse multidrop configuration unless "envelope" is _explicitly_ configured … This would prevent a significant class of shoot-self-in-foot problems.

perhaps treat a delivery as "temporarily failed" … This is so you don't lose mail if you configure the wrong envelope header.

fetchmail is famous for mangling messages it retrieves, rather than leaving them alone as a mail-handling program should. getmail will add trace information to messages (so you can see what happened, and when), but will otherwise leave message content alone.

In addition, fetchmail has a long history of security problems:

In July, 2004, it was noted that there may be at least 2 unfixed denial-of-service attacks, 2 unfixed remote-code-execution, 2 unfixed remote-user-access, and 3 unfixed remote-shell attacks against fetchmail. See http://www.mail-archive.com/euglug@euglug.org/msg00971.html for details

I've given up even trying to stay abreast of the various security holes in fetchmail, but others have noted continuing problems, including:

The fetchmail authors' boneheaded decision to create a configuration-file GUI editor (rather than actually giving fetchmail a sane configuration syntax) also came back to bite them in the ass: in October 2005, it became known that fetchmailconf created its files in such a way that users' passwords could be read during file creation.

Addendum, January 2007: since I wrote the above, the following new security problems have been discovered in fetchmail:

  • CVE-2005-4348 - anyone can crash fetchmail by sending messages without headers
  • CVE-2006-0321 - anyone can crash fetchmail by sending a message that fetchmail tries to bounce
  • CVE-2006-5867 - fetchmail can transmit passwords in plaintext even if the user has configured it not to
  • CVE-2006-5974 - anyone can cause fetchmail to crash by triggering certain error code paths

But don't just take my word for it; see http://docs.freebsd.org/cgi/mid.cgi?200102172349.QAA11724 and http://esr.1accesshost.com/ (note: went offline sometime in 2009 or 2010; the content is still available at http://web.archive.org/web/20080621090439/http://esr.1accesshost.com/ ).

getmail users have not had to worry about any of these security holes or design and implementation errors.

Configuring getmail

What is a "domain mailbox"?

A domain (or multidrop) mailbox is a POP3 mailbox which receives mail for all users in a given domain. Normal mailboxes contain mail for a single user (like jason@myisp.co.uk); some Internet Service Providers which provide webhosting or other services will provide a POP3 mailbox which receives mail for all addresses in a given domain (i.e. mail for service@smallcompany.net, sales@smallcompany.net, and indeed anything @smallcompany.net ends up in the same POP3 mailbox).

getmail provides a method of retrieving mail from a domain mailbox and distributing it among the various users automatically. The retriever classes MultidropPOP3Retriever, MultidropPOP3SSLRetriever, MultidropSDPSRetriever, MultidropIMAPRetriever, and MultidropIMAPSSLRetriever provide this capability.

See the documentation on the [retriever] section for details of what the requirements for a multidrop mailbox are. getmail user Matthias Andree also has a web page about multidrop mailboxes.

Do I have to run sendmail or another MTA to use getmail?

No. getmail delivers directly to maildirs, mboxrd files, or via arbitrary MDAs, and never injects mail via SMTP, so no MTA is necessary.

Will getmail deliver mail as root?

No. When run as the root user on a Unix-like system, getmail drops privileges (switches to an unprivileged group and user id) before delivering to maildirs or mboxrd files. You can specify the user explicitly, or let getmail use the owner of the maildir or mboxrd file.

If getmail attempts to deliver mail and finds it has UID 0 or GID 0, it will refuse the delivery and print an error message.

What's a maildir?

A maildir is a mail storage format invented by D. J. Bernstein (author of qmail) that requires no file locking to deliver to safely and reliably, even over NFS. getmail natively supports delivery to maildirs.

See http://qmail.org/man/man5/maildir.html and http://cr.yp.to/proto/maildir.html for details.

What's "mboxrd" format?

There are various sub-types of the mbox mail storage format. mboxrd is the most reliable of them, though (like all mbox types) it still relies on file locking and is therefore more easily corrupted than maildir format. In particular, using mbox files with multiple writers over NFS can be problematic.

For details on the differences between the various mbox sub-types, see http://qmail.org/man/man5/mbox.html.

What's this "envelope sender" and "envelope recipient" stuff?

The "envelope" of an email message is "message metadata"; that is, the message is information, and the envelope is information about the message (information about other information). Knowing this is critical to understanding what a domain or multidrop mailbox is, how it works, and what getmail can do for you.

Others have tried to explain this with varying degrees of success. I'll use the standard analogy of normal postal (i.e. non-electronic) mail:

Message header vs. message envelope

When you receive a letter (a reply from the customer-disservice department of your telephone company, say) it arrives in an envelope. You tear it open, remove the letter, and read it. At the top of the letter is the telephone company's return address, followed by the date the letter was written. Your name and mailing address follow that, and then the remainder of the letter.

The important thing to keep in mind is that the contents of the letter (including the addresses just discussed) are never looked at by the post office. If they can't deliver the letter (your mailing address on the envelope got smudged in the rain), they'll return it to the address listed in the top-left corner of the envelope. They don't check to make sure that the address listed there is the same as the one listed at the top of the letter. Similarly, when they can successfully deliver it, they don't check to make sure that the recipient name and address on the envelope matches the one listed on the letter between the date and the salutation.

The message header fields From: and Resent-from: are equivalent to the block of address information at the top of the letter; it usually contains the name and address of the sender of the message, but it is never actually used in the delivery of the message. Similarly, the To:, cc:, Resent-to:, and Resent-cc: header fields are the equivalent of the block of address information between the date and the salutation on the letter; they usually contain the names and addresses of the intended recipients of the message, but they too are not used in the delivery of the message.

Receiving messages without your address in the message header

You might open an envelope addressed to you and find that the letter inside makes no mention of your name. Your name and address don't appear anywhere in the letter, but it was still successfully delivered to you based on the envelope information. There's nothing strange about this. If someone else opens your mail for you, discards the envelopes, and places the contents in your in-basket, you might wonder how some of it ended up there, because there's nothing to connect you with the message contents.

Email is exactly like this. Each message has two parts, the message contents, and the message envelope. The message contents include the message header, and the message body. The message envelope is made up of exactly one envelope sender address (which can be empty) and one or more envelope recipient addresses. If the message cannot be delivered for any reason, and the envelope sender address is not empty, the message must be returned to the envelope sender address by the mail transfer agent (MTA) which last accepted responsibility for delivering the message. These notifications are known as "bounce messages" or sometimes as "non-delivery notifications". Bounce messages are sent using the empty envelope return path, to prevent mail loops from occurring when a bounce message itself cannot be delivered.

Confusion often arises among novice users about the difference between the message header and the message envelope; they seem to believe that they are not independent. This appears to be an artifact of their use of simple-minded GUI mail user agents (MUAs) that do not allow them to set the envelopes of their messages explicitly, but instead simply use the contents of the From: header field as the envelope sender address, and any addresses found in To:, cc:, and bcc: header fields as the envelope recipient addresses. While these are sensible as default values, more powerful MUAs allow the user to override this choice.

Responsibility for recording the message envelope

The last MTA to receive a message (usually the one running on the POP or IMAP server where you retrieve your mail from) essentially acts as your correspondence secretary, accepting your mail from the postman, opening it, and placing it into your in-basket. Note that this would normally destroy the important information contained in the message envelope. To prevent this loss of information, this MTA is supposed to copy the information from the envelope into new fields in the header of the message content, as if your secretrary copied the sender and recipient addresses onto the back of your letters in felt pen. Unfortunately, some MTAs do not always do this properly, and envelope information can then be lost. When this happens, it makes dealing with certain types of mail messages problematic:

  • bcc'd messages (bcc stands for blind carbon copy), where you are an envelope recipient, but your address does not appear in the message content (i.e., your address does not appear in a To:, cc:, or similar message header field). With bcc'd messages, the From: header field contains the name and address of the author of the message, and the To: and cc: header fields contain the names and addresses of the other, non-blind recipients of the message.
  • mailing list messages, where you are an envelope recipient, but your address does not appear in the message content (i.e., your address does not appear in a To:, cc:, or similar message header field). Mailing list messages have the envelope sender address set to the mailing list manager (so that it can monitor "bad" list addresses for bounces), while the From: header field contains the name and address of the author of the message. The envelope recipient addresses of mailing list messages are the addresses of the list subscribers, while the To: header field usually contains the address of the mailing list.
  • other, less common cases.

MTAs are supposed to record the envelope sender address by placing it into a new Return-Path: header field at the top of the message. They should then record the envelope recipient address(es) in another new header field; sometimes this header field is named Delivered-To:, but it can also be Envelope-To: or one of a few other names.

How this relates to domain or multidrop mailboxes

A domain or multidrop mailbox is one which receives mail for multiple email addresses (commonly all addresses in a given domain). If you do not want all of this mail to go to one person, you need to know who the messages were originally addressed to after retrieving them from the POP/IMAP multidrop mailbox. You cannot do this by looking at the To:, cc:, or other informational message header fields, because they do not actually reflect the message envelope at the time of delivery. Instead, you have to reconstruct the envelope information from the message header fields which the MTA on the server used to record it at the time of delivery.

If the final MTA does not record the message envelope (the envelope sender, and all envelope recipient addresses in the domain mailbox the message was sent to), then mail will be lost or misdirected regardless of which software you use to access the mailbox. The mailbox cannot actually be said to be a domain mailbox in this case; the defining characteristic of a domain mailbox is that it records the envelope correctly. The configuration of the MTA running on the server needs to be fixed so that the envelope is properly recorded for every message it receives.

This rc stuff seems complicated. Does it have to be?

The configuration file format is actually very simple; you don't need to worry about most of it if you're not interested in using those features. The simplest and most common getmail rc file configuration will be for users who want to retrieve all mail from a single-user POP3 mailbox, deliver those messages to a maildir or mbox file, and delete the mail from the server. For maildir, that configuration is:

[options]
delete = True

[retriever]
type = SimplePOP3Retriever
server = my-pop3-servername
username = my-pop3-username
password = my-pop3-password

[destination]
type = Maildir
path = ~/Maildir/

For an mbox file, that configuration is:

[options]
delete = True

[retriever]
type = SimplePOP3Retriever
server = my-pop3-servername
username = my-pop3-username
password = my-pop3-password

[destination]
type = Mboxrd
path = ~/inbox

How do I …

How do I retrieve mail from multiple accounts?

Create a separate getmail rc file for each account, and run getmail with multiple --rcfile options.

Of course, it's really easy to script this for a large number of rc-* files. You might create a script in $HOME/bin/run-getmail.sh containing:

#!/bin/sh
set -e
cd /path/to/my-rc-directory
rcfiles=""
for file in rc-* ; do
  rcfiles="$rcfiles --rcfile $file"
done
exec /path/to/getmail $rcfiles $@

See any beginner's tutorial on Unix shell scripting for details. Note: Since getmail 6.16

getmails
scans
$XDG_CONFIG_HOME/getmail/
for configuration files. See
man getmails
.

How do I get getmail to deliver messages to different mailboxes based on …

If you want getmail to sort messages based on who they're from, or what address appears in the To: or cc: header fields, or based on the Subject: field contents, or anything like that, pick a filtering MDA (like maildrop or procmail), and call it from a getmail MDA_external destination.

How do I stop getmail adding a Delivered-To: header to messages?

Use the delivered_to [options] parameter.

How do I stop getmail adding a Received: header to messages?

Use the received [options] parameter.

How do I make getmail deliver messages by re-injecting with SMTP?

You don't need to. getmail can deliver to maildirs, mboxrd files, or through arbitrary external MDAs.

If you still think you need to, you can use getmail's external MDA support to do so.

How do I create a maildir?

Use the maildirmake command, if you have it installed. Otherwise, run the following command from your shell:

$ mkdir -p /path/to/Maildir/{cur,new,tmp}

Some other maildir-aware programs ship with their own maildir-creation programs; you can use those, or make the above shell command a shellscript or alias if you like.

How do I create an mboxrd file?

Create a completely empty (i.e. zero bytes long) file via your favourite method. The standard utility touch is commonly used:

$ touch /path/to/mboxrd

How do I make getmail deliver messages to an mh folder?

mh clients (and nmh, or "new mh" clients) include a command for delivering a message into your mh folder. In nmh, this command is called rcvstore. You use it as an external message delivery agent (MDA) with getmail's MDA_external destination. Ensure your $HOME/.mh_profile file is configured properly; getmail user Frankye Fattarelli suggests a line like the following is necessary to indicate the path to your mh mail root:

Path: Mail

Then use MDA_external like this (which, after adjusting the path of the command to reflect your mh/nmh installation, should work with either mh or nmh):

[destination]
type = MDA_external
path = /usr/local/libexec/nmh/rcvstore
arguments = ("+inbox", )

Thanks to Frankye Fattarelli for contributing this answer.

How do I run getmail in "daemon" mode?

getmail does not have, and does not need, any special "daemon mode". You just run getmail under whatever process-supervision or periodic-job system you already have on your system.

For example, if you use daemontools/svscan/supervise, you can configure a getmail "service" using a simple run script like:

#!/bin/sh

/path/to/getmail [options]
sleep 1800

That example would run getmail continuously, sleeping for 30 minutes between runs. You can probably work out similar scripts for other process-supervision systems.

If you don't have such a system, you can use your system's cron utility to run getmail periodically, but you absolutely have to prevent multiple copies of getmail from being run by cron simultaneously. Most versions of cron have no protection for this built-in, so you have to use setlock or flock or a similar utility to prevent it. For more details, see How do I stop multiple instances of getmail from running at the same time? below. If you do not prevent multiple copies of getmail running against the same server (and IMAP folder) simultaneously, you will get odd behaviour, including retrieving the same messages multiple times.

How do I make getmail stop after retrieving X messages so that the server actually flushes deleted messages?

Use the max_messages_per_session option to limit the number of messages getmail will process in a single session. Some users with flaky servers use this option to reduce the chances of seeing messages more than once if the server dies in mid-session.

How do I make getmail retrieve mail from Hotmail?

Well, you could write a retriever that speaks Hotmail's proprietary, undocumented, and unsupported access protocol, or simply set up the POP3 proxy from the httpmail package, and have getmail retrieve mail from that POP3 proxy.

I'm using getmail. How do I make it …

These are supplementary questions I occasionally see about doing various things to enhance a getmail setup. The solution to many of them is to use a standard Unix technique of some sort to make the system behave in a certain manner, or otherwise change the behaviour of something that's actually outside of getmail proper.

I'm running getmail from cron. How do I temporarily stop it?

Some people ask about temporarily stopping getmail from running from a cron job, possibly because the mail server is down and they don't want to see the warnings cron mails them.

The easiest method is to comment out getmail from your crontab file:

  1. Run
    $ crontab -e
    to edit your crontab file.
  2. Place a # (pound) character at the start of the line containing the call to getmail.
  3. Save the changed file.

When you want to re-enable getmail, edit the file again and un-do the above change.

If you need to do this on a regular basis, you can instead use a "flag file" to tell the system whether or not to run getmail:

Change your cron job or shellscript that normally launches getmail to check for the presence of a certain file first, and have it not run getmail if that file is present. For example, your crontab entry could be changed to do this:

    [ -f ~/.getmail/do-not-run ] || /path/to/getmail

When you don't want getmail to run, touch that file:

    $ touch ~/.getmail/do-not-run

When you want getmail to run again, delete it:

    $ rm -f ~/.getmail/do-not-run

This is even safe for scripting, as creating and removing the file are atomic operations under Unix.

How do I stop multiple instances of getmail from running at the same time?

getmail has no problems running multiple instances in parallel, though you shouldn't attempt to use the same getmail rc file from two different instances at the same time; it will probably cause getmail to deliver duplicate copies of messages, "forget" that it has seen particular messages before, and other similar problems.

In particular, if you're running getmail from a crontab, you must do something to prevent cron from starting getmail if the previous invocation is still running.

If you need to prevent two instances of getmail from running simultaneously, use any standard Unix method of providing a mutex for this purpose. One example would be to run getmail under a program like setlock (part of the daemontools package). Change your script or crontab file to invoke getmail like this:

/path/to/setlock -n /path/to/lockfile /path/to/getmail [getmail options]

There are other programs that provide functionality similar to setlock.

Using getmail with other software

getmail user Frankye Fattarelli contributed to the following questions about integrating getmail with SpamAssassin and ClamAV.

How do I use SpamAssassin with getmail?

SpamAssassin can be run in standalone mode or in a client/server configuration. In both configurations, SpamAssassin accepts a wide variety of arguments; please refer to SpamAssassin's manual pages or online documentation for details.

To filter messages through SpamAssassin in a client/server configuration (i.e. with the spamd daemon), use a configuration like this:

[filter]
type = Filter_external
path = /usr/local/bin/spamc
arguments = ("-s", "10000")

The value supplied to the -s option is the maximum message size accepted (in bytes). The default is 250k.

A similar configuration without the spamd daemon would be:

[filter]
type = Filter_external
path = /usr/local/bin/spamassassin
arguments = ("--report", )

The --report option sends the message to the various spam-blocker databases and tags it as spam in your bayesian database.

Note that if you are using Bayesian (learning) filtering, and you've put your SpamAssassin filter after any getmail Filter_classifier, you may have a problem with your learning filter learning getmail's header fields. That is, the headers added by the other filters may get learned, and affect your database. To prevent this, ensure that SpamAssassin ignores these fields by adding the following to your SpamAssassin configuration:

bayes_ignore_header X-getmail-filter-classifier

How do I use ClamAV with getmail?

You should also read this message in the getmail users' mailing list archives and the ClamAV documentation if you want to use ClamAV with getmail.

ClamAV, like SpamAssassin, can by used in standalone or client/server configurations. In either case, you need to add the StreamSaveToDisk option to your clamav.conf file to enable scanning from stdin.

To use ClamAV without the clamd daemon, use a filter configuration like this:

[filter]
type = Filter_classifier
path = /usr/local/bin/clamscan
arguments = ("--stdout", "--no-summary",
    "--mbox", "--infected", "-")
exitcodes_drop = (1,)

The above assumes you do not want the infected emails to be delivered. If you do want them delivered, you would use a slightly different configuration:

[filter]
type = Filter_classifier
path = /usr/local/bin/clamscan
arguments = ("--stdout", "--no-summary",
    "--mbox", "--infected", "-")
exitcodes_keep = (0,1)

To use ClamAV with the clamd daemon, use a filter configuration like this:

[filter]
type = Filter_classifier
path = /usr/local/bin/clamdscan
arguments = ("--stdout", "--disable-summary", "-")
exitcodes_drop = (1, )

As with Clamscan (above), if you do want the infected messages delivered instead of dropped, you should modify your configuration as follows:

[filter]
type = Filter_classifier
path = /usr/local/bin/clamdscan
arguments = ("--stdout", "--disable-summary", "-")
exitcodes_keep = (0,1)

You may find it necessary to specify the paths of some decompression utilities used by ClamAV with additional arguments like:

arguments = ( …,
    "--unzip=/usr/local/bin/unzip",
    "--unrar=/usr/local/bin/unrar",
    "--unarj=/usr/local/bin/unarj",
    "--lha=/usr/local/bin/lha",
    "--jar=/usr/local/bin/unzip",
    "--tar=/usr/bin/tar",
    "--tgz=/usr/bin/tar"

Note: if you want to use the daemonized (client/server) version of ClamAV, ensure that your clamav.conf file contains:

ScanMail

The paths to the various decompression utilities must be specified in this file as well.

See the following mailing list message from Frankye Fattarelli for additional notes on using ClamAV with getmail: https://marc.info/?l=getmail&m=109128345509273&w=2

Getting prettier output from ClamAV

Using getmail's Filter_classifier, the output of your filtering program (in this case ClamAV) is placed into a X-getmail-filter-classifier: header field in the message. This can make auditing the actions of filters difficult if you use multiple filters and cannot tell which filter added which line.

To correct this, you can use an additional filter to change the name of the added filter header lines immediately after each filter is run. For example, reformail, from the maildrop package (which is in turn part of the Courier MTA ) can be used in this fashion to rename the added header fields (say, to "X-mypersonalmailscan") with a filter configuration like this:

type = Filter_external
path = /usr/local/bin/reformail
arguments = ("-R", "X-getmail-filter-classifier:",
    "X-mypersonalmailscan:")

Simply ensure ClamAV is invoked as the first filter, and this is invoked as the second filter (or immediately after the ClamAV filter, if it is the second, third, etc. filter).

How do I use F-Prot with getmail?

getmail user Kai Raven reports that getmail and F-Prot work fine together with the following getmailrc filter configuration:

[filter]
type = Filter_external
path = /usr/local/bin/f-prot-wrapper.sh

The wrapper script f-prot-wrapper.sh is a small shellscript by Ali Onur Cinar, and can be downloaded from his website.

How do I use procmail with getmail?

Simply invoke procmail as an external MDA. procmail requires that one of the following be true:

  • that the message begin with a Unix "From " line (the mbox message delimiter)
  • that procmail is invoked with the -f option supplying the envelope sender, so that it may generate the "From " line

To have getmail generate and prepend the "From " line to the start of the message, set the MDA_external parameter unixfrom to True:

[destination]
type = MDA_external
path = /path/to/procmail
unixfrom = True

To supply the -f option to procmail, do something like this:

[destination]
type = MDA_external
path = /path/to/procmail
arguments = ("-f", "%(sender)")

How do I use maildrop with getmail?

Simply invoke maildrop as an external MDA. maildrop requires that the message begin with a Unix "From " line (the mbox message delimiter), so you'll need to either set the MDA_external parameter unixfrom to True, or supply arguments that tell maildrop to recreate this line. One of the following would be fine:

[destination]
type = MDA_external
path = /path/to/maildrop
arguments = ("-f", "%(sender)")

Or:

[destination]
type = MDA_external
path = /path/to/maildrop
unixfrom = True

If you want to specify a maildrop rc file as one of its arguments, that would be something like:

[destination]
type = MDA_external
path = /path/to/maildrop
arguments = ("-f", "%(sender)", "~/.maildroprc")

How do I use TMDA with getmail?

Simply use the Filter_TMDA module as a message filter:

[filter-X]
type = Filter_TMDA

See the documentation for details on optional parameters to the Filter_TMDA module.

How can I get Gmail labels with getmail?

As of getmail version 4.34.0, getmail retrieves the labels and other metadata that Gmail makes available via an IMAP extension, and records that information in the message headers X-GMAIL-LABELS:, X-GMAIL-THRID:, and X-GMAIL-MSGID:.

I think I found this bug in getmail …

There are frequent reports like the following, which aren't bugs in getmail. Please read them before reporting them as bugs.

getmail doesn't download all my mail from Gmail …

There's a couple of different problems here. One is that Google's Gmail service violates the POP3 protocol by removing messages from the POP3 view of the mailbox without the user issuing a DELE command. They do this as soon as an RETR command is given, so if getmail tries to download a message and it fails for any reason (delivery fails due to a full disk, or the Gmail server fails to respond, or the network connection dies before the transfer is complete, or the Gmail server fails to respond to the QUIT command, or …), the next time getmail connects to that Gmail account, Gmail will have "helpfully" deleted the message from the POP3 mailbox, even though getmail never issued a DELE command. So Gmail silently destroys mail, from a POP3 perspective. There's nothing getmail can do about this.

Note this feature of Gmail is not well-publicized. The only mention I can find of it is here: http://mail.google.com/support/bin/answer.py?answer=13291&topic=1555

The other issue here is that Google doesn't include mail from your trash or spam folders in the POP3 view, so getmail can't see those messages either. That's generally less of an issue, provided their spam filters never give false positive results (ha!).

operation error (SimplePOP3Retriever: [...] does not uniquely identify messages [...] see documentation or use BrokenUIDLPOP3Retriever instead

The server you're trying to use does not properly uniquely identify messages (getmail noticed when it saw the same "unique" identifier twice in the same mailbox at the same time). getmail needs these identifiers to be unique so that it can properly tell the difference between new and old messages.

If you see this error message, and you've configured getmail to retrieve and immediately delete all messages, just switch to using the BrokenUIDLPOP3Retriever class (or its SSL variant) -- it'll work fine.

If you see this error message, and you're trying to leave messages on the server after retrieval (permanently, or for a few days with delete_after), you have a few options to try to resolve it:

  • If your provider also offers IMAP access to your mailbox, try one of the IMAP retrievers instead.
  • Change your configuration so you're not leaving messages on the server, and use BrokenUIDLPOP3Retriever instead.
  • Talk to your mail hosting provider, and see if they can fix their POP3 software so that it doesn't have this problem any more.

MemoryError on OS X

If you see errors like this while running getmail on Macintosh OS X:

python2.5(27172) malloc: *** vm_allocate(size=15699968) failed (error code=3)
python2.5(27172) malloc: *** error: can't allocate region
python2.5(27172) malloc: *** set a breakpoint in szone_error to debug
[...]

... which then end with MemoryError, please report the problem to Apple. The OS X implementation of realloc() is broken, and there's nothing getmail can do about it.

Errors connecting to Gmail with OpenSSL 1.1.1

If you experience connection/SSL errors connecting to Gmail servers, and your OpenSSL is version 1.1.1 or higher, the problem is that Gmail is failing the connection on the basis that SNI is not in use. To work around the problem, upgrade to getmail v.5.10 or later, or tell getmail to use TLSv1.2 rather than TLS1.3 in your retriever configuration and specify TLS v1.2 as the protocol to use:

[retriever]
...
ssl_version = tlsv1_2
getmail6-6.19.07/docs/faq.txt000066400000000000000000001412751474536475600157050ustar00rootroot00000000000000 getmail Frequently Asked Questions (FAQ) getmail6 is Copyright © 1998-2025 by Charles Cazabon and others: getmail is licensed under the GNU General Public License version 2 (only). Table of Contents * getmail documentation * * getmail documentation * * Features * Requirements * Obtaining getmail * Installing getmail * getmail configuration (version 5) * * Configuring getmail * * Creating a getmail rc file * Running getmail * * Commandline options * Using getmail as an MDA * Using getmail_fetch to retrieve mail from scripts * getmail troubleshooting * * Troubleshooting problems * * Error messages * Warning messages * Unexpected Behaviour * getmail frequently-asked questions (FAQs) * * Frequently-Asked Questions (FAQs) * * About getmail * * What is getmail? * What is getmail6 and how does it relate to getmail? * What platforms/machines does getmail run on? * * Does getmail run on MS Windows? * Does getmail run on Macintosh systems? * Does getmail require Unix/Linux? * How can I get support for getmail? * I think I found a bug! How do I report it? * I have a neat idea for random feature "foo" … how do I get you to implement it? * Why won't you implement random feature "foo"? * Does getmail support virus scanning of retrieved messages? * Does getmail support spam filtering of retrieved messages? * Does getmail support SSL? * Does getmail rewrite mail headers when it retrieves mail? * What are these oldmail* files? Can I delete or trim them? * Can I upgrade from getmail 3 to getmail 4/5? What about my "oldmail" files? * Why did you write getmail? Why not just use fetchmail? * Configuring getmail * * What is a "domain mailbox"? * Do I have to run sendmail or another MTA to use getmail? * Will getmail deliver mail as root? * What's a maildir? * What's "mboxrd" format? * What's this "envelope sender" and "envelope recipient" stuff? * * Message header vs. message envelope * Receiving messages without your address in the message header * Responsibility for recording the message envelope * How this relates to domain or multidrop mailboxes * This rc stuff seems complicated. Does it have to be? * How do I … * * How do I retrieve mail from multiple accounts? * How do I get getmail to deliver messages to different mailboxes based on … * How do I stop getmail adding a Delivered-To: header to messages? * How do I stop getmail adding a Received: header to messages? * How do I make getmail deliver messages by re-injecting with SMTP? * How do I create a maildir? * How do I create an mboxrd file? * How do I make getmail deliver messages to an mh folder? * How do I run getmail in "daemon" mode? * How do I make getmail stop after retrieving X messages so that the server actually flushes deleted messages? * How do I make getmail retrieve mail from Hotmail? * I'm using getmail. How do I make it … * * I'm running getmail from cron. How do I temporarily stop it? * How do I stop multiple instances of getmail from running at the same time? * Using getmail with other software * * How do I use SpamAssassin with getmail? * How do I use ClamAV with getmail? * * Getting prettier output from ClamAV * How do I use F-Prot with getmail? * How do I use procmail with getmail? * How do I use maildrop with getmail? * How do I use TMDA with getmail? * * How can I get Gmail labels with getmail? * I think I found this bug in getmail … * * getmail doesn't download all my mail from Gmail … * FutureWarning: %u/%o/%x/%X of negative int will return a signed string in Python 2.4 and up * AttributeError: 'module' object has no attribute 'fsync' * operation error (SimplePOP3Retriever: [...] does not uniquely identify messages [...] see documentation or use BrokenUIDLPOP3Retriever instead * MemoryError on OS X * MemoryError when using IMAP * Errors connecting to Gmail with OpenSSL 1.1.1 Frequently-Asked Questions (FAQs) The following questions about getmail have been asked more-or-less frequently. Please also read the unexpected behaviour section of the troubleshooting document. About getmail What is getmail? getmail is a mail retriever with support for POP3, POP3-over-SSL, IMAP4, IMAP4-over-SSL, and SDPS mail accounts. It supports normal single-user mail accounts and multidrop (domain) mailboxes. getmail is written in Python, and licensed under the GNU General Public License version 2. What is getmail6 and how does it relate to getmail? getmail is the name of this software and project as it was originally developed by Charles Cazabon. At the time of getmail version 5.14 getmail would not work with Python version 3 and newer. As support for Python 3 was not available for getmail at the time while many distributions were dropping support for all Python versions older than version 3, this fork was created. To avoid confusion and conflicts with Charles' project, we decided to call our fork getmail6 and start our version numbering from 6. As we intend getmail6 to be a drop-in replacement for getmail, names of files etc. have been kept the same as they are in getmail 5.14 and earlier. What platforms/machines does getmail run on? getmail runs on basically any platform. It's designed to, and written in a language that helps to maintain cross-platform compatibility. getmail is known to run on the following platforms: * Linux-based GNU systems (all distributions) * HURD-based GNU systems * FreeBSD * OpenBSD * NetBSD * HP/UX * Sun Solaris * IBM AIX * Digital/Compaq Tru64 (a.k.a OSF/1) UNIX * SGI Irix * other commercial Unices * Digital VMS / OpenVMS * BeOS * Amiga OS * OS/2 * Cygwin on Windows * Macintosh OS X * Macintosh OS 9 But getmail will also run on other, less common platforms. The only real requirement is that Python run on that platform, and porting Python is generally very easy. Does getmail run on MS Windows? Yes, under the free Cygwin package. Running recent versions of Python under Cygwin requires a process known as "rebasing" your Cygwin installation; you can find details in this Python developers' mailing list message. Does getmail run on Macintosh systems? Yes. Does getmail require Unix/Linux? No. How can I get support for getmail? getmail is Free Software. As such, it comes with no warranty. However, we will do our best to support getmail on a voluntary basis through our GitHub repository. If you have questions about getmail, the first step is to read the documentation, and the remainder of the Frequently Asked Questions. If your question isn't answered there, please open an issue on GitHub. If you post your question there, we will see it. I think I found a bug! How do I report it? First, make sure that you are running the latest version. You can always find what is the latest version by checking this page at the original web site: http://getmail6.org/. If you are running an older version of the software, chances are whatever bug you may have found has already been fixed. After this, please check our repository on GitHub to see if this issue has already been reported. If not, feel free to open an issue to report your bug. You should include at least the following information: * getmail version * Python version * any error message which getmail displayed * the output from running getmail with your normal options plus --dump * if your problem is getmail not determining the proper local recipient, please include the output of running getmail with your normal options plus --trace, showing the retrieval of one problematic message. If your bugreport contains confidential information, please exclude this from your report. I have a neat idea for random feature "foo" … how do I get you to implement it? Follow the same instructions as for reporting bugs above — yes, that means we would prefer you submit your idea as an issue in our repository allowing other users to also comment on it which may lead to a useful discussion if your feature has not been proposed before. Why won't you implement random feature "foo"? Every line of code added to getmail has a certain cost. Every feature added requires code, documentation, and support. Adding features increases the complexity of the software, confuses users, and leads to higher support costs. We therefore weigh features very carefully as a cost-versus-benefit tradeoff before deciding whether to add them. Some users are confused by this. They think that a feature you don't use has no cost, and therefore if it has any value to anyone, it should be added. That simply isn't the case; the costs of an unused feature are simply borne by others, including us. If you have asked me to add some feature, and we've said no, this may be the reason. Other possibilities include us simply not having had sufficient time to implement it yet. Does getmail support virus scanning of retrieved messages? Yes. You can use getmail message filtering options to do this with an external virus scanning program, or invoke your virus scanning program during delivery with getmail's support for external MDAs. Also see the FAQ about using getmail with the ClamAV program. Does getmail support spam filtering of retrieved messages? Yes. You can use getmail message filtering options to do this with an external spam filtering program, or invoke your spam filtering program during delivery with getmail's support for external MDAs. Also see the FAQ about using getmail with the SpamAssassin program. Does getmail support SSL? Yes. getmail has built in support for POP3-over-SSL and IMAP4-over-SSL. Does getmail rewrite mail headers when it retrieves mail? No. Rewriting message header fields is bad for many reasons; the biggest problem is that it causes a loss of critical technical information necessary to track down many mail problems. getmail will add a new Received: header field and a new Delivered-To: header field, but does not rewrite existing headers. You can disable the creation of these header fields. What are these oldmail* files? Can I delete or trim them? getmail stores "\0" of messages it has seen in your POP/IMAP account in the oldmail files. Do NOT delete or edit these files. You'll make getmail re-retrieve all your old mail, or even prevent getmail from running. The files are tiny by modern storage standards; you could have a million of these files and still not have to worry about the disk space they take up for a thousand years. Why did you write getmail? Why not just use fetchmail? The below text is by Charles Cazabon, getmail's original author: Short answer: … well, the short answer is mostly unprintable. The long answer is … well, long: I do not like some of the design choices which were made with fetchmail. getmail does things a little differently, and for my purposes, better. In addition, most people find getmail easier to configure and use than fetchmail. Perhaps most importantly, getmail goes to great lengths to ensure that mail is never lost, while fetchmail (in its default configuration) frequently loses mail, causes mail loops, bounces legitimate messages, and causes many other problems. When people have pointed out problems in fetchmail's design and implementation, it's maintainer has frequently ignored them, or (worse yet) gone in the completely wrong direction in the name of "fixing" the problems. For instance, fetchmail's configuration file syntax has been criticized as being needlessly difficult to write; instead of cleaning up the syntax, the maintainer instead included a GUI configuration-file-writing program, leading to comments like: The punchline is that fetchmail sucks, even if it does have giddily-engineered whizbang configurator apps. As an example, Dan Bernstein, author of qmail and other software packages, once noted to the qmail list: Last night, root@xxxxxxxxxxxxxxxxx reinjected thirty old messages from various authors to qmail@xxxxxxxxxxxxxx This sort of idiocy happens much more often than most subscribers know, thanks to a broken piece of software by Eric Raymond called fetchmail. Fortunately, qmail and ezmlm have loop-prevention mechanisms that stop these messages before they are distributed to subscribers. The messages end up bouncing to the wrong place, thanks to another fetchmail bug, but at least the mailing list is protected. --D. J. Bernstein The maintainer also ignored dozens of complaints about fetchmail's behaviour, stating (by fiat) that fetchmail was bug-free and had entered "maintenance mode", allowing him to ignore further bug reports. fetchmail's default configuration values frequently cause lost or misdirected mail, and seem to be chosen to cause maximum pain and inconvenience. From fetchmail's to-do file (emphasis mine): Maybe refuse multidrop configuration unless "envelope" is _explicitly_ configured … This would prevent a significant class of shoot-self-in-foot problems. perhaps treat a delivery as "temporarily failed" … This is so you don't lose mail if you configure the wrong envelope header. fetchmail is famous for mangling messages it retrieves, rather than leaving them alone as a mail-handling program should. getmail will add trace information to messages (so you can see what happened, and when), but will otherwise leave message content alone. In addition, fetchmail has a long history of security problems: * versions released before 20 June 2001 contain a buffer overflow, which can be remotely exploited (see www.securityfocus.com/bid/2877 for details). getmail is not vulnerable to buffer overflows, because buffers in Python are dynamically sized. * Another remotely-exploitable security hole discovered in fetchmail in June 2002; versions prior to 5.9.10 (released in June 2002) are exploitable . * Reading fetchmail's UPDATES file, it appears that another security problem was fixed in 5.9.12, where a server could crash fetchmail on 64-bit platforms. Also worrying is a mention that it includes a fix for "password shrouding". * Another remotely-exploitable security hole in fetchmail discovered in September 2002; this hole lets an attacker run arbitrary code on the victim's computer. * Another remotely-exploitable security hole in fetchmail discovered in December 2002; once again, a remote attacker can run arbitrary code on the machine running fetchmail in its default configuration. See this advisory for details. * January 2003: More buffer overflows in fetchmail let attackers run arbitrary code . * October 2003: Anyone can cause fetchmail to crash by sending you a message . Other problems are here , and I might have missed some . * Just in case you thought fetchmail was all better now, there's still new security problems being discovered in it. In December, 2005, it was revealed that anyone can send a fetchmail multidrop user a message that causes fetchmail to crash. In July, 2004, it was noted that there may be at least 2 unfixed denial-of-service attacks, 2 unfixed remote-code-execution, 2 unfixed remote-user-access, and 3 unfixed remote-shell attacks against fetchmail. See http://www.mail-archive.com/euglug@euglug.org/msg00971.html for details I've given up even trying to stay abreast of the various security holes in fetchmail, but others have noted continuing problems, including: * another arbitrary code execution vulnerability announced on 21 July 2005. The fetchmail authors' boneheaded decision to create a configuration-file GUI editor (rather than actually giving fetchmail a sane configuration syntax) also came back to bite them in the ass: in October 2005, it became known that fetchmailconf created its files in such a way that users' passwords could be read during file creation. Addendum, January 2007: since I wrote the above, the following new security problems have been discovered in fetchmail: * CVE-2005-4348 - anyone can crash fetchmail by sending messages without headers * CVE-2006-0321 - anyone can crash fetchmail by sending a message that fetchmail tries to bounce * CVE-2006-5867 - fetchmail can transmit passwords in plaintext even if the user has configured it not to * CVE-2006-5974 - anyone can cause fetchmail to crash by triggering certain error code paths But don't just take my word for it; see http://docs.freebsd.org/cgi/mid.cgi?200102172349.QAA11724 and http://esr.1accesshost.com/ (note: went offline sometime in 2009 or 2010; the content is still available at http://web.archive.org/web/20080621090439/http://esr.1accesshost.com/ ). getmail users have not had to worry about any of these security holes or design and implementation errors. Configuring getmail What is a "domain mailbox"? A domain (or multidrop) mailbox is a POP3 mailbox which receives mail for all users in a given domain. Normal mailboxes contain mail for a single user (like jason@myisp.co.uk); some Internet Service Providers which provide webhosting or other services will provide a POP3 mailbox which receives mail for all addresses in a given domain (i.e. mail for service@smallcompany.net, sales@smallcompany.net, and indeed anything @smallcompany.net ends up in the same POP3 mailbox). getmail provides a method of retrieving mail from a domain mailbox and distributing it among the various users automatically. The retriever classes MultidropPOP3Retriever, MultidropPOP3SSLRetriever, MultidropSDPSRetriever, MultidropIMAPRetriever, and MultidropIMAPSSLRetriever provide this capability. See the documentation on the [retriever] section for details of what the requirements for a multidrop mailbox are. getmail user Matthias Andree also has a web page about multidrop mailboxes. Do I have to run sendmail or another MTA to use getmail? No. getmail delivers directly to maildirs, mboxrd files, or via arbitrary MDAs, and never injects mail via SMTP, so no MTA is necessary. Will getmail deliver mail as root? No. When run as the root user on a Unix-like system, getmail drops privileges (switches to an unprivileged group and user id) before delivering to maildirs or mboxrd files. You can specify the user explicitly, or let getmail use the owner of the maildir or mboxrd file. If getmail attempts to deliver mail and finds it has UID 0 or GID 0, it will refuse the delivery and print an error message. What's a maildir? A maildir is a mail storage format invented by D. J. Bernstein (author of qmail) that requires no file locking to deliver to safely and reliably, even over NFS. getmail natively supports delivery to maildirs. See http://qmail.org/man/man5/maildir.html and http://cr.yp.to/proto/maildir.html for details. What's "mboxrd" format? There are various sub-types of the mbox mail storage format. mboxrd is the most reliable of them, though (like all mbox types) it still relies on file locking and is therefore more easily corrupted than maildir format. In particular, using mbox files with multiple writers over NFS can be problematic. For details on the differences between the various mbox sub-types, see http://qmail.org/man/man5/mbox.html. What's this "envelope sender" and "envelope recipient" stuff? The "envelope" of an email message is "message metadata"; that is, the message is information, and the envelope is information about the message (information about other information). Knowing this is critical to understanding what a domain or multidrop mailbox is, how it works, and what getmail can do for you. Others have tried to explain this with varying degrees of success. I'll use the standard analogy of normal postal (i.e. non-electronic) mail: Message header vs. message envelope When you receive a letter (a reply from the customer-disservice department of your telephone company, say) it arrives in an envelope. You tear it open, remove the letter, and read it. At the top of the letter is the telephone company's return address, followed by the date the letter was written. Your name and mailing address follow that, and then the remainder of the letter. The important thing to keep in mind is that the contents of the letter (including the addresses just discussed) are never looked at by the post office. If they can't deliver the letter (your mailing address on the envelope got smudged in the rain), they'll return it to the address listed in the top-left corner of the envelope. They don't check to make sure that the address listed there is the same as the one listed at the top of the letter. Similarly, when they can successfully deliver it, they don't check to make sure that the recipient name and address on the envelope matches the one listed on the letter between the date and the salutation. The message header fields From: and Resent-from: are equivalent to the block of address information at the top of the letter; it usually contains the name and address of the sender of the message, but it is never actually used in the delivery of the message. Similarly, the To:, cc:, Resent-to:, and Resent-cc: header fields are the equivalent of the block of address information between the date and the salutation on the letter; they usually contain the names and addresses of the intended recipients of the message, but they too are not used in the delivery of the message. Receiving messages without your address in the message header You might open an envelope addressed to you and find that the letter inside makes no mention of your name. Your name and address don't appear anywhere in the letter, but it was still successfully delivered to you based on the envelope information. There's nothing strange about this. If someone else opens your mail for you, discards the envelopes, and places the contents in your in-basket, you might wonder how some of it ended up there, because there's nothing to connect you with the message contents. Email is exactly like this. Each message has two parts, the message contents, and the message envelope. The message contents include the message header, and the message body. The message envelope is made up of exactly one envelope sender address (which can be empty) and one or more envelope recipient addresses. If the message cannot be delivered for any reason, and the envelope sender address is not empty, the message must be returned to the envelope sender address by the mail transfer agent (MTA) which last accepted responsibility for delivering the message. These notifications are known as "bounce messages" or sometimes as "non-delivery notifications". Bounce messages are sent using the empty envelope return path, to prevent mail loops from occurring when a bounce message itself cannot be delivered. Confusion often arises among novice users about the difference between the message header and the message envelope; they seem to believe that they are not independent. This appears to be an artifact of their use of simple-minded GUI mail user agents (MUAs) that do not allow them to set the envelopes of their messages explicitly, but instead simply use the contents of the From: header field as the envelope sender address, and any addresses found in To:, cc:, and bcc: header fields as the envelope recipient addresses. While these are sensible as default values, more powerful MUAs allow the user to override this choice. Responsibility for recording the message envelope The last MTA to receive a message (usually the one running on the POP or IMAP server where you retrieve your mail from) essentially acts as your correspondence secretary, accepting your mail from the postman, opening it, and placing it into your in-basket. Note that this would normally destroy the important information contained in the message envelope. To prevent this loss of information, this MTA is supposed to copy the information from the envelope into new fields in the header of the message content, as if your secretrary copied the sender and recipient addresses onto the back of your letters in felt pen. Unfortunately, some MTAs do not always do this properly, and envelope information can then be lost. When this happens, it makes dealing with certain types of mail messages problematic: * bcc'd messages (bcc stands for blind carbon copy), where you are an envelope recipient, but your address does not appear in the message content (i.e., your address does not appear in a To:, cc:, or similar message header field). With bcc'd messages, the From: header field contains the name and address of the author of the message, and the To: and cc: header fields contain the names and addresses of the other, non-blind recipients of the message. * mailing list messages, where you are an envelope recipient, but your address does not appear in the message content (i.e., your address does not appear in a To:, cc:, or similar message header field). Mailing list messages have the envelope sender address set to the mailing list manager (so that it can monitor "bad" list addresses for bounces), while the From: header field contains the name and address of the author of the message. The envelope recipient addresses of mailing list messages are the addresses of the list subscribers, while the To: header field usually contains the address of the mailing list. * other, less common cases. MTAs are supposed to record the envelope sender address by placing it into a new Return-Path: header field at the top of the message. They should then record the envelope recipient address(es) in another new header field; sometimes this header field is named Delivered-To:, but it can also be Envelope-To: or one of a few other names. How this relates to domain or multidrop mailboxes A domain or multidrop mailbox is one which receives mail for multiple email addresses (commonly all addresses in a given domain). If you do not want all of this mail to go to one person, you need to know who the messages were originally addressed to after retrieving them from the POP/IMAP multidrop mailbox. You cannot do this by looking at the To:, cc:, or other informational message header fields, because they do not actually reflect the message envelope at the time of delivery. Instead, you have to reconstruct the envelope information from the message header fields which the MTA on the server used to record it at the time of delivery. If the final MTA does not record the message envelope (the envelope sender, and all envelope recipient addresses in the domain mailbox the message was sent to), then mail will be lost or misdirected regardless of which software you use to access the mailbox. The mailbox cannot actually be said to be a domain mailbox in this case; the defining characteristic of a domain mailbox is that it records the envelope correctly. The configuration of the MTA running on the server needs to be fixed so that the envelope is properly recorded for every message it receives. This rc stuff seems complicated. Does it have to be? The configuration file format is actually very simple; you don't need to worry about most of it if you're not interested in using those features. The simplest and most common getmail rc file configuration will be for users who want to retrieve all mail from a single-user POP3 mailbox, deliver those messages to a maildir or mbox file, and delete the mail from the server. For maildir, that configuration is: [options] delete = True [retriever] type = SimplePOP3Retriever server = my-pop3-servername username = my-pop3-username password = my-pop3-password [destination] type = Maildir path = ~/Maildir/ For an mbox file, that configuration is: [options] delete = True [retriever] type = SimplePOP3Retriever server = my-pop3-servername username = my-pop3-username password = my-pop3-password [destination] type = Mboxrd path = ~/inbox How do I … How do I retrieve mail from multiple accounts? Create a separate getmail rc file for each account, and run getmail with multiple --rcfile options. Of course, it's really easy to script this for a large number of rc-* files. You might create a script in $HOME/bin/run-getmail.sh containing: #!/bin/sh set -e cd /path/to/my-rc-directory rcfiles="" for file in rc-* ; do rcfiles="$rcfiles --rcfile $file" done exec /path/to/getmail $rcfiles $@ See any beginner's tutorial on Unix shell scripting for details. Note: Since getmail 6.16 getmails scans $XDG_CONFIG_HOME/getmail/ for configuration files. See man getmails . How do I get getmail to deliver messages to different mailboxes based on … If you want getmail to sort messages based on who they're from, or what address appears in the To: or cc: header fields, or based on the Subject: field contents, or anything like that, pick a filtering MDA (like maildrop or procmail), and call it from a getmail MDA_external destination. How do I stop getmail adding a Delivered-To: header to messages? Use the delivered_to [options] parameter. How do I stop getmail adding a Received: header to messages? Use the received [options] parameter. How do I make getmail deliver messages by re-injecting with SMTP? You don't need to. getmail can deliver to maildirs, mboxrd files, or through arbitrary external MDAs. If you still think you need to, you can use getmail's external MDA support to do so. How do I create a maildir? Use the maildirmake command, if you have it installed. Otherwise, run the following command from your shell: $ mkdir -p /path/to/Maildir/{cur,new,tmp} Some other maildir-aware programs ship with their own maildir-creation programs; you can use those, or make the above shell command a shellscript or alias if you like. How do I create an mboxrd file? Create a completely empty (i.e. zero bytes long) file via your favourite method. The standard utility touch is commonly used: $ touch /path/to/mboxrd How do I make getmail deliver messages to an mh folder? mh clients (and nmh, or "new mh" clients) include a command for delivering a message into your mh folder. In nmh, this command is called rcvstore. You use it as an external message delivery agent (MDA) with getmail's MDA_external destination. Ensure your $HOME/.mh_profile file is configured properly; getmail user Frankye Fattarelli suggests a line like the following is necessary to indicate the path to your mh mail root: Path: Mail Then use MDA_external like this (which, after adjusting the path of the command to reflect your mh/nmh installation, should work with either mh or nmh): [destination] type = MDA_external path = /usr/local/libexec/nmh/rcvstore arguments = ("+inbox", ) Thanks to Frankye Fattarelli for contributing this answer. How do I run getmail in "daemon" mode? getmail does not have, and does not need, any special "daemon mode". You just run getmail under whatever process-supervision or periodic-job system you already have on your system. For example, if you use daemontools/svscan/supervise, you can configure a getmail "service" using a simple run script like: #!/bin/sh /path/to/getmail [options] sleep 1800 That example would run getmail continuously, sleeping for 30 minutes between runs. You can probably work out similar scripts for other process-supervision systems. If you don't have such a system, you can use your system's cron utility to run getmail periodically, but you absolutely have to prevent multiple copies of getmail from being run by cron simultaneously. Most versions of cron have no protection for this built-in, so you have to use setlock or flock or a similar utility to prevent it. For more details, see How do I stop multiple instances of getmail from running at the same time? below. If you do not prevent multiple copies of getmail running against the same server (and IMAP folder) simultaneously, you will get odd behaviour, including retrieving the same messages multiple times. How do I make getmail stop after retrieving X messages so that the server actually flushes deleted messages? Use the max_messages_per_session option to limit the number of messages getmail will process in a single session. Some users with flaky servers use this option to reduce the chances of seeing messages more than once if the server dies in mid-session. How do I make getmail retrieve mail from Hotmail? Well, you could write a retriever that speaks Hotmail's proprietary, undocumented, and unsupported access protocol, or simply set up the POP3 proxy from the httpmail package, and have getmail retrieve mail from that POP3 proxy. I'm using getmail. How do I make it … These are supplementary questions I occasionally see about doing various things to enhance a getmail setup. The solution to many of them is to use a standard Unix technique of some sort to make the system behave in a certain manner, or otherwise change the behaviour of something that's actually outside of getmail proper. I'm running getmail from cron. How do I temporarily stop it? Some people ask about temporarily stopping getmail from running from a cron job, possibly because the mail server is down and they don't want to see the warnings cron mails them. The easiest method is to comment out getmail from your crontab file: 1. Run $ crontab -e to edit your crontab file. 2. Place a # (pound) character at the start of the line containing the call to getmail. 3. Save the changed file. When you want to re-enable getmail, edit the file again and un-do the above change. If you need to do this on a regular basis, you can instead use a "flag file" to tell the system whether or not to run getmail: Change your cron job or shellscript that normally launches getmail to check for the presence of a certain file first, and have it not run getmail if that file is present. For example, your crontab entry could be changed to do this: [ -f ~/.getmail/do-not-run ] || /path/to/getmail When you don't want getmail to run, touch that file: $ touch ~/.getmail/do-not-run When you want getmail to run again, delete it: $ rm -f ~/.getmail/do-not-run This is even safe for scripting, as creating and removing the file are atomic operations under Unix. How do I stop multiple instances of getmail from running at the same time? getmail has no problems running multiple instances in parallel, though you shouldn't attempt to use the same getmail rc file from two different instances at the same time; it will probably cause getmail to deliver duplicate copies of messages, "forget" that it has seen particular messages before, and other similar problems. In particular, if you're running getmail from a crontab, you must do something to prevent cron from starting getmail if the previous invocation is still running. If you need to prevent two instances of getmail from running simultaneously, use any standard Unix method of providing a mutex for this purpose. One example would be to run getmail under a program like setlock (part of the daemontools package). Change your script or crontab file to invoke getmail like this: /path/to/setlock -n /path/to/lockfile /path/to/getmail [getmail options] There are other programs that provide functionality similar to setlock. Using getmail with other software getmail user Frankye Fattarelli contributed to the following questions about integrating getmail with SpamAssassin and ClamAV. How do I use SpamAssassin with getmail? SpamAssassin can be run in standalone mode or in a client/server configuration. In both configurations, SpamAssassin accepts a wide variety of arguments; please refer to SpamAssassin's manual pages or online documentation for details. To filter messages through SpamAssassin in a client/server configuration (i.e. with the spamd daemon), use a configuration like this: [filter] type = Filter_external path = /usr/local/bin/spamc arguments = ("-s", "10000") The value supplied to the -s option is the maximum message size accepted (in bytes). The default is 250k. A similar configuration without the spamd daemon would be: [filter] type = Filter_external path = /usr/local/bin/spamassassin arguments = ("--report", ) The --report option sends the message to the various spam-blocker databases and tags it as spam in your bayesian database. Note that if you are using Bayesian (learning) filtering, and you've put your SpamAssassin filter after any getmail Filter_classifier, you may have a problem with your learning filter learning getmail's header fields. That is, the headers added by the other filters may get learned, and affect your database. To prevent this, ensure that SpamAssassin ignores these fields by adding the following to your SpamAssassin configuration: bayes_ignore_header X-getmail-filter-classifier How do I use ClamAV with getmail? You should also read this message in the getmail users' mailing list archives and the ClamAV documentation if you want to use ClamAV with getmail. ClamAV, like SpamAssassin, can by used in standalone or client/server configurations. In either case, you need to add the StreamSaveToDisk option to your clamav.conf file to enable scanning from stdin. To use ClamAV without the clamd daemon, use a filter configuration like this: [filter] type = Filter_classifier path = /usr/local/bin/clamscan arguments = ("--stdout", "--no-summary", "--mbox", "--infected", "-") exitcodes_drop = (1,) The above assumes you do not want the infected emails to be delivered. If you do want them delivered, you would use a slightly different configuration: [filter] type = Filter_classifier path = /usr/local/bin/clamscan arguments = ("--stdout", "--no-summary", "--mbox", "--infected", "-") exitcodes_keep = (0,1) To use ClamAV with the clamd daemon, use a filter configuration like this: [filter] type = Filter_classifier path = /usr/local/bin/clamdscan arguments = ("--stdout", "--disable-summary", "-") exitcodes_drop = (1, ) As with Clamscan (above), if you do want the infected messages delivered instead of dropped, you should modify your configuration as follows: [filter] type = Filter_classifier path = /usr/local/bin/clamdscan arguments = ("--stdout", "--disable-summary", "-") exitcodes_keep = (0,1) You may find it necessary to specify the paths of some decompression utilities used by ClamAV with additional arguments like: arguments = ( …, "--unzip=/usr/local/bin/unzip", "--unrar=/usr/local/bin/unrar", "--unarj=/usr/local/bin/unarj", "--lha=/usr/local/bin/lha", "--jar=/usr/local/bin/unzip", "--tar=/usr/bin/tar", "--tgz=/usr/bin/tar" Note: if you want to use the daemonized (client/server) version of ClamAV, ensure that your clamav.conf file contains: ScanMail The paths to the various decompression utilities must be specified in this file as well. See the following mailing list message from Frankye Fattarelli for additional notes on using ClamAV with getmail: https://marc.info/?l=getmail&m=109128345509273&w=2 Getting prettier output from ClamAV Using getmail's Filter_classifier, the output of your filtering program (in this case ClamAV) is placed into a X-getmail-filter-classifier: header field in the message. This can make auditing the actions of filters difficult if you use multiple filters and cannot tell which filter added which line. To correct this, you can use an additional filter to change the name of the added filter header lines immediately after each filter is run. For example, reformail, from the maildrop package (which is in turn part of the Courier MTA ) can be used in this fashion to rename the added header fields (say, to "X-mypersonalmailscan") with a filter configuration like this: type = Filter_external path = /usr/local/bin/reformail arguments = ("-R", "X-getmail-filter-classifier:", "X-mypersonalmailscan:") Simply ensure ClamAV is invoked as the first filter, and this is invoked as the second filter (or immediately after the ClamAV filter, if it is the second, third, etc. filter). How do I use F-Prot with getmail? getmail user Kai Raven reports that getmail and F-Prot work fine together with the following getmailrc filter configuration: [filter] type = Filter_external path = /usr/local/bin/f-prot-wrapper.sh The wrapper script f-prot-wrapper.sh is a small shellscript by Ali Onur Cinar, and can be downloaded from his website. How do I use procmail with getmail? Simply invoke procmail as an external MDA. procmail requires that one of the following be true: * that the message begin with a Unix "From " line (the mbox message delimiter) * that procmail is invoked with the -f option supplying the envelope sender, so that it may generate the "From " line To have getmail generate and prepend the "From " line to the start of the message, set the MDA_external parameter unixfrom to True: [destination] type = MDA_external path = /path/to/procmail unixfrom = True To supply the -f option to procmail, do something like this: [destination] type = MDA_external path = /path/to/procmail arguments = ("-f", "%(sender)") How do I use maildrop with getmail? Simply invoke maildrop as an external MDA. maildrop requires that the message begin with a Unix "From " line (the mbox message delimiter), so you'll need to either set the MDA_external parameter unixfrom to True, or supply arguments that tell maildrop to recreate this line. One of the following would be fine: [destination] type = MDA_external path = /path/to/maildrop arguments = ("-f", "%(sender)") Or: [destination] type = MDA_external path = /path/to/maildrop unixfrom = True If you want to specify a maildrop rc file as one of its arguments, that would be something like: [destination] type = MDA_external path = /path/to/maildrop arguments = ("-f", "%(sender)", "~/.maildroprc") How do I use TMDA with getmail? Simply use the Filter_TMDA module as a message filter: [filter-X] type = Filter_TMDA See the documentation for details on optional parameters to the Filter_TMDA module. How can I get Gmail labels with getmail? As of getmail version 4.34.0, getmail retrieves the labels and other metadata that Gmail makes available via an IMAP extension, and records that information in the message headers X-GMAIL-LABELS:, X-GMAIL-THRID:, and X-GMAIL-MSGID:. I think I found this bug in getmail … There are frequent reports like the following, which aren't bugs in getmail. Please read them before reporting them as bugs. getmail doesn't download all my mail from Gmail … There's a couple of different problems here. One is that Google's Gmail service violates the POP3 protocol by removing messages from the POP3 view of the mailbox without the user issuing a DELE command. They do this as soon as an RETR command is given, so if getmail tries to download a message and it fails for any reason (delivery fails due to a full disk, or the Gmail server fails to respond, or the network connection dies before the transfer is complete, or the Gmail server fails to respond to the QUIT command, or …), the next time getmail connects to that Gmail account, Gmail will have "helpfully" deleted the message from the POP3 mailbox, even though getmail never issued a DELE command. So Gmail silently destroys mail, from a POP3 perspective. There's nothing getmail can do about this. Note this feature of Gmail is not well-publicized. The only mention I can find of it is here: http://mail.google.com/support/bin/answer.py?answer=13291&topic=1555 The other issue here is that Google doesn't include mail from your trash or spam folders in the POP3 view, so getmail can't see those messages either. That's generally less of an issue, provided their spam filters never give false positive results (ha!). operation error (SimplePOP3Retriever: [...] does not uniquely identify messages [...] see documentation or use BrokenUIDLPOP3Retriever instead The server you're trying to use does not properly uniquely identify messages (getmail noticed when it saw the same "unique" identifier twice in the same mailbox at the same time). getmail needs these identifiers to be unique so that it can properly tell the difference between new and old messages. If you see this error message, and you've configured getmail to retrieve and immediately delete all messages, just switch to using the BrokenUIDLPOP3Retriever class (or its SSL variant) -- it'll work fine. If you see this error message, and you're trying to leave messages on the server after retrieval (permanently, or for a few days with delete_after), you have a few options to try to resolve it: * If your provider also offers IMAP access to your mailbox, try one of the IMAP retrievers instead. * Change your configuration so you're not leaving messages on the server, and use BrokenUIDLPOP3Retriever instead. * Talk to your mail hosting provider, and see if they can fix their POP3 software so that it doesn't have this problem any more. MemoryError on OS X If you see errors like this while running getmail on Macintosh OS X: python2.5(27172) malloc: *** vm_allocate(size=15699968) failed (error code=3) python2.5(27172) malloc: *** error: can't allocate region python2.5(27172) malloc: *** set a breakpoint in szone_error to debug [...] ... which then end with MemoryError, please report the problem to Apple. The OS X implementation of realloc() is broken, and there's nothing getmail can do about it. Errors connecting to Gmail with OpenSSL 1.1.1 If you experience connection/SSL errors connecting to Gmail servers, and your OpenSSL is version 1.1.1 or higher, the problem is that Gmail is failing the connection on the basis that SNI is not in use. To work around the problem, upgrade to getmail v.5.10 or later, or tell getmail to use TLSv1.2 rather than TLS1.3 in your retriever configuration and specify TLS v1.2 as the protocol to use: [retriever] ... ssl_version = tlsv1_2 getmail6-6.19.07/docs/getmail.1000066400000000000000000000045541474536475600160770ustar00rootroot00000000000000.TH getmail "1" "2025" "getmail 6" "User Commands" .SH NAME getmail \- retrieve messages from one or more POP3, IMAP4, or SDPS mailboxes and deliver to a maildir, mboxrd-format mbox file, or external MDA .SH SYNOPSIS .B getmail [\fIOPTION\fR] ... .SH DESCRIPTION .\" Add any additional description here .PP getmail can retrieve messages from POP3, IMAP4, and SDPS mailboxes. SSL-wrapped POP3 and IMAP are also supported. Single-user and domain (multi\-drop) mailboxes are supported. .PP getmail has built-in support for delivering to maildirs, mboxrd-format mbox files, and external MDAs. .SH OPTIONS .TP \fB\-\-version\fR show program's version number and exit .TP \fB\-h\fR, \fB\-\-help\fR show a short usage summary and exit .TP \fB\-g\fIDIR\fR, \fB\-\-getmaildir\fR=\fIDIR\fR look in DIR for config/data files .TP \fB\-r\fIFILE\fR, \fB\-\-rcfile\fR=\fIFILE\fR load configuration from FILE (may be given multiple times) .TP \fB\-\-dump\fR dump configuration and exit (debugging) .TP \fB\-\-trace\fR print extended trace information (extremely verbose) .TP \fB\-i\fIFOLDER\fR, \fB\-\-idle\fR=\fIFOLDER\fR maintain connection and listen for new messages in \fR\fIFOLDER\fI\fR. This flag will only work if a single rc file is given, and will only work on IMAP connections where the server supports IMAP4 IDLE (RFC 2177). .PP The following options override any in the configuration file(s). .TP \fB\-v\fR, \fB\-\-verbose\fR operate more verbosely (can be given multiple times) .TP \fB\-q\fR, \fB\-\-quiet\fR do not print informational messages .TP \fB\-d\fR, \fB\-\-delete\fR delete messages from server after retrieving .TP \fB\-l\fR, \fB\-\-dont\-delete\fR do not delete messages from server after retrieving .TP \fB\-a\fR, \fB\-\-all\fR retrieve all messages .TP \fB\-n\fR, \fB\-\-new\fR retrieve only unread messages .SH AUTHOR Written by Charles Cazabon and others. .SH "REPORTING BUGS" Report bugs to .PP \fBIMPORTANT:\fR include the output of .B getmail [options] --dump\fR with any bug report. .SH COPYRIGHT Copyright \(co 1998-2025 Charles Cazabon and others. .br This is free software; see the file COPYING for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" The full documentation for .B getmail is maintained in HTML and plaintext formats. See the included files for details. getmail6-6.19.07/docs/getmail_fetch.1000066400000000000000000000050101474536475600172340ustar00rootroot00000000000000.TH getmail_fetch "1" "2025" "getmail 6" "User Commands" .SH NAME getmail_fetch \- retrieve messages from one or more POP3 or POP3-over-SSL mailboxes and deliver to a maildir, mboxrd-format mbox file, or external MDA .SH SYNOPSIS .B getmail_fetch [\fIOPTIONS\fR] \fISERVER\fR \fIUSERNAME\fR \fIPASSWORD\fR \fIDESTINATION\fR .SH DESCRIPTION .\" Add any additional description here .PP getmail_fetch retrieves messages from POP3 or POP3-over-SSL mailboxes and delivers to a maildir, mboxrd, or external MDA. This command is intended primarily for scripting, and as such does not require a client-side configuration file, and does not record any state. .PP The \fIDESTINATION\fR argument is interpreted as follows: .PP If it begins with a dot or a slash and ends with a slash, it is assumed to be a path to a maildir. .PP If it begins with a dot or a slash and does not end with a slash, it is assumed to be a path to an mboxrd file. .PP If it begins with a pipe, it is assumed to be a path to an external MDA command. Note that arguments can be included in this string by separating them by whitespace; untrusted data \fBmust not\fR be used in constructing this string. .SH OPTIONS .TP \fB\-\-version\fR show program's version number and exit .TP \fB\-h\fR, \fB\-\-help\fR show a short usage summary and exit .TP \fB\-v\fR, \fB\-\-verbose\fR output information messages while running .TP \fB\-q\fR, \fB\-\-quiet\fR output only on error .TP \fB\-m\fIFILE\fR, \fB\-\-message\fR=\fIFILE\fR read well-formatted RFC822 message from FILE and deliver prior to connecting to POP server .TP \fB\-p\fIPORT\fR, \fB\-\-port\fR=\fIPORT\fR use port PORT instead of default (POP3: 110, POP3-over-SSL: 995) .TP \fB\-d\fR, \fB\-\-delete\fR delete messages from server after delivery .TP \fB\-t\fITIMEOUT\fR, \fB\-\-timeout\fR=\fITIMEOUT\fR use timeout of TIMEOUT seconds instead of default 180 .TP \fB\-a\fR, \fB\-\-apop\fR use APOP authentication .TP \fB\-s\fR, \fB\-\-ssl\fR use POP3-over-SSL .SH AUTHOR Written by Charles Cazabon and others. .SH "REPORTING BUGS" Report bugs to .PP \fBIMPORTANT:\fR include the output of .B getmail [options] --dump\fR with any bug report. .SH COPYRIGHT Copyright \(co 1998-2025 Charles Cazabon and others. .br This is free software; see the file COPYING for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" The full documentation for .B getmail is maintained in HTML and plaintext formats. See the included files for details. getmail6-6.19.07/docs/getmail_maildir.1000066400000000000000000000022501474536475600175670ustar00rootroot00000000000000.TH getmail_maildir "1" "2025" "getmail 6" "User Commands" .SH NAME getmail_maildir \- read a message from stdin and deliver to a named maildir .SH SYNOPSIS .B getmail_maildir [\fIOPTION\fR] \fIPATH\fR .SH DESCRIPTION .\" Add any additional description here .PP Deliver a mail message from standard input, to the maildir named PATH. PATH must start with a dot or a slash and end with a slash. .PP .B getmail_maildir uses the SENDER environment variable to construct a .B Return-Path: header field and the contents of the RECIPIENT environment variable to construct a .B Delivered-To: header field at the top of the message. .TP \fB\-\-verbose, \-v\fR print a status message on success\fR .SH AUTHOR Written by Charles Cazabon and others. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 1998-2025 Charles Cazabon and others. .br This is free software; see the file COPYING for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" The full documentation for .B getmail_maildir is maintained in HTML and plaintext formats. See the included files for details. getmail6-6.19.07/docs/getmail_mbox.1000066400000000000000000000026751474536475600171260ustar00rootroot00000000000000.TH getmail_mbox "1" "2025" "getmail 6" "User Commands" .SH NAME getmail_mbox \- read a message from stdin and deliver to a named mboxrd-format mbox file with fcntl-style locking. .SH SYNOPSIS .B getmail_mbox [\fIOPTION\fR] \fIPATH\fR .SH DESCRIPTION .\" Add any additional description here .PP Deliver a mail message from standard input, to the mboxrd-format mbox file named PATH. PATH must start with a dot or a slash and must not end with a slash. fcntl-type locking is used; if your system requires another type of locking (such as .B flock or .B dotlock ), use an MDA configured for that style of locking instead. .PP .B getmail_mbox uses the SENDER environment variable to construct a .B Return-Path: header field and the contents of the RECIPIENT environment variable to construct a .B Delivered-To: header field at the top of the message. SENDER is also used in creating the mbox .B From_ line. .TP \fB\-\-verbose, \-v\fR print a status message on success\fR .SH AUTHOR Written by Charles Cazabon and others. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 1998-2025 Charles Cazabon and others. .br This is free software; see the file COPYING for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" The full documentation for .B getmail_mbox is maintained in HTML and plaintext formats. See the included files for details. getmail6-6.19.07/docs/getmaildocs.css000066400000000000000000000022701474536475600173710ustar00rootroot00000000000000body { background: white; color: black; font-family: sans-serif; font-size: 100%; padding-bottom: 20em; } p { margin: 0.1em; padding: 0.5em; } p.introduction { padding: 2em; color: black; background-color: #f0f0f0; } .about { font-size: 80%; } .title { font-weight: bold; text-decoration: underline; } .file { font-family: monospace; font-size: 130%; color: #700000; background-color: transparent; } pre.example { display : block; margin: 1em; padding: 0 1em 1em 1em; font-family: monospace; white-space: pre; color: black; background-color: #f0f0f0; } li pre.example { display : block; margin: 1em; padding: 1em; font-family: monospace; white-space: pre; color: black; background-color: #f0f0f0; } .meta { font-style: italic; color: #0000c0; background-color: transparent; } .meta:before { content: "<"; } .meta:after { content: ">"; } .sample { font-family: monospace; font-size: 130%; color: #000070; background-color: transparent; } .note { font-style: italic; } .warning { color: red; background-color: transparent; } .important { font-weight: bold; color: black; background-color: #e0e0ff; } .errmsg,.warnmsg { font-family: monospace; font-size: 150%; } hr.divider { margin-top: 5em; } getmail6-6.19.07/docs/getmailrc-examples000066400000000000000000000342771474536475600201060ustar00rootroot00000000000000# # This file contains various examples of configuration sections to use # in your getmail rc file. You need one file for each mail account you # want to retrieve mail from. These files should be placed in your # getmail configuration/data directory # (default: ~/.config/getmail/). # If you only need one rc file, name it getmailrc in that directory, # and you won't need to supply any commandline options to run getmail. # # # Example 0: Using pass as password_command to manage your passwords, # storing all messages in a maildir. # [retriever] type = SimpleIMAPSSLRetriever server = imap.gmail.com port = 993 username = password_command = ("pass", "") [destination] type = Maildir path = ~/Mail//INBOX/ [options] read_all = true delete = true # # Example 1: simplest case of retrieving mail from one POP3 server and # storing all messages in a maildir. # [retriever] type = SimplePOP3Retriever server = pop.example.net username = jeff.plotzky password = mailpassword [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 2: same as (1), but operate quietly, delete messages from # the server after retrieving them, and log getmail's actions (in detail) # to a file. # [options] verbose = 0 delete = true message_log = ~/.getmail/log message_log_verbose = true [retriever] type = SimplePOP3Retriever server = pop.example.net username = jeff.plotzky password = mailpassword [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 3: same as (1), but the mail account is accessed via IMAP4 instead # of POP3. # [retriever] type = SimpleIMAPRetriever server = mail.example.net username = jeff.plotzky password = mailpassword [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 4: same as (3), but retrieve mail from the INBOX, INBOX.spam, and # mailing-lists.getmail-users mail folders. # [retriever] type = SimpleIMAPRetriever server = mail.example.net username = jeff.plotzky password = mailpassword mailboxes = ("INBOX", "INBOX.spam", "mailing-lists.getmail-users") [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 5: same as (3), but move messages to the mail folder "sent-mail" # after retrieving them. Note that you do this by setting delete and # move_on_delete options. # [options] delete = true [retriever] type = SimpleIMAPRetriever server = mail.example.net username = jeff.plotzky password = mailpassword move_on_delete = sent-mail [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 6: same as (1), but deliver the messages to an mboxrd-format mbox # file as user "jeffp". # [retriever] type = SimplePOP3Retriever server = pop.example.net username = jeff.plotzky password = mailpassword [destination] type = Mboxrd path = ~jeffp/Mail/inbox user = jeffp # # Example 7: same as (1), but deliver the messages through an external MDA # which takes several arguments. # [retriever] type = SimplePOP3Retriever server = pop.example.net username = jeff.plotzky password = mailpassword [destination] type = MDA_external path = /usr/local/bin/my-mda arguments = ("--message-from-stdin", "--scan-message", "--to-maildir", "~jeffp/Maildir/") # # Example 8: retrieve mail from a corporate POP3-SSL domain mailbox, # sort messages for several local users and deliver to maildirs in their # home directories (except Sam, who likes mbox files, and Christina, who # uses procmail for further sorting), and deliver all other mail to # Joe, who serves as postmaster for the company. Sam also needs # to receive mail for "sam1", "sam23", etc, so we use a regular expression # matching "sam" plus zero or more decimal digits. # [retriever] type = MultidropPOP3SSLRetriever server = pop.example.net username = companylogin password = mailpassword # Our domain mailbox mailhost records the envelope recipient address in a # new Delivered-To: header field at the top of the message. envelope_recipient = delivered-to:1 [destination] type = MultiSorter default = [postmaster] locals = ( ("jeffk@company.example.net", "[jeff]"), ("martinh@company.example.net", "[martin]"), (r"sam\D*@company.example.net", "[sam]"), ("c.fellowes@company.example.net", "[christina-procmail]") ) [postmaster] type = Maildir path = ~joe/Mail/postmaster/ user = joe [jeff] type = Maildir path = ~jeffp/Maildir/ user = jeffp [martin] type = Maildir path = ~martinh/Maildir/ user = martinh [sam] type = Mboxrd path = ~sam/Mail/inbox user = sam [christina-procmail] type = MDA_external path = /usr/local/bin/procmail # procmail requires either that the message starts with an mboxrd-style # "From " line (which getmail can generate by setting "unixfrom" to True), or # that the -f option is provided as below. arguments = ("-f", "%(sender)", "-m", "/home/christina/.procmailrc") user = christina # # Example 9: same as (3), but use SpamAssassin to filter out spam, # and ClamAV to filter out MS worms. # [retriever] type = SimpleIMAPRetriever server = mail.example.net username = jeff.plotzky password = mailpassword [filter-1] type = Filter_external path = /usr/local/bin/spamc [filter-2] type = Filter_classifier path = /usr/local/bin/clamscan arguments = ("--stdout", "--no-summary", "--mbox", "--infected", "-") exitcodes_drop = (1,) [destination] type = Maildir path = ~jeffp/Maildir/ # # Example 10: same as (3), but deliver all mail to two different local # mailboxes. # [retriever] type = SimpleIMAPRetriever server = mail.example.net username = jeff.plotzky password = mailpassword [destination] type = MultiDestination destinations = ( "~jeff/Maildir/", "/var/log/mail-archive/current", ) # # Example 11: retrieve mail from a simple (non-multidrop) POP3 mailbox. # Then extract addresses from the message header (see documentation for which # fields are examined), and deliver mail containing the address # to ~/Mail/lists/list1/, mail containing the # address to ~/Mail/lists/list2/, # mail containing the address to ~/Mail/other/, # and all other mail gets delivered through the external MDA program # "my-mda" with some default arguments. # [retriever] type = SimplePOP3Retriever server = pop.example.net username = jeff.plotzky password = mailpassword [destination] type = MultiGuesser default = [my-mda] locals = ( ("list1@domain.example.net", "~/Mail/lists/list1/"), ("list2@otherdomain.example.com", "~/Mail/lists/list2/"), ("othername@example.org", "~/Mail/other/"), ) [my-mda] type = MDA_external path = /path/to/my-mda arguments = ("-f", "%(sender)", "${HOME}/.mymdarc") # # Example 12: Gmail xoauth2 example # # A client_id and client_secret identify a web app or a desktop app. # getmail-gmail-xoauth-tokens # creates a local server with loopback redirect (127.0.0.1) to get the authorization. # https://developers.google.com/identity/protocols/oauth2/native-app#redirect-uri_loopback # # To initialize do: # # Step1: Create a new OAuth 2.0 Client-ID # # - project create: # https://console.cloud.google.com/projectcreate # - consent screen: # https://console.cloud.google.com/apis/credentials/consent # only external available for non-workspace users. # [ADD OR REMOVE SCOPE] https://mail.google.com/ # Test User: Add all your emails you want to use with getmail. # - credential: # https://console.cloud.google.com/apis/credentials # [Create Credentials/Oauth client ID] Desktop App / getmail # DOWNLOAD JSON # # Step 2: for each email you mentioned as test user above, # create a file .json according the below template. # # - Edit the email in each json file # - copy two lines from the downloaded json into each json file # client_id # client_secret # # {"scope": "https://mail.google.com/", # "user": "your-gmail-user@gmail.com", # "client_id": "the new client id", # "client_secret": "the new secret", # "token_uri": "https://accounts.google.com/o/oauth2/token", # "auth_uri": "https://accounts.google.com/o/oauth2/auth", # "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"} # # Step 3: init step, execute for each json file # # getmail-gmail-xoauth-tokens --init /path-to-your-users-getmail-directory/.json # # It will ask you to visit a http url. # On the browser: # - There will be a warning "Google hasn't verified this app": continue. # - Then there will be the consent screen: continue. # # getmail-gmail-xoauth-tokens will update the .json file. # # Unfortunately this Step 3 needs to be repeated regularly. # Therefore on should better opt for an google generated "app password". # # Step 4: Update the `[retriever]` section of the rc file as below. # use_xoauth2 must be True, else [AUTHENTICATIONFAILED]. # [retriever] type = SimpleIMAPSSLRetriever use_xoauth2 = True server = imap.gmail.com username = your-gmail-user@gmail.com password_command = ("getmail-gmail-xoauth-tokens", "/path/to/your/users/getmail/directory/.json") # # Example 13: Microsoft Office 365 IMAP4 xoauth2 example # # To initialize do: # # Step1: Create App Registration in Azure # # In a browser, open https://portal.azure.com # Select "Microsoft Entra ID" (use the search if needed) # Select "App Registrations" # Select "New Registration" # Enter a project name, eg "getmail". # Select "Accounts in this organizational directory only (Single tenant)" from "Supported account types" # Select type "Web" and enter "http://localhost" for "Redirect URI" # Select "Register" # # Now, from the new App's details page, make a note of: # * client_id # * tenant_id # (These are in the "Essentials" section.) # # Next create a secret by selecting "Certificates and secrets" # Select "New client secret" # * Description: password # * Expires: select preferred expiry date # Make a note of: # * client_secret # It is available by selecting "Value". # # Next add permissions: # # Select "API permissions", then "Add a permission". # Select "Microsoft Graph" # Select "Delegated permissions" # Search and select the following permissions: # * IMAP.AccessAsUser.All # * offline_access # Select "Add permissions" # Step 2: Add the new credentials to the microsoft.json template # # {"scope": "https://outlook.office.com/IMAP.AccessAsUser.All offline_access", # "user": "firstname.lastname@example.com", # "client_id": "", # "client_secret": "", # "token_uri": "https://login.microsoftonline.com//oauth2/v2.0/token", # "auth_uri": "https://login.microsoftonline.com//oauth2/v2.0/authorize"} # # Step 3: execute: # # getmail-gmail-xoauth-tokens --init /path-to-your-users-getmail-directory/microsoft.json # # This will give you a URL you need to open in a browser. # Opening this URL will generate a HTTP-redirect that connects back and updates the json file. # Note: The script starts a local HTTP-server listening on http://localhost:8083. # If you connect from a remote machine, you will need to forward that port to your local # machine, so that the server can be reached via localhost:8083 on the machine running the browser. # (You only need to do this once.) # # getmail-gmail-xoauth-tokens is waiting for the reconnect with the URL that contains the verification code. # Once it received the callback # # getmail-gmail-xoauth-tokens will update the microsoft.json file. The json file # will now contain the required tokens. # # Step 4: Update the `[retriever] `section of the rc file. [retriever] type = SimpleIMAPSSLRetriever use_xoauth2 = True server = outlook.office365.com username = firstname.lastname@example.com password_command = ("getmail-gmail-xoauth-tokens", "/path-to-your-users-getmail-directory/microsoft.json") # # Example 14: Personal Microsoft accounts (Outlook.com, Hotmail, Live, or MSN) # IMAP4 xoauth2 example # # To initialize do: # # Step1: Create App Registration in Azure # (You need a Microsoft Azure tenant) # # In a browser, open https://portal.azure.com # Select "Microsoft Entra ID" (use the search if needed) # Select "App Registrations" # Select "New Registration" # Enter a project name, eg "getmail". # Select "Personal Microsoft accounts only" from "Supported account types" # Select type "Web" and enter "http://localhost" for "Redirect URI" # Select "Register" # # Now, from the new App's details page, make a note of: # * client_id # * tenant_id # (These are in the "Essentials" section.) # # Next create a secret by selecting "Certificates and secrets" # Select "New client secret" # * Description: password # * Expires: select preferred expiry date # Make a note of: # * client_secret # It is available by selecting "Value". # # Next add permissions: # # Select "API permissions", then "Add a permission". # Select "Microsoft Graph" # Select "Delegated permissions" # Search and select the following permissions: # * IMAP.AccessAsUser.All # * offline_access # Select "Add permissions" # Step 2: Add the new credentials to the microsoft.json template # # {"scope": "https://outlook.office.com/IMAP.AccessAsUser.All offline_access", # "user": "firstname.lastname@example.com", # "client_id": "", # "client_secret": "", # "token_uri": "https://login.microsoftonline.com/consumers/oauth2/v2.0/token", # "auth_uri": "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"} # # Step 3: execute: # # getmail-gmail-xoauth-tokens --init /path-to-your-users-getmail-directory/microsoft.json # # This will give you a URL you need to open in a browser. # Opening this URL will generate a HTTP-redirect that connects back and updates the json file. # Note: The script starts a local HTTP-server listening on http://localhost:8083. # If you connect from a remote machine, you will need to forward that port to your local # machine, so that the server can be reached via localhost:8083 on the machine running the browser. # (You only need to do this once.) # # getmail-gmail-xoauth-tokens is waiting for the reconnect with the URL that contains the verification code. # Once it received the callback # # getmail-gmail-xoauth-tokens will update the microsoft.json file. The json file # will now contain the required tokens. # # Step 4: Update the `[retriever] `section of the rc file. [retriever] type = SimpleIMAPSSLRetriever use_xoauth2 = True server = outlook.office365.com username = firstname.lastname@example.com password_command = ("getmail-gmail-xoauth-tokens", "/path-to-your-users-getmail-directory/microsoft.json") getmail6-6.19.07/docs/getmails.1000066400000000000000000000036541474536475600162620ustar00rootroot00000000000000.TH getmails "1" "2025" "getmail6" "User Commands" .SH NAME getmails \- wrapper for getmail to access multiple mailboxes .SH SYNOPSIS .B getmails [\fB-p\fR] [\fIOPTION\fR] ... .SH DESCRIPTION .\" Add any additional description here .PP .B getmails is a wrapper script for .B getmail to retrieve messages with multiple independent configuration files .I $XDG_CONFIG_HOME/getmail/* or for backward compatibility .I ~/.getmail/config/* instead of a single .I ~/.getmailrc file. The configuration file name can be any arbitrary names. It's a good idea to use the corresponding email address as its name since it is the most intuitive choice. For example .I $XDG_CONFIG_HOME/getmail/foo@example.org . To disable operation for an email account temporary, rename its configuration file to the hidden file such as .I $XDG_CONFIG_HOME/getmail/.foo@example.org . Then, it is ignored as configuration file. For the robust operation, the editor generated files such as .I $XDG_CONFIG_HOME/getmail/foo~ , .I $XDG_CONFIG_HOME/getmail/#foo# , .I $XDG_CONFIG_HOME/getmail/foo.swp , and .I $XDG_CONFIG_HOME/getmail/foo.bak are also ignored as configuration file. When there are multiple email configuration files, .B getmails retrieves mails sequentially. But adding an optional option .B -p before other options makes .B getmails invoke .B getmail with each configuration file in parallel to retrieve mails. The option provided as \fIOPTION\fR is passed to \fBgetmail\fR as is. The use of the \fB-r\fR option is not a good idea. The existence of .I $XDG_CONFIG_HOME/getmail/stop can prevent this wrapper script to execute \fBgetmail\fR. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright 2011-2017 Osamu Aoki .br Copyright 2025 Roland Puntaier .br This is free software; see the file COPYING for copying conditions. .br There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. getmail6-6.19.07/docs/troubleshooting.html000066400000000000000000000610511474536475600205030ustar00rootroot00000000000000 getmail troubleshooting (version 6)

getmail documentation

This is the documentation for getmail version 6, a port of getmail version 5 to python 3 without adding features.

getmail6 is Copyright © 1998-2025 by Charles Cazabon and others:
<charlesc-getmail @ pyropus.ca>
<roland.puntaier @ gmail.com>

getmail and getmail6 are licensed under the GNU General Public License version 2 (only).

Table of Contents

Troubleshooting problems

If you have suggestions for additions or changes to this documentation, please send them to us or open an issue on GitHub.

Error messages

getmail may output various diagnostic error messages. The common ones and their meanings are given below.

ImportError: getmail version 6.xy requires Python version 2.7.18 or later

You tried to run getmail with a version of Python prior to Python 2.7.18 This is unsupported. If you cannot install a newer Python alongside your current version, please use getmail version 5.

Configuration error: …

getmail detected an error in your configuration. Check your getmail rc file(s). getmail will do its best to point out the exact cause of the error. Some of the specific errors it may find include the following.

Configuration error: missing required configuration parameter name

A class object in your getmail rc file requires the parameter name, but it was not found in the appropriate section of the file.

Configuration error: configuration value name (value) not of required type type (why)

The configuration parameter name must be of type type, but the supplied value value does not appear to be of that type. Further information may be present in why.

The getmail documentation contains descriptions of the syntax for each parameter type.

Configuration error: maildir path missing trailing /

Maildir paths must start with dot or slash and end with a slash.

Configuration error: not a maildir (path)

The specified maildir path path does not appear to be a valid maildir. Check to ensure that it is a valid maildir, and that getmail has permission to write to it.

Configuration error: ... (path: maildir subdirectory "path" does not exist)

The specified maildir path path does not appear to be a valid maildir, as it is missing one of the required subdirectories. Check to ensure that it is a valid maildir and that getmail has permission to write to it.

Configuration error: not an mboxrd file (path)

The specified mboxrd path path does not appear to be a valid mboxrd file. To avoid corrupting files in the event of a user typo, getmail will not deliver messages to files that do not appear to be valid mboxrd files.

Configuration error: mboxrd does not exist

The specified mboxrd does not exist. getmail will not create mbox files; ensure they exist before trying to deliver to them.

Configuration error: the fieldname header field does not record the envelope recipient address

In a multidrop retriever configuration, you specified that the envelope recipient was recorded in a header field that getmail knows does not actually record that information.

Configuration error: refuse to invoke external commands as root or GID 0 by default

By default, getmail will not invoke external commands (in destinations or filters) when it is running with root privileges, for security reasons. See the documentation for possible solutions.

Configuration error: no such command path

An external command was specified as being located at path path, but the command executable was not found at that location.

Configuration error: path not executable

A specified external command at path path was found to not be an executable file.

Configuration error: destination specifies section name section which does not exist

A destination in the getmail rc file refers to another rc file section named section, but that section was not found in the file.

Retrieval error …

getmail detected an error while trying to retrieve a message. Some of the specific errors it may find include the following.

Retrieval error: server … is broken; …

The server claimed to have a particular message, but when getmail tried to retrieve it, the server returned an empty response in violation of the POP or IMAP protocol. getmail will skip on to the next message.

This problem is almost always with an MSExchange server, and is due to bugs in MSExchange. Delete the offending message from the mailbox via the webmail interface if you don't want to see this error message, and report the bug to the server administrator or Microsoft.

Delivery error …

getmail detected an error after retrieving a message but before delivery was successfully completed. Some of the specific errors it may find include the following.

Delivery error: maildir delivery process failed (refuse to deliver mail as root)

getmail will not deliver to a maildir as the root user. You may specify an alternate user to deliver as with the user directive in the destination section of your getmailrc file, or let getmail default to the user who is the owner of the maildir.

Delivery error: mbox delivery process failed (refuse to deliver mail as root)

getmail will not deliver to an mbox file as the root user. You may specify an alternate user to deliver as with the user directive in the destination section of your getmailrc file, or let getmail default to the user who is the owner of the mbox file.

Delivery error: mbox delivery process failed (not an mboxrd file)

The specified mbox file does not appear to be a valid mbox file.

Error: …

getmail detected an operational error. getmail will do its best to point out the exact cause of the error. Common causes are failures to connect to a remote mail server, timeouts during network operations, and other transient failures.

Error: server-info does not uniquely identify messages …

The POP3 server for this particular account was not able to uniquely identify the messages in the mailstore. You can use the BrokenUIDLPOP3Retriever class with this server instead, but functionality is limited because of the inability to identify messages properly.

Error: server-info failed to identify message X in UIDL output …

The POP3 server for this particular account completely failed to identify one of the messages in the mailstore when the UIDL command was issued. The POP server is in violation of the POP3 protocol, and getmail has no way to identify the message in question.

The most common cause of this is servers that use the contents of the Message-ID: header field as the UIDL value for the message - some messages (usually spam) lack a Message-ID: header field entirely, causing these servers to emit blank UIDL values for these messages, which is not permitted by the POP3 protocol. You may be able to work around the problem by deleting the problematic message(s) from the mailstore by other means (such as a webmail interface provided by your POP3 mailhost).

Otherwise, you'll need to either ask the postmaster of the POP3 mail host to fix the POP3 software they're using. In the meantime, you can use the BrokenUIDLPOP3Retriever class with this server instead, but functionality is limited because of the inability to identify messages properly.

Python(…) malloc: *** mmap(…) failed (…)

… followed by an Unhandled exception block and then MemoryError.

getmail ran out of memory.

If you are using IMAP and run into this problem retrieving messages that aren't hundreds of megabytes in size, you can almost certainly fix it by upgrading your Python installation to the latest 3.* release.

Warning messages

getmail may output various diagnostic warning messages. The common ones and their meanings are given below.

Warning: …

getmail detected a condition which might be problematic. Some of the specific warnings it may find include the following.

Warning: ignoring unknown parameter "parameter_name"

A getmail rc file contained an unknown parameter. This usually indicates that you've put the parameter in the wrong section of the file (such as putting the read_all parameter in the [retriever] section instead of in the [options] section).

Warning: filter filter returned fewer headers (X) than supplied (Y)

A message filter appeared to incorrectly remove header fields from the header of a message which it handled. getmail warns you about this so that you can check your filter for proper operation. Filters should add information to the message header or leave it alone; check the configuration for the filter program you are using.

Unexpected Behaviour

If getmail is behaving in a manner other that you expected, see the following sections.

getmail uses the wrong hostname for my machine

If getmail records a hostname other than the "right" one for your host (in its Received: trace header fields), check your /etc/hosts file and make sure the "right" name is the first hostname listed for any of the addresses of the machine.

getmail puts "unknown" in the Delivered-To: or Return-Path:header field

getmail records the envelope recipient address in the Delivered-To: header field, and the envelope sender in the Return-Path: header field. If this information is not available (because you're not using a multidrop retriever class, or the MTA on the POP/IMAP server is broken, for example), getmail uses the string "unknown". You can disable the creation of the Delivered-To: header field if you wish.

getmail isn't replacing my command argument "%(recipient)" with the message recipient address

The %(recipient), %(local), and %(domain) replacement strings are only replaced if the message is retrieved with a multidrop retriever class; otherwise, getmail does not "know" the recipient address of the message, and therefore cannot perform this replacement.

getmail seems to take longer than expected to initialize

If getmail takes more than a few seconds to initialize, run the following command to test:

python -c "import socket; print socket.getfqdn()"

If this seems to take a similarly long period of time to complete, the delay is in finding the fully-qualified hostname of your system. The fix is to ensure you have a valid mapping of address-to-hostname for all the addresses in your system. You can do this in your /etc/hosts file, in your authoritative content DNS server, or in another system-specific manner — please contact your OS vendor (or its public support mailing list) for assistance.

getmail6-6.19.07/docs/troubleshooting.txt000066400000000000000000000351661474536475600203660ustar00rootroot00000000000000 Link: Contents Up Index: getmail6 getmail documentation This is the documentation for getmail version 6, a port of getmail version 5 to python 3 without adding features. getmail6 is Copyright © 1998-2025 by Charles Cazabon and others: getmail and getmail6 are licensed under the GNU General Public License version 2 (only). Table of Contents * getmail documentation * * getmail documentation * * Features * Requirements * Obtaining getmail * Installing getmail * getmail configuration * * Configuring getmail * * Creating a getmail rc file * Running getmail * * Commandline options * Using getmail as an MDA * Using getmail_fetch to retrieve mail from scripts * getmail troubleshooting * * Troubleshooting problems * * Error messages * * ImportError: getmail version 6.xy requires Python version 2.7.18 or later * Configuration error: … * * Configuration error: missing required configuration parameter name * Configuration error: configuration value name (value) not of required type type (why) * Configuration error: maildir path missing trailing / * Configuration error: not a maildir (path) * Configuration error: ... (path: maildir subdirectory "path" does not exist) * Configuration error: not an mboxrd file (path) * Configuration error: mboxrd does not exist * Configuration error: the fieldname header field does not record the envelope recipient address * Configuration error: refuse to invoke external commands as root or GID 0 by default * Configuration error: no such command path * Configuration error: path not executable * Configuration error: destination specifies section name section which does not exist * Retrieval error … * * Retrieval error: server … is broken; … * Delivery error … * * Delivery error: maildir delivery process failed (refuse to deliver mail as root) * Delivery error: mbox delivery process failed (refuse to deliver mail as root) * Delivery error: mbox delivery process failed (not an mboxrd file) * Error: … * * Error: server-info does not uniquely identify messages … * Error: server-info failed to identify message X in UIDL output … * Python(…) malloc: *** mmap(…) failed (…) * Warning messages * * Warning: … * * Warning: ignoring unknown parameter "parameter_name" * Warning: filter filter returned fewer headers (X) than supplied (Y) * Unexpected Behaviour * * getmail uses the wrong hostname for my machine * getmail puts "unknown" in the Delivered-To: or Return-Path:header field * getmail isn't replacing my command argument "%(recipient)" with the message recipient address * getmail seems to take longer than expected to initialize * getmail frequently-asked questions (FAQs) (version 5) * * Frequently-Asked Questions (FAQs) * * About getmail * Configuring getmail * How do I … * Using getmail with other software * I think I found this bug in getmail … Troubleshooting problems If you have suggestions for additions or changes to this documentation, please send them to us or open an issue on GitHub. Error messages getmail may output various diagnostic error messages. The common ones and their meanings are given below. ImportError: getmail version 6.xy requires Python version 2.7.18 or later You tried to run getmail with a version of Python prior to Python 2.7.18 This is unsupported. If you cannot install a newer Python alongside your current version, please use getmail version 5. Configuration error: … getmail detected an error in your configuration. Check your getmail rc file(s). getmail will do its best to point out the exact cause of the error. Some of the specific errors it may find include the following. Configuration error: missing required configuration parameter name A class object in your getmail rc file requires the parameter name, but it was not found in the appropriate section of the file. Configuration error: configuration value name (value) not of required type type (why) The configuration parameter name must be of type type, but the supplied value value does not appear to be of that type. Further information may be present in why. The getmail documentation contains descriptions of the syntax for each parameter type. Configuration error: maildir path missing trailing / Maildir paths must start with dot or slash and end with a slash. Configuration error: not a maildir (path) The specified maildir path path does not appear to be a valid maildir. Check to ensure that it is a valid maildir, and that getmail has permission to write to it. Configuration error: ... (path: maildir subdirectory "path" does not exist) The specified maildir path path does not appear to be a valid maildir, as it is missing one of the required subdirectories. Check to ensure that it is a valid maildir and that getmail has permission to write to it. Configuration error: not an mboxrd file (path) The specified mboxrd path path does not appear to be a valid mboxrd file. To avoid corrupting files in the event of a user typo, getmail will not deliver messages to files that do not appear to be valid mboxrd files. Configuration error: mboxrd does not exist The specified mboxrd does not exist. getmail will not create mbox files; ensure they exist before trying to deliver to them. Configuration error: the fieldname header field does not record the envelope recipient address In a multidrop retriever configuration, you specified that the envelope recipient was recorded in a header field that getmail knows does not actually record that information. Configuration error: refuse to invoke external commands as root or GID 0 by default By default, getmail will not invoke external commands (in destinations or filters) when it is running with root privileges, for security reasons. See the documentation for possible solutions. Configuration error: no such command path An external command was specified as being located at path path, but the command executable was not found at that location. Configuration error: path not executable A specified external command at path path was found to not be an executable file. Configuration error: destination specifies section name section which does not exist A destination in the getmail rc file refers to another rc file section named section, but that section was not found in the file. Retrieval error … getmail detected an error while trying to retrieve a message. Some of the specific errors it may find include the following. Retrieval error: server … is broken; … The server claimed to have a particular message, but when getmail tried to retrieve it, the server returned an empty response in violation of the POP or IMAP protocol. getmail will skip on to the next message. This problem is almost always with an MSExchange server, and is due to bugs in MSExchange. Delete the offending message from the mailbox via the webmail interface if you don't want to see this error message, and report the bug to the server administrator or Microsoft. Delivery error … getmail detected an error after retrieving a message but before delivery was successfully completed. Some of the specific errors it may find include the following. Delivery error: maildir delivery process failed (refuse to deliver mail as root) getmail will not deliver to a maildir as the root user. You may specify an alternate user to deliver as with the user directive in the destination section of your getmailrc file, or let getmail default to the user who is the owner of the maildir. Delivery error: mbox delivery process failed (refuse to deliver mail as root) getmail will not deliver to an mbox file as the root user. You may specify an alternate user to deliver as with the user directive in the destination section of your getmailrc file, or let getmail default to the user who is the owner of the mbox file. Delivery error: mbox delivery process failed (not an mboxrd file) The specified mbox file does not appear to be a valid mbox file. Error: … getmail detected an operational error. getmail will do its best to point out the exact cause of the error. Common causes are failures to connect to a remote mail server, timeouts during network operations, and other transient failures. Error: server-info does not uniquely identify messages … The POP3 server for this particular account was not able to uniquely identify the messages in the mailstore. You can use the BrokenUIDLPOP3Retriever class with this server instead, but functionality is limited because of the inability to identify messages properly. Error: server-info failed to identify message X in UIDL output … The POP3 server for this particular account completely failed to identify one of the messages in the mailstore when the UIDL command was issued. The POP server is in violation of the POP3 protocol, and getmail has no way to identify the message in question. The most common cause of this is servers that use the contents of the Message-ID: header field as the UIDL value for the message - some messages (usually spam) lack a Message-ID: header field entirely, causing these servers to emit blank UIDL values for these messages, which is not permitted by the POP3 protocol. You may be able to work around the problem by deleting the problematic message(s) from the mailstore by other means (such as a webmail interface provided by your POP3 mailhost). Otherwise, you'll need to either ask the postmaster of the POP3 mail host to fix the POP3 software they're using. In the meantime, you can use the BrokenUIDLPOP3Retriever class with this server instead, but functionality is limited because of the inability to identify messages properly. Python(…) malloc: *** mmap(…) failed (…) … followed by an Unhandled exception block and then MemoryError. getmail ran out of memory. If you are using IMAP and run into this problem retrieving messages that aren't hundreds of megabytes in size, you can almost certainly fix it by upgrading your Python installation to the latest 3.* release. Warning messages getmail may output various diagnostic warning messages. The common ones and their meanings are given below. Warning: … getmail detected a condition which might be problematic. Some of the specific warnings it may find include the following. Warning: ignoring unknown parameter "parameter_name" A getmail rc file contained an unknown parameter. This usually indicates that you've put the parameter in the wrong section of the file (such as putting the read_all parameter in the [retriever] section instead of in the [options] section). Warning: filter filter returned fewer headers (X) than supplied (Y) A message filter appeared to incorrectly remove header fields from the header of a message which it handled. getmail warns you about this so that you can check your filter for proper operation. Filters should add information to the message header or leave it alone; check the configuration for the filter program you are using. Unexpected Behaviour If getmail is behaving in a manner other that you expected, see the following sections. getmail uses the wrong hostname for my machine If getmail records a hostname other than the "right" one for your host (in its Received: trace header fields), check your /etc/hosts file and make sure the "right" name is the first hostname listed for any of the addresses of the machine. getmail puts "unknown" in the Delivered-To: or Return-Path:header field getmail records the envelope recipient address in the Delivered-To: header field, and the envelope sender in the Return-Path: header field. If this information is not available (because you're not using a multidrop retriever class, or the MTA on the POP/IMAP server is broken, for example), getmail uses the string "unknown". You can disable the creation of the Delivered-To: header field if you wish. getmail isn't replacing my command argument "%(recipient)" with the message recipient address The %(recipient), %(local), and %(domain) replacement strings are only replaced if the message is retrieved with a multidrop retriever class; otherwise, getmail does not "know" the recipient address of the message, and therefore cannot perform this replacement. getmail seems to take longer than expected to initialize If getmail takes more than a few seconds to initialize, run the following command to test: python -c "import socket; print socket.getfqdn()" If this seems to take a similarly long period of time to complete, the delay is in finding the fully-qualified hostname of your system. The fix is to ensure you have a valid mapping of address-to-hostname for all the addresses in your system. You can do this in your /etc/hosts file, in your authoritative content DNS server, or in another system-specific manner — please contact your OS vendor (or its public support mailing list) for assistance. getmail6-6.19.07/getmail000077500000000000000000001417621474536475600150160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. import sys import os import os.path import time try: import ConfigParser except: import configparser as ConfigParser import netrc import poplib import imaplib import pprint from optparse import OptionParser, OptionGroup import socket import signal import errno import getpass # Optional gnome-keyring integration try: import gnomekeyring import glib glib.set_application_name('getmail') # And test to see if it's actually available if not gnomekeyring.is_available(): gnomekeyring = None except ImportError: gnomekeyring = None # Optional Python keyring integration try: import keyring except ImportError: keyring = None # Unix only try: import syslog except ImportError: pass try: from getmailcore import __version__, __license__, \ retrievers, destinations, filters, logging from getmailcore.exceptions import * from getmailcore.utilities import eval_bool, logfile, format_params, \ address_no_brackets, expand_user_vars except ImportError as o: sys.stderr.write('ImportError: %s\n' % o) sys.exit(127) log = logging.Logger() log.addhandler(sys.stdout, logging.INFO, maxlevel=logging.INFO) log.addhandler(sys.stderr, logging.WARNING) def blurb(): log.info('getmail version %s\n' % __version__) log.info('Copyright (C) 1998-2025 Charles Cazabon and others. ' 'Licensed under %s.\n'%__license__) options_bool = ( 'read_all', 'delete', 'delivered_to', 'received', 'message_log_verbose', 'message_log_syslog', 'fingerprint', 'use_netrc', 'to_oldmail_on_each_mail', 'only_oldmail_file', 'skip_imap_fetch_size', 'mark_read', ) options_int = ( 'delete_after', 'delete_bigger_than', 'max_message_size', 'max_messages_per_session', 'max_bytes_per_session', 'verbose', ) options_str = ( 'message_log', 'netrc_file', ) options_defaults = { 'rcfile' : 'getmailrc', 'verbose' : 1, 'read_all' : True, 'delete' : False, 'delete_after' : 0, 'delete_bigger_than' : 0, 'max_message_size' : 0, 'max_messages_per_session' : 0, 'max_bytes_per_session' : 0, 'delivered_to' : True, 'received' : True, 'message_log' : None, 'message_log_verbose' : False, 'message_log_syslog' : False, 'logfile' : None, 'fingerprint' : False, 'use_netrc' : False, 'netrc_file' : None, 'to_oldmail_on_each_mail' : False, 'only_oldmail_file': False, 'skip_imap_fetch_size' : False, 'mark_read': False, } ####################################### def convert_to_sigint(unused1, unused2): """Catch a SIGTERM and raise a SIGINT so getmail exits normally and does cleanup if killed with default signal. """ raise KeyboardInterrupt('from signal') signal.signal(signal.SIGTERM, convert_to_sigint) ####################################### def go(configs, idle, only_account=[]): """Main code. Returns True if all goes well, False if any error condition occurs. """ blurb() # needed by docs/COPYING 2c summary = [] errorexit = False idling = False if len(configs) > 1 and idle: log.info('more than one config file given with --idle, ignoring\n') idle = False for (configfile, retriever, _filters, destination, options) in configs: username = retriever.conf.get('username') if only_account and len(only_account) > 0 and username not in only_account: continue if options['skip_imap_fetch_size'] and ( options['max_message_size'] or options['max_bytes_per_session'] or options['delete_bigger_than']): log.error('%s: skipping, because skip_imap_fetch_size not valid with any of ' 'max_message_size max_bytes_per_session delete_bigger_than.\n' % retriever) continue if options['read_all'] and not options['delete']: if idle: # This is a nonsense combination of options; every time the # server returns from IDLE, all messages will be re-retrieved. log.error('%s: IDLE, read_all, and not delete - bad ' 'combination, skipping\n' % retriever) continue else: # Slightly less nonsensical, but still weird. log.warning('%s: read_all and not delete -- all messages will ' 'be retrieved each time getmail is run\n' % retriever) oplevel = options['verbose'] logverbose = options['message_log_verbose'] now = int(time.time()) msgs_retrieved = 0 bytes_retrieved = 0 msgs_skipped = 0 if options['message_log_syslog']: syslog.openlog('getmail', 0, syslog.LOG_MAIL) try: if not idling: log.info('%s:\n' % retriever) logline = 'Initializing %s:' % retriever if options['logfile'] and logverbose: options['logfile'].write(logline) if options['message_log_syslog'] and logverbose: syslog.syslog(syslog.LOG_INFO, logline) retriever.initialize(options) destination.retriever_info(retriever) # session ready for idling idling = idle for mailbox in retriever.mailboxes: if mailbox: # For POP this is None and uninteresting log.debug(' checking mailbox %s ...\n' % mailbox) try: retriever.select_mailbox(mailbox) except getmailMailboxSelectError as o: errorexit = True log.info(' mailbox %s not selectable (%s) - verify the ' 'mailbox exists and you have sufficient ' 'permissions\n' % (mailbox, o)) continue nummsgs = len(retriever) fmtlen = len(str(nummsgs)) for (msgnum, msgid) in enumerate(retriever): log.debug(' message %s ...\n' % msgid) msgnum += 1 retrieve = False reason = 'seen' delete = False timestamp = retriever.oldmail.get(msgid, None) size = retriever.getmsgsize(msgid) info = ('msg %*d/%*d (%d bytes)' % (fmtlen, msgnum, fmtlen, nummsgs, size)) if mailbox: info = '[%s] '%mailbox + info logline = '%s msgid %s' % (info, msgid) if options['read_all'] or timestamp is None: retrieve = True if options['only_oldmail_file']: retriever.delivered(msgid) retrieve = False reason = 'only oldmail file option' if (options['max_message_size'] and size > options['max_message_size']): retrieve = False reason = 'oversized' if (options['max_bytes_per_session'] and (bytes_retrieved + size) > options['max_bytes_per_session']): retrieve = False reason = 'would surpass max_bytes_per_session' try: if retrieve: try: msg = retriever.getmsg(msgid) except (getmailRetrievalError,getmailConfigurationError) as o: # Check if xoauth2 token was expired # (Exchange Online only) if 'AccessTokenExpired' in str(o): log.warn('Retrieval error: %s\n' % o) idling = False break errorexit = True log.error( 'Retrieval error: %s\n' 'Server for %s is broken; ' 'offered message %s but failed to provide it. ' 'Please notify the administrator of the ' 'server. Skipping message...\n' % (o, retriever, msgid) ) continue msgs_retrieved += 1 bytes_retrieved += size if oplevel > 1: info += (' from <%s>' % address_no_brackets(msg.sender)) if msg.recipient is not None: info += (' to <%s>' % address_no_brackets(msg.recipient)) logline += (' from <%s>' % address_no_brackets(msg.sender)) if msg.recipient is not None: logline += (' to <%s>' % address_no_brackets(msg.recipient)) for mail_filter in _filters: log.debug(' passing to filter %s\n' % mail_filter) msg = mail_filter.filter_message(msg, retriever) if msg is None: log.debug(' dropped by filter %s\n' % mail_filter) info += (' dropped by filter %s' % mail_filter) logline += (' dropped by filter %s' % mail_filter) retriever.delivered(msgid) break if msg is not None: r = destination.deliver_message(msg, options['delivered_to'], options['received']==True and msgid) log.debug(' delivered to %s\n' % r) info += ' delivered' if oplevel > 1: info += (' to %s' % r) logline += (' delivered to %s' % r) retriever.delivered(msgid) if options['delete']: delete = True else: logline += ' not retrieved (%s)' % reason msgs_skipped += 1 log.debug(' not retrieving (timestamp %s)\n' % timestamp) if oplevel > 1: info += ' not retrieved (%s)' % reason if (options['delete_after'] and timestamp and (now - timestamp) / 86400 >= options['delete_after']): log.debug( ' older than %d days (%s seconds), will delete\n' % (options['delete_after'], (now - timestamp)) ) delete = True if options['delete'] and timestamp: log.debug(' will delete\n') delete = True if (options['delete_bigger_than'] and size > options['delete_bigger_than']): log.debug(' bigger than %d, will delete\n' % options['delete_bigger_than']) delete = True if not retrieve and timestamp is None: # We haven't retrieved this message. Don't delete it. log.debug(' not yet retrieved, not deleting\n') delete = False if options['only_oldmail_file']: delete = False if delete: if retriever.delmsg(msgid): log.debug(' deleted\n') info += ', deleted' logline += ', deleted' except getmailDeliveryError as o: errorexit = True log.error('Delivery error (%s)\n' % o) info += ', delivery error (%s)' % o if options['logfile']: options['logfile'].write('Delivery error (%s)' % o) if options['message_log_syslog']: syslog.syslog(syslog.LOG_ERR, 'Delivery error (%s)' % o) except getmailFilterError as o: errorexit = True log.error('Filter error (%s)\n' % o) info += ', filter error (%s)' % o if options['logfile']: options['logfile'].write('Filter error (%s)' % o) if options['message_log_syslog']: syslog.syslog(syslog.LOG_ERR, 'Filter error (%s)' % o) if (retrieve or delete or oplevel > 1): log.info(' %s\n' % info) if options['logfile'] and (retrieve or delete or logverbose): options['logfile'].write(logline) if options['message_log_syslog'] and (retrieve or delete or logverbose): syslog.syslog(syslog.LOG_INFO, logline) if (options['max_messages_per_session'] and msgs_retrieved >= options['max_messages_per_session']): log.debug('hit max_messages_per_session (%d), breaking\n' % options['max_messages_per_session']) if oplevel > 1: log.info(' max messages per session (%d)\n' % options['max_messages_per_session']) raise StopIteration('max_messages_per_session %d' % options['max_messages_per_session']) except StopIteration: pass except KeyboardInterrupt as o: log.warning('%s: user aborted\n' % configfile) if options['logfile']: options['logfile'].write('user aborted') except socket.timeout as o: errorexit = True retriever.abort() if type(o) == tuple and len(o) > 1: o = o[1] log.error('%s: timeout (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('timeout error (%s)' % o) except (poplib.error_proto, imaplib.IMAP4.abort) as o: errorexit = True retriever.abort() log.error('%s: protocol error (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('protocol error (%s)' % o) except socket.gaierror as o: errorexit = True retriever.abort() if type(o) == tuple and len(o) > 1: o = o[1] log.error('%s: error resolving name (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('gaierror error (%s)' % o) except socket.error as o: errorexit = True retriever.abort() if type(o) == tuple and len(o) > 1: o = o[1] log.error('%s: socket error (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('socket error (%s)' % o) except getmailCredentialError as o: errorexit = True retriever.abort() log.error('%s: credential/login error (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('credential/login error (%s)' % o) except getmailLoginRefusedError as o: retriever.abort() log.error('%s: login refused error (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('login refused error (%s)' % o) except getmailOperationError as o: errorexit = True retriever.abort() log.error('%s: operation error (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('getmailOperationError error (%s)' % o) if options['message_log_syslog']: syslog.syslog(syslog.LOG_ERR, 'getmailOperationError error (%s)' % o) summary.append( (retriever, msgs_retrieved, bytes_retrieved, msgs_skipped) ) if idle: log.info(' %d messages (%d bytes) retrieved, %d skipped from %s\n' % (msgs_retrieved, bytes_retrieved, msgs_skipped, retriever)) else: log.info(' %d messages (%d bytes) retrieved, %d skipped\n' % (msgs_retrieved, bytes_retrieved, msgs_skipped)) if options['logfile'] and logverbose: options['logfile'].write( ' %d messages (%d bytes) retrieved, %d skipped\n' % (msgs_retrieved, bytes_retrieved, msgs_skipped) ) log.debug('retriever %s finished\n' % retriever) try: if idle and not retriever.supports_idle: log.info('--idle given, but server does not support IDLE\n') idle = False if idle and not errorexit: # TODO # Okay, so what should really happen here is that when go_idle # returns, getmail should use the *existing* connection to check # for new messages and then call go_idle again once that is # done. The current code layout doesn't lend itself very well to # that since the message download code is coupled with the # connection setup/teardown code. # # Therefore, we do a bit of a hack. # We add the current config back into configs, so that when the # main for loop over configs runs again, it will find the same # config again, and thus download the new messages and then go # back to IDLEing. Since the return value of go_idle changes the # value of idling, a failed connection will cause it to become # False, which will make the main go() loop reconnect, which is # what we want. # Expunge and close the mailbox to prevent the same messages # being pulled again in some configurations. try: retriever.close_mailbox() except imaplib.IMAP4.abort as o: # Treat "abort" exception as temporary failure log.info('%s: session aborted during close_mailbox (%s)\n' % (configfile, o)) idling = False try: if idling: idling = retriever.go_idle(idle) # Returned from idle retriever.set_new_timestamp() configs.append(configs[0]) continue except KeyboardInterrupt as o: # Because configs isn't appended to, this just means we'll # quit, which is presumably what the user wanted # The newline is to clear the ^C shown in terminal log.info('\n') pass except socket.error as o: if o.errno != errno.ECONNRESET: # Something unexpected happened raise #pass # Just exit after a reset connection. retriever.quit() except getmailOperationError as o: errorexit = True log.debug('%s: operation error during quit (%s)\n' % (configfile, o)) if options['logfile']: options['logfile'].write('%s: operation error during quit (%s)' % (configfile, o)) if sum([i for (unused, i, unused, unused) in summary]) and oplevel > 1: log.info('Summary:\n') for (retriever, msgs_retrieved, bytes_retrieved, unused) in summary: log.info('Retrieved %d messages (%s bytes) from %s\n' % (msgs_retrieved, bytes_retrieved, retriever)) return (not errorexit) def imap_search_flags(imap_search_n_set): """ >>> imap_search_n_set=[','] >>> imap_search_flags(imap_search_n_set) (['\\Seen'], ['UNSEEN']) >>> imap_search_n_set=['Unseen',','] >>> imap_search_flags(imap_search_n_set) (['\\Seen'], ['Unseen']) >>> imap_search_n_set=['Seen',',Seen'] >>> imap_search_flags(imap_search_n_set) (['\\Seen'], ['Seen']) >>> imap_search_n_set=['\\Seen'] >>> flags,search = imap_search_flags(imap_search_n_set) >>> '\\SEEN' in {x.upper() for x in flags} True """ flags = [] search = [] ooi = imap_search_n_set isflag = lambda x: x[0] in {',','\\'} mkflag = lambda x: '\\'+(x[1:] or 'Seen') if ooi: flags = [mkflag(x) for i in ooi for x in i.split() if isflag(x)] search = [x for i in ooi for x in i.split() if not isflag(x)] search = search or ['UNSEEN'] search = [x for x in search if x.strip()!='ALL'] return flags,search ####################################### def main(): try: parser = OptionParser(version='%%prog %s' % __version__) parser.add_option( '-g', '--getmaildir', dest='getmaildir', action='store', help='look in DIR for config/data files', metavar='DIR' ) parser.add_option( '-r', '--rcfile', dest='rcfile', action='append', default=[], help='load configuration from FILE (may be given multiple times)', metavar='FILE' ) parser.add_option( '--dump', dest='dump_config', action='store_true', default=False, help='dump configuration and exit (debugging)' ) parser.add_option( '--trace', dest='trace', action='store_true', default=False, help='print extended trace information (extremely verbose)' ) parser.add_option( '-i', '--idle', dest='idle', action='store', default='', help='maintain connection and listen for new messages in FOLDER. ' 'Only applies if a single rc file is given with a connection ' 'to an IMAP server that supports the IDLE command', metavar='FOLDER' ) if gnomekeyring: parser.add_option( '--store-password-in-gnome-keyring', dest='store_gnome_keyring', action='store_true', default=False, help='store the POP/IMAP password in the Gnome keyring' ) if keyring: parser.add_option( '--store-password-in-keyring', dest='store_keyring', action='store_true', default=False, help='store the POP/IMAP password using the Python keyring package' ) parser.add_option( '-s', '--searchset', dest='imap_search_n_set', action='append', help=r'''search and set. `-s` can be repeated to add more search strings to select mails to retrieve. After retrieval flags are set if `delete=True'. `-d` makes sure it is True. `-ds,` to set flags other than "\Deleted"`. `,` gets converted to the IMAP flag char `\`. If no flag is given "\Seen" is assumed. `-ds,` is like mark-read. `-s` overrides `imap_search` and `imap_on_delete` existing in the rcfile, or the defaults. `imap_search` defaults to "Unseen" and `imap_on_delete` defaults to "\Deleted \Seen". ''' ) overrides = OptionGroup( parser, 'Overrides', 'The following options override those specified in any ' 'getmailrc file.' ) overrides.add_option( '-v', '--verbose', dest='override_verbose', action='count', help='operate more verbosely (may be given multiple times)' ) overrides.add_option( '--fingerprint', dest='override_fingerprint', action='store_true', help='show SSL/TLS fingerprint and connection information' ) overrides.add_option( '-q', '--quiet', dest='override_verbose', action='store_const', const=0, help='operate quietly (only report errors)' ) overrides.add_option( '-d', '--delete', dest='override_delete', action='store_true', help='after retrieving flag Deleted or as given by `-s,`' ) overrides.add_option( '-l', '--dont-delete', dest='override_delete', action='store_false', help='after retrieving do not flag messages Deleted or as given by `-s,`' ) overrides.add_option( '-a', '--all', dest='override_read_all', action='store_true', help='retrieve all messages' ) overrides.add_option( '-n', '--new', dest='override_read_all', action='store_false', help='retrieve only unread messages' ) overrides.add_option( '--to-oldmail-on-each-mail', dest='override_to_oldmail_on_each_mail', action='store_true', help='store retrieved mails in oldmail file after every mail to avoid re-download in case of session crash' ) overrides.add_option( '--only-oldmail-file', dest='override_only_oldmail_file', action='store_true', help='generates an oldmail file of mails now on server without retreiving them (neither in the future, due to the generated oldmail file)' ) overrides.add_option( '-m', '--mark-read', dest='override_mark_read', action='store_true', help='mark read after retrieve (synonym to `-ds,`)' ) parser.add_option_group(overrides) parser.add_option( '-o', '--only-account', dest='only_account', action='append', help='check specified account only (may be given multiple times)' ) (options, args) = parser.parse_args(sys.argv[1:]) if args: raise getmailOperationError('unknown argument(s) %s ; try --help' % args) if options.trace: log.clearhandlers() s = '' for attr in dir(options): if attr.startswith('_'): continue if s: s += ',' s += '%s="%s"' % (attr, pprint.pformat(getattr(options, attr))) log.debug('parsed options: %s\n' % s) if options.getmaildir is None: getmaildir_type = 'Default' xdg_config = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.environ["HOME"], ".config")) getmaildir_xdg = os.path.join(xdg_config, 'getmail') getmaildir_home = os.path.join(os.environ["HOME"], ".getmail") if os.path.exists(getmaildir_xdg): getmaildir = getmaildir_xdg elif os.path.exists(getmaildir_home): getmaildir = getmaildir_home else: raise getmailOperationError('Could not find the getmail configuration directory. mkdir ~/.config/getmail/ or specify an alternate directory with the --getmaildir option.') else: getmaildir_type = 'Specified' getmaildir = expand_user_vars(options.getmaildir) if not os.path.exists(getmaildir): raise getmailOperationError( '%s config/data dir "%s" does not exist - create ' 'or specify alternate directory with --getmaildir option' % (getmaildir_type, getmaildir) ) if not os.path.isdir(getmaildir): raise getmailOperationError( '%s config/data dir "%s" is not a directory - fix ' 'or specify alternate directory with --getmaildir option' % (getmaildir_type, getmaildir) ) if not os.access(getmaildir, os.W_OK): raise getmailOperationError( '%s config/data dir "%s" is not writable - fix permissions ' 'or specify alternate directory with --getmaildir option' % (getmaildir_type, getmaildir) ) if not options.rcfile: if not os.path.exists( os.path.join(getmaildir,options_defaults['rcfile'])): options.rcfile = [x for x in os.listdir(getmaildir) # keep in sync with getmails if not x.endswith('~') and not x.endswith('#') and not x.startswith('oldmail-') and not x.startswith('.') and not x.endswith('.json') and not x.endswith('.swp') and not x.endswith('.bak') and os.path.isfile(os.path.join(getmaildir,x)) ] else: options.rcfile.append(options_defaults['rcfile']) configs = [] for filename in options.rcfile: path = os.path.join(getmaildir,filename) log.debug('processing rcfile %s\n' % path) if not os.path.exists(path): raise getmailOperationError('configuration file %s does ' 'not exist' % path) elif not os.path.isfile(path): raise getmailOperationError('%s is not a file' % path) f = open(path, 'r') defaultopt = { 'verbose' : options_defaults['verbose'], 'read_all' : options_defaults['read_all'], 'delete' : options_defaults['delete'], 'delete_after' : options_defaults['delete_after'], 'delete_bigger_than' : options_defaults['delete_bigger_than'], 'max_message_size' : options_defaults['max_message_size'], 'max_messages_per_session' : options_defaults['max_messages_per_session'], 'max_bytes_per_session' : options_defaults['max_bytes_per_session'], 'delivered_to' : options_defaults['delivered_to'], 'received' : options_defaults['received'], 'logfile' : options_defaults['logfile'], 'message_log' : options_defaults['message_log'], 'message_log_verbose' : options_defaults['message_log_verbose'], 'message_log_syslog' : options_defaults['message_log_syslog'], 'fingerprint' : options_defaults['fingerprint'], 'use_netrc' : options_defaults['use_netrc'], 'netrc_file' : options_defaults['netrc_file'], 'to_oldmail_on_each_mail' : options_defaults['to_oldmail_on_each_mail'], 'only_oldmail_file' : options_defaults['only_oldmail_file'], 'skip_imap_fetch_size' : options_defaults['skip_imap_fetch_size'], 'mark_read': options_defaults['mark_read'], } # Python's ConfigParser .getboolean() couldn't handle booleans in # the defaults. Submitted a patch; they fixed it a different way. # But for the extant, unfixed versions, an ugly hack.... parserdefaults = defaultopt.copy() for (key, value) in parserdefaults.items(): if type(value) == bool: parserdefaults[key] = str(value) try: configparser = ConfigParser.RawConfigParser(parserdefaults) try: configparser.read_file(f) except AttributeError: configparser.readfp(f) f.close() for option in options_bool: log.debug(' looking for option %s ... ' % option) if configparser.has_option('options', option): log.debug('got "%s"' % configparser.get('options', option)) try: defaultopt[option] = configparser.getboolean( 'options', option ) log.debug('-> %s' % defaultopt[option]) except ValueError: raise getmailConfigurationError( 'configuration file %s incorrect (option %s ' 'must be boolean, not %s)' % (path, option, configparser.get('options', option)) ) else: log.debug('not found') log.debug('\n') for option in options_int: log.debug(' looking for option %s ... ' % option) if configparser.has_option('options', option): log.debug( 'got "%s"' % configparser.get('options', option) ) try: defaultopt[option] = configparser.getint('options', option) log.debug('-> %s' % defaultopt[option]) except ValueError: raise getmailConfigurationError( 'configuration file %s incorrect (option %s ' 'must be integer, not %s)' % (path, option, configparser.get('options', option)) ) else: log.debug('not found') log.debug('\n') # Message log file for option in options_str: log.debug(' looking for option %s ... ' % option) if configparser.has_option('options', option): log.debug('got "%s"' % configparser.get('options', option)) defaultopt[option] = configparser.get('options', option) log.debug('-> %s' % defaultopt[option]) else: log.debug('not found') log.debug('\n') if defaultopt['message_log']: try: defaultopt['logfile'] = logfile(defaultopt['message_log']) except IOError as o: raise getmailConfigurationError( 'error opening message_log file %s (%s)' % (defaultopt['message_log'], o) ) # see if a netrc file is configured netrc_object = None if defaultopt['use_netrc']: # informs user via FileNotFoundError, if file is not there netrc_object = netrc.netrc( defaultopt['netrc_file'] and expand_user_vars(defaultopt['netrc_file'])) # Apply overrides from commandline for o in ( 'override_read_all', 'override_delete', 'override_verbose', 'override_fingerprint', 'override_to_oldmail_on_each_mail', 'override_only_oldmail_file', 'override_mark_read', ): val = getattr(options, o) if val is not None: option_org = o.split('_',1)[1] log.debug('overriding option %s from commandline %s\n' % (option_org, val)) defaultopt[option_org] = val imap_override = {} flags,search = imap_search_flags(options.imap_search_n_set) if defaultopt['mark_read']: if '\\SEEN' not in {x.upper() for x in flags}: flags += ['\\SEEN'] if 'UNSEEN' not in {x.upper() for x in search}: search += ['UNSEEN'] if flags: imap_override['imap_on_delete'] = '('+' '.join(flags)+')' options.override_delete = True # intention given by -s, if search: imap_override['imap_search'] = '('+' '.join(search)+')' # Clear out the ConfigParser defaults before processing further # sections configparser._defaults = {} # Retriever log.debug(' getting retriever\n') retriever_type = configparser.get('retriever', 'type') log.debug(' type="%s"\n' % retriever_type) retriever_func = getattr(retrievers, retriever_type) if not callable(retriever_func): raise getmailConfigurationError( 'configuration file %s specifies incorrect ' 'retriever type (%s)' % (path, retriever_type) ) retriever_args = { 'getmaildir' : getmaildir, 'configparser' : configparser, } for (name, value) in configparser.items('retriever'): if name in ('type', 'configparser'): continue if name == 'password': log.debug(' parameter %s=*\n' % name) else: log.debug(' parameter %s="%s"\n' % (name, value)) retriever_args[name] = value if netrc_object: # add username and password from netrc, as read above netrc_auth = netrc_object.authenticators( retriever_args['server']) if netrc_auth and netrc_auth[0]: retriever_args['username'] = netrc_auth[0] if netrc_auth and netrc_auth[2]: retriever_args['password'] = netrc_auth[2] log.debug(' instantiating retriever %s with args %s\n' % (retriever_type, format_params(retriever_args))) try: retriever = retriever_func(**retriever_args) log.debug(' checking retriever configuration for %s\n' % retriever) for (name, value) in imap_override.items(): if name in retriever: retriever.conf[name] = value retriever.checkconf() except getmailOperationError as o: log.error('Error initializing retriever: %s\n' % o) continue # Retriever is okay. Check if user wants us to store the # password in a Gnome keyring for future use. if (gnomekeyring and options.store_gnome_keyring or keyring and options.store_keyring): # Need to get the password first, if the user hasn't put # it in the rc file. if retriever.conf.get('password', None) is not None: password = retriever.conf['password'] elif retriever.conf.get('password_command', None): # Retrieve from an arbitrary external command password = retriever.run_password_command() else: password = getpass.getpass('Enter password for %s: ' % str(retriever)) if options.store_keyring: keyring.set_password( retriever.conf['server'] ,retriever.conf['username'] ,password) log.info('Stored password in Python keyring. Exiting.\n') else: gnomekeyring.set_network_password_sync( # keyring=None, user, domain=None, server, object=None, # protocol, authtype=None, port=0 None, retriever.conf['username'], None, retriever.conf['server'], None, retriever.received_with, None, 0, password ) log.info('Stored password in Gnome keyring. Exiting.\n') if keyring: log.info('... but Gnome keyring will not be used as you have Python keyring installed.\n') raise SystemExit() # Destination log.debug(' getting destination\n') destination_type = configparser.get('destination', 'type') log.debug(' type="%s"\n' % destination_type) destination_func = getattr(destinations, destination_type) if not callable(destination_func): raise getmailConfigurationError( 'configuration file %s specifies incorrect destination ' 'type (%s)' % (path, destination_type) ) destination_args = {'configparser' : configparser} for (name, value) in configparser.items('destination'): if name in ('type', 'configparser'): continue if name == 'password': log.debug(' parameter %s=*\n' % name) else: log.debug(' parameter %s="%s"\n' % (name, value)) destination_args[name] = value log.debug(' instantiating destination %s with args %s\n' % (destination_type, format_params(destination_args))) destination = destination_func(**destination_args) # Filters log.debug(' getting filters\n') _filters = [] filtersections = [ section.lower() for section in configparser.sections() if section.lower().startswith('filter') ] filtersections.sort() for section in filtersections: log.debug(' processing filter section %s\n' % section) filter_type = configparser.get(section, 'type') log.debug(' type="%s"\n' % filter_type) filter_func = getattr(filters, filter_type) if not callable(filter_func): raise getmailConfigurationError( 'configuration file %s specifies incorrect filter ' 'type (%s)' % (path, filter_type) ) filter_args = {'configparser' : configparser} for (name, value) in configparser.items(section): if name in ('type', 'configparser'): continue if name == 'password': log.debug(' parameter %s=*\n' % name) else: log.debug(' parameter %s="%s"\n' % (name, value)) filter_args[name] = value log.debug(' instantiating filter %s with args %s\n' % (filter_type, format_params(filter_args))) mail_filter = filter_func(**filter_args) _filters.append(mail_filter) except ConfigParser.NoSectionError as o: raise getmailConfigurationError( 'configuration file %s missing section (%s)' % (path, o) ) except ConfigParser.NoOptionError as o: raise getmailConfigurationError( 'configuration file %s missing option (%s)' % (path, o) ) except (ConfigParser.DuplicateSectionError, ConfigParser.InterpolationError, ConfigParser.MissingSectionHeaderError, ConfigParser.ParsingError) as o: raise getmailConfigurationError( 'configuration file %s incorrect (%s)' % (path, o) ) except getmailConfigurationError as o: raise getmailConfigurationError( 'configuration file %s incorrect (%s)' % (path, o) ) if defaultopt['verbose'] > 2: defaultopt['verbose'] = 2 if not options.trace and defaultopt['verbose'] == 0: log.clearhandlers() log.addhandler(sys.stderr, logging.WARNING) configs.append((os.path.basename(filename), retriever, _filters, destination, defaultopt.copy())) if options.dump_config: # Override any "verbose = 0" in the config file log.clearhandlers() log.addhandler(sys.stdout, logging.INFO, maxlevel=logging.INFO) log.addhandler(sys.stderr, logging.WARNING) blurb() for (filename, retriever, _filters, destination, configopt) in configs: log.info('getmail configuration:\n') log.info(' getmail version %s\n' % __version__) log.info(' Python version %s\n' % sys.version) log.info(' retriever: ') retriever.showconf() if _filters: for _filter in _filters: log.info(' filter: ') _filter.showconf() log.info(' destination: ') destination.showconf() log.info(' options:\n') for name in sorted(configopt.keys()): log.info(' %s : %s\n' % (name, configopt[name])) log.info('\n') sys.exit() # Go! success = go(configs, options.idle, options.only_account) if not success: raise SystemExit(127) except KeyboardInterrupt: log.warning('Operation aborted by user (keyboard interrupt)\n') sys.exit(0) except getmailConfigurationError as o: log.error('Configuration error: %s\n' % o) sys.exit(2) except getmailOperationError as o: log.error('Error: %s\n' % o) sys.exit(3) except Exception as o: log.critical( '\nException: please read docs/BUGS and include the ' 'following information in any bug report:\n\n' ) log.critical(' getmail version %s\n' % __version__) log.critical(' Python version %s\n\n' % sys.version) log.critical('Unhandled exception follows:\n') (exc_type, value, tb) = sys.exc_info() import traceback tblist = (traceback.format_tb(tb, None) + traceback.format_exception_only(exc_type, value)) if type(tblist) != list: tblist = [tblist] for line in tblist: log.critical(' %s\n' % line.rstrip()) log.critical('\nPlease also include configuration information ' 'from running getmail\n') log.critical('with your normal options plus "--dump".\n') sys.exit(4) ####################################### if __name__ == '__main__': main() getmail6-6.19.07/getmail-gmail-xoauth-tokens000077500000000000000000000150211474536475600207000ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright 2012 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Derived from oauth2.py (https://github.com/google/gmail-oauth2-tools). # Heavily modified and rewritten by Stefan Krah. # # Works for Microsoft Office 365 in addition to Gmail. import os import sys import json import argparse import time try: import urllib.request as urllibrequest import urllib.parse as urllibparse import urllib.error as urlliberror from http.server import HTTPServer, BaseHTTPRequestHandler urlparse = urllibparse def codebytes(b): return b.encode("utf8") except: #py2 import urlparse import urllib as urllibparse import urllib2 as urlliberror from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler urllibrequest = urllibparse def codebytes(b): return b force_local_redirect = True # Local webserver nonsense required to receive code from redirect class OAuthRedirectHandler(BaseHTTPRequestHandler): def do_GET(self): response = urlparse.urlparse(self.path) query_string = urlparse.parse_qs(response.query) code = query_string["code"][0] self.server.oauth_code = code self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(b"

Your json file is updated.

") class OAuthRedirectServer(HTTPServer): def __init__(self, port): self.oauth_code = None HTTPServer.__init__(self, ("localhost", int(port)), OAuthRedirectHandler) self.timeout = None class OAuth2(object): def __init__(self, token_data_path): self.token_data_path = token_data_path with open(self.token_data_path) as f: self.data = json.load(f) def copy(self, *keys): data = {} for k in keys: try: data[k] = self.data[k] except KeyError: pass return data def query(self, params): lst = [] for param in sorted(params.items(), key=lambda x: x[0]): escaped = urllibparse.quote(param[1], safe='~-._') lst.append('%s=%s' % (param[0], escaped)) return '&'.join(lst) def code_url(self, port): params = self.copy('scope', 'client_id', 'redirect_uri') if 'redirect_uri' not in params or force_local_redirect: params['redirect_uri'] = 'http://localhost:' + str(port) + '/' params['response_type'] = 'code' params['access_type'] = 'offline' if 'prompt' in self.data: params['prompt'] = self.data['prompt'] else: params['prompt'] = 'consent' return '%s?%s' % (self.data['auth_uri'], self.query(params)) def get_response(self, url, params): try: encoded = urllibparse.urlencode(params).encode('ascii') response = urllibrequest.urlopen(url, encoded).read() return json.loads(response) except urlliberror.HTTPError as httpError: error = httpError.read().decode() print("error: " + error) sys.exit(1) def update_config(self, d): self.data['access_token'] = d['access_token'] self.data['expires_at'] = time.time() + d['expires_in'] - 100 refresh_token = d.get('refresh_token') if refresh_token is not None: self.data['refresh_token'] = refresh_token with open(self.token_data_path, "w") as f: json.dump(self.data, f) def init_tokens(self, code, port): params = self.copy('user', 'client_id', 'client_secret', 'redirect_uri') if 'redirect_uri' not in params or force_local_redirect: params['redirect_uri'] = 'http://localhost:' + str(port) + '/' params['code'] = code params['grant_type'] = 'authorization_code' d = self.get_response(self.data['token_uri'], params) self.update_config(d) def refresh_tokens(self): params = self.copy('client_id', 'client_secret', 'refresh_token') params['grant_type'] = 'refresh_token' d = self.get_response(self.data['token_uri'], params) self.update_config(d) def token(self): if time.time() >= self.data.get('expires_at'): self.refresh_tokens() return self.data['access_token'] if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-i", "--init", action="store_true", default=False, help="initialize access and refresh tokens") parser.add_argument('tokenfile', metavar='', help="location of the token data file") parser.add_argument("-p", "--port", default=None, help="local port to use for receiving oauth2 redirect (default 8083)") args = parser.parse_args() if not args.port: force_local_redirect = False args.port = 8083 auth = OAuth2(args.tokenfile) if args.init: interrupted = False print("Visit this url to obtain a verification code:") print(" %s\n" % auth.code_url(args.port)) if not auth.copy('redirect_uri') or force_local_redirect: print("Press Ctrl+C if you are installing getmail on a device without a browser.") oauthd = OAuthRedirectServer(args.port) try: oauthd.handle_request() auth.init_tokens(oauthd.oauth_code, args.port) except: interrupted = True finally: oauthd.server_close() if (auth.copy('redirect_uri') and not force_local_redirect) or interrupted: print("Please paste the response from the address bar of the browser you used for the url above:") codeinput = input() response = urlparse.urlparse(codeinput) query_string = urlparse.parse_qs(response.query) code = query_string["code"][0] auth.init_tokens(code,args.port) print("\naccess token\n") print("%s" % auth.token()) sys.exit(0) getmail6-6.19.07/getmail_fetch000077500000000000000000000213701474536475600161570ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. import sys import os import socket import poplib import types from optparse import OptionParser try: from getmailcore import __version__, __license__, \ retrievers, destinations, message, logging from getmailcore.exceptions import * except ImportError as o: sys.stderr.write('ImportError: %s\n' % o) sys.exit(127) log = logging.Logger() log.addhandler(sys.stderr, logging.WARNING) def blurb(): log.info('getmail version %s\n' % __version__) log.info('Copyright (C) 1998-2025 Charles Cazabon and others. ' 'Licensed under %s.\n'%__license__) ####################################### class dummyInstance: pass ####################################### def error_exit(v, s): sys.stderr.write(s + '\n') sys.stderr.flush() sys.exit(v) ####################################### def go(retriever, destination, options, startmsg): msgs_retrieved = 0 if options.verbose: log.addhandler(sys.stdout, logging.INFO, maxlevel=logging.INFO) blurb() # needed by docs/COPYING 2c try: if startmsg is not None: destination.deliver_message(startmsg, False, False) log.info('%s:\n' % retriever) retriever.initialize(options) destination.retriever_info(retriever) nummsgs = len(retriever) fmtlen = len(str(nummsgs)) for (msgnum, msgid) in enumerate(retriever): msgnum += 1 size = retriever.getmsgsize(msgid) log.info(' msg %*d/%*d (%d bytes) ...' % (fmtlen, msgnum, fmtlen, nummsgs, size)) try: msg = retriever.getmsg(msgid) msgs_retrieved += 1 destination.deliver_message(msg, False, False) log.info(' delivered') retriever.delivered(msgid) if options.delete: if retriever.delmsg(msgid): log.info(', deleted') log.info('\n') except getmailDeliveryError as o: error_exit(7, 'Delivery error: %s' % o) try: retriever.quit() except getmailOperationError as o: log.warning('Operation error during quit (%s)\n' % o) except socket.timeout as o: error_exit(8, 'Timeout error: %s' % o) except socket.gaierror as o: error_exit(9, 'gaierror: %s' % o) except socket.error as o: error_exit(10, 'Socket error: %s' % o) except poplib.error_proto as o: error_exit(11, 'Protocol error: %s' % o) except getmailCredentialError as o: error_exit(13, 'Credential error: %s' % o) except getmailOperationError as o: error_exit(12, 'Operational error: %s' % o) log.info('%d messages retrieved\n' % msgs_retrieved) ####################################### def main(): try: parser = OptionParser( version='%%prog %s' % __version__, usage='%prog [options] server username password destination' ) parser.add_option('-q', '--quiet', action='store_false', dest='verbose', help='output only on error') parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=True, help='output informational messages ' '(default: verbose)') parser.add_option('-m', '--message', action='store', dest='message', help='deliver message from FILE first', metavar='FILE') parser.add_option('-p', '--port', action='store', type='int', dest='port', metavar='PORT', default=None, help='use server port PORT (default: POP: 110, ' 'POP3-over-SSL: 995)') parser.add_option('-d', '--delete', action='store_true', dest='delete', default=False, help='delete messages after retrieval (default: no)') parser.add_option('-t', '--timeout', action='store', type='int', dest='timeout', metavar='SECS', default=180, help='use timeout SECS (default: 180)') parser.add_option('-a', '--apop', action='store_true', dest='apop', default=False, help='use APOP authentication (default: no)') parser.add_option('-s', '--ssl', action='store_true', dest='ssl', default=False, help='use POP3-over-SSL (default: no)') (options, args) = parser.parse_args(sys.argv[1:]) if len(args) != 4: raise getmailOperationError('incorrect arguments; try --help' % args) def get(self, key, value=None): return getattr(self, key, value) try: options.get = types.MethodType(get, options, options.__class__) except TypeError: # py3 options.get = types.MethodType(get, options) msg = None if options.message is not None: try: f = open(options.message) msg = message.Message(fromfile=f) except IOError as o: error_exit( 1, 'Error reading message file "%s": %s' % (options.message, o) ) instance = dummyInstance() # Retriever if options.ssl: retriever_func = retrievers.BrokenUIDLPOP3SSLRetriever port = options.port or 995 else: retriever_func = retrievers.BrokenUIDLPOP3Retriever port = options.port or 110 retriever_args = { 'getmaildir' : os.getcwd(), 'configparser' : instance, 'timeout' : options.timeout, 'server' : args[0], 'port' : port, 'username' : args[1], 'password' : args[2], 'use_apop' : options.apop } try: retriever = retriever_func(**retriever_args) retriever.checkconf() except getmailOperationError as o: error_exit(3, 'Error initializing retriever: %s' % o) # Destination destination = args[3].strip() if destination.startswith('.') or destination.startswith('/'): if destination.endswith('/'): destination_func = destinations.Maildir destination_args = { 'path' : destination, 'configparser' : instance, } else: destination_func = destinations.Mboxrd destination_args = { 'path' : destination, 'configparser' : instance, } elif destination.startswith('|'): destination_func = destinations.MDA_external parts = destination[1:].split() cmd = parts[0] arguments = str(tuple(parts[1:])) destination_args = { 'path' : cmd, 'arguments' : arguments, 'allow_root_commands' : False, 'configparser' : instance, } else: error_exit(4, 'Unknown destination type "%s"' % destination) try: destination = destination_func(**destination_args) except getmailOperationError as o: error_exit( 5, 'Error initializing destination "%s": %s' % (destination, o) ) go(retriever, destination, options, msg) except KeyboardInterrupt: error_exit(6, 'Operation aborted by user (keyboard interrupt)') except getmailOperationError as o: error_exit(7, 'Operation error: %s' % o) except getmailConfigurationError as o: error_exit(8, 'Configuration error: %s' % o) except Exception as o: log.critical('\nException: please read docs/BUGS and include the ' 'following information in any bug report:\n\n') log.critical(' getmail_fetch version %s\n' % __version__) log.critical(' Python version %s\n\n' % sys.version) log.critical('Unhandled exception follows:\n') exc_type, value, tb = sys.exc_info() import traceback tblist = (traceback.format_tb(tb, None) + traceback.format_exception_only(exc_type, value)) if type(tblist) != list: tblist = [tblist] for line in tblist: log.critical(' %s\n' % line.rstrip()) sys.exit(9) ####################################### if __name__ == '__main__': main() getmail6-6.19.07/getmail_maildir000077500000000000000000000056411474536475600165120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''getmail_maildir Reads a message from stdin and delivers it to a maildir specified as a commandline argument. Expects the envelope sender address to be in the environment variable SENDER. Copyright (C) 2001-2025 Charles Cazabon and others This program is free software; you can redistribute it and/or modify it under the terms of version 2 (only) of the GNU General Public License as published by the Free Software Foundation. A copy of this license should be included in the file COPYING. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ''' import sys import os from getmailcore.message import Message from getmailcore.utilities import * from getmailcore.exceptions import * def main(): hostname = localhostname() verbose = False path = None for arg in sys.argv[1:]: if arg in ('-h', '--help'): sys.stdout.write('Usage: %s maildirpath\n' % sys.argv[0]) raise SystemExit elif arg in ('-v', '--verbose'): verbose = True elif not path: path = arg else: raise SystemExit('Error: maildir path specified twice (was %s, now %s)' % (path, arg)) if os.name == 'posix' and (os.geteuid() == 0 or os.getegid() == 0): raise SystemExit('Error: do not run this program as user root') if not (path and (path.startswith('.') or path.startswith('/')) and path.endswith('/')): raise SystemExit('Error: maildir must start with . or / and end with /') if not is_maildir(path): raise SystemExit('Error: %s is not a maildir' % path) try: msg = Message(fromfile=sys.stdin.buffer) except AttributeError: #py2 msg = Message(fromfile=sys.stdin) if 'SENDER' in os.environ: msg.sender = os.environ['SENDER'] if 'RECIPIENT' in os.environ: msg.recipient = os.environ['RECIPIENT'] try: d = deliver_maildir(path, msg.flatten(True, False), hostname) except getmailDeliveryError as o: raise SystemExit('Error: delivery error delivering to maildir %s (%s)' % (path, o)) except Exception as o: raise SystemExit('Error: other error delivering to maildir %s (%s)' % (path, o)) if verbose: sys.stdout.write('Delivered to maildir %s\n' % path) if __name__ == "__main__": main() getmail6-6.19.07/getmail_mbox000077500000000000000000000061751474536475600160410ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''getmail_mbox Reads a message from stdin and delivers it to an mbox file specified as a commandline argument. Expects the envelope sender address to be in the environment variable SENDER. Copyright (C) 2001-2025 Charles Cazabon and others. This program is free software; you can redistribute it and/or modify it under the terms of version 2 (only) of the GNU General Public License as published by the Free Software Foundation. A copy of this license should be included in the file COPYING. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ''' import sys import os import email from getmailcore.exceptions import * from getmailcore.message import Message from getmailcore import logging, constants, destinations def main(): verbose = False path = None for arg in sys.argv[1:]: if arg in ('-h', '--help'): sys.stdout.write('Usage: %s mboxpath\n' % sys.argv[0]) raise SystemExit elif arg in ('-v', '--verbose'): verbose = True elif not path: path = arg else: raise SystemExit('Error: mbox path specified twice (was %s, now %s)' % (path, arg)) if os.name == 'posix' and (os.geteuid() == 0 or os.getegid() == 0): raise SystemExit('Error: do not run this program as user root') logger = logging.Logger() logger.addhandler(sys.stderr, constants.WARNING) if verbose: logger.addhandler(sys.stdout, constants.INFO, constants.INFO) if not (path and (path.startswith('.') or path.startswith('/')) and not path.endswith('/')): raise SystemExit('Error: mbox must start with . or / and not end with /') if os.path.exists(path) and not os.path.isfile(path): raise SystemExit('Error: %s is not an mbox' % path) try: msg = Message(fromfile=sys.stdin.buffer) except AttributeError: #py2 msg = Message(fromfile=sys.stdin) if 'SENDER' in os.environ: msg.sender = os.environ['SENDER'] if 'RECIPIENT' in os.environ: msg.recipient = os.environ['RECIPIENT'] try: dest = destinations.Mboxrd(path=path) d = dest.deliver_message(msg, True, False) except getmailDeliveryError as o: raise SystemExit('Error: delivery error delivering to mboxrd %s (%s)' % (path, o)) except Exception as o: raise SystemExit('Error: other error delivering to mboxrd %s (%s)' % (path, o)) if verbose: sys.stdout.write('Delivered to mboxrd %s\n' % path) if __name__ == "__main__": main() getmail6-6.19.07/getmailcore/000077500000000000000000000000001474536475600157265ustar00rootroot00000000000000getmail6-6.19.07/getmailcore/__init__.py000077500000000000000000000022271474536475600200450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''A reliable mail-retriever toolkit. getmail is a reliable, modular, extensible mail retriever with support for simple and multidrop POP3 mailboxes, multidrop SDPS mailboxes, simple and multidrop IMAP mailboxes. Also supports POP3- and IMAP-over-SSL, message filtering, and other features. getmail is Copyright (C) 1998-2025 Charles Cazabon. All rights reserved. Distributed under the terms of the GNU General Public License version 2 (only). You should have received a copy of the license in the file COPYING. ''' import sys __version__ = '6.19.07' __license__ = 'GPL-2.0' __py_required__ = '2.7.16' __py_required_hex__ = 0x20710f0 if sys.hexversion < __py_required_hex__: raise ImportError('getmail version %s requires Python version %s ' 'or later'%(__version__,__py_required__)) __all__ = [ 'baseclasses', 'constants', 'destinations', 'exceptions', 'filters', 'imap_utf7', 'logging', 'message', 'retrievers', 'utilities', ] getmail6-6.19.07/getmailcore/_retrieverbases.py000077500000000000000000002336561474536475600215060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''Base and mix-in classes implementing retrievers (message sources getmail can retrieve mail from). None of these classes can be instantiated directly. In this module: Mix-in classes for SSL/non-SSL initialization: POP3initMixIn POP3SSLinitMixIn IMAPinitMixIn IMAPSSLinitMixIn Base classes: RetrieverSkeleton POP3RetrieverBase MultidropPOP3RetrieverBase IMAPRetrieverBase MultidropIMAPRetrieverBase ''' import sys import os import socket from socket import _socket from ssl import CertificateError import time import email import codecs try: from email.Header import decode_header import email.Parser as Parser except ImportError: from email.header import decode_header import email.parser as Parser import poplib import imaplib import re import select import base64 try: # do we have a recent pykerberos? HAVE_KERBEROS_GSS = False import kerberos if 'authGSSClientWrap' in dir(kerberos): HAVE_KERBEROS_GSS = True except ImportError: pass ### # Monkey-patch imaplib to support the ID command ([RFC 2971](https://www.rfc-editor.org/rfc/rfc2971)) - begin ### imaplib.Commands['ID'] = ('AUTH', 'SELECTED') def imap_id_rfc2971(self, *kv_pairs, **kw): """(typ, [data]) = .id(kv_pairs) 'kv_pairs' is a possibly empty list of keys and values. 'data' is a list of ID key value pairs or NIL. NB: a single argument is assumed to be correctly formatted and is passed through unchanged (for backward compatibility with earlier version). Exchange information for problem analysis and determination. The ID extension is defined in RFC 2971. """ name = 'ID' if not kv_pairs: data = 'NIL' elif len(kv_pairs) == 1: data = kv_pairs[0] # Assume invoker passing correctly formatted string (back-compat) else: data = '(%s)' % ' '.join([(arg and self._quote(arg) or 'NIL') for arg in kv_pairs]) return self._simple_command(name, data, **kw) imaplib.IMAP4.id = imap_id_rfc2971 ### # Monkey-patch imaplib to support the ID command (RFC 2971) - end ### try: import ssl SSLError = ssl.SSLError except: ssl = None SSLError = Exception import hashlib __all__ = [ 'IMAPinitMixIn', 'IMAPRetrieverBase', 'IMAPSSLinitMixIn', 'MultidropIMAPRetrieverBase', 'MultidropPOP3RetrieverBase', 'POP3_SSL_PORT', 'POP3initMixIn', 'POP3RetrieverBase', 'POP3SSLinitMixIn', 'RetrieverSkeleton', ] tocode = lambda x: isinstance(x,bytes) and x or x.encode() # If we have an ssl module: has_sni = ssl and getattr(ssl, 'HAS_SNI', False) proto_best = ssl and getattr(ssl, 'PROTOCOL_TLS', None) if not proto_best: proto_best = ssl and getattr(ssl, 'PROTOCOL_SSLv23', None) has_ciphers = sys.hexversion >= 0x2070000 # Monkey-patch SNI use into SSL.wrap_socket() if supported def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=proto_best, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None, server_hostname=None): if server_side and not certfile: raise ValueError("certfile must be specified for server-side " "operations") if keyfile and not certfile: raise ValueError("certfile must be specified") context = ssl.SSLContext(ssl_version) context.verify_mode = cert_reqs if ca_certs: context.load_verify_locations(ca_certs) if certfile: context.load_cert_chain(certfile, keyfile) if ciphers: context.set_ciphers(ciphers) return context.wrap_socket( sock=sock, server_side=server_side, do_handshake_on_connect=do_handshake_on_connect, suppress_ragged_eofs=suppress_ragged_eofs, server_hostname=has_sni and server_hostname or None ) if ssl: ssl.wrap_socket = wrap_socket ############################################## # to avoid deprecation warnings # match_hostname from Python 3.10 ssl.py # starting here down to ssl_match_hostname_END ############################################## def _dnsname_match(dn, hostname): """Matching according to RFC 6125, section 6.4.3 - Hostnames are compared lower case. - For IDNA, both dn and hostname must be encoded as IDN A-label (ACE). - Partial wildcards like 'www*.example.org', multiple wildcards, sole wildcard or wildcards in labels other then the left-most label are not supported and a CertificateError is raised. - A wildcard must match at least one character. """ if not dn: return False wildcards = dn.count('*') # speed up common case w/o wildcards if not wildcards: return dn.lower() == hostname.lower() if wildcards > 1: raise CertificateError( "too many wildcards in certificate DNS name: {!r}.".format(dn)) dn_leftmost, sep, dn_remainder = dn.partition('.') if '*' in dn_remainder: # Only match wildcard in leftmost segment. raise CertificateError( "wildcard can only be present in the leftmost label: " "{!r}.".format(dn)) if not sep: # no right side raise CertificateError( "sole wildcard without additional labels are not support: " "{!r}.".format(dn)) if dn_leftmost != '*': # no partial wildcard matching raise CertificateError( "partial wildcards in leftmost label are not supported: " "{!r}.".format(dn)) hostname_leftmost, sep, hostname_remainder = hostname.partition('.') if not hostname_leftmost or not sep: # wildcard must match at least one char return False return dn_remainder.lower() == hostname_remainder.lower() def _inet_paton(ipname): """Try to convert an IP address to packed binary form Supports IPv4 addresses on all platforms and IPv6 on platforms with IPv6 support. """ # inet_aton() also accepts strings like '1', '127.1', some also trailing # data like '127.0.0.1 whatever'. try: addr = _socket.inet_aton(ipname) except OSError: # not an IPv4 address pass else: if _socket.inet_ntoa(addr) == ipname: # only accept injective ipnames return addr else: # refuse for short IPv4 notation and additional trailing data raise ValueError( "{!r} is not a quad-dotted IPv4 address.".format(ipname) ) try: return _socket.inet_pton(_socket.AF_INET6, ipname) except OSError: raise ValueError("{!r} is neither an IPv4 nor an IP6 " "address.".format(ipname)) except AttributeError: # AF_INET6 not available pass raise ValueError("{!r} is not an IPv4 address.".format(ipname)) def _ipaddress_match(cert_ipaddress, host_ip): """Exact matching of IP addresses. RFC 6125 explicitly doesn't define an algorithm for this (section 1.7.2 - "Out of Scope"). """ # OpenSSL may add a trailing newline to a subjectAltName's IP address, # commonly with IPv6 addresses. Strip off trailing \n. ip = _inet_paton(cert_ipaddress.rstrip()) return ip == host_ip def match_hostname(cert, hostname): """Verify that *cert* (in decoded format as returned by SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 rules are followed. The function matches IP addresses rather than dNSNames if hostname is a valid ipaddress string. IPv4 addresses are supported on all platforms. IPv6 addresses are supported on platforms with IPv6 support (AF_INET6 and inet_pton). CertificateError is raised on failure. On success, the function returns nothing. """ # warnings.warn( # "ssl.match_hostname() is deprecated", # category=DeprecationWarning, # stacklevel=2 # ) if not cert: raise ValueError("empty or no certificate, match_hostname needs a " "SSL socket or SSL context with either " "CERT_OPTIONAL or CERT_REQUIRED") try: host_ip = _inet_paton(hostname) except ValueError: # Not an IP address (common case) host_ip = None dnsnames = [] san = cert.get('subjectAltName', ()) for key, value in san: if key == 'DNS': if host_ip is None and _dnsname_match(value, hostname): return dnsnames.append(value) elif key == 'IP Address': if host_ip is not None and _ipaddress_match(value, host_ip): return dnsnames.append(value) if not dnsnames: # The subject is only checked when there is no dNSName entry # in subjectAltName for sub in cert.get('subject', ()): for key, value in sub: # XXX according to RFC 2818, the most specific Common Name # must be used. if key == 'commonName': if _dnsname_match(value, hostname): return dnsnames.append(value) if len(dnsnames) > 1: raise CertificateError("hostname %r " "doesn't match either of %s" % (hostname, ', '.join(map(repr, dnsnames)))) elif len(dnsnames) == 1: raise CertificateError("hostname %r " "doesn't match %r" % (hostname, dnsnames[0])) else: raise CertificateError("no appropriate commonName or " "subjectAltName fields were found") def ssl_match_hostname(cert, hostname): try: return match_hostname(cert, hostname) except (ValueError, CertificateError) as e: raise getmailOperationError("%s"%e) ############################################## # match_hostname from Python 3.10 ssl.py # ssl_match_hostname_END ############################################## from getmailcore.exceptions import * from getmailcore.constants import * from getmailcore.message import * from getmailcore.utilities import * from getmailcore.baseclasses import * import getmailcore.imap_utf7 # registers imap4-utf-7 codec NOT_ENVELOPE_RECIPIENT_HEADERS = ( 'to', 'cc', 'bcc', 'received', 'resent-to', 'resent-cc', 'resent-bcc' ) # How long a vanished message is kept in the oldmail state file for IMAP # retrievers before we figure it's gone for good. This is to allow users # to only occasionally retrieve mail from certain IMAP folders without # losing their oldmail state for that folder. This is in seconds, so it's # 30 days. VANISHED_AGE = (60 * 60 * 24 * 30) # Regex used to remove problematic characters from oldmail filenames STRIP_CHAR_RE = r'[/\:;<>|]+' # Kerberos authentication state constants (GSS_STATE_STEP, GSS_STATE_WRAP) = (0, 1) # For matching imap LIST responses IMAP_LISTPARTS = re.compile( r'^\s*' r'\((?P[^)]*)\)' r'\s+' r'"(?P[^"]+)"' r'\s+' # I *think* this should actually be a double-quoted string "like/this" # but in testing we saw an MSexChange response that violated that # expectation: # (\HasNoChildren) "/" Calendar" # i.e. the leading quote on the mailbox name was missing. The following # works for both by treating the leading/trailing double-quote as optional, # even when mismatched. r'("?)(?P.+?)("?)' r'\s*$' ) def mailbox_names(resplist): mailboxes = [] for item in resplist: m = IMAP_LISTPARTS.match(item) if not m: raise getmailOperationError( 'no match for list response "%s"' % item ) g = m.groupdict() attributes = g['attributes'].split() if r'\Noselect' in attributes: # Can't select this mailbox, don't include it in output continue mailboxes.append(g['mailbox']) return mailboxes IMAP_ATOM_SPECIAL=re.compile(r'[\x00-\x1F\(\)\{ %\*"\\\]]') # Constants used in socket module NO_OBJ = object() EAI_NONAME = getattr(socket, 'EAI_NONAME', NO_OBJ) EAI_NODATA = getattr(socket, 'EAI_NODATA', NO_OBJ) EAI_FAIL = getattr(socket, 'EAI_FAIL', NO_OBJ) # Constant for POPSSL POP3_SSL_PORT = 995 # Python added poplib._MAXLINE somewhere along the way. As far as I can # see, it serves no purpose except to introduce bugs into any software # using poplib. Any computer running Python will have at least some megabytes # of userspace memory; arbitrarily causing message retrieval to break if any # "line" exceeds 2048 bytes is absolutely stupid. poplib._MAXLINE = 1 << 20 # 1MB; decrease this if you're running on a VIC-20 ####################################### class CertMixIn(object): def ssl_cipher_hash(self): if sys.version_info.major == 2: sslobj = self.conn.sslobj else: sslobj = self.conn.sock self.setup_received(sslobj) peercert = sslobj.getpeercert(True) ssl_cipher = sslobj.cipher() if ssl_cipher: ssl_cipher = '%s:%s:%s' % ssl_cipher if not peercert: actual_hash = None else: actual_hash = hashlib.sha256(peercert).hexdigest().lower() # Ensure cert is for server we're connecting to if ssl and self.conf['ca_certs']: ssl_match_hostname( sslobj.getpeercert(), self.conf.get('ssl_cert_hostname', None) or self.conf['server'] ) ssl_fingerprints = check_ssl_fingerprints(self.conf) if ssl_fingerprints: any_matches = False for expected_hash in ssl_fingerprints: if expected_hash == actual_hash: any_matches = True if not any_matches: raise getmailOperationError( 'socket ssl_fingerprints mismatch (got %s)' % actual_hash ) return ssl_cipher, actual_hash # # Mix-in classes # ####################################### class POP3initMixIn(object): '''Mix-In class to do POP3 non-SSL initialization. ''' SSL = False def _connect(self): self.log.trace() try: self.conn = poplib.POP3(self.conf['server'], self.conf['port']) self.setup_received(self.conn.sock) except poplib.error_proto as o: raise getmailOperationError('POP error (%s)' % o) except socket.timeout: raise #raise getmailOperationError('timeout during connect') except socket.gaierror as o: raise getmailOperationError( 'error resolving name %s during connect (%s)' % (self.conf['server'], o) ) self.log.trace('POP3 connection %s established' % self.conn + os.linesep) ####################################### class POP3_SSL_EXTENDED(poplib.POP3_SSL): # Extended SSL support for POP3 (certificate checking, # fingerprint matching, cipher selection, etc.) def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None, ssl_version=None, ca_certs=None, ssl_ciphers=None): self.host = host self.port = port self.keyfile = keyfile self.certfile = certfile self.ssl_version = ssl_version self.ca_certs = ca_certs self.ssl_ciphers = ssl_ciphers self.buffer = '' msg = "getaddrinfo returns an empty list" self.sock = None for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): (af, socktype, proto, canonname, sa) = res try: self.sock = socket.socket(af, socktype, proto) self.sock.connect(sa) except socket.error as e: msg = e if self.sock: self.sock.close() self.sock = None continue break if not self.sock: if isinstance(msg, Exception): raise msg else: raise socket.error(msg) extra_args = { 'server_hostname': host } if self.ssl_version: extra_args['ssl_version'] = self.ssl_version if self.ca_certs: extra_args['cert_reqs'] = ssl and ssl.CERT_REQUIRED extra_args['ca_certs'] = self.ca_certs if self.ssl_ciphers: extra_args['ciphers'] = self.ssl_ciphers if ssl: self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, **extra_args) self.file = self.sock.makefile('rb') self._debugging = 0 self.welcome = self._getresp() ####################################### class POP3SSLinitMixIn(CertMixIn): '''Mix-In class to do POP3 over SSL with the poplib.POP3_SSL class. ''' SSL = True def _connect(self): self.log.trace() (keyfile, certfile) = check_ssl_key_and_cert(self.conf) ca_certs = check_ca_certs(self.conf) ssl_version = check_ssl_version(self.conf) ssl_ciphers = check_ssl_ciphers(self.conf) using_extended_certs_interface = False try: if ca_certs or ssl_version or ssl_ciphers: using_extended_certs_interface = True msg = '' if keyfile: msg += 'with keyfile %s, certfile %s' % (keyfile, certfile) if ssl_version: if msg: msg += ', ' msg += ('using protocol version %s' % self.conf['ssl_version'].upper()) if ca_certs: if msg: msg += ', ' msg += 'with ca_certs %s' % ca_certs self.log.trace( 'establishing POP3 SSL connection to %s:%d %s' % (self.conf['server'], self.conf['port'], msg) + os.linesep ) self.conn = POP3_SSL_EXTENDED( self.conf['server'], self.conf['port'], keyfile, certfile, ssl_version, ca_certs, ssl_ciphers ) elif keyfile: self.log.trace( 'establishing POP3 SSL connection to %s:%d with ' 'keyfile %s, certfile %s' % (self.conf['server'], self.conf['port'], keyfile, certfile) + os.linesep ) self.conn = poplib.POP3_SSL( self.conf['server'], self.conf['port'], keyfile, certfile ) else: self.log.trace('establishing POP3 SSL connection to %s:%d' % (self.conf['server'], self.conf['port']) + os.linesep) self.conn = poplib.POP3_SSL(self.conf['server'], self.conf['port']) ssl_cipher, actual_hash = self.ssl_cipher_hash() except poplib.error_proto as o: raise getmailOperationError('POP error (%s)' % o) except socket.timeout: #raise getmailOperationError('timeout during connect') raise except socket.gaierror as o: raise getmailOperationError( 'error resolving name %s during connect (%s)' % (self.conf['server'], o) ) fingerprint_message = ('POP3 SSL connection %s established' % self.conn) fingerprint_message += ' with fingerprint %s' % actual_hash fingerprint_message += ' using cipher %s' % ssl_cipher fingerprint_message += os.linesep if self.app_options.get('fingerprint', False): self.log.info(fingerprint_message) else: self.log.trace(fingerprint_message) ####################################### class IMAPinitMixIn(object): '''Mix-In class to do IMAP non-SSL initialization. ''' SSL = False def _connect(self): self.log.trace() try: self.conn = imaplib.IMAP4(self.conf['server'], self.conf['port']) self.setup_received(self.conn.sock) except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) except socket.timeout: #raise getmailOperationError('timeout during connect') raise except socket.gaierror as o: raise getmailOperationError('socket error during connect (%s)' % o) self.log.trace('IMAP connection %s established' % self.conn + os.linesep) ####################################### class IMAP4_SSL_EXTENDED(imaplib.IMAP4_SSL): # Similar to above, but with extended support for SSL certificate checking, # fingerprints, etc. def __init__(self, host='', port=imaplib.IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_version=None, ca_certs=None, ssl_ciphers=None): self.ssl_version = ssl_version self.ca_certs = ca_certs self.ssl_ciphers = ssl_ciphers self.keyfile = keyfile self.certfile = certfile try: imaplib.IMAP4_SSL.__init__(self, host, port, keyfile, certfile) except TypeError: imaplib.IMAP4_SSL.__init__(self, host, port) def open(self, host='', port=imaplib.IMAP4_SSL_PORT, timeout=None): self.host = host self.port = port self.sock = socket.create_connection((host, port)) # Note timeout is available in python 3.9 in native imaplib's/ssl's open, # but wrap_socket does not support it. Keep it for the future. self.timeout = timeout extra_args = { 'server_hostname': host } if self.ssl_version: extra_args['ssl_version'] = self.ssl_version if self.ca_certs: extra_args['cert_reqs'] = ssl and ssl.CERT_REQUIRED extra_args['ca_certs'] = self.ca_certs if self.ssl_ciphers: extra_args['ciphers'] = self.ssl_ciphers if ssl: self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, **extra_args) self.file = self.sock.makefile('rb') ####################################### class IMAPSSLinitMixIn(CertMixIn): '''Mix-In class to do IMAP over SSL. ''' SSL = True def _connect(self): self.log.trace() (keyfile, certfile) = check_ssl_key_and_cert(self.conf) ca_certs = check_ca_certs(self.conf) ssl_version = check_ssl_version(self.conf) ssl_ciphers = check_ssl_ciphers(self.conf) using_extended_certs_interface = False try: if ca_certs or ssl_version or ssl_ciphers: using_extended_certs_interface = True msg = '' if keyfile: msg += 'with keyfile %s, certfile %s' % (keyfile, certfile) if ssl_version: if msg: msg += ', ' msg += ('using protocol version %s' % self.conf['ssl_version'].upper()) if ca_certs: if msg: msg += ', ' msg += 'with ca_certs %s' % ca_certs self.log.trace( 'establishing IMAP SSL connection to %s:%d %s' % (self.conf['server'], self.conf['port'], msg) + os.linesep ) self.conn = IMAP4_SSL_EXTENDED( self.conf['server'], self.conf['port'], keyfile, certfile, ssl_version, ca_certs, ssl_ciphers ) elif keyfile: self.log.trace( 'establishing IMAP SSL connection to %s:%d with keyfile ' '%s, certfile %s' % (self.conf['server'], self.conf['port'], keyfile, certfile) + os.linesep ) self.conn = IMAP4_SSL_EXTENDED( self.conf['server'], self.conf['port'], keyfile, certfile ) else: self.log.trace( 'establishing IMAP SSL connection to %s:%d' % (self.conf['server'], self.conf['port']) + os.linesep ) self.conn = imaplib.IMAP4_SSL(self.conf['server'], self.conf['port']) ssl_cipher, actual_hash = self.ssl_cipher_hash() except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) except socket.timeout: #raise getmailOperationError('timeout during connect') raise except socket.gaierror as o: try: errcode = o[0] except TypeError: errcode = o.args[0] if errcode in (EAI_NONAME, EAI_NODATA): # No such DNS name raise getmailDnsLookupError( 'no address for %s (%s)' % (self.conf['server'], o) ) elif errcode == EAI_FAIL: # DNS server failure raise getmailDnsServerFailure( 'DNS server failure looking up address for %s (%s)' % (self.conf['server'], o) ) else: raise getmailOperationError('socket error during connect (%s)' % o) except SSLError as o: raise getmailOperationError( (ssl and 'SSLError' or 'Error')+' during connect (%s)' % o ) fingerprint_message = ('IMAP SSL connection %s established' % self.conn) fingerprint_message += ' with fingerprint %s' % actual_hash fingerprint_message += ' using cipher %s' % ssl_cipher fingerprint_message += os.linesep if self.app_options['fingerprint']: self.log.info(fingerprint_message) else: self.log.trace(fingerprint_message) # # Base classes # ####################################### class RetrieverSkeleton(ConfigurableBase): '''Base class for implementing message-retrieval classes. Sub-classes should provide the following data attributes and methods: _confitems - a tuple of dictionaries representing the parameters the class takes. Each dictionary should contain the following key, value pairs: - name - parameter name - type - a type function to compare the parameter value against (i.e. str, int, bool) - default - optional default value. If not present, the parameter is required. __str__(self) - return a simple string representing the class instance. _getmsglist(self) - retrieve a list of all available messages, and store unique message identifiers in the dict self.msgnum_by_msgid. Message identifiers must be unique and persistent across instantiations. Also store message sizes (in octets) in a dictionary self.msgsizes, using the message identifiers as keys. _flagmsgbyid(self, msgid) - On default flag message msgid for deletion. For IMAP imap_on_delete allows other flag: return True if `Deleted` is among the flags. _getmsgbyid(self, msgid) - retrieve and return a message from the message store based on its message identifier. The message is returned as a Message() class object. The message will have additional data attributes "sender" and "recipient". sender should be present or "unknown". recipient should be non-None if (and only if) the protocol/method of message retrieval preserves the original message envelope. _getheaderbyid(self, msgid) - similar to _getmsgbyid() above, but only the message header should be retrieved, if possible. It should be returned in the same format. showconf(self) - should invoke self.log.info() to display the configuration of the class instance. Sub-classes may also wish to extend or over-ride the following base class methods: __init__(self, **args) __del__(self) initialize(self, options) checkconf(self) ''' def __init__(self, **args): self.headercache = {} self.deleted = {} self.set_new_timestamp() self.__oldmail_written = False self.__initialized = False self.gotmsglist = False self._clear_state() self.conn = None self.supports_idle = False self.supports_id = False ConfigurableBase.__init__(self, **args) def set_new_timestamp(self): self.timestamp = int(time.time()) def _clear_state(self): self.msgnum_by_msgid = {} self.msgid_by_msgnum = {} self.sorted_msgnum_msgid = () self.msgsizes = {} self.oldmail = {} self.__delivered = {} self.deleted = {} self.mailbox_selected = False def setup_received(self, sock): serveraddr = sock.getpeername() if len(serveraddr) == 2: # IPv4 self.remoteaddr = '%s:%s' % serveraddr elif len(serveraddr) == 4: # IPv6 self.remoteaddr = '[%s]:%s' % serveraddr[:2] else: # Shouldn't happen self.log.warning('unexpected peer address format %s' % str(serveraddr)) self.remoteaddr = str(serveraddr) self.received_from = '%s (%s)' % (self.conf['server'], self.remoteaddr) def __str__(self): self.log.trace() return str(self.conf) def list_mailboxes(self): raise NotImplementedError('virtual') def select_mailbox(self, mailbox): raise NotImplementedError('virtual') def __len__(self): self.log.trace() return len(self.msgnum_by_msgid) def __getitem__(self, i): self.log.trace('i == %d' % i) if not self.__initialized: raise getmailOperationError('not initialized') return self.sorted_msgnum_msgid[i][1] def _oldmail_filename(self, mailbox): assert (mailbox is None or (isinstance(mailbox, (unicode, bytes)))), ( 'bad mailbox %s (%s)' % (mailbox, type(mailbox)) ) filename = self.oldmail_filename if mailbox is not None: mailbox = re.sub(STRIP_CHAR_RE, '.', mailbox) # Use oldmail file per IMAP folder filename += '-' + mailbox # else: # mailbox is None, is POP, just use filename return filename def oldmail_exists(self, mailbox): '''Test whether an oldmail file exists for a specified mailbox.''' return os.path.isfile(self._oldmail_filename(mailbox)) def read_oldmailfile(self, mailbox): '''Read contents of an oldmail file. For POP, mailbox must be explicitly None. ''' assert not self.oldmail, ( 'still have %d unflushed oldmail' % len(self.oldmail) ) self.log.trace('mailbox=%s' % mailbox) filename = self._oldmail_filename(mailbox) logname = '%s:%s' % (self, mailbox or '') try: f = open(filename) except IOError: self.log.moreinfo('no oldmail file for %s%s' % (logname, os.linesep)) return for line in f: line = line.strip() if not line or not '\0' in line: # malformed continue try: (msgid, timestamp) = line.split('\0', 1) if msgid.count('/') == 2: # Was pre-4.22.0 file format, which includes the # mailbox name in the msgid, in the format # 'uidvalidity/mailbox/serveruid'. # Strip it out. fields = msgid.split('/') msgid = '/'.join([fields[0], fields[2]]) self.oldmail[msgid] = int(timestamp) except ValueError: # malformed self.log.info( 'skipped malformed line "%r" for %s%s' % (line, logname, os.linesep) ) f.close() self.log.moreinfo( 'read %i uids for %s%s' % (len(self.oldmail), logname, os.linesep) ) self.log.moreinfo('read %i uids in total for %s%s' % (len(self.oldmail), logname, os.linesep)) def write_oldmailfile(self, mailbox): '''Write oldmail info to oldmail file.''' self.log.trace('mailbox=%s' % mailbox) filename = self._oldmail_filename(mailbox) logname = '%s:%s' % (self, mailbox or '') oldmailfile = None wrote = 0 msgids = frozenset( self.__delivered.keys() ).union(frozenset(self.oldmail.keys())) try: oldmailfile = updatefile(filename) for msgid in msgids: self.log.debug('msgid %s ...' % msgid) t = self.oldmail.get(msgid, self.timestamp) self.log.debug(' timestamp %s' % t + os.linesep) oldmailfile.write('%s\0%i%s' % (msgid, t, os.linesep)) self.oldmail[msgid] = t wrote += 1 oldmailfile.close() self.log.moreinfo('wrote %i uids for %s%s' % (wrote, logname, os.linesep)) except IOError as o: self.log.error('failed writing oldmail file for %s (%s)' % (logname, o) + os.linesep) if oldmailfile: oldmailfile.abort() self.__oldmail_written = True def initialize(self, options): # Options - dict of application-wide settings, including ones that # aren't used in initializing the retriever. self.log.trace() self.checkconf() if 'timeout' in self.conf: socket.setdefaulttimeout(self.conf['timeout']) else: # Explicitly set to None in case it was previously set socket.setdefaulttimeout(None) # Construct base filename for oldmail files. # strip problematic characters from oldmail filename. Mostly for # non-Unix systems; only / is illegal in a Unix path component oldmail_filename = re.sub( STRIP_CHAR_RE, '-', 'oldmail-%(server)s-%(port)i-%(username)s' % self.conf ) self.oldmail_filename = os.path.join(self.conf['getmaildir'], oldmail_filename) self.received_from = None self.app_options = options self.__initialized = True def quit(self): if self.mailbox_selected is not False: self.write_oldmailfile(self.mailbox_selected) self._clear_state() def abort(self): '''On error conditions where you do not want modified state to be saved, call this before .quit(). ''' self._clear_state() def delivered(self, msgid): self.__delivered[msgid] = None if self.app_options.get('to_oldmail_on_each_mail',False): self.write_oldmailfile(self.mailbox_selected) self.__delivered = {} def getheader(self, msgid): if not self.__initialized: raise getmailOperationError('not initialized') if not msgid in self.headercache: self.headercache[msgid] = self._getheaderbyid(msgid) return self.headercache[msgid] def getmsg(self, msgid): if not self.__initialized: raise getmailOperationError('not initialized') return self._getmsgbyid(msgid) def getmsgsize(self, msgid): if not self.__initialized: raise getmailOperationError('not initialized') try: return self.msgsizes[msgid] except KeyError: raise getmailOperationError('no such message ID %s' % msgid) def delmsg(self, msgid): if not self.__initialized: raise getmailOperationError('not initialized') deleted = self._flagmsgbyid(msgid) self.deleted[msgid] = deleted return deleted def run_password_command(self): command = self.conf['password_command'][0] args = self.conf['password_command'][1:] rc, stdout, stderr = run_command(command,args) if rc: raise getmailOperationError( 'External program error (%s exited with %d)' % (command,rc) ) if stderr: self.log.warning( 'External password program "%s" wrote to stderr: %s' % (command, stderr) ) password = stdout self.conf['password'] = password return password ####################################### class POP3RetrieverBase(RetrieverSkeleton): '''Base class for single-user POP3 mailboxes. ''' def __init__(self, **args): RetrieverSkeleton.__init__(self, **args) self.log.trace() def select_mailbox(self, mailbox): assert mailbox is None, ( 'POP does not support mailbox selection (%s)' % mailbox ) if self.mailbox_selected is not False: self.write_oldmailfile(self.mailbox_selected) self._clear_state() if self.oldmail_exists(mailbox): self.read_oldmailfile(mailbox) self.mailbox_selected = mailbox self._getmsglist() def _getmsgnumbyid(self, msgid): self.log.trace() if not msgid in self.msgnum_by_msgid: raise getmailOperationError('no such message ID %s' % msgid) return self.msgnum_by_msgid[msgid] def _getmsglist(self): self.log.trace() try: (response, msglist, octets) = self.conn.uidl() self.log.debug('UIDL response "%s", %d octets' % (response, octets) + os.linesep) for (i, line) in enumerate(msglist): try: (msgnum, msgid) = tostr(line).split(None, 1) # Don't allow / in UIDs we store, as we look for that to # detect old-style oldmail files. Shouldn't occur in POP3 # anyway. msgid = msgid.replace('/', '-') except ValueError: # Line didn't contain two tokens. Server is broken. raise getmailOperationError( '%s failed to identify message index %d in UIDL output' ' -- see documentation or use ' 'BrokenUIDLPOP3Retriever instead' % (self, i) ) msgnum = int(msgnum) if msgid in self.msgnum_by_msgid: # UIDL "unique" identifiers weren't unique. # Server is broken. if self.conf.get('delete_dup_msgids', False): self.log.debug('deleting message %s with duplicate ' 'msgid %s' % (msgnum, msgid) + os.linesep) self.conn.dele(msgnum) else: raise getmailOperationError( '%s does not uniquely identify messages ' '(got %s twice) -- see documentation or use ' 'BrokenUIDLPOP3Retriever instead' % (self, msgid) ) else: self.msgnum_by_msgid[msgid] = msgnum self.msgid_by_msgnum[msgnum] = msgid self.log.debug('Message IDs: %s' % list(sorted(self.msgnum_by_msgid.keys())) + os.linesep) self.sorted_msgnum_msgid = sorted(self.msgid_by_msgnum.items()) (response, msglist, octets) = self.conn.list() for line in msglist: tostrline = tostr(line) msgnum = int(tostrline.split()[0]) msgsize = int(tostrline.split()[1]) msgid = self.msgid_by_msgnum.get(msgnum, None) # If no msgid found, it's a message that wasn't in the UIDL # response above. Ignore it and we'll get it next time. if msgid is not None: self.msgsizes[msgid] = msgsize # Remove messages from state file that are no longer in mailbox, # but only if the timestamp for them are old (30 days for now). # This is because IMAP users can have one state file but multiple # IMAP folders in different configuration rc files. for msgid in list(self.oldmail.keys()): timestamp = self.oldmail[msgid] age = self.timestamp - timestamp if msgid not in self.msgsizes and age > VANISHED_AGE: self.log.debug('removing vanished old message id %s' % msgid + os.linesep) del self.oldmail[msgid] except poplib.error_proto as o: raise getmailOperationError( 'POP error (%s) - if your server does not support the UIDL ' 'command, use BrokenUIDLPOP3Retriever instead' % o ) self.gotmsglist = True def _flagmsgbyid(self, msgid): self.log.trace() msgnum = self._getmsgnumbyid(msgid) self.conn.dele(msgnum) return True def _getmsgbyid(self, msgid): self.log.debug('msgid %s' % msgid + os.linesep) msgnum = self._getmsgnumbyid(msgid) self.log.debug('msgnum %i' % msgnum + os.linesep) try: response, lines, octets = self.conn.retr(msgnum) self.log.debug('RETR response "%s", %d octets' % (response, octets) + os.linesep) msg = Message(fromlines=lines+[b'']) return msg except poplib.error_proto as o: raise getmailRetrievalError( 'failed to retrieve msgid %s; server said %s' % (msgid, o) ) def _getheaderbyid(self, msgid): self.log.trace() msgnum = self._getmsgnumbyid(msgid) response, headerlist, octets = self.conn.top(msgnum, 0) try: parser = Parser.BytesHeaderParser() return parser.parsebytes(os.linesep.encode().join(headerlist)) except: #py2 parser = Parser.HeaderParser() return parser.parsestr(os.linesep.join(headerlist)) def initialize(self, options): self.log.trace() # POP doesn't support different mailboxes self.mailboxes = (None, ) # Handle password if self.conf.get('password', None) is None: if self.conf.get('password_command', None): # Retrieve from an arbitrary external command self.run_password_command() else: self.conf['password'] = get_password( self, self.conf['username'], self.conf['server'], self.received_with, self.log ) RetrieverSkeleton.initialize(self, options) try: self._connect() if self.conf['use_apop']: self.conn.apop(self.conf['username'], self.conf['password']) elif self.conf['use_xoauth2']: # octal 1 / ctrl-A used as separator auth = 'user=%s\1auth=Bearer %s\1\1' % (self.conf['username'], self.conf['password']) auth = base64.b64encode(auth.encode()).decode() self.conn._shortcmd('AUTH XOAUTH2 %s' % auth) else: self.conn.user(self.conf['username']) self.conn.pass_(self.conf['password']) self._getmsglist() self.log.debug('msgids: %s' % list(sorted(self.msgnum_by_msgid.keys())) + os.linesep) self.log.debug('msgsizes: %s' % self.msgsizes + os.linesep) # Remove messages from state file that are no longer in mailbox for msgid in list(self.oldmail.keys()): if msgid not in self.msgsizes: self.log.debug('removing vanished message id %s' % msgid + os.linesep) del self.oldmail[msgid] except poplib.error_proto as o: raise getmailOperationError('POP error (%s)' % o) def abort(self): self.log.trace() RetrieverSkeleton.abort(self) if not self.conn: return try: self.conn.rset() self.conn.quit() except (poplib.error_proto, socket.error) as o: pass self.conn = None def quit(self): RetrieverSkeleton.quit(self) self.log.trace() if not self.conn: return try: self.conn.quit() except (poplib.error_proto, socket.error) as o: raise getmailOperationError('POP error (%s)' % o) except AttributeError: pass self.conn = None ####################################### class MultidropPOP3RetrieverBase(POP3RetrieverBase): '''Base retriever class for multi-drop POP3 mailboxes. Envelope is reconstructed from Return-Path: (sender) and a header specified by the user (recipient). This header is specified with the "envelope_recipient" parameter, which takes the form [:]. field-number defaults to 1 and is counted from top to bottom in the message. For instance, if the envelope recipient is present in the second Delivered-To: header field of each message, envelope_recipient should be specified as "delivered-to:2". ''' def initialize(self, options): self.log.trace() POP3RetrieverBase.initialize(self, options) self.envrecipname = ( self.conf['envelope_recipient'].split(':')[0].lower() ) if self.envrecipname in NOT_ENVELOPE_RECIPIENT_HEADERS: raise getmailConfigurationError( 'the %s header field does not record the envelope ' 'recipient address' % self.envrecipname ) self.envrecipnum = 0 try: self.envrecipnum = int( self.conf['envelope_recipient'].split(':', 1)[1] ) - 1 if self.envrecipnum < 0: raise ValueError(self.conf['envelope_recipient']) except IndexError: pass except ValueError as o: raise getmailConfigurationError( 'invalid envelope_recipient specification format (%s)' % o ) def _getmsgbyid(self, msgid): self.log.trace() msg = POP3RetrieverBase._getmsgbyid(self, msgid) data = {} for (name, val) in msg.headers(): name = name.lower() val = val.strip() if name in data: data[name].append(val) else: data[name] = [val] try: line = data[self.envrecipname][self.envrecipnum] except (KeyError, IndexError) as unused: raise getmailConfigurationError( 'envelope_recipient specified header missing (%s)' % self.conf['envelope_recipient'] ) msg.recipient = address_no_brackets(line.strip()) return msg ####################################### class IMAPRetrieverBase(RetrieverSkeleton): '''Base class for single-user IMAP mailboxes. ''' def __init__(self, **args): RetrieverSkeleton.__init__(self, **args) self.log.trace() self.gss_step = 0 self.gss_vc = None self.gssapi = False def _clear_state(self): RetrieverSkeleton._clear_state(self) self.mailbox = None self.uidvalidity = None self.msgnum_by_msgid = {} self.msgid_by_msgnum = {} self.sorted_msgnum_msgid = () self._mboxuids = {} self._mboxuidorder = [] self.msgsizes = {} self.oldmail = {} self.__delivered = {} self._mboxmaxuid = 1 def checkconf(self): RetrieverSkeleton.checkconf(self) if self.conf['use_kerberos'] and not HAVE_KERBEROS_GSS: raise getmailConfigurationError( 'cannot use kerberos authentication; Python kerberos support ' 'not installed or does not support GSS' ) def gssauth(self, response): if not HAVE_KERBEROS_GSS: # shouldn't get here raise ValueError('kerberos GSS support not available') data = b''.join(codecs.encode(response,'base64').splitlines()) if self.gss_step == GSS_STATE_STEP: if not self.gss_vc: (rc, self.gss_vc) = kerberos.authGSSClientInit( 'imap@%s' % self.conf['server'] ) response = kerberos.authGSSClientResponse(self.gss_vc) rc = kerberos.authGSSClientStep(self.gss_vc, data.decode('ascii')) if rc != kerberos.AUTH_GSS_CONTINUE: self.gss_step = GSS_STATE_WRAP elif self.gss_step == GSS_STATE_WRAP: rc = kerberos.authGSSClientUnwrap(self.gss_vc, data.decode('ascii')) response = kerberos.authGSSClientResponse(self.gss_vc) rc = kerberos.authGSSClientWrap(self.gss_vc, response, self.conf['username']) response = kerberos.authGSSClientResponse(self.gss_vc) if not response: response = '' return codecs.decode(response.encode('ascii'),'base64') def _getmboxuidbymsgid(self, msgid): self.log.trace() if not msgid in self.msgnum_by_msgid: raise getmailOperationError('no such message ID %s' % msgid) uid = self._mboxuids[msgid] return uid def _parse_imapcmdresponse(self, cmd, *args): self.log.trace() try: result, resplist = getattr(self.conn, cmd)(*args) except imaplib.IMAP4.error as o: if cmd == 'login': # Percolate up raise else: raise getmailOperationError('IMAP error (%s)' % o) if result != 'OK': raise getmailOperationError( 'IMAP error (command %s returned %s %s)' % ('%s %s' % (cmd, args), result, resplist) ) if cmd.lower().startswith('login'): self.log.debug('login command response %s' % resplist + os.linesep) else: self.log.debug( 'command %s response %s' % (cmd, resplist) # don't print password: % ('%s %s' % (cmd, args), resplist) + os.linesep ) return resplist def _parse_imapuidcmdresponse(self, cmd, *args): self.log.trace() try: result, resplist = self.conn.uid(cmd, *args) except imaplib.IMAP4.error as o: if cmd == 'login': # Percolate up raise else: raise getmailOperationError('IMAP error (%s)' % o) if result != 'OK': raise getmailOperationError( 'IMAP error (command %s returned %s %s)' % (cmd, result, str(resplist)) # don't print password: % ('%s %s' % (cmd, args), result, resplist) ) self.log.debug('command uid %s response %s' % ('%s %s' % (cmd, str(args)), str(resplist)) + os.linesep) return resplist def _parse_imapattrresponse(self, line): self.log.trace('parsing attributes response line %s' % line + os.linesep) r = {} try: parts = line[line.index(b'(') + 1:line.rindex(b')')].split() while parts: # Flags starts a parenthetical list of valueless flags if parts[0].lower() == b'flags' and parts[1].startswith(b'('): while parts and not parts[0].endswith(b')'): del parts[0] if parts: # Last one, ends with ")" del parts[0] continue if len(parts) == 1: # Leftover part -- not name, value pair. raise ValueError name = parts.pop(0).lower() r[tostr(name)] = tostr(parts.pop(0)) except (ValueError, IndexError, AttributeError) as o: raise getmailOperationError( 'IMAP error (failed to parse attr response line "%s": %s)' % (line, o) ) self.log.trace('got %s' % r + os.linesep) return r def id(self): server_id = self.conn.id('name', 'getmail', 'version', '6.0.0') return server_id def list_mailboxes(self): '''List (selectable) IMAP folders in account.''' cmd = ('LIST', ) resplist = self._parse_imapcmdresponse(*cmd) return mailbox_names( codecs.decode(x,'imap4-utf-7') for x in resplist) def close_mailbox(self): # Close current mailbox so deleted mail is expunged. One getmail # user had a buggy IMAP server that didn't do the automatic expunge, # so we do it explicitly here if we've deleted any messages. if any(self.deleted): self.conn.expunge() self.write_oldmailfile(self.mailbox_selected) # And clear some state self.mailbox_selected = False self.mailbox = None self.uidvalidity = None self.msgnum_by_msgid = {} self.msgid_by_msgnum = {} self.sorted_msgnum_msgid = () self._mboxuids = {} self._mboxuidorder = [] self.msgsizes = {} self.oldmail = {} self.__delivered = {} self._mboxmaxuid = 1 self.conn.close() def _uidfile(self): filename = self.conf['uid_cache'] getmaildir = self.conf['getmaildir'] f = os.path.expanduser(filename) if not os.path.isabs(f) and getmaildir: f = os.path.join(getmaildir, f) return(f) def _uidvalidity(self): return self.uidvalidity.replace(" ","_") def _uidfindmax(self): self._mboxmaxuid = 1 self._mboxmaxuid_changed = False if (not self.conf['uid_cache'] or not self.uidvalidity): return try: with open(self._uidfile()) as C: for line in C: (mbox, oldvalidity, uid) = line.split(" ") if mbox == self.mailbox: if oldvalidity == self._uidvalidity(): self._mboxmaxuid = int(uid) return except ValueError: return except FileNotFoundError: return def _uidsavemax(self): if (not self.conf['uid_cache'] or not self._mboxmaxuid_changed or not self.uidvalidity): return uidcache = {} try: with open(self._uidfile()) as C: for line in C: (mbox, oldvalidity, uid) = line.split(" ") uidcache[mbox] = line except FileNotFoundError: pass uidcache[self.mailbox] = " ".join((self.mailbox, self._uidvalidity(), str(self._mboxmaxuid)))+"\n" with open(self._uidfile(),"w") as C: for line in uidcache.values(): print(line, file=C, end="") def _uidoldest(self): oldest = None response = self._parse_imapcmdresponse('FETCH','1','(UID)') for line in response: if not line: continue r = self._parse_imapattrresponse(line) if 'uid' not in r: continue uid = r['uid'].replace('/', '-') msgid = '%s/%s' % (self.uidvalidity, uid) oldest = msgid return oldest def select_mailbox(self, mailbox): self.log.trace() assert mailbox in self.mailboxes, ( 'mailbox not in config (%s)' % mailbox ) if self.mailbox_selected is not False: self.close_mailbox() self._clear_state() if self.oldmail_exists(mailbox): self.read_oldmailfile(mailbox) self.log.debug('selecting mailbox "%s"' % mailbox + os.linesep) try: if (self.app_options['delete'] or self.app_options['delete_after'] or self.app_options['delete_bigger_than']): read_only = False else: read_only = True if (len(mailbox) < 2 or ( mailbox[0],mailbox[-1]) != ('"','"') ) and IMAP_ATOM_SPECIAL.search(mailbox): (status, count) = self.conn.select( codecs.encode( self.conn._quote(mailbox), 'imap4-utf-7'), read_only) else: (status, count) = self.conn.select( codecs.encode( mailbox, 'imap4-utf-7'), read_only) if status == 'NO': # Specified mailbox doesn't exist, no permissions, etc. raise getmailMailboxSelectError(mailbox) self.mailbox_selected = mailbox # use *last* EXISTS returned count = int(count[-1]) uidvalidity = tostr(self.conn.response('UIDVALIDITY')[1][0] or b"") or None except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) except (IndexError, ValueError) as o: raise getmailOperationError( 'IMAP server failed to return correct SELECT response (%s)' % o ) self.log.debug('select(%s) returned message count of %d' % (mailbox, count) + os.linesep) self.mailbox = mailbox self.uidvalidity = uidvalidity self._uidfindmax() imap_search = self.conf['imap_search'] if imap_search: result, data = self.conn.search(None, imap_search) data = [d for d in data if d is not None] if result == 'OK' and data: msgs = b','.join(data[0].split()) if sys.version_info.major > 2: msgs = msgs.decode() self._getmsglist(msgs) else: self._getmsglist(count, start=self._mboxmaxuid) self._uidsavemax() return count def _remove_from_oldmail(self, oldest): # Remove messages from the oldmail state file that are no longer in mailbox, # but only if the timestamp for them are old (30 days for now). # This is because IMAP users can have one oldmail state file but multiple # IMAP folders in different configuration rc files. if oldest: (ov,ou) = oldest.split('/') for msgid in list(self.oldmail): timestamp = self.oldmail[msgid] age = self.timestamp - timestamp if msgid not in self.msgsizes and age > VANISHED_AGE: # Don't delete oldmails that might still be around # if we are only doing partial reads of the mailbox if oldest: (mv,mu) = msgid.split('/') if (ov == mv and ou < mu): continue self.log.debug('removing vanished old message id %s' % msgid + os.linesep) del self.oldmail[msgid] def _getmsglist(self, msgcount, start=1): self.log.trace() oldest = None try: if msgcount: # Get UIDs and sizes for all messages in mailbox if start > 1: fetchcmd=['UID', 'FETCH'] fetchcmd.append("%d:*"%start) else: fetchcmd=['FETCH'] fetchcmd.append(isinstance(msgcount,str) and msgcount or '1:%d'%(msgcount)) fetchcmd.append(('(UID)' if self.app_options['skip_imap_fetch_size'] else '(UID RFC822.SIZE)')) response = self._parse_imapcmdresponse(*fetchcmd) for line in response: if not line: # One user had a server that returned a null response # somehow -- try to just skip. continue r = self._parse_imapattrresponse(line) # RFC 3501, 6.1.2 states: # Since any command can return a status update as untagged data, the # NOOP command can be used as a periodic poll for new messages or # message status updates during a period of inactivity (this is the # preferred method to do this). # # This means also a FETCH command can contain a status update. # ProtonMail Bridge does so, a session can look like: # # a LOGIN me@domain password # ... # a SELECT INBOX # ... # a FETCH 1:50000 (UID RFC822.SIZE) # * 1 FETCH (UID 1 RFC822.SIZE 8938) # ... # * 49999 FETCH (UID 50001 RFC822.SIZE 15500) # * 50000 FETCH (UID 50002 RFC822.SIZE 9614) # * 51868 FETCH (FLAGS (NonJunk) UID 51958) # a OK FETCH completed # # While fetching, UID 51958 saw an update and gained new flags. # So we need to check whether FETCH response lines contain the # requested items. if 'uid' not in r: continue if (not self.app_options['skip_imap_fetch_size']) and ('rfc822.size' not in r): continue # Don't allow / in UIDs we store, as we look for that to # detect old-style oldmail files. Can occur with IMAP, at # least with some servers. uid = r['uid'].replace('/', '-') try: nuid = int(uid) if nuid > self._mboxmaxuid: self._mboxmaxuid = nuid self._mboxmaxuid_changed = True except ValueError: # Presumably non-numeric UID, which should be illegal but according to comment above it can happen pass msgid = '%s/%s' % (self.uidvalidity, uid) self._mboxuids[msgid] = r['uid'] self._mboxuidorder.append(msgid) self.msgnum_by_msgid[msgid] = None self.msgsizes[msgid] = (0 if self.app_options['skip_imap_fetch_size'] else int(r['rfc822.size'])) if start > 1: oldest = self._uidoldest() self._remove_from_oldmail(oldest) except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) self.gotmsglist = True def __getitem__(self, i): return self._mboxuidorder[i] def _flagmsgbyid(self, msgid): self.log.trace() try: uid = self._getmboxuidbymsgid(msgid) #self._selectmailbox(mailbox) # Delete message if self.conf['move_on_delete']: self.log.debug('copying message to folder "%s"' % self.conf['move_on_delete'] + os.linesep) response = self._parse_imapuidcmdresponse( 'COPY', uid, self.conf['move_on_delete'] ) self.log.debug('deleting message "%s"' % uid + os.linesep) # On GMail you need to remove LABELS before deleting if 'X-GM-EXT-1' in self.conn.capabilities: self._parse_imapuidcmdresponse('STORE', uid,'X-GM-LABELS', '()') flag = self.conf.get('imap_on_delete',None) or r'(\Deleted \Seen)' response = self._parse_imapuidcmdresponse( 'STORE', uid, 'FLAGS', flag ) return 'Deleted' in flag except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) def _getmsgpartbyid(self, msgid, part): self.log.trace() try: uid = self._getmboxuidbymsgid(msgid) # Retrieve message self.log.debug('retrieving body for message "%s"' % uid + os.linesep) try: response = self._parse_imapuidcmdresponse('FETCH', uid, part) except (imaplib.IMAP4.error, getmailOperationError) as o: # server gave a negative/NO response, most likely. Bad server, # no doughnut. raise getmailRetrievalError( 'failed to retrieve msgid %s; server said %s' % (msgid, o) ) # Response is really ugly: # # [ # ( # '1 (UID 1 RFC822 {704}', # 'message text here with CRLF EOL' # ), # ')', # # ] # MSExchange is broken -- if a message is badly formatted enough # (virus, spam, trojan), it can completely fail to return the # message when requested. try: try: sbody = response[0][1] except Exception as o: sbody = None if not sbody: raise getmailRetrievalError('bad message from server!') msg = Message(fromstring=sbody) except TypeError as o: # response[0] is None instead of a message tuple raise getmailRetrievalError('failed to retrieve msgid %s' % msgid) # record mailbox retrieved from in a header if self.conf['record_mailbox']: msg.add_header('X-getmail-retrieved-from-mailbox', tocode(self.mailbox_selected)) # google extensions: apply labels, etc if 'X-GM-EXT-1' in self.conn.capabilities: metadata = self._getgmailmetadata(uid, msg) for (header, value) in metadata.items(): msg.add_header(header, value) return msg except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) def _getgmailmetadata(self, uid, msg): """ Add Gmail labels and other metadata which Google exposes through an IMAP extension to headers in the message. See https://developers.google.com/google-apps/gmail/imap_extensions """ try: # ['976 (X-GM-THRID 1410134259107225671 X-GM-MSGID ' # '1410134259107225671 X-GM-LABELS (labels space ' # 'separated) UID 167669)'] response = self._parse_imapuidcmdresponse('FETCH', uid, '(X-GM-LABELS X-GM-THRID X-GM-MSGID)') except imaplib.IMAP4.error as o: self.log.warning('Could not fetch google imap extensions: %s' % o) return {} if not response or not response[0]: return {} ext = re.search( b'X-GM-THRID (?P\\d+) X-GM-MSGID (?P\\d+)' b' X-GM-LABELS \\((?P.*)\\) UID', response[0] ) if not ext: self.log.warning( 'Could not parse google imap extensions. Server said: %s' % repr(response)) return {} results = ext.groupdict() metadata = {} for item in ('LABELS', 'THRID', 'MSGID'): if results.get(item): metadata['X-GMAIL-%s' % item] = results[item] return metadata def _getmsgbyid(self, msgid): self.log.trace() if self.conf.get('use_peek', True): part = '(BODY.PEEK[])' else: part = '(RFC822)' return self._getmsgpartbyid(msgid, part) def _getheaderbyid(self, msgid): self.log.trace() if self.conf.get('use_peek', True): part = '(BODY.PEEK[header])' else: part = '(RFC822[header])' return self._getmsgpartbyid(msgid, part) def initialize(self, options): self.log.trace() self.mailboxes = self.conf.get('mailboxes', ('INBOX', )) # Handle password if ((self.conf.get('password', None) is None or self.conf['use_xoauth2']) and not (HAVE_KERBEROS_GSS and self.conf['use_kerberos'])): if self.conf['password_command']: # Retrieve from an arbitrary external command self.run_password_command() else: self.conf['password'] = get_password( self, self.conf['username'], self.conf['server'], self.received_with, self.log ) RetrieverSkeleton.initialize(self, options) try: self.log.trace('trying self._connect()' + os.linesep) self._connect() try: self.log.trace('logging in' + os.linesep) if self.conf['use_kerberos'] and HAVE_KERBEROS_GSS: self.conn.authenticate('GSSAPI', self.gssauth) elif self.conf['use_cram_md5']: self._parse_imapcmdresponse( 'login_cram_md5', self.conf['username'], self.conf['password'] ) elif self.conf['use_xoauth2']: # octal 1 / ctrl-A used as separator auth = 'user=%s\1auth=Bearer %s\1\1' % (self.conf['username'], self.conf['password']) self.conn.authenticate('XOAUTH2', lambda unused: auth) else: self._parse_imapcmdresponse('login', self.conf['username'], self.conf['password']) except imaplib.IMAP4.abort as o: raise getmailLoginRefusedError(o) except imaplib.IMAP4.error as o: if '[UNAVAILABLE]' in str(o): raise getmailLoginRefusedError(o) else: raise getmailCredentialError(o) self.log.trace('logged in' + os.linesep) # Some IMAP servers change the available capabilities after # authentication, i.e. they present a limited set before login. # The Python stlib IMAP4 class doesn't take this into account # and just checks the capabilities immediately after connecting. # Force a re-check now that we've authenticated. (typ, dat) = self.conn.capability() if dat == [None]: # No response, don't update the stored capabilities self.log.warning('no post-login CAPABILITY response from server\n') else: self.conn.capabilities = tuple(tostr(dat[-1]).upper().split()) if 'IDLE' in self.conn.capabilities: self.supports_idle = True imaplib.Commands['IDLE'] = ('AUTH', 'SELECTED') if 'ID' in self.conn.capabilities: self.supports_id = True if self.supports_id and self.conf['imap_id_extension']: self.id() if self.mailboxes == ('ALL', ): # Special value meaning all mailboxes in account self.mailboxes = tuple(self.list_mailboxes()) except imaplib.IMAP4.error as o: raise getmailOperationError('IMAP error (%s)' % o) def abort(self): self.log.trace() RetrieverSkeleton.abort(self) if not self.conn: return try: self.quit() except (imaplib.IMAP4.error, socket.error) as o: pass self.conn = None def go_idle(self, folder, timeout=300): """Initiates IMAP's IDLE mode if the server supports it Waits until state of current mailbox changes, and then returns. Returns True if the connection still seems to be up, False otherwise. May throw getmailOperationError if the server refuses the IDLE setup (e.g. if the server does not support IDLE) Default timeout is 5 minutes. """ if not self.supports_idle: self.log.warning('IDLE not supported, so not idling\n') raise getmailOperationError( 'IMAP4 IDLE requested, but not supported by server' ) # Based on current imaplib IDLE patch: http://bugs.python.org/issue11245 self.conn.untagged_responses = {} self.conn.select(folder) tag = self.conn._command('IDLE') data = self.conn._get_response() # read continuation response if data is not None: raise getmailOperationError( 'IMAP4 IDLE requested, but server refused IDLE request: %s' % data ) self.log.debug('Entering IDLE mode (server says "%s")\n' % self.conn.continuation_response) try: aborted = None (readable, unused, _) = select.select([self.conn.sock], [], [], timeout) except KeyboardInterrupt as o: # Delay raising this until we've stopped IDLE mode aborted = o if aborted is not None: self.log.debug('IDLE mode cancelled\n') elif readable: # The socket has data waiting; server has updated status self.log.info('IDLE message received\n') else: self.log.debug('IDLE timeout (%ds)\n' % timeout) try: self.conn.untagged_responses = {} self.conn.send(b'DONE\r\n') self.conn._command_complete('IDLE', tag) except imaplib.IMAP4.error as o: return False except BrokenPipeError as o: # The underlying connection closed during IDLE self.log.info('BrokenPipeError after IDLE\n') return False except TimeoutError as o: self.log.info('TimeoutError after IDLE\n') return False if aborted: raise aborted return True def quit(self): self.log.trace() if not self.conn: return try: if self.mailbox_selected is not False: self.close_mailbox() self.conn.logout() except imaplib.IMAP4.error as o: #raise getmailOperationError('IMAP error (%s)' % o) self.log.warning('IMAP error during logout (%s)' % o + os.linesep) RetrieverSkeleton.quit(self) self.conn = None ####################################### class MultidropIMAPRetrieverBase(IMAPRetrieverBase): '''Base retriever class for multi-drop IMAP mailboxes. Envelope is reconstructed from Return-Path: (sender) and a header specified by the user (recipient). This header is specified with the "envelope_recipient" parameter, which takes the form [:]. field-number defaults to 1 and is counted from top to bottom in the message. For instance, if the envelope recipient is present in the second Delivered-To: header field of each message, envelope_recipient should be specified as "delivered-to:2". ''' def initialize(self, options): self.log.trace() IMAPRetrieverBase.initialize(self, options) self.envrecipname = (self.conf['envelope_recipient'].split(':') [0].lower()) if self.envrecipname in NOT_ENVELOPE_RECIPIENT_HEADERS: raise getmailConfigurationError( 'the %s header field does not record the envelope recipient ' 'address' % self.envrecipname ) self.envrecipnum = 0 try: self.envrecipnum = int( self.conf['envelope_recipient'].split(':', 1)[1] ) - 1 if self.envrecipnum < 0: raise ValueError(self.conf['envelope_recipient']) except IndexError: pass except ValueError as o: raise getmailConfigurationError( 'invalid envelope_recipient specification format (%s)' % o ) def _getmsgbyid(self, msgid): self.log.trace() msg = IMAPRetrieverBase._getmsgbyid(self, msgid) data = {} for (name, encoded_value) in msg.headers(): name = name.lower() for (val, encoding) in decode_header(encoded_value): val = val.strip() if name in data: data[name].append(val) else: data[name] = [val] try: line = data[self.envrecipname][self.envrecipnum] except (KeyError, IndexError) as unused: raise getmailConfigurationError( 'envelope_recipient specified header missing (%s)' % self.conf['envelope_recipient'] ) msg.recipient = address_no_brackets(line.strip()) return msg getmail6-6.19.07/getmailcore/baseclasses.py000077500000000000000000000441461474536475600206040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''Base classes used elsewhere in the package. ''' import sys import os import time import signal import types import codecs from collections import namedtuple import tempfile import errno from threading import Condition import socket from argparse import Namespace import subprocess from getmailcore.exceptions import * import getmailcore.logging from getmailcore.utilities import * __all__ = [ 'ConfBool', 'ConfDirectory', 'ConfFile', 'ConfigurableBase', 'ConfInstance', 'ConfInt', 'ConfMaildirPath', 'ConfMboxPath', 'ConfPassword', 'ConfString', 'ConfTupleOfStrings', 'ConfTupleOfTupleOfStrings', 'ConfTupleOfUnicode', 'ForkingBase', 'run_command', ] if sys.version_info.major > 2: TemporaryFile23 = lambda: tempfile.TemporaryFile('bw+') else: TemporaryFile23 = lambda: tempfile.TemporaryFile('w+') ####################################### def run_command(command, args): # Simple subprocess wrapper for running a command and fetching its exit # status and output/stderr. if args is None: args = [] if isinstance(args, tuple): args = list(args) # Programmer sanity checks assert isinstance(command, (bytes, unicode)), ( 'command is %s (%s)' % (command, type(command)) ) assert isinstance(args, list), ( 'args is %s (%s)' % (args, type(args)) ) for arg in args: assert isinstance(arg, (bytes, unicode)), 'arg is %s (%s)' % (arg, type(arg)) with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr: cmd = [command] + args try: p = subprocess.Popen(cmd, stdout=stdout, stderr=stderr) except OSError as o: if o.errno == errno.ENOENT: # no such file, command not found raise getmailConfigurationError('Program "%s" not found' % command) #else: raise rc = p.wait() stdout.seek(0) stderr.seek(0) if sys.version_info.major == 2: return (rc, stdout.read().strip(), stderr.read().strip()) else: return (rc, stdout.read().decode().strip(), stderr.read().decode().strip()) # # Base classes # class ConfItem: securevalue = False def __init__(self, name, dtype, default=None, required=True): self.log = getmailcore.logging.Logger() self.name = name self.dtype = dtype self.default = default self.required = required def validate(self, configuration, val=None): if val is None: # If not passed in by subclass val = configuration.get(self.name, None) if val is None: # Not provided. if self.required: raise getmailConfigurationError( '%s: missing required configuration parameter' % self.name ) # Use default. return self.default if not isinstance(val,self.dtype) and val != self.default: # Got value, but not of expected type. Try to convert. if self.securevalue: self.log.debug('converting %s to type %s\n' % (self.name, self.dtype)) else: self.log.debug('converting %s (%s) to type %s\n' % (self.name, val, self.dtype)) try: if self.dtype == bool: val = eval_bool(val) else: val = self.dtype(eval(val)) except (ValueError, SyntaxError, TypeError) as o: raise getmailConfigurationError( '%s: configuration value (%s) not of required type %s (%s)' % (self.name, val, self.dtype, o) ) return val class ConfInstance(ConfItem): def __init__(self, name, default=None, required=True): ConfItem.__init__(self, name, object, default=default, required=required) class ConfString(ConfItem): def __init__(self, name, default=None, required=True): ConfItem.__init__(self, name, str, default=default, required=required) class ConfBool(ConfItem): def __init__(self, name, default=None, required=True): ConfItem.__init__(self, name, bool, default=default, required=required) class ConfInt(ConfItem): def __init__(self, name, default=None, required=True): ConfItem.__init__(self, name, int, default=default, required=required) class ConfTupleOfStrings(ConfString): def __init__(self, name, default=None, required=True): ConfString.__init__(self, name, default=default, required=required) def validate(self, configuration): val = ConfItem.validate(self, configuration) try: if not val: val = '()' tup = eval(val) if not isinstance(tup, tuple): raise ValueError('not a tuple') val = tup except (ValueError, SyntaxError) as o: raise getmailConfigurationError( '%s: incorrect format (%s)' % (self.name, o) ) result = [str(item) for item in val] return tuple(result) class ConfTupleOfUnicode(ConfString): def __init__(self, name, default=None, required=True, allow_specials=()): ConfString.__init__(self, name, default=default, required=required) self.specials = allow_specials def validate(self, configuration): _locals = dict([(v, v) for v in self.specials]) val = ConfItem.validate(self, configuration) try: if not val: val = '()' tup = eval(val, {}, _locals) if tup in self.specials: val = [tup] else: if not isinstance(tup, tuple): raise ValueError('not a tuple') vals = [] for item in tup: try: item = item.encode() vals.append(codecs.decode(item,'ascii')) except: try: vals.append(codecs.decode(item,'utf-8')) except UnicodeError as o: raise ValueError('not ascii or utf-8: %s' % item) val = vals except (ValueError, SyntaxError) as o: raise getmailConfigurationError( '%s: incorrect format (%s)' % (self.name, o) ) return tuple(val) class ConfTupleOfTupleOfStrings(ConfString): def __init__(self, name, default=None, required=True): ConfString.__init__(self, name, default=default, required=required) def validate(self, configuration): val = ConfItem.validate(self, configuration) try: if not val: val = '()' tup = eval(val) if not isinstance(tup, tuple): raise ValueError('not a tuple') val = tup except (ValueError, SyntaxError) as o: raise getmailConfigurationError( '%s: incorrect format (%s)' % (self.name, o) ) for tup in val: if not isinstance(tup, tuple): raise ValueError('contained value "%s" not a tuple' % tup) if len(tup) != 2: raise ValueError('contained value "%s" not length 2' % tup) for part in tup: if not isinstance(part,str): raise ValueError('contained value "%s" has non-string part ' '"%s"' % (tup, part)) return val class ConfPassword(ConfString): securevalue = True class ConfDirectory(ConfString): def __init__(self, name, default=None, required=True): ConfString.__init__(self, name, default=default, required=required) def validate(self, configuration): val = ConfString.validate(self, configuration) if val is None: return None val = expand_user_vars(val) if not os.path.isdir(val): raise getmailConfigurationError( '%s: specified directory "%s" does not exist' % (self.name, val) ) return val class ConfFile(ConfString): def __init__(self, name, default=None, required=True): ConfString.__init__(self, name, default=default, required=required) def validate(self, configuration): val = ConfString.validate(self, configuration) if val is None: return None val = expand_user_vars(val) if not os.path.isfile(val): raise getmailConfigurationError( '%s: specified file "%s" does not exist' % (self.name, val) ) return val class ConfMaildirPath(ConfDirectory): def validate(self, configuration): val = ConfDirectory.validate(self, configuration) if val is None: return None if not val.endswith('/'): raise getmailConfigurationError( '%s: maildir must end with "/"' % self.name ) for subdir in ('cur', 'new', 'tmp'): subdirpath = os.path.join(val, subdir) if not os.path.isdir(subdirpath): raise getmailConfigurationError( '%s: maildir subdirectory "%s" does not exist' % (self.name, subdirpath) ) return val class ConfMboxPath(ConfString): def __init__(self, name, default=None, required=True): ConfString.__init__(self, name, default=default, required=required) def validate(self, configuration): val = ConfString.validate(self, configuration) if val is None: return None val = expand_user_vars(val) if not os.path.isfile(val): raise getmailConfigurationError( '%s: specified mbox file "%s" does not exist' % (self.name, val) ) fd = os.open(val, os.O_RDWR) status_old = os.fstat(fd) try: f = os.fdopen(fd, 'br+') except ValueError: # py2 f = os.fdopen(fd, 'r+') # Check if it _is_ an mbox file. mbox files must start with "From " # in their first line, or are 0-length files. f.seek(0, 0) first_line = f.readline() if first_line and first_line[:5] != b'From ': # Not an mbox file; abort here raise getmailConfigurationError('%s: not an mboxrd file' % val) # Reset atime and mtime try: os.utime(val, (status_old.st_atime, status_old.st_mtime)) except OSError: # Not root or owner; readers will not be able to reliably # detect new mail. But you shouldn't be delivering to # other peoples' mboxes unless you're root, anyways. pass return val ####################################### class ConfigurableBase(object): '''Base class for user-configurable classes. Sub-classes must provide the following data attributes and methods: _confitems - a tuple of dictionaries representing the parameters the class takes. Each dictionary should contain the following key, value pairs: - name - parameter name - type - a type function to compare the parameter value against (i.e. str, int, bool) - default - optional default value. If not present, the parameter is required. ''' def __init__(self, **args): self.log = getmailcore.logging.Logger() self.log.trace() self.conf = {} for (name, value) in args.items(): if name not in self: self.log.warning('Warning: ignoring unknown parameter "%s" ' '(value: %s)\n' % (name, value)) continue if name.lower() == 'password': self.log.trace('setting %s to * (%s)\n' % (name, type(value))) else: self.log.trace('setting %s to "%s" (%s)\n' % (name, value, type(value))) self.conf[name] = value self.__confchecked = False self.checkconf() def checkconf(self): self.log.trace() if self.__confchecked: return for item in self._confitems: # New class-based configuration item self.log.trace('checking %s\n' % item.name) self.conf[item.name] = item.validate(self.conf) unknown_params = frozenset(self.conf.keys()).difference( frozenset([item.name for item in self._confitems]) ) for param in sorted(list(unknown_params), key=str.lower): self.log.warning('Warning: ignoring unknown parameter "%s" ' '(value: %s)\n' % (param, self.conf[param])) self.__confchecked = True self.log.trace('done\n') def _confstring(self): self.log.trace() confstring = '' for name in list(sorted(self.conf.keys())): if name.lower() == 'configparser': continue if confstring: confstring += ', ' if name.lower() == 'password': confstring += '%s="*"' % name else: confstring += '%s="%s"' % (name, self.conf[name]) return confstring def __contains__(self, confitem): return confitem in {item.name for item in self._confitems} ####################################### class ForkingBase(object): '''Base class for classes which fork children and wait for them to exit. Sub-classes must provide the following data attributes and methods: log - an object of type getmailcore.logging.Logger() ''' def _child_handler(self, sig, stackframe): def notify(): self.__child_exited.acquire() self.__child_exited.notify_all() self.__child_exited.release() self.log.trace('handler called for signal %s' % sig) try: pid, r = os.waitpid(self.child.childpid,0) except OSError as o: self.log.trace('handler called, but no children (%s)' % o) notify() return if self.__orig_handler: signal.signal(signal.SIGCHLD, self.__orig_handler) self.__child_pid = pid self.__child_status = r self.log.trace('handler reaped child %s with status %s' % (pid, r)) notify() def _prepare_child(self): self.log.trace('') self.__child_exited = Condition() self.__child_pid = 0 self.__child_status = None self.__orig_handler = None self.__orig_handler = signal.signal(signal.SIGCHLD, self._child_handler) def _wait_for_child(self, childpid): self.__child_exited.acquire() if self.__child_exited.wait(socket.getdefaulttimeout() or 60) == False: # py2, =3.3 memoryview to chars (from unsigned bytes), but # cast is absent in previous versions: thus, the lambda returns the # memoryview instance while ignoring the format import codecs memory_cast = getattr(memoryview, "cast", lambda *x: x[0]) def modified_base64(s): s_utf7 = s.encode('utf-7') return s_utf7[1:-1].replace(b'/', b',') def modified_unbase64(s): s_utf7 = b'+' + s.replace(b',', b'/') + b'-' return s_utf7.decode('utf-7') def encoder(s, errors=None): """ Encode the given C{unicode} string using the IMAP4 specific variation of UTF-7. @type s: C{unicode} @param s: The text to encode. @param errors: Policy for handling encoding errors. Currently ignored. @return: L{tuple} of a L{str} giving the encoded bytes and an L{int} giving the number of code units consumed from the input. """ r = bytearray() _in = [] valid_chars = set(map(chr, range(0x20,0x7f))) - {u"&"} for c in s: if c in valid_chars: if _in: r += b'&' + modified_base64(''.join(_in)) + b'-' del _in[:] r.append(ord(c)) elif c == u'&': if _in: r += b'&' + modified_base64(''.join(_in)) + b'-' del _in[:] r += b'&-' else: _in.append(c) if _in: r.extend(b'&' + modified_base64(''.join(_in)) + b'-') return (bytes(r), len(s)) def decoder(s, errors=None): """ Decode the given L{str} using the IMAP4 specific variation of UTF-7. @type s: L{str} @param s: The bytes to decode. @param errors: Policy for handling decoding errors. Currently ignored. @return: a L{tuple} of a C{unicode} string giving the text which was decoded and an L{int} giving the number of bytes consumed from the input. """ r = [] decode = [] s = memory_cast(memoryview(s), 'c') for c in s: if c == b'&' and not decode: decode.append(b'&') elif c == b'-' and decode: if len(decode) == 1: r.append(u'&') else: r.append(modified_unbase64(b''.join(decode[1:]))) decode = [] elif decode: decode.append(c) else: r.append(c.decode()) if decode: r.append(modified_unbase64(b''.join(decode[1:]))) return (u''.join(r), len(s)) class StreamReader(codecs.StreamReader): def decode(self, s, errors='strict'): return decoder(s) class StreamWriter(codecs.StreamWriter): def encode(self, s, errors='strict'): return encoder(s) _codecInfo = codecs.CodecInfo(encoder, decoder, StreamReader, StreamWriter) def imap4_utf_7(name): if name in {'imap4-utf-7', 'imap4_utf_7'}: return _codecInfo codecs.register(imap4_utf_7) getmail6-6.19.07/getmailcore/logging.py000077500000000000000000000101571474536475600177350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''Logging support for getmail. The new standard Python library module logging didn't cut it for me; it doesn't seem capable of handling some very simple requirements like logging messages of a certain level to one fd, and other messages of higher levels to a different fd (i.e. info to stdout, warnings to stderr). ''' import sys import os.path import traceback from getmailcore.constants import * __all__ = [ 'Logger', ] ####################################### class _Logger(object): '''Class for logging. Do not instantiate directly; use Logger() instead, to keep this a singleton. ''' def __init__(self): '''Create a logger.''' self.handlers = [] self.newline = False def __call__(self): return self def addhandler(self, stream, minlevel, maxlevel=CRITICAL): '''Add a handler for logged messages. Logged messages of at least level (and at most level , default CRITICAL) will be output to . If no handlers are specified, messages of all levels will be output to stdout. ''' self.handlers.append({'minlevel' : minlevel, 'stream' : stream, 'newline' : True, 'maxlevel' : maxlevel}) def clearhandlers(self): '''Clear the list of handlers. There should be a way to remove only one handler from a list. But that would require an easy way for the caller to distinguish between them. ''' self.handlers = [] def log(self, msglevel, msgtxt): '''Log a message of level containing text .''' if sys.version_info.major > 2 and isinstance(msgtxt,bytes): msgtxt = msgtxt.decode() for handler in self.handlers: if msglevel < handler['minlevel'] or msglevel > handler['maxlevel']: continue if not handler['newline'] and msglevel == DEBUG: handler['stream'].write('\n') try: handler['stream'].write(msgtxt) except UnicodeError: try: handler['stream'].write( msgtxt.encode('ascii',errors='backslashreplace').decode('ascii') ) except AttributeError: handler['stream'].write(msgtxt.decode('ascii',errors='backslashreplace')) handler['stream'].flush() if msgtxt.endswith('\n'): handler['newline'] = True else: handler['newline'] = False if not self.handlers: if not self.newline and msglevel == DEBUG: sys.stdout.write('\n') sys.stdout.write(msgtxt) sys.stdout.flush() if msgtxt.endswith('\n'): self.newline = True else: self.newline = False def trace(self, msg='trace\n'): '''Log a message with level TRACE. The message will be prefixed with filename, line number, and function name of the calling code. ''' trace = traceback.extract_stack()[-2] msg = '%s [%s:%i] %s' % (trace[FUNCNAME] + '()', os.path.basename(trace[FILENAME]), trace[LINENO], msg ) self.log(TRACE, msg) def debug(self, msg): '''Log a message with level DEBUG.''' self.log(DEBUG, msg) def moreinfo(self, msg): '''Log a message with level MOREINFO.''' self.log(MOREINFO, msg) def info(self, msg): '''Log a message with level INFO.''' self.log(INFO, msg) def warning(self, msg): '''Log a message with level WARNING.''' self.log(WARNING, msg) def error(self, msg): '''Log a message with level ERROR.''' self.log(ERROR, msg) def critical(self, msg): '''Log a message with level CRITICAL.''' self.log(CRITICAL, msg) # aliases warn = warning Logger = _Logger() getmail6-6.19.07/getmailcore/message.py000077500000000000000000000215451474536475600177360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''The getmail Message class. ''' from __future__ import unicode_literals import sys import os import time import re import email try: #py2 import email.Errors as Errors import email.Utils as Utils import email.Parser as Parser from email.Generator import Generator from email.Header import Header except ImportError: import email.errors as Errors import email.utils as Utils import email.parser as Parser from email.generator import Generator from email.header import Header from getmailcore.exceptions import * from getmailcore.utilities import mbox_from_escape, format_header, \ address_no_brackets import getmailcore.logging __all__ = [ 'Message', ] message_attributes = ( 'received_by', 'received_from', 'received_with', 'recipient', 'sender', ) _NL = os.linesep.encode() ####################################### def corrupt_message(why, fromlines=None, fromstring=None): log = getmailcore.logging.Logger() log.error('failed to parse retrieved message; constructing container for ' 'contents\n') if fromlines == fromstring == None: raise SystemExit('corrupt_message() called with wrong arguments') msg = email.message_from_string('') msg['From'] = '"unknown sender" <>' msg['Subject'] = 'Corrupt message received' msg['Date'] = Utils.formatdate(localtime=True) body = [ b'A badly-corrupt message was retrieved and could not be parsed', b'for the following reason:', b'', b' %s' % str(why).encode(), b'', b'Below the following line is the original message contents.', b'', b'--------------------------------------------------------------', ] if fromlines: body.extend([line.rstrip() for line in fromlines]) elif fromstring: body.extend([line.rstrip() for line in fromstring.splitlines()]) msg.set_payload(_NL.join(body)) for attr in message_attributes: setattr(msg, attr, '') return msg ####################################### class Message(object): '''Message class for getmail. Does sanity-checking on attribute accesses and provides some convenient interfaces to an underlying email.Message() object. ''' __slots__ = ( '__msg', '__raw', 'received_by', 'received_from', 'received_with', 'recipient', 'sender', ) def __init__(self, fromlines=None, fromstring=None, fromfile=None): #self.log = Logger() self.recipient = None self.received_by = None self.received_from = None self.received_with = None self.__raw = None try: parser = Parser.BytesParser() parsestr = parser.parsebytes except: #py2 parser = Parser.Parser() parsestr = parser.parsestr # Message is instantiated with fromlines for POP3, fromstring for # IMAP (both of which can be badly-corrupted or invalid, i.e. spam, # MS worms, etc). It's instantiated with fromfile for the output # of filters, etc, which should be saner. if fromlines: try: self.__msg = parsestr(_NL.join(fromlines)) #_msg = ucparse(parsestr,_NL.join(["über".encode('latin-1'),"Höhen".encode('latin-1')])) #type(_msg) # except (Errors.MessageError,UnicodeDecodeError) as o: self.__msg = corrupt_message(o, fromlines=fromlines) self.__raw = _NL.join(fromlines) elif fromstring: try: self.__msg = parsestr(fromstring) #_msg = ucparse(parsestr,"über\nHöhen".encode('latin-1')) except (Errors.MessageError,UnicodeDecodeError) as o: self.__msg = corrupt_message(o, fromstring=fromstring) self.__raw = fromstring elif fromfile: try: self.__msg = parser.parse(fromfile) #from io import BytesIO #fromfile=BytesIO(_NL.join(["über".encode('latin-1'),"Höhen".encode('latin-1')])) #_msg = ucparse(parser.parse,fromfile) except (Errors.MessageError,UnicodeDecodeError) as o: # Shouldn't happen self.__msg = corrupt_message(o, fromstring=fromfile.read()) # fromfile is only used by getmail_maildir, getmail_mbox, and # from reading the output of a filter. Ignore __raw here. else: # Can't happen? raise SystemExit('Message() called with wrong arguments') self.sender = address_no_brackets(self.__msg['Return-Path'] or self.__msg['Sender'] or 'unknown') def content(self): return self.__msg def copyattrs(self, othermsg): for attr in message_attributes: setattr(self, attr, getattr(othermsg, attr)) def flatten(self, delivered_to, received, mangle_from=False, include_from=False): '''Return a string with native EOL convention. The email module apparently doesn't always use native EOL, so we force it by writing out what we need, letting the generator write out the message, splitting it into lines, and joining them with the platform EOL. Note on mangle_from: the Python Generator class apparently only quotes "From ", not ">From " (i.e. it uses mboxo format instead of mboxrd). So we don't use its mangling, and do it by hand instead. ''' if include_from: # Mbox-style From line, not rfc822 From: header field. fromline = 'From %s %s' % (mbox_from_escape(self.sender), time.asctime()) + os.linesep else: fromline = '' # Write the Return-Path: header rpline = format_header('Return-Path', '<%s>' % self.sender) # Remove previous Return-Path: header fields. del self.__msg['Return-Path'] if delivered_to: dtline = format_header('Delivered-To', self.recipient or 'unknown') else: dtline = '' if received: rcvd = 'from %s by %s with %s' % ( self.received_from, self.received_by, self.received_with ) rcvd += ' getmail6' if received != True: rcvd += ' msgid:%s'%received if self.recipient is not None: rcvd += ' for <%s>' % self.recipient rcvd += '; ' + time.strftime('%d %b %Y %H:%M:%S -0000', time.gmtime()) rcvline = format_header('Received', rcvd) else: rcvline = '' # From_ handled above, always tell the generator not to include it try: try: #py3 bmsg = self.__msg.as_bytes( policy=self.__msg.policy.clone(linesep=os.linesep)) except AttributeError: #py2 bmsg = self.__msg.as_string() bmsg = _NL.join(bmsg.splitlines() + [b'']) if mangle_from: # do mboxrd-style "From " line quoting (add one '>') RE_FROMLINE = re.compile(b'^(>*From )', re.MULTILINE) bmsg = RE_FROMLINE.sub(b'>\\1', bmsg) return ((fromline+rpline+dtline+rcvline).encode('ASCII',errors="replace")+bmsg) except (TypeError,UnicodeEncodeError) as o: if self.__raw is None: # Argh -- a filter took a correctly-formatted message # and returned a badly-misformatted one? raise getmailDeliveryError('failed to parse retrieved message ' 'and could not recover (%s)' % o) self.__msg = corrupt_message(o, fromstring=self.__raw) return self.flatten(delivered_to, received, mangle_from, include_from) def add_header(self, name, content): content_rstriped = content.rstrip() try: self.__msg[name] = Header(content_rstriped) except (UnicodeDecodeError, LookupError): for chs in self.__msg.get_charsets(): if chs is None: continue try: self.__msg[name] = Header(content_rstriped,chs) return except UnicodeError: continue self.__msg[name] = Header(content_rstriped,'utf-8',errors="replace") def remove_header(self, name): del self.__msg[name] def headers(self): return self.__msg._headers def get_all(self, name, failobj=None): return self.__msg.get_all(name, failobj) getmail6-6.19.07/getmailcore/retrievers.py000077500000000000000000000600341474536475600205000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''Classes implementing retrievers (message sources getmail can retrieve mail from). Currently implemented: SimplePOP3Retriever SimplePOP3SSLRetriever BrokenUIDLPOP3Retriever BrokenUIDLPOP3SSLRetriever MultidropPOP3Retriever MultidropPOP3SSLRetriever MultidropSDPSRetriever SimpleIMAPRetriever -- IMAP, as a protocol, is a FPOS, and it shows. The Python standard library module imaplib leaves much up to the user because of this. SimpleIMAPSSLRetriever - the above, for IMAP-over-SSL. MultidropIMAPRetriever MultidropIMAPSSLRetriever ''' import os import poplib import imaplib import types from getmailcore.exceptions import * from getmailcore.constants import * from getmailcore.utilities import * from getmailcore.baseclasses import * from getmailcore._retrieverbases import * __all__ = [ 'BrokenUIDLPOP3Retriever', 'BrokenUIDLPOP3SSLRetriever', 'MultidropIMAPRetriever', 'MultidropIMAPSSLRetriever', 'MultidropPOP3Retriever', 'MultidropPOP3SSLRetriever', 'MultidropSDPSRetriever', 'SimpleIMAPRetriever', 'SimpleIMAPSSLRetriever', 'SimplePOP3Retriever', 'SimplePOP3SSLRetriever', ] # # Functional classes # ####################################### class SimplePOP3Retriever(POP3RetrieverBase, POP3initMixIn): '''Retriever class for single-user POP3 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ConfBool(name='delete_dup_msgids', required=False, default=False), ) received_from = None received_with = 'POP3' received_by = localhostname() def __str__(self): self.log.trace() return 'SimplePOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimplePOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class SimplePOP3SSLRetriever(POP3RetrieverBase, POP3SSLinitMixIn): '''Retriever class for single-user POP3-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ConfBool(name='delete_dup_msgids', required=False, default=False), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ConfFile(name='ca_certs', required=False, default=None), ConfTupleOfStrings(name='ssl_fingerprints', required=False, default=()), ConfString(name='ssl_version', required=False, default=None), ConfString(name='ssl_ciphers', required=False, default=None), ConfString(name='ssl_cert_hostname', required=False, default=None), ) received_from = None received_with = 'POP3-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'SimplePOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimplePOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class BrokenUIDLPOP3RetrieverBase(POP3RetrieverBase): '''Retriever base class for single-user POP3 mailboxes on servers that do not properly assign unique IDs to messages. Since with these broken servers we cannot rely on UIDL, we have to use message numbers, which are unique within a POP3 session, but which change across sessions. This class therefore can not be used to leave old mail on the server and download only new mail. ''' received_from = None received_by = localhostname() def _read_oldmailfile(self): '''Force list of old messages to be empty by making this a no-op, so duplicated IDs are always treated as new messages.''' self.log.trace() def write_oldmailfile(self, unused, **kwargs): '''Short-circuit writing the oldmail file.''' self.log.trace() def _getmsglist(self): '''Don't rely on UIDL; instead, use just the message number.''' self.log.trace() try: (response, msglist, octets) = self.conn.list() for line in msglist: msgnum = int(line.split()[0]) msgsize = int(line.split()[1]) self.msgnum_by_msgid[msgnum] = msgnum self.msgid_by_msgnum[msgnum] = msgnum self.msgsizes[msgnum] = msgsize self.sorted_msgnum_msgid = sorted(self.msgid_by_msgnum.items()) except poplib.error_proto as o: raise getmailOperationError('POP error (%s)' % o) self.gotmsglist = True ####################################### class BrokenUIDLPOP3Retriever(BrokenUIDLPOP3RetrieverBase, POP3initMixIn): '''For broken POP3 servers without SSL. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ) received_with = 'POP3' def __str__(self): self.log.trace() return 'BrokenUIDLPOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('BrokenUIDLPOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class BrokenUIDLPOP3SSLRetriever(BrokenUIDLPOP3RetrieverBase, POP3SSLinitMixIn): '''For broken POP3 servers with SSL. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ConfFile(name='ca_certs', required=False, default=None), ConfTupleOfStrings(name='ssl_fingerprints', required=False, default=()), ConfString(name='ssl_version', required=False, default=None), ConfString(name='ssl_ciphers', required=False, default=None), ConfString(name='ssl_cert_hostname', required=False, default=None), ) received_with = 'POP3-SSL' def __str__(self): self.log.trace() return 'BrokenUIDLPOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('BrokenUIDLPOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropPOP3Retriever(MultidropPOP3RetrieverBase, POP3initMixIn): '''Retriever class for multi-drop POP3 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ConfString(name='envelope_recipient'), ) received_from = None received_with = 'POP3' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropPOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropPOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropPOP3SSLRetriever(MultidropPOP3RetrieverBase, POP3SSLinitMixIn): '''Retriever class for multi-drop POP3-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), ConfBool(name='use_apop', required=False, default=False), ConfString(name='envelope_recipient'), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ConfFile(name='ca_certs', required=False, default=None), ConfTupleOfStrings(name='ssl_fingerprints', required=False, default=()), ConfString(name='ssl_version', required=False, default=None), ConfString(name='ssl_ciphers', required=False, default=None), ConfString(name='ssl_cert_hostname', required=False, default=None), ) received_from = None received_with = 'POP3-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropPOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropPOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropSDPSRetriever(SimplePOP3Retriever, POP3initMixIn): '''Retriever class for multi-drop SDPS (demon.co.uk) mailboxes. Extend POP3 class to include support for Demon's protocol extensions, known as SDPS. A non-standard command (*ENV) is used to retrieve the message envelope. See http://www.demon.net/helpdesk/products/mail/sdps-tech.shtml for details. Support originally requested by Paul Clifford for getmail v.2/3. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfBool(name='use_xoauth2', required=False, default=False), # Demon apparently doesn't support APOP ConfBool(name='use_apop', required=False, default=False), ) received_from = None received_with = 'SDPS' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropSDPSRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropSDPSRetriever(%s)' % self._confstring() + os.linesep) def _getmsgbyid(self, msgid): self.log.trace() msg = SimplePOP3Retriever._getmsgbyid(self, msgid) # The magic of SDPS is the "*ENV" command. Implement it: try: msgnum = self._getmsgnumbyid(msgid) resp, lines, octets = self.conn._longcmd('*ENV %i' % msgnum) except poplib.error_proto as o: raise getmailConfigurationError( 'server does not support *ENV (%s)' % o ) if len(lines) < 4: raise getmailOperationError('short *ENV response (%s)' % lines) msg.sender = lines[2] msg.recipient = lines[3] return msg ####################################### class SimpleIMAPRetriever(IMAPRetrieverBase, IMAPinitMixIn): '''Retriever class for single-user IMAPv4 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfTupleOfUnicode(name='mailboxes', required=False, default="('INBOX', )", allow_specials=('ALL',)), ConfBool(name='use_peek', required=False, default=True), ConfString(name='move_on_delete', required=False, default=None), ConfString(name='uid_cache', required=False, default=None), ConfBool(name='record_mailbox', required=False, default=True), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfBool(name='use_kerberos', required=False, default=False), ConfBool(name='use_xoauth2', required=False, default=False), ConfString(name='imap_search', required=False, default=None), ConfString(name='imap_on_delete', required=False, default=None), ConfBool(name='imap_id_extension', required=False, default=False), ) received_from = None received_with = 'IMAP4' received_by = localhostname() def __str__(self): self.log.trace() return 'SimpleIMAPRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimpleIMAPRetriever(%s)' % self._confstring() + os.linesep) ####################################### class SimpleIMAPSSLRetriever(IMAPRetrieverBase, IMAPSSLinitMixIn): '''Retriever class for single-user IMAPv4-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfTupleOfUnicode(name='mailboxes', required=False, default="('INBOX', )", allow_specials=('ALL',)), ConfBool(name='use_peek', required=False, default=True), ConfString(name='move_on_delete', required=False, default=None), ConfString(name='uid_cache', required=False, default=None), ConfBool(name='record_mailbox', required=False, default=True), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ConfFile(name='ca_certs', required=False, default=None), ConfTupleOfStrings(name='ssl_fingerprints', required=False, default=()), ConfString(name='ssl_version', required=False, default=None), ConfString(name='ssl_ciphers', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfBool(name='use_kerberos', required=False, default=False), ConfBool(name='use_xoauth2', required=False, default=False), ConfString(name='ssl_cert_hostname', required=False, default=None), ConfString(name='imap_search', required=False, default=None), ConfString(name='imap_on_delete', required=False, default=None), ConfBool(name='imap_id_extension', required=False, default=False), ) received_from = None received_with = 'IMAP4-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'SimpleIMAPSSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimpleIMAPSSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropIMAPRetriever(MultidropIMAPRetrieverBase, IMAPinitMixIn): '''Retriever class for multi-drop IMAPv4 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfTupleOfUnicode(name='mailboxes', required=False, default="('INBOX', )", allow_specials=('ALL',)), ConfBool(name='use_peek', required=False, default=True), ConfString(name='move_on_delete', required=False, default=None), ConfString(name='uid_cache', required=False, default=None), ConfBool(name='record_mailbox', required=False, default=True), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfBool(name='use_kerberos', required=False, default=False), ConfBool(name='use_xoauth2', required=False, default=False), ConfString(name='envelope_recipient'), ConfString(name='imap_search', required=False, default=None), ConfString(name='imap_on_delete', required=False, default=None), ConfBool(name='imap_id_extension', required=False, default=False), ) received_from = None received_with = 'IMAP4' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropIMAPRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropIMAPRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropIMAPSSLRetriever(MultidropIMAPRetrieverBase, IMAPSSLinitMixIn): '''Retriever class for multi-drop IMAPv4-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='password_command', required=False, default=()), ConfTupleOfUnicode(name='mailboxes', required=False, default="('INBOX', )", allow_specials=('ALL',)), ConfBool(name='use_peek', required=False, default=True), ConfString(name='move_on_delete', required=False, default=None), ConfString(name='uid_cache', required=False, default=None), ConfBool(name='record_mailbox', required=False, default=True), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ConfFile(name='ca_certs', required=False, default=None), ConfTupleOfStrings(name='ssl_fingerprints', required=False, default=()), ConfString(name='ssl_version', required=False, default=None), ConfString(name='ssl_ciphers', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfBool(name='use_kerberos', required=False, default=False), ConfBool(name='use_xoauth2', required=False, default=False), ConfString(name='envelope_recipient'), ConfString(name='ssl_cert_hostname', required=False, default=None), ConfString(name='imap_search', required=False, default=None), ConfString(name='imap_on_delete', required=False, default=None), ConfBool(name='imap_id_extension', required=False, default=False), ) received_from = None received_with = 'IMAP4-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropIMAPSSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropIMAPSSLRetriever(%s)' % self._confstring() + os.linesep) getmail6-6.19.07/getmailcore/utilities.py000077500000000000000000000551021474536475600203210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. '''Utility classes and functions for getmail. ''' import os import os.path import socket import signal import stat import time import glob import re import fcntl import pwd import grp import getpass import subprocess import sys try: import secrets except ImportError: #Py 2.7 class secrets: @staticmethod def token_hex(x): return ''.join(['%02x' % ord(char) for char in open('/dev/urandom').read(x)]) __all__ = [ 'address_no_brackets', 'change_uidgid', 'change_usergroup', 'check_ca_certs', 'check_ssl_ciphers', 'check_ssl_fingerprints', 'check_ssl_key_and_cert', 'check_ssl_version', 'deliver_maildir', 'eval_bool', 'expand_user_vars', 'format_header', 'get_password', 'gid_of_uid', 'is_maildir', 'localhostname', 'lock_file', 'logfile', 'mbox_from_escape', 'safe_open', 'tostr', 'uid_of_user', 'unicode', 'unlock_file', 'updatefile', ] if sys.version_info.major > 2: unicode = str tostr = lambda lts: lts.decode() else: tostr = lambda lts: lts # due to above unicode entry in __all__ unicode = unicode #noqa: PLW0127 try: import ssl except ImportError: ssl = None try: import hashlib except ImportError: hashlib = None # Optional gnome-keyring integration try: import gnomekeyring # And test to see if it's actually available if not gnomekeyring.is_available(): gnomekeyring = None except ImportError: gnomekeyring = None # Optional Python keyring integration try: import keyring except ImportError: keyring = None from getmailcore.exceptions import * logtimeformat = '%Y-%m-%d %H:%M:%S' _bool_values = { 'true' : True, 'yes' : True, 'on' : True, '1' : True, 'false' : False, 'no' : False, 'off' : False, '0' : False } osx_keychain_binary = '/usr/bin/security' ####################################### def lock_file(file, locktype): '''Do file locking.''' assert locktype in ('lockf', 'flock'), 'unknown lock type %s' % locktype if locktype == 'lockf': fcntl.lockf(file, fcntl.LOCK_EX) elif locktype == 'flock': fcntl.flock(file, fcntl.LOCK_EX) ####################################### def unlock_file(file, locktype): '''Do file unlocking.''' assert locktype in ('lockf', 'flock'), 'unknown lock type %s' % locktype if locktype == 'lockf': fcntl.lockf(file, fcntl.LOCK_UN) elif locktype == 'flock': fcntl.flock(file, fcntl.LOCK_UN) ####################################### def safe_open(path, mode, permissions=0o600): '''Open a file path safely. ''' if os.name != 'posix': return open(path, mode) try: fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL, permissions) file = os.fdopen(fd, mode) except OSError as o: raise getmailDeliveryError('failure opening %s (%s)' % (path, o)) return file ####################################### class updatefile(object): '''A class for atomically updating files. A new, temporary file is created when this class is instantiated. When the object's close() method is called, the file is synced to disk and atomically renamed to replace the original file. close() is automatically called when the object is deleted. ''' def __init__(self, filename): self.closed = False self.filename = filename self.tmpname = filename + '.tmp.%d' % os.getpid() # If the target is a symlink, the rename-on-close semantics of this # class would break the symlink, replacing it with the new file. # Instead, follow the symlink here, and replace the target file on # close. while os.path.islink(filename): filename = os.path.join(os.path.dirname(filename), os.readlink(filename)) try: f = safe_open(self.tmpname, 'w') except IOError as msg: raise IOError('%s, opening output file "%s"' % (msg, self.tmpname)) self.file = f self.write = f.write self.flush = f.flush def __del__(self): self.close() def abort(self): try: if hasattr(self, 'file'): self.file.close() except IOError: pass self.closed = True def close(self): if self.closed or not hasattr(self, 'file'): return self.file.flush() os.fsync(self.file.fileno()) self.file.close() os.rename(self.tmpname, self.filename) self.closed = True ####################################### class logfile(object): '''A class for locking and appending timestamped data lines to a log file. ''' def __init__(self, filename): self.closed = False self.filename = filename try: self.file = open(expand_user_vars(self.filename), 'a') except IOError as msg: raise IOError('%s, opening file "%s"' % (msg, self.filename)) def __del__(self): self.close() def __str__(self): return 'logfile(filename="%s")' % self.filename def close(self): if self.closed: return self.file.flush() self.file.close() self.closed = True def write(self, s): try: lock_file(self.file, 'flock') # Seek to end self.file.seek(0, 2) self.file.write(time.strftime(logtimeformat, time.localtime()) + ' ' + s.rstrip() + os.linesep) self.file.flush() finally: unlock_file(self.file, 'flock') ####################################### def format_params(d, maskitems=('password', ), skipitems=()): '''Take a dictionary of parameters and return a string summary. ''' s = '' for key in list(sorted(d.keys())): if key in skipitems: continue if s: s += ',' if key in maskitems: s += '%s=*' % key else: s += '%s="%s"' % (key, d[key]) return s ################################### def alarm_handler(*unused): '''Handle an alarm during maildir delivery. Should never happen. ''' raise getmailDeliveryError('Delivery timeout') ####################################### def is_maildir(d): '''Verify a path is a maildir. ''' dir_parent = os.path.dirname(d.endswith('/') and d[:-1] or d) or '.' if not os.access(dir_parent, os.X_OK): raise getmailConfigurationError( 'cannot read contents of parent directory of %s ' '- check permissions and ownership' % d ) if not os.path.isdir(d): return False if not os.access(d, os.X_OK): raise getmailConfigurationError( 'cannot read contents of directory %s ' '- check permissions and ownership' % d ) for sub in ('tmp', 'cur', 'new'): subdir = os.path.join(d, sub) if not os.path.isdir(subdir): return False if not os.access(subdir, os.W_OK): raise getmailConfigurationError( 'cannot write to maildir %s ' '- check permissions and ownership' % d ) return True ####################################### def deliver_maildir(maildirpath, data, hostname, dcount=None, filemode=0o600): '''Reliably deliver a mail message into a Maildir. Uses Dan Bernstein's documented rules for maildir delivery, and the updated naming convention for new files (modern delivery identifiers). See http://cr.yp.to/proto/maildir.html and http://qmail.org/man/man5/maildir.html for details. ''' if not is_maildir(maildirpath): raise getmailDeliveryError('not a Maildir (%s)' % maildirpath) # Set a 24-hour alarm for this delivery signal.signal(signal.SIGALRM, alarm_handler) signal.alarm(24 * 60 * 60) info = { 'deliverycount' : dcount, 'hostname' : hostname.replace('/', '\\057').replace( ':', '\\072'), 'pid' : os.getpid(), } dir_tmp = os.path.join(maildirpath, 'tmp') dir_new = os.path.join(maildirpath, 'new') for unused in range(3): t = time.time() info['secs'] = int(t) info['usecs'] = int((t - int(t)) * 1000000) info['unique'] = 'M%(usecs)dP%(pid)s' % info if info['deliverycount'] is not None: info['unique'] += 'Q%(deliverycount)s' % info info['unique'] += 'R%s' % secrets.token_hex(8) filename = '%(secs)s.%(unique)s.%(hostname)s' % info fname_tmp = os.path.join(dir_tmp, filename) fname_new = os.path.join(dir_new, filename) # File must not already exist if os.path.exists(fname_tmp): # djb says sleep two seconds and try again time.sleep(2) continue # Be generous and check cur/file[:...] just in case some other, dumber # MDA is in use. We wouldn't want them to clobber us and have the user # blame us for their bugs. curpat = os.path.join(maildirpath, 'cur', filename) + ':*' collision = glob.glob(curpat) if collision: # There is a message in maildir/cur/ which could be clobbered by # a dumb MUA, and which shouldn't be there. Abort. raise getmailDeliveryError('collision with %s' % collision) # Found an unused filename break else: signal.alarm(0) raise getmailDeliveryError('failed to allocate file in maildir') # Get user & group of maildir s_maildir = os.stat(maildirpath) # Open file to write try: if sys.version_info.major > 2: f = safe_open(fname_tmp, 'bw', filemode) else: f = safe_open(fname_tmp, 'w', filemode) f.write(data) f.flush() os.fsync(f.fileno()) f.close() except IOError as o: signal.alarm(0) raise getmailDeliveryError('failure writing file %s (%s)' % (fname_tmp, o)) # Move message file from Maildir/tmp to Maildir/new try: # #https://pypi.org/project/getmail_shutils/#description # #Version of getmail using shutil instead of os.link to allow it to be used with a shared destination folder in a VM. # shutil.copyfile(fname_tmp, fname_new) os.link(fname_tmp, fname_new) os.unlink(fname_tmp) except OSError: signal.alarm(0) try: os.unlink(fname_tmp) except KeyboardInterrupt: raise except Exception: pass raise getmailDeliveryError('failure renaming "%s" to "%s"' % (fname_tmp, fname_new)) # Delivery done # Cancel alarm signal.alarm(0) signal.signal(signal.SIGALRM, signal.SIG_DFL) return filename ####################################### def mbox_from_escape(s): '''Escape spaces, tabs, and newlines in the envelope sender address.''' return ''.join([(c in (' ', '\t', '\n')) and '-' or c for c in s]) or '<>' ####################################### def address_no_brackets(addr): '''Strip surrounding <> on an email address, if present.''' straddr = str(addr) splitaddr = straddr.split('<') if len(splitaddr) > 1: return splitaddr[1].split('>')[0] else: return straddr ####################################### def eval_bool(s): '''Handle boolean values intelligently. ''' try: return _bool_values[str(s).lower()] except KeyError: raise getmailConfigurationError( 'boolean parameter requires value to be one of true or false, ' 'not "%s"' % s ) ####################################### def gid_of_uid(uid): try: return pwd.getpwuid(uid).pw_gid except KeyError as o: raise getmailConfigurationError('no such specified uid (%s)' % o) ####################################### def uid_of_user(user): try: return pwd.getpwnam(user).pw_uid except KeyError as o: raise getmailConfigurationError('no such specified user (%s)' % o) ####################################### def change_usergroup(logger=None, user=None, group=None): ''' Change the current effective GID and UID to those specified by user and group. ''' uid = None gid = None if group: if logger: logger.debug('Getting GID for specified group %s\n' % group) try: gid = grp.getgrnam(group).gr_gid except KeyError as o: raise getmailConfigurationError('no such specified group (%s)' % o) if user: if logger: logger.debug('Getting UID for specified user %s\n' % user) uid = uid_of_user(user) change_uidgid(logger, uid, gid) ####################################### def change_uidgid(logger=None, uid=None, gid=None): ''' Change the current effective GID and UID to those specified by uid and gid. ''' try: if gid: if os.getegid() != gid: if logger: logger.debug('Setting egid to %d\n' % gid) os.setregid(gid, gid) if uid: if os.geteuid() != uid: if logger: logger.debug('Setting euid to %d\n' % uid) os.setreuid(uid, uid) except OSError as o: raise getmailDeliveryError('change UID/GID to %s/%s failed (%s)' % (uid, gid, o)) ####################################### def format_header(name, line): '''Take a long line and return rfc822-style multiline header. ''' header = '' line = (name.strip() + ': ' + ' '.join([part.strip() for part in line.splitlines()])) # Split into lines of maximum 78 characters long plus newline, if # possible. A long line may result if no space characters are present. while line and len(line) > 78: i = line.rfind(' ', 0, 78) if i == -1: # No space in first 78 characters, try a long line i = line.rfind(' ') if i == -1: # No space at all break if header: header += os.linesep + ' ' header += line[:i] line = line[i:].lstrip() if header: header += os.linesep + ' ' if line: header += line.strip() + os.linesep return header ####################################### def expand_user_vars(s): '''Return a string expanded for both leading "~/" or "~username/" and environment variables in the form "$varname" or "${varname}". ''' return os.path.expanduser(os.path.expandvars(s)) ####################################### def localhostname(): '''Return a name for localhost which is (hopefully) the "correct" FQDN. ''' n = socket.gethostname() if '.' in n: return n return socket.getfqdn() ####################################### def check_ssl_key_and_cert(conf): keyfile = conf['keyfile'] if keyfile is not None: keyfile = expand_user_vars(keyfile) certfile = conf['certfile'] if certfile is not None: certfile = expand_user_vars(certfile) if keyfile and not os.path.isfile(keyfile): raise getmailConfigurationError( 'optional keyfile must be path to a valid file' ) if certfile and not os.path.isfile(certfile): raise getmailConfigurationError( 'optional certfile must be path to a valid file' ) if (keyfile is None) ^ (certfile is None): raise getmailConfigurationError( 'optional certfile and keyfile must be supplied together' ) return (keyfile, certfile) ####################################### def check_ca_certs(conf): ca_certs = conf['ca_certs'] if ca_certs is not None: ca_certs = expand_user_vars(ca_certs) if ssl is None: raise getmailConfigurationError( 'specifying ca_certs not supported by this Python' ) if ca_certs and not os.path.isfile(ca_certs): raise getmailConfigurationError( 'optional ca_certs must be path to a valid file' ) return ca_certs ####################################### def check_ssl_version(conf): ssl_version = conf['ssl_version'] if ssl_version is None: return None if ssl is None: raise getmailConfigurationError( 'specifying ssl_version not supported by this Python' ) def get_or_fail(version, symbol): if symbol is not None: v = getattr(ssl, symbol, None) if v is not None: return v raise getmailConfigurationError( 'unknown or unsupported ssl_version "%s"' % version ) ssl_version = ssl_version.lower() if ssl_version == 'sslv23': return get_or_fail(ssl_version, 'PROTOCOL_SSLv23') elif ssl_version == 'sslv3': return get_or_fail(ssl_version, 'PROTOCOL_SSLv3') elif ssl_version == 'tlsv1': return get_or_fail(ssl_version, 'PROTOCOL_TLSv1') elif ssl_version == 'tlsv1_1' and 'PROTOCOL_TLSv1_1' in dir(ssl): return get_or_fail(ssl_version, 'PROTOCOL_TLSv1_1') elif ssl_version == 'tlsv1_2' and 'PROTOCOL_TLSv1_2' in dir(ssl): return get_or_fail(ssl_version, 'PROTOCOL_TLSv1_2') return get_or_fail(ssl_version, None) ####################################### def check_ssl_fingerprints(conf): ssl_fingerprints = conf['ssl_fingerprints'] if not ssl_fingerprints: return () if ssl is None or hashlib is None: raise getmailConfigurationError( 'specifying ssl_fingerprints not supported by this Python' ) normalized_fprs = [] for fpr in ssl_fingerprints: fpr = fpr.lower().replace(':','') if len(fpr) != 64: raise getmailConfigurationError( 'ssl_fingerprints must each be the SHA256 certificate hash in hex (with or without colons)' ) normalized_fprs.append(fpr) return normalized_fprs ####################################### def check_ssl_ciphers(conf): ssl_ciphers = conf['ssl_ciphers'] if ssl_ciphers: if sys.version_info < (2, 7, 0): raise getmailConfigurationError( 'specifying ssl_ciphers not supported by this Python' ) if re.search(r'[^a-zA-z0-9, :!\-+@=]', ssl_ciphers): raise getmailConfigurationError( 'invalid character in ssl_ciphers' ) return ssl_ciphers ####################################### keychain_password = None if os.name == 'posix': if keyring: def keychain_password(user, server, protocol, logger): return keyring.get_password(server,user) elif os.path.isfile(osx_keychain_binary): def keychain_password(user, server, protocol, logger): """Mac OSX: return a keychain password, if it exists. Otherwise, return None. """ # OSX protocol is not an arbitrary string; it's a code limited to # 4 case-sensitive chars, and only specific values. protocol = protocol.lower() if 'imap' in protocol: protocol = 'imap' elif 'pop' in protocol: protocol = 'pop3' else: # This will break. protocol = '????' # wish we could pass along a comment to this thing for the user prompt cmd = "%s find-internet-password -g -a '%s' -s '%s' -r '%s'" % ( osx_keychain_binary, user, server, protocol ) (status, output) = subprocess.getstatusoutput(cmd) if status != os.EX_OK or not output: logger.error('keychain command %s failed: %s %s' % (cmd, status, output)) return None password = None for line in output.split('\n'): #match = re.match(r'password: "([^"]+)"', line) #if match: # password = match.group(1) if 'password:' in line: pw = line.split(':', 1)[1].strip() if pw.startswith('"') and pw.endswith('"'): pw = pw[1:-1] password = pw if password is None: logger.debug('No keychain password found for %s %s %s\n' % (user, server, protocol)) return password elif gnomekeyring: def keychain_password(user, server, protocol, logger): """Gnome: return a keyring password, if it exists. Otherwise, return None. """ #logger.trace('trying Gnome keyring for user="%s", server="%s", protocol="%s"\n' # % (user, server, protocol)) try: # http://developer.gnome.org/gnome-keyring/3.5/gnome-keyring # -Network-Passwords.html#gnome-keyring-find-network-password-sync secret = gnomekeyring.find_network_password_sync( # user, domain=None, server, object=None, protocol, # authtype=None, port=0 user, None, server, None, protocol, None, 0 ) #logger.trace('got keyring result %s' % str(secret)) except gnomekeyring.NoMatchError: logger.debug('gnome-keyring does not know password for %s %s %s\n' % (user, server, protocol)) return None # secret looks like this: # [{'protocol': 'imap', 'keyring': 'Default', 'server': 'gmail.com', # 'user': 'hiciu', 'item_id': 1L, 'password': 'kielbasa'}] if secret and 'password' in secret[0]: return secret[0]['password'] return None #else: # Posix but no OSX keychain or Gnome keyring. # Fallthrough if keychain_password is None: def keychain_password(user, server, protocol, logger): """Neither Mac OSX keychain or Gnome keyring available: always return None. """ return None ####################################### def get_password(label, user, server, protocol, logger): # try keychain/keyrings first, where available password = keychain_password(user, server, protocol, logger) if password: logger.debug('using password from keychain/keyring\n') else: # no password found (or not on OSX), prompt in the usual way password = getpass.getpass('Enter password for %s: ' % label) return password getmail6-6.19.07/getmails000077500000000000000000000040101474536475600151610ustar00rootroot00000000000000#!/bin/sh # vim:se tw=78 sts=4: # Copyright (C) 2011-2017 Osamu Aoki , GPL2+ : ' Run getmail in parallel for all config files. Since getmail6 v6.18, if no --rcfile , getmail itself fetches using all config files, but serially. ' set -e if [ "$1" = "-p" ]; then para=true shift elif [ "$1" = "--" ]; then para=false shift else para=false fi # check file extension endwith () { [ "$1" != "${1%$2}" ] && return 0 || return 1 } startswith () { BASE1=${1##*/} [ "$BASE1" != "${BASE1#$2}" ] && return 0 || return 1 } isrc() { ! endwith "$1" '~' && \ ! endwith "$1" '#' && \ ! startswith "$1" 'oldmail-' && \ ! startswith "$1" '.' && \ ! endwith "$1" '.json' && \ ! endwith "$1" '.swp' && \ ! endwith "$1" '.bak' && \ [ -f "$1" ] && return 0 || return 1; } UID_BY_ID=$(id -u) PID_GETMAILS=$(pgrep -U $UID_BY_ID '^getmails$') if [ "x$PID_GETMAILS" != "x$$" ]; then echo "The getmails script is already running as PID=\"$PID_GETMAILS\" ." >&2 exit 1 fi configdir=${XDG_CONFIG_HOME:-$HOME/.config} if [ ! -d "$configdir" ]; then getmailrcdir="$HOME/.getmail/config" else getmailrcdir="$configdir/getmail" fi if [ -f $getmailrcdir/stop ]; then #note: stop needs to be in the config folder now echo "Do not run getmail ... (if not, remove $getmailrcdir/stop)" >&2 exit 1 fi rcfiles="getmail" # Address concerns raised by #863856 # emacs backup files: foo~ foo# # vim backup files: foo~ foo.swp # generic backup files: foo.bak # These are excluded but let's allow file names with @ if $para ; then pids="" for file in $getmailrcdir/* ; do if isrc $file ; then $rcfiles --rcfile "$file" "$@" & pids="$pids $!" fi done rc=0 if [ -n "$pids" ]; then for pid in $pids ; do wait $pid prc=$? if [ $prc -gt $rc ]; then rc=$prc fi done fi exit $rc else for file in $getmailrcdir/* ; do if isrc $file ; then rcfiles="$rcfiles --rcfile \"$file\"" fi done eval "$rcfiles $@" exit $? fi getmail6-6.19.07/pypi.sh000077500000000000000000000005501474536475600147530ustar00rootroot00000000000000# docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. if git status | grep nothing; then make dist make up6 sed -i -e "s/name.*=.*getmail6./name = 'getmail'/g" setup.py make dist make up git reset --hard else echo Commit changes first! fi getmail6-6.19.07/pyproject.toml000066400000000000000000000045471474536475600163610ustar00rootroot00000000000000[tool.codespell] ignore-words-list = "fpr,hepler,loos,te,thrid" [tool.ruff] lint.select = [ "AIR", # Airflow "ASYNC", # flake8-async "B", # flake8-bugbear "C90", # McCabe cyclomatic complexity "DTZ", # flake8-datetimez "E", # pycodestyle "F", # Pyflakes "FA", # flake8-future-annotations "FLY", # flynt "ICN", # flake8-import-conventions "INT", # flake8-gettext "ISC", # flake8-implicit-str-concat "PL", # Pylint "PT", # flake8-pytest-style "PYI", # flake8-pyi "RSE", # flake8-raise "RUF", # Ruff-specific rules "T10", # flake8-debugger "TCH", # flake8-type-checking "TID", # flake8-tidy-imports "W", # pycodestyle "YTT", # flake8-2020 # "A", # flake8-builtins # "ANN", # flake8-annotations # "ARG", # flake8-unused-arguments # "BLE", # flake8-blind-except # "C4", # flake8-comprehensions # "COM", # flake8-commas # "D", # pydocstyle # "DJ", # flake8-django # "EM", # flake8-errmsg # "ERA", # eradicate # "EXE", # flake8-executable # "FBT", # flake8-boolean-trap # "FIX", # flake8-fixme # "G", # flake8-logging-format # "I", # isort # "INP", # flake8-no-pep420 # "N", # pep8-naming # "NPY", # NumPy-specific rules # "PD", # pandas-vet # "PGH", # pygrep-hooks # "PIE", # flake8-pie # "PLR091", # Pylint Refactor just for max-args, max-branches, etc. # "PTH", # flake8-use-pathlib # "Q", # flake8-quotes # "RET", # flake8-return # "S", # flake8-bandit # "SIM", # flake8-simplify # "SLF", # flake8-self # "T20", # flake8-print # "TD", # flake8-todos # "TRY", # tryceratops # "UP", # pyupgrade ] lint.ignore = [ "B007", "B904", "E401", "E402", "E711", "E712", "E713", "E722", "E731", "F401", "F403", "F405", "F841", "PLR0402", "PLW2901", "RUF005", "RUF021", "RUF022", ] line-length = 142 target-version = "py37" [tool.ruff.lint.mccabe] max-complexity = 16 [tool.ruff.lint.pylint] allow-magic-value-types = ["bytes", "int", "str"] max-args = 11 max-branches = 20 max-returns = 7 max-statements = 56 [tool.ruff.lint.per-file-ignores] "__init__.py" = ["E402"] "test/*" = ["S101"] getmail6-6.19.07/setup.py000066400000000000000000000076731474536475600151620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6 # Please refer to the git history regarding who changed what and when in this file. import sys import os.path from setuptools import setup import distutils.sysconfig import site from getmailcore import __version__, __license__ # # distutils doesn't seem to handle documentation files specially; they're # just "data" files. The problem is, there's no easy way to say "install # the doc files under /doc/-/ (obeying any # --home= or --prefix=, which would be "normal". # This hacks around this limitation. # prefix = distutils.sysconfig.get_config_var('prefix') datadir = None args = sys.argv[1:] for (pos, arg) in enumerate(args): # hack hack hack if arg.startswith('--prefix='): # hack hack hack hack hack prefix = arg.split('=', 1)[1] elif arg == '--prefix': # hack hack hack hack hack hack hack prefix = args[pos + 1] elif arg.startswith('--install-data='): # hack hack hack hack hack datadir = arg.split('=', 1)[1] elif arg == '--install-data': # hack hack hack hack hack hack hack datadir = args[pos + 1] DOCDIR = os.path.join('share','doc','getmail-%s' % __version__) GETMAILDOCDIR = os.path.join(datadir or prefix, DOCDIR) MANDIR = os.path.join('share','man','man1') GETMAILMANDIR = os.path.join( datadir or prefix, MANDIR) if '--show-default-install-dirs' in args: print('Default installation directories:') print(' scripts : %s' % distutils.sysconfig.get_config_var('BINDIR')) print(' Python modules : %s' % distutils.sysconfig.get_python_lib()) print(' documentation : %s' % GETMAILDOCDIR) print(' man(1) pages : %s' % GETMAILMANDIR) raise SystemExit setup( name='getmail6', version=__version__, description='a mail retrieval, sorting, and delivering system', long_description=open('README').read(), author='Charles Cazabon, Roland Puntaier, and others', author_email='charlesc-getmail@pyropus.ca', maintainer_email='roland.puntaier@gmail.com', license=__license__, url='https://www.getmail6.org/', download_url='https://github.com/getmail6/getmail6/releases', classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Natural Language :: English', 'Operating System :: OS Independent', 'Operating System :: POSIX', 'Programming Language :: Python', 'Topic :: Communications :: Email', 'Topic :: Communications :: Email :: Filters', 'Topic :: Communications :: Email :: Post-Office :: IMAP', 'Topic :: Communications :: Email :: Post-Office :: POP3', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Utilities', ], packages=[ 'getmailcore' ], scripts=[ 'getmail', 'getmails', 'getmail_fetch', 'getmail_maildir', 'getmail_mbox', 'getmail-gmail-xoauth-tokens', ], data_files=[ (DOCDIR, [ './README', 'docs/BUGS', 'docs/COPYING', 'docs/CHANGELOG', 'docs/THANKS', 'docs/configuration.html', 'docs/configuration.txt', 'docs/documentation.html', 'docs/documentation.txt', 'docs/faq.html', 'docs/faq.txt', 'docs/getmaildocs.css', 'docs/getmailrc-examples', 'docs/troubleshooting.html', 'docs/troubleshooting.txt', ]), (MANDIR, [ 'docs/getmails.1', 'docs/getmail.1', 'docs/getmail_fetch.1', 'docs/getmail_maildir.1', 'docs/getmail_mbox.1', ]), ], ) getmail6-6.19.07/test/000077500000000000000000000000001474536475600144125ustar00rootroot00000000000000getmail6-6.19.07/test/README.rst000066400000000000000000000013031474536475600160760ustar00rootroot00000000000000Tests ===== Requirements: - pip install pytest bats is part of docker-mailserver. A `docker-mailserver `__ image is created at ``/tmp/mailserver``. To reset the test setup: .. code:: sh cd /tmp/mailserver docker-compose down cd .. sudo rm -rf /tmp/mailserver/ The ``sudo`` is needed for the self-signed ``.pem`` files, created by ``self_sign.sh``. The tests are in ``.bats`` files. ``make test3`` runs tests for Python 3. You might need to enter a python virtual environment first. ``/tmp/mailserver`` is not renewed on repeated runs. To force a renew in the next make run. .. code:: sh rm -rf /tmp/mailserver/python? getmail6-6.19.07/test/docker-compose.yml000066400000000000000000000014501474536475600200470ustar00rootroot00000000000000services: mailserver: build: context: /tmp/mailserver/docker-mailserver-getmail6test image: docker-mailserver-getmail6test hostname: ${HOSTNAME} domainname: ${DOMAINNAME} container_name: ${CONTAINER_NAME} env_file: mailserver.env ports: - "25:25" - "143:143" - "587:587" - "993:993" volumes: - maildata:/var/mail - mailstate:/var/mail-state - maillogs:/var/log/mail - ./config/:/tmp/docker-mailserver/${SELINUX_LABEL} restart: always environment: - SSL_TYPE=self-signed - ENABLE_POP3=1 - ENABLE_CLAMAV=1 - ENABLE_SPAMASSASSIN=1 - DOVECOT_TLS="yes" - PSS=test - TESTEMAIL=address@domain.tld cap_add: - SYS_PTRACE volumes: maildata: mailstate: maillogs: getmail6-6.19.07/test/docker-mailserver-getmail6test/000077500000000000000000000000001474536475600224365ustar00rootroot00000000000000getmail6-6.19.07/test/docker-mailserver-getmail6test/Dockerfile000066400000000000000000000012561474536475600244340ustar00rootroot00000000000000FROM ghcr.io/docker-mailserver/docker-mailserver:9.0.1 RUN apt-get update && apt-get -y install \ git \ iputils-ping \ make \ nmap \ procmail \ python3-pip \ && rm -rf /var/lib/apt/lists/* # add our user to postfix group so we can access dovecot's LMTP unix socket RUN useradd -m -s /bin/bash getmail \ && usermod -a -G postfix getmail COPY entrypoint.sh / RUN chmod +x /entrypoint.sh # use our own entrypoint ENTRYPOINT ["/entrypoint.sh"] # from https://github.com/docker-mailserver/docker-mailserver/blob/014dddafbc2e329b7c35aada498eeba8b940d83d/Dockerfile#L291 CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"] getmail6-6.19.07/test/docker-mailserver-getmail6test/entrypoint.sh000066400000000000000000000010151474536475600252020ustar00rootroot00000000000000#!/bin/bash -e # add user if it doesn't exist if ! /usr/local/bin/listmailuser 2>/dev/null | egrep "^${TESTEMAIL}$" > /dev/null; then echo "* creating mailuser" # the env vars are set by our docker-compose.yml /usr/local/bin/addmailuser "${TESTEMAIL}" "${PSS}" /usr/local/bin/addmailuser "other-user@example.com" "${PSS}" fi if [[ ! -e "/tmp/docker-mailserver/ssl" ]]; then echo "* generating SSL certificate" /tmp/docker-mailserver/self_sign.sh fi # run original init exec /usr/bin/dumb-init -- $@getmail6-6.19.07/test/prepare_test.sh000077500000000000000000000352731474536475600174600ustar00rootroot00000000000000#!/usr/bin/env bash # cwd = getmail/test DOCKER_CONFIG="/tmp/docker-mailserver" HOST_CONFIG="/tmp/mailserver/config" : ' # requires pytest, docker, docker-compose < V2 (V2 does not work) #---------------------------------------------------------- cd getmail6 # run all tests make test3 # force renew of /tmp/mailserver when running `make test`: rm /tmp/mailserver/python? # manual commands as root on docker #---------------------------------------------------------- cd test . ./prepare_test.sh clone_mailserver copy_tests # repeat on change # check mailserver after clone_mailserver /tmp/mailserver docker-compose ps docker-compose down docker-compose up -d # interactive as root d_prompt 0 # manual_install_in_mailserver #install getmail6 copied via prepare_test.sh copy_tests cd /tmp/docker-mailserver/getmail6/test . ./prepare_test.sh install_getmail # self_sign /tmp/docker-mailserver/self_sign.sh # TESTEMAIL from self_sign.sh source /tmp/docker-mailserver/self_sign.sh # add email which addmailuser ls /usr/local/bin/*mail* echo $TESTEMAIL $PSS addmailuser $TESTEMAIL $PSS listmailuser # dont delete the mail account as used for the next steps # delmailuser $TESTEMAIL # exit # run commands as user on docker #---------------------------------------------------------- ## prepare_test.sh used in test_getmail_with_docker_mailserver.bats cd /home/roland/mine/getmail6/test source prepare_test.sh echo $GETMAIL6REPO copy_tests cd /tmp/mailserver docker-compose up -d d_prompt 0 cd /tmp/docker-mailserver/getmail6/test . ./prepare_test.sh install_getmail exit d_prompt getmail getmail --version cd /tmp/docker-mailserver/getmail6/test . ./prepare_test.sh simple_dest_maildir POP3 true true simple_dest_maildir IMAP true true "record_mailbox=true" simple_dest_maildir IMAP true true "uid_cache=uid.txt" exit d_simple_dest_maildir "POP3 true true" ' #getmail6/test CWD="$(pwd)" # echo $CWD GETMAIL6REPO=${CWD%/*} source self_sign.sh export PSS TESTEMAIL NAME MAILSERVERSOURCE="${HOME}/msrc/docker-mailserver" if [[ ! -d "$MAILSERVERSOURCE" ]]; then MAILSERVERSOURCE='https://github.com/docker-mailserver/docker-mailserver' fi # echo $MAILSERVERSOURCE function clone_mailserver() { # clone to reuse bats scripts if [[ ! -f /tmp/mailserver/python3 ]]; then if [[ -d /tmp/mailserver ]]; then cd /tmp/mailserver docker-compose down &>/dev/null cd $CWD echo "need sudo to rm /tmp/mailserver" sudo rm -rf /tmp/mailserver fi git clone --recursive -c advice.detachedHead=false -b v9.0.1 $MAILSERVERSOURCE /tmp/mailserver cd /tmp/mailserver touch /tmp/mailserver/python3 yes | cp -f $CWD/docker-compose.yml /tmp/mailserver/ cp -R $CWD/docker-mailserver-getmail6test /tmp/mailserver/ cat > /tmp/mailserver/.env << EOF HOSTNAME="mail" DOMAINNAME="domain.tld" CONTAINER_NAME="${NAME}" SELINUX_LABEL="" EOF chmod a+x /tmp/mailserver/setup.sh fi } function copy_tests() { yes | cp -f $GETMAIL6REPO/test/*.bats /tmp/mailserver/test/ yes | cp -f $GETMAIL6REPO/test/self_sign.sh /tmp/mailserver/config/ cd /tmp/mailserver/config echo "need sudo to rm /tmp/mailserver/config/getmail6" sudo rm -rf getmail6 cp -R $GETMAIL6REPO getmail6 } function docker_up() { cd /tmp/mailserver # start container if not running if ! docker exec -t ${NAME} bash -c ":" &>/dev/null ; then docker-compose up --build -d docker-compose ps # update ClamAV after startup docker exec -u 0 -t ${NAME} bash -c "freshclam &> /dev/null" fi # always reinstall getmail6 to get newest changes docker exec -u 0 -t ${NAME} bash -c "yes | pip3 uninstall getmail6" docker exec -u 0 -t ${NAME} bash -c "rm /tmp/docker-mailserver/getmail6/pyproject.toml && pip3 install -e /tmp/docker-mailserver/getmail6" } d_prompt(){ docker exec -u $1 -it mail.domain.tld bash } install_getmail() { apt-get update apt-get -y install git make procmail iputils-ping nmap python3-pip vim cd /tmp/docker-mailserver/getmail6 rm pyproject.toml pip3 install -e . } #---- for test_getmail_with_docker_mailserver.bats ----# export TMPMAIL=/home/getmail/Mail export MAILDIR=$TMPMAIL/$TESTEMAIL export MAILDIRIN=$MAILDIR/INBOX declare -A PORTNR PORTNR["POP3"]=110 PORTNR["IMAP"]=143 PORTNR["IMAPSSL"]=993 PORTNR["POP3SSL"]=995 PORTNR["SMTP"]=25 PORTNR["SMTPSSL"]=587 export PORTNR d_info(){ cat > $HOME/d_info.sh << EOF RANDOMTXT="$RANDOMTXT" EOF } d_docker(){ cd /tmp/mailserver docker exec -u getmail $NAME bash -c "cd /tmp/docker-mailserver/getmail6/test source prepare_test.sh $@ " } ports_test(){ nmap -p25 localhost nmap -p143 localhost nmap -p587 localhost nmap -p993 localhost } d_ports_test(){ d_docker ports_test } randomtext(){ tr -dc A-Za-z0-9 /home/getmail/getmail < /home/getmail/getmail < /home/getmail/procmail < /home/getmail/getmail < /home/getmail/pass <

Dear Sir/Madam,

------=_NextPart_000_0012_A796884C.DCABE8FF-- Content-Type: multipart/mixed; boundary=\"----=_NextPart_000_0012_A796884C.DCABE8FF\" This is the test text. ------=_NextPart_000_0012_A796884C.DCABE8FF-- . QUIT EOF sleep 1 } d_multidropmail(){ d_docker multidropmail } multidrop_test() { RETRIEVER=$1 PORT=$2 multidropmail mail_clean cat > /home/getmail/getmail < /home/getmail/getmail < /home/getmail/getmail < /home/getmail/lmtpd.py < /home/getmail/getmail < /home/getmail/getmail < /home/getmail/getmail < /home/getmail/getmail < $CATOP/crlnumber << EOF 01 EOF SUBJCA="${SUBJ}for.test.ca" CONFIG="-subj $SUBJCA -passin pass:$PSS -passout pass:$PSS" openssl req $CONFIG -new -keyout $CATOP/private/cakey.pem -out $CATOP/careq.pem 2> /dev/null openssl ca -subj $SUBJCA -passin pass:$PSS -create_serial \ -out $CATOP/cacert.pem $CADAYS -batch \ -keyfile $CATOP/private/cakey.pem \ -selfsign -extensions v3_ca -infiles $CATOP/careq.pem 2> /dev/null } # persists function generate_self_signed() { SSL_CFG_PATH="/tmp/docker-mailserver/ssl" if [[ -f ${CATOP}/private/cakey.pem ]]; then return 0 fi #from /usr/local/bin/generate-ssl-certificate if [[ -z ${1} ]] then FQDN="$(hostname --fqdn)" else FQDN="${1}" fi if [[ ! -d ${SSL_CFG_PATH} ]] then mkdir "${SSL_CFG_PATH}" fi cd "${SSL_CFG_PATH}" || { echo "cd ${SSL_CFG_PATH} error" ; exit ; } rm -rf * create_demo_CA # Create an unpassworded private key and create an unsigned public key certificate openssl req -subj "${SUBJ}{$FQDN}" \ -new -nodes -keyout "${SSL_CFG_PATH}"/"${FQDN}"-key.pem \ -out "${SSL_CFG_PATH}"/"${FQDN}"-req.pem -days 3652 2> /dev/null [[ -f "${SSL_CFG_PATH}"/"${FQDN}"-key.pem ]] && echo "${FQDN}-key.pem is there" [[ -f "${SSL_CFG_PATH}"/"${FQDN}"-req.pem ]] && echo "${FQDN}-req.pem is there" # Sign the public key certificate with CA certificate openssl ca -out "${SSL_CFG_PATH}"/"${FQDN}"-cert.pem -batch \ -passin pass:$PSS -infiles "${SSL_CFG_PATH}"/"${FQDN}"-req.pem 2> /dev/null [[ -f "${SSL_CFG_PATH}"/"${FQDN}"-cert.pem ]] && echo "${FQDN}-cert.pem is there" # Combine certificates for courier cat "${SSL_CFG_PATH}"/"${FQDN}"-key.pem \ "${SSL_CFG_PATH}"/"${FQDN}"-cert.pem > "${SSL_CFG_PATH}"/"${FQDN}"-combined.pem [[ -f "${SSL_CFG_PATH}"/"${FQDN}"-combined.pem ]] && echo "${FQDN}-combined.pem is there" } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then generate_self_signed fi getmail6-6.19.07/test/spam.eml000066400000000000000000000232511474536475600160540ustar00rootroot00000000000000Delivered-To: EMAIL-REDACTED@example.com Received: by 2002:a05:6022:309a:b0:43:2441:240c with SMTP id f26csp669608laa; Tue, 15 Aug 2023 15:10:06 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHcLZeOyLv5owq9ieSZGCRD3fyuOoK1I1LVDAk5WEq0xaFijf4RG8s1q06TBOJwIhfOwA+B X-Received: by 2002:a05:6a20:2447:b0:144:3272:31ec with SMTP id t7-20020a056a20244700b00144327231ecmr196341pzc.23.1692137405688; Tue, 15 Aug 2023 15:10:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1692137405; cv=none; d=google.com; s=arc-20160816; b=DbEmcWvdisuZy3Wh4VeaGOuoQNkXvidN3qbCdhHBSVUk1rfflcbGQRd48BeOC6JfTk VqNsc2QL2uOQYnvltraO4Xa0K5QPV8gN1jDTVlSIryWe8e5Hi3GTkPasRUU9xyav4ZF6 r2w39px0e0lYcPn7OxvIzCzgVv4bciQa/ybc730ciW16n7SsHrJ4kuOGqgxxE444QUSP I484K92BUfxNFX8DtvCiCI6EKllc/la4Lham17h1dNf3ROq1PsgFp9NJfVfkKb8J6eX2 /zfAagUjIBT1JNQMqs42OAASo3PrBLqVgkAMbCEWRdmx0iYi5Q82FCQpCQBIbXAkf7J3 jBEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=feedback-id:message-id:to:subject:envelope-to:list-unsubscribe:from :date; bh=tRUnM384fXQnm8eyAilCpWIl8tnMdNkl7Rj9c5D29bU=; fh=kfoG8ec0SP6dly43PxymCzxcZ4LwCW+f/2tiWqcxHRM=; b=n2V0zYfeMpsJSzLIq2WrBRo8ZqN9ZhmD+VRCM8ld9EgrwcXeDjdo24DAIxJj/FcUHb dRS0oo3q0dqnSkRAgOWGtcYUDXIkD+stcvlpD2zaoa3XeNFV6Ph8BZxUovtOReHDflkV N6dvm3n3OyMf3xEsB5qz41vWULzLsVWZnIxkqz9xRPbbT4Tie2IVLardlBNa+/Dja5io iI43b8AwrMRQPzYlf5xwfNRJqiQ9WFYCTUM8y2C4f6aQGtCwPy120yZ9psWsBUFD+K5y JUEb611Y9TpY3K5QCSJEKVvKyHzuYETfjeuDGHhqvNiGPT2gk7OiENSmf49EuynFPJCe llxQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of newsletter.80275@irregularynx.uk.net designates 209.141.38.223 as permitted sender) smtp.mailfrom=newsletter.80275@irregularynx.uk.net Return-Path: Received: from juarez.capitalunique.com (juarez.capitalunique.com. [209.141.38.223]) by mx.google.com with ESMTP id q85-20020a632a58000000b00565cc4508ffsi2480783pgq.367.2023.08.15.15.10.04 for ; Tue, 15 Aug 2023 15:10:05 -0700 (PDT) Received-SPF: pass (google.com: domain of newsletter.80275@irregularynx.uk.net designates 209.141.38.223 as permitted sender) client-ip=209.141.38.223; Authentication-Results: mx.google.com; spf=pass (google.com: domain of newsletter.80275@irregularynx.uk.net designates 209.141.38.223 as permitted sender) smtp.mailfrom=newsletter.80275@irregularynx.uk.net Received: from efianalytics.com (efianalytics.com. 216.244.76.116) Date: Tue, 15 Aug 2023 18:10:04 -0400 (EDT) From: "⭕Cᴏɴꜰɪʀᴍ⭕" X-EMMAIL: mailto:marrybenh@kodehexa.net List-Unsubscribe: Envelope-To: Subject: Dchurch - 🔴 𝗣𝗮𝘆𝗼𝘂𝘁 𝗩𝗲𝗿𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻💸✅💸𝗬𝗼𝘂 𝗛𝗮𝘃𝗲 𝗕𝗲𝗲𝗻 𝗣𝗮𝗶𝗱💰 𝗧𝗮𝗸𝗲 𝗿𝗲𝗰𝗲𝗶𝗽𝘁 𝗼𝗳 𝘆𝗼𝘂𝗿 𝗰𝗮𝘀𝗵🤑_80275 To: <[an18]@itlgopk.uk> Message-Id: <-@vevida.net> X-MarketoID: vKD-Gha-rogvwyhi:0:jJKk4V:9:oB3EVd:besLKX8VE Feedback-ID: 1.us-east-1.psHyoMfnH8hkuQnF72JGzzQK1By9IkayShm7uIk1GIc=:AmazonSES X-PVIQ: mkto-RdMCgBVWW-NB6yqY-hZCeCx-FdLezk Content-Type: multipart/report; boundary="000000000000SGCNuB7Qm6umj710elBSQB"; To go on with the delivery, just confirm your contact details getmail6-6.19.07/test/test.py000066400000000000000000000055371474536475600157550ustar00rootroot00000000000000 from getmailcore.message import Message from getmailcore.exceptions import * import os, smtplib, ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.message import EmailMessage greetru = "привет" greetde = "Grüße" byeru = 'пока' def test_add_header(): m = EmailMessage() m["Subject"] = "test" m["From"] = "my@gmail.com" m["To"] = "your@gmail.com" t = greetru.encode('Windows-1251') m.set_content(t,'text','plain') #m.as_string() #m.get_content() #m.get_content_charset() #m.get_charsets() gm = Message(fromstring=m.as_bytes()) #te.decode('Windows-1251') gm.add_header('X-byeru',byeru) assert gm.content()['X-byeru'] == byeru ge = greetde.encode('latin1') gm.add_header('X-greetde',ge) #gm.content()['X-greetde'] assert gm.content()['X-greetde'] != greetde def test_add_header1(): m = EmailMessage() m["Subject"] = "test" m["From"] = "my@gmail.com" m["To"] = "your@gmail.com" t = greetru.encode('Windows-1251') m.set_content(t,'text','plain') m.set_param('charset','Windows-1251') #m.as_string() #m.get_content() #m.get_content_charset() #m.get_charsets() gm = Message(fromstring=m.as_bytes()) te = byeru.encode('Windows-1251') #te.decode('Windows-1251') gm.add_header('X-byeru',te) assert gm.content()['X-byeru'] == byeru ge = greetde.encode('latin1') gm.add_header('X-greetde',ge) #gm.content()['X-greetde'] ### if coding is really wrong: no way assert gm.content()['X-greetde'] != greetde def test_add_header2(): mm = MIMEMultipart("alternative") mm["Subject"] = "multipart test" mm["From"] = "my@gmail.com" mm["To"] = "your@gmail.com" p1 = MIMEText(greetru, "plain") p1.set_param('charset','Windows-1251') p2 = MIMEText(greetde, "plain") p2.set_param('charset','latin1') mm.attach(p1) mm.attach(p2) #mm.get_charsets() gmm = Message(fromstring=mm.as_bytes()) te = byeru.encode('Windows-1251') #te.decode('Windows-1251') gmm.add_header('X-byeru',te) assert gmm.content()['X-byeru'] == byeru ge = greetde.encode('latin1') gmm.add_header('X-greetde',ge) #gmm.content()['X-greetde'] ### if coding is really wrong: no way assert gmm.content()['X-greetde'] != greetde #mm.as_string() def test_spam_1(): fl = os.path.join(os.path.split(__file__)[0],'spam.eml') with open(fl,'br') as f: spam_eml = f.read() gm = Message(fromstring=spam_eml) gmfl = gm.flatten(None,None) assert b'corrupt' in gmfl # check recover def test_spam_2(): fl = os.path.join(os.path.split(__file__)[0],'spam.eml') gmm = Message(fromfile=open(fl,'rb')) try: gmfl = gmm.flatten(None,None) except getmailDeliveryError as o: assert 'could not recover' in str(o) # noqa: PT017 getmail6-6.19.07/test/test_getmail_with_docker_mailserver.bats000066400000000000000000000150431474536475600245640ustar00rootroot00000000000000load 'test_helper/common' : ' cd /tmp/mailserver test/bats/bin/bats test/test_getmail_with_docker_mailserver.bats ' #source config/self_sign.sh #export PSS TESTEMAIL NAME cd config/getmail6/test || exit source prepare_test.sh cd ../../.. run_only_test() { if [ "$BATS_TEST_NUMBER" -ne "$1" ]; then skip fi } function setup() { # run_only_test 3 setup_file } function teardown() { run_teardown_file_if_necessary } function setup_file() { source '.env' export HOSTNAME DOMAINNAME CONTAINER_NAME SELINUX_LABEL wait_for_finished_setup_in_container ${NAME} local STATUS=0 repeat_until_success_or_timeout --fatal-test "container_is_running ${NAME}" "${TEST_TIMEOUT_IN_SECONDS}" sh -c "docker logs ${NAME} | grep 'is up and running'" || STATUS=1 if [[ ${STATUS} -eq 1 ]]; then echo "Last ${NUMBER_OF_LOG_LINES} lines of container \`${NAME}\`'s log" docker logs "${NAME}" | tail -n "${NUMBER_OF_LOG_LINES}" fi return ${STATUS} } function teardown_file() { : # docker-compose down } @test "first" { skip 'this test must come first to reliably identify when to run setup_file' } @test "checking ssl" { run docker exec $NAME /bin/bash -c "\ openssl s_client -connect 0.0.0.0:25 -starttls smtp -CApath /etc/ssl/certs/" assert_success } @test "checking ports" { run d_ports_test assert_success } @test "SimplePOP3Retriever, destination Maildir" { run d_simple_dest_maildir "POP3 true true" } @test "SimplePOP3SSLRetriever, destination Maildir" { run d_simple_dest_maildir "POP3SSL true true" } @test "SimpleIMAPRetriever, destination Maildir" { run d_simple_dest_maildir "IMAP true true record_mailbox=true" } @test "SimpleIMAPSSLRetriever, destination Maildir" { run d_simple_dest_maildir "IMAPSSL true true" } @test "SimpleIMAPRetriever, destination Maildir, uid.txt" { run d_simple_dest_maildir "IMAP true true uid_cache=uid.txt" n1=$(d_docker "cat /home/getmail/uid.txt" | cut -d" " -f 3) run d_simple_dest_maildir "IMAP true true uid_cache=uid.txt" n2=$(d_docker "cat /home/getmail/uid.txt" | cut -d" " -f 3) [[ $(( n2 - n1 )) != 0 ]] } dest_maildir_with_uid_check() { testmail testmail testmail dest_maildir IMAPSSL true false "uid_cache=uid.txt" retrieve checkmail testmail testmail testmail dest_maildir IMAPSSL true false "uid_cache=uid.txt" retrieve checkmail } @test "SimplePOP3Retriever, destination MDA_external (procmail), filter spamassassin clamav" { run d_simple_dest_procmail_filter POP3 assert_success } @test "SimplePOP3SSLRetriever, destination MDA_external (procmail), filter spamassassin clamav" { run d_simple_dest_procmail_filter POP3SSL assert_success } @test "SimpleIMAPRetriever, destination MDA_external (procmail), filter spamassassin clamav" { run d_simple_dest_procmail_filter IMAP assert_success } @test "SimpleIMAPSSLRetriever, destination MDA_external (procmail), filter spamassassin clamav" { run d_simple_dest_procmail_filter IMAPSSL assert_success } bats_config_test(){ run d_config_test "$@" run d_retrieve assert_success } #896 is message size @test "BrokenUIDLPOP3Retriever, config test" { bats_config_test "BrokenUIDLPOP3Retriever 110 800 False False" bats_config_test "BrokenUIDLPOP3Retriever 110 900 True False" } @test "BrokenUIDLPOP3SSLRetriever, config test" { bats_config_test "BrokenUIDLPOP3SSLRetriever 995 800 0 0" bats_config_test "BrokenUIDLPOP3SSLRetriever 995 900 1 1" } @test "SimpleIMAPRetriever, config test" { bats_config_test "SimpleIMAPRetriever 143 800 false true" bats_config_test "SimpleIMAPRetriever 143 900 false true" } @test "SimpleIMAPSSLRetriever, config test" { bats_config_test "SimpleIMAPSSLRetriever 993 800 False False" bats_config_test "SimpleIMAPSSLRetriever 993 900 True True" } bats_multidrop_test() { run d_multidrop_test "$@" run d_retrieve assert_success } @test "MultidropPOP3Retriever" { bats_multidrop_test "SimplePOP3Retriever 110" bats_multidrop_test "MultidropPOP3Retriever 110" } @test "MultidropPOP3SSLRetriever" { bats_multidrop_test "SimplePOP3SSLRetriever 995" bats_multidrop_test "MultidropPOP3SSLRetriever 995" } @test "MultidropIMAPRetriever" { bats_multidrop_test "SimpleIMAPRetriever 143" bats_multidrop_test "MultidropIMAPRetriever 143" } @test "MultidropIMAPSSLRetriever" { bats_multidrop_test "SimpleIMAPSSLRetriever 993" bats_multidrop_test "MultidropIMAPSSLRetriever 993" } bats_multisorter_test() { run d_multisorter_test "$@" assert_success } @test "MultidropPOP3Retriever, Multisorter" { bats_multisorter_test "MultidropPOP3Retriever 110" } @test "MultidropPOP3SSLRetriever, Multisorter" { bats_multisorter_test "MultidropPOP3SSLRetriever 995" } @test "MultidropIMAPRetriever, Multisorter" { bats_multisorter_test "MultidropIMAPRetriever 143" } @test "MultidropIMAPSSLRetriever, Multisorter" { bats_multisorter_test "MultidropIMAPSSLRetriever 993" } check_lmtp_delivery() { run d_checkmail assert_failure run d_maildir_clean_retrieve IMAP assert_success run d_checkmail assert_success } @test "MDA_lmtp" { if head `which getmail` | grep 'python3' ; then run d_lmtp_test_py "SimpleIMAPRetriever 143" run d_retrieve assert_success run d_lmtp_test_unix_socket "SimpleIMAPRetriever 143" run d_retrieve assert_success check_lmtp_delivery run d_grep_mail "Subject: lmtp_test_unix_socket_x" assert_success run d_lmtp_test_override "SimpleIMAPRetriever 143" run d_retrieve assert_success check_lmtp_delivery run d_grep_mail "Subject: lmtp_test_override_x" assert_success run d_lmtp_test_override_fallback "SimpleIMAPRetriever 143" run d_retrieve assert_success check_lmtp_delivery run d_grep_mail "Subject: lmtp_test_override_fallback_x" assert_success fi } @test "SimpleIMAPSSLRetriever, ALL, no delete" { run d_imap_search "ALL false" run d_retrieve assert_success run d_checkmail assert_success run d_grep_mail utf-8 assert_failure } @test "SimpleIMAPRetriever, UNSEEN, set seen" { run d_imap_search "UNSEEN true" run d_retrieve assert_success run d_checkmail assert_success run d_grep_mail utf-8 assert_failure } @test "SimpleIMAPRetriever, UNSEEN, no unseen" { run d_imap_search "UNSEEN true" run d_retrieve assert_success run d_checkmail assert_failure } @test "SimpleIMAPSSLRetriever, ALL, delete" { run d_imap_search "ALL true" run d_retrieve assert_success run d_checkmail assert_success run d_grep_mail utf-8 assert_failure } @test "IMAP override via command line -s" { run d_override_test } @test "getmail_mbox test" { run d_local_mbox } @test "getmail_maildir test" { run d_local_maildir } @test "getmail_fetch test" { run d_fetch_maildir }