Full Code of nicolas33/offlineimap for AI

master ba4ecea9e4e4 cached
171 files
2.6 MB
691.2k tokens
1029 symbols
1 requests
Download .txt
Showing preview only (2,763K chars total). Download the full file or copy to clipboard to get everything.
Repository: nicolas33/offlineimap
Branch: master
Commit: ba4ecea9e4e4
Files: 171
Total size: 2.6 MB

Directory structure:
gitextract_hxxddv1_/

├── .coveragerc
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── COPYING
├── Changelog.maint.md
├── Changelog.md
├── MAINTAINERS.rst
├── MANIFEST.in
├── Makefile
├── README.md
├── TODO.rst
├── bin/
│   └── offlineimap
├── contrib/
│   ├── README.md
│   ├── helpers.py
│   ├── internet-urllib3.py
│   ├── release.py
│   ├── release.sh
│   ├── store-pw-with-gpg/
│   │   ├── README.md
│   │   ├── gpg-pw.py
│   │   ├── offlineimaprc.sample
│   │   └── passwords-gmail.txt
│   ├── systemd/
│   │   ├── README.md
│   │   ├── offlineimap-oneshot.service
│   │   ├── offlineimap-oneshot.timer
│   │   ├── offlineimap-oneshot@.service
│   │   ├── offlineimap-oneshot@.timer
│   │   ├── offlineimap.service
│   │   └── offlineimap@.service
│   ├── tested-by.py
│   └── upcoming.py
├── docs/
│   ├── Makefile
│   ├── build-uploads.sh
│   ├── doc-src/
│   │   ├── API.rst
│   │   ├── conf.py
│   │   ├── dco.rst
│   │   ├── index.rst
│   │   ├── repository.rst
│   │   └── ui.rst
│   ├── offlineimap.known_issues.txt
│   ├── offlineimap.txt
│   ├── offlineimapui.txt
│   ├── rfcs/
│   │   ├── README.md
│   │   ├── rfc1731.IMAP4_auth.txt
│   │   ├── rfc1732.compatibiliy_IMAP2-IMAP2bis.txt
│   │   ├── rfc1733.models_in_IMAP4.txt
│   │   ├── rfc1734.POP3_AUTHentication
│   │   ├── rfc2061.compatibility_IMAP4-IMAP2bis.txt
│   │   ├── rfc2086.IMAP4_ACL_extension.txt
│   │   ├── rfc2087.IMAP4_QUOTA_extension.txt
│   │   ├── rfc2088.IMAP4_non_synchronizing_literals.txt
│   │   ├── rfc2095.IMAP-POP_AUTHorize_extension.txt
│   │   ├── rfc2177.IMAP4_IDLE_command.txt
│   │   ├── rfc2180.IMAP4_multi-accessed_Mailbox_practice.txt
│   │   ├── rfc2192.IMAP_URL_scheme.txt
│   │   ├── rfc2193.IMAP4_Mailbox_referrals.txt
│   │   ├── rfc2195.IMAP-POP_AUTHorize_extension.txt
│   │   ├── rfc2221.IMAP4_Login_referrals.txt
│   │   ├── rfc2244.ACAP.txt
│   │   ├── rfc2342.IMAP4_Namespace.txt
│   │   ├── rfc2359.IMAP4_UIDPLUS_extension.txt
│   │   ├── rfc2595.TLS_with_IMAP-POP3_and_ACAP.txt
│   │   ├── rfc2683.IMAP4_Implementation_recommendations.txt
│   │   ├── rfc2831.Obsolete_Digest_AUTHentication_as_a_SASL_mech.txt
│   │   ├── rfc2971.IMAP4_ID_extension.txt
│   │   ├── rfc3028.Sieve_Mail_filtering_language.txt
│   │   ├── rfc3348.IMAP4_Child_Mailbox_extension.txt
│   │   ├── rfc3501.IMAP4rev1.txt
│   │   ├── rfc3502.MULTIAPPEND_extension.txt
│   │   ├── rfc3503.Message_Disposition_Notification.txt
│   │   ├── rfc3516.IMAP4_Binary_content_extension.txt
│   │   ├── rfc3691.IMAP_UNSELECT_command.txt
│   │   ├── rfc4314.IMAP4_ACL_extension.txt
│   │   ├── rfc4315.IMAP_UIDPLUS_extension.txt
│   │   ├── rfc4466.Collected_extensions_to_IMAP4_ABNF.txt
│   │   ├── rfc4467.IMAP_URLAUTH_extension.txt
│   │   ├── rfc4469.IMAP_CATENATE_extension.txt
│   │   ├── rfc4549.Sync_operations_for_disconnected_IMAP4_Clients.txt
│   │   ├── rfc4551.IMAP_Conditional_STORE_or_Quick_flag_changes_resync.txt
│   │   ├── rfc4731.IMAP4_Extension_to_SEARCH_command.txt
│   │   ├── rfc4978.IMAP_Compress_extension.txt
│   │   ├── rfc5032.IMAP_WITHIN_Search_extension.txt
│   │   ├── rfc5161.IMAP_ENABLE_extension.txt
│   │   ├── rfc5162.IMAP4_Extensions_for_Quick_Mailbox_resync.txt
│   │   ├── rfc5182.IMAP_extension_last_SEARCH_result.txt
│   │   ├── rfc5182.Sieve_and_extensions.txt
│   │   ├── rfc5255.IMAP_i18n.txt
│   │   ├── rfc5257.IMAP_ANNOTATE_extension.txt
│   │   ├── rfc5258.IMAP4_LIST_command_extension.txt
│   │   ├── rfc5423.IM_Store_Events.txt
│   │   ├── rfc5464.IMAP_METADATA_extension.txt
│   │   ├── rfc5465.IMAP_NOTIFY_extension.txt
│   │   ├── rfc5530.IMAP_Response_codes.txt
│   │   ├── rfc5738.IMAP_UTF8.txt
│   │   ├── rfc5788.IMAP4_Keyword_registry.txt
│   │   ├── rfc5819.IMAP4_extension_Returning_STATUS_info_in_LIST.txt
│   │   ├── rfc5957.IMAP4_SORT_extension.txt
│   │   ├── rfc6154.IMAP_LIST_Special-use_Mailboxes.txt
│   │   ├── rfc6203.IMAP4_Fuzzy_SEARCH_extension.txt
│   │   ├── rfc6237.IMAP4_Multimailbox_SEARCH_extension.txt
│   │   └── rfc6331.Moving_Digest-MD5_to_Historic
│   └── website-doc.sh
├── offlineimap/
│   ├── CustomConfig.py
│   ├── __init__.py
│   ├── accounts.py
│   ├── bundled_imaplib2.py
│   ├── emailutil.py
│   ├── error.py
│   ├── folder/
│   │   ├── Base.py
│   │   ├── Gmail.py
│   │   ├── GmailMaildir.py
│   │   ├── IMAP.py
│   │   ├── LocalStatus.py
│   │   ├── LocalStatusSQLite.py
│   │   ├── Maildir.py
│   │   ├── UIDMaps.py
│   │   └── __init__.py
│   ├── globals.py
│   ├── imaplibutil.py
│   ├── imapserver.py
│   ├── imaputil.py
│   ├── init.py
│   ├── localeval.py
│   ├── mbnames.py
│   ├── repository/
│   │   ├── Base.py
│   │   ├── Gmail.py
│   │   ├── GmailMaildir.py
│   │   ├── IMAP.py
│   │   ├── LocalStatus.py
│   │   ├── Maildir.py
│   │   └── __init__.py
│   ├── threadutil.py
│   ├── ui/
│   │   ├── Curses.py
│   │   ├── Machine.py
│   │   ├── Noninteractive.py
│   │   ├── TTY.py
│   │   ├── UIBase.py
│   │   ├── __init__.py
│   │   └── debuglock.py
│   ├── utils/
│   │   ├── __init__.py
│   │   ├── const.py
│   │   ├── distro.py
│   │   └── stacktrace.py
│   └── virtual_imaplib2.py
├── offlineimap.conf
├── offlineimap.conf.minimal
├── offlineimap.py
├── requirements.txt
├── scripts/
│   └── get-repository.sh
├── setup.cfg
├── setup.py
├── snapcraft.yaml
├── test/
│   ├── .gitignore
│   ├── OLItest/
│   │   ├── TestRunner.py
│   │   ├── __init__.py
│   │   └── globals.py
│   ├── README
│   ├── __init__.py
│   ├── credentials.conf.sample
│   └── tests/
│       ├── __init__.py
│       ├── test_00_globals.py
│       ├── test_00_imaputil.py
│       ├── test_01_basic.py
│       └── test_02_MappedIMAP.py
└── tests/
    ├── .gitignore
    ├── create_conf_file.py
    └── requirements.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
branch = True
source = offlineimap

[report]
exclude_lines =
    if self.debug:
    pragma: no cover
    raise NotImplementedError
    if __name__ == .__main__.:
ignore_errors = True
omit =
    tests/*
    test/*


================================================
FILE: .github/CODEOWNERS
================================================
# This is a comment.
# Each line is a file pattern followed by one or more owners.

# These owners will be the default owners for everything in the repo.
# Unless a later match takes precedence, @global-owner1 and @global-owner2
# will be requested for review when someone opens a pull request.
#*       @global-owner1 @global-owner2

# Order is important; the last matching pattern takes the most precedence.
# When someone opens a pull request that only modifies JS files, only @js-owner
# and not the global owner(s) will be requested for a review.
#*.js    @js-owner

# You can also use email addresses if you prefer. They'll be used to look up
# users just like we do for commit author emails.
#docs/*  docs@example.com


* @chris001


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================

#### General informations

- system/distribution (with version): 
- offlineimap version (`offlineimap -V`): 
- Python version: 
- server name or domain: 
- CLI options: 

#### Configuration file offlineimaprc

```
REMOVE PRIVATE DATA.
```

#### pythonfile (if any)

```
REMOVE PRIVATE DATA.
```


#### Logs, error

```
REMOVE PRIVATE DATA.
```

#### Steps to reproduce the error

- 
- 



================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
> This v1.1 template stands in `.github/`.

### This PR

> Add character x `[x]`.

- [ ] I've read the [DCO](http://www.offlineimap.org/doc/dco.html).
- [ ] I've read the [Coding Guidelines](http://www.offlineimap.org/doc/CodingGuidelines.html)
- [ ] The relevant informations about the changes stands in the commit message, not here in the message of the pull request.
- [ ] Code changes follow the style of the files they change.
- [ ] Code is tested (provide details).

### References

- Issue #no_space

### Additional information




================================================
FILE: .gitignore
================================================
# Backups.
.*.swp
.*.swo
*~
# websites.
/website/
/wiki/
# Generated files.
*.html
*.css
/docs/dev-doc/
/build/
*.pyc
offlineimap.1
offlineimapui.7
# Editors/IDEs
tags
# Generated conf files for Travis-CI tests.
oli-travis.conf


================================================
FILE: .travis.yml
================================================
language: python
python:
- '2.7'
notifications:
  webhooks:
    urls:
      - https://webhooks.gitter.im/e/975e807e0314c9fa189c
    on_success: always  # options: [always|never|change] default: always
    on_failure: always  # options: [always|never|change] default: always
    on_start: never
os:
- linux
env:
  global:
  - secure: jehlvkFxQbkvr73A0z3HGNC/knZQPKcaXLf6nByGpNE0ZTQKF7Y5KkNfeTcw4st7L7KuRZ1S/1bFtpMXTaplE6G0OtIEC4//SM+z+Dnadn2OY6wHiaapwZmmqDC5qVvcXPdmz/wTRsdrJSGLb2l6kEb91vRGbCCfHHf6Z2cF71U=
  - secure: kWdmWAFK4qrA73ONz1X8CJdHSER3bCBXjLfYHYEEMPCZep21bTITUXIfZBlSNN1888SQtYksuloRJmvj7xiY/hf/4lyWiqM3RgWQ+YptJMVOQX+Gara6vm4nGntKQwaXgZF2YHSh+NYwQm1VY6m0n1ye/vfOIJnYfgGTk5qAZYU=
  - secure: MzytYRX6HxgBj6Q3efkACTtDed8ZYO+P6UJrDA9IDtvffi8fAFb+wkQtKJrdcvMXNOap6fPe4c0EVGjgL5hFxmgC8yAh5t2YK7OhstAtq0ptKFlOcU24/drrkqoq040sAM/4Lc0nQCvYpz7bH370jzZl69rpbQWttwQR0i1e3Gw=
  - secure: RWvIOHSiv2kt6cfZR7MEueiAmC61bWMXAtgsC6gKq1u3BfENfqSBTA/heIy+nlu7AXK1b6hPMZDCHWK09Zz6Klkd9xZ1gkE/AARWseoo9UWgGjmfvqng1S6qpESeX2GnZGR9CuBXTPGhtbYLgtNlxAo+6uZLolz2utW2XNk3Z/Y=
  - secure: spivQv+vSJhE+ttn/Z6tANaINqiMSaJSucRqtoXR7PtioVDTOTmmL01Ja6dXuo8Ua5iVFtpZPDzqVpntQLKtjcywSK2zWnC9qbZYDfENr1/yIvfbSRjGeseq0eoY+fFp67FGZV4mIasdC3LOB0lRGOyrsX787fNKVQ8ZH0CRz0o=
  - secure: ZcY0TvTQnRCdoFkdbJPfDJJNx91tViwbpiOBkxNEa3u0RN48xkZkii35kNVBaEcVZHcT9C81ctHk4QX+plBkCsoj5GDf25scgcv1j9R9UoN/rIkmyTu1Znmc+3UQ2J+EnGLWVn5xJ7yT/l9NZeLfNbULQRjttwT4j2MBGxezgdM=
  matrix:
  - OUTLOOK_AUTH=PLAIN    GMAIL_AUTH=XOAUTH2
  - OUTLOOK_AUTH=LOGIN    GMAIL_AUTH=XOAUTH2
matrix:
  include:
  - os: osx
    language: generic
    env: PYTHON=2.7.14 OUTLOOK_AUTH=PLAIN GMAIL_AUTH=XOAUTH2
  - os: osx
    language: generic
    env: PYTHON=2.7.14 OUTLOOK_AUTH=LOGIN GMAIL_AUTH=XOAUTH2
  allow_failures:
  - os: osx
cache: pip
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update && brew install openssl readline;
  fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export OSX_BREW_SSLCACERTFILE="/usr/local/etc/openssl/cert.pem";
  fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated pyenv || brew upgrade pyenv;
  fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install pyenv-virtualenv; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pyenv install $PYTHON; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PYENV_VERSION="${PYTHON}"; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/Users/travis/.pyenv/shims:${PATH}";
  fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pyenv virtualenv $PYTHON myvenv; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pyenv versions; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python --version; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pyenv version; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python --version; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python -m pip install -U pip; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python -m easy_install -U setuptools;
  fi
install:
- pip install -r requirements.txt
- pip install -r tests/requirements.txt
- export PATH=$PATH:.
- python tests/create_conf_file.py
script:
- "./offlineimap.py -c ./oli-travis.conf"
- codecov


================================================
FILE: CODE_OF_CONDUCT.md
================================================

# Realistic Code of Conduct

1. We mostly care about making our softwares better.

2. Everybody is free to decide how to contribute.

3. We believe in free speech. Everyone's entitled to their opinion.

4. Feel offended? This might be very well-deserved.

5. We don't need a code of conduct imposed on us, thanks.

6. Ignoring this Realistic Code of Conduct is welcome.

<!--
vim: expandtab ts=2
-->


================================================
FILE: CONTRIBUTING.rst
================================================
.. -*- coding: utf-8 -*-
.. vim: spelllang=en ts=2 expandtab:

.. _OfflineIMAP: https://github.com/OfflineIMAP/offlineimap
.. _Github: https://github.com/OfflineIMAP/offlineimap
.. _repository: git://github.com/OfflineIMAP/offlineimap.git
.. _maintainers: https://github.com/OfflineIMAP/offlineimap/blob/next/MAINTAINERS.rst
.. _mailing list: http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project
.. _Developer's Certificate of Origin: https://github.com/OfflineIMAP/offlineimap/blob/next/docs/doc-src/dco.rst
.. _Community's website: http://www.offlineimap.org
.. _APIs in OfflineIMAP: http://www.offlineimap.org/documentation.html#available-apis
.. _documentation: http://www.offlineimap.org/documentation.html
.. _Coding Guidelines: http://www.offlineimap.org/doc/CodingGuidelines.html
.. _Know the status of your patches: http://www.offlineimap.org/doc/GitAdvanced.html#know-the-status-of-your-patch-after-submission
.. _How to fix a bug in open source software: https://opensource.com/life/16/8/how-get-bugs-fixed-open-source-software


=================
HOW TO CONTRIBUTE
=================

You'll find here the **basics** to contribute to OfflineIMAP_, addressed to
users as well as learning or experienced developers to quickly provide
contributions.

**For more detailed documentation, see the** `Community's website`_.

.. contents:: :depth: 3


Submit issues
=============

Issues are welcome to both Github_ and the `mailing list`_, at your own
convenience. Provide the following information:
- system/distribution (with version)
- offlineimap version (`offlineimap -V`)
- Python version
- server name or domain
- CLI options
- Configuration file (offlineimaprc)
- pythonfile (if any)
- Logs, error
- Steps to reproduce the error

Worth the read: `How to fix a bug in open source software`_.

You might help closing some issues, too. :-)


For the imaptients
==================

- `Coding Guidelines`_
- `APIs in OfflineIMAP`_
- `Know the status of your patches`_ after submission
- All the `documentation`_


Community
=========

All contributors to OfflineIMAP_ are benevolent volunteers. This makes hacking
to OfflineIMAP_ **fun and open**.

Thanks to Python, almost every developer can quickly become productive. Students
and novices are welcome. Third-parties patches are essential and proved to be a
wonderful source of changes for both fixes and new features.

OfflineIMAP_ is entirely written in Python, works on IMAP and source code is
tracked with Git.

*It is expected that most contributors don't have skills to all of these areas.*
That's why the best thing you could do for you, is to ask us about any
difficulty or question raising in your mind. We actually do our best to help new
comers. **We've all started like this.**

- The official repository_ is maintained by the core team maintainers_.

- The `mailing list`_ is where all the exciting things happen.


Getting started
===============

Occasional contributors
-----------------------

* Clone the official repository_.

Regular contributors
--------------------

* Create an account and login to Github.
* Fork the official repository_.
* Clone your own fork to your local workspace.
* Add a reference to your fork (once)::

  $ git remote add myfork https://github.com/<your_Github_account>/offlineimap.git

* Regularly fetch the changes applied by the maintainers::

  $ git fetch origin
  $ git checkout master
  $ git merge offlineimap/master
  $ git checkout next
  $ git merge offlineimap/next


Making changes (all contributors)
---------------------------------

1. Create your own topic branch off of ``next`` (recently updated) via::

   $ git checkout -b my_topic next

2. Check for unnecessary whitespaces with ``git diff --check`` before committing.
3. Commit your changes into logical/atomic commits.  **Sign-off your work** to
   confirm you agree with the `Developer's Certificate of Origin`_.
4. Write a good *commit message* about **WHY** this patch (take samples from
   the ``git log``).


Learn more
==========

There is already a lot of documentation. Here's where you might want to look
first:

- The directory ``offlineimap/docs`` has all kind of additional documentation
  (man pages, RFCs).

- The file ``offlineimap.conf`` allows to know all the supported features.

- The file ``TODO.rst`` express code changes we'd like and current *Work In
  Progress* (WIP).



================================================
FILE: COPYING
================================================
# 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.

                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
     51 Franklin St, 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 Library 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.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 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 St, 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.

  <signature of Ty Coon>, 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 Library General
Public License instead of this License.

----------------------------------------------------------------
In addition, as a special exception, the copyright holders give
permission to link the code of portions of this program with the OpenSSL
library under certain conditions as described in each individual source
file, and distribute linked combinations including the two.

You must obey the GNU General Public License in all respects for all of
the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete
this exception statement from your version. If you delete this exception
statement from all source files in the program, then also delete it
here.


================================================
FILE: Changelog.maint.md
================================================
---
layout: page
title: Changelog of the stable branch
---

* The following excerpt is only usefull when rendered in the website.
{:toc}

This is the Changelog of the maintenance branch.

**NOTE FROM THE MAINTAINER:**

	This branch comes almost as-is. With no URGENT requirements to update this
	branch (e.g. big security fix), it is left behind.
	If anyone volunteers to maintain it and backport patches, let us know!


### OfflineIMAP v6.7.0.3 (2016-07-26)

#### Bug Fixes

* sqlite: properly serialize operations on the database files


### OfflineIMAP v6.7.0.2 (2016-07-22)

#### Bug Fixes

* sqlite: close the database when no more threads need connection.


### OfflineIMAP v6.7.0.1 (2016-06-08)

#### Bug Fixes

* Correctly open and close sqlite databases.


### OfflineIMAP v6.3.2.1 (2011-03-23)

#### Bug Fixes

* Sanity checks for SSL cacertfile configuration.
* Fix regression (UIBase is no more).
* Make profiling mode really enforce single-threading.


================================================
FILE: Changelog.md
================================================
---
layout: page
title: Changelog of mainline
---

<!--
Note to mainainers:
* You should not edit this file manually; prefer the ./contrib/release.sh script.
* If you really want to do manual edition, keep in mind that it's exported as-is
  to the website which expect the current structure (title levels, title syntax,
  sub-sections, front matter, etc).
-->


* The following excerpt is only usefull when rendered in the website.
{:toc}

### OfflineIMAP v7.3.0 (2019-08-19)

#### Notes

Here comes a new release.

The upstream imaplib2 project is discontinued. That's why I've decided to take
over the maintenance of imaplib2 for offlineimap.

For the use of offlineimap I've applied the pending PRs from imaplib2.  I have
applied another change sent to offlineimap (see OfflineIMAP/offlineimap#623).

However, there are 3 important limitations:

- I intend to maintain imaplib2 for offlineimap only. Everything will take place
  in the offlineimap project. I'll neither package imaplib2 nor maintain any
  "official" repository dedicated to imaplib2. If you want imaplib2 but not
  offlineimap, you should extract the file `offlineimap/bundled_imaplib2.py` from
  the offlineimap repository. Please, send your patches for imaplib2.py to the
  offlineimap project directly.

- Starting from imaplib2 v2.100 (tagged: imaplib2-v2.100) I'm taking the patches
  in the lazy mode. This means that I won't make deep checks/tests of the
  changes.  Hence, the quality and the stability might become a bit more
  fluctuating. For more stability, you might like to only consider the imaplib2
  versions released with the stable versions of offlineimap. Don't expect
  changelogs dedicated to imaplib2. They will be part of the offlineimap
  changelogs.

- All of this only applies to the py2 version of imaplib2. Sadly, offlineimap
  has few chances to be ported on py3 so I don't aim to maintain the py3 version
  of imaplib2.


In this release, offlineimap is learning Happy Eyeballs.


This release was tested by:

- Nicolas Sebrecht


#### Authors

- Nicolas Sebrecht (4)
- Ben Cotterell (1)
- Dario Maiocchi (1)
- Ilias Tsitsimpis (1)
- Julien Cristau (1)
- Olivier Mehani (1)


#### Features

- Implement Happy Eyeballs. [Olivier Mehani]
- imaplib2 v2.101. [Nicolas Sebrecht]
- imaplib2 v2.100. [Nicolas Sebrecht]

#### Changes

- Update readme to give an hint about Linux distros. [Dario Maiocchi]
- travis: remove python3.6. [Nicolas Sebrecht]
- README: add required dependency to rfc6555. [Nicolas Sebrecht]

#### imaplib2

- Do not use TIMEOUT_MAX for Condition.wait(). [Ilias Tsitsimpis]
- Use SSLContext if available so we send SNI. [Julien Cristau]
- Don't expect trailing space on command completion. [Ben Cotterell]


### OfflineIMAP v7.2.4 (2019-06-08)

#### Notes

This release introduces mkdir -p alike folder creation and fixes cygwin support
in Windows.

This release was tested by:

- Nicolas Sebrecht


#### Authors

- Nicolas Sebrecht (4)
- kimim (2)
- Jelmer Vernooij (1)
- Kyle Altendorf (1)


#### Features

- mkdir -p alike folder creation. [Kyle Altendorf]

#### Fixes

- Use portable locker to support cygwin in Windows. [kimim]
- contrib/release.py: don't break if sphinx-build is missing. [Nicolas Sebrecht]

#### Changes

- Update FSF postal address. [Jelmer Vernooij]
- repository/IMAP: update copyright header date. [Nicolas Sebrecht]
- PULL_REQUEST_TEMPLATE: add space between brackets to enable the edition in the gui. [Nicolas Sebrecht]


### OfflineIMAP v7.2.3 (2019-02-17)

#### Notes

A tiny release for one minor bug fix.

This release was tested by:

- Nicolas Sebrecht


#### Authors

- Mart Lubbers (1)


#### Fixes

- add checks in curses ui for small windows. [Mart Lubbers]


### OfflineIMAP v7.2.2 (2018-12-22)

#### Notes

With this release offlineimap can renew the token for OAUTH2. There is better
integration for ArchLinux and OSX. SSL configuration options are more
consistent.

There are bug fixes about maxage and GSSAPI.

The imaplib2 library looks discontinued. I wonder we'll have no other choice
than maintaining our own fork.

This release was tested by:

- Nicolas Sebrecht


#### Authors

- Nicolas Sebrecht (5)
- Philippe Loctaux (4)
- Benedikt Heine (2)
- Carnë Draug (2)
- Frode Aannevik (1)
- Robbie Harwood (1)


#### Features

- 2890dec Added ssl certfile on osx for openssl pacakge on homebrew. [Philippe Loctaux]
- 761e10e Add Archlinux to list of supported distros. [Philippe Loctaux]

#### Fixes

- 8692799 Fix expired oauth2_access_token. [Frode Aannevik]
- 096aa07 Handle empty token with complete GSSAPI context. [Robbie Harwood]
- a51064e maxage: always compute the remote cache list for min_uid. [Nicolas Sebrecht]
- 698ec64 offlineimap.conf: minor fixes. [Nicolas Sebrecht]
- af3a35a offlineimap/utilis/distro.py: indentation fix. [Philippe Loctaux]
- d3ba837 Fix typo in exception message. [Benedikt Heine]
- c9005cd Check if username is provided before trying plain authentication.. [Carnë Draug]

#### Changes

- 5f9474e Print username instead of accountname when asking for password. [Carnë Draug]
- ce9a198 Chain tls_level and ssl_version only if ssl is enabled. [Benedikt Heine]
- 6ef5937 docs/website-doc.sh: minor improvements in comments of versions.yml. [Nicolas Sebrecht]
- 4544bb1 contrib/release.py: minor UI improvement. [Nicolas Sebrecht]
- d930125 fix dates in copyright lines. [Nicolas Sebrecht]


### OfflineIMAP v7.2.1 (2018-06-16)

#### Notes

This new version introduces interesting features. The fingerprints now accepts
hashes in sha224, sha256, sha384 and sha512 to improve the compatibility with
IMAP servers.

There's a new script in ./contrib to store passwords with GPG.

The new GSSAPI library for kerberos gets a fix about authentication. Gmail
labels can now have parenthesis and the hostname can have path separators in
theirs names.

There's a lot of other minors improvements to make offlineimap better
(in the documentation, UI, configuration file and the code).

This release was tested by:

- Nicolas Sebrecht

Thanks to all the contributors. A lot of patches are first time contributions to
this project. This is very pleasant.

Special thanks to Ilias Tsitsimpis, Eygene Ryabinkin, Chris Coleman our long
time contributors involved in this release and Sebastian Spaeth who is still
paying for the domain name!


#### Authors

- Nicolas Sebrecht (9)
- velleto (6)
- Chris Coleman (1)
- Edgar HIPP (1)
- Eygene Ryabinkin (1)
- Lorenzo (1)
- Michael Billington (1)
- Robbie Harwood (1)


#### Features

- Script to store passwords in a file with GPG or using OSX's secure keychain. [Lorenzo]
- Added support for sha512, sha384, sha256, sha224 hashing algorithms to calculate server certificate fingerprints.. [velleto]

#### Fixes

- Pass username through in GSSAPI connections. [Robbie Harwood]
- Gmail: allow parenthesis in labels. [Nicolas Sebrecht]
- Correct typographical errors in offlineimap.conf. [Michael Billington]
- Create filenames with no path separators in them. [Eygene Ryabinkin]

#### Changes

- imapserver: fix copyright line. [Nicolas Sebrecht]
- Available hashes added to documentation.. [velleto]
- Documented the now allowed use of colon separated fingerprints with examples.. [velleto]
- Allow users to keep colons between each hex pair of server certificate fingerprint in configuration file.. [velleto]
- Removed uneccessary call of list() on zip() object.. [velleto]
- Changed the 'exception raised' message, to be more understandable.. [velleto]
- Make CTRL-C message more clear. [Edgar HIPP]
- setup: add long_description. [Nicolas Sebrecht]
- offlineimap.conf: fix comment about gssapi. [Nicolas Sebrecht]
- Add self to maintainers. Update email address.. [Chris Coleman]
- Makefile: targz: don't set the abbrev in the archive directory name. [Nicolas Sebrecht]
- contrib: learn to build website/_uploads. [Nicolas Sebrecht]
- docs/website-doc.sh: limit the number of exported versions in _data/announces.yml. [Nicolas Sebrecht]
- Makefile: targz: update files. [Nicolas Sebrecht]
- Makefile: clean: remove __pycache__ directories. [Nicolas Sebrecht]


### OfflineIMAP v7.2.0 (2018-04-07)

#### Notes

The biggest change with this release is the introduction of automated tests;
thanks to Chris from http://www.espacenetworks.com.

Robbie Hardwood from RedHat switched the GSSAPI dependency from pykerberos to
python-gssapi because it's more active and has more pleasant interface.

The shebang is fixed back to python2 to fix issues on some environments.

The UI was improved to show both the local and remote foldernames (usefull when
nametrans is enabled).

Thanks to all the contributors.

This release was tested by:

- Nicolas Sebrecht
- Remi Locherer


#### Authors

- Nicolas Sebrecht (9)
- Musashi69 (1)
- Robbie Harwood (1)
- chris001 (1)


#### Features

- Autmomated testing using Travis and CodeCov.io!. [chris001]
- README: travis: add badge for the next branch. [Nicolas Sebrecht]
- travis: add notification to gitter room OfflineIMAP/offlineimap. [Nicolas Sebrecht]

#### Fixes

- offlineimap.py: fix shebang to python2. [Nicolas Sebrecht]
- bin/offlineimap: fix shebang to env python2. [Nicolas Sebrecht]

#### Changes

- Port to python-gssapi from pykerberos. [Robbie Harwood]
- requirements: add gssapi as optional dependency. [Nicolas Sebrecht]
- make UI output show local AND remote dirs involved. [Musashi69]
- maxsyncaccounts: improve documentation. [Nicolas Sebrecht]




### OfflineIMAP v7.1.5 (2018-01-13)

#### Notes

This minor release fixes a bug about maxage failing to upload some emails. Also,
this introduces the snapcraft.yaml to package offlineimap with this packaging
system.

This release was tested by:

- Nicolas Sebrecht
- Remi Locherer


#### Authors

- Nicolas Sebrecht (4)
- Evan Dandrea (1)
- John Ferlito (1)


#### Features

- Initial commit of snapcraft.yaml. [Evan Dandrea]

#### Fixes

- maxage: don't consider negative UIDs when computing min UID. [Nicolas Sebrecht]
- Add missing space to output string. [John Ferlito]

#### Changes

- folder: IMAP: improve search logging. [Nicolas Sebrecht]
- no UIDPLUS: improve logging on failures. [Nicolas Sebrecht]
- github: remove the trick to download the PR. [Nicolas Sebrecht]


### OfflineIMAP v7.1.4 (2017-10-29)

#### Notes

Here is a bugfix release for v7.1.3. Two regressions got fixes and the
--delete-folder CLI option now expects an UTF-8 folder name when utf8foldernames
is enabled.

This release was tested by:

- Nicolas Sebrecht

#### Authors

- Nicolas Sebrecht (5)
- Thomas Merkel (1)

#### Fixes

- utf8foldernames: fix missing decode argument. [Nicolas Sebrecht]
- Fix: if any tunnel (preauth_tunnel or transport_tunnel) the hostname should not be required. [Thomas Merkel]

#### Changes

- utf8foldernames: support --delete-folder with UTF-8 folder name. [Nicolas Sebrecht]
- contrib/release.py improvements


### OfflineIMAP v7.1.3 (2017-10-08)

#### Notes

This release introduces a new experimental utf8foldernames configuration option.

We already had the "tricky" decodefoldernames which is now deprecated. The new
code is the correct implementation for this feature. The changes are neat and
rather small. All the users having decodefoldernames are requested to move to
utf8foldernames. This requires to update almost all the functions like
nametrans, folderfilter, etc, because they work on the UTF-8 encoding. See the
documentation for more. Thank you Urs Liska for this contribution!

In the long run, the idea is to:

1. Remove decodefoldernames in favour of utf8foldernames.
2. Promote utf8foldernames up to stable.
3. Turn utf8foldernames on by default.

Currently, folders with non-ASCII characters in their name have to be fully
re-downloaded. So, there's a bit more work to be done to have (3) and maybe (2).

Also, this release includes a fix about remotehost and transporttunnel that
would require some testing. Thanks Thomas Merkel!

There are documentation improvements, improved errors and minor code cleanups,
too.

This release was tested by:

- Nicolas Sebrecht
- Remi Locherer


#### Authors

- Nicolas Sebrecht (11)
- Urs Liska (8)
- Thomas Merkel (1)

#### Features

- utf8: implement utf8foldernames option. [Urs Liska]
- utf8: document new feature, deprecate old one. [Urs Liska]

#### Fixes

- remotehost should not be required if transporttunnel is used. [Thomas Merkel]
- accounts: error out when no folder to sync. [Nicolas Sebrecht]
- sqlite: provide better message error for insert. [Nicolas Sebrecht]
- folder: Gmail: fix copyright header. [Nicolas Sebrecht]

#### Changes

- man: remove mention of experimental support for python 3. [Nicolas Sebrecht]
- man: mention the supported directions of the syncs. [Nicolas Sebrecht]
- folder: Gmail: remove dead code. [Nicolas Sebrecht]
- upcoming.py: get header template from external file. [Nicolas Sebrecht]
- upcoming.py: display a message with the filename once written. [Nicolas Sebrecht]
- contrib/helpers: sort testers by name. [Nicolas Sebrecht]
- Remove some unnecessary whitespace (in existing code). [Urs Liska]
- MAINTAINERS: Rainer is not currently active. [Nicolas Sebrecht]


### OfflineIMAP v7.1.2 (2017-07-10)

#### Notes

This release introduces better Davmail support, better reliability when in
IMAP/IMAP mode, better output on some errors, and minor fixes. The provided
systemd files are improved.

The imaplib2 requirement is now v2.57.

Remi Locherer is joining our tester team. Great!

Starting with this release, the feedbacks from the testers are recorded in the
release notes, the git logs and the Changelog. Thanks to all of them for
improving the releases.

This release was tested by:

- benutzer193
- Nicolas Sebrecht
- Remi Locherer

#### Authors

- Nicolas Sebrecht (20)
- Hugo Osvaldo Barrera (5)
- Alvaro Pereyra (1)
- benutzer193 (1)

#### Features

- contrib/release.py: consider positive feedbacks from testers. [Nicolas Sebrecht]
- Introduce the github CODEOWNERS file. [Nicolas Sebrecht]
- IMAP/IMAP: continue to sync if the local side does not return a valid UID on upload. [Nicolas Sebrecht]

#### Fixes

- folder/IMAP: introduce dedicated parsing for davmail (not supporting UIDPLUS). [Nicolas Sebrecht]
- offlineimap.conf: minor typo fix. [Alvaro Pereyra]
- Respect systemd conventions for timers. [Hugo Osvaldo Barrera]
- Use a pre-existing target for systemd services. [Hugo Osvaldo Barrera]
- Remove invalid systemd setting. [Hugo Osvaldo Barrera]
- systemd: remove unused watchdog functionality. [benutzer193]
- gitignore generated css file. [Nicolas Sebrecht]
- Changelog: fix syntax. [Nicolas Sebrecht]

#### Changes

- Increase imaplib2 requirement from v2.55 to v2.57. [Nicolas Sebrecht]
- folder/IMAP: improve the warning when we can't parse the returned UID. [Nicolas Sebrecht]
- Provide more details in error message when SSL fails on non-standard port. [Nicolas Sebrecht]
- Use basic logger (since systemd picks up stdout). [Hugo Osvaldo Barrera]
- Explain how to override systemd values. [Hugo Osvaldo Barrera]
- systemd: add documentation entry in configuration files. [Nicolas Sebrecht]
- offlineimap.conf: ssl must be disabled to force STARTTLS in some cases. [Nicolas Sebrecht]
- Advise singlethreadperfolder when offlineimap hangs. [Nicolas Sebrecht]
- offlineimap.conf: minor improvements. [Nicolas Sebrecht]
- contrib: more release automation. [Nicolas Sebrecht]
- MAINTAINERS: Remi Locherer joins the team of testers. [Nicolas Sebrecht]
- systemd: README: credit Hugo as contributor. [Nicolas Sebrecht]


### OfflineIMAP v7.1.1 (2017-05-28)

#### Notes

This release has some interesting fixes, including one for the Blinkenlights UI.

Otherwise, there is no big change since the previous version.

Furthermore, this release was tested by:

- Remi Locherer

#### Authors

- Nicolas Sebrecht (17)
- Chris Coleman (1)
- Ilias Tsitsimpis (1)
- Maximilian Kaul (1)
- benutzer193 (1)
- Ævar Arnfjörð Bjarmason (1)

#### Features

- contrib: introduce a tool to produce the "upcoming notes". [Nicolas Sebrecht]
- contrib: secure HTTPS test internet is connected.. [Chris Coleman]
- Env info (used by -V and banner): add openssl version. [Nicolas Sebrecht]
- docs: learn to build html files for the manual pages. [Nicolas Sebrecht]

#### Fixes

- Acquire lock before updating the CursesLogHandler window. [Ilias Tsitsimpis]
- maxage: use the remote folder first to compute min_uid. [Nicolas Sebrecht]
- Fix systemd.timer: initialize timer after boot. [benutzer193]
- XOAUTH2: don't try this authentication method when not configured. [Nicolas Sebrecht]
- mbnames: don't duplicate entries in autorefresh mode. [Nicolas Sebrecht]
- docs: update the instructions for creating OAuth projects for GMail. [Ævar Arnfjörð Bjarmason]
- Fixed typo in doc: tls_1_2 => tls1_2. [Maximilian Kaul]
- IMAP: UIDPLUS: correctly warn about weird responses from some servers. [Nicolas Sebrecht]
- website-doc: force copy of the new HTML generated man pages. [Nicolas Sebrecht]
- Makefile: fix clean target. [Nicolas Sebrecht]

#### Changes

- MAINTAINERS: benutzer193 joins the testers team. [Nicolas Sebrecht]
- IMAP: UIDPLUS: improve error message on response error for new UID. [Nicolas Sebrecht]
- Display the imaplib and python versions for each normal run. [Nicolas Sebrecht]
- imapserver: provide some SSL info while in imap debug mode. [Nicolas Sebrecht]
- manual: improve the documentation about sqlite migration. [Nicolas Sebrecht]
- documentation: add entry for faulting folders with Microsoft servers. [Nicolas Sebrecht]
- website-doc.sh: add hint on API removal. [Nicolas Sebrecht]
- README: refactorize sections. [Nicolas Sebrecht]



### OfflineIMAP v7.1.0 (2017-04-16)

#### Notes

The most important change is the removal of the status_backend configuration
option and that's why we're moving to v7.1.0.

There are other small bug fixes and improvements. However, the codebase didn't
change much since v7.0.14.

#### Authors

- Nicolas Sebrecht (6)
- benutzer193 (4)
- Ilias Tsitsimpis (1)

#### Fixes

- doc: Fix typo in offlineimap.1 man page. [Ilias Tsitsimpis]
- README: we moved to imaplib2 v2.57. [Nicolas Sebrecht]
- README: mark porting to py3 as stalled. [Nicolas Sebrecht]
- folder: UIDMaps: ignore KeyError failure while removing keys. [Nicolas Sebrecht]

#### Changes

- Remove support for the status_backend configuration option. [Nicolas Sebrecht]
- folder/IMAP: improve handling of "matchinguids" error while searching headers. [Nicolas Sebrecht]
- Adjust README to systemd service file changes. [benutzer193]
- Remove oneshot switch from systemd services. [benutzer193]
- Use oneshot services for systemd timers. [benutzer193]
- Create systemd oneshot services. [benutzer193]
- website-doc.sh: versions.yml: set versions in order. [Nicolas Sebrecht]



### OfflineIMAP v7.0.14 (2017-03-11)

#### Notes

Here is a new small fixup release for the v7.0 series. The first v7.0.0 release
is near to 8 months old. This v7.0.14 release is more reliable than v6.7.0.3.
Hence, I'm deprecating the v6.7 series.

Now, you are all enjoined to migrate to v7.0.14. Migrating back to v6.7 is not
supported so you might like to backup your local maildirs and metadata first.

We will fully remove the legacy text backend driver in near future. The SQLite
driver proved to be better for both performance and reliability.

With this release we use imaplib2 v2.57 to support some faulting IMAP servers,
fix a little bug about the backend migration to SQLite and serialize the sync
processes to prevent from issues when both IDLE and autorefresh are enabled.

Happy sync'ing!

#### Authors

- Nicolas Sebrecht (5)
- 927589452 (2)
- Jens Heinrich (1)
- Stéphane Graber (1)

#### Fixes

- SQLite: avoid concurrent writes on backend migration. [Nicolas Sebrecht]
- Fix ipv6 configuration handling. [Stéphane Graber]
- Prevent synchronization of identical folders from multiple threads. [Nicolas Sebrecht]

#### Changes

- Bump from imaplib2 v2.55 to v2.57. [Nicolas Sebrecht]
- scripts/get-repository.sh: use portable /bin/sh. [Jens Heinrich]
- MAINTAINERS: add new tester. [Nicolas Sebrecht]
- scripts/get-repository.sh: use env to call bash. [mailinglists@927589452.de]



### OfflineIMAP v7.0.13 (2017-01-27)

#### Notes

Here is a small release with some new features. IMAP servers are better supported.

The release cycle was improved. Now, we include a new freeze time before
important releases.

#### Authors

- Nicolas Sebrecht (8)
- lkcl (2)
- Chris Smart (1)

#### Features

- init: register SIGABRT and handle as per SIGUSR2. [Chris Smart]
- add documentation about SIGABRT. [Nicolas Sebrecht]
- learn repository retrycount configuration option. [lkcl]
- learn authproxy configuration option. [lkcl]

#### Fixes

- folder: IMAP: add missing whitespace in error message. [Nicolas Sebrecht]
- repository: IMAP: correctly check the response while listing remote folders. [Nicolas Sebrecht]
- release.sh: correctly sort releases to compute latest stable and rc. [Nicolas Sebrecht]

#### Changes

- manual: KNOWN ISSUES: add documentation about the deletions. [Nicolas Sebrecht]
- folder: IMAP: improve error message when Dovecot returns any data for UID FETCH. [Nicolas Sebrecht]
- MAINTAINERS: add new official testers. [Nicolas Sebrecht]



### OfflineIMAP v7.0.12 (2016-11-30)

#### Notes

Quick small release to fix v7.0.11 for the users of nametrans.

#### Authors

- Abdo Roig-Maranges (1)
- Darshit Shah (1)
- Nicolas Sebrecht (1)

#### Features

- Enable environment variable expansion on Repository.localfolders. [Darshit Shah]

#### Fixes

- repository: Base: fix typo in folder variable name. [Abdo Roig-Maranges]
- MAINTAINERS: minor: fix rendering. [Nicolas Sebrecht]



### OfflineIMAP v7.0.11 (2016-11-30)

#### Notes

Very small release to fix a regression about structure comparison in v7.0.10.

#### Authors

- Nicolas Sebrecht (2)

#### Fixes

- repository: Base: fix folder structure comparison. [Nicolas Sebrecht]

#### Changes

- MAINTAINERS: add all the contributors. [Nicolas Sebrecht]



### OfflineIMAP v7.0.10 (2016-11-28)

#### Notes

This release is mainly about improving reliability. The biggest changes are
about comparing the local and remote structures of folders.

The Gmail repository type allows to tune some predefined options for advanced
use cases.

Offlineimap learns where to find the default OpenSUSE certificate.

Some code refactoring and documentation improvements.

#### Authors

- Nicolas Sebrecht (15)
- Stéphane Albert (4)
- Abdo Roig-Maranges (2)
- Xudong Zhang (1)
- altruizine (1)
- Ævar Arnfjörð Bjarmason (1)

#### Features

- GMail: Add ability to set a custom host/port/ssl etc.. [Ævar Arnfjörð Bjarmason]
- Add OpenSUSE to list of supported distros. [altruizine]

#### Fixes

- repository: Base: fix name of the status folder. [Abdo Roig-Maranges]
- repository: Base: rework the structure folders comparison. [Nicolas Sebrecht]
- Fix remaining instance of check_uid_validity refactoring. [Abdo Roig-Maranges]
- Fix the profile mode. [Nicolas Sebrecht]
- folder: Maildir: actually try to use Delivery-Date if Date is broken. [Nicolas Sebrecht]
- Fix decodefoldernames not applying in folder sync. [Stéphane Albert]
- Fix mbnames writing with folders using utf-8. [Stéphane Albert]
- Fix utf7 decode error not caught. [Stéphane Albert]
- Fix md5 folder generation wanting unicode. [Stéphane Albert]
- Fix bug: should not compare list to int. [Xudong Zhang]

#### Changes

- folder: IMAP: display error message before starting next try. [Nicolas Sebrecht]
- offlineimap.conf: XOAUTH2: certificate validation is required for Gmail. [Nicolas Sebrecht]
- offlineimap.conf: autorefresh points to maxsyncaccounts. [Nicolas Sebrecht]
- offlineimap.conf: use 'Offlineimap' to name the software. [Nicolas Sebrecht]
- offlineimap.conf: add comments for the readonly configuration option. [Nicolas Sebrecht]
- offlineimap.conf: mbnames: provide sample for the folderfilter option. [Nicolas Sebrecht]
- Minor code refactoring. [Nicolas Sebrecht]
- Don't allow negative values for autorefresh. [Nicolas Sebrecht]
- Manual: add known issues entry about XOAUTH2 "invalid_grant". [Nicolas Sebrecht]
- repository: Gmail: fix copyright line. [Nicolas Sebrecht]


### OfflineIMAP v7.0.9 (2016-10-29)

#### Notes

Let's go for this small but still interesting release.

The Blinkenlights UI got fixed. Reliability for IMAP/IMAP setups is improved.

The sqlite backend now honors the fsync configuration option. This allows
commits to the database to be postponed. This might be usefull to disable the
default fsync for some use cases like cache migration from text to sqlite,
syncing after long away periods and more generally when a lot of new email
entries must be written to the cache.

Because of this change the old fsync option is marked EXPERIMENTAL. However,
setups using the plain text cache are not concerned. Bear in mind that disabling
fsync greatly decreases reliability when resuming from unexpected halts.

Small code cleanups, too.

#### Authors

- Nicolas Sebrecht (4)
- Giel van Schijndel (1)
- Ilias Tsitsimpis (1)

#### Features

- SQLite: make postponing transaction committing possible.. [Giel van Schijndel]

#### Fixes

- UIDMaps: ensure we don't update the map file in dry run mode. [Nicolas Sebrecht]
- UIDMaps: prevent from leaving a truncated map file. [Nicolas Sebrecht]
- Fix flickering in Blinkenlights UI. [Ilias Tsitsimpis]

#### Changes

- UIDMaps: reorder imports. [Nicolas Sebrecht]
- folder: IMAP: remove unused import. [Nicolas Sebrecht]



### OfflineIMAP v7.0.8 (2016-10-08)

#### Notes

Very small release to fix the broken UI relying on Curses. Thanks for the
contributors!

#### Authors

- Nicolas Sebrecht (4)
- Ilias Tsitsimpis (1)
- Stéphane Albert (1)

#### Features

- Introduce contrib/README.md. [Nicolas Sebrecht]

#### Fixes

- Import ui before threadutil to resolve circular dependency. [Ilias Tsitsimpis]
- Fix implicit call to unicode() from UI functions. [Stéphane Albert]

#### Changes

- imapserver: minor code cleaning: reorder methods. [Nicolas Sebrecht]
- website-doc.sh: print usage when no argument is given. [Nicolas Sebrecht]
- Changelog: add remark about singlethreadperfolder in the resume. [Nicolas Sebrecht]



### OfflineIMAP v7.0.7 (2016-09-21)

#### Notes

With this release, IDLE mode is a bit improved regarding stability. Offlineimap
learns the default path to the certificate for Gentoo.

The singlethreadperfolder configuration option is marked stable.

There are few improvements for logs and documentation. Minor code refactoring,
too.

#### Authors

- Nicolas Sebrecht (12)
- Dan Loewenherz (1)
- Espen Henriksen (1)

#### Features

- Add gentoo cert path for OS-DEFAULT. [Espen Henriksen]
- Remove EXPERIMENTAL flag for the singlethreadperfolder configuration option. [Nicolas Sebrecht]

#### Fixes

- Ensure logs are in bytes for PLAIN authentication. [Nicolas Sebrecht]
- Minor: utils: distro: fix copyright line. [Nicolas Sebrecht]
- README: minor copy edits. [Dan Loewenherz]
- IDLE: protect all calls to imapobj.noop() (coonection might be dropped). [Nicolas Sebrecht]
- XOAUTH2: raise error if string 'error' is in the response. [Nicolas Sebrecht]

#### Changes

- Set singlethreadperfolder configuration option when in idle mode. [Nicolas Sebrecht]
- repository: IMAP: cache the idle folders in memory. [Nicolas Sebrecht]
- mbnames: add info output messages in dry run mode. [Nicolas Sebrecht]
- mbnames: remove non-required argument. [Nicolas Sebrecht]
- offlineimap.conf: explain hooks in idle mode. [Nicolas Sebrecht]
- Explain how to submit issues in more files. [Nicolas Sebrecht]
- README: explain the a2x dependency to build the man page. [Nicolas Sebrecht]



### OfflineIMAP v7.0.6 (2016-08-21)

#### Notes

Evaluated XOAUTH2 configuration options are fixed. With this release,
offlineimap can try to keep the UIDs in order.

#### Authors

- Nicolas Sebrecht (10)
- James E. Blair (2)

#### Features

- Learn singlethreadperfolder configuration option. [James E. Blair]
- folder: Base: sort message UID list. [James E. Blair]

#### Fixes

- Maildir: add missing exception instance "as e" in except clause. [Nicolas Sebrecht]
- XOAUTH2: fix evaluated configuration options. [Nicolas Sebrecht]

#### Changes

- XOAUTH2: improve error message while trying to get access token. [Nicolas Sebrecht]
- Show python version for -V CLI option. [Nicolas Sebrecht]
- README: link Python 3 version to issues. [Nicolas Sebrecht]
- offlineimap.conf: add note about Gmail\All Mail keeping the emails while deleted. [Nicolas Sebrecht]
- release.sh: minor enhancements. [Nicolas Sebrecht]


### OfflineIMAP v7.0.5 (2016-08-10)

#### Notes

Bugfix release. The machineui is fixed and the dry-run mode is a bit improved.

Thanks to all the contributors and bug reporters. This release is yours.

#### Authors

- Nicolas Sebrecht (6)
- Wieland Hoffmann (2)
- Łukasz Żarnowiecki (2)
- Christopher League (1)

#### Fixes

- don't delete messages in local cache in dry-run mode. [Nicolas Sebrecht]
- Fix typo in format string in machineui. [Christopher League]

#### Changes

- folder: IMAP: change raw assert to OfflineImapError. [Nicolas Sebrecht]
- folder: IMAP: add 'imap' debug output before calling FETCH. [Nicolas Sebrecht]
- explicitly set __hash__ of Base class to None. [Łukasz Żarnowiecki]
- imapserver: change lambdas with map to list comprehension. [Łukasz Żarnowiecki]
- Clarify which settings are required for mbnames. [Wieland Hoffmann]
- Remove an unused import. [Wieland Hoffmann]
- folder: Base: minor style fix. [Nicolas Sebrecht]
- CONTRIBUTING: add link to external page on "How to fix a bug". [Nicolas Sebrecht]
- README: add link to the official repository on top of the page. [Nicolas Sebrecht]



### OfflineIMAP v7.0.4 (2016-08-02)

#### Notes

Small bugfix release for Gmail users.

#### Authors

- Nicolas Sebrecht (1)

#### Fixes

- ConfigHelperMixin must be new-style class to not break inheritance. [Nicolas Sebrecht]


### OfflineIMAP v7.0.3 (2016-07-30)

#### Notes

Here's a new bugfix release for the v7.0.x series. Only time we let us know if
it's a good release. However, I'm more confident.

Thanks for the reports and feedbacks!


#### Authors

- Nicolas Sebrecht (11)


#### Fixes

- Make systemd service kill offlineimap as expected. [Nicolas Sebrecht]
- XOAUTH2: fix the \*\_eval configuration options. [Nicolas Sebrecht]
- IMAP: don't take junk data for valid mail content. [Nicolas Sebrecht]
- offlineimap.conf: allow non-spaces in the account list. [Nicolas Sebrecht]
- Properly ignore folders with invalid characters (sep) in their name. [Nicolas Sebrecht]

#### Changes

- Add the repository name when connecting. [Nicolas Sebrecht]
- Github template: add system/distribution. [Nicolas Sebrecht]
- XOAUTH2: use one "public" attribute everywhere for self.oauth2_request_url. [Nicolas Sebrecht]
- Code style and minor code enhancements. [Nicolas Sebrecht]
- Manual: add known issue about netrc. [Nicolas Sebrecht]



### OfflineIMAP v7.0.2 (2016-07-27)

#### Notes

Small release to fix regression introduced in v7.0.0.

#### Authors

- Nicolas Sebrecht (1)
- Philipp Meier (1)
- Ævar Arnfjörð Bjarmason (1)

#### Features

- offlineimap.conf: learn to evaluate oauth2 related options. [Nicolas Sebrecht]

#### Fixes

- GmailMaildir: don't add a tuple to syncmessagesto_passes. [Philipp Meier]
- Remove double import of "six". [Ævar Arnfjörð Bjarmason]



### OfflineIMAP v7.0.1 (2016-07-26)

#### Notes

This is a small stable release fixing all the reported regressions and issues
about v7.0.0.

#### Authors

- Nicolas Sebrecht (9)

#### Fixes

- sqlite: properly serialize operations on the databases. [Nicolas Sebrecht]
- IMAP/IMAP: fix import issue about UIDMaps. [Nicolas Sebrecht]
- offlineimap.conf: allow non-spaces in the account list. [Nicolas Sebrecht]
- website-doc.sh: fix link in announces.yml. [Nicolas Sebrecht]
- release.sh: don't mess the mainline Changelog with commits in maint. [Nicolas Sebrecht]

#### Changes

- Improve error message when ssl_version must be set due to the tls_level. [Nicolas Sebrecht]
- Code cleanups.
- website-doc: order announces by date. [Nicolas Sebrecht]



### OfflineIMAP v7.0.0 (2016-07-22)

#### Notes

Finally, the new v7.0.0 is ready. This comes with breaking changes:

- Passwords are now expected in Unicode almost everywhere. They are used with
  the UTF-8 charset. However, some configuration options are not UTF-8 friendly
  mostly because of library limitations (e.g.: `remotepass`).

  Users with Unicode caracters in the passwords are recommended to use a file or
  `remotepasseval`.

- The sqlite database is the default.

  Please, read [this blog post]({% post_url 2016-05-19-sqlite-becomes-default %}).

- The PID file is no longer used because offlineimap is able to run multiple
  instances.

Please read the intermediate changelogs.


#### Authors

- Nicolas Sebrecht (9)

#### Features

- release.sh: learn to merge maint branch into next before releasing. [Nicolas Sebrecht]

#### Fixes

- sqlite: close the database when no more threads need access. [Nicolas Sebrecht]
- Fix attribute name _utime_from_header. [Nicolas Sebrecht]
- Maildir: OfflineImapError is missing the severity argument. [Nicolas Sebrecht]
- Fix: configparser does not know about python types like u"". [Nicolas Sebrecht]
- Manual: offlineimapui: fix minor rendering issue. [Nicolas Sebrecht]

#### Changes

- --info: allow user to enter a password. [Nicolas Sebrecht]
- Remove dead code: the description of the passes is never used. [Nicolas Sebrecht]
- offlineimap.conf: improve documentation for copy_ignore_eval. [Nicolas Sebrecht]



### OfflineIMAP v7.0.0-rc5 (2016-07-12)

#### Notes

This is a short -rc5 to stabilize the code with late improvements, mostly.

#### Authors

- Nicolas Sebrecht (9)
- Ævar Arnfjörð Bjarmason (1)

#### Features

- learn --delete-folder CLI option. [Nicolas Sebrecht]

#### Fixes

- mbnames: fix the filename extension for the intermediate files. [Nicolas Sebrecht]
- manual: offlineimap knows -V CLI option. [Nicolas Sebrecht]
- manual: remove unkown --column CLI option. [Nicolas Sebrecht]
- code of conduct: try to clarify what item 3 might mean. [Ævar Arnfjörð Bjarmason]

#### Changes

- mbnames: enable action at correct time. [Nicolas Sebrecht]
- mbnames: output message on errors while reading intermediate files. [Nicolas Sebrecht]
- --help: move -V option up. [Nicolas Sebrecht]
- init: factorize code to get active accounts. [Nicolas Sebrecht]


### OfflineIMAP v7.0.0-rc4 (2016-07-04)

#### Notes

Here we are to stabilize the code. I don't expect to merge features anymore.

When emails failed to download, offlineimap was raising the same issues again
and again. Users can now filter emails based on UID numbers.

The mbnames was missing a way to remove obsolete entries from deleted accounts.
Hence, --mbnames-prune is added.

Syncing folders with the local "sep" characters in their names was causing
troubles on next syncs. They are now filtered with a warning message.

IMAP/IMAP mode is improved: this was suffuring a (rare) bug related to
concurrent writes.

Usual code cleanups and minor improvements are included in this release.

I think this candidate is more stable than the previous v6.7.0 stable. Enjoy!

#### Authors

- Nicolas Sebrecht (17)

#### Features

- Learn to not download UIDs defined by the user. [Nicolas Sebrecht]
- Learn --mbnames-prune CLI option. [Nicolas Sebrecht]

#### Fixes

- UIDMaps (IMAP/IMAP mode): correctly protect from concurrent writes. [Nicolas Sebrecht]
- Correctly reraise errors with six. [Nicolas Sebrecht]
- Don't sync folders with local separator characters in their names. [Nicolas Sebrecht]

#### Changes

- Minor: improve "Copy message" output. [Nicolas Sebrecht]
- threadutil: use 'with' statements for lock. [Nicolas Sebrecht]
- Code cleanups and minor improvements. [Nicolas Sebrecht]
- release.sh: get_git_who(): remove unnecessary blank line. [Nicolas Sebrecht]
- website-doc.sh: fix line continuation. [Nicolas Sebrecht]


### OfflineIMAP v7.0.0-rc3 (2016-06-27)

#### Notes

The most important changes are:

- The passwords (and usernames) are now expected in Unicode.
- The sync_deletes feature is marked stable.
- It is possible to disable STARTTLS if it is failing.
- mbnames correctly honors the `-a` CLI option.

#### Authors

- Nicolas Sebrecht (31)
- Ilias Tsitsimpis (1)

#### Features

- Learn to disable STARTTLS. [Nicolas Sebrecht]
- Require usernames and passwords to be UTF-8 encoded. [Nicolas Sebrecht]
- offlineimap.conf: sync_deletes option is stable. [Nicolas Sebrecht]
- Learn -V CLI option. [Nicolas Sebrecht]

#### Fixes

- Don't try to copy messages with UID == 0. [Nicolas Sebrecht]
- Avoid removing of data when user removed a maildir. [Nicolas Sebrecht]
- When called with -a, mbnames must not erase entries of other accounts. [Nicolas Sebrecht]
- GmailMaildir: quick mode is not compatible with utime_from_header. [Nicolas Sebrecht]
- Manual: offlineimapui: minor typo fix. [Ilias Tsitsimpis]

#### Changes

- --info displays the imaplib2 version and whether it's the bundled or system one. [Nicolas Sebrecht]
- Bump from imaplib2 v2.53 to v2.55. [Nicolas Sebrecht]
- Move requirements.txt to the root directory. [Nicolas Sebrecht]
- README: rename "Requirements" section to "Requirements & dependencies". [Nicolas Sebrecht]
- README: add imaplib2 dependency and remove libraries in the standard libraries. [Nicolas Sebrecht]
- offlineimap.conf: improved comments. [Nicolas Sebrecht]
- sqlite was made mandatory: import error can fail at import time. [Nicolas Sebrecht]
- release.sh: put the authors directly to the AUTHORS section. [Nicolas Sebrecht]
- release.sh: learn users how to get the requirements file for pip. [Nicolas Sebrecht]
- website-doc.sh: include maintenance releases in the list of announces. [Nicolas Sebrecht]
- website-doc.sh: announces.yml: fill the page for the links. [Nicolas Sebrecht]
- Remove dead code and other code cleanups. [Nicolas Sebrecht]
- Style and comments improvements. [Nicolas Sebrecht]



### OfflineIMAP v7.0.0-rc2 (2016-06-04)

#### Notes

Enable offlineimap to run with Python 3. This feature is still experimental but
very welcome those days. Thanks Łukasz Żarnowiecki to work on this!

You are all welcome to test offlineimap with Python 3 and report both sucess and
failures.

Maintainers, we now work with a virtual imaplib2. Under the hood, the imported
imaplib2 can be the bundled version or any other (recent enough) imaplib2
provided by the system. If you already package imaplib2 and want to avoid
duplication of code, just remove the bundled version of imaplib2 while packaging
offlineimap and it should work out of the box. Be care, the filenames have
change.

#### Authors

- Nicolas Sebrecht (9)
- Łukasz Żarnowiecki (2)

#### Features

- Introduce a virtual imaplib2. [Nicolas Sebrecht]
- Mark Python 3 supported and experimental. [Nicolas Sebrecht]
- Allow to run under python3 without special env. [Łukasz Żarnowiecki]
- Maildir: Create top level dir recursively. [Łukasz Żarnowiecki]

#### Fixes

- IMAP: ignore UID with 0 as value when searching for UIDs. [Nicolas Sebrecht]
- Minor: fix copyright date. [Nicolas Sebrecht]

#### Changes

- Threading: improve comments. [Nicolas Sebrecht]
- Bump imaplib2 from v2.52 to v2.53. [Nicolas Sebrecht]
- globals: use whitespaces instead of tabs. [Nicolas Sebrecht]
- six: add requirements for pip. [Nicolas Sebrecht]
- README: add six library requirement. [Nicolas Sebrecht]

### OfflineIMAP v7.0.0-rc1 (2016-05-19)

#### Notes

We are starting a new major release cycle.

The dabatase for the cache is now sqlite by default. This means downgrading to
previous versions is prone to errors if you don't have sqlite enabled in your
configuration. All users should enable the sqlite database now to avoid issues.
Expect the legacy text files to be deprecated and removed in the future.

The long time awaited feature to not delete any message while allowing adding
new messages and sync flags is now implemented and marked stable. Thanks to the
testers and your feedbacks!

Łukasz started the work to support Python 3. Because of this, the six dependency
is required.

If you have scripts using the pid file, be aware this file is no longer used
because running multiple instances of the program is supported for years.

There a lot of code factorization and documentation improvements, especially
around threads.

I'm happy new contributors joined the official team, especially Łukasz and Ilias
(Debian maintainer). Thank you!

#### Authors

- Nicolas Sebrecht (32)
- Łukasz Żarnowiecki (17)
- Dodji Seketeli (1)
- Om Prakash (1)

#### Features

- Make sqlite status cache the default. [Nicolas Sebrecht]
- Learn to not delete messages. [Nicolas Sebrecht]
- Inform when maxage/startdate is in the future. [Łukasz Żarnowiecki]
- offlineimap.conf: XOAUTH2: expose and document the oauth2_request_url option. [Nicolas Sebrecht]
- offlineimap.conf: improve documentation for oauth2. [Nicolas Sebrecht]

#### Fixes

- sqlite: open database when we use it rather than at instantiation time. [Nicolas Sebrecht]
- SQLite: close db when done. [Nicolas Sebrecht]
- conf: newmail_hook is a remote option. [Nicolas Sebrecht]
- folder: utime_from_header is for Maildir only. [Nicolas Sebrecht]
- Handle maxage for davmail correctly. [Łukasz Żarnowiecki]
- XOAUTH2: don't force oauth2_request_url to be defined. [Nicolas Sebrecht]
- XOAUTH2: raise error when oauth_request_url is missing for IMAP type. [Nicolas Sebrecht]
- IMAP: don't try to create empty folders. [Nicolas Sebrecht]
- Really execute the recipe of the 'docs' target in top-most Makefile. [Dodji Seketeli]

#### Changes

- release.sh: make no differences between contributors. [Nicolas Sebrecht]
- threading: fix variable names about namespaces. [Nicolas Sebrecht]
- imapserver: use boolean where it makes sense. [Nicolas Sebrecht]
- threading: suggeststhreads must honor CLI and conf options. [Nicolas Sebrecht]
- threading: improve variable names and factorize code. [Nicolas Sebrecht]
- py3: raise exceptions using six module. [Łukasz Żarnowiecki]
- threading: minor improvements. [Nicolas Sebrecht]
- instancelimitedsems does not need a lock but must be used with global. [Nicolas Sebrecht]
- threading: get rid of the syncaccount function. [Nicolas Sebrecht]
- get rid of offlineimap/syncmaster.py. [Nicolas Sebrecht]
- threading: rename threadslist to accountThreads. [Nicolas Sebrecht]
- threading: simplify names. [Nicolas Sebrecht]
- Encode utf-8 argument for md5 function. [Łukasz Żarnowiecki]
- Replace dictionary iteration methods. [Łukasz Żarnowiecki]
- threading: simplify the monitoring code for threads. [Nicolas Sebrecht]
- threadutil: don't limit the number of threads. [Nicolas Sebrecht]
- threading: add comments. [Nicolas Sebrecht]
- Wrap zip calls with list call. [Łukasz Żarnowiecki]
- Remove xreadlines calls. [Łukasz Żarnowiecki]
- Replace xrange with range. [Łukasz Żarnowiecki]
- Replace has_key method to "key in dict". [Łukasz Żarnowiecki]
- Change filter with lambda to list comprehension. [Łukasz Żarnowiecki]
- Replace calls to long with int calls. [Łukasz Żarnowiecki]
- Add workaround for string.split for Python3. [Łukasz Żarnowiecki]
- Convert basestring to str. [Łukasz Żarnowiecki]
- Rename email.Parser to email.parser. [Łukasz Żarnowiecki]
- Do not mix tabs with spaces. [Łukasz Żarnowiecki]
- Convert except X,T to except X as T. [Łukasz Żarnowiecki]
- Add tags to gitignore. [Łukasz Żarnowiecki]
- don't write a pid file. [Nicolas Sebrecht]
- manual: improve rendering. [Nicolas Sebrecht]
- manual: improve sqlite section. [Nicolas Sebrecht]
- minor: logs: print readonly message in all debug modes. [Nicolas Sebrecht]
- accounts.py: minor improvements. [Nicolas Sebrecht]
- folder: properly factorize initialization and dropping of self.message. [Nicolas Sebrecht]
- offlineimap.txt: minor typo fixes. [Om Prakash]

### OfflineIMAP v6.7.0 (2016-03-10)

#### Notes

New stable release out!

With the work of Ilias, maintainer at Debian, OfflineIMAP is learning a new CLI
option to help fixing filenames for the users using nametrans and updating from
versions prior to v6.3.5.  Distribution maintainers might want to backport this
feature for their packaged versions out after v6.3.5. Have a look at commit
c84d23b65670f to know more.

OfflineIMAP earns the slogan "Get the emails where you need them", authored by
Norbert Preining.

Julien Danjou, the author of the book _The Hacker’s Guide To Python_, shared us
his screenshot of a running session of OfflineIMAP.

I recently created rooms for chat sessions at Gitter. It appears to be really
cool, supports seamless authentication with a github account, persistent logs,
desktop/mobile clients and many more usefull features. Join us at Gitter!

- https://gitter.im/OfflineIMAP/offlineimap  [NEW]
- https://gitter.im/OfflineIMAP/imapfw       [NEW]

Now, the OfflineIMAP community has 2 official websites:

- http://www.offlineimap.org (for offlineimap)
- http://imapfw.offlineimap.org (for imapfw) [NEW]

The Twitter account was resurrected, too. Feel free to join us:

  https://twitter.com/OfflineIMAP

Finally, the teams of the OfflineIMAP organization at Github were renewed to
facilitate the integration of new contributors and directly improve both the
documentation and the websites.

As a side note, the [imapfw repository](https://github.com/OfflineIMAP/imapfw)
has now more than 50 stargazers. This is very encouraging.

Thank you much everybody for your various contributions into OfflineIMAP!

#### Authors

- Ben Boeckel (1)
- Ebben Aries (1)
- Ilias Tsitsimpis (1)

#### Features

- Introduce a code of conduct.
- Add github templates.
- Change hard coding of AF_UNSPEC to user-defined address-families per repository. [Ebben Aries]
- Add documentation for the ipv6 configuration option.

#### Fixes

- Identify and fix messages with FMD5 inconsistencies. [Ilias Tsitsimpis]
- Curses, UIBase: remove references to __bigversion__. [Ben Boeckel]
- Sphinx doc: remove usage of __bigversion__.
- MANIFEST: exclude rfcs (used for Pypi packages).
- Changelog: fix typo.

#### Changes

- release.sh: move the authors section up.
- release.sh: add pypi instructions.
- MAINTAINERS: update.




### OfflineIMAP v6.7.0-rc2 (2016-02-22)

#### Notes

Learn to abruptly abort on multiple Ctrl+C.

Some bugs got fixed. XOAUTH2 now honors the proxy configuration option.  Error
message was improved when it fails to write a new mail in a local Maildir.

I've enabled the hook for integration with Github. You'll get notifications on
updates of the master branch of the repository (mostly for new releases). I may
write some tweets about OfflineIMAP sometimes.

#### Features

- Abort after three Ctrl-C keystrokes.

#### Fixes

- Fix year of copyright.
- Versioning: avoid confusing pip by spliting out __version__ with __revision__.
- Fix: exceptions.OSError might not have attribute EEXIST defined.
- XOAUTH2 handler: urlopen with proxied socket.
- Manual: small grammar fix.
- Fix typos in offlineimap(1) manpage.

#### Changes

- Update links to the new URL www.offlineimap.org.


### OfflineIMAP v6.7.0-rc1 (2016-01-24)

#### Notes

Starting a new cycle with all EXPERIMENTAL and TESTING stuff marked stable.
Otherwise, not much exciting yet. There's pending work that would need some
love by contributors:

- https://github.com/OfflineIMAP/offlineimap/issues/211
- https://github.com/OfflineIMAP/offlineimap/pull/111
- https://github.com/OfflineIMAP/offlineimap/issues/184

#### Features

- Allow authorization via XOAUTH2 using access token.

#### Fixes

- Revert "Don't output initial blurb in "quiet" mode".
- Fix Changelog.

#### Changes

- Declare newmail_hook option stable.
- Declare utime_from_header option stable.
- Decode foldernames is removed EXPERIMENTAL flag.
- Declare XOAUTH2 stable.
- Declare tls_level option stable.
- Declare IMAP Keywords option stable.


### OfflineIMAP v6.6.1 (2015-12-28)

#### Notes

This is a very small new stable release for two fixes.

Amending support for BINARY APPEND which is not correctly implemented. Also,
remove potential harms from dot files in a local maildir.

#### Fixes

- Bump imaplib2 from 2.53 to 2.52. Remove support for binary send.
- Ignore aloo dot files in the Maildir while scanning for mails.


### OfflineIMAP v6.6.0 (2015-12-05)

#### Features

- Maildir learns to mimic Dovecot's format of lower-case letters (a,b,c..) for
  "custom flags" or user keywords.

#### Fixes

- Broken retry loop would break connection management.
- Replace rogue `print` statement by `self.ui.debug`.

#### Changes

- Bump imaplib2 from v2.52 to v2.53.
- Code cleanups.
- Add a full stack of all thread dump upon EXIT or KILL signal in thread debug
  mode.


### OfflineIMAP v6.6.0-rc3 (2015-11-05)

#### Notes

Changes are slowing down and the code is under serious testing by some new
contributors. Everything expected at this time in the release cycle. Thanks to
them.

SSL is now enabled by default to prevent from sending private data in clear
stream to the wild.

#### Features

- Add new config option `filename_use_mail_timestamp`.

#### Fixes

- Bump from imaplib2 v2.51 to v2.52.
- Minor fixes.

#### Changes

- Enable SSL by default.
- Fix: avoid writing password to log.
- offlineimap.conf: improve namtrans doc a bit.


### OfflineIMAP v6.6.0-rc2 (2015-10-15)

#### Notes

Interesting job was done in this release with 3 new features:

- Support for XOAUTH2;
- New 'tls_level' configuration option to automatically discard insecure SSL protocols;
- New interface 'syslog' comes in, next to the -s CLI option. This allows better
  integration with systemd.

I won't merge big changes until the stable is out. IOW, you can seriously start
testing this rc2.

#### Features

- Add a new syslog ui.
- Introduce the 'tls_level' configuration option.
- Learn XOAUTH2 authentication (used by Gmail servers).
- Manual IDLE section improved (minor).

#### Fixes

- Configuration option utime_from_header handles out-of-bounds dates.
- offlineimap.conf: fix erroneous assumption about ssl23.
- Fix status code to reflect success or failure of a sync.
- contrib/release.sh: fix changelog edition.

#### Changes

- Bump imaplib2 from v2.48 to v2.51.
- README: new section status and future.
- Minor code cleanups.
- Makefile: improve building of targz.
- systemd: log to syslog rather than stderr for better integration.


### OfflineIMAP v6.6.0-rc1 (2015-09-28)

#### Notes

Let's go with a new release.

Basic UTF support was implemented while it is still exeprimental. Use this with
care.  OfflineIMAP can now send the logs to syslog and notify on new mail.


#### Features

- logging: add a switch to log to syslog.
- Added the newmail_hook.
- utf-7 feature is set experimental.

#### Fixes

- offlineimap.conf: fix a typo in the new mail hook example.
- Fix language.
- Fix spelling inconsistency.
- offlineimap.conf: don't use quotes for sep option.
- man page: fingerprint can be used with SSL.
- fix #225 « Runonce (offlineimap -o) does not stop if autorefresh is declared in DEFAULT section ».
- CONTRIBUTING: fix links to offlineimap.org.

#### Changes

- Bump imaplib2 from 2.43 to 2.48
- README: small improvements



### OfflineIMAP v6.5.7 (2015-05-15)

#### Notes

Almost no change since last release candidate. This is a sign that this release
is stable. ,-)

There was big changes since previous stable and users - especially distribution
maintainers - should really read the intermediate changelogs.

At the beginning of this year, I've tried to implement Unicode support. As you
know, I was not satisfied with the result. Then, I've published my code analysis
where I talk about doing a lot of refactoring for more proper OOP practices.
What's new is that I've actually done it and stopped this work as soon as I
realized that it means entirely rewriting the software.

On top of this, I'm not fully satisfied with other current limitations:
- old legacy support;
- migration to Python 3;
- complex multithreading design;
- some restrictions of the GPLv2 license;
- etc.

That's why I've started a new product. I'll publish it in the coming weeks under
the MIT license.

#### Features

- Better documentation for Windows users.
- contrib/release.sh (v0.2): fixes and improvements.

#### Fixes

- Report exceptions via exit code.
- Proxy feature leaks DNS support: offlineimap.conf talks about this.
- Email parsing for date coudn't work: fix datetuple dst check.

#### Changes

- Little code refactoring.


### OfflineIMAP v6.5.7-rc4 (2015-04-07)

#### Notes

Contrary to what the detailed following changes look like, here is a much bigger
release than expected.

Most important change is about maxage being sightly revisited. The whole
internal logic was found broken. Janna Martl did the hard work of raising the
issues and get them fixed.

New configuration options are added.

Maintainer Dmitrijs Ledkovs has left the organization. We wish you well! ,-)
Sebastian Spaeth let us know he will be almost inactive. We wish you well, too!

#### Features

- Add configuration option "utime_from_header" (TESTING).
- Add systemd integration files.
- mbnames: add new option "incremental" to write the file once per account.

#### Fixes

- maxage: fix timezone issues, remove IMAP-IMAP support, add startdate option.
- Test suites fixed and improved.
- Fix inaccurate UI messages when some messages are internally excluded from the
  cached lists.

#### Changes

- imaplib2: bump to v2.43.
- More documentations moves to the website.
- Maintainer Dmitrijs has left the organization.
- Remove unnecessary imaplib2 workaround.
- release.sh: script for maintainers improved.


### OfflineIMAP v6.5.7-rc3 (2015-03-19)

#### Notes

Here comes a much bigger release than expected! With this release, the new
website is made official.

Distribution maintainers, be aware that we now have a new man page
offlineimapui(7)!

Also, the man page offlineimap(1) is sightly revised to explain the command line
options. Since `offlineimap --help` won't detail the options anymore, it becomes
critical.

The maxage feature was broken by design and could delete mails on one side. It
is still under heavy work to fix issues when timezones are not synced. Gmail is
known to use different timezones accross mailboxes.

The IMAP library imaplib2 was updated for the upcoming course to Python 3.

The most other important changes are:

- Possibility to use a proxy.
- All the documentation are SIGHTLY revisited and updated from all the available
  places (sources files in the repository, wiki, website). A lot was moved from
  the wiki and the sources to the website.
- the RFCs are available in the repository.

#### Features

- Add proxy support powered by PySocks.
- New man page offlineimapui to explain the available UIs.
- Add a CONTRIBUTING.rst file.
- Add a `TODO.rst` list for the contributors.
- Add a script for maintainers to roll out new releases.
- Add the `scripts/get-repository.sh` script to work on the website and the wiki.
- Doc: add IMAP RFCs.

#### Fixes

- Don't loose local mails because of maxage.
- Properly handle the cached messagelist.
- Do not error if `remoteuser` is not configured.
- imaplibutil: add missing errno import.
- LocalStatusSQLite: labels: don't fail if database returns unexpected None value.
- IDLE: continue trying selecting the folder on `OfflineImapError.Error`.

#### Changes

- imaplib2: bump to v2.42
- `--help` becomes concise.
- Changelogs: move format back to markdown/kramdown to be more compatible with Jekyll.
- README: deep cleanups.
- code cleanups.
- code: more style consistency.
- sqlite: provide offending filename when open fails.
- MANUAL: full refactoring, change format to asciidoc.
- MANUAL: rename "KNOWN BUGS" TO "KNOWN ISSUES".
- MANUAL: add known issues entry about socktimeout for suspended sessions.
- offlineimap.conf: say what is the default value for the sep option.
- sqlite: provide information on what is failing for `OperationalError`.
- remove obsolete documentation.


### OfflineIMAP v6.5.7-rc2 (2015-01-18)

#### Notes

This release candidate should be minor for most users.

The best points are about SSL not falling back on other authentication methods
when failing, better RAM footprint and reduced I/O access.

Documentation had our attention, too.

There's some code cleanups and code refactoring, as usual.

#### Features

* Do not keep reloading pyhtonfile, make it stateful.
* HACKING: how to create tags.
* MANUAL: add minor sample on how to retrieve a password with a helper python file.

#### Fixes

* Make OS-default CA certificate file to be requested explicitely.
* SSL: do not fallback on other authentication mode if it fails.
* Fix regression introduced while style patching.
* API documentation: properly auto-document main class, fixes.
* ui: Machine: remove offending param for a _printData() call.
* Drop caches after having processed folders.

#### Changes

* Fix unexpected garbage code.
* Properly re-raise exception to save original tracebacks.
* Refactoring: avoid redefining various Python keywords.
* Code: improvements of comments and more style consistency.
* Configuration file: better design and other small improvements.
* nametrans documentation: fix minor error.
* Unused import removal.
* Add a note about the incorrect rendering of the docstring with Sphinx.
* Errors handling: log the messages with level ERROR.
* MAINTAINERS: add mailing list maintainers.
* Fixed copyright statement.
* COPYING: fix unexpected characters.


### OfflineIMAP v6.5.7-rc1 (2015-01-07)

#### Notes

I think it's time for a new release candidate. Our release cycles are long
enough and users are asked to use the current TIP of the next branch to test
our recent patches.

The current version makes better support for environment variable expansion and
improves OS portability. Gmail should be better supported: we are still
expecting feedbacks. Embedded library imaplib2 is updated to v2.37.
Debugging messages are added and polished.

There's some code cleanups and refactoring, also.


#### Features

* Expand environment variables in the following
  configuration items:
  - general.pythonfile;
  - general.metadata;
  - mbnames.filename;
  - Repository.localfolders.
  - Repository.sslcacertfile.
  Make tilde and environment variable expansion in the following
  configuration items:
  - Repository.sslclientcert;
  - Repository.sslclientkey.
* Support default CA bundle locations for a couple of
  known Unix systems (Michael Vogt, GutHub pull #19)
* Added default CA bundle location for OpenBSD
  (GitHub pull #120) and DragonFlyBSD.

#### Fixes

* Fix unbounded recursion during flag update (Josh Berry).
* Do not ignore gmail labels if header appears multiple times
* Delete gmail labels header before adding a new one
* Fix improper header separator for X-OfflineIMAP header
* Match header names case-insensitively
* Create SQLite database directory if it doesn't exist
  yet; warn if path is not a directory (Nick Farrell,
  GutHub pull #102)
* Properly manipulate contents of messagelist for folder
* Fix label processing in GmailMaildir
* Properly capitalize OpenSSL
* Fix warning-level message processing by MachineUI
  (GitHub pull #64, GitHub pull #118).
* Properly generate tarball from "sdist" command (GitHub #137)
* Fix Markdown formatting
* Fix typo in apply_xforms invocation
* Merge pull request #136 from aroig/gh/label-fix
* Fix mangled message headers for servers without UIDPLUS:
  X-OfflineIMAP was added with preceeding '\n' instead of
  '\r\n' just before message was uploaded to the IMAP server.
* Add missing version bump for 6.5.6 (it was released with
  6.5.5 in setup.py and other places).

#### Changes

* Warn about a tricky piece of code in addmessageheader
* Rename addmessageheader()'s crlf parameter to linebreak
* addmessageheader: fix case #2 and flesh out docstring
* addmessageheader(): add debug for header insertion
* Add version qualifier to differentiate releases and development ones
* More clearly show results of folder name translation
* IMAP: provide message-id in error messages
* Trade recursion by plain old cycle
* Avoid copying array every time, just slice it
* Added OpenSSL exception clause to our main GPL to allow
  people to link with OpenSSL in run-time.  It is needed
  at least for Debian, see
    https://lists.debian.org/debian-legal/2002/10/msg00113.html
  for details.
* Brought CustomConfig.py into more proper shape
* Updated bundled imaplib2 to 2.37:
  - add missing idle_lock in _handler()
* Imaplib2: trade backticks to repr()
* Introduce CustomConfig method that applies set of transforms
* imaplibutil.py: remove unused imports
* CustomConfig.py: remove unused imports
* init.py: remove unused import
* repository/Base.py: remove unused import
* repository/GmailMaildir.py: remove unused import
* repository/LocalStatus.py: remove unused import
* ui/Curses.py: remove unused import
* ui/UIBase.py: remove unused import
* localeval: comment on security issues
* docs: remove obsolete comment about SubmittingPatches.rst
* utils/const.py: fix ident
* ui/UIBase: folderlist(): avoid built-in list() redefinition
* more consistent style



### OfflineIMAP v6.5.6 (2014-05-14)

* Fix IDLE mode regression (it didn't worked) introduced
  after v6.5.5 (pointy hat goes to Eygene Ryabinkin, kudos --
  to Tomasz Żok)


### OfflineIMAP v6.5.6-rc1 (2014-05-14)

* Add knob to invoke folderfilter dynamically on each sync (GitHub#73)
* Add knob to apply compression to IMAP connections (Abdó Roig-Maranges)
* Add knob to filter some headers before uploading message
  to IMAP server (Abdó Roig-Maranges)
* Allow to sync GMail labels and implement GmailMaildir repository that
  adds mechanics to change message labels (Abdó Roig-Maranges)
* Allow to migrate status data across differend backends
  (Abdó Roig-Maranges)
* Support XDG Base Directory Specification
  (if $XDG_CONFIG_HOME/offlineimap/config exists, use it as the
  default configuration path; ~/.offlineimaprc is still tried after
  XDG location) (GitHub#32)
* Allow multiple certificate fingerprints to be specified inside
  'cert_fingerprint'


### OfflineIMAP v6.5.5 (2013-10-07)

* Avoid lockups for IMAP synchronizations running with the
  "-1" command-line switch (X-Ryl669 <boite.pour.spam@gmail.com>)
* Dump stacktrace for all threads on SIGQUIT: ease debugging
  of threading and other issues
* SIGHUP is now handled as the termination notification rather than
  the signal to reread the configuration (Dmitrijs Ledkovs)
* Honor the timezone of emails (Tobias Thierer)
* Allow mbnames output to be sorted by a custom sort key by specifying
  a 'sort_keyfunc' function in the [mbnames] section of the config.
* Support SASL PLAIN authentication method.  (Andreas Mack)
* Support transport-only tunnels that requre full IMAP authentication.
  (Steve Purcell)
* Make the list of authentication mechanisms to be configurable.
  (Andreas Mack)
* Allow to set message access and modification timestamps based
  on the "Date" header of the message itself.  (Cyril Russo)
* "peritem" format string for [mbnames] got new expansion key
  "localfolders" that corresponds to the same parameter of the
  local repository for the account being processed.
* [regression] pass folder names to the foldersort function,
  revert the documented behaviour
* Fix handling of zero-sized IMAP data items (GitHub#15).
* Updated bundled imaplib2 to 2.35:
  - fix for Gmail sending a BYE response after reading >100 messages
    in a session;
  - includes fix for GitHub#15: patch was accepted upstream.
* Updated bundled imaplib2 to 2.36: it includes support for SSL
  version override that was integrated into our code before,
  no other changes.
* Fixed parsing of quoted strings in IMAP responses: strings like "\\"
  were treated as having \" as the escaped quote, rather than treating
  it as the quoted escaped backslash (GitHub#53).
* Execute pre/post-sync hooks during synchronizations
  toggled by IMAP IDLE message processing. (maxgerer@gmail.com)
* Catch unsuccessful local mail uploads when IMAP server
  responds with "NO" status; that resulted in a loss of such
  local messages. (Adam Spiers)
* Don't create folders if readonly is enabled.
* Learn to deal with readonly folders to properly detect this
  condition and act accordingly.  One example is Gmail's "Chats"
  folder that is read-only, but contains logs of the quick chats. (E.
  Ryabinkin)
* Fix str.format() calls for Python 2.6 (D. Logie)
* Remove APPENDUID hack, previously introduced to fix Gmail, no longer
  necessary, it might have been breaking things. (J. Wiegley)
* Improve regex that could lead to 'NoneType' object has no attribute
  'group' (D. Franke)
* Improved error throwing on repository misconfiguration

### OfflineIMAP v6.5.4 (2012-06-02)

* bump bundled imaplib2 library 2.29 --> 2.33
* Actually perform the SSL fingerprint check (reported by J. Cook)
* Curses UI, don't use colors after we shut down curses already (C.Höger)
* Document that '%' needs encoding as '%%' in configuration files.
* Fix crash when IMAP.quickchanged() led to an Error (reported by sharat87)
* Implement the createfolders setting to disable folder propagation (see docs)

### OfflineIMAP v6.5.3.1 (2012-04-03)

* Don't fail if no dry-run setting exists in offlineimap.conf
  (introduced in 6.5.3)


### OfflineIMAP v6.5.3 (2012-04-02)

* --dry-run mode protects us from performing any actual action.  It will
  not precisely give the exact information what will happen. If e.g. it
  would need to create a folder, it merely outputs "Would create folder
  X", but not how many and which mails it would transfer.

* internal code changes to prepare for Python3

* Improve user documentation of nametrans/folderfilter

* Fixed some cases where invalid nametrans rules were not caught and
  we would not propagate local folders to the remote repository.
  (now tested in test03)

* Revert "* Slight performance enhancement uploading mails to an IMAP
  server in the common case." It might have led to instabilities.

* Revamped documentation structure. `make` in the `docs` dir or `make
  doc` in the root dir will now create the 1) man page and 2) the user
  documentation using sphinx (requiring python-doctools, and
  sphinx). The resulting user docs are in `docs/html`. You can also
  only create the man pages with `make man` in the `docs` dir.

* -f command line option only works on the untranslated remote
  repository folder names now. Previously folderfilters had to match
  both the local AND remote name which caused unwanted behavior in
  combination with nametrans rules. Clarify in the help text.

* Some better output when using nonsensical configuration settings

* Improve compatability of the curses UI with python 2.6

### OfflineIMAP v6.5.2.1 (2012-04-04)

* Fix python2.6 compatibility with the TTYUI backend (crash)

* Fix TTYUI regression from 6.5.2 in refresh loop (crash)

* Fix crashes related to UIDVALIDITY returning "None"

* Beginning of a test suite. So far there is only one test. Configure
  test/credentials.conf and invoke with "python setup.py test"

* Make folders containing quotes work rather than crashing
  (reported by Mark Eichin)

* Improve delete msg performance with SQLITE backend

* Enforce basic UI when using the --info switch

* Remove the Gmail "realdelete" option, as it could lead to potential
  data loss.

### OfflineIMAP v6.5.2 (2012-01-17)

* Gmail "realdelete" option is considered harmful and has the potential
  for data loss. Analysis at
  http://article.gmane.org/gmane.mail.imap.offlineimap.general/5265
  Warnings were added to offlineimap.conf

* Rather than write out the nametrans'lated folder names for mbnames, we
  now write out the local untransformed box names. This is generally
  what we want. This became relevant since we support nametrans rules on
  the local side since only a short time. Reported by Paul Collignan.

* Some sanity checks and improved error messages.

* Revert 6.5.1.1 change to use public imaplib2 function, it was reported to
  not always work.

* Don't fail when ~/netrc is not readable by us.

* Don't emit noisy regular sleeping announcements in Basic UI.

### OfflineIMAP v6.5.1.2 (2012-01-07) - "Baby steps"

Smallish bug fixes that deserve to be put out.

* Fix possible crash during --info run
* Fix reading in Maildirs, where we would attempt to create empty
  directories on REMOTE.
* Do not attempt to sync lower case custom Maildir flags. We do not
  support them (yet) (this prevents many scary bogus sync messages)
* Add filter information to the filter list in --info output

### OfflineIMAP v6.5.1.1 (2012-01-07) - "Das machine control is nicht fur gerfinger-poken und mittengrabben"

Blinkenlights UI 6.5.0 regression fixes only.

* Sleep led to crash ('abort_signal' not existing)

* Make exit via 'q' key work again cleanly

### OfflineIMAP v6.5.1 (2012-01-07) - "Quest for stability"

* Fixed Maildir regression "flagmatchre" not found. (regressed in 6.5.0)

* Have console output go by default to STDOUT and not STDERR (regression
  in 6.5.0)

* Fixed MachineUI to urlencode() output lines again, rather than
  outputting multi-line items. It's ugly as hell, but it had been that
  way for years.

* Remove the old global locking system. We lock only the accounts that
  we currently sync, so you can invoke OfflineImap multiple times now as
  long as you sync different accounts. This system is compatible with
  all releases >= 6.4.0, so don't run older releases simultanous to this
  one.

### OfflineIMAP v6.5.0 (2012-01-06)

This is a CRITICAL bug fix release for everyone who is on the 6.4.x series.
Please upgrade to avoid potential data loss! The version has been bumped to
6.5.0, please let everyone know that the 6.4.x series is problematic.

* Uploading multiple emails to an IMAP server would lead to wrong UIDs
  being returned (ie the same for all), which confused offlineimap and
  led to recurrent upload/download loops and inconsistencies in the
  IMAP<->IMAP uid mapping.

* Uploading of Messages from Maildir and IMAP<->IMAP has been made more
  efficient by renaming files/mapping entries, rather than actually
  loading and saving the message under a new UID.

* Fix regression that broke MachineUI

### OfflineIMAP v6.4.4 (2012-01-06)

This is a bugfix release, fixing regressions occurring in or since 6.4.0.

* Fix the missing folder error that occured when a new remote folder was
  detected (IMAP<->Maildir)

* Possibly fixed bug that prevented us from ever re-reading Maildir
  folders, so flag changes and deletions were not detected when running
  in a refresh loop. This is a regression that was introduced in about
  6.4.0.

* Never mangle maildir file names when using nonstandard Maildir flags
  (such as 'a'), note that they will still be deleted as they are not
  supported in the sync to an IMAP server.

### OfflineIMAP v6.4.3 (2012-01-04)

#### New Features

* add a --info command line switch that outputs useful information about
  the server and the configuration for all enabled accounts.

#### Changes

* Reworked logging which was reported to e.g. not flush output to files
  often enough. User-visible changes:
  a) console output goes to stderr (for now).
  b) file output has timestamps and looks identical in the basic and
  ttyui UIs.
  c) File output should be flushed after logging by default (do
  report if not).

* Bumped bundled imaplib2 to release 2.29

* Make ctrl-c exit cleanly rather aborting brutally (which could leave
  around temporary files, half-written cache files, etc). Exiting on
  SIGTERM and CTRL-C can take a little longer, but will be clean.


### OfflineIMAP v6.4.2 (2011-12-01)

* IMAP<->IMAP sync with a readonly local IMAP repository failed with a
  rather mysterious "TypeError: expected a character buffer object"
  error. Fix this my retrieving the list of folders early enough even
  for readonly repositories.

* Fix regression from 6.4.0. When using local Maildirs with "/" as a
  folder separator, all folder names would get a trailing slash
  appended, which is plain wrong.

### OfflineIMAP v6.4.1 (2011-11-17)

#### Changes

* Indicate progress when copying many messages (slightly change log format)

* Output how long an account sync took (min:sec).

#### Bug Fixes

* Syncing multiple accounts in single-threaded mode would fail as we try
  to "register" a thread as belonging to two accounts which was
  fatal. Make it non-fatal (it can be legitimate).

* New folders on the remote would be skipped on the very sync run they
  are created and only by synced in subsequent runs. Fixed.

* a readonly parameter to select() was not always treated correctly,
  which could result in some folders being opened read-only when we
  really needed read-write.

### OfflineIMAP v6.4.0 (2011-09-29)

This is the first stable release to support the forward-compatible per-account
locks and remote folder creation that has been introduced in the 6.3.5 series.

* Various regression and bug fixes from the last couple of RCs

### OfflineIMAP v6.3.5-rc3 (2011-09-21)

#### Changes

* Refresh server capabilities after login, so we know that Gmail
  supports UIDPLUS (it only announces that after login, not
  before). This prevents us from adding custom headers to Gmail uploads.

#### Bug Fixes

* Fix the creation of folders on remote repositories, which was still
  botched on rc2.

### OfflineIMAP v6.3.5-rc2 (2011-09-19)

#### New Features

* Implement per-account locking, so that it will possible to sync
  different accounts at the same time. The old global lock is still in
  place for backward compatibility reasons (to be able to run old and
  new versions of OfflineImap concurrently) and will be removed in the
  future. Starting with this version, OfflineImap will be
  forward-compatible with the per-account locking style.

* Implement RFC 2595 LOGINDISABLED. Warn the user and abort when we
  attempt a plaintext login but the server has explicitly disabled
  plaintext logins rather than crashing.

* Folders will now also be automatically created on the REMOTE side of
  an account if they exist on the local side. Use the folderfilters
  setting on the local side to prevent some folders from migrating to
  the remote side.  Also, if you have a nametrans setting on the remote
  repository, you might need a nametrans setting on the local repository
  that leads to the original name (reverse nametrans).

#### Changes

* Documentation improvements concerning 'restoreatime' and some code cleanup

* Maildir repositories now also respond to folderfilter= configurations.

#### Bug Fixes

* New emails are not created with "-rwxr-xr-x" but as "-rw-r--r--"
  anymore, fixing a regression in 6.3.4.

### OfflineIMAP v6.3.5-rc1 (2011-09-12)

#### Notes

Idle feature and SQLite backend leave the experimental stage! ,-)

#### New Features

* When a message upload/download fails, we do not abort the whole folder
  synchronization, but only skip that message, informing the user at the
  end of the sync run.

* If you connect via ssl and 'cert_fingerprint' is configured, we check
  that the server certificate is actually known and identical by
  comparing the stored sha1 fingerprint with the current one.

#### Changes

* Refactor our IMAPServer class. Background work without user-visible
  changes.
* Remove the configurability of the Blinkenlights statuschar. It
  cluttered the main configuration file for little gain.
* Updated bundled imaplib2 to version 2.28.

#### Bug Fixes

* We protect more robustly against asking for inexistent messages from the
  IMAP server, when someone else deletes or moves messages while we sync.
* Selecting inexistent folders specified in folderincludes now throws
  nice errors and continues to sync with all other folders rather than
  exiting offlineimap with a traceback.



### OfflineIMAP v6.3.4 (2011-08-10)

#### Notes

Here we are. A nice release since v6.3.3, I think.

#### Changes

* Handle when UID can't be found on saved messages.



### OfflineIMAP v6.3.4-rc4 (2011-07-27)

#### Notes

There is nothing exciting in this release. This is somewhat expected due to the
late merge on -rc3.

#### New Features

* Support maildir for Windows.

#### Changes

* Manual improved.


### OfflineIMAP v6.3.4-rc3 (2011-07-07)

#### Notes

Here is a surprising release. :-)

As expected we have a lot bug fixes in this round (see git log for details),
including a fix for a bug we had for ages (details below) which is a very good
news.

What makes this cycle so unusual is that I merged a feature to support StartTLS
automatically (thanks Sebastian!). Another very good news.

We usually don't do much changes so late in a cycle. Now, things are highly
calming down and I hope a lot of people will test this release. Next one could
be the stable!

#### New Features

* Added StartTLS support, it will automatically be used if the server
  supports it.

#### Bug Fixes

* We protect more robustly against asking for inexistent messages from the
  IMAP server, when someone else deletes or moves messages while we sync.


### OfflineIMAP v6.3.4-rc2 (2011-06-15)

#### Notes

This was a very active rc1 and we could expect a lot of new fixes for the next
release.

The most important fix is about a bug that could lead to data loss. Find more
information about his bug here:

  http://permalink.gmane.org/gmane.mail.imap.offlineimap.general/3803

The IDLE support is merged as experimental feature.

#### New Features

* Implement experimental IDLE feature.

#### Changes

* Maildirs use less memory while syncing.

#### Bug Fixes

* Saving to Maildirs now checks for file existence without race conditions.
* A bug in the underlying imap library has been fixed that could
  potentially lead to data loss if the server interrupted responses with
  unexpected but legal server status responses. This would mainly occur
  in folders with many thousands of emails. Upgrading from the previous
  release is strongly recommended.


### OfflineIMAP v6.3.4-rc1 (2011-05-16)

#### Notes

Welcome to the v6.3.4 pre-release cycle. Your favorite IMAP tool wins 2 new
features which were asked for a long time:
* an experimental SQL-based backend for the local cache;
* one-way synchronization cabability.

Logic synchronization is reviewed and simplified (from 4 to 3 passes) giving
improved performance.

Lot of work was done to give OfflineIMAP a better code base. Raised errors can
now rely on a new error system and should become the default in the coming
releases.

As usual, we ask our users to test this release as much as possible, especially
the SQL backend. Have fun!

#### New Features

* Begin sphinx-based documentation for the code.
* Enable 1-way synchronization by settting a [Repository ...] to
  readonly = True. When e.g. using offlineimap for backup purposes you
  can thus make sure that no changes in your backup trickle back into
  the main IMAP server.
* Optional: experimental SQLite-based backend for the LocalStatus
  cache. Plain text remains the default.

#### Changes

* Start a enhanced error handling background system. This is designed to not
  stop a whole sync process on all errors (not much used, yet).
* Documentation improvements: the FAQ wins new entries and add a new HACKING
  file for developers.
* Lot of code cleanups.
* Reduced our sync logic from 4 passes to 3 passes (integrating upload of
  "new" and "existing" messages into one function). This should result in a
  slight speedup.
* No whitespace is stripped from comma-separated arguments passed via
  the -f option.
* Give more detailed error when encountering a corrupt UID mapping file.

#### Bug Fixes

* Drop connection if synchronization failed. This is needed if resuming the
  system from suspend mode gives a wrong connection.
* Fix the offlineimap crash when invoking debug option 'thread'.
* Make 'thread' command line option work.


### OfflineIMAP v6.3.3 (2011-04-24)

#### Notes

Make this last candidate cycle short. It looks like we don't need more tests as
most issues were raised and solved in the second round. Also, we have huge work
to merge big and expected features into OfflineIMAP.

Thanks to all contributors, again. With such a contribution rate, we can release
stable faster. I hope it will be confirmed in the longer run!

#### Changes

* Improved documentation for querying password.


### OfflineIMAP v6.3.3-rc3 (2011-04-19)

#### Notes

It's more than a week since the previous release. Most of the issues raised were
discussed and fixed since last release. I think we can be glad and confident for
the future while the project live his merry life.

#### Changes

* The -f option did not work with Folder names with spaces. It works
  now, use with quoting e.g. -f "INBOX, Deleted Mails".
* Improved documentation.
* Bump from imaplib2 v2.20 to v2.22.
* Code refactoring.

#### Bug Fixes

* Fix IMAP4 tunnel with imaplib2.


### OfflineIMAP v6.3.3-rc2 (2011-04-07)

#### Notes

We are now at the third week of the -rc1 cycle. I think it's welcome to begin
the -rc2 cycle.  Things are highly calming down in the code even if we had
much more feedbacks than usual. Keep going your effort!

I'd like to thank reporters who involved in this cycle:
  - Баталов Григорий
  - Alexander Skwar
  - Christoph Höger
  - dtk
  - Greg Grossmeier
  - h2oz7v
  - Iain Dalton
  - Pan Tsu
  - Vincent Beffara
  - Will Styler

(my apologies if I forget somebody) ...and all active developers, of course!

The imaplib2 migration looks to go the right way to be definetly released but
still needs more tests.  So, here we go...

#### Changes

* Increase compatability with Gmail servers which claim to not support
  the UIDPLUS extension but in reality do.

#### Bug Fixes

* Fix hang when using Ctrl+C in some cases.


### OfflineIMAP v6.3.3-rc1 (2011-03-16)

#### Notes

Here is time to begin the tests cycle. If feature topics are sent, I may merge
or delay them until the next stable release.

Main change comes from the migration from imaplib to imaplib2. It's internal
code changes and doesn't impact users. UIDPLUS and subjectAltName for SSL are
also great improvements.

This release includes a hang fix due to infinite loop. Users seeing OfflineIMAP
hang and consuming a lot of CPU are asked to update.

That beeing said, this is still an early release candidate you should use for
non-critical data only!

#### New Features

* Implement UIDPLUS extension support. OfflineIMAP will now not insert
  an X-OfflineIMAP header if the mail server supports the UIDPLUS
  extension.
* SSL: support subjectAltName.

#### Changes

* Use imaplib2 instead of imaplib.
* Makefile use magic to find the version number.
* Rework the repository module
* Change UI names to Blinkenlights,TTYUI,Basic,Quiet,MachineUI.
  Old names will still work, but are deprecated.
  Document that we don't accept a list of UIs anymore.
* Reworked the syncing strategy. The only user-visible change is that
  blowing away LocalStatus will not require you to redownload ALL of
  your mails if you still have the local Maildir. It will simply
  recreate LocalStatus.
* TTYUI ouput improved.
* Code cleanups.

#### Bug Fixes

* Fix ignoring output while determining the rst2xxx command name to build
  documentation.
* Fix hang because of infinite loop reading EOF.
* Allow SSL connections to send keep-alive messages.
* Fix regression (UIBase is no more).
* Make profiling mode really enforce single-threading
* Do not send localized date strings to the IMAP server as it will
  either ignore or refuse them.


### OfflineIMAP v6.3.2 (2010-02-21)

#### Notes

First of all I'm really happy to announce our new official `website
<http://offlineimap.org>`_. Most of the work started from the impulse
of Philippe LeCavalier with the help of Sebastian Spaeth and other
contributors. Thanks to everybody.

In this release, we are still touched by the "SSL3 write pending" but I think
time was long enough to try to fix it. We have our first entry in the "KNOWN
BUG" section of the manual about that. I'm afraid it could impact a lot of users
if some distribution package any SSL library not having underlying (still
obscure) requirements. Distribution maintainers should be care of it. I hope
this release will help us to have more reports.

This release will also be the root of our long maintenance support.

Other bugs were fixed.

#### Bug Fixes

* Fix craches for getglobalui().
* Fix documentation build.
* Restore compatibiliy with python 2.5.


### OfflineIMAP v6.3.2-rc3 (2010-02-06)

#### Notes

We are still touched by the "SSL3 write pending" bug it would be really nice to
fix before releasing the coming stable. In the worse case, we'll have to add the
first entry in the "KNOWN BUG" section of the manual. I'm afraid it could impact
a lot of users if some distribution package any SSL library not having
underlying (still obscure) requirements.

The best news with this release are the Curse UI fixed and the better reports
on errors.

In this release I won't merge any patch not fixing a bug or a security issue.

More feedbacks on the main issue would be appreciated.

#### Changes

* Sample offlineimap.conf states it expects a PEM formatted certificat.
* Give better trace information if an error occurs.
* Have --version ONLY print the version number.
* Code cleanups.

#### Bug Fixes

* Fix Curses UI (simplified by moving from MultiLock to Rlock implementation).
* Makefile: docutils build work whether python extension command is stripped or not.
* Makefile: clean now removes HTML documentation files.


### OfflineIMAP v6.3.2-rc2 (2010-12-21)

#### Notes

We are beginning a new tests cycle. At this stage, I expect most people will try
to intensively stuck OfflineIMAP. :-)

#### New Features

* Makefile learn to build the package and make it the default.
* Introduce a Changelog to involve community in the releasing process.
* Migrate documentation to restructuredtext.

#### Changes

* Improve CustomConfig documentation.
* Imply single threading mode in debug mode exept for "-d thread".
* Code and import cleanups.
* Allow UI to have arbitrary names.
* Code refactoring around UI and UIBase.
* Improve version managment and make it easier.
* Introduce a true single threading mode.

#### Bug Fixes

* Understand multiple EXISTS replies from servers like Zimbra.
* Only verify hostname if we actually use CA cert.
* Fix ssl ca-cert in the sample configuration file.
* Fix 'Ctrl+C' interruptions in threads.
* Fix makefile clean for files having whitespaces.
* Fix makefile to not remove unrelated files.
* Fixes in README.
* Remove uneeded files.


### OfflineIMAP v6.3.2-rc1 (2010-12-19)

#### Notes

We are beginning a tests cycle. If feature topics are sent, I may merge or
delay them until the next stable release.

#### New Features

* Primitive implementation of SSL certificates check.

#### Changes

* Use OptionParser instead of getopts.
* Code cleanups.

#### Bug Fixes

* Fix reading password from UI.


### OfflineIMAP v6.3.1 (2010-12-11)

#### Notes

Yes, I know I've just annouced the v6.3.0 in the same week. As said, it
was not really a true release for the software. This last release
includes fixes and improvements it might be nice to update to.

Thanks to every body who helped to make this release with patches and
tips through the mailing list. This is clearly a release they own.

#### Changes

* cProfile becomes the default profiler. Sebastian Spaeth did refactoring to
  prepare to the coming unit test suites.
* UI output formating enhanced.
* Some code cleanups.

#### Bug Fixes

* Fix possible overflow while working with Exchange.
* Fix time sleep while exiting threads.


### OfflineIMAP v6.3.0 (2010-12-09)

#### Notes

This release is more "administrative" than anything else and mainly marks the
change of the maintainer. New workflow and policy for developers come in.  BTW,
I don't think I'll maintain debian/changelog. At least, not in the debian way.

Most users and maintainers may rather want to skip this release.

#### Bug Fixes

* Fix terminal display on exit.
* netrc password authentication.
* User name querying from netrc.


================================================
FILE: MAINTAINERS.rst
================================================
.. -*- coding: utf-8 -*-

Contacts
========

- Abdó Roig-Maranges
  - email: abdo.roig at gmail.com
  - github: aroig

- Ben Boeckel
  - email: mathstuf at gmail.com
  - github: mathstuf

- benutzer193
  - email: registerbn at gmail.com
  - github: benutzer193

- Chris Coleman
  - email: chris at espacenetworks.com
  - github: chris001

- Darshit Shah
  - email: darnir at gmail.com
  - github: darnir

- Eygene Ryabinkin
  - email: rea at freebsd.org
  - github: konvpalto
  - other: FreeBSD maintainer

- Igor Almeida
  - email: igor.contato at gmail.com
  - github: igoralmeida

- Ilias Tsitsimpis
  - email: i.tsitsimpis at gmail.com
  - github: iliastsi
  - other: Debian maintainer

- "J"
  - email: offlineimap at 927589452.de
  - github: 927589452
  - other: FreeBSD user

- Łukasz Żarnowiecki
  - email: dolohow at outlook.com
  - github: dolohow

- Nicolas Sebrecht
  - email: nicolas.s-dev at laposte.net
  - github: nicolas33
  - system: Linux

- Remi Locherer
  - email: remi.locherer at relo.ch
  - system: OpenBSD maintainer

- Sebastian Spaeth
  - email: sebastian at sspaeth.de
  - github: spaetz
  - other: left the project but still responding


Testers
=======

- Abdó Roig-Maranges
- Ben Boeckel
- Chris Coleman
- Darshit Shah
- Eygene Ryabinkin
- Igor Almeida
- Ilias Tsitsimpis
- "J"
- Łukasz Żarnowiecki
- Nicolas Sebrecht
- Remi Locherer


Maintainers
===========

- Eygene Ryabinkin
- Sebastian Spaeth
- Nicolas Sebrecht
- Chris Coleman


Github
------

- Eygene Ryabinkin
- Sebastian Spaeth
- Nicolas Sebrecht


Mailing List
------------

- Eygene Ryabinkin
- Sebastian Spaeth
- Nicolas Sebrecht


Twitter
-------

- Nicolas Sebrecht


Pypi
----

- Nicolas Sebrecht
- Sebastian Spaeth


================================================
FILE: MANIFEST.in
================================================
global-exclude .gitignore .git *.bak *.orig *.rej
include setup.py
include COPYING
include Changelog*
include MAINTAINERS
include MANIFEST.in
include Makefile
include README.md
include offlineimap.conf*
include offlineimap.py
recursive-include contrib *
recursive-include offlineimap *.py
recursive-include bin *
recursive-include docs *
recursive-include test *
prune docs/rfcs


================================================
FILE: Makefile
================================================
# Copyright (C) 2002 - 2018 John Goerzen & contributors.
#
#    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 St, Fifth Floor, Boston, MA  02110-1301 USA

# Warning: VERSION, ABBREV and TARGZ are used in docs/build-uploads.sh.
VERSION=$(shell ./offlineimap.py --version)
ABBREV=$(shell git log --format='%h' HEAD~1..)
TARGZ=offlineimap-v$(VERSION)-$(ABBREV)
SHELL=/bin/bash
RST2HTML=`type rst2html >/dev/null 2>&1 && echo rst2html || echo rst2html.py`

all: build

build:
	python setup.py build
	@echo
	@echo "Build process finished, run 'python setup.py install' to install" \
		"or 'python setup.py --help' for more information".

clean:
	-python setup.py clean --all
	-rm -f bin/offlineimapc 2>/dev/null
	-find . -name '*.pyc' -exec rm -f {} \;
	-find . -name '*.pygc' -exec rm -f {} \;
	-find . -name '*.class' -exec rm -f {} \;
	-find . -name '.cache*' -exec rm -f {} \;
	-find . -type d -name '__pycache__' -exec rm -rf {} \;
	-rm -f manpage.links manpage.refs 2>/dev/null
	-find . -name auth -exec rm -vf {}/password {}/username \;
	-$(MAKE) -C docs clean

.PHONY: docs
docs:
	@$(MAKE) -C docs

websitedoc:
	@$(MAKE) -C websitedoc

targz: ../$(TARGZ)
../$(TARGZ):
	cd .. && tar -zhcv --transform s,^offlineimap,offlineimap-v$(VERSION), -f $(TARGZ).tar.gz --exclude '.*.swp' --exclude '.*.swo' --exclude '*.pyc' --exclude '__pycache__' offlineimap/{bin,Changelog.md,Changelog.maint.md,contrib,CONTRIBUTING.rst,COPYING,docs,MAINTAINERS.rst,Makefile,MANIFEST.in,offlineimap,offlineimap.conf,offlineimap.conf.minimal,offlineimap.py,README.md,requirements.txt,scripts,setup.cfg,setup.py,snapcraft.yaml,test,tests,TODO.rst}

rpm: targz
	cd .. && sudo rpmbuild -ta $(TARGZ)


================================================
FILE: README.md
================================================
Upstream status (`master` branch):
[![OfflineIMAP build status on Travis-CI.org](https://travis-ci.org/OfflineIMAP/offlineimap.svg?branch=master)](https://travis-ci.org/OfflineIMAP/offlineimap)
[![OfflineIMAP code coverage on Codecov.io](https://codecov.io/gh/OfflineIMAP/offlineimap/branch/master/graph/badge.svg)](https://codecov.io/gh/OfflineIMAP/offlineimap)
[![Gitter chat](https://badges.gitter.im/OfflineIMAP/offlineimap.png)](https://gitter.im/OfflineIMAP/offlineimap)

Upstream status (`next` branch):
[![OfflineIMAP build status on Travis-CI.org](https://travis-ci.org/OfflineIMAP/offlineimap.svg?branch=next)](https://travis-ci.org/OfflineIMAP/offlineimap)

[offlineimap]: http://github.com/OfflineIMAP/offlineimap
[website]: http://www.offlineimap.org
[wiki]: http://github.com/OfflineIMAP/offlineimap/wiki
[blog]: http://www.offlineimap.org/posts.html

Links:
* Official github code repository: [offlineimap]
* Website: [website]
* Wiki: [wiki]
* Blog: [blog]

# OfflineIMAP

***"Get the emails where you need them."***

[Official offlineimap][offlineimap].


## Description

OfflineIMAP is software that downloads your email mailbox(es) as **local
Maildirs**. OfflineIMAP will synchronize both sides via *IMAP*.

## Why should I use OfflineIMAP?

IMAP's main downside is that you have to **trust** your email provider to
not lose your email. While certainly unlikely, it's not impossible.
With OfflineIMAP, you can download your Mailboxes and make you own backups of
your [Maildir](https://en.wikipedia.org/wiki/Maildir).

This allows reading your email offline without the need for your mail
reader (MUA) to support IMAP operations. Need an attachment from a
message without internet connection? No problem, the message is still there.


## Project status and future

> As one of the maintainer of OfflineIMAP, I'd like to put my efforts into
> [imapfw](http://github.com/OfflineIMAP/imapfw). **imapfw** is software in
> development that I intend to replace OfflineIMAP with in the long term.
>
> That's why I'm not going to continue OfflineIMAP development. I'll continue
> to maintain OfflineIMAP (fixing small bugs, reviewing patches and merging,
> and rolling out new releases), but that's all.
>
> While I keep tracking issues for OfflineIMAP, you should not expect future support.
>
> You won't be left at the side. OfflineIMAP's community is large enough so that
> you'll find people for most of your issues.
>
> Get news from the [blog][blog].
>
>                                  Nicolas Sebrecht. ,-)


## License

GNU General Public License v2.


## Downloads

You should first check if your distribution already packages OfflineIMAP for you.
Downloads releases as [tarball or zipball](https://github.com/OfflineIMAP/offlineimap/tags).

If you are running Linux Os, you can install offlineimap with:

-  openSUSE `zypper in offlineimap`
-  archLinux `pacman -S offlineimap`
-  fedora `dnf install offlineimap`

## Feedbacks and contributions

**The user discussions, development, announcements and all the exciting stuff take
place on the mailing list.** While not mandatory to send emails, you can
[subscribe here](http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project).

Bugs, issues and contributions can be requested to both the mailing list or the
[official Github project][offlineimap].  Provide the following information:
- system/distribution (with version)
- offlineimap version (`offlineimap -V`)
- Python version
- server name or domain
- CLI options
- Configuration file (offlineimaprc)
- pythonfile (if any)
- Logs, error
- Steps to reproduce the error


## The community

* OfflineIMAP's main site is the [project page at Github][offlineimap].
* There is the [OfflineIMAP community's website][website].
* And finally, [the wiki][wiki].


## Requirements & dependencies

* Python v2.7+
* six (required)
* rfc6555 (required)
* imaplib2 >= 2.57 (optional)
* gssapi (optional), for Kerberos authentication
* portalocker (optional), if you need to run offlineimap in Cygwin for Windows

* Python v3.4+ ***[STALLED] (experimental: [see known issues](https://github.com/OfflineIMAP/offlineimap/issues?q=is%3Aissue+is%3Aopen+label%3APy3))***

## Documentation

All current and updated documentation is on the [community's website][website].


### Read documentation locally

You might want to read the documentation locally. Get the sources of the website.
For the other documentation, run the appropriate make target:

```sh
$ ./scripts/get-repository.sh website
$ cd docs
$ make html  # Requires rst2html
$ make man   # Requires a2x (http://asciidoc.org)
$ make api   # Requires sphinx
```


================================================
FILE: TODO.rst
================================================
.. vim: spelllang=en ts=2 expandtab :

.. _coding style: https://github.com/OfflineIMAP/offlineimap/blob/next/docs/CodingGuidelines.rst

============================
TODO list by relevance order
============================

Should be the starting point to improve the `coding style`_.

Write your WIP directly in this file.

TODO list
---------

* Better names for variables, objects, etc.


* Improve comments.

  Most of the current comments assume a very good
  knowledge of the internals. That sucks because I guess nobody is
  anymore aware of ALL of them. Time when this was a one guy made
  project has long passed.


* Better policy on objects.

  - Turn ALL attributes private and use accessors. This is not
    "pythonic" but such pythonic thing turn the code into intricated
    code.

  - Turn ALL methods not intended to be used outside, private.


* Revamp the factorization.

  It's not unusual to find "factorized" code
  for bad reasons: because it made the code /look/ nicer, but the
  factorized function/methods is actually called from ONE place. While it
  might locally help, such practice globally defeat the purpose because
  we lose the view of what is true factorized code and what is not.


* Namespace the factorized code.

  If a method require a local function, DON'T USE yet another method. Use a
  local namespaced function.::

    class BLah(object):
        def _internal_method(self, arg):
            def local_factorized(local_arg):
                # local_factorized's code
            # _internal_method's code.

  Python allows local namespaced functions for good reasons.


* Better inheritance policy.

  Take the sample of the folder/LocalStatus(SQlite) and folder/Base stuffs. It's
  *nearly IMPOSSIBLE* to know and understand what parent method is used by what
  child, for what purpose, etc. So, instead of (re)defining methods in the wild,
  keep the well common NON-redefined stuff into the parent and define the
  required methods in the childs. We really don't want anything like::

    def method(self):
        raise NotImplemented

  While this is common practice in Python, think about that again: how a
  parent object should know all the expected methods/accessors of all the
  possible kind of childs?

  Inheritance is about factorizing, certainly **NOT** about **defining the
  interface** of the childs.


* Introduce as many as intermediate inherited objects as required.

  Keeping linear inheritance is good because Python sucks at playing
  with multiple parents and it keeps things simple. But a parent should
  have ALL its methods used in ALL the childs. If not, it's a good
  sign that a new intermediate object should be introduced in the
  inheritance line.

* Don't blindly inherit from library objects.

  We do want **well defined interfaces**. For example, we do too much things
  like imapobj.methodcall() while the imapobj is far inherited from imaplib2.

  We have NO clue about what we currently use from the library.
  Having a dump wrappper for each call should be made mandatory for
  objects inherited from a library. Using composed objects should be
  seriously considered in this case, instead of using inheritance.

* Use factories.

  Current objects do too much initialization stuff varying with the context it
  is used. Move things like that into factories and keep the objects definitions
  clean.


* Make it clear when we expect a composite object and what we expect
  exactly.

  Even the more obvious composed objects are badly defined. For example,
  the ``conf`` instances are spread across a lot of objects. Did you know
  that such composed objects are sometimes restricted to the section the
  object works on, and most of the time it's not restricted at all?
  How many time it requires to find and understand on what we are
  currently working?


* Seriously improve our debugging/hacking sessions (AGAIN).

  Until now, we have limited the improvements to allow better/full stack traces.
  While this was actually required, we now hit some limitations of the whole
  exception-based paradigm. For example, it's very HARD to follow an instance
  during its life time. I have a good overview of what we could do in this area,
  so don't matter much about that if you don't get the point or what could be
  done.

* Support Unicode.


================================================
FILE: bin/offlineimap
================================================
#!/usr/bin/env python2
# Startup from system-wide installation
# Copyright (C) 2002-2018 John Goerzen & contributors
#
#    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 St, Fifth Floor, Boston, MA  02110-1301 USA

from offlineimap import OfflineImap

oi = OfflineImap()
oi.run()


================================================
FILE: contrib/README.md
================================================

README
======

**This "./contrib" directory is where users share their own scripts and tools.**

Everything here is submitted and maintained *by the users for the users*. You're
welcome to add your own stuff. There is no barrier on your contributions here.
We think it's expected to find contributions of various quality.


================================================
FILE: contrib/helpers.py
================================================
"""

Put into Public Domain, by Nicolas Sebrecht.

Helpers for maintenance scripts.

"""

from os import chdir, makedirs, system, getcwd
from os.path import expanduser
import shlex
from subprocess import check_output, check_call, CalledProcessError

import yaml


FS_ENCODING = 'UTF-8'
ENCODING = 'UTF-8'

MAILING_LIST = 'offlineimap-project@lists.alioth.debian.org'
CACHEDIR = '.git/offlineimap-release'
EDITOR = 'vim'
MAILALIASES_FILE = expanduser('~/.mutt/mail_aliases')
TESTERS_FILE = "{}/testers.yml".format(CACHEDIR)
ME = "Nicolas Sebrecht <nicolas.s-dev@laposte.net>"


def run(cmd):
    return check_output(cmd, timeout=5).rstrip()

def goTo(path):
    try:
        chdir(path)
        return True
    except FileNotFoundError:
        print("Could not find the '{}' directory in '{}'...".format(
            path, getcwd())
        )
    return False


class Author(object):
    def __init__(self, name, count, email):
        self.name = name
        self.count = count
        self.email = email

    def getName(self):
        return self.name

    def getCount(self):
        return self.count

    def getEmail(self):
        return self.email


class Git(object):
    @staticmethod
    def getShortlog(ref):
        shortlog = ""

        cmd = shlex.split("git shortlog --no-merges -n v{}..".format(ref))
        output = run(cmd).decode(ENCODING)

        for line in output.split("\n"):
            if len(line) > 0:
                if line[0] != " ":
                    line = "  {}\n".format(line)
                else:
                    line = "     {}\n".format(line.lstrip())
            else:
                line = "\n"

            shortlog += line

        return shortlog

    @staticmethod
    def add(files):
        cmd = shlex.split("git add -- {}".format(files))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def commit(msg):
        cmd = shlex.split("git commit -s -m '{}'".format(msg))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def tag(version):
        cmd = shlex.split("git tag -a 'v{}' -m 'v{}'".format(version, version))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def stash(msg):
        cmd = shlex.split("git stash create '{}'".format(msg))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def mergeFF(ref):
        cmd = shlex.split("git merge --ff '{}'".format(ref))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def getDiffstat(ref):
        cmd = shlex.split("git diff --stat v{}..".format(ref))
        return run(cmd).decode(ENCODING)

    @staticmethod
    def isClean():
        try:
            check_call(shlex.split("git diff --quiet"))
            check_call(shlex.split("git diff --cached --quiet"))
        except CalledProcessError:
            return False
        return True

    @staticmethod
    def buildMessageId():
        cmd = shlex.split(
            "git log HEAD~1.. --oneline --pretty='%H.%t.upcoming.%ce'")
        return run(cmd).decode(ENCODING)

    @staticmethod
    def resetKeep(ref):
        return run(shlex.split("git reset --keep {}".format(ref)))

    @staticmethod
    def getRef(ref):
        return run(shlex.split("git rev-parse {}".format(ref))).rstrip()

    @staticmethod
    def rmTag(tag):
        return run(shlex.split("git tag -d {}".format(tag)))

    @staticmethod
    def checkout(ref, create=False):
        if create:
            create = "-b"
        else:
            create = ""

        cmd = shlex.split("git checkout {} {}".format(create, ref))
        run(cmd)
        head = shlex.split("git rev-parse HEAD")
        revparseRef = shlex.split("git rev-parse {}".format(ref))
        if run(head) != run(revparseRef):
            raise Exception("checkout to '{}' did not work".format(ref))

    @staticmethod
    def makeCacheDir():
        try:
            makedirs(CACHEDIR)
        except FileExistsError:
            pass

    @staticmethod
    def getLocalUser():
        cmd = shlex.split("git config --get user.name")
        name = run(cmd).decode(ENCODING)
        cmd = shlex.split("git config --get user.email")
        email = run(cmd).decode(ENCODING)
        return name, email

    @staticmethod
    def buildDate():
        cmd = shlex.split("git log HEAD~1.. --oneline --pretty='%cD'")
        return run(cmd).decode(ENCODING)

    @staticmethod
    def getAuthorsList(sinceRef):
        authors = []

        cmd = shlex.split("git shortlog --no-merges -sne v{}..".format(sinceRef))
        output = run(cmd).decode(ENCODING)

        for line in output.split("\n"):
            count, full = line.strip().split("\t")
            full = full.split(' ')
            name = ' '.join(full[:-1])
            email = full[-1]

            authors.append(Author(name, count, email))

        return authors

    @staticmethod
    def getCommitsList(sinceRef):
        cmd = shlex.split(
            "git log --no-merges --format='- %h %s. [%aN]' v{}..".format(sinceRef)
        )
        return run(cmd).decode(ENCODING)

    @staticmethod
    def chdirToRepositoryTopLevel():
        cmd = shlex.split("git rev-parse --show-toplevel")
        topLevel = run(cmd)

        chdir(topLevel)


class OfflineimapInfo(object):
    def getVersion(self):
        cmd = shlex.split("./offlineimap.py --version")
        return run(cmd).rstrip().decode(FS_ENCODING)

    def editInit(self):
        return system("{} ./offlineimap/__init__.py".format(EDITOR))



class User(object):
    """Interact with the user."""

    @staticmethod
    def request(msg, prompt='--> '):
        print(msg)
        return input(prompt)

    @staticmethod
    def pause(msg=False):
        return User.request(msg, prompt="Press Enter to continue..")

    @staticmethod
    def yesNo(msg, defaultToYes=False, prompt='--> '):
        endMsg = " [y/N]: No"
        if defaultToYes:
            endMsg = " [Y/n]: Yes"
        msg += endMsg
        answer = User.request(msg, prompt).lower()
        if answer in ['y', 'yes']:
            return True
        if defaultToYes is not False and answer not in ['n', 'no']:
            return True
        return False


class Tester(object):
    def __init__(self, name, email, feedback):
        self.name = name
        self.email = email
        self.feedback = feedback

    def __str__(self):
        return "{} {}".format(self.name, self.email)

    def getName(self):
        return self.name

    def getEmail(self):
        return self.email

    def getFeedback(self):
        return self.feedback

    def positiveFeedback(self):
        return self.feedback is True

    def setFeedback(self, feedback):
        assert feedback in [True, False, None]
        self.feedback = feedback

    def switchFeedback(self):
        self.feedback = not self.feedback


class Testers(object):
    def __init__(self):
        self.testers = None
        self._read()

    def _read(self):
        self.testers = []
        with open(TESTERS_FILE, 'r') as fd:
            testers = yaml.load(fd)
            for tester in testers:
                name = tester['name']
                email = tester['email']
                feedback = tester['feedback']
                self.testers.append(Tester(name, email, feedback))
        self.testers.sort(key=lambda x: x.getName().lower())

    @staticmethod
    def listTestersInTeam():
        """Returns a list of emails extracted from my mailaliases file."""

        cmd = shlex.split("grep offlineimap-testers {}".format(MAILALIASES_FILE))
        output = run(cmd).decode(ENCODING)
        emails = output.lstrip("alias offlineimap-testers ").split(', ')
        return emails

    def add(self, name, email, feedback=None):
        self.testers.append(Tester(name, email, feedback))

    def remove(self, tester):
        self.testers.remove(tester)

    def get(self):
        return self.testers

    def getList(self):
        testersList = ""
        for tester in self.testers:
            testersList += "- {}\n".format(tester.getName())
        return testersList

    def getListOk(self):
        testersOk = []
        for tester in self.testers:
            if tester.positiveFeedback():
                testersOk.append(tester)
        return testersOk

    def reset(self):
        for tester in self.testers:
            tester.setFeedback(None)

    def write(self):
        testers = []
        for tester in self.testers:
            testers.append({
                'name': tester.getName(),
                'email': tester.getEmail(),
                'feedback': tester.getFeedback(),
            })
        with open(TESTERS_FILE, 'w') as fd:
            fd.write(yaml.dump(testers))



================================================
FILE: contrib/internet-urllib3.py
================================================
#!/usr/bin/env python

import urllib3
import certifi

def isInternetConnected(url="www.ietf.org"):
  result = False
  http = urllib3.PoolManager(
    cert_reqs='CERT_REQUIRED', # Force certificate check.
    ca_certs=certifi.where(),  # Path to the Certifi bundle.
  )
  try:
    r = http.request('HEAD', 'https://' + url)
    result = True
  except Exception as e:  # urllib3.exceptions.SSLError
    result = False
  return result

print isInternetConnected()


================================================
FILE: contrib/release.py
================================================
#!/usr/bin/python3

"""

Put into Public Domain, by Nicolas Sebrecht.

Make a new release.

"""

#TODO: announce: cc list on announce includes all testers
#TODO: announce: remove empty sections
#TODO: websitedoc up
#TODO: website branch not including all changes!


from os import system, path, rename
from datetime import datetime
from subprocess import check_call
import shlex
import time
from email import utils

from helpers import (
    MAILING_LIST, CACHEDIR, EDITOR, Git, OfflineimapInfo, Testers, User, run, goTo
)


__VERSION__ = "0.2"

SPHINXBUILD = 'sphinx-build'
DOCSDIR = 'docs'
CHANGELOG_MAGIC = '{:toc}'
WEBSITE_LATEST = "website/_data/latest.yml"

CHANGELOG_EXCERPT = "{}/changelog.excerpt.md".format(CACHEDIR)
CHANGELOG_EXCERPT_OLD = "{}.old".format(CHANGELOG_EXCERPT)
CHANGELOG = "Changelog.md"
ANNOUNCE_FILE = "{}/announce.txt".format(CACHEDIR)

WEBSITE_LATEST_SKEL = """# DO NOT EDIT MANUALLY: it is generated by the release script.
stable: v{stable}
"""

CHANGELOG_SKEL = """
### OfflineIMAP v{version} ({date})

#### Notes


This release was tested by:

{testersList}

#### Authors

{authorsList}

#### Features


#### Fixes


#### Changes



{commitsList}

"""

END_MESSAGE = """
Release is ready!
Make your checks and push the changes for both offlineimap and the website.
Announce template stands in '{announce}'.
Command samples to do manually:

- git push <remote> master next {new_version}
- python setup.py sdist && twine upload dist/* && rm -rf dist MANIFEST
- cd website
- git checkout master
- git merge {website_branch}
- git push <remote> master
- cd ..
- git send-email {announce}

...and write a Twitter message.
Have fun! ,-)
"""


class State(object):
    def __init__(self):
        self.master = None
        self.next = None
        self.website = None
        self.tag = None

    def setTag(self, tag):
        self.tag = tag

    def save(self):
        self.master = Git.getRef('master')
        self.next = Git.getRef('next')

    def saveWebsite(self):
        Git.chdirToRepositoryTopLevel()
        goTo('website')
        self.website = Git.getRef('master')
        goTo('..')

    def restore(self):
        Git.chdirToRepositoryTopLevel()
        try:
            Git.checkout('-f')
        except:
            pass
        # Git.checkout('master')
        # Git.resetKeep(self.master)
        # Git.checkout('next')
        # Git.resetKeep(self.next)

        if self.tag is not None:
            Git.rmTag(self.tag)

        if self.website is not None:
            if goTo('website'):
                Git.checkout(self.website)
                goTo('..')


class Changelog(object):
    def __init__(self):
        self.shouldUsePrevious = False

    def edit(self):
        return system("{} {}".format(EDITOR, CHANGELOG_EXCERPT))

    def update(self):
        # Insert excerpt to CHANGELOG.
        system("sed -i -e '/{}/ r {}' '{}'".format(
                CHANGELOG_MAGIC, CHANGELOG_EXCERPT, CHANGELOG
            )
        )
        # Remove trailing whitespaces.
        system("sed -i -r -e 's, +$,,' '{}'".format(CHANGELOG))

    def savePrevious(self):
        rename(CHANGELOG_EXCERPT, CHANGELOG_EXCERPT_OLD)

    def isPrevious(self):
        if path.isfile(CHANGELOG_EXCERPT_OLD):
            return True
        return False

    def showPrevious(self):
        output = run(shlex.split("cat '{}'".format(CHANGELOG_EXCERPT_OLD)))
        for line in output.splitlines():
            print(line.decode('utf-8')) # Weird to have to decode bytes here.

    def usePrevious(self):
        rename(CHANGELOG_EXCERPT_OLD, CHANGELOG_EXCERPT)
        self.shouldUsePrevious = True

    def usingPrevious(self):
        return self.shouldUsePrevious

    def writeExcerpt(self, version, date,
            testersList, authorsList, commitsList):

        with open(CHANGELOG_EXCERPT, 'w+') as fd:
            fd.write(CHANGELOG_SKEL.format(
                version=version,
                date=date,
                testersList=testersList,
                authorsList=authorsList,
                commitsList=commitsList,
            ))

    def getSectionsContent(self):
        dict_Content = {}

        with open(CHANGELOG_EXCERPT, 'r') as fd:
            currentSection = None
            for line in fd:
                line = line.rstrip()
                if line == "#### Notes":
                    currentSection = 'Notes'
                    dict_Content['Notes'] = ""
                    continue # Don't keep this title.
                elif line == "#### Authors":
                    currentSection = 'Authors'
                    dict_Content['Authors'] = ""
                    continue # Don't keep this title.
                elif line == "#### Features":
                    currentSection = 'Features'
                    dict_Content['Features'] = ""
                    continue # Don't keep this title.
                elif line == "#### Fixes":
                    currentSection = 'Fixes'
                    dict_Content['Fixes'] = ""
                    continue # Don't keep this title.
                elif line == "#### Changes":
                    currentSection = 'Changes'
                    dict_Content['Changes'] = ""
                    continue # Don't keep this title.
                elif line == "-- ":
                    break # Stop extraction.

                if currentSection is not None:
                    dict_Content[currentSection] += "{}\n".format(line)

        #TODO: cleanup empty sections.
        return dict_Content


class Announce(object):
    def __init__(self, version):
        self.fd = open(ANNOUNCE_FILE, 'w')
        self.version = version

    def setHeaders(self, messageId, date):
        self.fd.write("Message-Id: {}\n".format(messageId))
        self.fd.write("Date: {}\n".format(date))
        self.fd.write("From: Nicolas Sebrecht <nicolas.s-dev@laposte.net>\n")
        self.fd.write("To: {}\n".format(MAILING_LIST))
        self.fd.write(
            "Subject: [ANNOUNCE] OfflineIMAP v{} released\n".format(self.version))
        self.fd.write("\n")

        self.fd.write("""
OfflineIMAP v{version} is out.

Downloads:
  http://github.com/OfflineIMAP/offlineimap/archive/v{version}.tar.gz
  http://github.com/OfflineIMAP/offlineimap/archive/v{version}.zip

Pip:
  wget "https://raw.githubusercontent.com/OfflineIMAP/offlineimap/v{version}/requirements.txt" -O requirements.txt
  pip install -r ./requirements.txt --user git+https://github.com/OfflineIMAP/offlineimap.git@v{version}

""".format(version=self.version)
        )

    def setContent(self, dict_Content):
        self.fd.write("\n")
        for section in ['Notes', 'Authors', 'Features', 'Fixes', 'Changes']:
            if section in dict_Content:
                if section != "Notes":
                    self.fd.write("# {}\n".format(section))
                self.fd.write(dict_Content[section])
                self.fd.write("\n")
        # Signature.
        self.fd.write("-- \n")
        self.fd.write("Nicolas Sebrecht\n")

    def close(self):
        self.fd.close()


class Website(object):
    def updateUploads(self):
        req = ("add new archive to uploads/ on the website? "
            "(warning: checksums will change if it already exists)")
        if User.yesNo(req, defaultToYes=True) is False:
            return False
        if check_call(shlex.split("./docs/build-uploads.sh")) != 0:
            return exit(5)
        return True

    def updateAPI(self):
        req = "update API of the website? (requires {})".format(SPHINXBUILD)
        if User.yesNo(req, defaultToYes=True) is False:
            return False

        try:
            if check_call(shlex.split("{} --version".format(SPHINXBUILD))) != 0:
                raise RuntimeError("{} not found".format(SPHINXBUILD))
        except:
            print("""
Oops! you don't have {} installed?"
Cannot update the webite documentation..."
You should install it and manually run:"
  $ cd {}"
  $ make websitedoc"
Then, commit and push changes of the website.""".format(SPHINXBUILD, DOCSDIR))
            User.pause()
            return False

        Git.chdirToRepositoryTopLevel()
        if not goTo('website'):
            User.pause()
            return False
        if not Git.isClean:
            print("There is WIP in the website repository: stashing")
            Git.stash('WIP during offlineimap API import')

        goTo('..')
        return True

    def buildLatest(self, version):
        Git.chdirToRepositoryTopLevel()
        with open(WEBSITE_LATEST, 'w') as fd:
            fd.write(WEBSITE_LATEST_SKEL.format(stable=version))

    def exportDocs(self):
        if not goTo(DOCSDIR):
            User.pause()
            return

        if check_call(shlex.split("make websitedoc")) != 0:
            print("error while calling 'make websitedoc'")
            exit(3)

    def createImportBranch(self, version):
        branchName = "import-v{}".format(version)

        Git.chdirToRepositoryTopLevel()
        if not goTo("website"):
            User.pause()
            return

        Git.checkout(branchName, create=True)
        Git.add('.')
        Git.commit("update for offlineimap v{}".format(version))

        User.pause(
            "website: branch '{}' is ready for a merge in master!".format(
                branchName
            )
        )
        goTo('..')
        return branchName


class Release(object):
    def __init__(self):
        self.state = State()
        self.offlineimapInfo = OfflineimapInfo()
        self.testers = Testers()
        self.changelog = Changelog()
        self.websiteBranch = "NO_BRANCH_NAME_ERROR"


    def getVersion(self):
        return self.offlineimapInfo.getVersion()

    def prepare(self):
        if not Git.isClean():
            print("The git repository is not clean; aborting")
            exit(1)
        Git.makeCacheDir()
        Git.checkout('next')

    def requestVersion(self, currentVersion):
        User.request("going to make a new release after {}".format(currentVersion))

    def updateVersion(self):
        self.offlineimapInfo.editInit()

    def checkVersions(self, current, new):
        if new == current:
            print("version was not changed; stopping.")
            exit(1)

    def updateChangelog(self):
        if self.changelog.isPrevious():
            self.changelog.showPrevious()
            if User.yesNo("A previous Changelog excerpt was found. Use it?"):
                self.changelog.usePrevious()

        if not self.changelog.usingPrevious():
            date = datetime.now().strftime('%Y-%m-%d')
            testersList = ""
            testers = self.testers.getListOk()
            authorsList = ""
            authors = Git.getAuthorsList(currentVersion)

            for tester in testers:
                testersList += "- {}\n".format(tester.getName())
            for author in authors:
                authorsList += "- {} ({})\n".format(
                    author.getName(), author.getCount()
                )
            commitsList = Git.getCommitsList(currentVersion)
            date = datetime.now().strftime('%Y-%m-%d')
            self.changelog.writeExcerpt(
                newVersion, date, testersList, authorsList, commitsList
            )

        self.changelog.edit()
        self.changelog.update()

    def writeAnnounce(self):
        announce = Announce(newVersion)

        messageId = utils.make_msgid('release.py', 'laposte.net')
        nowtuple = datetime.now().timetuple()
        nowtimestamp = time.mktime(nowtuple)
        date = utils.formatdate(nowtimestamp)

        announce.setHeaders(messageId, date)
        announce.setContent(self.changelog.getSectionsContent())
        announce.close()

    def make(self):
        Git.add('offlineimap/__init__.py')
        Git.add('Changelog.md')
        commitMsg = "v{}\n".format(newVersion)
        for tester in self.testers.getListOk():
            commitMsg = "{}\nTested-by: {} {}".format(
                commitMsg, tester.getName(), tester.getEmail()
            )
        Git.commit(commitMsg)
        self.state.setTag(newVersion)
        Git.tag(newVersion)
        Git.checkout('master')
        Git.mergeFF('next')
        Git.checkout('next')

    def updateWebsite(self, newVersion):
        self.state.saveWebsite()
        website = Website()
        website.buildLatest(newVersion)
        res_upload = website.updateUploads()
        res_api = website.updateAPI()
        if res_api:
            res_export = website.exportDocs()
        if True in [res_upload, res_api, res_export]:
            self.websiteBranch = website.createImportBranch(newVersion)

    def getWebsiteBranch(self):
        return self.websiteBranch

    def after(self):
        for protectedRun in [self.testers.reset, self.changelog.savePrevious]:
            try:
                protectedRun()
            except Exception as e:
                print(e)

    def restore(self):
        self.state.restore()


if __name__ == '__main__':
    release = Release()
    Git.chdirToRepositoryTopLevel()

    try:
        release.prepare()
        currentVersion = release.getVersion()

        release.requestVersion(currentVersion)
        release.updateVersion()
        newVersion = release.getVersion()

        release.checkVersions(currentVersion, newVersion)
        release.updateChangelog()

        release.writeAnnounce()
        User.pause()

        release.make()
        release.updateWebsite(newVersion)
        release.after()

        websiteBranch = release.getWebsiteBranch()
        print(END_MESSAGE.format(
                announce=ANNOUNCE_FILE,
                new_version=newVersion,
                website_branch=websiteBranch)
        )
    except Exception as e:
        release.restore()
        raise


================================================
FILE: contrib/release.sh
================================================
#!/bin/sh
#
# Put into Public Domain, by Nicolas Sebrecht
#
# Create new releases in OfflineIMAP.

# TODO: https://developer.github.com/v3/repos/releases/#create-a-release
# https://developer.github.com/libraries/
# https://github.com/turnkeylinux/octohub
# https://github.com/michaelliao/githubpy (onefile)
# https://github.com/sigmavirus24/github3.py
# https://github.com/copitux/python-github3
# https://github.com/PyGithub/PyGithub
# https://github.com/micha/resty (curl)

# TODO: move configuration out and source it.
# TODO: implement rollback.

__VERSION__='v0.3'

SPHINXBUILD=sphinx-build

MAILING_LIST='offlineimap-project@lists.alioth.debian.org'
GITHUB_FILE_LINK_PREFIX='https://raw.githubusercontent.com/OfflineIMAP/offlineimap'

DOCSDIR='docs'
ANNOUNCE_MAGIC='#### Notes '
CHANGELOG_MAGIC='{:toc}'
CHANGELOG='Changelog.md'
CACHEDIR='.git/offlineimap-release'
WEBSITE='website'
WEBSITE_LATEST="${WEBSITE}/_data/latest.yml"

TMP_CHANGELOG_EXCERPT="${CACHEDIR}/changelog.excerpt.md"
TMP_CHANGELOG_EXCERPT_OLD="${TMP_CHANGELOG_EXCERPT}.old"
TMP_CHANGELOG="${CACHEDIR}/changelog.md"
TMP_ANNOUNCE="${CACHEDIR}/announce.txt"

True=0
False=1
Yes=$True
No=$False

DEBUG=$True

#
# $1: EXIT_CODE
# $2..: message
function die () {
  n=$1
  shift
  echo $*
  exit $n
}


function debug () {
  if test $DEBUG -eq $True
  then
    echo "DEBUG: $*" >&2
  fi
}



#
# $1: question
# $2: message on abort
#
function ask () {
  echo
  echo -n "--- $1 "
  read -r ans
  test "n$ans" = 'n' -o "n$ans" = 'ny' && return $Yes
  test "n$ans" = "ns" -o "n$ans" = 'nn' && return $No
  die 1 "! $2"
}



#
# $1: message
# $1: path to file
#
function edit_file () {
  ask "Press Enter to $1"
  test $? -eq $Yes && {
    $EDITOR "$2"
    reset
  }
}



function fix_pwd () {
  debug 'in fix_pwd'
  cd "$(git rev-parse --show-toplevel)" || \
    die 2 "cannot determine the root of the repository"
}


function prepare_env () {
  debug 'in prepare_env'
  mkdir "$CACHEDIR" 2>/dev/null
  test ! -d "$CACHEDIR" && die 5 "Could not make cache directory $CACHEDIR"
}


function check_dirty () {
  debug 'in check_dirty'
  git diff --quiet 2>/dev/null && git diff --quiet --cached 2>/dev/null || {
    die 4 "Commit all your changes first!"
  }
}


function welcome () {
  debug 'in welcome'
cat <<EOF
You will be prompted to answer questions.
Answer by:
- 'y'       : yes, continue (default)
- '<Enter>' : yes, continue
- 'n'       : no
- 's'       : skip (ONLY where applicable, otherwise continue)

Any other key will abort the program.
EOF
  ask 'Ready?'
}


function checkout_next () {
  debug 'in checkout_next'
  git checkout --quiet next || {
    die 6 "Could not checkout 'next' branch"
  }
}


function merge_maint () {
  debug 'in merge_maint'
  git merge --quiet -Xours maint || {
    die 7 "Could not merge 'maint' branch"
  }
}


function get_version () {
  debug 'in get_version'
  echo "v$(./offlineimap.py --version)"
}


function update_offlineimap_version () {
  debug 'in update_offlineimap_version'
  edit_file 'update the version in __init__.py' offlineimap/__init__.py
}


#
# $1: previous version
#
function get_git_history () {
  debug 'in get_git_history'
  git log --format='- %h %s. [%aN]' --no-merges  "${1}.."
}


#
# $1: previous version
#
function get_git_who () {
  debug 'in get_git_who'
  git shortlog --no-merges -sn "${1}.." | \
          sed -r -e 's, +([0-9]+)\t(.*),- \2 (\1),'
}



#
# $1: new version
# $2: shortlog
function changelog_template_part1 () {
  debug 'in changelog_template_part1'
  cat <<EOF
// vim: expandtab ts=2 syntax=markdown

// WARNING: let at least one empy line before the real content.
//
// Write a new Changelog entry.
//
// Comments MUST start at the beginning of the lile with two slashes.
// They will by be ignored by the template engine.
//
### OfflineIMAP $1 ($(date +%Y-%m-%d))

#### Notes

// Add some notes. Good notes are about what was done in this release from the
// bigger perspective.
// HINT: explain most important changes.

#### Authors

EOF
}


function changelog_template_part2 () {
  debug 'in changelog_template_part2'
  cat <<EOF

#### Features

// Use list syntax with '- '

#### Fixes

// Use list syntax with '- '

#### Changes

// Use list syntax with '- '

// The preformatted log was added below. Make use of this to fill the sections
// above.

EOF
}



#
# $1: new version
# $2: previous version
#
function update_changelog () {
  debug 'in update_changelog'

  # Write Changelog excerpt.
  if test ! -f "$TMP_CHANGELOG_EXCERPT"
  then
    changelog_template_part1 "$1" > "$TMP_CHANGELOG_EXCERPT"
    get_git_who "$2" >> "$TMP_CHANGELOG_EXCERPT"
    changelog_template_part2 >> "$TMP_CHANGELOG_EXCERPT"
    get_git_history "$2" >> "$TMP_CHANGELOG_EXCERPT"
    edit_file "the Changelog excerpt" $TMP_CHANGELOG_EXCERPT

    # Remove comments.
    grep -v '//' "$TMP_CHANGELOG_EXCERPT" > "${TMP_CHANGELOG_EXCERPT}.nocomment"
    mv -f "${TMP_CHANGELOG_EXCERPT}.nocomment" "$TMP_CHANGELOG_EXCERPT"
  fi

  # Write new Changelog.
  cat "$CHANGELOG" > "$TMP_CHANGELOG"
  debug "include excerpt $TMP_CHANGELOG_EXCERPT to $TMP_CHANGELOG"
  sed -i -e "/${CHANGELOG_MAGIC}/ r ${TMP_CHANGELOG_EXCERPT}" "$TMP_CHANGELOG"
  debug 'remove trailing whitespaces'
  sed -i -r -e 's, +$,,' "$TMP_CHANGELOG"   # Remove trailing whitespaces.
  debug "copy to $TMP_CHANGELOG -> $CHANGELOG"
  cp -f "$TMP_CHANGELOG" "$CHANGELOG"

  # Check and edit Changelog.
  ask "Next step: you'll be asked to review the diff of $CHANGELOG"
  while true
  do
    git diff -- "$CHANGELOG" | less
    ask 'edit Changelog?' $CHANGELOG
    test ! $? -eq $Yes && break
    # Asked to edit the Changelog; will loop again.
    $EDITOR "$CHANGELOG"
  done
}


#
# $1: new version
#
function git_release () {
  debug 'in git_release'
  git commit -as -m"$1"
  git tag -a "$1" -m"$1"
  git checkout master
  git merge next
  git checkout next
}



function get_last_rc () {
  git tag | grep -E '^v([0-9][\.-]){3}rc' | sort -V | tail -n1
}

function get_last_stable () {
  git tag | grep -E '^v([0-9][\.])+' | grep -v '\-rc' | sort -V | tail -n1
}

function update_website_releases_info() {
  cat > "$WEBSITE_LATEST" <<EOF
# DO NOT EDIT MANUALLY: it is generated by a script (release.sh)
stable: $(get_last_stable)
rc: $(get_last_rc)
EOF
}


#
# $1: new version
#
function update_website () {
  debug 'in update_website'

  ask "update API of the website? (require $SPHINXBUILD)"
  if test $? -eq $Yes
  then
    # Check sphinx is available.
    $SPHINXBUILD --version > /dev/null 2>&1
    if test ! $? -eq 0
    then
      echo "Oops! you don't have $SPHINXBUILD installed?"
      echo "Cannot update the webite documentation..."
      echo "You should install it and run:"
      echo "  $ cd docs"
      echo "  $ make websitedoc"
      echo "Then, commit and push changes of the website."
      ask 'continue'
      return
    fi

    # Check website sources are available.
    cd website
    if test ! $? -eq 0
    then
      echo "ERROR: cannot go to the website sources"
      ask 'continue'
      return
    fi
    # Stash any WIP in the website sources.
    git diff --quiet 2>/dev/null && git diff --quiet --cached 2>/dev/null || {
      echo "There is WIP in the website repository, stashing"
      echo "git stash create 'WIP during offlineimap API import'"
      git stash create 'WIP during offlineimap API import'
      ask 'continue'
    }

    cd .. # Back to offlineimap.git.
    update_website_releases_info
    cd "./$DOCSDIR" # Enter the docs directory in offlineimap.git.
    # Build the docs!
    make websitedoc && {
      # Commit changes in a branch.
      cd ../website # Enter the website sources.
      branch_name="import-$1"
      git checkout -b "$branch_name"
      git add '_doc/versions'
      git commit -a -s -m"update for offlineimap $1"
      echo "website: branch '$branch_name' ready for a merge in master!"
    }
    ask 'website updated locally; continue'
  fi
}


function git_username () {
  git config --get user.name
}
function git_usermail () {
  git config --get user.email
}

#
# $1: new version
#
function announce_header () {
  cat <<EOF
Message-Id: <$(git log HEAD~1.. --oneline --pretty='%H.%t.release.%ce')>
Date: $(git log HEAD~1.. --oneline --pretty='%cD')
From: $(git_username) <$(git_usermail)>
To: $MAILING_LIST
Subject: [ANNOUNCE] OfflineIMAP $1 released

OfflineIMAP $1 is out.

Downloads:
  http://github.com/OfflineIMAP/offlineimap/archive/${1}.tar.gz
  http://github.com/OfflineIMAP/offlineimap/archive/${1}.zip

Pip:
  wget "${GITHUB_FILE_LINK_PREFIX}/${1}/requirements.txt" -O requirements.txt
  pip install -r ./requirements.txt --user git+https://github.com/OfflineIMAP/offlineimap.git@${1}
EOF
}


function announce_footer () {
  cat <<EOF

-- 
$(git_username)
EOF
}


#
# $1: new version
# $2: previous version
#
function build_announce () {
  announce_header "$1" > "$TMP_ANNOUNCE"
  grep -v '^### OfflineIMAP' "$TMP_CHANGELOG_EXCERPT" | \
    grep -v '^#### Notes' >> "$TMP_ANNOUNCE"
  sed -i -r -e "s,^$ANNOUNCE_MAGIC,," "$TMP_ANNOUNCE"
  sed -i -r -e "s,^#### ,# ," "$TMP_ANNOUNCE"
  announce_footer >> "$TMP_ANNOUNCE"
}


function edit_announce () {
   edit_file 'edit announce' "$TMP_ANNOUNCE"
}



#
# run
#
function run () {
  debug 'in run'
  fix_pwd
  check_dirty
  prepare_env
  checkout_next
  clear
  welcome

  if test -f "$TMP_CHANGELOG_EXCERPT"
  then
    head "$TMP_CHANGELOG_EXCERPT"
    ask "A previous Changelog excerpt (head above) was found, use it?"
    if test ! $? -eq $Yes
    then
      mv -f "$TMP_CHANGELOG_EXCERPT" "$TMP_CHANGELOG_EXCERPT_OLD"
    fi
  fi

  previous_version="$(get_version)"
  message="Safety check: release after version:"
  ask "$message $previous_version ?"
  update_offlineimap_version
  new_version="$(get_version)"
  ask "Safety check: make a new release with version: '$new_version'" "Clear changes and restart"

  update_changelog "$new_version" "$previous_version"
  build_announce "$new_version" "$previous_version"
  edit_announce

  # Wait for the mainline and announce to be built to not include commits from
  # maint.
  merge_maint
  git_release $new_version

  # Wait for all the Changelogs to be up-to-date in next.
  update_website $new_version
}

run
cat <<EOF

Release is ready!
Make your checks and push the changes for both offlineimap and the website.
Announce template stands in '$TMP_ANNOUNCE'.
Command samples to do manually:
- git push <remote> master next $new_version
- python setup.py sdist && twine upload dist/* && rm -rf dist MANIFEST
- cd website
- git checkout master
- git merge $branch_name
- git push <remote> master
- cd ..
- git send-email $TMP_ANNOUNCE
Have fun! ,-)
EOF

# vim: expandtab ts=2 :


================================================
FILE: contrib/store-pw-with-gpg/README.md
================================================
# gpg-offlineimap

Python bindings for offlineimap to use gpg instead of storing cleartext passwords

Author: Lorenzo G.
[GitHub](https://github.com/lorenzog/gpg-offlineimap)

## Quickstart

Requirements: a working GPG set-up. Ideally with gpg-agent. Should work
out of the box on most modern Linux desktop environments.

 1. Enable IMAP in gmail (if you have two factor authentication, you
    need to create an app-specific password)

 2. Create a directory `~/Mail`

 3. In `~/Mail`, create a password file `passwords-gmail.txt`. Format:
    `account@gmail.com password`. Look at the example file in this
    directory.

 4. **ENCRYPT** the file: `gpg -e passwords-gmail.txt`. It should create
    a file `passwords-gmail.txt.gpg`. Check you can decrypt it: `gpg -d
    passwords-gmail.txt.gpg`: it will ask you for your GPG password and
    show it to you.

 5. Use the file  `offlineimaprc.sample` as a sample for your own
    `.offlineimaprc`; edit it by following the comments. Minimal items
    to configure: the `remoteuser` field and the `pythonfile` parameter
    pointing at the `offlineimap.py` file in this directory.

 6. Run it: `offlineimap`. It should ask you for your GPG passphrase to
    decrypt the password file.

 7. If all works well, delete the cleartext password file.




================================================
FILE: contrib/store-pw-with-gpg/gpg-pw.py
================================================
#!/usr/bin/python
# Originally taken from: http://stevelosh.com/blog/2012/10/the-homely-mutt/
# by Steve Losh
# Modified by Lorenzo Grespan on Jan, 2014

import re
import subprocess
from sys import argv
import logging
from os.path import expanduser
import unittest
import os
import sys

logging.basicConfig(level=logging.INFO)


DEFAULT_PASSWORDS_FILE = os.path.join(
    os.path.expanduser('~/Mail'),
    'passwords.gpg')


def get_keychain_pass(account=None, server=None):
    '''Mac OSX keychain password extraction'''
    params = {
        'security': '/usr/bin/security',
        'command': 'find-internet-password',
        'account': account,
        'server': server,
        'keychain': expanduser('~') + '/Library/Keychains/login.keychain',
    }
    command = ("%(security)s -v %(command)s"
               " -g -a %(account)s -s %(server)s %(keychain)s" % params)
    output = subprocess.check_output(
        command, shell=True, stderr=subprocess.STDOUT)
    outtext = [l for l in output.splitlines()
               if l.startswith('password: ')][0]
    return find_password(outtext)


def find_password(text):
    '''Helper method for osx password extraction'''
    # a non-capturing group
    r = re.match(r'password: (?:0x[A-F0-9]+  )?"(.*)"', text)
    if r:
        return r.group(1)
    else:
        logging.warn("Not found")
        return None


def get_gpg_pass(account, storage):
    '''GPG method'''
    command = ("gpg", "-d", storage)
    # get attention
    print '\a'  # BEL
    output = subprocess.check_output(command)
    # p = subprocess.Popen(command, stdout=subprocess.PIPE)
    # output, err = p.communicate()
    for line in output.split('\n'):
        r = re.match(r'{} ([a-zA-Z0-9]+)'.format(account), line)
        if r:
            return r.group(1)
    return None


def get_pass(account=None, server=None, passwd_file=None):
    '''Main method'''
    if not passwd_file:
        storage = DEFAULT_PASSWORDS_FILE
    else:
        storage = os.path.join(
            os.path.expanduser('~/Mail'),
            passwd_file)
    if os.path.exists('/usr/bin/security'):
        return get_keychain_pass(account, server)
    if os.path.exists(storage):
        logging.info("Using {}".format(storage))
        return get_gpg_pass(account, storage)
    else:
        logging.warn("No password file found")
        sys.exit(1)
    return None


# test with: python -m unittest <this module name>
# really basic tests.. nothing to see. move along
class Tester(unittest.TestCase):
    def testMatchSimple(self):
        text = 'password: "exampleonetimepass "'
        self.assertTrue(find_password(text))

    def testMatchComplex(self):
        text = r'password: 0x74676D62646D736B646970766C66696B0A  "anotherexamplepass\012"'
        self.assertTrue(find_password(text))


if __name__ == "__main__":
    print get_pass(argv[1], argv[2], argv[3])


================================================
FILE: contrib/store-pw-with-gpg/offlineimaprc.sample
================================================
[general]
# GPG quirks, leave unconfigured
ui = ttyui
# you can use any name as long as it matches the 'account1, 'account2' in the rest
# of the file
accounts = account1, account2
# this is where the `gpg-pw.py` file is on disk
pythonfile=~/where/is/the/file/gpg-pw.py
fsync = False

# you can call this any way you like
[Account account1]
localrepository = account1-local
remoterepository = account1-remote
# no need to touch this
status_backend = sqlite

[Account account2]
localrepository = account2-local
remoterepository = account2-remote
status_backend = sqlite

# thi sis a gmail account
[Repository account1-local]
type = Maildir
# create with maildirmake or by hand by creating cur, new, tmp
localfolders = ~/Mail/Mailboxes/account1
# standard Gmail stuff
nametrans = lambda folder: { 'drafts': '[Gmail]/Drafts',
                             'sent':   '[Gmail]/Sent mail',
                             'flagged': '[Gmail]/Starred',
                             'trash':   '[Gmail]/Trash',
                             'archive': '[Gmail]/All Mail' 
                            }.get(folder, folder)

[Repository account1-remote]
maxconnections = 1
type = Gmail
ssl=yes
# for osx, you might need to download the certs by hand
#sslcacertfile=~/Mail/certs.pem
#sslcacertfile=~/Mail/imap.gmail.com.pem
# sslcacertfile=/etc/ssl/cert.pem

# or use Linux's standard certs
sslcacertfile=/etc/ssl/certs/ca-certificates.crt
# your account
remoteuser = account1@gmail.com
remotepasseval = get_pass(account="account1@gmail.com", server="imap.gmail.com", passwd_file="passwords-gmail.txt.gpg")
realdelete = no
createfolders = no
nametrans = lambda folder: {'[Gmail]/Drafts':    'drafts',
                            '[Gmail]/Sent Mail': 'sent',
                            '[Gmail]/Starred':   'star',
                            '[Gmail]/Trash':     'trash',
                            '[Gmail]/All Mail':  'archive',
                            }.get(folder, folder)
folderfilter = lambda folder: folder not in ['[Gmail]/Trash',
                                             '[Gmail]/Spam',
                                             ]

[Repository account2-remote]
# copy the stanza above, change the 'account' parameter of get_pass, etc.


================================================
FILE: contrib/store-pw-with-gpg/passwords-gmail.txt
================================================
account1@gmail.com password1
account2@gmail.com password2


================================================
FILE: contrib/systemd/README.md
================================================
---
layout: page
title: Integrating OfflineIMAP into systemd
author: Ben Boeckel
date: 2015-03-22
contributors: Abdo Roig-Maranges, benutzer193, Hugo Osvaldo Barrera
updated: 2017-06-01
---

<!-- This file is copied to the website by script. -->


## Systemd units

These unit files are meant to be used in the user session. You may drop them
into `/etc/systemd/user` or `${XDG_DATA_HOME}/systemd/user` followed by
`systemctl --user daemon-reload` to have systemd aware of the unit files.

These files are meant to be triggered either manually using `systemctl --user
start offlineimap.service` or by enabling the timer unit using `systemctl --user
enable offlineimap-oneshot.timer`. Additionally, specific accounts may be
triggered by using `offlineimap@myaccount.timer` or
`offlineimap-oneshot@myaccount.service`.

If the defaults provided by these units doesn't suit your setup, any of the
values may be overridden by using `systemctl --user edit offlineimap.service`.
This'll prevent having to copy-and-edit the original file.


================================================
FILE: contrib/systemd/offlineimap-oneshot.service
================================================
[Unit]
Description=Offlineimap Service (oneshot)
Documentation=man:offlineimap(1)

[Service]
Type=oneshot
ExecStart=/usr/bin/offlineimap -o -u basic
# Give 120 seconds for offlineimap to gracefully stop before hard killing it:
TimeoutStopSec=120

[Install]
WantedBy=mail.target


================================================
FILE: contrib/systemd/offlineimap-oneshot.timer
================================================
[Unit]
Description=Offlineimap Query Timer

[Timer]
OnBootSec=1m
OnUnitInactiveSec=15m

[Install]
WantedBy=default.target


================================================
FILE: contrib/systemd/offlineimap-oneshot@.service
================================================
[Unit]
Description=Offlineimap Service for account %i (oneshot)
Documentation=man:offlineimap(1)

[Service]
Type=oneshot
ExecStart=/usr/bin/offlineimap -o -a %i -u basic
# Give 120 seconds for offlineimap to gracefully stop before hard killing it.
TimeoutStopSec=120

[Install]
WantedBy=default.target


================================================
FILE: contrib/systemd/offlineimap-oneshot@.timer
================================================
[Unit]
Description=Offlineimap Query Timer for account %i

[Timer]
OnBootSec=1m
OnUnitInactiveSec=15m

[Install]
WantedBy=default.target


================================================
FILE: contrib/systemd/offlineimap.service
================================================
[Unit]
Description=Offlineimap Service
Documentation=man:offlineimap(1)

[Service]
ExecStart=/usr/bin/offlineimap -u basic
Restart=on-failure
RestartSec=60

[Install]
WantedBy=default.target


================================================
FILE: contrib/systemd/offlineimap@.service
================================================
[Unit]
Description=Offlineimap Service for account %i
Documentation=man:offlineimap(1)

[Service]
ExecStart=/usr/bin/offlineimap -a %i -u basic
Restart=on-failure
RestartSec=60

[Install]
WantedBy=default.target


================================================
FILE: contrib/tested-by.py
================================================
#!/usr/bin/python3

"""

Put into Public Domain, by Nicolas Sebrecht.

Manage the feedbacks of the testers for the release notes.

"""

from os import system
import argparse

from helpers import CACHEDIR, EDITOR, Testers, User, Git


class App(object):
    def __init__(self):
        self.args = None
        self.testers = Testers()
        self.feedbacks = None


    def _getTestersByFeedback(self):
        if self.feedbacks is not None:
            return self.feedbacks

        feedbackOk = []
        feedbackNo = []

        for tester in self.testers.get():
            if tester.getFeedback() is True:
                feedbackOk.append(tester)
            else:
                feedbackNo.append(tester)

        for array in [feedbackOk, feedbackNo]:
            array.sort(key=lambda t: t.getName())

        self.feedbacks = feedbackOk + feedbackNo

    def parseArgs(self):
        parser = argparse.ArgumentParser(description='Manage the feedbacks.')

        parser.add_argument('--add', '-a', dest='add_tester',
                help='Add tester')
        parser.add_argument('--delete', '-d', dest='delete_tester',
                type=int,
                help='Delete tester NUMBER')
        parser.add_argument('--list', '-l', dest='list_all_testers',
                action='store_true',
                help='List the testers')
        parser.add_argument('--switchFeedback', '-s', dest='switch_feedback',
                action='store_true',
                help='Switch the feedback of a tester')

        self.args = parser.parse_args()

    def run(self):
        if self.args.list_all_testers is True:
            self.listTesters()
        if self.args.switch_feedback is True:
            self.switchFeedback()
        elif self.args.add_tester:
            self.addTester(self.args.add_tester)
        elif type(self.args.delete_tester) == int:
            self.deleteTester(self.args.delete_tester)

    def addTester(self, strTester):
        try:
            splitted = strTester.split('<')
            name = splitted[0].strip()
            email = "<{}".format(splitted[1]).strip()
        except Exception as e:
            print(e)
            print("expected format is: 'Firstname Lastname <email>'")
            exit(2)
        self.testers.add(name, email)
        self.testers.write()

    def deleteTester(self, number):
        self.listTesters()
        removed = self.feedbacks.pop(number)
        self.testers.remove(removed)

        print("New list:")
        self.feedbacks = None
        self.listTesters()
        print("Removed: {}".format(removed))
        ans = User.request("Save on disk? (s/Q)").lower()
        if ans in ['s']:
            self.testers.write()


    def listTesters(self):
        self._getTestersByFeedback()

        count = 0
        for tester in self.feedbacks:
            feedback = "ok"
            if tester.getFeedback() is not True:
                feedback = "no"
            print("{:02d} - {} {}: {}".format(
                    count, tester.getName(), tester.getEmail(), feedback
                )
            )
            count += 1

    def switchFeedback(self):
        self._getTestersByFeedback()
        msg = "Switch tester: [<number>/s/q]"

        self.listTesters()
        number = User.request(msg)
        while number.lower() not in ['s', 'save', 'q', 'quit']:
            if number == '':
                continue
            try:
                number = int(number)
                self.feedbacks[number].switchFeedback()
            except (ValueError, IndexError) as e:
                print(e)
                exit(1)
            finally:
                self.listTesters()
                number = User.request(msg)
        if number in ['s', 'save']:
            self.testers.write()
        self.listTesters()

    def reset(self):
        self.testers.reset()
        self.testers.write()

    #def updateMailaliases(self):

if __name__ == '__main__':
    Git.chdirToRepositoryTopLevel()

    app = App()
    app.parseArgs()
    app.run()


================================================
FILE: contrib/upcoming.py
================================================
#!/usr/bin/python3

"""

Put into Public Domain, by Nicolas Sebrecht.

Produce the "upcoming release" notes.

"""

from os import system

from helpers import (
    MAILING_LIST, CACHEDIR, EDITOR, Testers, Git, OfflineimapInfo, User
)



UPCOMING_FILE = "{}/upcoming.txt".format(CACHEDIR)
UPCOMING_HEADER = "{}/upcoming-header.txt".format(CACHEDIR)

# Header is like:
#
#Message-Id: <{messageId}>
#Date: {date}
#From: {name} <{email}>
#To: {mailinglist}
#Cc: {ccList}
#Subject: [ANNOUNCE] upcoming offlineimap v{expectedVersion}
#
## Notes
#
#I think it's time for a new release.
#
#I aim to make the new release in one week, approximately. If you'd like more
#time, please let me know. ,-)
#
#Please, send me a mail to confirm it works for you. This will be written in the
#release notes and the git logs.
#
#
## Authors
#


if __name__ == '__main__':
    offlineimapInfo = OfflineimapInfo()

    print("Will read headers from {}".format(UPCOMING_HEADER))
    Git.chdirToRepositoryTopLevel()
    oVersion = offlineimapInfo.getVersion()
    ccList = Testers.listTestersInTeam()
    authors = Git.getAuthorsList(oVersion)
    for author in authors:
        email = author.getEmail()
        if email not in ccList:
            ccList.append(email)

    with open(UPCOMING_FILE, 'w') as upcoming, \
         open(UPCOMING_HEADER, 'r') as fd_header:
        header = {}

        header['messageId'] = Git.buildMessageId()
        header['date'] = Git.buildDate()
        header['name'], header['email'] = Git.getLocalUser()
        header['mailinglist'] = MAILING_LIST
        header['expectedVersion'] = User.request("Expected new version?")
        header['ccList'] = ", ".join(ccList)

        upcoming.write(fd_header.read().format(**header).lstrip())
        upcoming.write(Git.getShortlog(oVersion))

        upcoming.write("\n\n# Diffstat\n\n")
        upcoming.write(Git.getDiffstat(oVersion))
        upcoming.write("\n\n\n-- \n{}\n".format(Git.getLocalUser()[0]))

    system("{} {}".format(EDITOR, UPCOMING_FILE))
    print("{} written".format(UPCOMING_FILE))


================================================
FILE: docs/Makefile
================================================
# This program is free software under the terms of the GNU General Public
# License. See the COPYING file which must come with this package.

SOURCES = $(wildcard *.rst)
HTML_TARGETS = $(patsubst %.rst,%.html,$(SOURCES))

RM = rm
RST2HTML=`type rst2html >/dev/null 2>&1 && echo rst2html || echo rst2html.py`
RST2MAN=`type rst2man >/dev/null 2>&1 && echo rst2man || echo rst2man.py`
SPHINXBUILD = sphinx-build

docs: man api

html: $(HTML_TARGETS)

$(HTML_TARGETS): %.html : %.rst
	$(RST2HTML) $? $@

manhtml: offlineimap.html offlineimapui.html

offlineimap.html: offlineimap.txt offlineimap.known_issues.txt
	a2x -v -d manpage -D manhtml -f xhtml $<

offlineimapui.html: offlineimapui.txt
	a2x -v -d manpage -D manhtml -f xhtml $<


man: offlineimap.1 offlineimapui.7

offlineimap.1: offlineimap.txt offlineimap.known_issues.txt
	a2x -v -d manpage -f manpage $<

offlineimapui.7: offlineimapui.txt
	a2x -v -d manpage -f manpage $<

api:
	$(SPHINXBUILD) -b html -d html/doctrees doc-src html

websitedoc:
	./website-doc.sh releases
	./website-doc.sh api
	./website-doc.sh html
	./website-doc.sh contrib

clean:
	$(RM) -f $(HTML_TARGETS)
	$(RM) -f offlineimap.1
	$(RM) -f offlineimap.7
	$(RM) -f manhtml/*
	$(RM) -rf html/*
	-find . -name '*.html' -exec rm -f {} \;

.PHONY: clean doc


================================================
FILE: docs/build-uploads.sh
================================================
#!/bin/sh
#
# vim: expandtab ts=2 :

WEBSITE_UPLOADS='./website/_uploads'

while true
do
  test -d .git && break
  cd ..
done

set -e

echo "make clean"
make clean >/dev/null
echo "make targz"
make targz >/dev/null

# Defined in the root Makefile.
version="$(./offlineimap.py --version)"
abbrev="$(git log --format='%h' HEAD~1..)"
targz="../offlineimap-v${version}-${abbrev}.tar.gz"

filename="offlineimap-v${version}.tar.gz"

mv -v "$targz" "${WEBSITE_UPLOADS}/${filename}"
cd "$WEBSITE_UPLOADS"
for digest in sha1 sha256 sha512
do
  target="${filename}.${digest}"
  echo "Adding digest ${WEBSITE_UPLOADS}/${target}"
  "${digest}sum" "$filename" > "$target"
done


================================================
FILE: docs/doc-src/API.rst
================================================
.. OfflineImap API documentation

.. currentmodule:: offlineimap

.. _API docs:

:mod:`offlineimap's` API documentation
======================================

Within :mod:`offlineimap`, the classes :class:`OfflineImap` provides the
high-level functionality. The rest of the classes should usually not needed to
be touched by the user. Email repositories are represented by a
:class:`offlineimap.repository.Base.BaseRepository` or derivatives (see
:mod:`offlineimap.repository` for details). A folder within a repository is
represented by a :class:`offlineimap.folder.Base.BaseFolder` or any derivative
from :mod:`offlineimap.folder`.

This page contains the main API overview of OfflineImap |release|.

OfflineImap can be imported as::

 from offlineimap import OfflineImap


:mod:`offlineimap` -- The OfflineImap module
=============================================

.. module:: offlineimap

.. autoclass:: offlineimap.OfflineImap(cmdline_opts = None)
   :members:
   :inherited-members:
   :undoc-members:
   :private-members:


:class:`offlineimap.account`
============================

An :class:`accounts.Account` connects two email repositories that are to be
synced. It comes in two flavors, normal and syncable.

.. autoclass:: offlineimap.accounts.Account

.. autoclass:: offlineimap.accounts.SyncableAccount
   :members:
   :inherited-members:

   .. autodata:: ui

      Contains the current :mod:`offlineimap.ui`, and can be used for logging etc.

:exc:`OfflineImapError` -- A Notmuch execution error
--------------------------------------------------------

.. autoexception:: offlineimap.error.OfflineImapError
   :members:

   This exception inherits directly from :exc:`Exception` and is raised
   on errors during the offlineimap execution. It has an attribute
   `severity` that denotes the severity level of the error.


:mod:`offlineimap.globals` -- module with global variables
==========================================================

Module `offlineimap.globals` provides the read-only storage
for the global variables.

All exported module attributes can be set manually, but this practice
is highly discouraged and shouldn't be used.
However, attributes of all stored variables can only be read, write
access to them is denied.

Currently, we have only :attr:`options` attribute that holds
command-line options as returned by OptionParser.
The value of :attr:`options` must be set by :func:`set_options`
prior to its first use.

.. automodule:: offlineimap.globals
   :members:

   .. data:: options

      You can access the values of stored options using the usual
      syntax, offlineimap.globals.options.<option-name>


================================================
FILE: docs/doc-src/conf.py
================================================
# -*- coding: utf-8 -*-
#
# pyDNS documentation build configuration file, created by
# sphinx-quickstart on Tue Feb  2 10:00:47 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../..'))

from offlineimap import __version__, __author__, __copyright__
# -- General configuration -----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
              'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
autoclass_content = "both"

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'OfflineIMAP'
copyright = __copyright__

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = False

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []


# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'default'
#html_style = ''
# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['html']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
html_use_modindex = False

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'dev-doc'


# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'offlineimap.tex', u'OfflineIMAP Documentation',
   u'OfflineIMAP contributors', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True


# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}


================================================
FILE: docs/doc-src/dco.rst
================================================
.. _dco

Developer's Certificate of Origin
=================================

v1.1::

  By making a contribution to this project, I certify that:

  (a) The contribution was created in whole or in part by me and I
      have the right to submit it under the open source license
      indicated in the file; or

  (b) The contribution is based upon previous work that, to the best
      of my knowledge, is covered under an appropriate open source
      license and I have the right under that license to submit that
      work with modifications, whether created in whole or in part
      by me, under the same open source license (unless I am
      permitted to submit under a different license), as indicated
      in the file; or

  (c) The contribution was provided directly to me by some other
      person who certified (a), (b) or (c) and I have not modified
      it.

  (d) I understand and agree that this project and the contribution
      are public and that a record of the contribution (including all
      personal information I submit with it, including my sign-off) is
      maintained indefinitely and may be redistributed consistent with
      this project or the open source license(s) involved.


Then, you just add a line saying::

  Signed-off-by: Random J Developer <random@developer.example.org>

This line can be automatically added by git if you run the git-commit command
with the ``-s`` option. Signing can made be afterword with ``--amend -s``.

Notice that you can place your own ``Signed-off-by:`` line when forwarding
somebody else's patch with the above rules for D-C-O.  Indeed you are encouraged
to do so.  Do not forget to place an in-body ``From:`` line at the beginning to
properly attribute the change to its true author (see above).

Also notice that a
Download .txt
gitextract_hxxddv1_/

├── .coveragerc
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── COPYING
├── Changelog.maint.md
├── Changelog.md
├── MAINTAINERS.rst
├── MANIFEST.in
├── Makefile
├── README.md
├── TODO.rst
├── bin/
│   └── offlineimap
├── contrib/
│   ├── README.md
│   ├── helpers.py
│   ├── internet-urllib3.py
│   ├── release.py
│   ├── release.sh
│   ├── store-pw-with-gpg/
│   │   ├── README.md
│   │   ├── gpg-pw.py
│   │   ├── offlineimaprc.sample
│   │   └── passwords-gmail.txt
│   ├── systemd/
│   │   ├── README.md
│   │   ├── offlineimap-oneshot.service
│   │   ├── offlineimap-oneshot.timer
│   │   ├── offlineimap-oneshot@.service
│   │   ├── offlineimap-oneshot@.timer
│   │   ├── offlineimap.service
│   │   └── offlineimap@.service
│   ├── tested-by.py
│   └── upcoming.py
├── docs/
│   ├── Makefile
│   ├── build-uploads.sh
│   ├── doc-src/
│   │   ├── API.rst
│   │   ├── conf.py
│   │   ├── dco.rst
│   │   ├── index.rst
│   │   ├── repository.rst
│   │   └── ui.rst
│   ├── offlineimap.known_issues.txt
│   ├── offlineimap.txt
│   ├── offlineimapui.txt
│   ├── rfcs/
│   │   ├── README.md
│   │   ├── rfc1731.IMAP4_auth.txt
│   │   ├── rfc1732.compatibiliy_IMAP2-IMAP2bis.txt
│   │   ├── rfc1733.models_in_IMAP4.txt
│   │   ├── rfc1734.POP3_AUTHentication
│   │   ├── rfc2061.compatibility_IMAP4-IMAP2bis.txt
│   │   ├── rfc2086.IMAP4_ACL_extension.txt
│   │   ├── rfc2087.IMAP4_QUOTA_extension.txt
│   │   ├── rfc2088.IMAP4_non_synchronizing_literals.txt
│   │   ├── rfc2095.IMAP-POP_AUTHorize_extension.txt
│   │   ├── rfc2177.IMAP4_IDLE_command.txt
│   │   ├── rfc2180.IMAP4_multi-accessed_Mailbox_practice.txt
│   │   ├── rfc2192.IMAP_URL_scheme.txt
│   │   ├── rfc2193.IMAP4_Mailbox_referrals.txt
│   │   ├── rfc2195.IMAP-POP_AUTHorize_extension.txt
│   │   ├── rfc2221.IMAP4_Login_referrals.txt
│   │   ├── rfc2244.ACAP.txt
│   │   ├── rfc2342.IMAP4_Namespace.txt
│   │   ├── rfc2359.IMAP4_UIDPLUS_extension.txt
│   │   ├── rfc2595.TLS_with_IMAP-POP3_and_ACAP.txt
│   │   ├── rfc2683.IMAP4_Implementation_recommendations.txt
│   │   ├── rfc2831.Obsolete_Digest_AUTHentication_as_a_SASL_mech.txt
│   │   ├── rfc2971.IMAP4_ID_extension.txt
│   │   ├── rfc3028.Sieve_Mail_filtering_language.txt
│   │   ├── rfc3348.IMAP4_Child_Mailbox_extension.txt
│   │   ├── rfc3501.IMAP4rev1.txt
│   │   ├── rfc3502.MULTIAPPEND_extension.txt
│   │   ├── rfc3503.Message_Disposition_Notification.txt
│   │   ├── rfc3516.IMAP4_Binary_content_extension.txt
│   │   ├── rfc3691.IMAP_UNSELECT_command.txt
│   │   ├── rfc4314.IMAP4_ACL_extension.txt
│   │   ├── rfc4315.IMAP_UIDPLUS_extension.txt
│   │   ├── rfc4466.Collected_extensions_to_IMAP4_ABNF.txt
│   │   ├── rfc4467.IMAP_URLAUTH_extension.txt
│   │   ├── rfc4469.IMAP_CATENATE_extension.txt
│   │   ├── rfc4549.Sync_operations_for_disconnected_IMAP4_Clients.txt
│   │   ├── rfc4551.IMAP_Conditional_STORE_or_Quick_flag_changes_resync.txt
│   │   ├── rfc4731.IMAP4_Extension_to_SEARCH_command.txt
│   │   ├── rfc4978.IMAP_Compress_extension.txt
│   │   ├── rfc5032.IMAP_WITHIN_Search_extension.txt
│   │   ├── rfc5161.IMAP_ENABLE_extension.txt
│   │   ├── rfc5162.IMAP4_Extensions_for_Quick_Mailbox_resync.txt
│   │   ├── rfc5182.IMAP_extension_last_SEARCH_result.txt
│   │   ├── rfc5182.Sieve_and_extensions.txt
│   │   ├── rfc5255.IMAP_i18n.txt
│   │   ├── rfc5257.IMAP_ANNOTATE_extension.txt
│   │   ├── rfc5258.IMAP4_LIST_command_extension.txt
│   │   ├── rfc5423.IM_Store_Events.txt
│   │   ├── rfc5464.IMAP_METADATA_extension.txt
│   │   ├── rfc5465.IMAP_NOTIFY_extension.txt
│   │   ├── rfc5530.IMAP_Response_codes.txt
│   │   ├── rfc5738.IMAP_UTF8.txt
│   │   ├── rfc5788.IMAP4_Keyword_registry.txt
│   │   ├── rfc5819.IMAP4_extension_Returning_STATUS_info_in_LIST.txt
│   │   ├── rfc5957.IMAP4_SORT_extension.txt
│   │   ├── rfc6154.IMAP_LIST_Special-use_Mailboxes.txt
│   │   ├── rfc6203.IMAP4_Fuzzy_SEARCH_extension.txt
│   │   ├── rfc6237.IMAP4_Multimailbox_SEARCH_extension.txt
│   │   └── rfc6331.Moving_Digest-MD5_to_Historic
│   └── website-doc.sh
├── offlineimap/
│   ├── CustomConfig.py
│   ├── __init__.py
│   ├── accounts.py
│   ├── bundled_imaplib2.py
│   ├── emailutil.py
│   ├── error.py
│   ├── folder/
│   │   ├── Base.py
│   │   ├── Gmail.py
│   │   ├── GmailMaildir.py
│   │   ├── IMAP.py
│   │   ├── LocalStatus.py
│   │   ├── LocalStatusSQLite.py
│   │   ├── Maildir.py
│   │   ├── UIDMaps.py
│   │   └── __init__.py
│   ├── globals.py
│   ├── imaplibutil.py
│   ├── imapserver.py
│   ├── imaputil.py
│   ├── init.py
│   ├── localeval.py
│   ├── mbnames.py
│   ├── repository/
│   │   ├── Base.py
│   │   ├── Gmail.py
│   │   ├── GmailMaildir.py
│   │   ├── IMAP.py
│   │   ├── LocalStatus.py
│   │   ├── Maildir.py
│   │   └── __init__.py
│   ├── threadutil.py
│   ├── ui/
│   │   ├── Curses.py
│   │   ├── Machine.py
│   │   ├── Noninteractive.py
│   │   ├── TTY.py
│   │   ├── UIBase.py
│   │   ├── __init__.py
│   │   └── debuglock.py
│   ├── utils/
│   │   ├── __init__.py
│   │   ├── const.py
│   │   ├── distro.py
│   │   └── stacktrace.py
│   └── virtual_imaplib2.py
├── offlineimap.conf
├── offlineimap.conf.minimal
├── offlineimap.py
├── requirements.txt
├── scripts/
│   └── get-repository.sh
├── setup.cfg
├── setup.py
├── snapcraft.yaml
├── test/
│   ├── .gitignore
│   ├── OLItest/
│   │   ├── TestRunner.py
│   │   ├── __init__.py
│   │   └── globals.py
│   ├── README
│   ├── __init__.py
│   ├── credentials.conf.sample
│   └── tests/
│       ├── __init__.py
│       ├── test_00_globals.py
│       ├── test_00_imaputil.py
│       ├── test_01_basic.py
│       └── test_02_MappedIMAP.py
└── tests/
    ├── .gitignore
    ├── create_conf_file.py
    └── requirements.txt
Download .txt
SYMBOL INDEX (1029 symbols across 48 files)

FILE: contrib/helpers.py
  function run (line 28) | def run(cmd):
  function goTo (line 31) | def goTo(path):
  class Author (line 42) | class Author(object):
    method __init__ (line 43) | def __init__(self, name, count, email):
    method getName (line 48) | def getName(self):
    method getCount (line 51) | def getCount(self):
    method getEmail (line 54) | def getEmail(self):
  class Git (line 58) | class Git(object):
    method getShortlog (line 60) | def getShortlog(ref):
    method add (line 80) | def add(files):
    method commit (line 85) | def commit(msg):
    method tag (line 90) | def tag(version):
    method stash (line 95) | def stash(msg):
    method mergeFF (line 100) | def mergeFF(ref):
    method getDiffstat (line 105) | def getDiffstat(ref):
    method isClean (line 110) | def isClean():
    method buildMessageId (line 119) | def buildMessageId():
    method resetKeep (line 125) | def resetKeep(ref):
    method getRef (line 129) | def getRef(ref):
    method rmTag (line 133) | def rmTag(tag):
    method checkout (line 137) | def checkout(ref, create=False):
    method makeCacheDir (line 151) | def makeCacheDir():
    method getLocalUser (line 158) | def getLocalUser():
    method buildDate (line 166) | def buildDate():
    method getAuthorsList (line 171) | def getAuthorsList(sinceRef):
    method getCommitsList (line 188) | def getCommitsList(sinceRef):
    method chdirToRepositoryTopLevel (line 195) | def chdirToRepositoryTopLevel():
  class OfflineimapInfo (line 202) | class OfflineimapInfo(object):
    method getVersion (line 203) | def getVersion(self):
    method editInit (line 207) | def editInit(self):
  class User (line 212) | class User(object):
    method request (line 216) | def request(msg, prompt='--> '):
    method pause (line 221) | def pause(msg=False):
    method yesNo (line 225) | def yesNo(msg, defaultToYes=False, prompt='--> '):
  class Tester (line 238) | class Tester(object):
    method __init__ (line 239) | def __init__(self, name, email, feedback):
    method __str__ (line 244) | def __str__(self):
    method getName (line 247) | def getName(self):
    method getEmail (line 250) | def getEmail(self):
    method getFeedback (line 253) | def getFeedback(self):
    method positiveFeedback (line 256) | def positiveFeedback(self):
    method setFeedback (line 259) | def setFeedback(self, feedback):
    method switchFeedback (line 263) | def switchFeedback(self):
  class Testers (line 267) | class Testers(object):
    method __init__ (line 268) | def __init__(self):
    method _read (line 272) | def _read(self):
    method listTestersInTeam (line 284) | def listTestersInTeam():
    method add (line 292) | def add(self, name, email, feedback=None):
    method remove (line 295) | def remove(self, tester):
    method get (line 298) | def get(self):
    method getList (line 301) | def getList(self):
    method getListOk (line 307) | def getListOk(self):
    method reset (line 314) | def reset(self):
    method write (line 318) | def write(self):

FILE: contrib/internet-urllib3.py
  function isInternetConnected (line 6) | def isInternetConnected(url="www.ietf.org"):

FILE: contrib/release.py
  class State (line 93) | class State(object):
    method __init__ (line 94) | def __init__(self):
    method setTag (line 100) | def setTag(self, tag):
    method save (line 103) | def save(self):
    method saveWebsite (line 107) | def saveWebsite(self):
    method restore (line 113) | def restore(self):
  class Changelog (line 133) | class Changelog(object):
    method __init__ (line 134) | def __init__(self):
    method edit (line 137) | def edit(self):
    method update (line 140) | def update(self):
    method savePrevious (line 149) | def savePrevious(self):
    method isPrevious (line 152) | def isPrevious(self):
    method showPrevious (line 157) | def showPrevious(self):
    method usePrevious (line 162) | def usePrevious(self):
    method usingPrevious (line 166) | def usingPrevious(self):
    method writeExcerpt (line 169) | def writeExcerpt(self, version, date,
    method getSectionsContent (line 181) | def getSectionsContent(self):
  class Announce (line 218) | class Announce(object):
    method __init__ (line 219) | def __init__(self, version):
    method setHeaders (line 223) | def setHeaders(self, messageId, date):
    method setContent (line 246) | def setContent(self, dict_Content):
    method close (line 258) | def close(self):
  class Website (line 262) | class Website(object):
    method updateUploads (line 263) | def updateUploads(self):
    method updateAPI (line 272) | def updateAPI(self):
    method buildLatest (line 302) | def buildLatest(self, version):
    method exportDocs (line 307) | def exportDocs(self):
    method createImportBranch (line 316) | def createImportBranch(self, version):
  class Release (line 337) | class Release(object):
    method __init__ (line 338) | def __init__(self):
    method getVersion (line 346) | def getVersion(self):
    method prepare (line 349) | def prepare(self):
    method requestVersion (line 356) | def requestVersion(self, currentVersion):
    method updateVersion (line 359) | def updateVersion(self):
    method checkVersions (line 362) | def checkVersions(self, current, new):
    method updateChangelog (line 367) | def updateChangelog(self):
    method writeAnnounce (line 395) | def writeAnnounce(self):
    method make (line 407) | def make(self):
    method updateWebsite (line 422) | def updateWebsite(self, newVersion):
    method getWebsiteBranch (line 433) | def getWebsiteBranch(self):
    method after (line 436) | def after(self):
    method restore (line 443) | def restore(self):

FILE: contrib/store-pw-with-gpg/gpg-pw.py
  function get_keychain_pass (line 23) | def get_keychain_pass(account=None, server=None):
  function find_password (line 41) | def find_password(text):
  function get_gpg_pass (line 52) | def get_gpg_pass(account, storage):
  function get_pass (line 67) | def get_pass(account=None, server=None, passwd_file=None):
  class Tester (line 88) | class Tester(unittest.TestCase):
    method testMatchSimple (line 89) | def testMatchSimple(self):
    method testMatchComplex (line 93) | def testMatchComplex(self):

FILE: contrib/tested-by.py
  class App (line 17) | class App(object):
    method __init__ (line 18) | def __init__(self):
    method _getTestersByFeedback (line 24) | def _getTestersByFeedback(self):
    method parseArgs (line 42) | def parseArgs(self):
    method run (line 59) | def run(self):
    method addTester (line 69) | def addTester(self, strTester):
    method deleteTester (line 81) | def deleteTester(self, number):
    method listTesters (line 95) | def listTesters(self):
    method switchFeedback (line 109) | def switchFeedback(self):
    method reset (line 131) | def reset(self):

FILE: offlineimap/CustomConfig.py
  class CustomConfigParser (line 28) | class CustomConfigParser(SafeConfigParser):
    method __init__ (line 29) | def __init__(self):
    method getdefault (line 33) | def getdefault(self, section, option, default, *args, **kwargs):
    method getdefaultint (line 43) | def getdefaultint(self, section, option, default, *args, **kwargs):
    method getdefaultfloat (line 53) | def getdefaultfloat(self, section, option, default, *args, **kwargs):
    method getdefaultboolean (line 62) | def getdefaultboolean(self, section, option, default, *args, **kwargs):
    method getlist (line 71) | def getlist(self, section, option, separator_re):
    method getdefaultlist (line 84) | def getdefaultlist(self, section, option, default, separator_re):
    method getmetadatadir (line 93) | def getmetadatadir(self):
    method getlocaleval (line 101) | def getlocaleval(self):
    method getsectionlist (line 116) | def getsectionlist(self, key):
    method set_if_not_exists (line 128) | def set_if_not_exists(self, section, option, value):
    method apply_xforms (line 138) | def apply_xforms(self, string, transforms):
  function CustomConfigDefault (line 157) | def CustomConfigDefault():
  class ConfigHelperMixin (line 168) | class ConfigHelperMixin(object):
    method _confighelper_runner (line 181) | def _confighelper_runner(self, option, default, defaultfunc, mainfunc,...
    method getconfig (line 205) | def getconfig(self):
    method getsection (line 216) | def getsection(self):
    method getconf (line 226) | def getconf(self, option, default = CustomConfigDefault):
    method getconf_xform (line 240) | def getconf_xform(self, option, xforms, default = CustomConfigDefault):
    method getconfboolean (line 256) | def getconfboolean(self, option, default = CustomConfigDefault):
    method getconfint (line 270) | def getconfint(self, option, default = CustomConfigDefault):
    method getconffloat (line 286) | def getconffloat(self, option, default = CustomConfigDefault):
    method getconflist (line 300) | def getconflist(self, option, separator_re,

FILE: offlineimap/accounts.py
  function getaccountlist (line 45) | def getaccountlist(customconfig):
  class Account (line 50) | class Account(CustomConfig.ConfigHelperMixin):
    method __init__ (line 62) | def __init__(self, config, name):
    method getlocaleval (line 92) | def getlocaleval(self):
    method getconfig (line 96) | def getconfig(self):
    method getname (line 99) | def getname(self):
    method __str__ (line 102) | def __str__(self):
    method getaccountmeta (line 105) | def getaccountmeta(self):
    method getsection (line 109) | def getsection(self):
    method set_abort_event (line 113) | def set_abort_event(cls, config, signum):
    method get_abort_event (line 138) | def get_abort_event(self):
    method _sleeper (line 155) | def _sleeper(self):
    method serverdiagnostics (line 191) | def serverdiagnostics(self):
    method deletefolder (line 201) | def deletefolder(self, foldername):
  class SyncableAccount (line 217) | class SyncableAccount(Account):
    method __init__ (line 227) | def __init__(self, *args, **kwargs):
    method __lock (line 233) | def __lock(self):
    method _unlock (line 254) | def _unlock(self):
    method syncrunner (line 269) | def syncrunner(self):
    method get_local_folder (line 317) | def get_local_folder(self, remotefolder):
    method __sync (line 327) | def __sync(self):
    method callhook (line 444) | def callhook(self, cmd):
  function syncfolder (line 468) | def syncfolder(account, remotefolder, quick):

FILE: offlineimap/bundled_imaplib2.py
  function Int2AP (line 149) | def Int2AP(num):
  class Request (line 164) | class Request(object):
    method __init__ (line 168) | def __init__(self, parent, name=None, callback=None, cb_arg=None, cb_s...
    method abort (line 186) | def abort(self, typ, val):
    method get_response (line 191) | def get_response(self, exc_fmt=None):
    method deliver (line 205) | def deliver(self, response):
  class IMAP4 (line 217) | class IMAP4(object):
    class error (line 298) | class error(Exception): pass    # Logical errors - debug required
    class abort (line 299) | class abort(error): pass        # Service errors - close and retry
    class readonly (line 300) | class readonly(abort): pass     # Mailbox status changed to READ-ONLY
    method __init__ (line 317) | def __init__(self, host=None, port=None, debug=None, debug_file=None, ...
    method __getattr__ (line 427) | def __getattr__(self, attr):
    method _mode_ascii (line 434) | def _mode_ascii(self):
    method _mode_utf8 (line 445) | def _mode_utf8(self):
    method open (line 460) | def open(self, host=None, port=None):
    method open_socket (line 473) | def open_socket(self):
    method ssl_wrap_socket (line 507) | def ssl_wrap_socket(self):
    method start_compressing (line 574) | def start_compressing(self):
    method read (line 583) | def read(self, size):
    method send (line 598) | def send(self, data):
    method shutdown (line 612) | def shutdown(self):
    method socket (line 626) | def socket(self):
    method enable_compression (line 637) | def enable_compression(self):
    method pop_untagged_responses (line 653) | def pop_untagged_responses(self):
    method recent (line 667) | def recent(self, **kw):
    method response (line 682) | def response(self, code, **kw):
    method append (line 696) | def append(self, mailbox, flags, date_time, message, **kw):
    method authenticate (line 723) | def authenticate(self, mechanism, authobject, **kw):
    method capability (line 752) | def capability(self, **kw):
    method check (line 761) | def check(self, **kw):
    method close (line 768) | def close(self, **kw):
    method copy (line 786) | def copy(self, message_set, new_mailbox, **kw):
    method create (line 793) | def create(self, mailbox, **kw):
    method delete (line 800) | def delete(self, mailbox, **kw):
    method deleteacl (line 807) | def deleteacl(self, mailbox, who, **kw):
    method enable (line 814) | def enable(self, capability):
    method examine (line 827) | def examine(self, mailbox='INBOX', **kw):
    method expunge (line 837) | def expunge(self, **kw):
    method fetch (line 848) | def fetch(self, message_set, message_parts, **kw):
    method getacl (line 861) | def getacl(self, mailbox, **kw):
    method getannotation (line 869) | def getannotation(self, mailbox, entry, attribute, **kw):
    method getquota (line 877) | def getquota(self, root, **kw):
    method getquotaroot (line 886) | def getquotaroot(self, mailbox, **kw):
    method id (line 900) | def id(self, *kv_pairs, **kw):
    method idle (line 922) | def idle(self, timeout=None, **kw):
    method list (line 936) | def list(self, directory='""', pattern='*', **kw):
    method login (line 950) | def login(self, user, password, **kw):
    method login_cram_md5 (line 966) | def login_cram_md5(self, user, password, **kw):
    method _CRAM_MD5_AUTH (line 974) | def _CRAM_MD5_AUTH(self, challenge):
    method logout (line 982) | def logout(self, **kw):
    method lsub (line 1010) | def lsub(self, directory='""', pattern='*', **kw):
    method myrights (line 1020) | def myrights(self, mailbox, **kw):
    method namespace (line 1029) | def namespace(self, **kw):
    method noop (line 1038) | def noop(self, **kw):
    method partial (line 1046) | def partial(self, message_num, message_part, start, length, **kw):
    method proxyauth (line 1057) | def proxyauth(self, user, **kw):
    method rename (line 1068) | def rename(self, oldmailbox, newmailbox, **kw):
    method search (line 1075) | def search(self, charset, *criteria, **kw):
    method select (line 1090) | def select(self, mailbox='INBOX', readonly=False, **kw):
    method setacl (line 1126) | def setacl(self, mailbox, who, what, **kw):
    method setannotation (line 1136) | def setannotation(self, *args, **kw):
    method setquota (line 1144) | def setquota(self, root, limits, **kw):
    method sort (line 1155) | def sort(self, sort_criteria, charset, *search_criteria, **kw):
    method starttls (line 1166) | def starttls(self, keyfile=None, certfile=None, ca_certs=None, cert_ve...
    method status (line 1224) | def status(self, mailbox, names, **kw):
    method store (line 1233) | def store(self, message_set, command, flags, **kw):
    method subscribe (line 1243) | def subscribe(self, mailbox, **kw):
    method thread (line 1253) | def thread(self, threading_algorithm, charset, *search_criteria, **kw):
    method uid (line 1262) | def uid(self, command, *args, **kw):
    method unsubscribe (line 1278) | def unsubscribe(self, mailbox, **kw):
    method xatom (line 1288) | def xatom(self, name, *args, **kw):
    method _append_untagged (line 1307) | def _append_untagged(self, typ, dat):
    method _check_bye (line 1334) | def _check_bye(self):
    method _checkquote (line 1344) | def _checkquote(self, arg):
    method _choose_nonull_or_dflt (line 1360) | def _choose_nonull_or_dflt(self, dflt, *args):
    method _command (line 1373) | def _command(self, name, *args, **kw):
    method _command_complete (line 1493) | def _command_complete(self, rqb, kw):
    method _command_completer (line 1507) | def _command_completer(self, cb_arg_list):
    method _deliver_dat (line 1537) | def _deliver_dat(self, typ, dat, kw):
    method _deliver_exc (line 1544) | def _deliver_exc(self, exc, dat, kw):
    method _end_idle (line 1551) | def _end_idle(self):
    method _get_untagged_response (line 1566) | def _get_untagged_response(self, name, leave=False):
    method _match (line 1582) | def _match(self, cre, s):
    method _put_response (line 1591) | def _put_response(self, resp):
    method _quote (line 1700) | def _quote(self, arg):
    method _release_state_change (line 1705) | def _release_state_change(self):
    method _request_pop (line 1712) | def _request_pop(self, name, data):
    method _request_push (line 1730) | def _request_push(self, tag=None, name=None, **kw):
    method _simple_command (line 1742) | def _simple_command(self, name, *args, **kw):
    method _untagged_response (line 1751) | def _untagged_response(self, typ, dat, name):
    method _close_threads (line 1771) | def _close_threads(self):
    method _handler (line 1786) | def _handler(self):
    method _reader (line 1866) | def _reader(self):
    method _reader (line 1961) | def _reader(self):
    method _writer (line 2032) | def _writer(self):
    method _init_debug (line 2069) | def _init_debug(self, debug=None, debug_file=None, debug_buf_lvl=None):
    method _dump_ur (line 2084) | def _dump_ur(self, lvl):
    method _log (line 2099) | def _log(self, lvl, line):
    method _mesg (line 2124) | def _mesg(self, s, tn=None, secs=None):
    method _print_log (line 2137) | def _print_log(self):
  class IMAP4_SSL (line 2154) | class IMAP4_SSL(IMAP4):
    method __init__ (line 2184) | def __init__(self, host=None, port=None, keyfile=None, certfile=None, ...
    method open (line 2194) | def open(self, host=None, port=None):
    method read (line 2207) | def read(self, size):
    method send (line 2222) | def send(self, data):
    method ssl (line 2245) | def ssl(self):
  class IMAP4_stream (line 2253) | class IMAP4_stream(IMAP4):
    method __init__ (line 2271) | def __init__(self, command, debug=None, debug_file=None, identifier=No...
    method open (line 2281) | def open(self, host=None, port=None):
    method read (line 2295) | def read(self, size):
    method send (line 2309) | def send(self, data):
    method shutdown (line 2323) | def shutdown(self):
  class _Authenticator (line 2330) | class _Authenticator(object):
    method __init__ (line 2335) | def __init__(self, mechinst):
    method process (line 2338) | def process(self, data, rqb):
    method encode (line 2344) | def encode(self, inp):
    method decode (line 2369) | def decode(self, inp):
  class _IdleCont (line 2377) | class _IdleCont(object):
    method __init__ (line 2382) | def __init__(self, parent, timeout):
    method process (line 2387) | def process(self, data, rqb):
  function Internaldate2Time (line 2409) | def Internaldate2Time(resp):
  function Time2Internaldate (line 2454) | def Time2Internaldate(date_time):
  function ParseFlags (line 2480) | def ParseFlags(resp):
  function responder (line 2579) | def responder(cb_arg_list):
  function run (line 2592) | def run(cmd, args, cb=True):

FILE: offlineimap/emailutil.py
  function get_message_date (line 21) | def get_message_date(content, header='Date'):

FILE: offlineimap/error.py
  class OfflineImapError (line 1) | class OfflineImapError(Exception):
    class ERROR (line 4) | class ERROR:
    method __init__ (line 16) | def __init__(self, reason, severity, errcode=None):
    method reason (line 38) | def reason(self):

FILE: offlineimap/folder/Base.py
  class BaseFolder (line 29) | class BaseFolder(object):
    method __init__ (line 32) | def __init__(self, name, repository):
    method getname (line 94) | def getname(self):
    method __str__ (line 98) | def __str__(self):
    method __unicode__ (line 102) | def __unicode__(self):
    method __enter__ (line 107) | def __enter__(self):
    method __exit__ (line 112) | def __exit__(self, exc_type, exc_val, exc_tb):
    method accountname (line 118) | def accountname(self):
    method sync_this (line 124) | def sync_this(self):
    method dofsync (line 132) | def dofsync(self):
    method suggeststhreads (line 135) | def suggeststhreads(self):
    method waitforthread (line 143) | def waitforthread(self):
    method quickchanged (line 148) | def quickchanged(self, statusfolder):
    method getinstancelimitnamespace (line 157) | def getinstancelimitnamespace(self):
    method storesmessages (line 163) | def storesmessages(self):
    method getvisiblename (line 170) | def getvisiblename(self):
    method getexplainedname (line 175) | def getexplainedname(self):
    method getrepository (line 183) | def getrepository(self):
    method getroot (line 188) | def getroot(self):
    method getsep (line 193) | def getsep(self):
    method getfullname (line 198) | def getfullname(self):
    method getfolderbasename (line 204) | def getfolderbasename(self):
    method check_uidvalidity (line 216) | def check_uidvalidity(self):
    method _getuidfilename (line 232) | def _getuidfilename(self):
    method get_saveduidvalidity (line 238) | def get_saveduidvalidity(self):
    method save_uidvalidity (line 255) | def save_uidvalidity(self):
    method get_uidvalidity (line 269) | def get_uidvalidity(self):
    method cachemessagelist (line 277) | def cachemessagelist(self):
    method ismessagelistempty (line 286) | def ismessagelistempty(self):
    method dropmessagelistcache (line 293) | def dropmessagelistcache(self):
    method getmessagelist (line 298) | def getmessagelist(self):
    method msglist_item_initializer (line 305) | def msglist_item_initializer(self, uid):
    method uidexists (line 314) | def uidexists(self, uid):
    method getmessageuidlist (line 319) | def getmessageuidlist(self):
    method getmessagecount (line 326) | def getmessagecount(self):
    method getmessage (line 331) | def getmessage(self, uid):
    method getmaxage (line 336) | def getmaxage(self):
    method getmaxsize (line 371) | def getmaxsize(self):
    method getstartdate (line 375) | def getstartdate(self):
    method get_min_uid_file (line 396) | def get_min_uid_file(self):
    method save_min_uid (line 403) | def save_min_uid(self, min_uid):
    method retrieve_min_uid (line 409) | def retrieve_min_uid(self):
    method savemessage (line 422) | def savemessage(self, uid, content, flags, rtime):
    method getmessagetime (line 446) | def getmessagetime(self, uid):
    method getmessagemtime (line 451) | def getmessagemtime(self, uid):
    method getmessageflags (line 456) | def getmessageflags(self, uid):
    method getmessagekeywords (line 461) | def getmessagekeywords(self, uid):
    method savemessageflags (line 466) | def savemessageflags(self, uid, flags):
    method addmessageflags (line 475) | def addmessageflags(self, uid, flags):
    method addmessagesflags (line 489) | def addmessagesflags(self, uidlist, flags):
    method deletemessageflags (line 498) | def deletemessageflags(self, uid, flags):
    method deletemessagesflags (line 511) | def deletemessagesflags(self, uidlist, flags):
    method getmessagelabels (line 520) | def getmessagelabels(self, uid):
    method savemessagelabels (line 525) | def savemessagelabels(self, uid, labels, ignorelabels=set(), mtime=0):
    method addmessagelabels (line 534) | def addmessagelabels(self, uid, labels):
    method addmessageslabels (line 547) | def addmessageslabels(self, uidlist, labels):
    method deletemessagelabels (line 555) | def deletemessagelabels(self, uid, labels):
    method deletemessageslabels (line 568) | def deletemessageslabels(self, uidlist, labels):
    method addmessageheader (line 577) | def addmessageheader(self, content, linebreak, headername, headervalue):
    method __find_eoh (line 685) | def __find_eoh(self, content):
    method getmessageheader (line 702) | def getmessageheader(self, content, name):
    method getmessageheaderlist (line 728) | def getmessageheaderlist(self, content, name):
    method deletemessageheaders (line 748) | def deletemessageheaders(self, content, header_list):
    method change_message_uid (line 784) | def change_message_uid(self, uid, new_uid):
    method deletemessage (line 795) | def deletemessage(self, uid):
    method deletemessages (line 802) | def deletemessages(self, uidlist):
    method copymessageto (line 810) | def copymessageto(self, uid, dstfolder, statusfolder, register=1):
    method __syncmessagesto_copy (line 879) | def __syncmessagesto_copy(self, dstfolder, statusfolder):
    method __syncmessagesto_delete (line 953) | def __syncmessagesto_delete(self, dstfolder, statusfolder):
    method combine_flags_and_keywords (line 984) | def combine_flags_and_keywords(self, uid, dstfolder):
    method __syncmessagesto_flags (line 1020) | def __syncmessagesto_flags(self, dstfolder, statusfolder):
    method syncmessagesto (line 1076) | def syncmessagesto(self, dstfolder, statusfolder):
    method __eq__ (line 1129) | def __eq__(self, other):
    method __ne__ (line 1144) | def __ne__(self, other):

FILE: offlineimap/folder/Gmail.py
  class GmailFolder (line 29) | class GmailFolder(IMAPFolder):
    method __init__ (line 45) | def __init__(self, imapserver, name, repository, decode=True):
    method getmessage (line 64) | def getmessage(self, uid):
    method getmessagelabels (line 104) | def getmessagelabels(self, uid):
    method msglist_item_initializer (line 111) | def msglist_item_initializer(self, uid):
    method cachemessagelist (line 117) | def cachemessagelist(self, min_date=None, min_uid=None):
    method savemessage (line 176) | def savemessage(self, uid, content, flags, rtime):
    method _messagelabels_aux (line 205) | def _messagelabels_aux(self, arg, uidlist, labels):
    method savemessagelabels (line 230) | def savemessagelabels(self, uid, labels):
    method addmessageslabels (line 248) | def addmessageslabels(self, uidlist, labels):
    method deletemessageslabels (line 260) | def deletemessageslabels(self, uidlist, labels):
    method copymessageto (line 272) | def copymessageto(self, uid, dstfolder, statusfolder, register = 1):
    method syncmessagesto_labels (line 303) | def syncmessagesto_labels(self, dstfolder, statusfolder):

FILE: offlineimap/folder/GmailMaildir.py
  class GmailMaildirFolder (line 28) | class GmailMaildirFolder(MaildirFolder):
    method __init__ (line 31) | def __init__(self, root, name, sep, repository):
    method quickchanged (line 45) | def quickchanged(self, statusfolder):
    method msglist_item_initializer (line 71) | def msglist_item_initializer(self, uid):
    method cachemessagelist (line 76) | def cachemessagelist(self, min_date=None, min_uid=None):
    method getmessagelabels (line 88) | def getmessagelabels(self, uid):
    method getmessagemtime (line 110) | def getmessagemtime(self, uid):
    method savemessage (line 116) | def savemessage(self, uid, content, flags, rtime):
    method savemessagelabels (line 141) | def savemessagelabels(self, uid, labels, ignorelabels=set()):
    method copymessageto (line 201) | def copymessageto(self, uid, dstfolder, statusfolder, register=1):
    method syncmessagesto_labels (line 235) | def syncmessagesto_labels(self, dstfolder, statusfolder):

FILE: offlineimap/folder/IMAP.py
  class IMAPFolder (line 43) | class IMAPFolder(BaseFolder):
    method __init__ (line 44) | def __init__(self, imapserver, name, repository, decode=True):
    method __selectro (line 79) | def __selectro(self, imapobj, force=False):
    method getfullIMAPname (line 93) | def getfullIMAPname(self):
    method suggeststhreads (line 100) | def suggeststhreads(self):
    method waitforthread (line 113) | def waitforthread(self):
    method getmaxage (line 116) | def getmaxage(self):
    method getinstancelimitnamespace (line 126) | def getinstancelimitnamespace(self):
    method get_uidvalidity (line 130) | def get_uidvalidity(self):
    method quickchanged (line 152) | def quickchanged(self, statusfolder):
    method _msgs_to_fetch (line 190) | def _msgs_to_fetch(self, imapobj, min_date=None, min_uid=None):
    method msglist_item_initializer (line 263) | def msglist_item_initializer(self, uid):
    method cachemessagelist (line 268) | def cachemessagelist(self, min_date=None, min_uid=None):
    method getmessage (line 314) | def getmessage(self, uid):
    method getmessagetime (line 343) | def getmessagetime(self, uid):
    method getmessageflags (line 347) | def getmessageflags(self, uid):
    method getmessagekeywords (line 351) | def getmessagekeywords(self, uid):
    method __generate_randomheader (line 354) | def __generate_randomheader(self, content):
    method __savemessage_searchforheader (line 382) | def __savemessage_searchforheader(self, imapobj, headername, headerval...
    method __savemessage_fetchheaders (line 416) | def __savemessage_fetchheaders(self, imapobj, headername, headervalue):
    method __getmessageinternaldate (line 507) | def __getmessageinternaldate(self, content, rtime=None):
    method savemessage (line 583) | def savemessage(self, uid, content, flags, rtime):
    method _fetch_from_imap (line 769) | def _fetch_from_imap(self, uids, retry_num=1):
    method _store_to_imap (line 835) | def _store_to_imap(self, imapobj, uid, field, data):
    method savemessageflags (line 855) | def savemessageflags(self, uid, flags):
    method addmessageflags (line 879) | def addmessageflags(self, uid, flags):
    method __addmessagesflags_noconvert (line 882) | def __addmessagesflags_noconvert(self, uidlist, flags):
    method addmessagesflags (line 886) | def addmessagesflags(self, uidlist, flags):
    method deletemessageflags (line 894) | def deletemessageflags(self, uid, flags):
    method deletemessagesflags (line 898) | def deletemessagesflags(self, uidlist, flags):
    method __processmessagesflags_real (line 901) | def __processmessagesflags_real(self, operation, uidlist, flags):
    method __processmessagesflags (line 946) | def __processmessagesflags(self, operation, uidlist, flags):
    method change_message_uid (line 956) | def change_message_uid(self, uid, new_uid):
    method deletemessage (line 965) | def deletemessage(self, uid):
    method deletemessages (line 969) | def deletemessages(self, uidlist):
    method __deletemessages_noconvert (line 972) | def __deletemessages_noconvert(self, uidlist):

FILE: offlineimap/folder/LocalStatus.py
  class LocalStatusFolder (line 26) | class LocalStatusFolder(BaseFolder):
    method __init__ (line 32) | def __init__(self, name, repository):
    method storesmessages (line 43) | def storesmessages(self):
    method isnewfolder (line 46) | def isnewfolder(self):
    method getfullname (line 50) | def getfullname(self):
    method msglist_item_initializer (line 54) | def msglist_item_initializer(self, uid):
    method readstatus_v1 (line 57) | def readstatus_v1(self, fp):
    method readstatus (line 78) | def readstatus(self, fp):
    method cachemessagelist (line 105) | def cachemessagelist(self):
    method openfiles (line 154) | def openfiles(self):
    method closefiles (line 157) | def closefiles(self):
    method purge (line 160) | def purge(self):
    method save (line 169) | def save(self):
    method saveall (line 174) | def saveall(self):
    method savemessage (line 196) | def savemessage(self, uid, content, flags, rtime, mtime=0, labels=set()):
    method getmessageflags (line 220) | def getmessageflags(self, uid):
    method getmessagetime (line 224) | def getmessagetime(self, uid):
    method savemessageflags (line 228) | def savemessageflags(self, uid, flags):
    method savemessagelabels (line 232) | def savemessagelabels(self, uid, labels, mtime=None):
    method savemessageslabelsbulk (line 237) | def savemessageslabelsbulk(self, labels):
    method addmessageslabels (line 244) | def addmessageslabels(self, uids, labels):
    method deletemessageslabels (line 249) | def deletemessageslabels(self, uids, labels):
    method getmessagelabels (line 254) | def getmessagelabels(self, uid):
    method savemessagesmtimebulk (line 257) | def savemessagesmtimebulk(self, mtimes):
    method getmessagemtime (line 264) | def getmessagemtime(self, uid):
    method deletemessage (line 268) | def deletemessage(self, uid):
    method deletemessages (line 272) | def deletemessages(self, uidlist):

FILE: offlineimap/folder/LocalStatusSQLite.py
  class DatabaseFileLock (line 28) | class DatabaseFileLock(object):
    method __init__ (line 31) | def __init__(self):
    method __enter__ (line 35) | def __enter__(self):
    method __exit__ (line 38) | def __exit__(self, typ, value, tb):
    method registerNewUser (line 41) | def registerNewUser(self):
    method removeOneUser (line 44) | def removeOneUser(self):
    method getLock (line 47) | def getLock(self):
    method shouldClose (line 50) | def shouldClose(self):
  class LocalStatusSQLiteFolder (line 54) | class LocalStatusSQLiteFolder(BaseFolder):
    method __init__ (line 74) | def __init__(self, name, repository):
    method __enter__ (line 96) | def __enter__(self):
    method __exit__ (line 101) | def __exit__(self, exc_type, exc_val, exc_tb):
    method openfiles (line 108) | def openfiles(self):
    method purge (line 141) | def purge(self):
    method storesmessages (line 150) | def storesmessages(self):
    method getfullname (line 153) | def getfullname(self):
    method isnewfolder (line 157) | def isnewfolder(self):
    method __sql_write (line 160) | def __sql_write(self, sql, args=None, executemany=False):
    method __upgrade_db (line 196) | def __upgrade_db(self, from_ver):
    method __create_db (line 220) | def __create_db(self):
    method msglist_item_initializer (line 237) | def msglist_item_initializer(self, uid):
    method cachemessagelist (line 242) | def cachemessagelist(self):
    method closefiles (line 268) | def closefiles(self):
    method save (line 278) | def save(self):
    method saveall (line 282) | def saveall(self):
    method savemessage (line 336) | def savemessage(self, uid, content, flags, rtime, mtime=0, labels=set()):
    method savemessageflags (line 367) | def savemessageflags(self, uid, flags):
    method getmessageflags (line 374) | def getmessageflags(self, uid):
    method savemessagelabels (line 378) | def savemessagelabels(self, uid, labels, mtime=None):
    method savemessageslabelsbulk (line 389) | def savemessageslabelsbulk(self, labels):
    method addmessageslabels (line 400) | def addmessageslabels(self, uids, labels):
    method deletemessageslabels (line 410) | def deletemessageslabels(self, uids, labels):
    method getmessagelabels (line 420) | def getmessagelabels(self, uid):
    method savemessagesmtimebulk (line 424) | def savemessagesmtimebulk(self, mtimes):
    method getmessagemtime (line 433) | def getmessagemtime(self, uid):
    method deletemessage (line 438) | def deletemessage(self, uid):
    method deletemessages (line 445) | def deletemessages(self, uidlist):

FILE: offlineimap/folder/Maildir.py
  function _gettimeseq (line 47) | def _gettimeseq(date=None):
  class MaildirFolder (line 62) | class MaildirFolder(BaseFolder):
    method __init__ (line 63) | def __init__(self, root, name, sep, repository):
    method getfullname (line 91) | def getfullname(self):
    method get_uidvalidity (line 96) | def get_uidvalidity(self):
    method _iswithintime (line 103) | def _iswithintime(self, messagename, date):
    method _parse_filename (line 118) | def _parse_filename(self, filename):
    method _scanfolder (line 159) | def _scanfolder(self, min_date=None, min_uid=None):
    method quickchanged (line 239) | def quickchanged(self, statusfolder):
    method msglist_item_initializer (line 255) | def msglist_item_initializer(self, uid):
    method cachemessagelist (line 259) | def cachemessagelist(self, min_date=None, min_uid=None):
    method getmessage (line 267) | def getmessage(self, uid):
    method getmessagetime (line 280) | def getmessagetime(self, uid):
    method new_message_filename (line 285) | def new_message_filename(self, uid, flags=set(), date=None):
    method save_to_tmp_file (line 299) | def save_to_tmp_file(self, filename, content):
    method savemessage (line 348) | def savemessage(self, uid, content, flags, rtime):
    method getmessageflags (line 420) | def getmessageflags(self, uid):
    method savemessageflags (line 424) | def savemessageflags(self, uid, flags):
    method change_message_uid (line 467) | def change_message_uid(self, uid, new_uid):
    method deletemessage (line 495) | def deletemessage(self, uid):
    method migratefmd5 (line 517) | def migratefmd5(self, dryrun=False):

FILE: offlineimap/folder/UIDMaps.py
  class MappedIMAPFolder (line 29) | class MappedIMAPFolder(IMAPFolder):
    method __init__ (line 43) | def __init__(self, imapserver, name, repository, decode=True):
    method _getmapfilename (line 54) | def _getmapfilename(self):
    method _loadmaps (line 58) | def _loadmaps(self):
    method _savemaps (line 96) | def _savemaps(self):
    method _uidlist (line 120) | def _uidlist(self, mapping, items):
    method cachemessagelist (line 134) | def cachemessagelist(self, min_date=None, min_uid=None):
    method dropmessagelistcache (line 176) | def dropmessagelistcache(self):
    method uidexists (line 180) | def uidexists(self, ruid):
    method getmessageuidlist (line 188) | def getmessageuidlist(self):
    method getmessagecount (line 198) | def getmessagecount(self):
    method getmessagelist (line 208) | def getmessagelist(self):
    method getmessage (line 232) | def getmessage(self, uid):
    method savemessage (line 237) | def savemessage(self, uid, content, flags, rtime):
    method getmessageflags (line 285) | def getmessageflags(self, uid):
    method getmessagetime (line 289) | def getmessagetime(self, uid):
    method savemessageflags (line 293) | def savemessageflags(self, uid, flags):
    method addmessageflags (line 301) | def addmessageflags(self, uid, flags):
    method addmessagesflags (line 305) | def addmessagesflags(self, uidlist, flags):
    method change_message_uid (line 310) | def change_message_uid(self, ruid, new_ruid):
    method _mapped_delete (line 333) | def _mapped_delete(self, uidlist):
    method deletemessageflags (line 348) | def deletemessageflags(self, uid, flags):
    method deletemessagesflags (line 352) | def deletemessagesflags(self, uidlist, flags):
    method deletemessage (line 357) | def deletemessage(self, uid):
    method deletemessages (line 362) | def deletemessages(self, uidlist):

FILE: offlineimap/globals.py
  function set_options (line 10) | def set_options(source):

FILE: offlineimap/imaplibutil.py
  class UsefulIMAPMixIn (line 36) | class UsefulIMAPMixIn(object):
    method __getselectedfolder (line 37) | def __getselectedfolder(self):
    method select (line 42) | def select(self, mailbox='INBOX', readonly=False, force=False):
    method _mesg (line 75) | def _mesg(self, s, tn=None, secs=None):
    method open_socket (line 79) | def open_socket(self):
    method _open_socket_for_af (line 88) | def _open_socket_for_af(self, af):
  class IMAP4_Tunnel (line 117) | class IMAP4_Tunnel(UsefulIMAPMixIn, IMAP4):
    method __init__ (line 125) | def __init__(self, tunnelcmd, **kwargs):
    method open (line 131) | def open(self, host, port):
    method set_nonblocking (line 143) | def set_nonblocking(self, fd):
    method read (line 152) | def read(self, size):
    method send (line 166) | def send(self, data):
    method shutdown (line 172) | def shutdown(self):
  function new_mesg (line 178) | def new_mesg(self, s, tn=None, secs=None):
  class WrappedIMAP4_SSL (line 187) | class WrappedIMAP4_SSL(UsefulIMAPMixIn, IMAP4_SSL):
    method __init__ (line 190) | def __init__(self, *args, **kwargs):
    method open (line 204) | def open(self, host=None, port=None):
  class WrappedIMAP4 (line 227) | class WrappedIMAP4(UsefulIMAPMixIn, IMAP4):
    method __init__ (line 230) | def __init__(self, *args, **kwargs):
  function Internaldate2epoch (line 240) | def Internaldate2epoch(resp):

FILE: offlineimap/imapserver.py
  class IMAPServer (line 45) | class IMAPServer(object):
    method __init__ (line 55) | def __init__(self, repos):
    method _get_proxy (line 147) | def _get_proxy(self, proxysection, dfltsocket):
    method __getpassword (line 171) | def __getpassword(self):
    method __md5handler (line 186) | def __md5handler(self, response):
    method __loginauth (line 195) | def __loginauth(self, imapobj):
    method __plainhandler (line 201) | def __plainhandler(self, response):
    method __xoauth2handler (line 223) | def __xoauth2handler(self, response):
    method __gsshandler (line 282) | def __gsshandler(self, token):
    method __start_tls (line 327) | def __start_tls(self, imapobj):
    method __authn_gssapi (line 354) | def __authn_gssapi(self, imapobj):
    method __authn_cram_md5 (line 371) | def __authn_cram_md5(self, imapobj):
    method __authn_plain (line 375) | def __authn_plain(self, imapobj):
    method __authn_xoauth2 (line 379) | def __authn_xoauth2(self, imapobj):
    method __authn_login (line 387) | def __authn_login(self, imapobj):
    method __authn_helper (line 398) | def __authn_helper(self, imapobj):
    method __verifycert (line 470) | def __verifycert(self, cert, hostname):
    method acquireconnection (line 509) | def acquireconnection(self):
    method connectionwait (line 684) | def connectionwait(self):
    method close (line 697) | def close(self):
    method keepalive (line 715) | def keepalive(self, timeout, event):
    method releaseconnection (line 756) | def releaseconnection(self, connection, drop_conn=False):
  class IdleThread (line 776) | class IdleThread(object):
    method __init__ (line 777) | def __init__(self, parent, folder=None):
    method start (line 792) | def start(self):
    method stop (line 795) | def stop(self):
    method join (line 798) | def join(self):
    method noop (line 801) | def noop(self):
    method __dosync (line 821) | def __dosync(self):
    method __idle (line 838) | def __idle(self):

FILE: offlineimap/imaputil.py
  function __debug (line 34) | def __debug(*args):
  function dequote (line 40) | def dequote(s):
  function quote (line 52) | def quote(s):
  function flagsplit (line 62) | def flagsplit(s):
  function __options2hash (line 74) | def __options2hash(list):
  function flags2hash (line 88) | def flags2hash(flags):
  function imapsplit (line 96) | def imapsplit(imapstring):
  function flagsimap2maildir (line 196) | def flagsimap2maildir(flagstring):
  function flagsimap2keywords (line 206) | def flagsimap2keywords(flagstring):
  function flagsmaildir2imap (line 214) | def flagsmaildir2imap(maildirflaglist):
  function uid_sequence (line 223) | def uid_sequence(uidlist):
  function __split_quoted (line 255) | def __split_quoted(s):
  function format_labels_string (line 292) | def format_labels_string(header, labels):
  function parse_labels_string (line 310) | def parse_labels_string(header, labels_str):
  function labels_from_header (line 333) | def labels_from_header(header_name, header_value):
  function decode_mailbox_name (line 351) | def decode_mailbox_name(name):
  function IMAP_utf8 (line 381) | def IMAP_utf8(foldername):
  function utf8_IMAP (line 385) | def utf8_IMAP(foldername):
  function modified_base64 (line 391) | def modified_base64(s):
  function doB64 (line 395) | def doB64(_in, r):
  function encoder (line 400) | def encoder(s):
  function modified_unbase64 (line 417) | def modified_unbase64(s):
  function decoder (line 421) | def decoder(s):
  class StreamReader (line 443) | class StreamReader(codecs.StreamReader):
    method decode (line 444) | def decode(self, s, errors='strict'):
  class StreamWriter (line 447) | class StreamWriter(codecs.StreamWriter):
    method decode (line 448) | def decode(self, s, errors='strict'):
  function imap4_utf_7 (line 451) | def imap4_utf_7(name):

FILE: offlineimap/init.py
  function syncitall (line 46) | def syncitall(list_accounts, config):
  class OfflineImap (line 66) | class OfflineImap(object):
    method get_env_info (line 75) | def get_env_info(self):
    method run (line 86) | def run(self):
    method __parse_cmd_options (line 103) | def __parse_cmd_options(self):
    method __dumpstacks (line 359) | def __dumpstacks(self, context=1, sighandler_deep=2):
    method _get_activeaccounts (line 394) | def _get_activeaccounts(self, options):
    method __sync (line 420) | def __sync(self, options):
    method __sync_singlethreaded (line 492) | def __sync_singlethreaded(self, list_accounts, profiledir):
    method __serverdiagnostics (line 519) | def __serverdiagnostics(self, options):
    method __deletefolder (line 525) | def __deletefolder(self, options):
    method __migratefmd5 (line 533) | def __migratefmd5(self, options):

FILE: offlineimap/localeval.py
  class LocalEval (line 25) | class LocalEval(object):
    method __init__ (line 28) | def __init__(self, path=None):
    method eval (line 43) | def eval(self, text, namespace=None):

FILE: offlineimap/mbnames.py
  function add (line 36) | def add(accountname, folder_root, foldername):
  function init (line 46) | def init(conf, ui, dry_run):
  function prune (line 53) | def prune(accounts):
  function write (line 62) | def write():
  function writeIntermediateFile (line 74) | def writeIntermediateFile(accountname):
  class _IntermediateMbnames (line 86) | class _IntermediateMbnames(object):
    method __init__ (line 89) | def __init__(self, accountname, folder_root, mbnamesdir, folderfilter,
    method add (line 100) | def add(self, foldername):
    method get_folder_root (line 104) | def get_folder_root(self):
    method write (line 107) | def write(self):
  class _Mbnames (line 128) | class _Mbnames(object):
    method __init__ (line 129) | def __init__(self, config, ui, dry_run):
    method _iterIntermediateFiles (line 179) | def _iterIntermediateFiles(self):
    method _removeIntermediateFile (line 185) | def _removeIntermediateFile(self, path):
    method addAccountFolder (line 192) | def addAccountFolder(self, accountname, folder_root, foldername):
    method get_incremental (line 207) | def get_incremental(self):
    method is_enabled (line 214) | def is_enabled(self):
    method prune (line 217) | def prune(self, accounts):
    method pruneAll (line 229) | def pruneAll(self):
    method write (line 233) | def write(self):
    method writeIntermediateFile (line 272) | def writeIntermediateFile(self, accountname):

FILE: offlineimap/repository/Base.py
  class BaseRepository (line 28) | class BaseRepository(CustomConfig.ConfigHelperMixin):
    method __init__ (line 29) | def __init__(self, reposname, account):
    method restore_atime (line 66) | def restore_atime(self):
    method connect (line 75) | def connect(self):
    method holdordropconnections (line 85) | def holdordropconnections(self):
    method dropconnections (line 88) | def dropconnections(self):
    method getaccount (line 91) | def getaccount(self):
    method getname (line 94) | def getname(self):
    method __str__ (line 97) | def __str__(self):
    method accountname (line 101) | def accountname(self):
    method getuiddir (line 106) | def getuiddir(self):
    method getmapdir (line 109) | def getmapdir(self):
    method getsection (line 113) | def getsection(self):
    method getconfig (line 117) | def getconfig(self):
    method readonly (line 121) | def readonly(self):
    method getlocaleval (line 126) | def getlocaleval(self):
    method getfolders (line 129) | def getfolders(self):
    method forgetfolders (line 134) | def forgetfolders(self):
    method getsep (line 140) | def getsep(self):
    method getkeywordmap (line 143) | def getkeywordmap(self):
    method should_sync_folder (line 146) | def should_sync_folder(self, fname):
    method should_create_folders (line 151) | def should_create_folders(self):
    method makefolder (line 160) | def makefolder(self, foldername):
    method deletefolder (line 165) | def deletefolder(self, foldername):
    method getfolder (line 168) | def getfolder(self, foldername, decode=True):
    method sync_folder_structure (line 177) | def sync_folder_structure(self, local_repo, status_repo):
    method startkeepalive (line 285) | def startkeepalive(self):
    method stopkeepalive (line 290) | def stopkeepalive(self):
    method getlocalroot (line 296) | def getlocalroot(self):

FILE: offlineimap/repository/Gmail.py
  class GmailRepository (line 23) | class GmailRepository(IMAPRepository):
    method __init__ (line 31) | def __init__(self, reposname, account):
    method gethost (line 35) | def gethost(self):
    method getoauth2_request_url (line 49) | def getoauth2_request_url(self):
    method getport (line 64) | def getport(self):
    method getssl (line 77) | def getssl(self):
    method getpreauthtunnel (line 88) | def getpreauthtunnel(self):
    method getfolder (line 91) | def getfolder(self, foldername, decode=True):
    method getfoldertype (line 95) | def getfoldertype(self):
    method gettrashfolder (line 98) | def gettrashfolder(self, foldername):
    method getspamfolder (line 102) | def getspamfolder(self):

FILE: offlineimap/repository/GmailMaildir.py
  class GmailMaildirRepository (line 22) | class GmailMaildirRepository(MaildirRepository):
    method __init__ (line 23) | def __init__(self, reposname, account):
    method getfoldertype (line 29) | def getfoldertype(self):

FILE: offlineimap/repository/IMAP.py
  class IMAPRepository (line 34) | class IMAPRepository(BaseRepository):
    method __init__ (line 35) | def __init__(self, reposname, account):
    method startkeepalive (line 58) | def startkeepalive(self):
    method stopkeepalive (line 69) | def stopkeepalive(self):
    method holdordropconnections (line 77) | def holdordropconnections(self):
    method dropconnections (line 81) | def dropconnections(self):
    method get_copy_ignore_UIDs (line 84) | def get_copy_ignore_UIDs(self, foldername):
    method getholdconnectionopen (line 97) | def getholdconnectionopen(self):
    method getkeepalive (line 102) | def getkeepalive(self):
    method getsep (line 108) | def getsep(self):
    method gethost (line 118) | def gethost(self):
    method get_remote_identity (line 150) | def get_remote_identity(self):
    method get_auth_mechanisms (line 160) | def get_auth_mechanisms(self):
    method getuser (line 180) | def getuser(self):
    method getport (line 214) | def getport(self):
    method getipv6 (line 224) | def getipv6(self):
    method getssl (line 227) | def getssl(self):
    method getsslclientcert (line 230) | def getsslclientcert(self):
    method getsslclientkey (line 234) | def getsslclientkey(self):
    method getsslcacertfile (line 238) | def getsslcacertfile(self):
    method gettlslevel (line 280) | def gettlslevel(self):
    method getsslversion (line 283) | def getsslversion(self):
    method getstarttls (line 286) | def getstarttls(self):
    method get_ssl_fingerprint (line 289) | def get_ssl_fingerprint(self):
    method setoauth2_request_url (line 298) | def setoauth2_request_url(self, url):
    method getoauth2_request_url (line 301) | def getoauth2_request_url(self):
    method getoauth2_refresh_token (line 308) | def getoauth2_refresh_token(self):
    method getoauth2_access_token (line 318) | def getoauth2_access_token(self):
    method getoauth2_client_id (line 328) | def getoauth2_client_id(self):
    method getoauth2_client_secret (line 338) | def getoauth2_client_secret(self):
    method getpreauthtunnel (line 348) | def getpreauthtunnel(self):
    method gettransporttunnel (line 351) | def gettransporttunnel(self):
    method getreference (line 354) | def getreference(self):
    method getdecodefoldernames (line 357) | def getdecodefoldernames(self):
    method getidlefolders (line 360) | def getidlefolders(self):
    method getmaxconnections (line 367) | def getmaxconnections(self):
    method getexpunge (line 372) | def getexpunge(self):
    method getpassword (line 375) | def getpassword(self):
    method getfolder (line 431) | def getfolder(self, foldername, decode=True):
    method getfoldertype (line 436) | def getfoldertype(self):
    method connect (line 439) | def connect(self):
    method forgetfolders (line 443) | def forgetfolders(self):
    method getfolders (line 446) | def getfolders(self):
    method deletefolder (line 524) | def deletefolder(self, foldername):
    method makefolder (line 539) | def makefolder(self, foldername):
    method makefolder_single (line 567) | def makefolder_single(self, foldername):
  class MappedIMAPRepository (line 584) | class MappedIMAPRepository(IMAPRepository):
    method getfoldertype (line 585) | def getfoldertype(self):

FILE: offlineimap/repository/LocalStatus.py
  class LocalStatusRepository (line 26) | class LocalStatusRepository(BaseRepository):
    method __init__ (line 27) | def __init__(self, reposname, account):
    method _instanciatefolder (line 56) | def _instanciatefolder(self, foldername):
    method setup_backend (line 59) | def setup_backend(self, backend):
    method import_other_backend (line 65) | def import_other_backend(self, folder):
    method getsep (line 86) | def getsep(self):
    method makefolder (line 89) | def makefolder(self, foldername):
    method getfolder (line 108) | def getfolder(self, foldername):
    method getfolders (line 125) | def getfolders(self):
    method forgetfolders (line 135) | def forgetfolders(self):

FILE: offlineimap/repository/Maildir.py
  class MaildirRepository (line 25) | class MaildirRepository(BaseRepository):
    method __init__ (line 26) | def __init__(self, reposname, account):
    method _append_folder_atimes (line 50) | def _append_folder_atimes(self, foldername):
    method restore_atime (line 59) | def restore_atime(self):
    method getlocalroot (line 73) | def getlocalroot(self):
    method debug (line 77) | def debug(self, msg):
    method getsep (line 80) | def getsep(self):
    method getkeywordmap (line 83) | def getkeywordmap(self):
    method makefolder (line 86) | def makefolder(self, foldername):
    method deletefolder (line 133) | def deletefolder(self, foldername):
    method getfolder (line 136) | def getfolder(self, foldername):
    method _getfolders_scandir (line 151) | def _getfolders_scandir(self, root, extension=None):
    method getfolders (line 207) | def getfolders(self):
    method getfoldertype (line 212) | def getfoldertype(self):
    method forgetfolders (line 215) | def forgetfolders(self):

FILE: offlineimap/repository/__init__.py
  class Repository (line 33) | class Repository(object):
    method __new__ (line 38) | def __new__(cls, account, reqtype):
    method __init__ (line 86) | def __init__(self, account, reqtype):

FILE: offlineimap/threadutil.py
  function semaphorereset (line 34) | def semaphorereset(semaphore, originalstate):
  class accountThreads (line 44) | class accountThreads(object):
    method __init__ (line 48) | def __init__(self):
    method add (line 52) | def add(self, thread):
    method remove (line 56) | def remove(self, thread):
    method pop (line 60) | def pop(self):
    method wait (line 66) | def wait(self):
  function monitor (line 80) | def monitor():
  class ExitNotifyThread (line 134) | class ExitNotifyThread(Thread):
    method __init__ (line 146) | def __init__(self, *args, **kwargs):
    method run (line 155) | def run(self):
    method set_exit_exception (line 168) | def set_exit_exception(self, exc, st=None):
    method exit_exception (line 176) | def exit_exception(self):
    method exit_stacktrace (line 184) | def exit_stacktrace(self):
  function initInstanceLimit (line 196) | def initInstanceLimit(limitNamespace, instancemax):
  class InstanceLimitedThread (line 208) | class InstanceLimitedThread(ExitNotifyThread):
    method __init__ (line 209) | def __init__(self, limitNamespace, *args, **kwargs):
    method start (line 213) | def start(self):
    method run (line 220) | def run(self):

FILE: offlineimap/ui/Curses.py
  class CursesUtil (line 31) | class CursesUtil:
    method __init__ (line 32) | def __init__(self, *args, **kwargs):
    method curses_colorpair (line 41) | def curses_colorpair(self, col_name):
    method init_colorpairs (line 46) | def init_colorpairs(self):
    method lock (line 70) | def lock(self, block=True):
    method unlock (line 79) | def unlock(self):
    method exec_locked (line 89) | def exec_locked(self, target, *args, **kwargs):
    method refresh (line 98) | def refresh(self):
    method isactive (line 104) | def isactive(self):
  class CursesAccountFrame (line 108) | class CursesAccountFrame:
    method __init__ (line 118) | def __init__(self, ui, account):
    method drawleadstr (line 132) | def drawleadstr(self, secs = 0):
    method setwindow (line 149) | def setwindow(self, curses_win, acc_num):
    method get_new_tframe (line 164) | def get_new_tframe(self):
    method sleeping (line 174) | def sleeping(self, sleepsecs, remainingsecs):
    method syncnow (line 184) | def syncnow(self):
  class CursesThreadFrame (line 194) | class CursesThreadFrame:
    method __init__ (line 197) | def __init__(self, ui, acc_win, x, y):
    method setcolor (line 208) | def setcolor(self, color, modifier=0):
    method display (line 218) | def display(self):
    method update (line 228) | def update(self, acc_win, x, y):
    method std_color (line 236) | def std_color(self):
  class InputHandler (line 240) | class InputHandler(ExitNotifyThread):
    method __init__ (line 247) | def __init__(self, ui):
    method get_next_char (line 257) | def get_next_char(self):
    method run (line 270) | def run(self):
    method set_char_hdlr (line 277) | def set_char_hdlr(self, callback):
    method input_acquire (line 294) | def input_acquire(self):
    method input_release (line 303) | def input_release(self):
  class CursesLogHandler (line 310) | class CursesLogHandler(logging.StreamHandler):
    method emit (line 313) | def emit(self, record):
  class Blinkenlights (line 333) | class Blinkenlights(UIBase, CursesUtil):
    method __init__ (line 346) | def __init__(self, *args, **kwargs):
    method setup_consolehandler (line 351) | def setup_consolehandler(self):
    method isusable (line 369) | def isusable(s):
    method init_banner (line 389) | def init_banner(self):
    method acct (line 425) | def acct(self, *args):
    method connecting (line 431) | def connecting(self, *args):
    method syncfolders (line 435) | def syncfolders(self, *args):
    method syncingfolder (line 439) | def syncingfolder(self, *args):
    method skippingfolder (line 443) | def skippingfolder(self, *args):
    method loadmessagelist (line 447) | def loadmessagelist(self, *args):
    method syncingmessages (line 451) | def syncingmessages(self, *args):
    method ignorecopyingmessage (line 455) | def ignorecopyingmessage(self, *args):
    method copyingmessage (line 459) | def copyingmessage(self, *args):
    method deletingmessages (line 463) | def deletingmessages(self, *args):
    method addingflags (line 467) | def addingflags(self, *args):
    method deletingflags (line 471) | def deletingflags(self, *args):
    method callhook (line 475) | def callhook(self, *args):
    method warn (line 480) | def warn(self, msg, minor=0):
    method threadExited (line 484) | def threadExited(self, thread):
    method gettf (line 494) | def gettf(self):
    method on_keypressed (line 519) | def on_keypressed(self, key):
    method sleep (line 541) | def sleep(self, sleepsecs, account):
    method sleeping (line 546) | def sleeping(self, sleepsecs, remainingsecs):
    method resizeterm (line 553) | def resizeterm(self):
    method mainException (line 558) | def mainException(self):
    method getpass (line 561) | def getpass(self, username, config, errmsg=None):
    method setupwindows (line 579) | def setupwindows(self, resize=False):
    method draw_bannerwin (line 614) | def draw_bannerwin(self):
    method draw_logwin (line 631) | def draw_logwin(self):
    method getaccountframe (line 643) | def getaccountframe(self, acc_name):
    method terminate (line 656) | def terminate(self, *args, **kwargs):
    method threadException (line 670) | def threadException(self, thread):

FILE: offlineimap/ui/Machine.py
  class MachineLogFormatter (line 30) | class MachineLogFormatter(logging.Formatter):
    method format (line 33) | def format(s, record):
  class MachineUI (line 54) | class MachineUI(UIBase):
    method __init__ (line 55) | def __init__(s, config, loglevel=logging.INFO):
    method _printData (line 67) | def _printData(s, handler, command, msg):
    method _msg (line 76) | def _msg(s, msg):
    method warn (line 79) | def warn(s, msg, minor=0):
    method registerthread (line 83) | def registerthread(s, account):
    method unregisterthread (line 87) | def unregisterthread(s, thread):
    method debugging (line 91) | def debugging(s, debugtype):
    method acct (line 94) | def acct(s, accountname):
    method acctdone (line 97) | def acctdone(s, accountname):
    method validityproblem (line 100) | def validityproblem(s, folder):
    method connecting (line 105) | def connecting(s, reposname, hostname, port):
    method syncfolders (line 109) | def syncfolders(s, srcrepos, destrepos):
    method syncingfolder (line 113) | def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
    method loadmessagelist (line 118) | def loadmessagelist(s, repos, folder):
    method messagelistloaded (line 122) | def messagelistloaded(s, repos, folder, count):
    method syncingmessages (line 126) | def syncingmessages(s, sr, sf, dr, df):
    method ignorecopyingmessage (line 131) | def ignorecopyingmessage(s, uid, srcfolder, destfolder):
    method copyingmessage (line 136) | def copyingmessage(s, uid, num, num_to_copy, srcfolder, destfolder):
    method folderlist (line 141) | def folderlist(s, list):
    method uidlist (line 144) | def uidlist(s, list):
    method deletingmessages (line 147) | def deletingmessages(s, uidlist, destlist):
    method addingflags (line 151) | def addingflags(s, uidlist, flags, dest):
    method deletingflags (line 156) | def deletingflags(s, uidlist, flags, dest):
    method threadException (line 161) | def threadException(s, thread):
    method terminate (line 167) | def terminate(s, exitstatus=0, errortitle='', errormsg=''):
    method mainException (line 171) | def mainException(s):
    method threadExited (line 174) | def threadExited(s, thread):
    method sleeping (line 178) | def sleeping(s, sleepsecs, remainingsecs):
    method getpass (line 185) | def getpass(s, username, config, errmsg=None):
    method init_banner (line 198) | def init_banner(s):
    method callhook (line 202) | def callhook(s, msg):

FILE: offlineimap/ui/Noninteractive.py
  class Basic (line 23) | class Basic(UIBase):
    method __init__ (line 26) | def __init__(self, config, loglevel = logging.INFO):
  class Quiet (line 29) | class Quiet(UIBase):
    method __init__ (line 31) | def __init__(self, config, loglevel = logging.WARNING):
  class Syslog (line 34) | class Syslog(UIBase):
    method __init__ (line 36) | def __init__(self, config, loglevel = logging.INFO):
    method setup_consolehandler (line 39) | def setup_consolehandler(self):
    method setup_sysloghandler (line 50) | def setup_sysloghandler(self):

FILE: offlineimap/ui/TTY.py
  class TTYFormatter (line 27) | class TTYFormatter(logging.Formatter):
    method __init__ (line 30) | def __init__(self, *args, **kwargs):
    method format (line 35) | def format(self, record):
  class TTYUI (line 54) | class TTYUI(UIBase):
    method setup_consolehandler (line 55) | def setup_consolehandler(self):
    method isusable (line 74) | def isusable(self):
    method getpass (line 79) | def getpass(self, username, config, errmsg=None):
    method mainException (line 90) | def mainException(self):
    method sleeping (line 98) | def sleeping(self, sleepsecs, remainingsecs):

FILE: offlineimap/ui/UIBase.py
  function setglobalui (line 40) | def setglobalui(newui):
  function getglobalui (line 46) | def getglobalui():
  class UIBase (line 53) | class UIBase(object):
    method __init__ (line 54) | def __init__(self, config, loglevel=logging.INFO):
    method setup_consolehandler (line 79) | def setup_consolehandler(self):
    method setup_sysloghandler (line 96) | def setup_sysloghandler(self):
    method setlogfile (line 107) | def setlogfile(self, logfile):
    method _msg (line 125) | def _msg(self, msg):
    method info (line 131) | def info(self, msg):
    method warn (line 136) | def warn(self, msg, minor=0):
    method error (line 139) | def error(self, exc, exc_traceback=None, msg=None):
    method registerthread (line 177) | def registerthread(self, account):
    method unregisterthread (line 191) | def unregisterthread(self, thr):
    method getthreadaccount (line 198) | def getthreadaccount(self, thr=None):
    method debug (line 209) | def debug(self, debugtype, msg):
    method add_debug (line 225) | def add_debug(self, debugtype):
    method debugging (line 234) | def debugging(self, debugtype):
    method invaliddebug (line 239) | def invaliddebug(self, debugtype):
    method getnicename (line 242) | def getnicename(self, object):
    method isusable (line 251) | def isusable(self):
    method getpass (line 260) | def getpass(self, username, config, errmsg = None):
    method folderlist (line 264) | def folderlist(self, folder_list):
    method msgtoreadonly (line 269) | def msgtoreadonly(self, destfolder, uid, content, flags):
    method flagstoreadonly (line 278) | def flagstoreadonly(self, destfolder, uidlist, flags):
    method labelstoreadonly (line 287) | def labelstoreadonly(self, destfolder, uidlist, labels):
    method deletereadonly (line 296) | def deletereadonly(self, destfolder, uidlist):
    method init_banner (line 307) | def init_banner(self):
    method connecting (line 314) | def connecting(self, reposname, hostname, port):
    method acct (line 326) | def acct(self, account):
    method acctdone (line 332) | def acctdone(self, account):
    method syncfolders (line 340) | def syncfolders(self, src_repo, dst_repo):
    method makefolder (line 348) | def makefolder(self, repo, foldername):
    method syncingfolder (line 355) | def syncingfolder(self, srcrepos, srcfolder, destrepos, destfolder):
    method skippingfolder (line 362) | def skippingfolder(self, folder):
    method validityproblem (line 366) | def validityproblem(self, folder):
    method loadmessagelist (line 374) | def loadmessagelist(self, repos, folder):
    method messagelistloaded (line 379) | def messagelistloaded(self, repos, folder, count):
    method syncingmessages (line 385) | def syncingmessages(self, sr, srcfolder, dr, dstfolder):
    method ignorecopyingmessage (line 390) | def ignorecopyingmessage(self, uid, src, destfolder):
    method copyingmessage (line 396) | def copyingmessage(self, uid, num, num_to_copy, src, destfolder):
    method deletingmessages (line 403) | def deletingmessages(self, uidlist, destlist):
    method addingflags (line 410) | def addingflags(self, uidlist, flags, dest):
    method deletingflags (line 414) | def deletingflags(self, uidlist, flags, dest):
    method addinglabels (line 418) | def addinglabels(self, uidlist, label, dest):
    method deletinglabels (line 422) | def deletinglabels(self, uidlist, label, dest):
    method settinglabels (line 426) | def settinglabels(self, uid, num, num_to_set, labels, dest):
    method collectingdata (line 430) | def collectingdata(self, uidlist, source):
    method serverdiagnostics (line 437) | def serverdiagnostics(self, repository, type):
    method savemessage (line 488) | def savemessage(self, debugtype, uid, flags, folder):
    method getThreadDebugLog (line 496) | def getThreadDebugLog(self, thread):
    method delThreadDebugLog (line 506) | def delThreadDebugLog(self, thread):
    method getThreadExceptionString (line 510) | def getThreadExceptionString(self, thread):
    method threadException (line 516) | def threadException(self, thread):
    method terminate (line 524) | def terminate(self, exitstatus = 0, errortitle = None, errormsg = None):
    method threadExited (line 552) | def threadExited(self, thread):
    method callhook (line 562) | def callhook(self, msg):
    method sleep (line 570) | def sleep(self, sleepsecs, account):
    method sleeping (line 589) | def sleeping(self, sleepsecs, remainingsecs):

FILE: offlineimap/ui/debuglock.py
  class DebuggingLock (line 23) | class DebuggingLock:
    method __init__ (line 24) | def __init__(self, name):
    method acquire (line 28) | def acquire(self, blocking = 1):
    method release (line 34) | def release(self):
    method logmsg (line 38) | def logmsg(self, msg):
    method print_tb (line 44) | def print_tb(self, msg):

FILE: offlineimap/utils/const.py
  class ConstProxy (line 8) | class ConstProxy(object):
    method __init__ (line 12) | def __init__(self):
    method __getattr__ (line 16) | def __getattr__(self, name):
    method __setattr__ (line 23) | def __setattr__(self, name, value):
    method __delattr__ (line 28) | def __delattr__(self, name):
    method set_source (line 33) | def set_source(self, source):

FILE: offlineimap/utils/distro.py
  function get_os_name (line 35) | def get_os_name():
  function get_os_sslcertfile_searchpath (line 57) | def get_os_sslcertfile_searchpath():
  function get_os_sslcertfile (line 78) | def get_os_sslcertfile():

FILE: offlineimap/utils/stacktrace.py
  function dump (line 10) | def dump(out):

FILE: setup.py
  class TestCommand (line 30) | class TestCommand(Command):
    method initialize_options (line 38) | def initialize_options(self):
    method finalize_options (line 41) | def finalize_options(self):
    method run (line 44) | def run(self):

FILE: test/OLItest/TestRunner.py
  class OLITestLib (line 32) | class OLITestLib():
    method __init__ (line 39) | def __init__(self, cred_file = None, cmd='offlineimap'):
    method create_test_dir (line 52) | def create_test_dir(cls, suffix=''):
    method get_default_config (line 68) | def get_default_config(cls):
    method write_config_file (line 84) | def write_config_file(cls, config=None):
    method delete_test_dir (line 101) | def delete_test_dir(cls):
    method run_OLI (line 109) | def run_OLI(cls):
    method delete_remote_testfolders (line 124) | def delete_remote_testfolders(cls, reponame=None):
    method create_maildir (line 176) | def create_maildir(cls, folder):
    method delete_maildir (line 190) | def delete_maildir(cls, folder):
    method create_mail (line 199) | def create_mail(cls, folder, mailfile=None, content=None):
    method count_maildir_mails (line 220) | def count_maildir_mails(cls, folder):
    method get_maildir_uids (line 241) | def get_maildir_uids(cls, folder):

FILE: test/tests/test_00_globals.py
  class Opt (line 7) | class Opt:
    method __init__ (line 8) | def __init__(self):
  class TestOfflineimapGlobals (line 14) | class TestOfflineimapGlobals(unittest.TestCase):
    method setUpClass (line 17) | def setUpClass(klass):
    method test_initial_state (line 21) | def test_initial_state(self):
    method test_object_changes (line 26) | def test_object_changes(self):
    method test_modification (line 32) | def test_modification(self):
    method test_deletion (line 36) | def test_deletion(self):
    method test_nonexistent_key (line 40) | def test_nonexistent_key(self):
    method test_double_init (line 44) | def test_double_init(self):

FILE: test/tests/test_00_imaputil.py
  function setUpModule (line 30) | def setUpModule():
  function tearDownModule (line 34) | def tearDownModule():
  class TestInternalFunctions (line 46) | class TestInternalFunctions(unittest.TestCase):
    method setUpClass (line 52) | def setUpClass(cls):
    method test_01_imapsplit (line 57) | def test_01_imapsplit(self):
    method test_02_flagsplit (line 65) | def test_02_flagsplit(self):
    method test_04_flags2hash (line 73) | def test_04_flags2hash(self):
    method test_05_flagsimap2maildir (line 78) | def test_05_flagsimap2maildir(self):
    method test_06_flagsmaildir2imap (line 83) | def test_06_flagsmaildir2imap(self):
    method test_07_uid_sequence (line 91) | def test_07_uid_sequence(self):

FILE: test/tests/test_01_basic.py
  function setUpModule (line 28) | def setUpModule():
  function tearDownModule (line 32) | def tearDownModule():
  class TestBasicFunctions (line 43) | class TestBasicFunctions(unittest.TestCase):
    method setUp (line 44) | def setUp(self):
    method tearDown (line 47) | def tearDown(self):
    method test_01_olistartup (line 50) | def test_01_olistartup(self):
    method test_02_createdir (line 63) | def test_02_createdir(self):
    method test_03_createdir_quote (line 74) | def test_03_createdir_quote(self):
    method test_04_nametransmismatch (line 90) | def test_04_nametransmismatch(self):
    method test_05_createmail (line 111) | def test_05_createmail(self):
    method test_06_createfolders (line 134) | def test_06_createfolders(self):

FILE: test/tests/test_02_MappedIMAP.py
  function setUpModule (line 28) | def setUpModule():
  function tearDownModule (line 32) | def tearDownModule():
  class TestBasicFunctions (line 43) | class TestBasicFunctions(unittest.TestCase):
    method test_01_MappedImap (line 58) | def test_01_MappedImap(self):
Condensed preview — 171 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,838K chars).
[
  {
    "path": ".coveragerc",
    "chars": 219,
    "preview": "[run]\nbranch = True\nsource = offlineimap\n\n[report]\nexclude_lines =\n    if self.debug:\n    pragma: no cover\n    raise Not"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 739,
    "preview": "# This is a comment.\n# Each line is a file pattern followed by one or more owners.\n\n# These owners will be the default o"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 388,
    "preview": "\n#### General informations\n\n- system/distribution (with version): \n- offlineimap version (`offlineimap -V`): \n- Python v"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 537,
    "preview": "> This v1.1 template stands in `.github/`.\n\n### This PR\n\n> Add character x `[x]`.\n\n- [ ] I've read the [DCO](http://www."
  },
  {
    "path": ".gitignore",
    "chars": 228,
    "preview": "# Backups.\n.*.swp\n.*.swo\n*~\n# websites.\n/website/\n/wiki/\n# Generated files.\n*.html\n*.css\n/docs/dev-doc/\n/build/\n*.pyc\nof"
  },
  {
    "path": ".travis.yml",
    "chars": 3125,
    "preview": "language: python\npython:\n- '2.7'\nnotifications:\n  webhooks:\n    urls:\n      - https://webhooks.gitter.im/e/975e807e0314c"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 401,
    "preview": "\n# Realistic Code of Conduct\n\n1. We mostly care about making our softwares better.\n\n2. Everybody is free to decide how t"
  },
  {
    "path": "CONTRIBUTING.rst",
    "chars": 4390,
    "preview": ".. -*- coding: utf-8 -*-\n.. vim: spelllang=en ts=2 expandtab:\n\n.. _OfflineIMAP: https://github.com/OfflineIMAP/offlineim"
  },
  {
    "path": "COPYING",
    "chars": 19351,
    "preview": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public Li"
  },
  {
    "path": "Changelog.maint.md",
    "chars": 964,
    "preview": "---\nlayout: page\ntitle: Changelog of the stable branch\n---\n\n* The following excerpt is only usefull when rendered in the"
  },
  {
    "path": "Changelog.md",
    "chars": 88238,
    "preview": "---\nlayout: page\ntitle: Changelog of mainline\n---\n\n<!--\nNote to mainainers:\n* You should not edit this file manually; pr"
  },
  {
    "path": "MAINTAINERS.rst",
    "chars": 1714,
    "preview": ".. -*- coding: utf-8 -*-\n\nContacts\n========\n\n- Abdó Roig-Maranges\n  - email: abdo.roig at gmail.com\n  - github: aroig\n\n-"
  },
  {
    "path": "MANIFEST.in",
    "chars": 379,
    "preview": "global-exclude .gitignore .git *.bak *.orig *.rej\ninclude setup.py\ninclude COPYING\ninclude Changelog*\ninclude MAINTAINER"
  },
  {
    "path": "Makefile",
    "chars": 2312,
    "preview": "# Copyright (C) 2002 - 2018 John Goerzen & contributors.\n#\n#    This program is free software; you can redistribute it a"
  },
  {
    "path": "README.md",
    "chars": 4638,
    "preview": "Upstream status (`master` branch):\n[![OfflineIMAP build status on Travis-CI.org](https://travis-ci.org/OfflineIMAP/offli"
  },
  {
    "path": "TODO.rst",
    "chars": 4333,
    "preview": ".. vim: spelllang=en ts=2 expandtab :\n\n.. _coding style: https://github.com/OfflineIMAP/offlineimap/blob/next/docs/Codin"
  },
  {
    "path": "bin/offlineimap",
    "chars": 918,
    "preview": "#!/usr/bin/env python2\n# Startup from system-wide installation\n# Copyright (C) 2002-2018 John Goerzen & contributors\n#\n#"
  },
  {
    "path": "contrib/README.md",
    "chars": 323,
    "preview": "\nREADME\n======\n\n**This \"./contrib\" directory is where users share their own scripts and tools.**\n\nEverything here is sub"
  },
  {
    "path": "contrib/helpers.py",
    "chars": 8689,
    "preview": "\"\"\"\n\nPut into Public Domain, by Nicolas Sebrecht.\n\nHelpers for maintenance scripts.\n\n\"\"\"\n\nfrom os import chdir, makedirs"
  },
  {
    "path": "contrib/internet-urllib3.py",
    "chars": 461,
    "preview": "#!/usr/bin/env python\n\nimport urllib3\nimport certifi\n\ndef isInternetConnected(url=\"www.ietf.org\"):\n  result = False\n  ht"
  },
  {
    "path": "contrib/release.py",
    "chars": 13842,
    "preview": "#!/usr/bin/python3\n\n\"\"\"\n\nPut into Public Domain, by Nicolas Sebrecht.\n\nMake a new release.\n\n\"\"\"\n\n#TODO: announce: cc lis"
  },
  {
    "path": "contrib/release.sh",
    "chars": 10685,
    "preview": "#!/bin/sh\n#\n# Put into Public Domain, by Nicolas Sebrecht\n#\n# Create new releases in OfflineIMAP.\n\n# TODO: https://devel"
  },
  {
    "path": "contrib/store-pw-with-gpg/README.md",
    "chars": 1298,
    "preview": "# gpg-offlineimap\n\nPython bindings for offlineimap to use gpg instead of storing cleartext passwords\n\nAuthor: Lorenzo G."
  },
  {
    "path": "contrib/store-pw-with-gpg/gpg-pw.py",
    "chars": 2884,
    "preview": "#!/usr/bin/python\n# Originally taken from: http://stevelosh.com/blog/2012/10/the-homely-mutt/\n# by Steve Losh\n# Modified"
  },
  {
    "path": "contrib/store-pw-with-gpg/offlineimaprc.sample",
    "chars": 2241,
    "preview": "[general]\n# GPG quirks, leave unconfigured\nui = ttyui\n# you can use any name as long as it matches the 'account1, 'accou"
  },
  {
    "path": "contrib/store-pw-with-gpg/passwords-gmail.txt",
    "chars": 58,
    "preview": "account1@gmail.com password1\naccount2@gmail.com password2\n"
  },
  {
    "path": "contrib/systemd/README.md",
    "chars": 1031,
    "preview": "---\nlayout: page\ntitle: Integrating OfflineIMAP into systemd\nauthor: Ben Boeckel\ndate: 2015-03-22\ncontributors: Abdo Roi"
  },
  {
    "path": "contrib/systemd/offlineimap-oneshot.service",
    "chars": 278,
    "preview": "[Unit]\nDescription=Offlineimap Service (oneshot)\nDocumentation=man:offlineimap(1)\n\n[Service]\nType=oneshot\nExecStart=/usr"
  },
  {
    "path": "contrib/systemd/offlineimap-oneshot.timer",
    "chars": 122,
    "preview": "[Unit]\nDescription=Offlineimap Query Timer\n\n[Timer]\nOnBootSec=1m\nOnUnitInactiveSec=15m\n\n[Install]\nWantedBy=default.targe"
  },
  {
    "path": "contrib/systemd/offlineimap-oneshot@.service",
    "chars": 302,
    "preview": "[Unit]\nDescription=Offlineimap Service for account %i (oneshot)\nDocumentation=man:offlineimap(1)\n\n[Service]\nType=oneshot"
  },
  {
    "path": "contrib/systemd/offlineimap-oneshot@.timer",
    "chars": 137,
    "preview": "[Unit]\nDescription=Offlineimap Query Timer for account %i\n\n[Timer]\nOnBootSec=1m\nOnUnitInactiveSec=15m\n\n[Install]\nWantedB"
  },
  {
    "path": "contrib/systemd/offlineimap.service",
    "chars": 191,
    "preview": "[Unit]\nDescription=Offlineimap Service\nDocumentation=man:offlineimap(1)\n\n[Service]\nExecStart=/usr/bin/offlineimap -u bas"
  },
  {
    "path": "contrib/systemd/offlineimap@.service",
    "chars": 212,
    "preview": "[Unit]\nDescription=Offlineimap Service for account %i\nDocumentation=man:offlineimap(1)\n\n[Service]\nExecStart=/usr/bin/off"
  },
  {
    "path": "contrib/tested-by.py",
    "chars": 4056,
    "preview": "#!/usr/bin/python3\n\n\"\"\"\n\nPut into Public Domain, by Nicolas Sebrecht.\n\nManage the feedbacks of the testers for the relea"
  },
  {
    "path": "contrib/upcoming.py",
    "chars": 2067,
    "preview": "#!/usr/bin/python3\n\n\"\"\"\n\nPut into Public Domain, by Nicolas Sebrecht.\n\nProduce the \"upcoming release\" notes.\n\n\"\"\"\n\nfrom "
  },
  {
    "path": "docs/Makefile",
    "chars": 1284,
    "preview": "# This program is free software under the terms of the GNU General Public\n# License. See the COPYING file which must com"
  },
  {
    "path": "docs/build-uploads.sh",
    "chars": 664,
    "preview": "#!/bin/sh\n#\n# vim: expandtab ts=2 :\n\nWEBSITE_UPLOADS='./website/_uploads'\n\nwhile true\ndo\n  test -d .git && break\n  cd .."
  },
  {
    "path": "docs/doc-src/API.rst",
    "chars": 2651,
    "preview": ".. OfflineImap API documentation\n\n.. currentmodule:: offlineimap\n\n.. _API docs:\n\n:mod:`offlineimap's` API documentation\n"
  },
  {
    "path": "docs/doc-src/conf.py",
    "chars": 6677,
    "preview": "# -*- coding: utf-8 -*-\n#\n# pyDNS documentation build configuration file, created by\n# sphinx-quickstart on Tue Feb  2 1"
  },
  {
    "path": "docs/doc-src/dco.rst",
    "chars": 2601,
    "preview": ".. _dco\n\nDeveloper's Certificate of Origin\n=================================\n\nv1.1::\n\n  By making a contribution to this"
  },
  {
    "path": "docs/doc-src/index.rst",
    "chars": 447,
    "preview": ".. OfflineImap documentation master file\n.. _OfflineIMAP: http://www.offlineimap.org\n\n\nWelcome to OfflineIMAP's develope"
  },
  {
    "path": "docs/doc-src/repository.rst",
    "chars": 2247,
    "preview": ".. currentmodule:: offlineimap.repository\n\n:mod:`offlineimap.repository` -- Email repositories\n-------------------------"
  },
  {
    "path": "docs/doc-src/ui.rst",
    "chars": 857,
    "preview": ":mod:`offlineimap.ui` -- A flexible logging system\n--------------------------------------------------------\n\n.. currentm"
  },
  {
    "path": "docs/offlineimap.known_issues.txt",
    "chars": 6527,
    "preview": "\n* Deletions.\n+\nWhile in usual run the deletions are propagated. To prevent from data loss,\nremoving a folder makes offl"
  },
  {
    "path": "docs/offlineimap.txt",
    "chars": 12238,
    "preview": "\nofflineimap(1)\n==============\n\nNAME\n----\nofflineimap - Synchronize mailboxes and Maildirs both ways or one either way.\n"
  },
  {
    "path": "docs/offlineimapui.txt",
    "chars": 4172,
    "preview": "\nofflineimapui(7)\n================\n\nNAME\n----\nofflineimapui - The User Interfaces\n\nDESCRIPTION\n-----------\n\nOfflineIMAP "
  },
  {
    "path": "docs/rfcs/README.md",
    "chars": 102,
    "preview": "\nAll RFCs related to IMAP.\n\nTODO: Add a brief introduction here to introduce the most important RFCs.\n"
  },
  {
    "path": "docs/rfcs/rfc1731.IMAP4_auth.txt",
    "chars": 11433,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           J. Myers\nRequest for Comments: 1731               "
  },
  {
    "path": "docs/rfcs/rfc1732.compatibiliy_IMAP2-IMAP2bis.txt",
    "chars": 9276,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 1732               "
  },
  {
    "path": "docs/rfcs/rfc1733.models_in_IMAP4.txt",
    "chars": 6205,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 1733               "
  },
  {
    "path": "docs/rfcs/rfc1734.POP3_AUTHentication",
    "chars": 17543,
    "preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt"
  },
  {
    "path": "docs/rfcs/rfc2061.compatibility_IMAP4-IMAP2bis.txt",
    "chars": 5867,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 2061               "
  },
  {
    "path": "docs/rfcs/rfc2086.IMAP4_ACL_extension.txt",
    "chars": 13925,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           J. Myers\nRequest for Comments: 2086               "
  },
  {
    "path": "docs/rfcs/rfc2087.IMAP4_QUOTA_extension.txt",
    "chars": 8542,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           J. Myers\nRequest for Comments: 2087               "
  },
  {
    "path": "docs/rfcs/rfc2088.IMAP4_non_synchronizing_literals.txt",
    "chars": 4052,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           J. Myers\nRequest for Comments: 2088               "
  },
  {
    "path": "docs/rfcs/rfc2095.IMAP-POP_AUTHorize_extension.txt",
    "chars": 10446,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                       J. Klensin\nRequest for Comments: 2095                 "
  },
  {
    "path": "docs/rfcs/rfc2177.IMAP4_IDLE_command.txt",
    "chars": 6770,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           B. Leiba\nRequest for Comments: 2177               "
  },
  {
    "path": "docs/rfcs/rfc2180.IMAP4_multi-accessed_Mailbox_practice.txt",
    "chars": 24750,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          M. Gahrns\nRequest for Comments: 2180               "
  },
  {
    "path": "docs/rfcs/rfc2192.IMAP_URL_scheme.txt",
    "chars": 31426,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          C. Newman\nRequest for Comments: 2192               "
  },
  {
    "path": "docs/rfcs/rfc2193.IMAP4_Mailbox_referrals.txt",
    "chars": 16248,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        M. Gahrns\nRequest for Comments: 2193                 "
  },
  {
    "path": "docs/rfcs/rfc2195.IMAP-POP_AUTHorize_extension.txt",
    "chars": 10468,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                       J. Klensin\nRequest for Comments: 2195                 "
  },
  {
    "path": "docs/rfcs/rfc2221.IMAP4_Login_referrals.txt",
    "chars": 9251,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           M. Gahrns\nRequest for Comments: 2221              "
  },
  {
    "path": "docs/rfcs/rfc2244.ACAP.txt",
    "chars": 154610,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          C. Newman\nRequest for Comments: 2244               "
  },
  {
    "path": "docs/rfcs/rfc2342.IMAP4_Namespace.txt",
    "chars": 19489,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Gahrns\nRequest for Comments: 2342                "
  },
  {
    "path": "docs/rfcs/rfc2359.IMAP4_UIDPLUS_extension.txt",
    "chars": 10862,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           J. Myers\nRequest for Comments: 2359               "
  },
  {
    "path": "docs/rfcs/rfc2595.TLS_with_IMAP-POP3_and_ACAP.txt",
    "chars": 32440,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          C. Newman\nRequest for Comments: 2595               "
  },
  {
    "path": "docs/rfcs/rfc2683.IMAP4_Implementation_recommendations.txt",
    "chars": 56300,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           B. Leiba\nRequest for Comments: 2683               "
  },
  {
    "path": "docs/rfcs/rfc2831.Obsolete_Digest_AUTHentication_as_a_SASL_mech.txt",
    "chars": 58124,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           P. Leach\nRequest for Comments: 2831               "
  },
  {
    "path": "docs/rfcs/rfc2971.IMAP4_ID_extension.txt",
    "chars": 14670,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        T. Showalter\nRequest for Comments: 2971              "
  },
  {
    "path": "docs/rfcs/rfc3028.Sieve_Mail_filtering_language.txt",
    "chars": 73582,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                       T. Showalter\nRequest for Comments: 3028               "
  },
  {
    "path": "docs/rfcs/rfc3348.IMAP4_Child_Mailbox_extension.txt",
    "chars": 11868,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          M. Gahrns\nRequest for Comments: 3348               "
  },
  {
    "path": "docs/rfcs/rfc3501.IMAP4rev1.txt",
    "chars": 227639,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 3501               "
  },
  {
    "path": "docs/rfcs/rfc3502.MULTIAPPEND_extension.txt",
    "chars": 13379,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 3502               "
  },
  {
    "path": "docs/rfcs/rfc3503.Message_Disposition_Notification.txt",
    "chars": 16937,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 3503               "
  },
  {
    "path": "docs/rfcs/rfc3516.IMAP4_Binary_content_extension.txt",
    "chars": 14598,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                       L. Nerenberg\nRequest for Comments: 3516               "
  },
  {
    "path": "docs/rfcs/rfc3691.IMAP_UNSELECT_command.txt",
    "chars": 8436,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 3691               "
  },
  {
    "path": "docs/rfcs/rfc4314.IMAP4_ACL_extension.txt",
    "chars": 56599,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 4314               "
  },
  {
    "path": "docs/rfcs/rfc4315.IMAP_UIDPLUS_extension.txt",
    "chars": 16629,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 4315               "
  },
  {
    "path": "docs/rfcs/rfc4466.Collected_extensions_to_IMAP4_ABNF.txt",
    "chars": 33752,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 4466               "
  },
  {
    "path": "docs/rfcs/rfc4467.IMAP_URLAUTH_extension.txt",
    "chars": 36714,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         M. Crispin\nRequest for Comments: 4467               "
  },
  {
    "path": "docs/rfcs/rfc4469.IMAP_CATENATE_extension.txt",
    "chars": 21822,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         P. Resnick\nRequest for Comments: 4469               "
  },
  {
    "path": "docs/rfcs/rfc4549.Sync_operations_for_disconnected_IMAP4_Clients.txt",
    "chars": 75417,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                   A. Melnikov, Ed.\nRequest for Comments: 4549               "
  },
  {
    "path": "docs/rfcs/rfc4551.IMAP_Conditional_STORE_or_Quick_flag_changes_resync.txt",
    "chars": 50265,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 4551               "
  },
  {
    "path": "docs/rfcs/rfc4731.IMAP4_Extension_to_SEARCH_command.txt",
    "chars": 15431,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 4731               "
  },
  {
    "path": "docs/rfcs/rfc4978.IMAP_Compress_extension.txt",
    "chars": 17554,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                     A. Gulbrandsen\nRequest for Comments: 4978               "
  },
  {
    "path": "docs/rfcs/rfc5032.IMAP_WITHIN_Search_extension.txt",
    "chars": 8921,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                     E. Burger, Ed.\nRequest for Comments: 5032               "
  },
  {
    "path": "docs/rfcs/rfc5161.IMAP_ENABLE_extension.txt",
    "chars": 12220,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                A. Gulbrandsen, Ed.\nRequest for Comments: 5161               "
  },
  {
    "path": "docs/rfcs/rfc5162.IMAP4_Extensions_for_Quick_Mailbox_resync.txt",
    "chars": 51620,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                        A. Melnikov\nRequest for Comments: 5162               "
  },
  {
    "path": "docs/rfcs/rfc5182.IMAP_extension_last_SEARCH_result.txt",
    "chars": 24520,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                     A. Melnikov\nRequest for Comments: 5182                  "
  },
  {
    "path": "docs/rfcs/rfc5182.Sieve_and_extensions.txt",
    "chars": 87531,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                   P. Guenther, Ed.\nRequest for Comments: 5228               "
  },
  {
    "path": "docs/rfcs/rfc5255.IMAP_i18n.txt",
    "chars": 41403,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                          C. Newman\nRequest for Comments: 5255               "
  },
  {
    "path": "docs/rfcs/rfc5257.IMAP_ANNOTATE_extension.txt",
    "chars": 58786,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           C. Daboo\nRequest for Comments: 5257               "
  },
  {
    "path": "docs/rfcs/rfc5258.IMAP4_LIST_command_extension.txt",
    "chars": 65074,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                           B. Leiba\nRequest for Comments: 5258               "
  },
  {
    "path": "docs/rfcs/rfc5423.IM_Store_Events.txt",
    "chars": 34800,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         R. Gellens\nRequest for Comments: 5423               "
  },
  {
    "path": "docs/rfcs/rfc5464.IMAP_METADATA_extension.txt",
    "chars": 39425,
    "preview": "\n\n\nNetwork Working Group                                           C. Daboo\nRequest for Comments: 5464                  "
  },
  {
    "path": "docs/rfcs/rfc5465.IMAP_NOTIFY_extension.txt",
    "chars": 46208,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                     A. Gulbrandsen\nRequest for Comments: 5465               "
  },
  {
    "path": "docs/rfcs/rfc5530.IMAP_Response_codes.txt",
    "chars": 17169,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                     A. Gulbrandsen\nRequest for Comments: 5530               "
  },
  {
    "path": "docs/rfcs/rfc5738.IMAP_UTF8.txt",
    "chars": 32061,
    "preview": "\n\n\n\n\n\nNetwork Working Group                                         P. Resnick\nRequest for Comments: 5738               "
  },
  {
    "path": "docs/rfcs/rfc5788.IMAP4_Keyword_registry.txt",
    "chars": 22497,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                       A. Melnikov\nRequest for Comments: 5788               "
  },
  {
    "path": "docs/rfcs/rfc5819.IMAP4_extension_Returning_STATUS_info_in_LIST.txt",
    "chars": 10584,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                       A. Melnikov\nRequest for Comments: 5819               "
  },
  {
    "path": "docs/rfcs/rfc5957.IMAP4_SORT_extension.txt",
    "chars": 8709,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                           D. Karp\nRequest for Comments: 5957               "
  },
  {
    "path": "docs/rfcs/rfc6154.IMAP_LIST_Special-use_Mailboxes.txt",
    "chars": 25544,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                          B. Leiba\nRequest for Comments: 6154               "
  },
  {
    "path": "docs/rfcs/rfc6203.IMAP4_Fuzzy_SEARCH_extension.txt",
    "chars": 13606,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                       T. Sirainen\nRequest for Comments: 6203               "
  },
  {
    "path": "docs/rfcs/rfc6237.IMAP4_Multimailbox_SEARCH_extension.txt",
    "chars": 21234,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                          B. Leiba\nRequest for Comments: 6237               "
  },
  {
    "path": "docs/rfcs/rfc6331.Moving_Digest-MD5_to_Historic",
    "chars": 14047,
    "preview": "\n\n\n\n\n\nInternet Engineering Task Force (IETF)                       A. Melnikov\nRequest for Comments: 6331               "
  },
  {
    "path": "docs/website-doc.sh",
    "chars": 4096,
    "preview": "#!/bin/sh\n#\n# vim: expandtab ts=2 :\n\nARGS=$*\n\nSPHINXBUILD=sphinx-build\nTMPDIR='/tmp/offlineimap-sphinx-doctrees'\nWEBSITE"
  },
  {
    "path": "offlineimap/CustomConfig.py",
    "chars": 11007,
    "preview": "# Copyright (C) 2003-2016 John Goerzen & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "offlineimap/__init__.py",
    "chars": 833,
    "preview": "__all__ = ['OfflineImap']\n\n__productname__ = 'OfflineIMAP'\n# Expecting trailing \"-rcN\" or \"\" for stable releases.\n__vers"
  },
  {
    "path": "offlineimap/accounts.py",
    "chars": 27601,
    "preview": "# Copyright (C) 2003-2016 John Goerzen & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "offlineimap/bundled_imaplib2.py",
    "chars": 96458,
    "preview": "#!/usr/bin/env python\n\n\"\"\"Threaded IMAP4 client.\n\nBased on RFC 3501 and original imaplib module.\n\nPublic classes:   IMAP"
  },
  {
    "path": "offlineimap/emailutil.py",
    "chars": 1552,
    "preview": "# Some useful functions to extract data out of emails\n# Copyright (C) 2002-2015 John Goerzen & contributors\n#\n#    This "
  },
  {
    "path": "offlineimap/error.py",
    "chars": 1439,
    "preview": "class OfflineImapError(Exception):\n    \"\"\"An Error during offlineimap synchronization\"\"\"\n\n    class ERROR:\n        \"\"\"Se"
  },
  {
    "path": "offlineimap/folder/Base.py",
    "chars": 42988,
    "preview": "# Base folder support\n# Copyright (C) 2002-2016 John Goerzen & contributors\n#\n#    This program is free software; you ca"
  },
  {
    "path": "offlineimap/folder/Gmail.py",
    "chars": 16295,
    "preview": "# Gmail IMAP folder support\n# Copyright (C) 2002-2017 John Goerzen & contributors.\n#\n#    This program is free software;"
  },
  {
    "path": "offlineimap/folder/GmailMaildir.py",
    "chars": 14067,
    "preview": "# Maildir folder support with labels\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free "
  },
  {
    "path": "offlineimap/folder/IMAP.py",
    "chars": 43089,
    "preview": "# IMAP folder support\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free software; you c"
  },
  {
    "path": "offlineimap/folder/LocalStatus.py",
    "chars": 9840,
    "preview": "# Local status cache virtual folder\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free s"
  },
  {
    "path": "offlineimap/folder/LocalStatusSQLite.py",
    "chars": 17880,
    "preview": "# Local status cache virtual folder: SQLite backend\n# Copyright (C) 2009-2017 Stewart Smith and contributors.\n#\n#    Thi"
  },
  {
    "path": "offlineimap/folder/Maildir.py",
    "chars": 23101,
    "preview": "# Maildir folder support\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free software; yo"
  },
  {
    "path": "offlineimap/folder/UIDMaps.py",
    "chars": 14557,
    "preview": "# Base folder support\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free software; you c"
  },
  {
    "path": "offlineimap/folder/__init__.py",
    "chars": 64,
    "preview": "from . import Base, Gmail, IMAP, Maildir, LocalStatus, UIDMaps\n\n"
  },
  {
    "path": "offlineimap/globals.py",
    "chars": 323,
    "preview": "# Copyright 2013-2016 Eygene A. Ryabinkin & contributors.\n#\n# Module that holds various global objects.\n\nfrom offlineima"
  },
  {
    "path": "offlineimap/imaplibutil.py",
    "chars": 9562,
    "preview": "# imaplib utilities\n# Copyright (C) 2002-2016 John Goerzen & contributors\n#    This program is free software; you can re"
  },
  {
    "path": "offlineimap/imapserver.py",
    "chars": 37669,
    "preview": "# IMAP server support\n# Copyright (C) 2002-2018 John Goerzen & contributors.\n#\n#    This program is free software; you c"
  },
  {
    "path": "offlineimap/imaputil.py",
    "chars": 14639,
    "preview": "# IMAP utility module\n# Copyright (C) 2002-2015 John Goerzen & contributors\n#\n#    This program is free software; you ca"
  },
  {
    "path": "offlineimap/init.py",
    "chars": 21685,
    "preview": "# OfflineIMAP initialization code\n# Copyright (C) 2002-2017 John Goerzen & contributors\n#\n#    This program is free soft"
  },
  {
    "path": "offlineimap/localeval.py",
    "chars": 1694,
    "preview": "\"\"\"Eval python code with global namespace of a python source file.\"\"\"\n\n# Copyright (C) 2002-2016 John Goerzen & contribu"
  },
  {
    "path": "offlineimap/mbnames.py",
    "chars": 9123,
    "preview": "# Mailbox name generator\n# Copyright (C) 2002-2016 John Goerzen & contributors\n#\n#    This program is free software; you"
  },
  {
    "path": "offlineimap/repository/Base.py",
    "chars": 11478,
    "preview": "\"\"\" Base repository support \"\"\"\n\n# Copyright (C) 2002-2017 John Goerzen & contributors\n#\n#    This program is free softw"
  },
  {
    "path": "offlineimap/repository/Gmail.py",
    "chars": 4069,
    "preview": "# Gmail IMAP repository support\n# Copyright (C) 2008-2016 Riccardo Murri <riccardo.murri@gmail.com> &\n# contributors\n#\n#"
  },
  {
    "path": "offlineimap/repository/GmailMaildir.py",
    "chars": 1335,
    "preview": "# Maildir repository support\n# Copyright (C) 2002-2015 John Goerzen & contributors\n# <jgoerzen@complete.org>\n#\n#    This"
  },
  {
    "path": "offlineimap/repository/IMAP.py",
    "chars": 22650,
    "preview": "\"\"\" IMAP repository support \"\"\"\n\n# Copyright (C) 2002-2019 John Goerzen & contributors\n#\n#    This program is free softw"
  },
  {
    "path": "offlineimap/repository/LocalStatus.py",
    "chars": 5078,
    "preview": "# Local status cache repository support\n# Copyright (C) 2002-2017 John Goerzen & contributors\n#\n#    This program is fre"
  },
  {
    "path": "offlineimap/repository/Maildir.py",
    "chars": 8940,
    "preview": "# Maildir repository support\n# Copyright (C) 2002-2015 John Goerzen & contributors\n#\n#    This program is free software;"
  },
  {
    "path": "offlineimap/repository/__init__.py",
    "chars": 3736,
    "preview": "# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free software; you can redistribute it and"
  },
  {
    "path": "offlineimap/threadutil.py",
    "chars": 8036,
    "preview": "# Copyright (C) 2002-2016 John Goerzen & contributors\n# Thread support module\n#\n#    This program is free software; you "
  },
  {
    "path": "offlineimap/ui/Curses.py",
    "chars": 23806,
    "preview": "# Curses-based interfaces\n# Copyright (C) 2003-2018 John Goerzen & contributors.\n#\n#    This program is free software; y"
  },
  {
    "path": "offlineimap/ui/Machine.py",
    "chars": 8047,
    "preview": "# Copyright (C) 2007-2018 John Goerzen & contributors.\n#\n#    This program is free software; you can redistribute it and"
  },
  {
    "path": "offlineimap/ui/Noninteractive.py",
    "chars": 2006,
    "preview": "# Noninteractive UI\n# Copyright (C) 2002-2016 John Goerzen & contributors.\n#\n#    This program is free software; you can"
  },
  {
    "path": "offlineimap/ui/TTY.py",
    "chars": 4191,
    "preview": "# TTY UI\n# Copyright (C) 2002-2018 John Goerzen & contributors\n#\n#    This program is free software; you can redistribut"
  },
  {
    "path": "offlineimap/ui/UIBase.py",
    "chars": 24594,
    "preview": "# UI base class\n# Copyright (C) 2002-2018 John Goerzen & contributors.\n#\n#    This program is free software; you can red"
  },
  {
    "path": "offlineimap/ui/__init__.py",
    "chars": 1314,
    "preview": "# UI module\n# Copyright (C) 2010-2011 Sebastian Spaeth & contributors\n#\n#    This program is free software; you can redi"
  },
  {
    "path": "offlineimap/ui/debuglock.py",
    "chars": 1740,
    "preview": "# Locking debugging code -- temporary\n# Copyright (C) 2003-2015 John Goerzen & contributors\n#\n#    This program is free "
  },
  {
    "path": "offlineimap/utils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "offlineimap/utils/const.py",
    "chars": 1111,
    "preview": "# Copyright (C) 2013-2014 Eygene A. Ryabinkin and contributors\n#\n# Collection of classes that implement const-like behav"
  },
  {
    "path": "offlineimap/utils/distro.py",
    "chars": 2902,
    "preview": "# Copyright 2006-2018 Eygene A. Ryabinkin & contributors.\n#\n# Module that supports distribution-specific functions.\n\nimp"
  },
  {
    "path": "offlineimap/utils/stacktrace.py",
    "chars": 679,
    "preview": "# Copyright 2013 Eygene A. Ryabinkin\n# Functions to perform stack tracing (for multithreaded programs\n# as well as for s"
  },
  {
    "path": "offlineimap/virtual_imaplib2.py",
    "chars": 2017,
    "preview": "# Copyright (C) 2016-2016 Nicolas Sebrecht & contributors\n#\n#    This program is free software; you can redistribute it "
  },
  {
    "path": "offlineimap.conf",
    "chars": 53844,
    "preview": "# Offlineimap sample configuration file\n\n# This file documents *all* possible options and can be quite scary.\n# Looking "
  },
  {
    "path": "offlineimap.conf.minimal",
    "chars": 323,
    "preview": "# Sample minimal config file.  Copy this to ~/.offlineimaprc and edit to\n# get started fast.\n\n[general]\naccounts = Test\n"
  },
  {
    "path": "offlineimap.py",
    "chars": 940,
    "preview": "#!/usr/bin/env python2\n# Startup from single-user installation\n# Copyright (C) 2002-2018 John Goerzen & contributors\n#\n#"
  },
  {
    "path": "requirements.txt",
    "chars": 64,
    "preview": "# Requirements\nsix\ngssapi[kerberos]\nportalocker[cygwin]\nrfc6555\n"
  },
  {
    "path": "scripts/get-repository.sh",
    "chars": 1969,
    "preview": "#!/bin/sh\n#\n# Licence: this file is in the public domain.\n#\n# Download and configure the repositories of the website or "
  },
  {
    "path": "setup.cfg",
    "chars": 41,
    "preview": "\n[metadata]\ndescription-file = README.md\n"
  },
  {
    "path": "setup.py",
    "chars": 2529,
    "preview": "#!/usr/bin/env python\n\n# $Id: setup.py,v 1.1 2002/06/21 18:10:49 jgoerzen Exp $\n\n# IMAP synchronization\n# Module: instal"
  },
  {
    "path": "snapcraft.yaml",
    "chars": 372,
    "preview": "name: offlineimap\nversion: git\nsummary: OfflineIMAP\ndescription: |\n  OfflineIMAP is software that downloads your email m"
  },
  {
    "path": "test/.gitignore",
    "chars": 55,
    "preview": "credentials.conf\ntmp_*\n*.pyc\nOLItest/*.pyc\ntests/*.pyc\n"
  },
  {
    "path": "test/OLItest/TestRunner.py",
    "chars": 10095,
    "preview": "# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "test/OLItest/__init__.py",
    "chars": 1441,
    "preview": "# OfflineImap test library\n# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    This program is free software; y"
  },
  {
    "path": "test/OLItest/globals.py",
    "chars": 1406,
    "preview": "#Constants, that don't rely on anything else in the module\n# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    "
  },
  {
    "path": "test/README",
    "chars": 745,
    "preview": "Documentation for the OfflineImap Test suite.\n\nHow to run the tests\n====================\n\n- Copy the credentials.conf.sa"
  },
  {
    "path": "test/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/credentials.conf.sample",
    "chars": 224,
    "preview": "[Repository IMAP]\ntype = IMAP\nremotehost = localhost\nssl = no\n#sslcacertfile = \n#cert_fingerprint = \nremoteuser = user@d"
  },
  {
    "path": "test/tests/__init__.py",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "test/tests/test_00_globals.py",
    "chars": 1173,
    "preview": "#!/usr/bin/env python\n# Copyright 2013 Eygene A. Ryabinkin\n\nfrom offlineimap import globals\nimport unittest\n\nclass Opt:\n"
  },
  {
    "path": "test/tests/test_00_imaputil.py",
    "chars": 3867,
    "preview": "# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "test/tests/test_01_basic.py",
    "chars": 7055,
    "preview": "# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "test/tests/test_02_MappedIMAP.py",
    "chars": 2727,
    "preview": "# Copyright (C) 2012- Sebastian Spaeth & contributors\n#\n#    This program is free software; you can redistribute it and/"
  },
  {
    "path": "tests/.gitignore",
    "chars": 7,
    "preview": "*.pyc\n\n"
  },
  {
    "path": "tests/create_conf_file.py",
    "chars": 3873,
    "preview": "#!/bin/env python\n# Copyright 2018 Espace LLC/espacenetworks.com.  Written by @chris001.\n# This must be run from the mai"
  },
  {
    "path": "tests/requirements.txt",
    "chars": 35,
    "preview": "pytest\npytest-cov\ncoverage\ncodecov\n"
  }
]

About this extraction

This page contains the full source code of the nicolas33/offlineimap GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 171 files (2.6 MB), approximately 691.2k tokens, and a symbol index with 1029 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!