Compare commits

...

1323 Commits

Author SHA1 Message Date
Manos Pitsidianakis f70496f14c
Add codemeta.json
https://codemeta.github.io/

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-18 15:08:57 +03:00
Manos Pitsidianakis 8a16cf6db4
listing/thread: fix wrong column index crash
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 6m4s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m58s Details
columns[0] was jused in every for loop instead of columns[n], which
would make the debug_assert_eq(area generation, column generation) panic

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-17 13:17:34 +03:00
Manos Pitsidianakis 11f3077b06
args: add more possible values for manpage names
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-14 21:47:02 +03:00
Manos Pitsidianakis dedee908d1
Update `notify` dep from 4.0.17 to 6.1.1
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m26s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m2s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m32s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-11 21:48:30 +03:00
Manos Pitsidianakis 255e93764a
Update `linkify` dep from 0.8.1 to 0.10.0
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-11 21:48:25 +03:00
Manos Pitsidianakis c5e9e67604
docs: add historical-manpages dir
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m11s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m50s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m33s Details
Add some old manpages that may be of interest to users:

- maildir (5)
- mbox (5)
- mbox (5qmail)
- qmail-maildir (5)

Under meli/docs/historical-manpages/

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-11 21:19:15 +03:00
Manos Pitsidianakis ae96038fbf
Make unicode-segmentation a hard dependency
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m48s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m42s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m5s Details
meli/melib are UTF8 software, so we should have proper Unicode support.

A compile-time env var is added, `UNICODE_REGENERATE_TABLES` to force
network access and rebuild the cached unicode tables.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-11 21:15:47 +03:00
Manos Pitsidianakis 07072e2e3f
melib/thread: prevent panic if envelope is deleted
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-06 21:08:37 +03:00
Manos Pitsidianakis aa5737a004
compose: prevent drawing pager on embedded mode
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-06 21:07:40 +03:00
Manos Pitsidianakis 48cb9ee204
Fix compilation for macos
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m25s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m2s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-04-05 16:27:25 +03:00
Guillaume Ranquet c53a32de4c
thread: re-enables horizontal thread view
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m46s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m32s Details
Re-implemnts horizontal thread view.
Default is still vertical, but pressing toggle_layout now has an effect.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2024-04-05 16:00:58 +03:00
Manos Pitsidianakis a69c674c07
Fix new 1.77 clippy lints
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m20s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m33s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-24 16:04:51 +02:00
Manos Pitsidianakis 6a66afe93e
view: make add contact dialog scrollable on overflow
If contact entries in the add contact dialog are too many to fit in the
dialog area, show a scrollbar and allow the user to navigate it.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-24 15:21:05 +02:00
Manos Pitsidianakis 974502c6ff
melib/addressbook: impl Hash for Card
Implement hashing for Card.

This fixes the appearance of duplicate entries in the add contacts
selector in an envelope view when an address appears more than one time
in the envelope headers.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-24 15:14:20 +02:00
Manos Pitsidianakis 3e9144657b
meli: store children process metadata
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 7m2s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 10m42s Details
Store children process metadata. Pid and command lines can then be shown
in the UI and in logs.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-23 10:43:58 +02:00
Manos Pitsidianakis 35a9f33aab
listing: extract common FlagString logic
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-21 21:04:22 +02:00
Manos Pitsidianakis 475609fe92
listing: make {prev,next}_entry shortcut behavior consistent
prev_entry, next_entry shortcuts (default bindings: Ctrl+p and Ctrl+n)
were not behaving consistently in all different listing index styles. In
particular in some conditions the switch entry shortcuts worked at most
once because the cursor position was not updated properly. This commit
fixes that.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-21 13:35:41 +02:00
Manos Pitsidianakis 38bca8f8bc
docs/meli.conf.5: mention use_oauth2=true for gmail oauth2
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m52s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m42s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-17 14:07:07 +02:00
Manos Pitsidianakis ec01a4412a
melib/imap: turn some sync connections to unsync
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 23:47:30 +02:00
Manos Pitsidianakis 4e941a9e8b
accounts: add default_mailbox setting
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m28s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m30s Details
Add a default mailbox setting:

> The mailbox that is the default to open / view for this account. Must be
> a valid mailbox name.
>
> If not specified, the default is [`Self::root_mailbox`].

Closes: #350
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 18:16:05 +02:00
Manos Pitsidianakis 742f038f74
accounts: move sent_mailbox to settings
In the next commits we will add a `default_mailbox` field. Instead of
poluting the `Account` struct with more setting fields, consolidate them
on its `settings`.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 18:15:51 +02:00
Manos Pitsidianakis 484712b0c3
accounts: check for unrecoverable errors in is_online
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 18:15:50 +02:00
Manos Pitsidianakis 264782d228
Various unimportant minor style/doc fixups
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 18:15:50 +02:00
Manos Pitsidianakis 41e965b8a3
meli/accounts: split mbox/job stuff in submodules
accounts.rs is getting rather long (almost 3K lines) so split standalone
stuff in submodules.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-16 12:48:57 +02:00
Manos Pitsidianakis f31b5c4000
melib/connections: don't print raw bytes as escaped unicode
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-15 13:20:07 +02:00
Manos Pitsidianakis 8014af2563
imap/protocol_parser: reduce debug prints
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-15 13:19:26 +02:00
Manos Pitsidianakis 4ce616aeca
CI: fix lints.yaml rustup install step
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m23s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m39s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m3s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-15 11:42:58 +02:00
Manos Pitsidianakis a3aaec382a
melib/conf: remove unused imports
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m24s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m38s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 17s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-14 19:18:23 +02:00
Manos Pitsidianakis b8b24282a0
Update all instances of old domains with meli-email.org
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 11m42s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 19s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m12s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-10 21:38:12 +02:00
Manos Pitsidianakis e481880321
Various manpage touchups and URL updates
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-10 21:34:46 +02:00
Geert Stappers a88b8c5ea0 debian/changelog warning fix
Added
- actual change log entries
- a space in front of hyphen hyphen
- empty lines

Signed-off-by: Geert Stappers <stappers@stappers.it>
2024-03-10 16:43:51 +02:00
Manos Pitsidianakis b820bd6d9c
melib/imap: remove unused imap_trace! and fix comp
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m29s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 4m25s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-10 13:08:58 +02:00
Manos Pitsidianakis 3b93fa8e7c
state.rs: don't draw messages above embedded terminal
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m21s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m13s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-08 16:56:47 +02:00
Manos Pitsidianakis 634bd1917a
melib/imap: convert log prints to traces
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m17s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m23s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-08 16:56:33 +02:00
Manos Pitsidianakis b5fd3f57a7
listing.rs: make self.view an Option
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m52s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m21s Details
Prevent accessing a ThreadView if it has not been initialized by making
an uninitialized ThreadView impossible.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-08 16:56:01 +02:00
Manos Pitsidianakis 1fcb1d59b8
build.rs: remove rerun when build.rs changes
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-08 16:55:28 +02:00
Manos Pitsidianakis e2cdebe89c
Add option to highlight self in mailing list threads
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m34s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m24s Details
Use under `listing` options such as:

globally
========

  [listing]
  highlight_self = true

per-account
===========

  [accounts.work]
  root_mailbox = '[Gmail]'
  format = "imap"
  subscribed_mailboxes = ["*"]
  listing.index_style = "compact"
  listing.highlight_self = true

per-mailbox
===========

  [accounts.work.mailboxes]
  "INBOX/Lists/project-devel" = { listing.highlight_self=true }

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-06 17:39:28 +02:00
Manos Pitsidianakis 3884c0da1f
docs/meli.conf.5: small typographic fixups
- Add macro for literal string values to enable showing unicode
 literal characters
- Fix bool/boolean inconsistency
- Fix "true" / true inconsistency
- Add macro for horizontal rule in subsections
- Add terminal subsection about unicode modifier / combining marks for
  emojis

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-06 17:39:28 +02:00
Manos Pitsidianakis 26928e3ae9
terminal: fix compilation for macos
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m24s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m15s Details
Fixes: 70fc2b455c ("Update nix dependency to 0.27")
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-03 22:57:09 +02:00
Manos Pitsidianakis 070930e671
meli/sqlite3: Fix auto index build when missing
An error was returned from the db_path function, preventing the issuing
of the reindex command in the background.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-03 22:57:09 +02:00
Manos Pitsidianakis c7aee72525
melib: add clippy::doc_markdown
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-03 22:57:09 +02:00
Manos Pitsidianakis 30a3205e4f
meli: Add clippy::doc_markdown
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-03 11:38:57 +02:00
Manos Pitsidianakis 9af284b8db
listing: Don't hide unread count for mailboxes that are partly truncated
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m4s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m31s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-03-01 17:14:05 +02:00
Manos Pitsidianakis 62aee4644b
Add subcommand to print log file location
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m44s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 5m50s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-29 12:04:49 +02:00
Manos Pitsidianakis 5af2e1ee66
Add subcommand to print config file location
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-29 11:53:30 +02:00
Manos Pitsidianakis 4e7b665672
sqlite caching refactor
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m54s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m32s Details
General refactoring to make blocking operations use special blocking
thread workers, SQL operations to use transactions, and setting up WAL
journal mode mode to minimize locking.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-24 19:16:42 +02:00
Manos Pitsidianakis fd64fe0bf8
README.md: update codeberg.org URL
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-21 19:54:50 +02:00
Manos Pitsidianakis 51e3f163d4
melib/jmap: Use Url instead of String in deserializing
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m55s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 18m21s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m7s Details
Catch invalid URLs at the parsing stage.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-13 14:13:53 +02:00
Manos Pitsidianakis 417b24cd84
meli: print invalid command on error
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m19s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m29s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m1s Details
Instead of printing just "invalid command", print the command as well.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-13 14:13:52 +02:00
Manos Pitsidianakis 873a67d0fb
Replace erroneous use of set_err_kind with set_kind
set_err_kind() is a method of the IntoError trait, not an Error method;
it is meant to be used for any error type that can be converted into
Error. Since melib::Error implements Into<melib::Error> tautologically,
this was not a compilation error. Nevertheless, the correct thing to do
is use the type method directly to set ErrorKind.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-11 17:13:05 +02:00
Manos Pitsidianakis c332c2f5ff
Fix new clippy lints (mostly clippy::blocks_in_conditions)
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-11 17:13:05 +02:00
Manos Pitsidianakis 1048ce6824
melib/utils: add hostname() utility function
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-11 17:13:05 +02:00
Manos Pitsidianakis 70fc2b455c
Update nix dependency to 0.27
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-11 17:13:05 +02:00
Manos Pitsidianakis 8de8addd11
melib/datetime: add cfg for musl builds
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-11 17:13:05 +02:00
Manos Pitsidianakis 1fe3619208
conf: Make conf validation recognize AccountSettings extra keys
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m19s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m39s Details
AccountSettings extra keys like `vcard_folder` were not taken into
account when validating a config.

This commit introduces an AccountSettings::validate_config() method that
checks for the presence and validity of this key value.

Fixes #349

#349

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-04 14:52:06 +02:00
Manos Pitsidianakis 0b468d88ad
addressbook/vcard: improve Error messages
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-02-04 14:52:06 +02:00
Manos Pitsidianakis 1eca34b398
Set lowest priority to shortcut command UIEvents
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m2s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m14s Details
Commit (a37d5fc1 conf/shortcuts: implement
a key to command mapping) introduced shortcuts that expand to user
defined commands. To allow already existing shortcuts to take
precedence, the check for the user-defined shortcuts should be the last
one in the evaluation order.

Example problem scenario:
- Press new_mail shortcut (e.g. `m`)
- Code in listing.rs searches if it matches any of the commands, and
  regardless if it matches or not, stops the evaluation and returns.
- New mail composer never shows up.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-21 12:03:40 +02:00
Manos Pitsidianakis 5afc078587
Update README.md, DEVELOPMENT.md and create BUILD.md
README.md is quite lengthy so split extraneous info to other `.md`
files.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-20 12:09:34 +02:00
Guillaume Ranquet a37d5fc1d1 conf/shortcuts: implement a key to command mapping
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m15s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 20m58s Details
Permits users to map keys in their configuration file to an array of meli commands

e.g:
[shortcuts.listing]
commands = [ { command = [ "tag remove trash", "flag unset trash" ], shortcut = "D" },
             { command = [ "tag add trash", "flag set trash" ], shortcut = "d" } ]

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2024-01-18 15:53:35 +01:00
Manos Pitsidianakis 60f26f9dae
melib: Fix some old pre-intradoc rustdoc links
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m3s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 18m7s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-11 09:22:28 +02:00
Ethra e80ea9c9de
Changed default manpage install path
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m15s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m32s Details
2024-01-11 05:08:58 +03:00
Manos Pitsidianakis 64e60cb0ee
listing: fix select modifier regression
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m51s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m56s Details
Commit 61a0c3c27f ("listing: do not clear
selection after action") broke select/jump modifiers (e.g. prefixing a
jump with a number).

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-08 12:32:50 +02:00
Manos Pitsidianakis 81d1c0536b
scripts: add mandoc_lint.sh
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-06 16:22:16 +02:00
Manos Pitsidianakis cd448924ed
listing: add clear-selection command
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m36s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m27s Details
Add a command that performs what Escape does: clears the selection.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-06 15:35:56 +02:00
Manos Pitsidianakis 61a0c3c27f
listing: do not clear selection after action
Clear selection only when Escape is pressed, not after action is
completed. The user might want to perform further actions on the
selection.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-06 15:20:00 +02:00
Manos Pitsidianakis 7952006870
melib/percent_encoding: remove doctests, add tests module
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m37s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m34s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-04 10:41:00 +02:00
Manos Pitsidianakis ddab3179c2
melib/wcwidth: move tests to tests module
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-04 10:40:31 +02:00
Manos Pitsidianakis 7861fb0402
Fix typos found with `typos` tool
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-03 11:08:55 +02:00
Manos Pitsidianakis 148f0433d9
meli: implement flag set/unset action in UI
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m12s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 16m30s Details
Also document it in manpages meli.1 and meli.7

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 21:28:21 +02:00
Manos Pitsidianakis 8185f2cf7d
meli: add deny clippy lints and fix them
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m56s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 17m46s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 15:59:13 +02:00
Manos Pitsidianakis 0270db0123
melib: From<&[u8]> -> From<B: AsRef<[u8]>>
This change allows byte literals to be used with the from trait method.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 15:16:47 +02:00
Manos Pitsidianakis 8ddd673dd8
melib/imap/untagged: update all mailboxes
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m41s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 17m22s Details
When receiving an envelope event (deleted, or changed flags), update all
mailboxes that contain that envelope hash; not just the currently
selected mailbox.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 14:36:29 +02:00
Manos Pitsidianakis e3351d2755
melib/imap: fix set unseen updating all mboxes
When manually setting an envelope as not seen, all mailboxes had their
unseen count increased. This commit updates only those that include the
envelope in the first place.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 14:34:50 +02:00
Manos Pitsidianakis 31401fa35c
melib/backends: add LazyCountSet::contains method
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 14:34:05 +02:00
Manos Pitsidianakis 33408146a1
Fix feature permutation mis-compilations found with cargo-hack
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m54s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m46s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m28s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 11:38:42 +02:00
Manos Pitsidianakis 8a95febb78
CI: set debuginfo=0 in test/lint builds
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m55s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 20m39s Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m47s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 11:03:29 +02:00
Manos Pitsidianakis 73d5b24e98
melib/tests: merge integration tests in one crate
Saves about 0.5 seconds from compilation and runtime.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2024-01-01 10:44:31 +02:00
Manos Pitsidianakis 0da97dd8c1
mail/listing: check row_updates in is_dirty()
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m0s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m3s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m10s Details
If there are row_updates, it means we need to redraw. But in the draw()
call, we check is_dirty() to decide whether to proceed drawing. Add
row_updates not being empty into the dirty conditions.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 20:02:58 +02:00
Manos Pitsidianakis 933bf157ae
melib/email/parser: ack \ as an atom
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m31s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m5s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m24s Details
I think this is not spec compliant but the MIME spec (rfc6068 - The
'mailto' URI Scheme) uses it for "valid" addresses.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:56:52 +02:00
Manos Pitsidianakis f685726eac
melib/email/parser: add backtrace field to ParsingError
Add backtrace field to ParsingError when the build is for testing or
documentation.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:56:52 +02:00
Manos Pitsidianakis ab1b946fd9
melib/error: don't print details if it's an empty string.
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:56:52 +02:00
Manos Pitsidianakis ce4ba06ce9
command: add a flag set/unset command
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m12s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m39s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m13s Details
e.g. "flag unset draft"

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:51:45 +02:00
Manos Pitsidianakis bebb473d1b
melib/mbox: derive extra traits for enums
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:51:45 +02:00
Manos Pitsidianakis f0866a3965
meli: make config error more user-friendly
If `send_mail` is incorrect, display a long-ish list of valid examples.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:51:45 +02:00
Manos Pitsidianakis f63774fa6d
Fix new clippy lints (1.75)
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-29 19:51:44 +02:00
Manos Pitsidianakis 808aa4942d
melib: rename text_processing to text for the whole brevity thing
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-26 16:47:42 +02:00
Manos Pitsidianakis 08518e1ca8
terminal: remove obsolete position.rs module
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m15s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m15s Details
The functions in terminal::position were pretty much obsolete after
commit

0e3a0c4b70 Add safe UI widget area drawing API

So this commit does a little cleanup and removes the module.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 19:49:23 +02:00
Manos Pitsidianakis 34a2d52e7e
Fix rustdoc::redundant_explicit_links
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m28s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m37s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 19:14:00 +02:00
Manos Pitsidianakis 4026e25428
melib/notmuch: add some doc comments
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 19:13:15 +02:00
Manos Pitsidianakis ca7d7bb95d
melib/notmuch: use message freeze/thaw for flag changes
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 19:12:45 +02:00
Manos Pitsidianakis ebe1b3da7e
melib/notmuch: wrap *mut struct fields in NonNull<_>
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 18:50:23 +02:00
Manos Pitsidianakis 506ae9f594
melib/error: Add ErrorKind::LinkedLibrary variant
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 18:43:12 +02:00
Manos Pitsidianakis b6f769b2f4
mail/listing: add field names to row_attr! bool values
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 18:35:09 +02:00
Manos Pitsidianakis 3691cd2962
accounts.rs: send EnvelopeUpdate event after self.collection.update_flags()
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-17 18:20:14 +02:00
Manos Pitsidianakis 97eb636375
Makefile: add dpkg --print-architecture to deb filename
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-13 15:43:00 +02:00
Manos Pitsidianakis b3079715f6
melib/smtp: disable flakey test_smtp()
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m49s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m54s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-13 09:45:17 +02:00
Manos Pitsidianakis 86bbf1ea57
melib/notmuch: refresh NotmuchMailbox counts when setting flags
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m59s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 17m21s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-12 20:03:14 +02:00
Manos Pitsidianakis 1b0bdd0a9a
melib/notmuch: split queries and mailbox into submodules
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-12 20:03:14 +02:00
Manos Pitsidianakis 7412c23870
Bump meli version to 0.8.5-rc.3
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m53s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m47s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 16m25s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 9m34s Details
Build .deb package / Package for debian on ${{ matrix.arch }} (amd64, linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m38s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-11 13:18:50 +02:00
Manos Pitsidianakis 500fe7f7e4
Update CHANGELOG.md
Use git-cliff.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-11 13:13:19 +02:00
Manos Pitsidianakis 2419f4bd40
CI: add debian package build workflow
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m35s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m29s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m40s Details
Build .deb package / Package for debian on ${{ matrix.arch }} (amd64, linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m18s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 9m27s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-11 09:32:40 +02:00
Manos Pitsidianakis 59c99fdc79
debian: update debian package metadata
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 21:22:23 +02:00
Manos Pitsidianakis 5f8d7c8039
debian: Update deb-dist target command with author metadata
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 19:38:29 +02:00
Manos Pitsidianakis 876616d45b
CI: use actions/upload-artifact@v3
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m43s Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m32s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m33s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m23s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:56:38 +02:00
Manos Pitsidianakis c41f35fdd5
CI: use actions/checkout@v3
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:56:22 +02:00
Manos Pitsidianakis 773254864b
CI: remove on-push hooks for actions w/ run on-pr
Because it results in jobs being scheduled twice, once because of push
and once because they are in a PR.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:51:03 +02:00
Manos Pitsidianakis e19f3e572c
Cargo-sort all Cargo.toml files
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Has been cancelled Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Has been cancelled Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled Details
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled Details
Cargo manifest lints / Lint Cargo manifests on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m22s Details
With:

cargo sort  --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace meli
cargo sort  --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace melib
cargo sort  --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace tools
cargo sort  --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace fuzz

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:33:10 +02:00
Manos Pitsidianakis 1617212c5b
CI: add scripts/check_debian_changelog.sh lint
Check if latest version in debian/changelog matches the version in
meli/Cargo.toml.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:33:10 +02:00
Manos Pitsidianakis 3ba1603af2
CI: add manifest file only lints workflow
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-10 15:33:10 +02:00
Manos Pitsidianakis 0a617410ec
CI: split test.yaml to test.yaml and lints.yaml
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 22:08:38 +02:00
Manos Pitsidianakis 5ff4e8ae68
CI: run builds.yaml when any manifest file changes
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 22:08:38 +02:00
Manos Pitsidianakis c4344529e3
Add .git-blame-ignore-revs file
See DEVELOPMENT.md for info.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 21:30:02 +02:00
Manos Pitsidianakis f900dbea46
Use cargo-derivefmt to sort derives alphabetically
Used https://github.com/dcchut/cargo-derivefmt

With command:

cargo install --locked \
--git https://github.com/dcchut/cargo-derivefmt \
--bin cargo-derivefmt \
--rev 2ff93de7fb418180458dd1ba27e5655607c23ab6

Since it's not on crates.io at the moment.

Sample diff:

  -#[derive(Debug, Deserialize, Clone, Serialize)]
  +#[derive(Clone, Debug, Deserialize, Serialize)]

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 21:28:12 +02:00
Manos Pitsidianakis f3e85738e7
meli: move build.rs scripts to build directory
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 21:24:18 +02:00
Manos Pitsidianakis 3a70979483
Update minimum rust version from 1.65.0 to 1.68.2
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m45s Details
Found with `cargo msrv --bisect --min 1.67.0`

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 18:01:12 +02:00
Manos Pitsidianakis 24971d1960
Fix compilation with 1.70.0 cargo
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 18m9s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 11m26s Details
Cargo bug: https://github.com/rust-lang/cargo/issues/10788

Caused meli to not be able to be installed with 1.70.0 cargo.

This commit expresses the static dependencies differently to allow both
1.70.0 and later versions understand the optional dependency feature
activation.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 17:28:05 +02:00
Manos Pitsidianakis e37997d697
mail/view: store Link URL value in Link type
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 11m10s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 18m3s Details
Due to changes in how decoded email body is stored in `ViewFilter`s,
the previous way of accessing URL values (by using the `start` and `end`
offset in the e-mail body) meant the offset values might not be correct.

Store the value right away to prevent this from happening.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-09 15:08:56 +02:00
Manos Pitsidianakis 3adba40e32
scripts/make_html_manual_page.py: add macos manpage mirror url
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-06 16:33:19 +02:00
Manos Pitsidianakis da251455a0
Bump meli version to 0.8.5-rc.2
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 33m57s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-04 17:25:22 +02:00
Manos Pitsidianakis d16afc7d8d
Bump version to 0.8.5-rc.2
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-04 17:22:56 +02:00
Manos Pitsidianakis 7eedd86051
listings: remove address_list! macro
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m52s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 26m46s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-04 16:29:33 +02:00
Manos Pitsidianakis c751b2e845
Re-enable conversations listing style
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-04 16:22:53 +02:00
Manos Pitsidianakis 031d0f7dc7
terminal: add area.is_empty() checks in cell iterators
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-04 16:20:43 +02:00
Manos Pitsidianakis 2c6f180df9
meli/notifications: fix macos compilation
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 20m57s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 20m56s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-03 18:49:14 +02:00
Manos Pitsidianakis 63a63253d7
melib/datetime: use type alias for c_char
On arm64, it's u8, not i8.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-03 18:08:48 +02:00
Manos Pitsidianakis 71f3ffe740
Update Makefile
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-03 18:08:48 +02:00
Manos Pitsidianakis 10c3b0eabe
Bump version to 0.8.5-rc.1
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 22m13s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 11m32s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 19:20:34 +02:00
Andrei Zisu 64898a0583
melib/imap: Make UIDStore constructor pub
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 32m9s Details
I honestly forget exactly why this change is needed, so I need to
recheck.
2023-12-02 19:05:13 +02:00
Andrei Zisu 77a8d9e2c2
melib: Make ModSequence publicly accessible
This way it can be imported from this namespace in depending code.
2023-12-02 19:00:48 +02:00
Manos Pitsidianakis ed8a5de2cb
Re-enable EditAttachments component
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m41s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 31m32s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:40 +02:00
Manos Pitsidianakis b5cc2a095f
Upgrade MailboxManager component to new TUI API
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:40 +02:00
Manos Pitsidianakis 5dd71ef1cd
Upgrade JobsView component to new TUI API
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:40 +02:00
Manos Pitsidianakis 28fa66cc2a
Fix ThreadedListing for new TUI API
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:40 +02:00
Manos Pitsidianakis 3b4acc15a5
view/filters: add tests
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis 23c15261e7
mail/view: abstract envelope view filters away
Modularize an envelope view by introducing a stack of "view filters".

Example uses:

- html email can have a view on top of it that is plain text conversion
- selecting and viewing text/* attachments is just appending a new filter at
  the stack

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis 62b8465f2c
Fix ThreadView for new TUI API
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis 1c1be7d6c9
melib/address: add display_name(), display_slice(), display_name_slice() methods
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis ccf6f9a26e
listing: remember previous `set [index_style]]` preferences
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis 3495ffd61b
types: Change UIEvent::Notification structure
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:39 +02:00
Manos Pitsidianakis 458258e1aa
Re-enable compact listing style
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis d018f07aa5
Retouch manual pages
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis 0114e69542
Add next_search_result and previous_search_result shortcuts
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis 0a74c7d0e5
terminal/embedded: overhaul refactor
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis 54d21f25fd
Re-add contact list and editor support
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis ba7a97e90b
utilities/tables: add x axis scroll support
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis bcec745c24
utilities: fix command and status bar drawing
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:38 +02:00
Manos Pitsidianakis b61fc3ab64
utilities: add HelpView struct for shortcuts widget
Re-enable shortcuts view by moving its state in a separate struct.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:37 +02:00
Manos Pitsidianakis c2ae19d120
mail/view/thread: return Option from current_pos
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:37 +02:00
Manos Pitsidianakis 84f3641ec1
Re-add on-screen message display
Introduce new DisplayMessageBox struct that handles the rendering of
notifications/notices on the overlay screen.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:37 +02:00
Manos Pitsidianakis 0e3a0c4b70
Add safe UI widget area drawing API
Make Screen generic over its display kind: Screen<Tty> and
Screen<Virtual>. The latter is for "cached" renderings we want to keep
and copy to the actual screen when the Component::draw() method is
called. Only Screen<Tty> can write to stdout and it needs an stdout
handle.

Add a generation integer field to Screen, that changes each time it is
resized. This way, we can track if "stale" areas are used and panic on
runtime (in debug mode).

Introduce a new type, Area, that keeps metadata about a subsection of a
Screen, and the generation it came from. New areas can only be created
from a Screen and by operating on an Area to create subsections of it.

This way, it's impossible to make an area refer to (x, y) cells outside
the screen generation of its provenance. If stabilised this API should
eliminate all out of bounds accesses in CellBuffers.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-12-02 17:20:37 +02:00
Manos Pitsidianakis 7645ff1b87
terminal/cells: rename write_string{to_grid,}
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-30 11:04:05 +02:00
Manos Pitsidianakis e0adcdfe15
terminal/cells: move rest of methods under CellBuffer
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-30 11:04:05 +02:00
Manos Pitsidianakis ab14f81900
terminal/cells: make write_string_to_grid a CellBuffer method
For future reference, refactoring was done with comby:

comby -review ":[w~\s]write_string_to_grid(:[s], &mut :[var],:[rest])" ":[var].write_string_to_grid(:[s],:[rest])" .rs
comby -review ":[w~\s]write_string_to_grid(:[s],:[var],:[rest])" ":[var].write_string_to_grid(:[s],:[rest])" .rs

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-30 11:04:05 +02:00
Manos Pitsidianakis cd2ba80f8e
debian: update metadata
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-27 21:23:00 +02:00
Manos Pitsidianakis c1c41c9126
Update README.md and add Codeberg mirror
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 18m31s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-27 21:21:17 +02:00
Manos Pitsidianakis a1cbb1988b
types/File: return Results instead of panicking
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m54s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 23m47s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-27 09:40:40 +02:00
Manos Pitsidianakis 470cae6b88
Update thread cache on email flag modifications
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 23m53s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 23m44s Details
On a previous commit email flag modification logic was changed, but
threads cache was not updated, leading to threads unread count being
stale.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-27 09:37:58 +02:00
Manos Pitsidianakis 23507932f9
imap: update cache on set_flags
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-27 09:37:58 +02:00
Manos Pitsidianakis 0500e451da
listing/plain: add missing EnvelopeRemove event handler
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 11m53s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-24 15:27:11 +02:00
Manos Pitsidianakis 6506fffb94
Rewrite email flag modifications
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m35s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 14m2s Details
Flag and tag modifications are now somewhat typed better, and the
frontend applies them on its own on success. This means that if you set
an unseen mail as seen but it was already seen in the backend, you will
see the change locally. Previously it would remain unseen.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-24 12:58:21 +02:00
Manos Pitsidianakis f81a1e2338
Bump version to 0.8.4
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m6s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 10m49s Details
0.8.3 had a misbehaving test.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 21:48:59 +02:00
Manos Pitsidianakis ef30228e08
melib/draft: fix failing test
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 21:48:59 +02:00
Manos Pitsidianakis 111a1160ad
Bump version to 0.8.3
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 14m25s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 19m26s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:39:58 +02:00
Manos Pitsidianakis bfc78a0803
melib/compose: replace CRLF with LF when editing
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:31:04 +02:00
Manos Pitsidianakis 7387b67eee
Enable "static" build for C library dependencies by default
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:26:16 +02:00
Manos Pitsidianakis af241d25cb
melib: bump version to 0.8.3
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:13:03 +02:00
Manos Pitsidianakis fa33a9468a
Move managesieve-client binary to tools/
This binary was included in the meli crate distribution which wasn't
intended. It's for development purposes only.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:10:23 +02:00
Manos Pitsidianakis 2db021fa0a
meli: remove regexp from default features
It's barely used and has no reason to be default.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 15:04:38 +02:00
Manos Pitsidianakis 43bfd4131d
Update ahash dependency
Previous one was yanked.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 14:53:46 +02:00
Manos Pitsidianakis ac2a5dcdd1
melib: add display() method for Address
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 14:14:56 +02:00
Manos Pitsidianakis 688e39a67e
Fix clippy lints
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 14:05:59 +02:00
Manos Pitsidianakis 0e60bdf26e
Cargo.toml: add "iterator" feature to signal-hook
This dependency is necessary, though for some reason the build doesn't
always fail if it's not specified.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-22 14:02:09 +02:00
Manos Pitsidianakis 8a21be2177
melib/imap: replace splice with truncate
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m56s Details
splice() was calling memmove, it is a really inefficient way of
truncating a string.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-09 09:56:21 +02:00
Manos Pitsidianakis 606f487fc5
README.md: add IRC channel badge
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-11-04 13:44:24 +02:00
Manos Pitsidianakis 0f3b529459
listing: hoist format_date() to ListingTrait method
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m32s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m27s Details
For reusability.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-10-21 16:31:01 +03:00
Manos Pitsidianakis 5a7919bb03
listing/plain: use ConversationsListing::format_date
Its own format_date method has a wrong implementation.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-10-21 16:31:00 +03:00
Manos Pitsidianakis f702dc220c
Fix new clippy lints.
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-10-21 16:31:00 +03:00
Andrei Zisu e95c275d68 Remove duplicate end sequence
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 20m52s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 21m18s Details
Signed-off-by: Andrei Zisu <matzipan@gmail.com>
2023-10-02 22:07:20 +02:00
Andrei Zisu 3105a0373b Add quit command
Signed-off-by: Andrei Zisu <matzipan@gmail.com>
2023-10-02 22:07:17 +02:00
Andrei Zisu 7aec5b8e78 Fix SMTP example doc
Signed-off-by: Andrei Zisu <matzipan@gmail.com>
2023-10-02 22:07:10 +02:00
Manos Pitsidianakis e1b55340fa
state.rs: show error description when TIOCGWINSZ ioctl fails
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m48s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m59s Details
In some situations, we're not compatible with the terminal. Show an
error with details when the terminal size request happens.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-25 09:50:56 +03:00
Manos Pitsidianakis d3cbf184e6
compose: add extra_submission_headers fields in composer form and autocomplete for Newsgroups
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 10m32s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m14s Details
Submitting to NNTP/Usenet servers requires you to specify which news
groups the post/article is going to. This commit places all
extra_submission_headers from a backend (in this case only NNTP
implements this) in the composing form fields.

Fixes #267

nntp should add Newsgroups header if missing
<https://git.meli.delivery/meli/meli/issues/267>

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-23 18:51:02 +03:00
Manos Pitsidianakis e88957ae6e
melib/backends: add extra_submission_headers field in MailBackendCapabilities struct
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-23 17:44:42 +03:00
Manos Pitsidianakis 3d85ca2edf
Bump version to 0.8.2
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 9m44s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m6s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-22 12:30:54 +02:00
Manos Pitsidianakis 7888d8b2a5
melib/utils/xdg: fix doc test compilation
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-22 12:26:42 +02:00
Manos Pitsidianakis eb5d49c41a
meli/terminal/cells: use Self in self methods
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 11m25s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-20 19:30:50 +02:00
Manos Pitsidianakis 714744366f
mail/listing: revert 22525d40 behavior when sidebar not focused
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-20 19:25:44 +02:00
Manos Pitsidianakis 73b3ed559d
mail/view: fix forward dialog not workng
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m55s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-16 20:28:33 +03:00
Manos Pitsidianakis 22525d40fb
mail/listing: go to end when pressing next/page down for the second time
When navigating the sidebar menu, if you reach the last account entry
and hit next account, nothing happens. This commit makes it so that
you're pointed to the last mailbox instead.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-14 15:08:44 +03:00
Manos Pitsidianakis 7eed82783a
Bump version to 0.8.1
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 10m17s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 11m23s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-13 19:08:21 +03:00
Manos Pitsidianakis 3944e4e60e
meli: update to 2021 edition
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m57s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-13 18:51:30 +03:00
Manos Pitsidianakis fe0a96f085
melib: update to 2021 edition
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-13 18:47:00 +03:00
Manos Pitsidianakis 81974311c2
mail/view: show current number command buffer
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m44s Details
The command buffer in the envelope view records numbers the user presses
and then combines them with an action (e.g. type n in decimal; press
open_url action to open nth link, analogously with attachments).

Since a few commits ago, the command buffer number stopped being printed
on the envelope view.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-13 15:29:06 +03:00
Manos Pitsidianakis 64ba0459ee
mail/compose: init cursor at To: header field
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 21m4s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m26s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-11 08:52:18 +03:00
Manos Pitsidianakis 39e99770da
Use Context::current_dir() when saving files to relative paths
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 19m13s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 19m19s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-09 19:05:46 +03:00
Manos Pitsidianakis a4f0dbac26
Add current working directory tracking to Context
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 17m13s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 17m1s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-09 17:15:15 +03:00
Manos Pitsidianakis 7e4ed2fa10
view/envelope: fix some out of bounds drawing.
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 9m31s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-09 12:54:59 +03:00
Manos Pitsidianakis 45d4f611b1
Add install-man cli subcommand to install manpages on your system
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m28s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m46s Details
If meli is installed via cargo or a package without manpages, this
command can be used to install them to the user's system.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-09 12:31:34 +03:00
Manos Pitsidianakis 747e39bf55
meli: add print-used-paths subcommand
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 21m42s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 10m2s Details
Print all paths that meli creates/uses e.g. XDG data path and temp dir
path.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-08 17:39:11 +03:00
Manos Pitsidianakis 9b9c38f769
mellib/imap: don't flood user with sqlite3 errors if db is corrupted
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-08 17:10:07 +03:00
Manos Pitsidianakis bb4d200036
command/parser: unify toggle_* parsers
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-07 15:47:54 +03:00
Manos Pitsidianakis 63abf1e890
Update README.md
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 15m9s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-07 12:56:45 +03:00
Manos Pitsidianakis 7e3e938631
mail/view: fix out-of-bounds draw when terminal is small
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m52s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-07 12:37:24 +03:00
Manos Pitsidianakis c43aeb0eb1
melib/email/parser: fix invalid address parse on folded values
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-07 12:34:49 +03:00
Manos Pitsidianakis 54862f8651
listing.rs: add hide_sidebar_on_launch option
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-07 12:33:51 +03:00
Manos Pitsidianakis b673af02ac
accounts.rs: move to crate root
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-05 15:44:43 +03:00
Manos Pitsidianakis dd4d0b7972
state.rs: fix typo
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-05 15:34:12 +03:00
Manos Pitsidianakis 6476985ce6
Add Cross.toml for aarch64-unknown-linux-gnu builds
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-05 15:33:28 +03:00
Manos Pitsidianakis 6d5ebb5b04
command: split code into submodules, add better error reporting
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-05 15:06:09 +03:00
Manos Pitsidianakis f0075b86cf
ui: show descriptive tab names for composer and threads
Instead of showing the nondescript tab names in the tab area,
use Subject or To: data from the draft in the composer and a subject
from the thread entries.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-03 10:17:24 +03:00
Manos Pitsidianakis a615b4701b
dependencies: embed xdg-utils crate
No reason to have it out of the tree.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-03 10:13:25 +03:00
Manos Pitsidianakis 0a9c89b6b3
mail/view/thread: add toggle_layout shortcut
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m12s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 7m52s Details
Toggles between horizontal and vertical layout. Previously it was
decided on the terminal width.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-02 21:47:14 +03:00
Manos Pitsidianakis 49c36009ce
mail/view: don't initialize entire thread at once
For large threads, this would result in a lot of futures being created.
The user just wants to read one entry, not all of them. So prioritize
the open entry and some of the latest ones as an optimistic
pre-fetching.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-02 21:29:35 +03:00
Manos Pitsidianakis c7825c76c3
mail/view: handle dialog Esc in the parent component
Dialogues handled Esc themselves, which meant the event didn't bubble up
to their parent to let them know they should remove the dialogue. This
commit fixes that.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-02 21:29:35 +03:00
Manos Pitsidianakis 3344a8dbf6
mail/view: remove unnecessary Clone derives
There's no need to clone MailViews when opening them in new tabs,
just initialize new ones with the same metadata. That saves us the
trouble of implementing Clone for all related types.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-02 19:36:12 +03:00
Manos Pitsidianakis 1b3bebe304
view/thread: open earliest unread email instead of first in thread
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-02 19:04:35 +03:00
Manos Pitsidianakis 85af524458
email/parser.rs: fix invalid mailto() results when body field exists
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m56s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 7m38s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-01 15:57:46 +03:00
Manos Pitsidianakis 0132677ff5
commands.rs: Introduce CommandError with context
There was no way to provide context to the user why their
command failed to be parsed. This commit paves the way of returning for
invalid domain values, invalid types, etc.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-01 15:55:43 +03:00
Manos Pitsidianakis 2dc2940586
melib/build.rs: add feature to use cache instead of downloading unicode data
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-01 15:55:43 +03:00
Manos Pitsidianakis 49a38a23bf
jmap: fix invalid Type link references
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-01 15:55:42 +03:00
Manos Pitsidianakis b4f2f33576
remove deflate feature; make it a hard dependency
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-09-01 15:55:42 +03:00
Manos Pitsidianakis a337e2269e
contacts: refactor module structure
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m14s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m51s Details
To prepare for more functionality, refactor contacts module.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-30 01:12:45 +03:00
Manos Pitsidianakis 46636d8748
Bump version to 0.8.0
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m1s Details
Build release binary / Build on ${{ matrix.build }} (meli-linux-amd64, linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 7m10s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-29 15:04:55 +03:00
Manos Pitsidianakis 65e82d8896
Add meli/README.md symbolic link
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-29 15:01:42 +03:00
Manos Pitsidianakis 1c79786ea2
Add scripts/make_html_manual_page.py
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-29 14:55:16 +03:00
Manos Pitsidianakis 290cfb86c0
themes: add a highlighted_selected theme key
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 12m29s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 12m40s Details
So that if you select a mail/thread entry that is currently under the
cursor (making it `highlighted`) you can also see its `selected` status.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 23:50:17 +03:00
Damian Poddebniak 5459a84f3d chore: Update to imap-codec 1.0.0 (w/o `-beta`)
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m7s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 12m2s Details
2023-08-28 18:16:48 +02:00
Manos Pitsidianakis 31aa9ad29e
listing: autogen mbox filename when exporting mail to directories
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m9s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m7s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 18:20:38 +03:00
Manos Pitsidianakis 59513b2670
melib/jmap: implement Backend::submit(), server-side submission
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 14m12s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m3s Details
Well this was more complex that it should have been. And not very
optimized because we're not using pipelining in the submit() path:

1. first upload email bytes as a Blob object. This requires a standalone
   API post call at a specific url so it cannot be changed with followup
   calls to reference the blob's id.
2. Create an EmailObject in the drafts folder.
3. Create an EmailSubmission object referencing the email id of prevous
   call. Unfortunately I cannot get the Result Reference to work in
   stalwart jmap, so for now this is too a separate transaction.

Caveat emptor: Errors might not be returned to the user.

Closes #277.

https://git.meli.delivery/meli/meli/issues/277

https://git.meli.delivery/meli/meli/pulls/279

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 17:16:38 +03:00
Manos Pitsidianakis 38bc1369cc
melib/jmap: add an Identity type.
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:54:34 +03:00
Manos Pitsidianakis 5d8f07c805
melib/jmap: rename some objects better
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:39:20 +03:00
Manos Pitsidianakis b95f778335
melib/jmap: move JmapSession to its own module
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:39:17 +03:00
Manos Pitsidianakis 29fd8522e6
melib/jmap: implement Backend::create_mailbox()
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:35:13 +03:00
Manos Pitsidianakis 31982931f5
melib/jmap: use Argument<OBJ> (value or resultreference) where appropriate
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:31:05 +03:00
Manos Pitsidianakis d9467d5fcd
melib/jmap: save all core capabilities to session store
We will need this in the future when we're going to support extra
extensions like Blob and also now to support server submission.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:27:34 +03:00
Manos Pitsidianakis 11432ba2c3
melib/jmap: make `null` fields into Option<_>s
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:21:47 +03:00
Manos Pitsidianakis 4f9b97736a
melib/jmap: Rename EmailImport to EmailImportObject
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:19:18 +03:00
Manos Pitsidianakis 6ebdc7f9ae
melib/jmap: add Id<_>::empty() contructor
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:16:11 +03:00
Manos Pitsidianakis 37a787e6bb
melib/jmap: use IndexMap instead of HashMap
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 14:12:05 +03:00
Manos Pitsidianakis c875dda496
melib/jmap: add last_method_response field to Connection
For book keeping.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 13:58:20 +03:00
Manos Pitsidianakis f7a4741bf1
melib/jmap: add jmap-trace feature
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-28 13:47:42 +03:00
Manos Pitsidianakis 3433f7c41e
.gitea: update PULL_REQUEST_TEMPLATE.md
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m31s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m49s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-26 11:31:30 +03:00
Manos Pitsidianakis 9037f08495
listing: replace hardcoded Key::{Home,End} values with shortcut values
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 9m23s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m56s Details
Replace hardcoded Key::{Home,End} values with shortcut values
"home_page" and "end_page".

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-26 11:25:20 +03:00
Manos Pitsidianakis ffba203a3b
sidebar: add support for Home and End key navigation
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-26 11:17:21 +03:00
Manos Pitsidianakis 8551e1ba0b
clippy: fix new 1.72 default clippy lints
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m23s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 9m16s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-25 19:49:26 +03:00
Manos Pitsidianakis 64982b4cab
mail/view/thread: fix page{up,down} event bubbling up
When pressing PageUp, PageDown, Home, End shortcuts in ThreadView
entries, the event is bubbled up to the mailbox listing because
ThreadView::process_event() didn't return `true`. This commit fixes
this bug.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-25 18:20:56 +03:00
Manos Pitsidianakis 4d22b669bf
Cargo.lock: update dependencies
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Failing after 14m14s Details
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 7m40s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-25 08:25:12 +03:00
Manos Pitsidianakis 974b3a5305
Update bitflags, rusqlite dependencies
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-25 08:22:36 +03:00
Manos Pitsidianakis f162239fcc
.gitea/workflows: change `on:` conditions for test.yaml
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 7m58s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-24 11:37:35 +03:00
Manos Pitsidianakis 946309c6f3
melib: do some small parser refactoring
- Use HeaderName in parsers instead of raw byte strings.
- Use byte literal constants where appropriate instead of repeating
  &b"___"[..]

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-24 11:32:21 +03:00
Manos Pitsidianakis 66c21ab173
melib/email: move StandardHeader to its own module
Extract StandardHeader code to its own module to reduce name.rs
line-count size.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-24 09:11:46 +03:00
Manos Pitsidianakis 3963103d55
contacts: prevent duplicate contact creation
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m9s Details
When adding contacts from an envelope view, its hash/id/key was a random
Uuidv4, so it was always possible to add a contact again and again with
no limits. Now, the id is calculated from the hash of the email address
and display name, preventing duplicate additions.

Note that the hash algorithm is not supposed to be stable across
versions, meaning that in the future the same contact might have a
different hash. This means a more sophisticated method for
detecting/disallowing dupes must eventually be introduced.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-23 17:29:21 +03:00
Manos Pitsidianakis ab57e9420d
contacts: add delete_contact shortcut
New exciting feature: contact deletion.

Default shortcut is `d`.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-23 17:27:48 +03:00
Manos Pitsidianakis 095d24f914
.gitea: add PULL_REQUEST_TEMPLATE.md
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 13m50s Details
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 16:07:41 +03:00
Manos Pitsidianakis 96f0b3e6b4
components: fix shortcut section order
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 8m2s Details
Shortcut sections are shown in order, sorted by focus--as if widgets are
stacked vertically by the order you've opened them. In some widgets that
order was wrong.

Also, when a parent widget retrieved its child shortcuts, sometimes it
overwrote children sections if they both have them. This commit adds a
sealed trait ExtendShortcutsMaps that instead of overriding them, it
merges them with the child map having the priority.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 14:53:29 +03:00
Manos Pitsidianakis f193bdf685
meli/jobs_view: add column headers and sorting
Sort with `sort <index> [asc/desc]` command or by pressing `1..5` keys.
Press them again to toggle between asc and desc.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 14:48:24 +03:00
Manos Pitsidianakis f93adb683a
meli/terminal: replace change_color uses with change_theme
change_color() predated addition of Cell Attributes (Bold, Underline,
etc) so it didn't accept an attribute argument.

This commit adds a change_theme() function that does the same thing as
change_color() but also sets the cell attributes. It also takes a
ThemeAttribute as an argument instead of {fg, bg, attrs} individually.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 12:45:15 +03:00
Manos Pitsidianakis a1e7006186
melib: move Sort{Order,Field} to utils mod
We want to use SortOrder enum for non-thread purposes in the next
commit, so move it out of the thread module.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 12:41:50 +03:00
Manos Pitsidianakis 52874f9a97
mail/view: cancel previous jobs on MailView drop/update
When MailView loads a new thread or gets dropped, cancel all pending
jobs.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-21 10:19:14 +03:00
Manos Pitsidianakis b3858de2f4
melib/error: impl From<io::ErrorKind> for ErrorKind
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 7m43s Details
We inspect errors in the frontend to check for network errors. If the
network error comes from std::io, this would get converted to an Error
with description "timed out", kind OSError, and source the actual
networking error.

This commit converts network std::io::ErrorKinds into appropriate
native ErrorKinds.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
2023-08-19 09:11:59 +03:00
Manos Pitsidianakis dc2b00442b
melib: run rustfmt and cargo-sort 2023-08-19 09:07:55 +03:00
Manos Pitsidianakis da8e810448
melib/connections: remove leftover debug prints 2023-08-19 09:06:31 +03:00
Damian Poddebniak 4f6081b663 chore: Update to `imap-codec 1.0.0-beta`.
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 8m0s Details
2023-08-17 17:36:13 +02:00
Manos Pitsidianakis 67d2da0f88
ci: disable smtp::test::test_smtp in test.yaml
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 7m47s Details
For some network-inside-docker reason this test fails, even thought it
works on my machines(TM).
2023-08-16 20:34:33 +03:00
Manos Pitsidianakis df638cceec
melib/connections: remove stale failing doc code example
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 7m50s Details
This leftover doc test/example was failing to compile, so remove it.
2023-08-16 19:50:48 +03:00
Manos Pitsidianakis 97d3686815
melib/connections: use Happy Eyeballs algorithm Ꙭ
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 12m17s Details
This commit adds a Happy Eyeballs [1] implementation taken from the
happy-eyeballs crate, which is in public domain.

While the function lookup_ip[0] iterates through the addresses returned by
A and AAAA records from a DNS lookup, it returns the first one  which
always is an IPv4 address, unless there only is an AAAA record.

RFC6555 [1] recommends an algorithm for choosing the fastest address to
connect to, called "Happy Eyeballs". Ꙭ

[0]: melib/src/utils/connections.rs:497
[1]: https://www.rfc-editor.org/rfc/rfc6555

Fixes #268
2023-08-15 09:55:46 +03:00
kdwarn 6578a56668 Update cargo install directions
The release available on crates.io is 2 years old and would not compile ("enum `TokenTree` is private"), but installing from repo works.
2023-08-14 18:35:09 +00:00
Manos Pitsidianakis 0f60009ea9
Makefile: add RUSTFLAGS with -D warnings
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 8m21s Details
2023-08-11 19:15:14 +03:00
Manos Pitsidianakis 5c2b04719b
Normalize std::fmt::* imports
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 7m42s Details
2023-08-11 13:16:47 +03:00
Manos Pitsidianakis 7c9a4b4b7c
meli: Move components/mail -> mail 2023-08-11 13:01:32 +03:00
Manos Pitsidianakis 64ab65ddff
meli: Move components/contacts -> contacts 2023-08-11 12:49:06 +03:00
Manos Pitsidianakis 005bf3881e
meli: Move components/utilities -> utilities 2023-08-11 12:46:16 +03:00
Manos Pitsidianakis a5446975c2
terminal: move braille and screen to their own module files 2023-08-11 11:00:59 +03:00
Manos Pitsidianakis 7c7f6e1923
melib/thread: don't increase Thread length for duplicates
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 15m44s Details
If an envelope that was already in the Threads object was inserted,
the only modification would be to erroneously increase the Thread len by
one.
2023-08-10 18:48:53 +03:00
Manos Pitsidianakis 84081f4ed7
melib/nntp: minor style fix 2023-08-10 18:48:36 +03:00
Manos Pitsidianakis bf543855dc
melib/email: add PartialEq<str> for MessageID 2023-08-10 18:45:44 +03:00
Manos Pitsidianakis 448e0635e0
melib/nntp: log error when command length exceeds 512 octets
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 8m59s Details
According to RFC 3977:

> Command lines MUST NOT exceed 512 octets, which includes the
> terminating CRLF pair

This commit adds a log::error entry when any sent command exceeds that
limit and recommends the user to report this as a bug.
2023-08-10 18:31:22 +03:00
Manos Pitsidianakis 4e654d2d02
melib/nntp: limit LIST ACTIVE command length to 512 octets
According to RFC 3977:

> Command lines MUST NOT exceed 512 octets, which includes the
> terminating CRLF pair

Sending a `LIST ACTIVE` command with lots of newgroups and passing the
512 byte limit is therefore invalid. This commit splits the mailboxes in
chunks and sends a separate command for each maximal chunk that has
a valid length.

Fixes #269.

Reported-by: r3k2
2023-08-10 18:30:59 +03:00
Manos Pitsidianakis 40d4ecefa0
melib/nntp: accept invalid (non-ascii) address comment text
NNTP servers may return addresses that are not RFC 5322 compliant. An
address with a comment with non-ascii characters will make the parser loop indefinitely.

Fixes #269.
2023-08-10 18:29:58 +03:00
Manos Pitsidianakis 0ee1b6e018
account: start background watch job in init
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 10m20s Details
When IsOnline was introduced, the background watch job stopped being
created when the connection was initialized. Restore that behavior.
2023-07-28 19:34:56 +03:00
Manos Pitsidianakis 8cb2a515e1
melib/smtp: use localhost in lieu of 127.0.0.1 for CI
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 9m49s Details
2023-07-26 12:40:32 +03:00
Manos Pitsidianakis 6e27edcb77
ci: use cargo-nextest
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 9m19s Details
2023-07-24 14:14:48 +03:00
Manos Pitsidianakis ae25ffba43
melib/smtp: don't do plain EHLO before starting Tls connection
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 6m7s Details
2023-07-24 10:57:35 +03:00
Manos Pitsidianakis 1e084c1d85
melib: move backends out of the backends module
No reason to have such a deep module tree.
2023-07-22 22:17:01 +03:00
Manos Pitsidianakis 9216e7bc65
melib/connections: add opt id string for tracing 2023-07-22 22:17:01 +03:00
Manos Pitsidianakis 8ecdb6df31
melib/imap: add imap-trace feature 2023-07-22 21:27:05 +03:00
Manos Pitsidianakis b65934facc
melib/nntp: add nntp-trace feature 2023-07-22 21:15:59 +03:00
Manos Pitsidianakis 89c90f224a
melib: add `nntp` feature 2023-07-22 21:01:42 +03:00
Manos Pitsidianakis 7db930cabd
melib: rename `jmap_backend` feature to `jmap` 2023-07-22 20:54:55 +03:00
Manos Pitsidianakis e9f09a153c
melib: rename `mbox_backend` feature to `mbox` 2023-07-22 20:52:37 +03:00
Manos Pitsidianakis fe7dcc508e
melib: rename `notmuch_backend` feature to `notmuch` 2023-07-22 20:51:12 +03:00
Manos Pitsidianakis fe027fa300
melib: rename `maildir_backend` feature to `maildir` 2023-07-22 20:48:09 +03:00
Manos Pitsidianakis 129f10911b
melib: rename `imap_backend` feature to `imap` 2023-07-22 20:46:23 +03:00
Manos Pitsidianakis 51e9fbe8f2
sqlite3: add account_name identifier to sqlite3 index database name 2023-07-22 20:43:08 +03:00
Manos Pitsidianakis 4874e30f3c
melib: add smtp-trace feature
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Successful in 5m52s Details
If it's enabled, every read/write in an SMTP transaction will be logged
on TRACE level.
2023-07-22 16:25:54 +03:00
Manos Pitsidianakis 073d43b9b8
melib/test: move data files to data subdir
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 6m22s Details
2023-07-21 18:12:39 +03:00
Manos Pitsidianakis 8e698cabcf
Fix unreachable-pub and disjoint-capture lint errors
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 6m8s Details
2023-07-20 00:19:42 +03:00
Manos Pitsidianakis 1d0405ed5b
ci: add env vars
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 3m48s Details
2023-07-19 23:23:05 +03:00
Manos Pitsidianakis bb7e119ade
Add gitea CI workflows
Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (push) Failing after 29s Details
2023-07-19 17:12:15 +03:00
Manos Pitsidianakis 29b43e2c88
melib/datetime: replace mktime with timegm 2023-07-19 15:53:30 +03:00
Manos Pitsidianakis 2df7354751
mail/{listing,view}: fix overflow substracts 2023-07-19 10:19:11 +03:00
Manos Pitsidianakis 6280bc75e5
melib/jmap: fix blob download URL formatting 2023-07-18 16:13:58 +03:00
Manos Pitsidianakis 48a10f7241
melib: remove unused BackendOp::fetch_flags() method 2023-07-18 16:13:58 +03:00
Manos Pitsidianakis 0219dc8707
melib/jmap: respect max_objects_in_get when fetching email
Fixes #144
2023-07-18 16:13:44 +03:00
Manos Pitsidianakis c4c245ee19
melib/jmap: respect danger_accept_invalid_certs setting 2023-07-18 16:13:41 +03:00
Manos Pitsidianakis 53cba4beee
Update README.md relative file paths 2023-07-16 20:06:44 +03:00
Manos Pitsidianakis 561ba9c87b
listing: add relative_list_indices setting for thread listing 2023-07-16 14:13:55 +03:00
Manos Pitsidianakis 8abc9358a7
mail/pgp: add newline after Version: 1 header 2023-07-16 11:46:45 +03:00
Manos Pitsidianakis e9cd800f49
melib/nntp: add support for storing read status locally 2023-07-16 11:46:33 +03:00
Manos Pitsidianakis 519257b08f
listing: add relative_menu_indices setting for menubar 2023-07-15 20:29:25 +03:00
Manos Pitsidianakis ab418c1d39
pgp: refresh documentation, fix encryption/signing
Closes #259
2023-07-15 19:33:20 +03:00
Manos Pitsidianakis cf9a04a591
Add metadata to Jobs, and add JobManager tab
Opened with command `manage-jobs`
2023-07-14 00:24:38 +03:00
Manos Pitsidianakis 369c1dbdac
view/html: show `open` command in status bar 2023-07-13 23:02:10 +03:00
Manos Pitsidianakis 4e55fbc90d
nntp: add SEEN flag to all envs, since NNTP has no flags 2023-07-13 21:03:55 +03:00
Manos Pitsidianakis 5ceddf412e
Update CHANGELOG.md 2023-07-13 17:21:10 +03:00
Manos Pitsidianakis 13fe64a027
Cache pgp signature verification results 2023-07-13 17:18:13 +03:00
Manos Pitsidianakis 6086a3789d
Fix libgpgme segfault error and re-enable gpg
Closes #255
2023-07-13 16:51:37 +03:00
Manos Pitsidianakis 5b5869a2ec
logging: re-enable print to stderr ifdef MELI_DEBUG_STDERR 2023-07-13 16:49:50 +03:00
Manos Pitsidianakis 866166eb8e
attachments: don't print parsing error for empty bytes 2023-07-13 16:48:59 +03:00
Manos Pitsidianakis d4e605c098
Add tagref source code annotations
Source Code Annotation Tags:

Global tags (in tagref format <https://github.com/stepchowfun/tagref>)
for source code annotation:

- [tag:hardcoded_color_value] Replace hardcoded color values with user
   configurable ones.
- [tag:needs_unit_test]
- [tag:needs_user_doc]
- [tag:needs_dev_doc]
- [tag:FIXME]
- [tag:TODO]
- [tag:VERIFY] Verify whether this is the correct way to do something
- [tag:DEBT] Technical debt
2023-07-13 16:47:11 +03:00
Manos Pitsidianakis a5770c89f4
Add Woodpecker-CI check pipeline
ci/woodpecker/push/check Pipeline failed Details
2023-07-11 13:05:52 +03:00
Manos Pitsidianakis 74e15316db
view/envelope: open message/rfc822 attachments in subview instead of new tab 2023-07-10 08:34:35 +03:00
Manos Pitsidianakis d93ee413a7
melib/datetime: add timestamp_to_string_utc
Tests were using `timestamp_to_string` which in turn uses `localtime_r`
which assumes the local machine's time zone. Use gmtime_r instead.

Fixes #252
2023-07-09 18:50:35 +03:00
Manos Pitsidianakis c2ed3e283f
view/envelope: fix Source::* view showing only envelope body
Instead of the entire envelope source
2023-07-08 13:59:09 +03:00
Manos Pitsidianakis b0e867eb68
Move src to meli/src 2023-07-08 13:58:32 +03:00
Manos Pitsidianakis b5657201db
melib: fix doctest compilation errors 2023-07-08 13:58:15 +03:00
rek2 3803d788ab
if auth is false checks if config has password entry 2023-07-07 14:00:16 +03:00
rek2 b5f205b77b add availability to use server_password_command in the nntp backend like in the IMAP backend 2023-07-06 05:23:24 +02:00
Damian Poddebniak 7c33f8999b chore: Use published imap-codec 0.10.0. 2023-07-05 14:46:39 +02:00
Damian Poddebniak 34a54d3c05 docs: Add some `TODO(#222)`s. 2023-07-04 21:32:07 +02:00
Damian Poddebniak 9d51b6bd52 chore: Update `imap-codec`. 2023-07-04 20:56:51 +02:00
Manos Pitsidianakis 7998e1e77e
melib/datetime: add missing LC libc constants for openbsd target_os
Fixes #242

"Compilation failure on master on OpenBSD" #242
2023-07-04 00:23:47 +03:00
Manos Pitsidianakis 957abf4e72
Update cargo dependencies
Concerns #242 - "Compilation failure on master on OpenBSD"
2023-07-04 00:21:16 +03:00
Manos Pitsidianakis e3dfeaad7e
Fix compilation error when building without `gpgme` feature 2023-07-03 14:59:37 +03:00
Manos Pitsidianakis 619fbef129
melib/thread: recursively calculate update_show_subject()
Walk the entire thread tree and update show_subject collectively when a
new entry is added.
2023-07-03 11:05:16 +03:00
Manos Pitsidianakis 342df091a0
mail/view: don't set all thread to seen when opening a thread entry 2023-07-03 11:04:49 +03:00
Manos Pitsidianakis 1bcc0bbece
melib/mbox: add mbox parsing test 2023-07-03 11:00:51 +03:00
Manos Pitsidianakis e8e49e741b
melib/mbox: fix wrong per message offset 2023-07-03 11:00:49 +03:00
Manos Pitsidianakis 1dc1d86848
melib/shellexpand: fix infinite loop bug
Introduced in recent "fixing clippy lints" commit
2023-07-03 09:52:03 +03:00
Manos Pitsidianakis ba7f5dce1c
listing/thread: fix display of threaded conversations tree structure
When missing intermediate and/or parent messages in a thread, the
printed thread tree branches were completely invalid. This commit makes
sure thread node entries that have no corresponding envelopes are
accounted for in the tree structure.
2023-07-03 09:46:28 +03:00
Manos Pitsidianakis 0b258a1f05
meli: clippy lint fixes 2023-07-03 09:38:51 +03:00
Manos Pitsidianakis 5f29faa640
melib: clippy lint fixes 2023-07-03 09:38:47 +03:00
Manos Pitsidianakis 6858ee1fab
meli: move subcommand handling to its own module 2023-07-03 09:38:43 +03:00
Manos Pitsidianakis f98e36cee5
melib: Replace old-style /*! module doc comments with //! 2023-07-03 09:38:37 +03:00
Manos Pitsidianakis f0d88005fb
melib/email: change message/rfc822 Display repr
Put subject first.
2023-07-03 09:37:56 +03:00
Manos Pitsidianakis e64923eeaa
melib/email/headers/names: fix debug_assert condition
On invalid parsings, _cnt can be equal to probe and chunk len because
the value won't be a valid header
2023-06-22 14:06:39 +03:00
Manos Pitsidianakis 65179d4816
composer: fix cursor/widget focus scrolling logic
Scrolling up/down with scroll_{up,down} shortcuts didn't work correctly,
because the form widget used its own shortcuts. This commit refactors
the cursor logic.
2023-06-22 13:23:27 +03:00
Manos Pitsidianakis 0c0a678cff
state.rs: fix overlay widgets not being reaped after Unrealize event 2023-06-21 12:11:01 +03:00
Manos Pitsidianakis f5cfbd32e6
melib/imap: on set_flags, update {un,}seen sets in all mailboxes
Some envelopes might be in several mailboxes, for example in Gmail's
implementation of IMAP.
2023-06-20 13:22:52 +03:00
Manos Pitsidianakis 363f493099
listing: add {previous,next}_entry shortcuts to quickly open other mail entries
When reading a mail entry, with Ctrl+n you can switch to the next entry,
and with Ctrl+p to the previous one. They can be reconfigured by setting
the shortcuts.listing.next_entry and shortcuts.listing.previous_entry
settings.
2023-06-19 22:15:06 +03:00
Manos Pitsidianakis 8cab9d9da8
listing/thread: add option to hide consecutive identical From values inside a thread
The config setting is listing.threaded_repeat_identical_from_values and
the default value is false

Before:

data:image/webp;base64,UklGRiIlAABXRUJQVlA4WAoAAAAgAAAANwMAdwAASUNDUKACAAAAAAKgbGNtcwRAAABtbnRyUkdCIFhZWiAH5wAGABMABgAjACNhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJWUDggXCIAABC3AJ0BKjgDeAA+kT6bSyWjIiGhka34sBIJZ27dX0EOqLnw8Hsord1P5fhIYpNsT6IOfq8//IsfV2/w+Sk+Qf5t+K/8z/Tv2b/wP+I/J/+o+RL7b+5fmZ/eP+p9p3lf04mqb0//SfyA/Hf7z/un+u/v37PeQT9A/znqF/jX8P/o/2ZftNzHGcf5X0DvXH5X/hv79+SHza+wf5X80vdL66/5T3Bf8l/Mf7Z+a/7////69/qH94/qv7//0H2le9P6d9yH2C/iT/hf5n0yP6j/B/vV/rf//8yfoH/W/1z9tP3////6Lf2P+Q/7z+wf9z/6/4r/////7U////0iwRKB+16NPpWBjSw1eu9v3gVoBpCrSue1gpBqdY8lDCD5SoYDopxQipVf6DmeuFHlcScuD0rQX+7MyfqewG9p42ZSpB7Q5tmcOCwS5Tz7liiGUNA0xtnzm9lDFvLMhARxufUB8DuH5wKGqRw0GSdjT19WOnVpwKGqRw0GSdjT19WOnVpwKGqRw0GSdjT5JG3JiLpyFmvuLlfonK4VV3mxzvK96fsx1VrlcEiCST+YepZel8PgZzYwf2uoL1s5G/Lw5nGZG4LkMeNHl7vBJitYe+GDN5KSafPa7hGWyHlj2H8KqAnNXs9B2UeWdsO2Q3nDJ3oraRMG1yrNxr+er6mBMw5LIcf+Q32GFdkumc5xq+cYi9272ZACNamYVVhavkmChDhmUfpyUVXLz3nyuKvPpkbjOYqfjnNvomTbWRROx4YIqo7mB3etcvEhrw/4PpjzBS9WhpN6mXYzCf5CqveKFzy+9PgMWGWTEjWYGY3L9KrryfHKWS9FxNMCJIiSmSdUuVz26uY2SSoBqA7FY3Z2B/J9p4VgJGY2qTdj084qn5dNthCn/9T9NovWdRUnYQiznWK03QKa2HVi/h4XN41VTeDSB/fYzQiz46iS+UfNbvEycovdPqrFk8q3uFhyhHUhpZjMVeGjuNR/gfLCwD78MY8nWdXytFIrOgZn6+NkEhVazie5FwZd9VC650gpgedaMDLgl99sUC4oeJ2kJNqgoQe6klRbVy637OKAFGkMvZsMlHMFTitRgAGopA9YD5FU7giGCExMw3XXfG+53BoYfLItscFcGsvn+rx3m7svYJflddLqr1dj5VfHJN1/7K7eoTWTHKZNlEHevsh9YxIrWD0I3n5lbk/nETW5gG2Kz4z9UZLbqWYvucK9vnAUCXdDLszaX5TLGm3gILt/4NrmbEUhz9qmCS+neKpdRfEz8+/nife3Iiv9nSrkWCEy1u4eDDIcn1VZaSP/qub80CvfvV+ReEK6+iV48ynIvp+aEoSqt4FuEY4rZ67LUek5nDBsWfXhiawInw3aiz9KPvEOPzTPZHaKshIYWrpjZmd0vdBlJua5/jFJqGVrbMjB4ryOqOiMVdR5V/9TWC5hMWByqVYn+Phxo0nsRlQKEIHCKB6IsKljURPpxx1RDH/Hyaixo+b/LEP5IuvRi+XEJdL0zvwcHQ+0ZgGrVllZcgsNiL8rVmQEtedmcSmopPQYRzgeevqTotc3h+cfZTB9h2GQN/oymuoaR1/3pGegK7tAJ/GSymTcP9B0Y+7E5sJmV+6tUSf8CvDgiFqpZvtz0kOz44DVnbmewYcRKBODodIs5CKuQGgt089lr+NhVOkQEW1XtfDWCqsDjvHMFc/b1Yxy3efxe6FIhZi/rYVToQK4TZPXLWrTES6YvEIS20JaiHJj4vANbURG5hsgLgxzBZxGVzOdv0wTFJfT3JMkZJHRnzS67zG0uXmHKuVkQwrJXGABMKdX42RMjEDsB0XW8j7tAPnS3jbDxSSHykdSalWWTMFMkJMFlUSeGu7XMvajeW0TgPW4spoIiT5cvCatEK7VZJ7DkJHUBlAhf2/hpx8VVRlJosUdXFSMwoyIpatSi0clFoYo2GeL5Zk/75OhO1Lvw68jiVEUAAD6zjWwgAQTW1I/m4Ctc4La00bYW/W27iQ7UoUuvqptzmXixYhdeFe2gf0XdAvBZW+IWarqhDUpz+WrYNBEdCjARf7mBP7N3zOb8GB3KQMk06EJ7KtN+KifKzhdz1TT+AF7Cv7Hd2Lke6QycsHF30wDU/uq7WdFYiQvM9mcPzNIvpjp9CpqdOVUMU4iJA03rR2NI2AqSMm2c87qfhLXGxSmwwnX27hYSsIASNU17HcrdtwL5gVBmx56Asc9CIw12KK7CtKqXUEmmnxc3Ip184Bjh0a3f6BkVHibpzQ5h8lz5s0qIhi+LGxuo6A5MyNzwI/hhs6t/t/NqDtf+8WwJJg+rsE1NLnh3MrtZTLD7kB8BBJsQvtb8mSKEJhQv0RisJpdqKwJhWEdgY7Y0kjbyUh3s2XzOQMpcYEPRWVke6dkG+zzhp7TccYo4deg7k1GU5Y9tnKxcbyV180aS4YXZgS92aQ72bL5nIGUuL1QWbihbjlpyVNJDeMCEsDF/V9FEwb9g/dul8UHS8xekh8w5trLEgAXDgVRxDKjif5XJXUsyxLjTtSrmkdM7lPOHn0C8vYnGYEvHU6Hb/0a2Lff4srs7IC73meGKk3OZSKqLzIxMivE5VLj2cnEi7LzBGBeHi/EH0sHemD0u+ulxC8PzxvciE/iyuzsgLveZ4YqTc5lIqovMjEyK8TlUuPZycSLsvMEYF4eL8QfSwd6YPS766XELw/PG9yIT+LK7OyAu95nhipNzmUiqi8yMTIrxOVS49nJxIuy8wRgXh4vxB9LB3qPaqqqXh6Q4Fczd5BgsDKbRM4JlGI+Y1WOiLFfXsVfJcO1lnSVAgV2pcf2C12+d+tJqc1heIRMsxnNHrZHc7iQTJhAmPptRkhUtsFYOLpJ8BBqEzY1pluX7//5gQ6cEAvY6fxsITiHS9DNdLXcyQaPUfRr+oSldQj3PX1EpjJf9/uXCNtyp6Zu71sIDCL/CaTXsSkbaAPkwJFbV0c/fXc7mPe51xwPOyZnZPeMpyClblQbMeqzmBcoZqWAZ1FhAFYCRgcS/ujLdlAysH3Ke80WEtxOkMneVgWHbO/QOPxjynm38mQSeZJ4cdqkv347YZjHRt08QFB2PxtAYL/TsCjwpATVCZwO+/iiIFriXkyMIwG3W0fySd4d0HlvgNvmR2zCaHkhU8YNo2Y4u7btnj6JMLa/s8Ijs+CGQOKJwc6Usf0kEt8RJppwI/G9DQj3Wcg1ul5oyt1G1Vf3wD8cfaAH3LQKMYRwAigIBiaJQ0TyQV3vaZ4+ydLkJOGV++Gv3sTyfrTehZXPfLe0Li2gmgTJljUZAtbPNeVWiBwiipy/8tKQUFY/W5pSE/sYlVcHp7tCinIqkARdV6hkABpGTm9ZLCbw7WQzUjhTZZ5qJwhdXEhmZCsmJMP7TPUN2NrlFpe0+aBkn91CK7v4yBWvRAH7Uzp+ps2/ltYhM38imgBP3MBY2c0DwARpLEjHtpXBmXCTs4AqL83IMZutI/6VuoNcS/6gIwlAXqaUc34LMyIwTyO1dKc9fOCGIgyj5PGPxQVSZN3BPqdpZkaLMJhbsSUf8GVzurnd5UZMAbRFrm4qRULibNSdXqjs/NdVWQcTeHWu2LPJ/D9/4oT1pXCLOj1z+kAKjocp5ZU4xZ0VSHpEKkr9AIroJN/IxeceaRsApLkDouSzvVl1AShlsjh9o7o6xUi+Ccc+RB+xyyQakTvb1UVFNQcV+Yvy2vANfTAUGRMfNWamRhXn7+wYojYqZEVRLEcJl1+9d69f9DQWc6JZDRnhR/j9uOWJF9Ytpg0CWJywmZKztjXcECd5QBSggBBw2d9s/z39/a4hulE96J8Jsa9uWspMDK6l8ptHzllvWBHGHzPlRqPmbs3yaoUCfD5840VLAi8zuMRLylkJX9kbxzxVZvJmSF6MpgXtNT2j/Qd+AyohhdOXWfwaWL5Czhg4/YgqTNqtqDP/mK+4H9WoxowOfQBBfg92j83/WZLh4PfVzm8vfu/zfJBAXZZD8BgyXnMqtFz9iq0c0EztoDiyUx+1DHI3Vxx/WSXZI+gPNjXBooqRGwH+PS38sd7E5sC4Ja2d/1zWOhgZZXiYYgphw1Pxsgk5NhDwxt3v9jXt75wmHbfphwISNkri8yc5SGaeTP0WjyvfGFmOm2G94N1y1A6GDWEIPCs02Asmxr28mZK0NA8UXX8ZNKRokpOd7VRviex/X8U0W1jE5RMhkzhK3QMD9hUIjmYHtQx0kw+wGry5u8HKIYb/zhDT02InOEBhxpKKkUETIgtT6nMcGrOkPV/0BUA1GI10RMUpgUvm3A/c+Ef2Oln6LfEXF3JUfcH0S/vqTmJBx76tunMwdu/wPbOBW4nUyte54RDPqgbKJGNTvkBjs581WcwKVe5JcHGRn76JZgaV9Nek5qx2iSPzolkNbidTK3yFnKx7dpt5/34HYQClZMHiHAcitMvqKhEqEQV8m+fK3ZVrQ+4LhhGeN5E0lpLyAyjtDGXpkIyEfGIxCFzIZ9fBdDv34HYQClZKu+FoCaqnPsxsNIcF26FDBwKEmjA5HY9b3HtQx0kw+sUXR8bdIocnOUiiF/NVofxMYw9ghNEcnrn8gz4byQnSYFyqv7TldQ93X23vfbHy5W/hjZX2jyz9OcPKivHcglPi0FfE14RCrU916PAaYM4b5yH1nuZBibg1ZROreosgFBFiz/I5MHue0v95G03W0ciUCWQX0HIrpZLS5Noevw9WxlQ0s2baXcWkW//KMcnckMVKUpR7fJzPBRsK1Khi1oikAB5Gr+Zeosz0O/6Xbek3n0D26GFWP9PfWqzMi8/jqpyi2OtDgbfFvLFMJH/1Wa9GVZsGXbEYA7uNeT2Fq8BNsBxqcNixJsp2MCcAQXmB3IsYNy4oAztMCeImWg4WjrP0TSfWmw05HQiUlfwGbN5jA45N+kBA0cXGnR2FPJx4mGZW1DXjAcZiVtBc3jCUBjk7qTmUhXYh9PkuLOFEWTjZKoHK4NcBqDTx775Z95x1dMnfjytRcvF8zGAZU4Gu+FwkFJ/l+TC4pflU0l7fjd0RoBi270Rj4igfFAAf86Wmq8Jpgblt99na18lpTs7aYTdM+qlPPEWhyys7m41GNoUo4uHbn5RL4oH8uED8YQY4Hk2xfQeXO7lU0l7eulOgWzkYZf3mDNFOSFCHkeBKD6aCE+5iEnFzOztqmBl7b2r3UC+x+Vuh6w2dl1QzZe7djGZZHp+QIyfu3NK0d1EX3541AXxknIGFbgp8k4qRPLxhzzLnkDuvPxosH8HoB5WV5JUMDTx9VbQRtpMM7W4pYm772ujFlN/EWOsEGUWm8L7iYqKkZ+hinMEpFAIm7mVv0A8rK8kl8jmiBt0ShMr1AYD8Lb6P5q5RQPi4U5gTcuc9scCpE+dscg1XpJWokVVCY+ASTylWs14e9++ZJwWfxtUubV18KUB6QQQS9MhEOZU3XnblaB5GHNx1B/6tJB8eMIYyU3z1OuY5jjtytA8jDlzCZffaH/RB2mrIIikG2CCg4JVdR5ioEjI1jIxjmptbqFTKdY9zksg6dZvm8h/Au814E96Jr5eyb0801iCkrymbmVKvHLyHioEjUxsOeYBlHTbV96M3SvSXozqQuzll++1S/gZqZUTu7T60GP3FXZ/xtXXJX1fbi9TCbQBIzSU2Yu84RmBDIUdpk57ilB25cfAy5IkQJI8C2jVey082qFXlD228PUWAyXSEcJXZJ4loY148o6XBzoZAB/VmSelzX/0WIwg8kTPHD/+Zfhap4O6S0aarNiy3NCT0PS+3I9Jb1ogrfEimE05OHjBY3i8te/4AFcRTzduCrTMZG+6MY6fuoFm5neH7vBWogigFkVZ6fCwLPZNiRlH6B2v5aNm8sY09lU3kCYLhbmi6Z4TlWg9kwt17ljrGXrcHajk/rkEVj/7whtDyr+zD2EoyGuWMlezBJEa1PxqezvIQQXs1MVrpfwlvPYVoxXbDYuOX94GEenkxbaMubXe6PFWDPSl7TZWnDygfAEjwWQsG5Wp/4lMeTosjzaXnMlhKIxEw/bRKQXGTwu9AhwmwqZmVNFo7V5f0OTYJvaU/bOf5fGjeZ+U9/RfsjM6bqzzJ3zwWM5wZZuTM+LoufeOB+xrjAfg/S6jrG2bP9zkYOgizYujdF6/ZYdwhVYVkKhAC8I5/4M4/k+wPI2L+m3gQLbCZgMP+hkXtyH++CxcHkZWOiEv99CDVPQ3gIyaTtq7COUEtlHZLpxXvsUdIfK0z3Y0jbJ3LiyxhYPP1dFskbnWkl13v5izZAoBCwXVrZVIqcxX8hYPBPZIu0BytRaspwYilCY4ar0j38eStx+frQ2jXviU4P6WZhZRrKOKLDq84rEowcljgvaL32rn6+uaKgmA+aXgvUxxLgJRnmZkRdbdt6FLMkAhAkVpcHgggZarIDFoyoiVJXLUlpunlUjhngu4jlggcQCd6wQQfP/CZwmppuAQQbMyh+CF/w0tfQI7eDvrMyRELiESvofxUCIvud8vSm91IpwPcHbqk8cdgl5BjEI4H0JKucYtlMUXvQcrTHo1kHvCMYh6ls9d45UbdLokL3EUUyra0ooiia0hqhJTndkP/jIg0K7ADb0J5Zp7H4IX+WosapQBAgs9qg8EDoEcfgW0apUUyFTDDxBh39rw3XQpaLBjO/7DDBnAe9TZcrry3VtgC7GP6eNKVTlaY9GA9ZqGkPc7+HlSpU7LE1n46m8GX2FHUfAwR+fclLGiHioEaracvvf+oAdRuwvDWmF8gmWQg3f4EyMxXlb3NWdkxrK8kqJzK3hrCgOWDfXeazDVXvfWR50/DS/B4QRqyoPj4fv/gv8zJEfFh5AyTYmt4l2EGKVJQEqoHf+oaTiwq85l17JmVy5deqq2TWJnrvHKjbEiomlt9y84cFqtiw8XNACKcHzleqEcA3nR+PaEGMk1rJNN1pWHpONR1zCkspXA4OhqVfYiEa8kdOG+Pwvi8yuSUumhtbhqs55Air+zS1IVVHhKWNEPFQI1NLTxzu3uvdcISgtSc77H2H4FjYx+sSi2u5G8342ZoMa3N3EI6EKVU+GsxBBkYw5O+EnsqbANiYWsrFUiEZWeRl+QiDheT0YBzu/oi7xsj67GzDRkIQ8x5JEuDrhMZu08LzuDSfKit6Kq9yxqy7AyX3Hc1KFZMEmd0AdLG4srLT21fZntZJ72+QP5w31/UyrflCjudbHgdo4ueHWxmlPmd+MuLCJo4cWN270jNyXHe/t1qrRP4Q5cBzIIXeX3nPn5ma0w6bP3blpblS0Ey0Fu28Ivf4x9nWjIYhfYqdiIRp9Re7fdV/P4azcPpG6bb+V3HjHGImQ7vLENhLChY0+I79aXxXpmhSWDVEZJc0GwW2bGJeMsYGukizIVrySFQayuGcskCzL1Zv4FXmJR8fyBN3oZkqwiACULJebSGwoByJ1aoOsJVLBzzS8cx/A9QMh9fAsMVOfxCPneZphlPx2eZ9zNA7Y2/Q5Ga8nfhbwcLQBP2Wmi9HNhEBKDqVi/baXt0LVfgmzxHSi99YpVSG3z37ZdT8Bd7Y582b7QaXcjHa/5YQDYUqet3g66OqvkHlc/tGGtNuj5rYBochPn/oXtzrRSi1o8zU33hx7d9XdV/P36xL+BSGXTaOSSdlgm6V65qGyoTJityyGwSdjSQGlkW6hTMND7Toy4V2e7Co1J7pb8KBqzAbAYvxdfNGkc1AF/WqJ91P6egrs0Mxl9gRZrbt54Rud51WRH9hm2HQvGZ8oQft5ca2tjw9y7x/hZVYYeFrnqua+1CfgvW+m6HDr0HcmorUcWBWJ77IC73meGKk47rDfcQOxYStf7II8KZBQZK7OyAu95n37hQxiU/Dp6CuzQzDi6LiHEV+1w0ydPL63AAux/Ga2nQL9CvY1MS2/563pXPVc19ugIMxkWMWJca2tjwUHLEo6iv2uGmTp5gCc384xP0PqCuzQzFRUoZD0TIrrjQHvW5iy4wA/6egrs0MnLcZcKEYDBok0PX9O70HPRxPZ2nRkenUwMv7K2i4twHWfjrjQHvXZC37HSfEzQaBy/IcOvQdyaiDB5YAE+hG+JD8jQjwCghrI76r6KJg37B++JHWakS7CvSMuvmjSXCcAvXCNzLxfpv8wmRg7CmJJOdhhwgFSC3xzeIz9D3/66PmZ+YdxJXVk4Tf+VSfsLtNITKsbrZxRJ910fjmPh4NhYH6fiTTWqjgEbPyeXK6wgoFYqNa4BkUWLMwksEUIjxiTd1rAFyzHNg3eFSs5N+EvBfd8MQVxzAUowCrvR2XBHWxJY4SmT2iE0GP3E+DA6WNJKqcda+0ljCEHpVp6bscWCEZlHoXNnFO6NufuV1hpdKeVPT6yO9i6PULHr0bJTtVfDK4CG5N/GbsCb7fJVUgYbSxbZorsvuo6OG4r5qfX3DnUZoo0bWH4MyNTuCwG6iycScQ0jXCDTQdd9c/aPwkSo2o+OYyf9qVVWJENRw10bY6SkuKice5DzgFrMByr3jMKq3lsgAAma0OechrYmvCL8tPn0xLO27rWcWSQbgzw0F1/DR3EesChOxPuR2PPiliS2NliKQ+AzCSouosKg+pOyVwpzuCqtAw8rhzUkUcYMalGzj/oW31NLmzcqQ/72ttHMRqt5101iLh9dsLWtLoJN/IyY53GKKWKIMxtq+gxNtLyenVy/kHjedoKGC6A4Lz5wTU4ItLPAmGAV+quBtN2BkCALBTEkRcfz39/Pxct3mhpEz+yv9Ve0D7ofIlJOczTY/HgvBuWglViB3bJ4qmJW241kjrZpPBklJqjrtlMovCpUG0fwAzcPVpImbGq9zY7D6lUJrvclZV2dsk6mVnEbLda5vvNIW8ofsybDpGmdc/HI7PAPwqJeQN+CvV0z408rm4Nr8rU2EtHEbFu24u2CGVeArAyC0SWr2A4ypMwxJHMytIaNFnZFDZaJy37vKAN+JG4Q1+8bzAOW7+ZYKb5tSyYNO9qUCKIRchyI+/fMlzXXk7dGLFIIGY0YnpaOZKB14bQoTy7dy6HfvwOwmiMuTnSuYByoW2/yBmEafJSPT+LUQRhhGeN5E0lpLyopkKU6iaKzeTMkL0ntQGSmOEntM6ZGlBHOJhDtSRpceqQ6Ela0BNVP93/LmaINLMO1V+stXmLfOjk8iByEDlnAgWP/oWJ4QQW84bNZvKH7MmPArYy0VCIYcc8H24Y6ULT5dl14fvDGnVbG1KgjtAWb2YnRLN2L+Qt6JQKsFKHsX3HtQxyNzgMkjrwBNsygVkIncX/jgdO3KH4fXDR0hfJ4K5XpjqECYUwjT5KR6fxZubSu7sCLzO4h3eubLWJhsRKNEBIZNZh9AO7GygVkIm+lDgdO6IkV+PECTFDpkoWBbKDsKx5Z75991ulKrPQArow0YIskCmUxbmtYEvdMY0djfE+7lonYZJo4C5kpjhJ7TOmRtXZOF9js8HtQOT6y4mnajOJgwtbg2vytTYS0BL+2ezwX3EjzruxzZPeGKZV+U3OVtGPTgxLF2NoN2psof0yBBygDp6QMIMiK0+QxyBzFFNTd9zh5odXXlWTotU6IHAdXsSygKF8EoyG6vc+gCC+6CuPDC05GRthnKsVLP/lor+TuqmBmPuZdIjX76Um7l1898e7t9/ou3P+TMBHiZeG+4eaPUFJCgYK74ERS+WcndxmmzWbyh+zJjwWsDNGj/Qi6k1UWZB4b5ZBn1im2z/Di4QfaEPEc+cyZ4kB4yttGXdrOA7nnTEoxolOm1X1WCZqmz7agrjETfSz/ayV1exUlOtV1ajubSk9pJbpJzc++tCGsDCbYLyYhrISabHHgxsdN6/8fLUdq8LMSQPqnp2S71i2hqwESxO2JvaBn8ggUD2qJJ1t2jLHoLNC5MeSTl//nstL9a6/Bw81vr9/rQPHkNpzYCQPvSaNKXmIUcM9XY+xy5EhIV3WUPueIF3wTb8yd94PCStKcNWaluyAJu42K46pGWJjz+MM8iF5Gtx1ajATYFbEUfWIG/q8uOt764/q/BS3SvleQeYLqednc4aK+KSr3yLbZ6g5KglsimkVPUl1OB0OfUd71k7Oaet6gP0kyn3xvjNCk53bjmHTEhHfXu3IUfwOWijrCt/8t/ipH9n0CFIo5nwsF1w+e5hej+UGamrRFDUGe1d9FvxmvZ0e7X4WRwzx3tQdBRV2H1/1T6fJvWOuoYuDzLvwTzroqZ4ayeBgXQDPmolI/OSfuekm+1X+8dtlWYHhz1dwtP0yOpPPyHJ99+4llwhJss7+YQ4s048hixYuOU2P24AU3/4An+clax4PZP4WZuLN3JHm7xhvB/H1KBa4em5q4ki0G+n3F68ZLNXzZBnzzz29uKHE0RKZNq/Ao1caZTAeIvfTr1SBkL7qjbs8crYw59cWNAWKApbC8SqYb1LZ0HCEnDWuiaK4H9fc8sXEpM1eos4fV7kgxQNtfjV2pevkUnphrZgBU8IH91ccyjB8EuKorgf19z6wQr+gobJqr76bDR4rjLhw3ZONoLbGfeILpcGC+0lvaQrWQwqCYL7PC+1H/tDd7Jr79JCB/dXHXyeDnd1DC9hKhBjESToIQPCB/dXGjEXu7gkVq6Oog8K/FDfS5dqv7wUiK+kNfyOWro6iDwr8UBomOAUlvaQrWQwIWZVculXzIBxLkW5ot41dqv7wUiHYXj+IgTqd7MCK84WJo43x9pLe0hWshkOhB5UML2EqEGMRqBvud1DHFfracQcsA6PYgX1RUySVWQym09avvpsNHiuMtVDdk42gtsaAql5CPqw2+LsF3aondgOH+GfYu1nbgTnJddIyxGlfLpUViUofOsJSHhyD38/xU+3PlR+Ym7ZkWChRdpzKFX+ihHXCRG2fWbW5UU4QAtVsyRIFEdiRCXEUWnzI1m2xvqLohKH/AWx3lGrSwtmi2j6N1HR0iVZDueJx7ERZLJ4ug5H+G3XhEuOdvb/7KIFN5fi7gMNyGdbob6FTnuTSWdNAUTDs+TzdIKvcgcTKd679uDdn40BhUePQ8mgF6kemEV7K+X5ZHwUuhcGYUgNmKRHhEImof75iDBOzWjiD4vGQ2lHh9pqF3DS+0vxF4nr/94hLyN/YRxQjtxJHgQWEixC7HnTZ45x4WOy3AmxfyMxJe8545wG583VxQ7jT1GjR970PHrx1gBk/5qawBU8av88YArEsQmFvWj3w5sWSXf/iV3LVCdwvpOBA+DQ1RXJ4LDbBAz/17vtTj32cJyR59AUUFNwFp2RRx1tWdH/BuLEdXdAkBbyG3rUS/Fw8c5LXhBqij0aBV/TTTrU8SgQ/KhVjlHxvyZXaeQy9SA27+H3KjY0KB5Aw9upMllHabwDDqlKVzwhKlvV73FmHlHjrNG7pzWKMHkLhhriZezDteAFX3RBkYvkOXs7bDy+w+3+cp2B0Cq4n8zeqB0I3WY9ElH16hWm/LAIlu4p1diGMn08sSSJVL5Ydk6IQGWHdf3X1IhYhoJil0css1Ahl/xyofkiJ0PCCghVdSm3ks7npGWGsTsiLwBHXkl0oiru8v+MkXt/WFnrW3lRfSbwZAlzoHy2yhumtagDDp0talMTk9hTV1q2/nZcIMKQBC9ZN5ohQxYToeEFBJNGLn+A7FdtVgAVDtEj2vaO3x63dBjUrlsXz8I3HdoFEFNKRLDxKpYVea5P/YQfbHxHR9e+SGNsQpPVIFbfGeyTwT6t3a8XvdqLq+nFdP6QNIBYHSi6clKIAAAAA==

After:

data:image/webp;base64,UklGRp4fAABXRUJQVlA4WAoAAAAgAAAANwMAdwAASUNDUKACAAAAAAKgbGNtcwRAAABtbnRyUkdCIFhZWiAH5wAGABMABgAjACNhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJWUDgg2BwAANCfAJ0BKjgDeAA+kTqbSyWioiGiEK3QsBIJZ278fJf44moaRbeYY99GPxdhM8FefZ5+Hz/8i29Xj/EZI/5s/q39l/Sr8U/Z9+8/5X8gP6b5F/sH8T+S3yT/d14B9KXtf0f8cf7T+zn3y/ef8l+V/kR8Qv8h/in9C+0XluQEfnX84/w39y/Kb5Bvav9h6J/W31Pfyj/Lfmv77/0/+tf5v9//YM8P9gz8U/8H7kfeq/pf8H+7n+Y///zq+cf9p/fP7b/kP6//////+jf9v/jv++/s3/c/Zj5e////vvfp+ixb9nEMPK6coCgH9HMr++tAlmTo8U9Ihmj/ScN81lceBj5Hml5JrxQSP5p8AsOZcWaLZSP5LOVe22YTbo4ZMJT/9YQ3A8zoOgi4BbqHsm05n+J0hGfKG06CLgFuoeybTmf4nSEZ8obToIuAW6h7JtOZ/idIRnyhtOgi4BbqHsm05n+J0hGfKG06CLgFuoeybTmf4ncF+8KAhVqeaNt0uX0PrJ1Bs8CiyXhOar9HYtnyr9oY+h/RrXFsuIN0KNdIvGLGRue6o17hkJZf32krWNJZA6McPDZ4NSzoW6QnQ/PzHQyXreK2kpZPgjYWyIxL1ZSBvDU7BxexYb7ZNsYq9m8zh/qCxxVbJSVqjIb3XuTZ3GRwUPmnSLZ2sZv5RmRhGxOA8r87t147I3XJeSQpX8TWspNGKJLGoMi/8FmlHi9xTmwh8auJNJPYjxIfpjXxW+h9ewuuti/b/xDWbuB6CjNFPN6OhOFZnxr4AtdtmpENYvgMWv7faxhBFx5aBLIoBqp9TaX82fxH6yWgSCWO8Cw2vMyfmEiGN69FOXIS/gVqFp7TAOKKcYLBz4/t+bUa8g2QYVx8ot7j9Hp8Yhm2UeYB9kOFL2kjx3MNXOJB0Nv3aCe02hkvUWKl6K3AntBUvRW4E9oKl6K3AntBUvRSKQgsgoVC93bi6JU7HJ7ZBTlXpW+ICHaaqMX166o5OuWVDCSjto6YArvqyh9XDdL1WhVQaXordJYGlGxW5TgHNYPNeMfDmX78w7ghk84Gp9tpxRrbsWN/GsAZh59yX4H8GNMQuceNklQtBW9eUkMuwp0IYazrckf8AA7Tf75i/DIu9qN7el9qVXGPol4/M5Ubm/Wdatsi8YixPCRfemzl5OvSJPbbctrn+oN7SEtvpnCCCwcf6yBVdemSpyU+mMQaxL3pSydcRtoDVP+Rm/CN9BDdw4dA7E8gD61mAsrwPbDymBaRAXQ+rkPB0dMMB2tSAuP/7AsPQd1J2cuPXbzgyu1GXmmC5jEP0DocrFzD+beQazdMa4wmTE8DyVhHx7n1LPjtxcY8joROuHEWKk2+iZc0OVMCV8sWq2a1BrMsH7Fc3BcJCyfCpBCY7youRZUWrl/jA7Bd05py68tc5j/0eIZpIZlHECnIHe3dNRQJPxHZ8/zKeSoTIjP/kPIT9t/3u2Jrjl27NMk49ObqLtw9RTjWRH03C4141zA0/i0M14YixinPAL4dZqjbdGYQnt9iUihOCHAd0S+UDPYCpd7G2zyp5uv2n1q2951XSjzHG9e6T702uLeSHuKjzpdYe7OWzGNvzMxeOIEWx7c4UmEtaxr8yxFNzCWIXwtWA+7asQrgtbD0RJMPh0X2TKOCn99O6JTUg0p9K8JkzYUDK6efbc6o8+HUnmLZ+NQqIDzQqN7TbRZww5TqbP00EAD3pTSdQdtxq3jYpgB3RYIP4px69cbgvjAPr4zOx/JflRY4QqjiL4U93ATBzuJqtLTmEuVZyp1PdwEz2ttohQx3waYSDePsIKcgTz2KiR27k8QZlYHAeR4g3uKsi0L7ryJP1wKnx2YMx0UszGiBqNcsYpz/D02UId3f86llXWqq4DwJRrvvOQ+I6GsYIGoXyvQs6/P6qzC8IrrsZy/wqFVbkncBQ3Ijw2GWx6R9ljXjxREnATgFMJ/WXccM7hjNY5Debn5n20pm4liN9Lm17taWaQQRK8xz3CPKXOltVScnY5Xnwjw9nuUs7/fkJqwwrER4IA0uJGkZEibYk1ZStyky8UIid8UYYt5GyFha9yI62zdxeiBYg2AHHKJb0bGbfnzD5/BUskpbV2wsc2FAvyS2PXLZGPGUNu18ZpkBkQf66lhTyH2wsc2FAqobHh7PcpZ3+/ITVhhWIogQBpcSNIyJE2xKLj2Sr4HHKJb0bGbfnzD5/BUskpc6W1VJydjkmmWizVATk/xD7fJ1pD1kOvcXGbWrqE2rGx87CKf/h7PcpZ3+/ITVhhWIogQBq4ToJds2RMwgeizVATk/xD7fJ1pD1kOvcXGbWrqE2rGx87CKf/h7PcpZ3+/ITVhhWIogQBq4ToJds2RMwgeizVATk/xD7fJ1pD1kOvcXGbWrqE2rGx87CKgAA6Dyl1cbA9NEmCMbO4Epmp0lSrtn6ikhwGsQIGjjHLwik6avHRgsMqg2nsUZxQWCrM3TZx1Rcw+rup06mGPQHuT1tsdXMoP4ZnVHl8vMu95u8JICL5toUEVoQuty7Q88iJWrcS41njBOfh6uTwuaewXkrYwcKgImIFzB/RriDibrioKasZ8c39rRCLsk59P4th/SY/QWCcHokbahgKBy2LEamukdqdRjwcqcsQaqI2e9IsiS1SbPJK4qM1wUs9mzfQak+c4jMNT+Tf1rriHqVGrHCETrjpCV2We4EexCStYxT9skJJUZ01ibwFu289argwRlgObXihSwR+SXSW834OTEXikrmxIqU1DxOwVNhdJMLQakhwMhHbmHLfCfPCFFAvs2qrz94q2tP7Lw1p37hQkn5VQqqD0g2HRhOAEmZAV6Wn70wqvsoQsoNN4jHXQe37uLE67BD7rz2RDGZZ8EwU/Bt8AYYS/d6b4v8vZYrXqmtgxuszDe6aD38C71oVm5tjcPTpFc+8O5idA2FA1VtCzDQnPCmTg2YCsob2MjoM3cMsCgKgNdd0bNhbNg9DffcrvcXadxGP5x4NDB6/rUvp9GaVEudtWOw/PXh0pbikAyO0wfbFEOk6iY+gcyougDlLULtu8Q20/U9wHHVRY031TFqM2NN/76MkgkZr4yDNnFjQ0F9pSypDzrm5g+J9/kKL8i0hY2RBkX4iS9U1xb1N1wYTmYBUN8PYUIF/wtT8cIvPGGUAzN90PISwjdlXCwaG9yvc5x3LWYYcP6f0/iwFYiL1heHghHyC8IqzzH+h0NwlDuu0xxUzHEBiIBr1y68M2o02dmf1motRbZqBkLPdcEhQeKsW/SdEnBKp4iJBILd2zbRhLaG0UoxmOHxWz1hA6YJGT5VtawpxbxdHFrQ5Z+4w69P1R095mwF3E2mg4CtXhPJkUeXWc2iPuapBLf6PQZdrN7pxLBuq9apVu+i1hH5gce57Ks/AfFXaLsg26shuAJ/xQjdBtYTGtIuNV5msXEMW2XDY4gMRANb7kDEatheGR11HwymdzXcYpnljwnl1MntClq+Dk5xxvMqzOOwSIFPn6eOmWtxlunUlX0Rmn/Xly3Q7BeRfaGtSUw+HUoGauodrSCrgw/qtR3h2Q16OpwldGEtoh7lpT6JqC4FNtq2+fmIU+VbqHBUcxLuMUzyx4Ty6mT2hS1e1jyY+i/rIAnAARiEjSVFKYeIVleM4fgYG7XjCFcYRgzhfkoenWvHsQuLMtfJ3lx/Iopsrg5TuKObbqE2LLRf/ziOaCLjVjT7LCFLZ0TbxdZCTM7z/AwGgjbUlMPh1KBmrqHa0gq4HKfbxNpoOBbiKH1rYgqS/RMO8euDPxV4e4+leM4fgYG7Tntgmp96W6DN2S7IdtQoNNHUNyYTfSD4l3QDOij6X/l/j059VxTGQgnyKUph4hWV4zh98DbaXi1+qkK3Y+eDxXD06149iFxZlr5O8uP5FIFyf3rQ0jeJjzeIfMS+SIzuGiE/brjTt/gXVWxZwGWNx2U9dIIGQNMhQ/1twdox0mYAC8bZ2KMj+Vt0HWnM054o4hdOSVbzAuqt5tQ0FoUWaMxLBCwAFtGzX4WQrNtp2TGjUBrSgX26M6wg4XK+SA2K3Cdy230CdNtGpsQ3BUJmAT0Ikue38L22A72PpPZSPIxfNiT2dT1gEX9Z189gu/fTQJty1p7UBtjCRRQSYfcmLAEGgqnCA4eDgcHMNwJNVEaTgFoIwVphyHzn/uA/ZjYrSDn4UOIYbWnEZMbWmE2MWbfNpBd3rpPOFtczXqh2uVbcTiv4tIrEn2k5IQRSfVs55/6b5kBmK54hC0OzfnBdy6x+lAU2hPkY1imTJovwv8CFw5bFm8wf28og5yf/iwmzNITAuEScMcRVvq9LIEygkTMTaCKbGJgrOTJJMqz+5ZtNSl+cM3FWRRQ40H8X5ZCYvq8H08X2dbsOQRGAG4QcdQ7MzvjQGRQbF21+XQYIQSg4Pc6q+F+qH7F1HtGuN7Leaz7EnAZ3lHVDjxBDYhmdFf+qYHwEcCDeUkPIJoTm41StUKMCP+BAAAAaE+rTVhHPU66NdtpiSeoMHtX2kgBGsw7hA7QU2AL3/QFcdOj5cLjh9SItxCUwUxe+gROgQ1XLyS3B7L0YJZ4Tuv8HhlQm0vwyvtg7efzJ+nmXOfNqzXL0mVP5vx+5OF/7updqUbiiedORRxJ66mJkCFFMJdoOPJyEOlv2NjVKpZ/eGkBK5wNrznf1yUaCdOA4U/yvsnTTzoRzoWcCvN/pNOzR/9CCFaxLdfTYZhyfnCuRV6JUKe3Vkis/eMjqgdBA6uMfFl1aUdgYLheOyRiUvw4UvFmZSl5VWCZozfQyFX37a8xGtTqrCJE395Ba7h3YO0DPuBjtiFRHail3xGzQ0732oWHqRJ+ls/+WDGIsOH70koX5e7lib1f+lQKCNIst0qFnl3ZDa439mlpoyOG/UIBUVB1qy2fjDHZ9zlrNSV6c8OJWxa1XYJEmrJ+KvvpvHRCbcs+67jcphS5qCrvtM3/hHrsgQqmSorDMjUULr26/lsRfajmiPrJhGm3sSdztVvtCNwGPlYhuP2GxED6bwsLhIDuO7c0bAZ3q2IWCDg/dIayllxsGDywtXtoasmsz0lqskomVKmTz/aGYBxRx6UgBDBdTyua27xNZCVVtPJg/jv5EKwb8bu32CnPF+lgb6wk+kfoQVy/zXHmSkdujD30MzpRGnebHUYAAAFP8VAt7lXbanGuE4Y/7xEOYLpwrIpPGRbJw566vh6TiHFekb3urVHWMBzkTnJeutjlQBfRgcdOnr6oKYmbgtPUz0hbkVpUcDvvL70UtsjUGOiv7EEMymUy1UBFDFUZj6Bs9V8i7tUEUE3ohdfHN6dvFURfqOIGZOZkjZYBYBJOyzmThWVdldiLi2ACEG3wRyzqP3LQxO5vbKhKc2iFe29AhpxyH0v4paYx0V6sryKzxhwVawjNgWMpVbEEZ61k9mTDxSIaYkzTiwkLpylXxW6R0DAspJwYZyqy8kIhtTSJH9Hb1EKUq+hf4ViT5vCrX5D52iBJmEcaMasPDOn2BZBQ5DyYfEFwDSmEaGuuekm8OHtzxQNGLP+LRwVJpoBJm9Xxhuk8Zd0ZBSXheLuKjsyZEsC8x+1YFfsRyWmh3ZlqvXXCQsA6agJa2jl1WVnGw0h++wKho/X9UGFrm+Totjjy9OtM/90xMbJ7/s5nDwvADdIdPP5xY87jMzmcwnT2ajBQbsjtkyDf7f6Zv404A3FAXUVpx9SNdRlZD6qMzp+vPq2ZSsGuouuD92d14jEwPmQ/4amMjMsPDomU8rbKJvpTo92ogsOy+c7MQXfimYgXZCz2MlVavXA1lqr0P5GwwZ6T6MLUdTEmC5HBGi/TmBR0Bszxu+SJYYea93X7m3oOE9Zr76ij8hV/0CPNyQC+30nha1sLwLfw3m2OJ1pZQhvWsq+5hq9twIguMzxxd2uUm45kInPxpMJ5IEebkgF9gyu2WTVyg/HYBbBhCmJ9/3w84xQP5GwwZWjRTi6XGfNJuB2zZuN26rFX/UG/QhVw1OwiLq6MetJvDebY4CAqHxV/1Bv0IVcNTsIBdGeR/sEk9u/PbZJEg5WEpjkvfv4DXLI0n64z5pNwO2bLx2Z4imKpjMAFTMFLnJewyslJqvDebY4JCAvX9jG9aG+SJYYF7wC6Mzxx8HCes1+AuuAVKSDm8o2JeQ2bjRKoWuTKPg4T1mvvqHzZ0UMPLw+3QIce1Xi7rX7mb896+nC+R4e+F+K1OxOFoIuzu1Av6MPYEnDQLuPU7ZWghuLzgXZu//rpJHaG+XfNzLQ/No810lqbm0LqqWnsmc4S9t8WwBjyJKSPgt/DxVHlOBRhmqIXxvYqalv4utkhcGeS4yMZZEXLxLUPTQ3pfznSDXjMQQBZ6aSwJFfVI8KBNWrpzFQX9Kn2JqJ1RyItWR4BALs6f8Xr7UxZS9tAhsYmYo0N0nb9YDPcaUkY396YYkm9JAVBBlYYLtJM+vER3lDluKDTMxNuGdqHQFKyjObxVyFWJhqk/i+XDzu/yvqSL9dALhCaLSQFcQUS0psVHVSe1nOvZTZlG+u+wDPwXJdnoFctJ6sQ7cKHj3v+E7cIM35DimEi3RSGziP/9mEdO3WD3IfRsLQFBRRj4dpbz2UwIXP74wIzzMNS9ocdhZZqzMr+kA/Dr0gzA2D+5/8AhtT8Qshdw5lh7/ZLtXOAj//kH0AcQ6FvQZYUxefeU/PAUwbH9IHIWJY4O4ujR6nFYr/wKpNde90ho7qOpsFCZsFjBGXRL6vzqvYbszv23roCotdJIxVrdXNzfmH/veUOW36GMm5YTf05eDphbkWG1+FJV8dsBk2cb2yu19x6Q7xRvktMbyK/6UABwKqH1A784Rcg7w61kr35nLQt9sH3KZClDw+8xCjq2cRg7btkkq38WIhe5cQK/R58D6BzKpRm8GDKUElPm8YyLFmnFNPthxrZqpDkjEvJJdvDsbef+Kb0IOym12MYhdR+SnCW1TbGefiCP1aBLhhjWS3IzADi6esEcxhh+n8TgjlLx2lTk3yDZzxgvD+9Tv2Q/9iPYEv3t34wHHLpLcCs9J7h8/CmGeHJ8Y1tS1ocs/y70IsB3A5nx2GTV4UcZ8cRRM57pHC3MWtqOGcR6T79Xt4x/D6ysMxILDOEhug089nv8KYZ4crdzV2pa0OWf5SBt02X93r0cVSRiokagOfYSzKSPx+GR4xs9L7oq/xTKFQdG30UI2FFjd3n29mhcnTFHJYMuV1yjtuE4PoFZdSGBFCosM+vYuHhvRqFeXwGQX23u7slVsPuK19CgEIwgwWx48oc1sAZaa1acvZRCbcCUwZcrydOovfUoDkEpVd5O11avpM+JLLNaZeISaoVBNIDn9hMV6B2GptPHdKY0xqsQb7u7z7XSdtgFPfCmGeHK80Wx3wOrwU/hrqd8OR0dfFkeMNMYm+W/i+F11N3gRnrByIeHYyJ1oJqKBHpkJiJkTaEAsOUHY9836puYuQCxPikwHiRWLWKFpGgsaDVekAow2/vwgvlcZw/jGOOpbMNbMK/bsV/0FAhmE/H7cUp8V8VyCXCvFySSgmLr8kXcxI/R+VP+QrqIDhjVEdvMmd1AqfazZXi+F11N3ksjS3TPb0UgZfqt6k6ObeOa2MhR747lzT6iW39tTeJ+W0nE+T96Gpb4uytL8y/yhkTXM1dWEFTzlKfDL3JGxayNdM7OCYm8jcQ7qCek/Juy8RyS4iRfBgybDXR7hBwOazqPPgfQOZVKM3gxycduPT+mOu/K4zh/GMdLs5sUjPhc41jAnPGjWlBjs1l29l4jku7z6bQmd3ugc03fz8TTkaKHw6lAzV1Dtm1aWLGWCphsEb/3pigxzLCDAiGrIGZmK88hp++e4ICRB9jmZAOLEjDKAPSxQHNN38/JDLa+Ag+lg76HmyUScLAR7lB3CvZCFioYB8LrqbvCp8wpZquGApd/WHSsyiEN1oYq//wA2XGs/C1kCVMNltSIJ8axC+DO/ihamgnB65zZjcFpZPurtcKCuCne6gBIEEJBXRicc7ocT73waZLMS1iOIqG04q97F1kQD0sDMRuhtjCuSXy5XXAhgf9sQvDf1js0L+AmounGmu6gJxjmtV7YJVmPD5w/zaQXd68N8I81zmQbIsFLeR7q3BERjskCHYQv2L9lvj/S5zKPwNOHVIGYrniELS5i3rmx3dEDdzFYJt/YnI+lhiCY4mKqY2/uPZlYRHmbcRlzP3jilxiEy+ZQp0NsNlOQvYokdCgjacPBkJnM7GGUV6Fbunulqt30gs0Jxw+pRxhDXr7KUBnHzmMHVYAA3EYNKazPP9cQf5RCExkeNZetypOSyskW38C4dP9K4Rb3HPVJdnBQbbpC9WdM7WvK54hC0uYt65sd3RA3cxWCbf4YX6JPVvhIQ0bVlzhGvZloLVLGJm4jLqxXvFhYeN5notsQ+CULemndHDp+Q6m4n4o5fNd2SshkBneUdUORx05o1+u/Hs4uXsHV4q3Z3cgn+LmhObjVK1ZDyVjaiiqo6Xgxtk9UbQR/ja/h6BuBu6gzawVsGRu64hl84FrdjQlwVHlBiPN1xLmkDeCaDgZNg/LI/QRPAqPKDPyA8sjqBNk4tonFDFO7sVlsQtBlnPWEsJc0gbwTOcmn4S8L+YozEpBSXhR53hiTyuVhtgsQLCH7sIFrdjQlwVHlBOLy4lzSBvBMuhwgcgbW7GhLgoyIoQrW2KjXuMYKTocV0glFjZ7duy4I70Mr9eGaDI3dcQSt4/wJWEtf4IJDCDr4BrbFRr3GiCiaarTw6jMSkE/guHABraXNIG8Ey6HCB1kgglizqQwAhbtQiCpDmWoSMrGxTNk10VEOozEpBP4LZnw0p+CSjEQAUgIL6TY9l0KBVutshkJ8VNN5XKw2wXFMoXJxaS6FAq3W2QySIe+GZCTgH/lc92UlYFIylqe7PNxwetO/bOF1Lh5NKkHTl0hJB4Xv8PmkTwNo9pDr52BPZpeXjEoiwxdQulyTGCrTY55cRXdVX9Lrx2xe2JWElnOH+gwNYIbzx/07EtQJCuP0qK16jnf+rzw9TKKCNf6zyKhu5x2B0vKqravPQjVltxHuxxpVsBddpZ1q3HjDRVH2G9goZNPGtLSYoDbPQAG+V/GLxXmN/mK+jNdcp93C/FZilikj6LCO2trG7r86E5q5ryS2u1tMnf53jjHDb5hG8/mHsR4O3aoHylz56WesGty6tr/3lc+zMSBGY19rNtJ2NlBRsUUwiP8FDsMf6j5aOQLsaag/29f7CenT/dV/9u8VieiYCNirYd8WV0of4Qmo2rfcb8jAXUDOVBz8+goXtEtsMJ3XG0LaNhFbnBKZEdjl1aQh4VecNoGgJ6SDd/iAyaZjgEGrxvsX1soJNUtD8TtUQ0sKsdZd+Vod82o0xzTnS3J3F+QNwfjrBwSWKhJKJQ34GJGpHTswtlwRRVzU1cnjYYVhPtfnwtDvs8JXhcbIZ3EJlTONlHcxa054N18O300KTiP/4wv5W4Peqx9heBoeiUQvUeCVcV7AgVGir3+jSwzkdqMaAPudyoQJIIZnVOjT8uSnDT0x3Fz6pM3eWDkXRHgTLb2az9IXEwSJMw0OnIiq3Z40yOyN7F2vZEKgaAUqtQgIvg58g75N0jjiKg6AV/iAtJF1pUTMqJ16V6mdoNsf9CDeYLu5lV7WYQ8XVKAmpc1IABxJpI+HSLa99C3ZSAhCQ++tsX5ePQUyPUR6NTGsDQClVlBW+LbJkwWvCaIdG8KU12LejTexTitItAJAuNNOYrtkYZ1f06/JLQcxqIihZffjG2KyNAR43/wzKuiS7olarZQ3D1ozG5AS3QQPawxXaWEi6y3u47h07OXsIB0wAA=
2023-06-19 10:01:02 +03:00
Manos Pitsidianakis b05d929975
account: impl exponential backoff when retrying connection
When connection fails to be established because of network issues,
perform an exponential back-off reconnection.

https://cloud.google.com/iot/docs/how-tos/exponential-backoff

This will limit maximum waiting time before next attempt and also
prevent reconnecting without wait when there's no reason to (network or
remote server is down).

The algorithm is really crude:

Instead of uniformly sampling 1..=1000 milliseconds, we sample (4 *
random::<u8>()) which is at most 4 * 255 = 1020 which is good enough.

1. Try to connect immediately
2. If it fails, set retries = 1.
3. Try to reconnect after retries * (4 * random::<u8>())
4. If it fails, set retries *= 2 = 2.
5. Try to reconnect after retries * (4 * random::<u8>())
6. If it fails, set retries *= 2 = 4.
7. Try to reconnect after retries * (4 * random::<u8>())
8. If it fails, set retries *= 2 = 8. Stop increasing retries from now
   on.
9. Try to reconnect in a loop after retries * (4 * random::<u8>())
2023-06-19 10:01:02 +03:00
Manos Pitsidianakis 5699baecfb
melib: add utils::{futures, random} 2023-06-19 10:01:02 +03:00
Manos Pitsidianakis 02e86d1fad
listing/conversations: check for subject overflow on draw 2023-06-18 13:13:08 +03:00
Manos Pitsidianakis fdc0861ac0
view/thread.rs: fix expanded_hash argument off by one error
When calling ThreadView::new with an envelope hash, Some(expanded_hash),
in the arguments, when translating it to a cursor position (usize) it
was mistakenly subtracted with 1 resulting in the wrong thread entry
showing up as expanded.
2023-06-17 21:14:15 +03:00
Manos Pitsidianakis 45bac6eb16
meli: Tidy up use of debug!
melib::debug! macro was deprecated when we started using the `log` crate
in `melib`. This commit replaces it with log::{debug,trace}! macro uses.
2023-06-17 21:14:15 +03:00
Manos Pitsidianakis 575509f1ed
mail/listing.rs: move mail view to listing parent component
Instead of having a different widget to view mail in for each Listing
(plain, threaded, compact, etc) use a single widget in the listing's
parent type.

This will help with making the listing logic more modular in future
refactors to allow all combinations of listing/mail view/ thread view
positions and layouts.
2023-06-17 21:14:15 +03:00
Manos Pitsidianakis 5c9b3fb044
component: impl Component for Box<dyn Component>
Useful to have.
2023-06-17 21:02:08 +03:00
Manos Pitsidianakis 155fb41b93
components.rs: remove unused Component::set_id method 2023-06-17 21:02:07 +03:00
Manos Pitsidianakis 96537e48c5
Add {Timer,Component}Id wrapper types over Uuid 2023-06-17 21:02:07 +03:00
Manos Pitsidianakis 4da5366959
Remove bincode dep, use serde_json for sqlite3 values 2023-06-17 20:11:12 +03:00
Manos Pitsidianakis fd0faade06
melib/imap: add connection instance id string for debugging in logs
- Add an ID field in ImapConnection and ImapStream that records where
  each instance was created. This is useful for differentiating main
  backend connections from watching thread connections (the ones that
  listen to updates from the IMAP server with IDLE or polling).
- Add an imap_trace! macro that uses log::trace! internally but also
  prepends the connection's ID string to each log line.
2023-06-17 20:11:10 +03:00
Manos Pitsidianakis 8f14a2373e
melib/imap: put imap-codec logic under the imap_backend feature 2023-06-17 20:10:23 +03:00
Damian Poddebniak 330887c4f5
refactor: Introduce imap-codec. 2023-06-17 20:10:21 +03:00
Damian Poddebniak 6c6d9f4b4e
chore: Improve ordering of `flag_impl!`s. 2023-06-17 13:32:45 +03:00
Damian Poddebniak 579372b4a7
chore: Improve readability of `Envelope`.
* Sorted according to RFC.
* Separated IMAP4rev1 and other values.
2023-06-17 13:32:45 +03:00
Manos Pitsidianakis b6c93e49f2
docs/meli.conf.5: add use_tls option in IMAP connection settings 2023-06-14 12:44:04 +03:00
Manos Pitsidianakis d33f9d54c7
terminal/keys: remove unreachable!() in Key::serialize 2023-06-13 16:27:35 +03:00
Manos Pitsidianakis cd85d83324
melib/email: replace timestamp with Date value in message/rfc822 Display 2023-06-13 16:27:35 +03:00
Manos Pitsidianakis d7e6b40b7e accounts: auto re-index sqlite3 database if it's missing
Instead of telling user to do it themselves
2023-06-05 20:05:43 +03:00
Manos Pitsidianakis e0257c9d8d
Run cargo-sort 2023-06-04 21:13:55 +03:00
Manos Pitsidianakis 27a4dcb916
Fix some rustdoc lints 2023-06-04 21:13:55 +03:00
Manos Pitsidianakis bf615e7d93
melib/thread: check for case when envelope has its own message id in References and In-Reply-To
Emails sent from meli's gitea do this, and it makes them invisible in
thread listings.
2023-06-04 21:13:55 +03:00
Manos Pitsidianakis b92a80a23a
melib/imap: resync even if UIDVALIDITY is missing from cache
I think this is related to #98 meli gets stuck on `set seen' for mail (threads) at random

https://git.meli.delivery/meli/meli/issues/98
2023-06-04 21:13:55 +03:00
Manos Pitsidianakis f8623d4b2c
melib/imap: implement more ResponseCode cases 2023-06-04 21:13:55 +03:00
Manos Pitsidianakis 299c8e0f99
meli: restructure pub use melib::* imports 2023-06-04 21:13:54 +03:00
Manos Pitsidianakis c5ecaceae1
melib/search: fix some search criteria in Query type 2023-06-04 21:13:49 +03:00
Manos Pitsidianakis 6bf1756de8 melib/search: implement more search criteria in Query type 2023-06-04 17:07:06 +03:00
Manos Pitsidianakis 23d95973d4 melib/backends/imap: add search.rs module
Add trait to convert melib::search::Query type to an IMAP appropriate
query string (search criteria).
2023-06-03 22:33:41 +03:00
Manos Pitsidianakis 6388bea9a0 melib/email/headers: fix &[u8] index in HeaderMap 2023-06-03 19:31:09 +03:00
Manos Pitsidianakis f537c24909 utilities/widgets.rs: move text field to its own module 2023-06-03 14:43:00 +03:00
Guillaume Ranquet daf42fd456 config_macros.rs: fix build error with quote 1.0.28
With quote 1.0.28 the TokenTree enum is declared as a private enum
thus causing this error at build time:

error[E0603]: enum `TokenTree` is private
   --> config_macros.rs:114:54
    |
114 | ...                   if let quote::__private::TokenTree::Group(g) =
    |                                                ^^^^^^^^^ private enum

Use enum definition from proc_macro2 instead.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2023-06-01 12:15:27 +03:00
Manos Pitsidianakis 58889bcadd
pager: Add show_extra_headers option
Show custom set headers on pager, if existent.

Quoting meli.conf(5):

> show_extra_headers [String]           (optional) Extra headers to
>                                      display, if present, in the
>                                      default header preamble of
>                                      the pager.  This setting is
>                                      useful especially when used
>                                      per-folder or per-account.
>                                      For example, if you use
>                                      ‘rss2email’ (See r2e(1)) the
>                                      e-mail you will receive will
>                                      have the ‘X-RSS-Feed’ header
>                                      by default.  You can show
>                                      them only in the folder
>                                      where you keep your feed
>                                      items:
>
>                                      [accounts."personal".mailboxes]
>                                      INBOX = {}
>                                      "INBOX/Sent" = { sort_order=0 }
>                                      "INBOX/Feeds" = { pager.show_extra_headers = ["X-RSS-Feed"] }
>                                      (empty)
2023-05-31 19:13:44 +03:00
Manos Pitsidianakis d332e4578d
melib/headers: add proper Display impl for HeaderName 2023-05-31 18:22:17 +03:00
Manos Pitsidianakis 954329d848 Set file extensions to temp files, use `open` in macos
If html_filter fails, meli unwraps it. Also, if it can't find an xdg default app it also fails.

So use xdg-open and open as failsaifes.

But that requires `open` to know it's an html file, so implemented setting temp file extensions as well.
2023-05-30 21:36:24 +03:00
Manos Pitsidianakis aebff3d3d9 melib: implement mailto RFC properly
This allows mailto links with `In-Reply-To` parameters to work properly.

PS Mailto links can be used with the `mailto MAILTO_URI` command
2023-05-30 16:52:29 +00:00
Manos Pitsidianakis 235fceaf21 melib: Add standard heeder constants in email::headers
Like `http` crate does
2023-05-30 16:52:29 +00:00
Damian Poddebniak 1eea8bab77 tests: Fix `test_imap_fetch_response`. 2023-05-28 08:32:32 +00:00
Damian Poddebniak 30866f752b chore: Bypass rustfmt bug. 2023-05-25 15:48:19 +02:00
Manos Pitsidianakis 1f8ac2287b
docs/external-tools.md: fix ftplugin location and add example mail.vim file 2023-05-22 14:46:42 +03:00
Manos Pitsidianakis c9d26bb415
mail/compose: add configurable custom hooks with shell commands
Quoting the docs at meli.conf(5):

```text
 custom_compose_hooks [{ name = String, command = String }]

 (optional) Custom compose-hooks that run shell scripts.
 compose-hooks run before submitting an e-mail.
 They perform draft validation and/or transformations.
 If a custom hook exits with an error status or prints output to
 stdout and stderr, it will show up in the UI as a notification.

 Example:

 [composing]
 editor_cmd = '~/.local/bin/vim +/^$'
 embed = true
 custom_compose_hooks = [ { name ="spellcheck", command="aspell --mode email --dont-suggest --ignore-case list" }]
 ```
2023-05-19 10:34:32 +03:00
Manos Pitsidianakis cc27639fca
melib/email/compose: use Envelope attachments when editing and don't add already existing headers 2023-05-19 09:21:11 +03:00
Damian Poddebniak f63f6445ad chore: Improve error message when `m4` executable is missing. 2023-05-17 09:22:12 +00:00
Damian Poddebniak 682ea5547e chore: Add `.idea` (CLion) to `.gitignore`. 2023-05-17 09:22:12 +00:00
Manos Pitsidianakis 24103f3310
docs: add external-tools.md document 2023-05-17 09:33:52 +03:00
Manos Pitsidianakis 91557c2c43
mail/listing.rs: prevent list blank when refreshing account
Mail list would go blank if the currently focused account received a
Status update event.
2023-05-16 19:48:48 +03:00
Manos Pitsidianakis 428f752b20
Remove obsolete crate::components::mail::get_display_name() 2023-05-16 19:22:13 +03:00
Manos Pitsidianakis 77020e0c19
Update CHANGELOG.md 2023-05-16 17:38:03 +03:00
Manos Pitsidianakis 8c671935f9
Add compose (pre-submission) hooks for validation/linting
compose-hooks run before submitting an e-mail.
They perform draft validation and/or transformations.
If a hook encounters an error or warning, it will show up as a notification.
The currently available hooks are:
- past-date-warn
  Warn if Date header value is far in the past or future.
- important-header-warn
  Warn if important headers (From, Date, To, Cc, Bcc) are missing or invalid.
- missing-attachment-warn
  Warn if Subject, draft body mention attachments but they are missing.
- empty-draft-warn
  Warn if draft has no subject and no body.

They can be disabled with [composing.disabled_compose_hooks] setting.
2023-05-16 17:31:56 +03:00
Manos Pitsidianakis 1f1ea30769
components/mail/view: on draw() set dirty on return 2023-05-01 16:33:19 +03:00
Manos Pitsidianakis 85d4316a6a
Replace old logging module with the `log` create 2023-05-01 16:22:35 +03:00
Manos Pitsidianakis 30cc5d3d02
docs: add edit-config in manpages 2023-05-01 08:43:36 +03:00
Manos Pitsidianakis b1a7188771
Clippy fixes 2023-04-30 20:47:53 +03:00
Manos Pitsidianakis 3a02b6fb80
README.md: mention how to override w3m with html_filter 2023-04-30 19:14:47 +03:00
3nt3 34bb532e8d
Mention w3m dependency
Fixes #181

Signed-off-by: 3nt3 <gott@3nt3.de>
2023-04-30 18:05:21 +02:00
Manos Pitsidianakis 47e6d5d935
meli: add edit-config CLI subcommand that opens config files on EDITOR 2023-04-26 13:36:57 +03:00
Manos Pitsidianakis 39d9c2af3b
melib/smtp: fix test smtp server logic 2023-04-26 12:08:15 +03:00
Manos Pitsidianakis d679a74450
melib/jmap: Implement Bearer token authentication
Fastmail now uses an API token in a http header for authentication.

This can be used either as a server_password or provided by a
server_password_command like oauth2.
2023-04-10 20:58:49 +03:00
Manos Pitsidianakis d9c07def0f
Add command to select charset encoding for email
Open dialog to select charset with `d`.
2023-04-10 11:42:50 +03:00
Manos Pitsidianakis 939dc15e28
Fix melib tests 2023-04-10 11:19:14 +03:00
Manos Pitsidianakis 3adf72aed0
Add support for utf-7 encoding
Closes #175
2023-04-10 10:33:46 +03:00
Johannes Schilling 2447a2cbfe melib/jmap: avoid relying on hardcoded hash values
The hash values seem to have changed in the meantime, or aren't the same
on all environments.
2023-03-09 10:37:58 +02:00
Manos Pitsidianakis d7ec97f03b Small rustfmt change 2023-03-09 10:37:00 +02:00
Johannes Schilling fbc1007ff4 jmap: deserialize `null` to empty vec for messageId
The spec says MessageId can be `null`, handle that case and deserialize
it to an empty Vec.
2023-03-09 10:30:34 +02:00
cos 256a3e252e Update minimum supported rust version
Code requires label_break_value feature, which was [stabilized][0] in
release 1.65.0 of the toolchain.

[0]: https://github.com/rust-lang/rust/pull/99332
2023-03-06 09:56:43 +02:00
Manos Pitsidianakis 3a10953f05 debian/: update fix-prefix-for-debian.patch 2023-03-06 09:52:51 +02:00
Manos Pitsidianakis 11140b4a76 Fix test output
test_compose_reply_subject_prefix requires access to / path, and fails
when building with deb-dist
2023-03-06 09:49:02 +02:00
cos 671ce9f694 debian/: add missing build dependencies
quilt has unconditionally been used in debian/rules since the initial
addition of debian packaging support in commit bb80de.

sqlite3 has been a default feature since at least commit 6ceed3,
possibly longer through rusqlite.
2023-03-06 09:20:03 +02:00
Johannes Schilling 12cb717bda melib: add server_password_command to jmap
Move the handling of either `server_password` or
`server_password_command` from the imap backend to the common
`AccountSettings` struct and add it for jmap as well.
2023-03-06 09:11:55 +02:00
Manos Pitsidianakis f9ac9b607a Temporarily disable libgpgme functions because of a bug
`Possible incorrect libgpgme API usage causes a SIGABRT when verifying
signatures #176`

<https://git.meli.delivery/meli/meli/issues/176>
2023-02-11 17:51:36 +02:00
Manos Pitsidianakis 660bacb926 Add `mailto` command to open composer with initial values from mailto template 2022-12-30 17:02:10 +02:00
Manos Pitsidianakis de2f46fe61 rustfmt changes 2022-12-27 18:40:26 +02:00
Manos Pitsidianakis 5443b7e8f3 melib/sieve: remove literal_map() parse combinator 2022-12-27 18:38:20 +02:00
Manos Pitsidianakis 3c847ad26a melib/sieve.rs: add beginning of sieve parser
Concerns #153

Support filtering rules to move mails to folders #153 <https://git.meli.delivery/meli/meli/issues/153>
2022-12-27 18:29:27 +02:00
Manos Pitsidianakis 2878bbb8c8 melib/addressbook: add parser for mutt alias file 2022-12-23 02:32:22 +02:00
Manos Pitsidianakis 40c6647db8 Fix multipart/related with main text/html part not displayed correctly 2022-12-09 14:06:20 +02:00
Manos Pitsidianakis f63ce388f7 commands: move ManageMailboxes to Tab Actions 2022-12-09 12:58:56 +02:00
Manos Pitsidianakis c06c3f5893 mail/listing/conversations: draw gap between list and mail view 2022-12-09 12:50:17 +02:00
Manos Pitsidianakis abc56eae43 mail/listing: fix SEEN flag update hiding mail view momentarily 2022-12-09 12:49:46 +02:00
Manos Pitsidianakis 7606317f24 melib/notmuch: add support for virtual mailbox hierarchy
Add optional "parent" property to notmuch mailbox configuration.

Closes #167

https://git.meli.delivery/meli/meli/issues/167
2022-12-09 12:35:10 +02:00
Manos Pitsidianakis 4f45b10974 mail/listing: fix tag updates not showing up right away
Closes #132
Closes #133
2022-12-09 12:30:51 +02:00
Manos Pitsidianakis 5634f95553 Rename MeliError struct to Error 2022-12-08 22:20:05 +02:00
Manos Pitsidianakis 259aeb0087 Convert {Account,Mailbox}Hash from typedef to wrapper struct 2022-12-08 22:10:58 +02:00
Manos Pitsidianakis 7382e30160 Convert EnvelopeHash from typedef to wrapper struct 2022-12-08 20:43:52 +02:00
Manos Pitsidianakis 2427b097c5 themes: make tag_default background lighter on light theme
Closes #164
2022-12-04 16:31:49 +02:00
Manos Pitsidianakis 252d2bdf2f Replace hardcoded /bin/false with 'false'
Credits to http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/mail/meli/patches/patch-src_conf_rs?rev=1.1.1.1&content-type=text/x-cvsweb-markup
2022-12-04 15:53:57 +02:00
Manos Pitsidianakis eaecc5ea12 melib/notmuch: remove hardcoded major .so version for non linux/macos target_os
Credits to http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/ports/mail/meli/patches/patch-melib_src_backends_notmuch_rs?rev=1.1&content-type=text/x-cvsweb-markup for discovering this.
2022-12-04 15:44:20 +02:00
Manos Pitsidianakis 4b96bd591f mail/listing: add ColorCache constructor to deduplicate code 2022-12-04 14:16:36 +02:00
Manos Pitsidianakis b9030a684c listings: fix selection not appearing immediately and invalid motions 2022-12-01 21:20:20 +02:00
Manos Pitsidianakis 2224a7100f melib/imap: reset imap cache on init error 2022-12-01 21:06:33 +02:00
Manos Pitsidianakis 7924aa8bfe melib/jmap: fix compilation 2022-11-28 16:56:37 +02:00
Manos Pitsidianakis 7af893597f conf/shortcuts.rs: replace use of Self::DESCRIPTION with Shortcuts struct consts 2022-11-28 16:18:49 +02:00
Manos Pitsidianakis 7d9cabb023 Add mailbox manager tab 2022-11-28 15:44:12 +02:00
Manos Pitsidianakis ee9d458b05 accounts.rs: implement mailbox {un,}sub actions 2022-11-28 15:30:19 +02:00
Manos Pitsidianakis 5ba7b2cd7b meli: fix clippy lints for meli binary 2022-11-24 19:58:23 +02:00
Manos Pitsidianakis 104352e595 Add table UI widget 2022-11-24 19:58:06 +02:00
Manos Pitsidianakis bd22f986f0 melib: fix clippy lints 2022-11-14 19:14:19 +02:00
_ ded9adde61 More descriptive "Unimplemented" messages 2022-11-13 19:04:29 +02:00
Manos Pitsidianakis 6317984136 Makefile: add --bin flag to meli cargo build target 2022-11-13 18:59:12 +02:00
Manos Pitsidianakis db227dea34 build.rs: add error messages if `mandoc`,`man` binaries are missing 2022-11-11 20:08:00 +02:00
Manos Pitsidianakis 282af86e83 docs: fix NAME sections manual pages for correct whatis(1) parsing
Reference used was WHATIS PARSING section in lexgrog(1).

This change enables the manual page to be returned with a whatis(1)
query:

 $ whatis meli
 meli (1)             - terminal e-mail client
2022-11-11 16:53:41 +02:00
Manos Pitsidianakis cc439b239a mail/listing.rs: add RowsState struct
Keep state of rows in lists in this struct to reduce code duplication in
list implementations
2022-11-07 20:36:59 +02:00
Manos Pitsidianakis b776409d6c melib/thread.rs: add thread, env hash index fields 2022-11-07 16:26:47 +02:00
Manos Pitsidianakis 56fc43bcf8 melib: add As{Ref,Mut} impls for RwRef{,Mut} 2022-11-07 16:25:37 +02:00
spike 59b95f83d2 fix docs 2022-10-30 13:31:23 +01:00
Manos Pitsidianakis 88a1f0d4bc melib/imap/parser: fix FETCH response parsing bug
Closes #160
Closes #128
2022-10-23 21:05:06 +03:00
Manos Pitsidianakis 64346dd3fe melib/parsec: add map_res, quoted_slice, is_a, alt, take, take_literal 2022-10-22 22:47:14 +03:00
Manos Pitsidianakis 17b42b1a6c melib/parsec: add json deserialization tests 2022-10-22 22:47:10 +03:00
Manos Pitsidianakis 6d20abdde7 melib/gpgme: add #[allow(deref_nullptr)] in bindgen tests 2022-10-22 22:45:15 +03:00
Manos Pitsidianakis 803d3414fd melib/imap/managesieve: implement some rfc5804 commands
Try with managesieve REPL in src/managesieve.rs:

cargo run --bin managesieve-client ~/.config/meli/config.toml
"accountname"

rfc5804 <https://www.rfc-editor.org/rfc/rfc5804.html>
2022-10-22 21:14:53 +03:00
Manos Pitsidianakis 3697b7d960 melib/datetime: don't use LC_ category in place of LC_ masks in libc calls
LC_ masks are bit masks, whereas category values are not.

Concerns #159

[imap] all mail timestamps are zero/epoch #159
https://git.meli.delivery/meli/meli/issues/159
2022-10-17 18:06:58 +03:00
Manos Pitsidianakis dd0baa82e9 Spawn user-given command strings with sh -c ".."
If given string contains arguments, Command::new(string) will fail.

Reported in #159 https://git.meli.delivery/meli/meli/issues/159
2022-10-17 17:40:25 +03:00
Manos Pitsidianakis 0ef4dde939 melib/jmap: wrap serde_json deserialize errors in human readable errors 2022-10-13 10:59:10 +03:00
Manos Pitsidianakis 55ed962425 melib/jmap: use server_url instead of server_hostname + server_port in config 2022-10-13 10:40:48 +03:00
Manos Pitsidianakis 46a038dc68 conf.rs: remove interactive messages when #[cfg(test)] 2022-10-09 20:08:36 +03:00
Manos Pitsidianakis 16646976d7 compose: fix reply subject prefixes stripping original prefix
Unintelligent heuristic but should cover most cases?

Configurable subject response prefix #142
https://git.meli.delivery/meli/meli/issues/142

Closes #142
2022-10-09 18:31:01 +03:00
Manos Pitsidianakis ffb12c6d1a conf.rs: make all public struct fields public 2022-10-09 18:30:22 +03:00
Manos Pitsidianakis 7e09b1807f melib/collection: replace _Ref deref unwraps with expect() 2022-10-09 18:28:41 +03:00
Manos Pitsidianakis 129573e0fd melib/maildir: rename root_path to root_mailbox 2022-10-09 18:28:07 +03:00
Manos Pitsidianakis 0c08cb737c melib/jmap: mark mailboxes as subscribed on personal accounts
The spec https://jmap.io/spec-mail.html#mailboxes says a mailbox property `isSubscribed` should be considered true if the account is marked as `isPersonal`.

Closes #157

JMAP incompatible with Stalwart server #157 https://git.meli.delivery/meli/meli/issues/157
2022-10-04 15:58:36 +03:00
Manos Pitsidianakis 117d7fbe04 melib/jmap/rfc8620.rs: make private fields public 2022-10-04 15:51:43 +03:00
Manos Pitsidianakis 347be54305 melib/error: add NetworkErrorKind enum 2022-10-04 15:49:34 +03:00
Manos Pitsidianakis 7935e49a00 conf/accounts.rs: check properly if mailbox request is an error 2022-10-04 15:42:24 +03:00
Manos Pitsidianakis c54a31f7cc listing/offline.rs: break line for error messages 2022-10-04 15:41:40 +03:00
Manos Pitsidianakis c3fdafde3b Documentation touchups 2022-09-26 18:04:53 +03:00
Manos Pitsidianakis c6bdda03cf melib/backends.rs: fix notmuch error shown on any missing backend 2022-09-24 22:23:43 +03:00
Manos Pitsidianakis e450ad0f9c types.rs: remove unused struct 2022-09-19 22:04:10 +03:00
Manos Pitsidianakis 0ed10711ef notifications: add new_mail_script option
Preferred over `script` option for new email notifications
2022-09-19 21:58:59 +03:00
Manos Pitsidianakis d8d43a16fe HtmlView: add html_open config setting
Add config setting in case xdg query default app for text/html mime type
doesn't yield results.
2022-09-19 21:40:12 +03:00
Manos Pitsidianakis b87d54ea3f melib/backends.rs: impl Into<BTreeSet<EnvelopeHash>> for EnvelopeHashBatch 2022-09-19 15:18:25 +03:00
Manos Pitsidianakis a7a50d3078 src/: Box<_> some large fields in biggest types
As reported by `cargo +nightly typesize`
2022-09-19 15:18:25 +03:00
Manos Pitsidianakis b138d9bc61 melib: fix some clippy lints 2022-09-19 15:18:25 +03:00
Manos Pitsidianakis 787c64c2da conf.rs: remove expect()s from create_config_file()
No reason to expect(), just return the error.
2022-09-13 19:30:20 +03:00
Manos Pitsidianakis 0df46a63ec Show error if sqlite3 search backend is set but doesn't exist
Closes #114
2022-09-11 17:42:22 +03:00
Manos Pitsidianakis 94bd84b45d Fix clippy lints for `meli` crate 2022-09-11 15:19:40 +03:00
Manos Pitsidianakis 388d4e35d6 listing/offline.rs: add in-progress messages while connecting in IMAP 2022-09-11 15:00:30 +03:00
Manos Pitsidianakis 9cbbf71e0f melib/email/attachments: Add DecodeOptions struct for decoding 2022-09-11 01:22:06 +03:00
Manos Pitsidianakis 3688369278 melib/smtp: add smtp test 2022-09-10 21:39:56 +03:00
Manos Pitsidianakis 3c0f5d8274 melib/smtp: add BINARYMIME support to smtp client
Concerns #49

IMAP: Lemonade profile tracking issue
2022-09-10 19:02:17 +03:00
Manos Pitsidianakis a72c96a26a melib/smtp: add 8BITMIME support to smtp client
Concerns #49

IMAP: Lemonade profile tracking issue
2022-09-10 19:02:17 +03:00
Manos Pitsidianakis 8c7b001aa5 listing/conversations.rs: add `thread_subject_pack` command to pack different inner thread subjects in entry title 2022-09-09 02:03:13 +03:00
Manos Pitsidianakis 9dc4d4055c listing: add focus_{left,right} shortcuts to switch focus
This allows you to make the mail entry column occupy the whole screen if
you press focus_right (Right key) twice.
2022-09-07 16:39:15 +03:00
Manos Pitsidianakis 3d92b41075 Add cli-docs feature to the default set 2022-09-06 21:59:30 +03:00
Manos Pitsidianakis 7c7115427d docs/meli.7: complete guide document 2022-09-06 21:41:26 +03:00
Manos Pitsidianakis 5fa4b6260c docs/meli.7: add more screenshots 2022-09-05 19:40:53 +03:00
Manos Pitsidianakis 4a20fc42e1 Update CHANGELOG.md 2022-09-05 17:05:39 +03:00
Manos Pitsidianakis f76f4ea3f7 docs: add meli.7, a general tutorial document
This commit also changes some shortcut names.
2022-09-05 16:25:59 +03:00
Manos Pitsidianakis 2de69d17f1 melib/compose: fix erroneous placement of newlnes for wrap_header_preamble suffix 2022-09-03 17:47:58 +03:00
Manos Pitsidianakis cbe593cf31 mail/compose: add configurable header preample suffix and prefix for editing
This commit adds a new configuration value for the composing section of
settings. Quoting the documentation:

 wrap_header_preamble: Option<(String, String)>
 optional

 Wrap header preample when editing a draft in an editor. This allows you
 to write non-plain text email without the preamble creating syntax
 errors. They are stripped when you return from the editor. The values
 should be a two element array of strings, a prefix and suffix. This can
 be useful when for example you're writing Markdown; you can set the
 value to ["<!--",\ "-->"] which wraps the headers in an HTML comment.
2022-09-02 16:09:45 +03:00
Manos Pitsidianakis a484b397c6 melib/notmuch: show informative error messages if libloading fails
Add instructions on how to solve this, and also a config setting
`library_file_path` to set the path manually if necessary.
2022-09-02 15:17:30 +03:00
Manos Pitsidianakis eb5949dc9b melib/error.rs: switch summary<->details identifiers
They are more intuitive like this.
2022-09-02 12:12:12 +03:00
Manos Pitsidianakis aa99b0d787 compose: implement configurable subject prefix stripping when replying
Introduce functionality to strip email subject from a set list of
prefixes or from a user set list.

Also, added a setting for the reply prefix (default is "Re:").

Closes #142
2022-09-01 22:32:33 +03:00
Manos Pitsidianakis da9c80ccfd melib: Enhance SubjectPrefix with strip_prefixes_from_list() method
And make it public.
2022-09-01 22:32:33 +03:00
Manos Pitsidianakis a73885acb1 Improve embed terminal
- Add character attribute support
- Add cursor key mode support
- Fix buggy set fg / bg sequences

And added a bin under tools to test arbitrary apps using the embedded
terminal:

 cargo run -p tools --bin embed -- "htop" 2> .htop.debug.log
2022-09-01 22:24:01 +03:00
Manos Pitsidianakis 480000ebbb melib/notmuch: show error if account directory does not contain ".notmuch" subdirectory
Bug reported by user on mailing list.
2022-08-30 12:23:25 +03:00
Manos Pitsidianakis 29042aba59 melib/datetime: add mbox date format parse 2022-08-29 11:19:21 +03:00
Manos Pitsidianakis a42a6ca868 notifications.rs: show notifications in terminal if no alternative
If no alternative (dbus or notification command) show notifications
inside the terminal.
2022-08-28 17:39:20 +03:00
Manos Pitsidianakis bde87af387 Refactor filter() method in Listing trait 2022-08-28 17:29:30 +03:00
Manos Pitsidianakis 10497952f7 Wrap stdout in BufWriter
Hopefully this makes redrawing the terminal faster
2022-08-28 17:28:37 +03:00
Manos Pitsidianakis 0c0bee4482 Makefile: add missing .PHONY targets, fix missing tab indentation 2022-08-27 17:41:07 +03:00
Manos Pitsidianakis ca48896865 Cargo.toml: add strip option to profile.release 2022-08-27 17:39:23 +03:00
Manos Pitsidianakis 7650805c60 Bring stripped binary size down to 7MiB 2022-08-27 16:18:56 +03:00
Manos Pitsidianakis e29041f733 Rename src/bin.rs to src/main.rs 2022-08-27 15:02:48 +03:00
Manos Pitsidianakis f4e0970d46 mail/compose.rs: add ability to kill embed process
If embed editor process is unresponsive, there was no way to kill it.
Add force kill option by pressing Ctrl+C.
2022-08-27 15:02:15 +03:00
Manos Pitsidianakis 9cb66ef818 Fix all clippy warnings in `meli` crate 2022-08-25 16:38:02 +03:00
Guillaume Ranquet d921b3c320 compact.rs: use mail sorting parameters from config
Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2022-08-25 12:52:39 +03:00
Guillaume Ranquet 9205f3b8af conf.rs: handle a per account mail order parameter
The new order parameter adds the possibility to specify a
sort order on a per account basis.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2022-08-25 12:52:39 +03:00
Guillaume Ranquet 97ff3e787f conf.rs: only add toml files to the themes
By default, all files under MELI_CONFIG/themes are added to the
configuration files.
If one of these files is a binary file, this will provoke an error.

Summary: InvalidData
stream did not contain valid UTF-8
Caused by: stream did not contain valid UTF-8
Kind: OS Errorthread 'main' panicked at 'failed', melib/src/error.rs:201:9

Fixes the potential issue by filtering by file extension.

Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
2022-08-25 12:52:39 +03:00
Manos Pitsidianakis 824f614a69 mail/view: Fix HtmlView not being redrawn when parent is dirty 2022-08-22 23:12:48 +03:00
Manos Pitsidianakis ed3dbc8586 listing/conversations: fix crashes when listing is empty 2022-08-22 23:11:43 +03:00
Manos Pitsidianakis 7fca5f01ef melib/jmap: fix jmap build with isahc 1.7.2 2022-08-18 18:12:44 +03:00
Manos Pitsidianakis b716e4383e Add collapse option for mailboxes in sidebar menu
Closes #130

Feature request: collapsible folders with total counter #130 https://git.meli.delivery/meli/meli/issues/130
2022-08-18 18:05:48 +03:00
Manos Pitsidianakis 4a79b2021d Update dependency versions 2022-08-01 04:09:42 +03:00
Manos Pitsidianakis daa900ec9a Fix embed terminal in macos
Pseudoterminal wasn't created correctly on macos
2022-07-31 18:17:59 +03:00
Manos Pitsidianakis ca84906d7d notifications: escape all quotes in applescript on macos 2022-07-03 13:32:01 +03:00
Manos Pitsidianakis ce269c64e1 conf: don't fail on `server_password_command`
Don't fail when parsing an IMAP config when it has
`server_password_command` set.

Closes #139

Meli stopped recognizing server_password_command configuration #139
2022-06-04 17:57:41 +03:00
Manos Pitsidianakis 0f6f3e30c6 conf: add IMAP config in config parse test 2022-06-04 17:56:54 +03:00
Manos Pitsidianakis e6d6e1f588 compose: don't unwrap if pseudoterminal creation fails
Show error notification instead.
2022-06-04 17:46:22 +03:00
Manos Pitsidianakis dc5afa13db notifications: use osascript/applescript for notifications on macos 2022-06-04 17:46:22 +03:00
Manos Pitsidianakis d6355a3043 melib/email/parser: impl Debug for ParsingError 2022-06-04 17:46:22 +03:00
Manos Pitsidianakis 6a843d4983 melib/maildir: export list_mail_in_maildir_fs() function 2022-06-04 17:46:22 +03:00
Manos Pitsidianakis 9558b2ae92 melib/email: parse Cp1253 as windows1253 encoding 2022-06-04 17:19:06 +03:00
Manos Pitsidianakis 4fdc90b31e Use `open` instead of `xdg-open` in macos
TODO: make this configurable instead.
2022-06-04 17:19:06 +03:00
Manos Pitsidianakis 8563bccd1b listing/conversations: don't cache CellBuffer, only row info
Caching a CellBuffer (a terminal grid view) takes too much RAM on big
mailboxes. Store just the information needed to write each row entry
when needed to draw a page instead.
2022-06-04 17:17:42 +03:00
Manos Pitsidianakis 721891c295 Update nom dependency 2022-05-02 17:04:13 +03:00
Manos Pitsidianakis 2c23ca34cd Update most Cargo dependencies 2022-05-02 17:03:56 +03:00
Manos Pitsidianakis 2eb22a290a
Stop hardcoding certain component colors
`Color::Byte` references were before themes were introduced in the code
base. Their presence is a bug and they should all be replaced by theme
values.

Closes #124

Stop hardcoding certain component colors #124
https://git.meli.delivery/meli/meli/issues/124
2022-03-22 21:00:21 +02:00
Manos Pitsidianakis 5823178cc2
themes.rs: add test that looks in source code for invalid theme key references 2022-03-22 20:26:06 +02:00
Manos Pitsidianakis 81184b182c
Add extra_identities configuration flag
Closes #119

Multi identies per account #119 https://git.meli.delivery/meli/meli/issues/119
2022-03-21 20:53:37 +02:00
Manos Pitsidianakis aa3524dd30
melib/backends/notmuch: fix tag not being removed in set_flags()
May be related to #132

Cannot remove tags in the notmuch backend #132

> Running tag remove TAG on the notmuch backend does nothing. At a
> glance, this seems to be because NotmuchMailbox::set_flags never bothers
> to remove tags that are already present but not in the list of new tags.
> I could try fixing it, but I have no idea how the contribution process
> works here (my guess is the mailing list, but, well, #131).

https://git.meli.delivery/meli/meli/issues/132
2022-03-21 13:13:47 +02:00
Manos Pitsidianakis 23c2355662
utilities.rs: fill and align shortcut table columns 2022-03-20 19:18:40 +02:00
Manos Pitsidianakis d3e62e3d74
utilities/dialogs.rs: use conf shortcuts for scroll {up, down} 2022-03-20 17:15:10 +02:00
Manos Pitsidianakis a866b29499
docs/meli.conf.5: update valid shortcut entries from src/conf/shortcuts.rs
Closes #136

docs/meli.conf.5 does not contain all shortcuts #136 https://git.meli.delivery/meli/meli/issues/136
2022-03-20 17:14:42 +02:00
Manos Pitsidianakis f5dc25ae0d
conf.rs: check that all conf flags are recognized in validation
This commit adds logic in configuration file validation that checks that
each account "extra" field is empty after getting it back from the
backend validation. This is to ensure the user doesn't set options that
are invalidly stated in the documentation or by accident.

Closes #135

Configuration error (xxx): the following flags are set but are not recognized: ["index_style"] https://git.meli.delivery/meli/meli/issues/135
2022-03-20 16:35:18 +02:00
Manos Pitsidianakis d0de04854e
listing.rs: add {in,de}crease_sidebar shortcuts
`increase_sidebar`: Increase sidebar width.
Default value Ctrl-p

`decrease_sidebar`: Decrease sidebar width.
Default value Ctrl-o
2022-02-25 16:40:27 +02:00
Manos Pitsidianakis 340d6451a3
listing.rs: add config setting for sidebar ratio 2022-02-25 16:20:08 +02:00
Manos Pitsidianakis e9aaa7b067
melib/datetime: use *const c_char instead of *const i8 for portability
Using *const i8 broke compatibility with arm64.

Fixes #127
2022-02-07 13:34:26 +02:00
Manos Pitsidianakis d4b690d5d3
melib/imap: send password as byte literal on LOGIN
Concerns #125

Escape IMAP passwords properly https://git.meli.delivery/meli/meli/issues/125
2022-01-10 15:51:27 +02:00
Manos Pitsidianakis ce2068d36b
melib/jmap: fix background watch using JSON paths incorrectly 2022-01-08 20:00:26 +02:00
Manos Pitsidianakis 0d8bedd2d5
melib/jmap: make is_online() await for connection
Closes	#126 https://git.meli.delivery/meli/meli/issues/126
2022-01-08 19:36:11 +02:00
Manos Pitsidianakis 81d1265601
melib/imap: escape IMAP passwords properly
Closes #125
2021-12-12 11:59:22 +02:00
Manos Pitsidianakis d8e9a00563
melib/imap: add quoted REFERENCES field in parsing of responses 2021-11-25 17:53:01 +02:00
Geoff Beier 330a2b20ed
conf.rs: flush stdout in Ask() after printing 2021-11-15 16:48:01 +02:00
Manos Pitsidianakis 36e29cb6fd
Add configurable mailbox sort order
Closes #25

```
     sort_order unsigned integer           (optional) Override sort order on the sidebar for this mailbox.  Example:

                                           [accounts."imap.example.com".mailboxes]
                                             "INBOX" = { index_style = "plain" }
                                             "INBOX/Sent" = { sort_order = 0 }
                                             "INBOX/Drafts" = { sort_order = 1 }
                                             "INBOX/Lists" = { sort_order = 2 }
```
2021-10-31 18:15:39 +02:00
Manos Pitsidianakis 5f003a31be
melib/addressbook/vcard: Parse vCards with just LF instead of CRLF line endings
According to the vcard RFC
https://datatracker.ietf.org/doc/html/rfc6350#section-3.2 all lines must
end with CRLF (\r\n or 0x0d 0x0a)

Some VCard sources use only newline, which, while spec violating is easy
to recover from. So parse them as if they are correct.

Closes #121
2021-10-24 14:31:22 +03:00
Manos Pitsidianakis 2580522931
melib/addressbook: log vcard parsing failures 2021-10-24 14:18:29 +03:00
Manos Pitsidianakis 15ca25af73
Bump version to 0.7.2 2021-10-15 12:36:37 +03:00
Manos Pitsidianakis 37d0846195
melib/email/address: quote display_name if it contains "," 2021-10-15 12:29:52 +03:00
Manos Pitsidianakis ffc498a5d0
melib/smtp: fix Cc and Bcc ignored when sending mail 2021-10-15 12:27:51 +03:00
Manos Pitsidianakis d25eb00a11
command: improve(?) command completion and add test 2021-10-07 21:29:46 +03:00
Manos Pitsidianakis 240374950a
melib/email/address: quote display_name if it contains "." 2021-10-04 12:36:22 +03:00
Manos Pitsidianakis 505adca54d
Add forward mail option
Forward email with shortcut 'forward' (default ctrl+f)

This opens a composing tab letting you to select receiver etc.

"composing" config setting "forward_as_attachment" selects the
forwarding behavior:

- "ask" asks you ever time
- true always forwards by attaching the entire email as a single
attachment
- false always forwards by inlining the email, like most email clients
do.

Closes #120
2021-10-02 13:38:50 +03:00
Manos Pitsidianakis e090c31f96
state: Move grid to Screen struct under terminal mod 2021-09-20 13:56:51 +03:00
Manos Pitsidianakis 20feb50475
view/thread: open the latest email in the thread by default 2021-09-18 11:36:17 +03:00
Manos Pitsidianakis f975e1004c
Add url_launcher config setting 2021-09-16 16:43:43 +03:00
Manos Pitsidianakis b88c3c573d
Add add_addresses_to_contacts command 2021-09-16 16:27:21 +03:00
Manos Pitsidianakis 32901f57d2
Add show_date_in_my_timezone pager config flag
Closes #28
2021-09-15 22:19:19 +03:00
Manos Pitsidianakis d1712557cb
docs: add pager filter documentation 2021-09-13 13:34:10 +03:00
Manos Pitsidianakis a977351f0a
mail/view: respect per-folder/account pager filter override 2021-09-13 13:21:09 +03:00
Manos Pitsidianakis e7b9d2963c
pager: add filter command, esc to clear filter 2021-09-12 17:39:51 +03:00
Manos Pitsidianakis 25579d8807
terminal/cells: remove ansi module 2021-09-12 16:36:36 +03:00
Manos Pitsidianakis 22fb2ed46c
Implement pager filter through EmbedGrid
Parse pager filter output as an EmbedGrid instead of the old ansi parser
module.
2021-09-12 14:55:24 +03:00
Manos Pitsidianakis 733de5a5fb
Fix some clippy suggestions 2021-09-12 14:33:00 +03:00
Manos Pitsidianakis 592339bdca
embed: split EmbedGrid to EmbedTerminal and EmbedGrid
An embedded pseudoterminal was enclosed in the EmbedGrid struct. This
commit splits it into EmbedTerminal and EmbedGrid, with EmbedGrid
containing only the CellBuffer grid logic. With this change we can reuse
EmbedGrid to parse ANSI output from external programs into meli's
CellBuffer's.
2021-09-12 13:47:32 +03:00
Manos Pitsidianakis ae8c2addab
Show compile time features in with command argument
Show compile time feature flags with compiled-with subcommand

Closes #115
2021-09-08 22:09:32 +03:00
Manos Pitsidianakis bc08bf1d13
Bump version to 0.7.1 2021-09-08 16:20:02 +03:00
Manos Pitsidianakis 7533df86e0
Fix compilation for netbsd-9.2
$ rustc -V
rustc 1.52.1
$ cargo -V
cargo 1.52.0

Pre-requisite steps needed for build:
- Needed to install mozilla certs
- Needed to set OPENSSL_DIR=/usr
2021-09-06 18:54:40 +03:00
Manos Pitsidianakis 526a246430
melib/nntp: update total/new counters on new articles 2021-09-05 16:02:37 +03:00
Alex.F 69916f267b
add 'GB18030' charset 2021-09-05 13:08:32 +03:00
Manos Pitsidianakis 13c5798c7b
conf/shortcuts.rs: add info_message_{next,previous} 2021-09-05 13:08:05 +03:00
Manos Pitsidianakis 07e166e1fb
melib/error: Add kinds: NotImplemented, NotSupported, OSError 2021-09-05 12:39:15 +03:00
Manos Pitsidianakis 72a2ba20dc
conf/accounts.rs: print info when displaying watch error 2021-09-05 12:38:40 +03:00
Manos Pitsidianakis c8da6d2049
melib/nntp: implement refresh 2021-09-05 12:09:29 +03:00
Manos Pitsidianakis 90042379a6
melib/{imap,nntp}: throw error on extra unusued conf flags 2021-09-04 21:49:31 +03:00
Manos Pitsidianakis f40ae9e11b
Change all Down/Up shortcuts to j/k 2021-09-04 20:06:07 +03:00
Manos Pitsidianakis 09f3edba76
config: show explanation if `composing` field missing 2021-09-04 20:06:07 +03:00
Manos Pitsidianakis 09dc0a2409
melib/conf: deserialize ToggleFlag from bool & string 2021-09-04 20:06:07 +03:00
Manos Pitsidianakis 3bc187c570
melib/collections: add RwRef{,Mut} structs 2021-09-04 17:05:11 +03:00
Manos Pitsidianakis 05393d8caa
listing/conversations: highlight two rows instead of three 2021-09-04 16:56:55 +03:00
Manos Pitsidianakis b49d965695
Fix unused var etc warnings 2021-09-04 16:52:17 +03:00
Manos Pitsidianakis 6235164df2
melib/nntp: increase chunk size 2021-09-04 16:06:42 +03:00
Manos Pitsidianakis 521f634e7b
melib/nntp: implement NNTP posting 2021-09-04 00:42:19 +03:00
Manos Pitsidianakis 978939d8e3
Bump version to 0.7.0 2021-09-03 16:10:37 +03:00
Manos Pitsidianakis d1437ff275
command/actions.rs: ask confirmation for delete 2021-09-03 14:10:58 +03:00
Manos Pitsidianakis f097593bed
melib/datetime: fix date format string 2021-08-12 10:38:05 +03:00
Ludovic LANGE b08570349d
Notmuch dynamic library can now be loaded on macos
On macos, the name of dynamic libraries is a little bit different than on Unix.
The code was looking for a `libnotmuch.so.5` library, while on macos
it's installed as `libnotmuch.5.dylib`.

This commit uses cfg attribute to conditionally change the library name.

Closes #106
2021-07-19 05:04:04 +03:00
Manos Pitsidianakis d6bf700175
Update Cargo.lock 2021-07-19 03:52:48 +03:00
Manos Pitsidianakis 5a9f63c51e
listing/compact: Fix off-by-one error in area calculation 2021-07-18 10:26:19 +03:00
Manos Pitsidianakis 9d7e877510
listing/plain: move flags to subject column 2021-07-18 10:26:19 +03:00
Manos Pitsidianakis ad2a10478e
listing/compact: move flags to subject column
Flags (attachment, unseen, etc) being their own column
overcomplicated code, so just prepend them to subject column.
2021-07-18 10:26:19 +03:00
Manos Pitsidianakis 64b62352d0
listing: add {unseen,selected,attachment,thread_snoozed} flag config values 2021-07-18 10:26:19 +03:00
Manos Pitsidianakis b411daddaa
listing/conversations: remove entry padding box character
Remove padding each entry with '░' box character.
2021-07-18 10:26:19 +03:00
Ludovic LANGE 66c6b62aa6
Cargo.lock: Update lexical-core version
Fixes compilation on macos 10.15.3, rustc 1.53.0
2021-07-05 23:41:55 +03:00
Manos Pitsidianakis eea9ac2b58
README.md: update with new IRC channel location 2021-06-13 11:27:33 +03:00
Manos Pitsidianakis d16866e0f0
notifications: run update_xbiff even if notifications disabled 2021-01-15 16:41:40 +02:00
Manos Pitsidianakis bcca9abe66
docs: Use example.com in documentation
Closes #96
2021-01-15 16:41:40 +02:00
Manos Pitsidianakis 24b4c117e7
melib: don't use both {set,push}_references()
set_references() already calls push_references()
2021-01-15 16:41:40 +02:00
Manos Pitsidianakis b0fba401e6
melib/mbox: consistent line endings in MboxFormat::append 2021-01-11 19:11:08 +02:00
Manos Pitsidianakis 48d4343082
utilities/ProgressSpinner: add interval field and new spinners 2021-01-11 19:11:08 +02:00
Manos Pitsidianakis 2dfeb29b75
jobs/Timer: add set_interval() 2021-01-11 19:11:08 +02:00
Manos Pitsidianakis 63d2fb93f4
melib/nntp: fix not connecting with TLS 2021-01-11 19:11:08 +02:00
Manos Pitsidianakis cf9457882a
melib/mbox: add MboxMetadata type and write support 2021-01-11 19:11:08 +02:00
Manos Pitsidianakis 3fa9e355c2
melib/email: add Flag is_*() methods 2021-01-11 18:46:22 +02:00
Manos Pitsidianakis 3dae84182c
melib/mbox: add module-level doc 2021-01-11 18:46:11 +02:00
Manos Pitsidianakis a4ae4da8b1
Add export-mbox command 2021-01-10 01:45:03 +02:00
Manos Pitsidianakis 4050f6893f
melib/mbox: add MboxFormat::append() method
Add support for writing mbox files
2021-01-10 01:40:54 +02:00
Manos Pitsidianakis dcccd303ac
melib/mbox: rename MboxReader to MboxFormat 2021-01-10 01:40:54 +02:00
Manos Pitsidianakis 22a64e2d76
melib: Remove unnecessary "pub use" std exports 2021-01-10 01:40:27 +02:00
Manos Pitsidianakis 781a1d0e1b
melib/backends: add collection() method to MailBackend
Keep track of the Collection state in the backend side
2021-01-10 01:31:27 +02:00
Manos Pitsidianakis eb8d29813c
utilities/Tabbed: send VisibilityChange event on changing tab 2021-01-08 18:37:51 +02:00
Manos Pitsidianakis 08af46f5ef
melib/datetime: fix test compile failure 2021-01-08 18:37:51 +02:00
Manos Pitsidianakis 2f47f1eebd
melib/jmap: fix mailbox children relationships being ignored 2021-01-08 15:23:25 +02:00
Manos Pitsidianakis 622ded8021
compose: add attribution line for replies 2021-01-08 15:01:38 +02:00
Manos Pitsidianakis 6d63429ad3
Add scrolling context to StatusBar
- Whenever a scrolling context is entered/exited, send a ScrollUpdate event.
- StatusBar maintains a stack of scrolling contexts and displays the
last one, if it exists. Each context is associated with a ComponentId.
- To handle dangling contexts after their Components aren't visible
anymore, send a VisibilityChange event in situations where that scenario
is possible.
2021-01-08 15:01:38 +02:00
Manos Pitsidianakis 5eb4342af8
Update dependencies, update indexmap to ^1.6 2021-01-08 15:01:38 +02:00
Manos Pitsidianakis eca10a5660
melib/backends: add mailbox management events to RefreshEventKind
Add mailbox management events from RFC 5423 Internet Message Store
Events

https://tools.ietf.org/html/rfc5423#page-8
2021-01-08 15:01:38 +02:00
Manos Pitsidianakis a697dfabbd
melib/jmap: use receivedAt as alternative to Date in Envelope gen 2021-01-08 15:01:38 +02:00
Manos Pitsidianakis 23997bdec0
melib/jmap: add UTCDate queries in EmailFilterCondition
Not necessarily working, added as stubs for future work

Closes #62
2021-01-08 15:01:37 +02:00
Manos Pitsidianakis 2e6a1e1ef8
melib/datetime: rename tests for consistency 2021-01-08 15:01:37 +02:00
Manos Pitsidianakis fe200a3218
melib/datetime: isolate unsafe blocks
Isolate unsafe blocks where possible to make code review easier
2021-01-08 15:01:37 +02:00
Manos Pitsidianakis bf9143d8e4
melib/datetime: use Cow<'_, CStr> in timestamp_to_string()
Use Cow to avoid unnecessary allocations when provided a nul-terminated
format string
2021-01-08 15:01:37 +02:00
Manos Pitsidianakis 441dcb62ca
melib/datetime: add format string constants 2021-01-08 15:01:37 +02:00
Manos Pitsidianakis 4cd3e28244
melib/datetime: fix import style inconsistencies 2021-01-08 15:01:37 +02:00
Manos Pitsidianakis 3dba6fdf60
melib/datetime: add posix locale arg in timestamp_to_string() 2021-01-08 15:01:37 +02:00
Manos Pitsidianakis 50cd81772f
melib/jmap: impl watch() with polling
Concerns #22
2021-01-05 19:45:26 +02:00
Manos Pitsidianakis 613c3de3d2
melib/connections: add async sleep(dur: Duration) 2021-01-05 19:45:26 +02:00
Manos Pitsidianakis 62db7d7f32
melib/jmap: put JmapSession behind mutex
And deserialize API urls to Arc<String>.
2021-01-05 17:12:14 +02:00
Manos Pitsidianakis 1c25ae12eb
Use default_cell in CellBuffer resize(), clear() 2021-01-05 17:12:14 +02:00
Manos Pitsidianakis ccc083cf88
Rewrite Cellbuffer Debug impl 2021-01-05 17:12:14 +02:00
Manos Pitsidianakis db69349251
melib/notmuch: avoid parsing entire email in Envelope creation 2021-01-05 17:12:13 +02:00
Manos Pitsidianakis 806254436b
melib/notmuch: add AccountHash field to NotmuchDb 2021-01-05 17:12:12 +02:00
Manos Pitsidianakis 4f164dc700
melib/notmuch: cleanup Query new() method 2021-01-05 17:11:08 +02:00
Manos Pitsidianakis ab0ef1b63c
melib/thread: hash Message-ID for ThreadNodeHash
Make ThreadNodeHash predictable.
2021-01-05 17:11:08 +02:00
Manos Pitsidianakis b966ee8fbd
melib/email: return &mut Self in set_*() methods
Return &mut Self to allow chaining setter methods
2021-01-05 17:11:08 +02:00
Manos Pitsidianakis 34e970d922
melib/datetime: Add Locale struct for error checking 2021-01-04 23:18:55 +02:00
Zisu Andrei f7cbd9a64d
melib/datetime: Set C locale for strptime parsing
This is the locale that should be used for computer interoperability
when doing date and time parsing and formatting.

Without this, on systems which don't have the US locale set, the parsing
returns 0.
2021-01-04 23:18:55 +02:00
Manos Pitsidianakis 829f1243fb
melib/imap: fix invalid FETCH edge case
If mailbox was empty, this FETCH would request "0:*" which is an invalid
message set since message sequence numbers start with 1.
2020-12-30 16:19:15 +02:00
Manos Pitsidianakis 1be30968ca
melib/mbox: fix FreeBSD compilation
Reported in #92
2020-12-29 21:12:38 +02:00
Manos Pitsidianakis 92475c349a
melib/mbox: return Result in file locking 2020-12-29 21:11:52 +02:00
Manos Pitsidianakis 2d5f5e767c
listing/conversations: hash addr by addr_spec in from_address_list
While accumulating addresses for the 'From' list for each envelope
entry, hash the addresses by the address spec (i.e. the email address)
instead of the entire address. This prevents duplicates of the same
email address but with different display names.
2020-12-25 06:10:28 +02:00
Zisu Andrei 0034f195e3
melib/imap: Lazy evaluate idle capability
With the eager evaluation, you run the risk of checking the capabilities
store before any connection to the server may have been opened.
Therefore, the capabilities uid_store will be empty and it will fall
back to poll_with_examine even if the server might have support for
idle.
2020-12-25 06:10:28 +02:00
Manos Pitsidianakis 9124ad0ae7
conf/accounts: remove some unnecessary unwraps 2020-12-25 06:10:28 +02:00
Manos Pitsidianakis ed826357a3
Don't unwrap try_recv() on async jobs channels
Job might have been canceled.
2020-12-25 06:10:28 +02:00
Manos Pitsidianakis b2e853dd7b
melib/imap: update unseen count on \Seen set_flags() 2020-12-24 10:58:31 +02:00
matzipan@gmail.com aa503deb76
melib/imap: Set special usage attributes for INBOX
Without this change, the usage is not correctly identified when calling
imap_mailboxes in the imap backend.
2020-12-24 10:51:57 +02:00
Manos Pitsidianakis fee8f5b575
melib/backends: move IsSubscribedFn to backends mod 2020-12-24 10:50:07 +02:00
Manos Pitsidianakis 7e977fe627
melib/imap/cache/sync: explicitly remove new seen messages from unseen counter 2020-12-24 10:50:07 +02:00
Manos Pitsidianakis 09684e821d
melib/imap: check INBOX when pausing IDLE 2020-12-24 10:50:07 +02:00
Manos Pitsidianakis 10b10e6267
README.md: add mirror links 2020-12-07 15:53:41 +02:00
Manos Pitsidianakis 48e7a493a9
Add reload-config command
Closes #84 Add "reload configuration" command
2020-12-02 21:01:22 +02:00
Manos Pitsidianakis e5b0ff4fe2
state: remove runtime_settings 2020-12-02 21:01:22 +02:00
Manos Pitsidianakis 68f9d1220b
melib/imap: remove DoubleEndedIterator for ImapLineIterator 2020-12-02 17:10:43 +02:00
Manos Pitsidianakis 1408690a9a
melib/imap: don't retry watch conn on non-network error 2020-12-02 17:10:43 +02:00
Manos Pitsidianakis 76814cea20
themes/sail: make only headers_name bold 2020-12-02 17:10:43 +02:00
Manos Pitsidianakis 7e1e57a2df
conf/themes: add mail.view.headers_names and mail.view.headers_area
Allow separate customization of header names and the rest of the header
area.
2020-12-02 17:10:42 +02:00
Manos Pitsidianakis f8a47586e9
mail/listing: show mailbox loading state in status 2020-12-02 17:10:42 +02:00
Manos Pitsidianakis 7efbe6d692
listing: fix menu/sidebar not being redrawn on updates 2020-12-01 20:03:58 +02:00
Manos Pitsidianakis 0f86934e16
mail/status: display in-progress jobs first 2020-12-01 20:03:58 +02:00
Manos Pitsidianakis c5a5c2666b
utilities/pager: show scrolling percentage and/or search results position 2020-12-01 20:03:58 +02:00
Manos Pitsidianakis 7db32ff1b3
terminal/cells: return success flag in CellBuffer::resize() 2020-12-01 01:04:27 +02:00
Manos Pitsidianakis 857d4d546f
utilities/pager: use LineBreakText for lazy line breaking 2020-12-01 01:04:27 +02:00
Manos Pitsidianakis 5327dae02d
melib/text_processing: add LineBreakText iterator
A lazy stateful iterator for line breaking text. Useful for very long text where you don't want to linebreak it completely before user requests specific lines.
2020-12-01 01:04:27 +02:00
Manos Pitsidianakis c990687e5f
docs/meli-themes.5: replace toml spec dead link 2020-12-01 01:04:27 +02:00
Manos Pitsidianakis 453bb0b2b2
melib/smtp: implement gmail XOAUTH2 authentication method 2020-11-30 06:52:16 +02:00
Manos Pitsidianakis 4914f29e20
themes: make conversations defaults grey 2020-11-30 02:20:09 +02:00
Manos Pitsidianakis bedf181aff
melib/imap: examine all mailboxes before idle 2020-11-30 02:20:09 +02:00
Manos Pitsidianakis 9dd21eea50
melib/threads: prefer local ThreadNode env_hash
When inserting an envelope in a thread and its Message-ID already exists
with an associated envelope, overwrite the association if the previous
associated envelope is from a foreign mailbox and current envelope is
not. This happens when mail from a sent folder has been inserted in eg
your INBOX, but somehow INBOX has a copy of your own message as well.
This can happen when mailing lists that send you copies of your own
posts.

The problem with this was that in IMAP your mailing list copy was unseen
and you could not mark it seen because the thread only knew about your
Sent mailbox copy.
2020-11-30 02:20:09 +02:00
Manos Pitsidianakis 4939a1ad9e
melib/imap: remove some debug prints 2020-11-30 02:20:09 +02:00
Manos Pitsidianakis 8e7583a32f
melib/imap: don't clear mailbox counts before fetching 2020-11-30 02:20:09 +02:00
Manos Pitsidianakis 5f6b4745b8
melib/imap: don't use UNSEEN select response for unseen count
UNSEEN field in SELECT/EXAMINE response is meant to be the message
sequence number of the first unseen message, not the count of unseen
messages.
2020-11-30 02:20:08 +02:00
Manos Pitsidianakis 76c1c1a213
melib/imap: don't examine unloaded mailboxes for updates
In examine_updates() which is periodically executed in the IMAP watch
thread, the mailbox's contents get fetched regardless if the user has
fetched the mailbox before. That means eg a large mailbox that was
unused by the user might perform a large fetch all of a sudden without
the user's knowledge/touch.

Add `warm` property in ImapMailbox that states whether the mailbox has
been loaded before in current execution.

Closes #88 IMAP: don't examine unloaded mailboxes for updates, just for message count stats
2020-11-30 02:20:08 +02:00
Manos Pitsidianakis ddfadc748d
melib/imap: don't fetch RFC822 except when requested
In some cases when handling new server events, the entire body message
was unnecessarily fetched.

Closes #87 IMAP: don't fetch RFC822 except when requested
2020-11-30 02:20:08 +02:00
Manos Pitsidianakis 66dea9148b
mail/view: don't update() if coordinates are unchanged 2020-11-29 00:54:27 +02:00
Manos Pitsidianakis 7b3fb86483
mail/view: reset self.theme_default on loading envelope
self.theme_default might have initial value from MailView::default()
which does not correspond to actual theme_default
2020-11-28 20:33:14 +02:00
Manos Pitsidianakis d8c978ed2d
mail/view/thread: fix scrollbar incorrect rendering 2020-11-28 20:33:14 +02:00
Manos Pitsidianakis d076ff573f
MailView, StatusBar: Fix area bound check 2020-11-28 20:33:14 +02:00
Manos Pitsidianakis 6cbb89a8e5
utilities/widgets: fix tiny scrollbar grievances
- set minimum width/height to 1
 - set reverse terminal attribute on !use_color
 - use < > ^ v arrows and # block char if ascii_drawing
2020-11-28 20:33:14 +02:00
Manos Pitsidianakis aa89969dca
utilities: use align_area in shortcut help panel
Before this commit shortcut help panel used to span almost all of the screen.

Use align_area() to center shortcut help box to its minimally required
size.
2020-11-28 20:33:14 +02:00
Manos Pitsidianakis 6a67322570
utilities: add scrollbar on y overflow in shortcuts panel 2020-11-28 20:33:14 +02:00
Manos Pitsidianakis 3e109cabf0
Add sail theme 2020-11-28 20:33:14 +02:00
Manos Pitsidianakis 1cbb6828f2
Add nord theme 2020-11-28 16:33:30 +02:00
Manos Pitsidianakis de018294e4
conf/themes: make notifications bg default color instead of red 2020-11-28 16:33:30 +02:00
Manos Pitsidianakis 6dd3b0bb4f
Fix theme_default not being respected 2020-11-28 16:33:30 +02:00
Manos Pitsidianakis 714ccb5e16
Move Color to src/terminal/color.rs 2020-11-28 16:33:30 +02:00
Manos Pitsidianakis 8d9247e9a3
listing: show auto-hide scrollbar in sidebar menu
Setting to turn it off is listing.show_menu_scrollbar.

Concerns #85 Accounts sidebar doesn't scroll
2020-11-28 16:33:10 +02:00
Manos Pitsidianakis b659749880
listing: scroll account sidebar menu
Closes #85 Accounts sidebar doesn't scroll
2020-11-28 16:32:16 +02:00
Manos Pitsidianakis b053aaa145
listing: prevent invalid area in print_account() 2020-11-28 16:03:36 +02:00
Manos Pitsidianakis 883b3e3a4f
mail/view: show multipart/alternative files properly in attachment list
Show entire multipart/alternative alternatives in attachment list
instead of only the displayed one, in order for the user to be able to
switch alternatives at will.
2020-11-28 15:59:25 +02:00
Manos Pitsidianakis 98c1ece28d
Update xdg-util dependency to 0.4.0 2020-11-28 15:59:25 +02:00
Manos Pitsidianakis 54b2066f73
mail/view: set dirty after closing ContactSelector 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis 007e6320d5
utilities: respect theme_default in shortcut dialog 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis e01275cd93
utilities/dialogs: add cursot Unfocused state as default 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis 879af75d88
utilities/dialogs: use align_area to create box 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis 6a5bb2e057
Add align_area() and Alignment enum 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis 311c1a8a95
utilities/dialogs: respect theme_default 2020-11-25 21:19:22 +02:00
Manos Pitsidianakis ce5c7848e8
utilities: move dialogs to its own submodule 2020-11-25 21:19:22 +02:00
Andrew Jeffery daee4e46de
Allow configuration of the sidebar divider
This adds the config option listing.sidebar_divider to set the character
used to show the divider (defaults to ' ') along with the corresponding
theme in mail.sidebar_divider which defaults to the default theme.
2020-11-25 15:54:47 +02:00
Manos Pitsidianakis 92c12d3526
melib/imap: implement OAUTH2 authentication 2020-11-24 14:28:28 +02:00
Manos Pitsidianakis 0a8a0c04c8
compose: treat inline message/rfc822 as attachments 2020-11-24 14:28:28 +02:00
Manos Pitsidianakis ede5851baf
utilities: reverse order of drawing fields in form
Reverse order of drawing since a field might have an auto complete
prompt below it, so rendering the field below instead of above next
would overwrite it.
2020-11-24 14:28:28 +02:00
Manos Pitsidianakis 79345b3e84
utilities/StatusBar: fix lack of bounds checking in hist_area 2020-11-24 14:28:28 +02:00
Manos Pitsidianakis b46cd09ca6
compose: pass body text when replying
Get rendered body text when creating a new reply Composer instead of
rendering the text in the Composer constructor.

Closes #86
2020-11-24 10:36:31 +02:00
Manos Pitsidianakis bf56c88918
compose: respect auto_choose_multipart_alternative when rendering multipart/alternative attachments to text 2020-11-24 10:36:31 +02:00
Manos Pitsidianakis 73372ff1e7
compose: add show_comments arg to attachment_displays_to_text()
Toggle display of attachment comments (for example "this html attachment
was rendered with X filter...") when rendering text.
2020-11-24 10:36:21 +02:00
Manos Pitsidianakis d4f508642a
widgets: allow text overflow in text fields
Show text content of a text field that exceeds the visible width
properly.
2020-11-24 10:36:21 +02:00
Manos Pitsidianakis f69f623818
Fix some invalid area calculations 2020-11-24 02:23:07 +02:00
Manos Pitsidianakis 2ef2add67f
imap: fix untrimmed query str resulting in invalid search criteria in cyrus 2020-11-24 02:18:41 +02:00
Manos Pitsidianakis 458209b448
view/thread: clear empty space in draw_list 2020-11-24 02:18:41 +02:00
Manos Pitsidianakis b7c48a1ed0
view/thread: make list draw area consistent 2020-11-24 02:18:41 +02:00
Manos Pitsidianakis f25f93fccf
utilities: Fix incorrect calculations in ScrollBar 2020-11-24 02:18:31 +02:00
Manos Pitsidianakis 31e4ed006d
listing: fix off by one error in PageDown movement 2020-11-24 02:18:31 +02:00
Manos Pitsidianakis 179ed52add
compose: grey embed area when embed is stopped
When stopping the embedded terminal with Ctrl-Z or SIGSTOP, show the
terminal area greyed out with a message box.
2020-11-24 02:18:21 +02:00
Manos Pitsidianakis ebc290cc2a
compose: set format flowed if configured in pager 2020-11-24 02:18:21 +02:00
Manos Pitsidianakis f9ce5327c2
melib/imap: fix some LazyCountSet logic errors in sync 2020-11-24 02:18:21 +02:00
Manos Pitsidianakis 5b86c342fb
Update smallvec dependency to 1.5.0
Fixes panicking when loading cached serialized email from older versions
of meli.

https://github.com/servo/rust-smallvec/pull/238
2020-11-22 06:24:38 +02:00
Manos Pitsidianakis 0aa5cf273f
mail/status: don't overwrite "In-progress jobs header" 2020-11-21 02:09:39 +02:00
Manos Pitsidianakis 041257f9a6
melib/text_processing: fix CodePointsIterator implementation
Old implementation was redundant and broken.
2020-11-21 02:09:18 +02:00
Manos Pitsidianakis 1da6d75b08
melib/text_processing: add new wcwidth implementation
Download and parse Unicode data files to judge code point width.
Inspired by https://github.com/ridiculousfish/widecharwidth/
2020-11-21 02:09:18 +02:00
Manos Pitsidianakis a7c0bca8ce
Fix test errors and warnings 2020-11-21 02:09:18 +02:00
Manos Pitsidianakis 023afbaae3
RateLimit: remove unupdated test 2020-11-16 00:45:18 +02:00
Manos Pitsidianakis 1c62de57ae
Never return true on processing JobFinished
JobFinished events are not meant to be inhibited.
2020-11-15 21:30:54 +02:00
Manos Pitsidianakis 76f8bdc558
Add configurable shortcut for 'quit'
Quit ('q' button) was hardcoded, switch to configurable shortcut setting
instead.
2020-11-15 21:30:54 +02:00
Manos Pitsidianakis d404910a0f
melib/smtp: impl AUTH LOGIN
AUTH LOGIN is deprecated but predictably still around.
2020-11-15 21:30:54 +02:00
Manos Pitsidianakis c0e3e78940
listing: dont overdraw menu over listing 2020-11-15 21:30:54 +02:00
Manos Pitsidianakis aaee6d094c
Fix NO_COLOR cursor highlight in sidebar and progress spinner 2020-11-12 03:19:56 +02:00
Manos Pitsidianakis 60350eaa88
mail/status: add "general" shortcut section 2020-11-12 03:19:56 +02:00
Manos Pitsidianakis aa73bd71c3
listing: fix mailbox tree rendering
Indentation value was being interpreted mirrored (raw binary value in
parenthesis):

   0  testing_account (0)
   1   ┣━Archives     (0)
   2   ┃ ┣━2014       (1)
   3   ┃ ┃ ┗━10       (11)
   4   ┃ ┗━2015       (1)
   5     ┃ ┗━05       (10) <- invalid/mirrored
   6   ┣━Drafts       (0)

Should be:

   0  testing_account (0)
   1   ┣━Archives     (0)
   2   ┃ ┣━2014       (1)
   3   ┃ ┃ ┗━10       (11)
   4   ┃ ┗━2015       (1)
   5   ┃   ┗━05       (10)
   6   ┣━Drafts       (0)
2020-11-11 17:14:34 +02:00
Manos Pitsidianakis aa7ebf2918
melib/smtp: fix SMTP syntax error on DSN notify use 2020-11-10 20:30:50 +02:00
Manos Pitsidianakis 2544f54107
melib/compose: prevent bare newlines in finalised drafts 2020-11-10 17:26:06 +02:00
Manos Pitsidianakis 72084da185
Add store_sent_mail option for gmail
- store_sent_mail boolean

Store sent mail after successful submission.
This setting is meant to be disabled for non-standard behaviour in
gmail, which auto-saves sent mail on its own.
2020-11-09 22:22:11 +02:00
Manos Pitsidianakis 23777171f2
listing: clear_area in draw_menu
Completely clear area in draw_menu instead of resetting ch, fg, bg etc.
2020-11-09 19:45:09 +02:00
Manos Pitsidianakis cbaf21764c
Remove status tab, move account status page to listing 2020-11-09 19:35:47 +02:00
Manos Pitsidianakis da69eecafe
mail/status: make AccountStatus public
And fix areas passed to write_string_to_grid() to have the same y
coordinate in upper_left and bottom_right part.
2020-11-09 18:44:56 +02:00
Manos Pitsidianakis f0800f38a8
melib/maildir: make MaildirOp return Result<PathBuf> 2020-11-09 03:36:40 +02:00
Manos Pitsidianakis a34f0aac5b
melib: fix bincode serialization
Previous commit changed bincode deserializes in maildir and sqlite3.rs
from bincode::deserialize_from to using bincode::config::DefaultOptions
and bincode::Options trait's method deserialize_from.

However, these two different deserializes use a different default
settings: https://github.com/servo/bincode/issues/348

Specifically, varint encoding for integers is the default for
DefaultOptions but not when using bincode::{de,}serialize_* functions.
That means that serialized structs were not able to be deserialized.
This commit makes all {de,}serializations use the DefaultOptions
interface rather than the top level functions.
2020-11-09 00:40:32 +02:00
Manos Pitsidianakis 353ac2d029
melib: set upper limit for bincode deserialize
If struct memory layout changes, bincode deserialize fails with memory
allocation error of an obscene amount of bytes. Set upper limit to
deserialized bytes in each place deserialize happens.
2020-11-06 19:05:09 +02:00
Manos Pitsidianakis 6c07046b66
Update bincode dependency to 1.3.1 2020-11-06 18:38:18 +02:00
Manos Pitsidianakis 8ac5558d65
Makefile: add CARGO_ARGS env var
Intended for use with cross-arch compilation flags (--target etc).
2020-11-05 21:11:27 +02:00
Manos Pitsidianakis 43d3d3681e
Makefile: replace install(1) with mkdir, rm, cp, chmod
install(1) is missing in some systems, so replace it with POSIX tools.

Closes #83.
2020-11-05 21:09:42 +02:00
Rudi Horn f1bdae65ee
melib/jmap: add HTTP redirect policy to client
Meli currently uses the .well-known/jmap URL and the RFC8620 requires that any redirects are followed (https://tools.ietf.org/html/rfc8620#section-2.2). This small change allows redirects to happen.
2020-11-04 20:07:51 +02:00
Manos Pitsidianakis 6cc43540d6
docs/meli.conf.5: add SmtpPassword examples 2020-10-30 22:40:56 +02:00
Manos Pitsidianakis 6392904047
Replace PosixTimer with async timers 2020-10-29 13:18:36 +02:00
Manos Pitsidianakis 57e6cf3980
Limit dbus dependency to target_os = "linux" 2020-10-28 23:28:41 +02:00
Manos Pitsidianakis 9a9c876f4a
melib: add more encodings
Add more encodings already supported by `encoding` crate:

  - iso-8859-3,
  - iso-8859-4,
  - iso-8859-5,
  - iso-8859-6,
  - iso-8859-8,
  - iso-8859-10,
  - iso-8859-13,
  - iso-8859-14,
  - iso-8859-16,
  - gb-2312
  - big-5
  - iso-2022-jp
  - euc-jp
  - koi8-r
  - koi8-u
  - utf-16
2020-10-26 22:26:29 +02:00
Manos Pitsidianakis afa74ccfb5
compose: add From text entry autocomplete 2020-10-24 14:36:39 +03:00
Manos Pitsidianakis 560771b32a
widgets: select AutoCompleteEntry on Enter 2020-10-24 14:32:02 +03:00
Manos Pitsidianakis 7b1ab389fa
Remove unused plugin interface 2020-10-21 17:58:30 +03:00
Manos Pitsidianakis 594a2bd0dd
listing: add set operations to range select actions
Add symmetric difference (default), union, difference and intersection
modifiers for selecting ranges. That way you can quickly construct the
selection set you need.
2020-10-21 14:36:51 +03:00
Manos Pitsidianakis 05ef863a45
utilities: move PageMovement to components mod 2020-10-21 13:19:13 +03:00
Manos Pitsidianakis d5aa2cb3ef
melib/line_break: add segment tree impl
The widths of subslices of a line are calculated in each call to
`binary_search_by` when reflowing long lines. This can be done in Ologn
queries with a segment tree.
2020-10-20 23:53:00 +03:00
Manos Pitsidianakis f7fc2e31e0
melib: Remove unused crossbeam dependency 2020-10-20 23:30:29 +03:00
Manos Pitsidianakis 00f5c4b9c0
melib/maildir: split parsing into big chunks 2020-10-20 23:27:10 +03:00
Manos Pitsidianakis 4b91de3d59
state: remove overlay widgets on ComponentKill events 2020-10-20 23:19:13 +03:00
Manos Pitsidianakis eb36034740
accounts: autoload Sent folders automatically 2020-10-20 23:18:27 +03:00
Manos Pitsidianakis d4e347289c
melib/README: update feature table 2020-10-20 23:15:52 +03:00
Manos Pitsidianakis 662706607b
melib: remove memmap dependency
It's unmaintained, and the IO performance gains are negligible
2020-10-20 22:41:44 +03:00
Manos Pitsidianakis b904f91f45
README: replace svg with webp screenshots
Gitea doesn't render svg images (delivers them as text/plain)
2020-10-20 22:13:02 +03:00
Manos Pitsidianakis 9f39a7c5a1
statusbar: delete num buffer chars with Backspace 2020-10-20 15:09:00 +03:00
Manos Pitsidianakis 126ed8a189
statusbar: don't overwrite num buffer when progress spinner is deactivated 2020-10-20 15:04:50 +03:00
Manos Pitsidianakis 91fe7435f7
melib/imap: add suggestion on STARTTLS error
If server port is 993 (IMAPS) and starttls is enabled, suggest turning
it off if starttls fails.
2020-10-20 14:58:16 +03:00
Manos Pitsidianakis 7a9c150f33
melib/imap: fetch References header along with ENVELOPE
Threading was broken if information was needed from References header.
For example, mailman might alter some Message-IDs when using its NNTP
bridge and the complete references are necessary to rebuild the thread,
which is only available in References whereas ENVELOPE has only
In-Reply-To.
2020-10-18 17:42:54 +03:00
Manos Pitsidianakis b9f4d718c7
melib/sqlite3: reset db on version mismatch 2020-10-18 17:41:06 +03:00
Manos Pitsidianakis 54cb4ea623
melib/build.rs: remove unnecessary file creation 2020-10-18 15:34:09 +03:00
Manos Pitsidianakis 7919e95ddd
terminal/embed: remove some allocations and unwraps 2020-10-18 15:02:18 +03:00
Manos Pitsidianakis 89940dd606
cli-docs: compress included text 2020-10-17 20:50:29 +03:00
Manos Pitsidianakis b69bc219c3
README.md: Add screenshots and update text 2020-10-17 15:02:38 +03:00
Manos Pitsidianakis bb51d36579
composer: send NewJob event on submission 2020-10-16 22:30:56 +03:00
Manos Pitsidianakis a2456fa3f5
docs/meli.conf.5: small fixes & additions 2020-10-16 22:28:00 +03:00
Manos Pitsidianakis 3b97e66c10
docs/meli.conf.5: add progress_spinner_sequence doc 2020-10-16 15:47:00 +03:00
Manos Pitsidianakis ddfec3e207
listing: fix menu draw artifact 2020-10-16 15:46:21 +03:00
Manos Pitsidianakis a702a04043
melib/attachments: add SMIME signature variant 2020-10-16 12:47:16 +03:00
Manos Pitsidianakis 6264ee011f
terminal/embed: remove unwraps from kill() calls
If child process has exited, this will panic.
2020-10-16 12:41:21 +03:00
Manos Pitsidianakis 5acd7dfe1c
mail/view: prevent spurious redraw in special modes 2020-10-16 12:40:02 +03:00
Manos Pitsidianakis 8090d614e2
conf/pager: remove unused max_width option 2020-10-16 12:37:46 +03:00
Manos Pitsidianakis 3949cecb75
mail/composer: add scrollbars 2020-10-16 12:37:01 +03:00
Manos Pitsidianakis 1e7b40e6b3
utilities: move pager to its own module 2020-10-15 22:44:15 +03:00
Manos Pitsidianakis d8d66641e2
utilities/widgets: only advance stage by timer in ProgressSpinner 2020-10-15 21:45:12 +03:00
Manos Pitsidianakis 393c5d0d53
state: cull redraws of floating notifications
Cull redraws by keeping track of whether the floating box has been
initialised and whether its area has been drawn over by other dirty areas.
2020-10-15 21:28:28 +03:00
Manos Pitsidianakis 4c1a9b2485
Fix minor warnings 2020-10-15 19:01:42 +03:00
Manos Pitsidianakis 03a1d5a985
listing: Update status on all update events
Envelope counter totals might remain stale in the bottom status bar.
2020-10-15 19:00:37 +03:00
Manos Pitsidianakis 279c288a22
Alter enter_command_mode shortcut to `:`
Just like vi.
2020-10-14 20:21:22 +03:00
Manos Pitsidianakis e4cddbad25
mail/view: send NewJob event on new jobs
NewJob event wasn't sent so the message loading jobs were not accounted
in the busy spinner animation
2020-10-14 20:16:54 +03:00
Manos Pitsidianakis 67f50d95f4
Add quit command 2020-10-14 20:14:07 +03:00
Manos Pitsidianakis 0c68807814
Add export-mail command 2020-10-14 20:13:15 +03:00
Manos Pitsidianakis 4e72b6552a
conf: add setting for progress spinner
Choose between 30-something built in sequences (integers between 0-30)
or define your own list of strings for the progress spinner animation.

Default: 0
2020-10-14 20:07:39 +03:00
Manos Pitsidianakis 310d02042f
Rename toggle_thread_snooze to "toggle thread_snooze"
For consistency with other toggle commands.
2020-10-14 14:58:02 +03:00
Manos Pitsidianakis 188e020bd1
Add opt-in mouse support
Sidebar width can be resized with mouse hold and drag.
2020-10-14 14:58:02 +03:00
Manos Pitsidianakis 20840625d6
melib/gpgme: trim header file 2020-10-14 01:03:57 +03:00
Manos Pitsidianakis d51d0187a6
melib/imap: change byte cache String -> Vec<u8> 2020-10-13 21:46:03 +03:00
Manos Pitsidianakis 2944fc992b
melib/imap/untagged: handle EXPUNGE if our MSNs invalid 2020-10-13 21:18:26 +03:00
Manos Pitsidianakis 535d04f4f0
melib/imap/untagged: lower mbox count on EXPUNGE events 2020-10-13 21:17:27 +03:00
Manos Pitsidianakis 6f31388b27
compose: add EditAttachments menu 2020-10-13 17:17:57 +03:00
Manos Pitsidianakis 5337a54d96
compose: move gpg mod to its own file 2020-10-13 17:06:30 +03:00
Manos Pitsidianakis b343530f0c
widgets: add button type parameter to FormWidget 2020-10-13 17:04:40 +03:00
Manos Pitsidianakis cd68008e67
melib: Implement delete_messages for IMAP, Maildir 2020-10-13 13:57:04 +03:00
Manos Pitsidianakis 19891a3042
Cargo.toml: set codegen-units = 1 2020-10-11 18:11:04 +03:00
Manos Pitsidianakis 9ce62c735a
compose: add key selection state for gpg operations
Closes #81
2020-10-11 18:11:04 +03:00
Manos Pitsidianakis 39fab67523
compose: use melib::Bytes pretty print for attachment size 2020-10-11 16:53:05 +03:00
Manos Pitsidianakis 0ca7b0042e
utilities: ensure Form/Button widgets are not always non-dirty 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 406af1848f
compose: add `add-attachment-file-picker` command 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis a4b78532b7
Refactor job structs into JoinHandle
Put oneshot::channel<R> into JoinHandle<R>
2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 4dd8474c30
gpgme: add PartialEq impl for Key 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 0dd9e6a34b
compose: kill selectors on ComponentKill 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis eb1cb5cec6
compose: expand cursor reach to attachment area 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis e42c9281fd
Fix input events going to hidden components 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis bc74379b27
mailview: don't process_event if coordinates uninitialised 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis be45b0c02d
compose: add encrypt layer 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 3ec1ecb349
Add import mail action 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis afe7eed9ef
melib/compose: don't base64 encode unless it's not ascii 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 59e60f8d28
gpgme: add context flag set/get 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis a2f11c341d
compose: add async draft filter stack in sending mail
Add a stack of "filter" closures that edit a draft before sending it.
Add PGP signing filter. An encryption filter will be added in a future
commit.
2020-10-11 16:53:04 +03:00
Manos Pitsidianakis afee1e2be5
melib/compose: fix wrong Content-Type on PGP signatures and message/rfc822 2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 08df7f39b2
Add toggle encrypt action in composer
Does nothing for now, will be used in a future commit.
2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 5d968b7c40
imap: fix out of bounds panic on receive EXPUNGE
Closes #82
2020-10-11 16:53:04 +03:00
Manos Pitsidianakis 347b54e0f7
segment_tree: get_max() return 0 if tree empty 2020-10-05 21:10:00 +03:00
Manos Pitsidianakis 74f31875b8
listing: fix menu gaining focus if not visible 2020-10-05 21:10:00 +03:00
Manos Pitsidianakis 23ca41e3e8
add libgpgme feature 2020-10-05 21:10:00 +03:00
Manos Pitsidianakis b9c07bacef
melib: decode text inline message/rfc822 attachments 2020-09-27 20:57:42 +03:00
Manos Pitsidianakis 87443f156f
docs/meli.1: add copyto, moveto, delete commands 2020-09-26 18:18:24 +03:00
Manos Pitsidianakis b0e50a29bd
melib/list_management: don't ignore "NO" in List-Post 2020-09-25 13:45:48 +03:00
Manos Pitsidianakis 1ddde400ee
debian/: bump version to 0.6.2 2020-09-24 18:15:46 +03:00
Manos Pitsidianakis 6ccb4e9544
melib: bump version to 0.6.2 2020-09-24 17:13:07 +03:00
Manos Pitsidianakis e407b1e224
melib: add README.md and email module doco 2020-09-24 16:54:06 +03:00
Manos Pitsidianakis a1e3f269de
melib/imap: don't manually check for mailbox permissions 2020-09-24 12:17:32 +03:00
Manos Pitsidianakis e556191bab
melib/imap: hide LOGIN from debug log 2020-09-24 12:16:50 +03:00
Manos Pitsidianakis ce559b05d7
melib/imap: EXAMINE instead of SELECT in IDLE connection 2020-09-24 12:15:00 +03:00
Manos Pitsidianakis 36cc0d4212
melib/jmap: implement refresh()
Closes #77
2020-09-23 10:52:19 +03:00
Manos Pitsidianakis 425f4b9930
melib/jmap: add Type parameter to Id, State
Make Id, State have a type parameter to the object it refers to (eg
`Id<EmailObject>`) instead of just a String
2020-09-23 10:52:19 +03:00
Manos Pitsidianakis 19d4a191d8
melib/jmap: add email state sync 2020-09-21 16:17:37 +03:00
Manos Pitsidianakis 20dd4cfaf6
Makefile: fix error with manpage path 2020-09-20 23:10:46 +03:00
Manos Pitsidianakis 4cf0b9ffec
melib/jmap: impl copy_messages()
Closes #76
2020-09-20 15:00:03 +03:00
Manos Pitsidianakis 559de5e140
Add docs/ folder 2020-09-20 15:00:03 +03:00
Manos Pitsidianakis baa44109f2
melib/thread: "merge" duplicate messages in threads 2020-09-20 15:00:03 +03:00
Manos Pitsidianakis 28deba708c
melib/imap: check if FETCH reply was intended for us
After sending a FETCH, the command results might be mixed with
unsolicited FETCH replies. Check if that happens.
2020-09-20 15:00:03 +03:00
Manos Pitsidianakis a187cee1d3
plugins: place socket in XDG_RUNTIME_DIR, not CWD
Closes #78
2020-09-20 13:31:18 +03:00
Manos Pitsidianakis ea0fb114e1
melib/imap: delete reverse_modseq storage
Modsequences are not unique, and many messages may share the same
modsequence. So storing a reverse mapping of modsequences to messages is
invalid.
2020-09-20 13:29:57 +03:00
Manos Pitsidianakis 8e036f045c
melib/imap: accept literal astrings in bodystructure 2020-09-19 22:54:11 +03:00
Manos Pitsidianakis 3210ee5c67
melib/jmap: impl save() message
Closes #60
2020-09-19 20:44:39 +03:00
Manos Pitsidianakis cfc380b47d
melib/jmap: allow empty to,from etc fields in EmailObject 2020-09-19 14:59:23 +03:00
Manos Pitsidianakis fba69d1e5d
SearchBackend: add Auto variant as default 2020-09-18 21:38:50 +03:00
Manos Pitsidianakis 7dfa6c0639
view/thread: use reverse colors in cursor in case of NO_COLOR 2020-09-18 21:28:41 +03:00
Manos Pitsidianakis 82cd690005
sqlite3: only update when SearchBackend is sqlite3 2020-09-18 21:06:34 +03:00
Manos Pitsidianakis 8eb78ae01b
sidebar: compute mailbox tree only for subscribed mailboxes 2020-09-18 21:06:33 +03:00
Manos Pitsidianakis 05e4dbcd5a
melib: update smol to 1.0.0 2020-09-18 21:06:33 +03:00
Manos Pitsidianakis 40b63cc3e0
melib/imap: fix unseen count on cache sync 2020-09-18 12:21:05 +03:00
Manos Pitsidianakis 38eff71971
IMAP: don't show \Recent flag as tag
Closes #74
2020-09-18 12:12:14 +03:00
Manos Pitsidianakis 3004789f32
melib/imap: FETCH comma-sep list on untagged Recent response
FETCHing RECENT messages when receiving an untagged RECENT response from
the server didn't separate the message numbers with comma but with
space, which is invalid.
2020-09-18 12:10:44 +03:00
Manos Pitsidianakis 9bafba3905
melib/imap: don't print raw bytes in debug prints 2020-09-18 12:08:56 +03:00
Manos Pitsidianakis 98949a4a72
melib/imap: expand special mailbox detection cases 2020-09-18 12:08:02 +03:00
Manos Pitsidianakis fbf2b7dc7b
sidebar: add customizable mailbox tree
Concerns #72
2020-09-17 16:49:19 +03:00
Manos Pitsidianakis 10a3430233
melib/line_break: fix panics from Unicode13 linebreak test cases 2020-09-17 02:59:51 +03:00
Manos Pitsidianakis 83bee279e6
melib/email/compose: set attachment status
Set Content-Disposition: attachment to, well, attachments.
2020-09-16 19:57:06 +03:00
Manos Pitsidianakis e8f3b6aa24
melib/imap: check for max uid == 0 when resyncing 2020-09-16 19:46:11 +03:00
Manos Pitsidianakis 64a2af3777
melib/email: smarter attachment detection
Look for Content-Disposition: attachment to detect attachments
2020-09-16 18:14:25 +03:00
Manos Pitsidianakis e518b3f16d
melib/imap: use SystemTime for IMAP server timeout 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis d862e7bf53
statustab: don't process scrolling events if account is open 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 005c879a12
accounts: remove job timeout 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 8a8c790f8c
accounts: fix blocking jobs not spawning on blocking workers 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis e60eb23f4d
statustab: show active jobs 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 92b25de34e
melib/EnvelopeHashBatch: impl len method 2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 096c2970b3
melib/email/parser: impl RFC6532
RFC6532 International Mail Headers
2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 3618bdcffb
melib/imap: treat server input as bytes
Server input was assumed valid ascii and converted haphazardly to &str.
Don't do that, since it might not be valid UTF8.
2020-09-16 15:17:48 +03:00
Manos Pitsidianakis 366e557e1c
melib/email: don't do case sensitive eq for mime parameters 2020-09-16 13:11:29 +03:00
Manos Pitsidianakis 9b0180fdbc
melib/email/parser: impl RFC5322 parser for dates 2020-09-16 13:11:28 +03:00
Manos Pitsidianakis 07742ec053
utilities: ensure command suggestions are LIFO 2020-09-16 13:11:28 +03:00
Manos Pitsidianakis f83df69d2f
utilities/widgets: ensure ProgressSpinner is cleaned up 2020-09-16 13:11:28 +03:00
Manos Pitsidianakis 0e2641f7ed
melib/imap: always retry connection in watch() 2020-09-16 13:11:28 +03:00
Manos Pitsidianakis 67c722958b
melib/email/parser: quoted-printable accept message ending with soft line break 2020-09-15 10:17:56 +03:00
Manos Pitsidianakis a5b6f29f2b
melib/imap: ensure connection is alive before fetching bytes/flags 2020-09-15 02:00:27 +03:00
Manos Pitsidianakis 3b10fa3895
melib/imap: set 9min tcp keepalive on connection 2020-09-15 01:59:28 +03:00
Manos Pitsidianakis 42c4c61518
melib/connections: impl tcp keepalive 2020-09-15 01:17:32 +03:00
Manos Pitsidianakis dee62cc118
melib/imap: fix NoSelect mailboxes not showing up as subscribed 2020-09-14 19:45:28 +03:00
Manos Pitsidianakis 17a4ccdcbc
melib/imap: perform reconnect on IDLE failure 2020-09-14 19:32:43 +03:00
Manos Pitsidianakis 670675edcc
melib/imap: impl LIST-EXTENDED
Closes #69
2020-09-13 17:40:26 +03:00
Manos Pitsidianakis 315af9bc05
shortcut!: prevent panic if shortcut key $section is missing 2020-09-13 16:42:26 +03:00
Manos Pitsidianakis f6d5c968ea
Update dependencies (cargo update) 2020-09-13 16:34:07 +03:00
Manos Pitsidianakis fadf20d7b1
NotificationType: add melib::ErrorKind 2020-09-13 15:23:14 +03:00
Manos Pitsidianakis 352f7505fc
melib/imap: don't poll \Noselect mailboxes for updates 2020-09-13 00:24:26 +03:00
Manos Pitsidianakis 46e3bb8074
conf/accounts: call is_online if Refresh job fails 2020-09-13 00:03:12 +03:00
Manos Pitsidianakis 281a6ee6ae
Makefile: add build-rustdoc target 2020-09-12 23:50:40 +03:00
Manos Pitsidianakis 3ef60f2688
jobs: add module doco 2020-09-12 23:43:10 +03:00
Manos Pitsidianakis c9a06b9b5c
mail/view: unset self.dirty early on draw 2020-09-12 23:39:07 +03:00
Manos Pitsidianakis 776918f586
samples/themes: update orca.toml 2020-09-12 23:36:59 +03:00
Manos Pitsidianakis 51db5b6c2f
listing/conversations: redraw selection undo on Esc 2020-09-12 23:08:09 +03:00
Manos Pitsidianakis 14de776314
listing/plain: add row_attr! macro 2020-09-12 23:05:58 +03:00
Manos Pitsidianakis 20b02ffd4f
Lookup tag color/ignore settings in all three setting levels
There are three setting levels for tag settings:

- per mailbox override    ^
- per account override    |
- global setting          |
                        depth

So lookup in each of them in this order for configuration, not just the
deepest level.
2020-09-12 23:02:06 +03:00
Manos Pitsidianakis 06a58a70bd
melib/imap: introduce a conf flag for server timeout
timeout integer                       (optional) Timeout to use for server connections in seconds.  A timeout of 0 seconds means there's no timeout.  (16)
2020-09-12 22:07:42 +03:00
Manos Pitsidianakis 96985c9c1f
melib/imap: set conn to Err if watch returns Err 2020-09-12 21:34:34 +03:00
Manos Pitsidianakis 7c6e3658c7
melib/imap: try NOOPing in connect() 2020-09-12 21:33:25 +03:00
Manos Pitsidianakis 5079881a4c
melib/imap: add tags to tag_index when setting new tags 2020-09-12 21:32:19 +03:00
Manos Pitsidianakis 6d9cdce923
melib/imap: don't fail utterly if cache fails on fetch
Show notice to user, and then try a fresh fetch. Also try resetting the
cache if possible.
2020-09-12 21:29:09 +03:00
Manos Pitsidianakis 7b324359c5
melib/imap: ignore case for supported capability report in
MailBackendExtensionStatus
2020-09-12 21:22:17 +03:00
Manos Pitsidianakis 41664bbe91
Don't panic if no dbus notification server is available 2020-09-12 21:06:50 +03:00
Manos Pitsidianakis 4829e13c47
melib/maildir: impl copy_messages for Maildir 2020-09-11 17:02:27 +03:00
Manos Pitsidianakis a1585d4006
components/listing: draw rows select status at all times 2020-09-11 17:02:27 +03:00
Manos Pitsidianakis ed27ed604c
listing: select multiple messages with a motion
- Press a number (movement multiplier)
- Press "select_entry" shortcut (default: v)
- Press a movement (arrow keys, PageUp/Down, Home/End)
- Resulting selection will be symmetric difference of previous selection
plus all the entries traversed with movement
2020-09-11 17:02:27 +03:00
Manos Pitsidianakis 9e20f6556a
melib/imap: refactor command generation on copy_messages 2020-09-11 17:02:27 +03:00
Manos Pitsidianakis d00055fdb1
melib/imap: update online instant only on server read IO 2020-09-11 17:02:27 +03:00
Manos Pitsidianakis 1751509739
melib/imap: prevent false IDLE wakeups
Prevent IDLE loop waking up when receiving continuation "+ " lines
2020-09-11 17:02:27 +03:00
Manos Pitsidianakis 5cd03fff0f
melib/email/parser: add mailing list parser module
Specifically, rfc2369 list header action list
2020-09-11 00:08:56 +03:00
Manos Pitsidianakis 927a0c3cc0
melib/imap: prevent panic in untagged fetch response 2020-09-11 00:06:32 +03:00
Manos Pitsidianakis bda5bd963a
mail/view: cache message body/text in MailView state 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis 1fe873887f
components/utilities: keep track of finished jobs
Keep track of finished jobs in case we get a job notification more than
once.
2020-09-10 21:19:38 +03:00
Manos Pitsidianakis f05dd379ae
Send NewJob event on all job startups 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis 65357625ea
conf: impl DotAddressable for NotificationsSettings 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis 1ac3a7a903
Make dbus dependency optional
Put dbus dependency behing `dbus-notifications` feature.
2020-09-10 21:19:38 +03:00
Manos Pitsidianakis faa12a2d41
melib/email/address: add contains_address,subaddress methods 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis c0c588be9c
melib/maildir: add message flag initialize in bytes
Maildir flags from filesystem path was not set correctly on Envelope
initialization in maildir backend
2020-09-10 21:19:38 +03:00
Manos Pitsidianakis be57b65dae
melib/email: add flags arg to Mail::new 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis d57dd9c98e
melib/email/address: return Option in get_display_name 2020-09-10 21:19:38 +03:00
Manos Pitsidianakis c6c0da7fcb
melib: cleanup commit
Cleanup melib module exports, add some document tests, change some
documentation.
2020-09-10 21:19:38 +03:00
Manos Pitsidianakis d14f26569e
melib/email/parser: Add rfc5322 compliant parser for MessageID 2020-09-10 20:36:25 +03:00
Manos Pitsidianakis 5d107db8b8
melib/email/parser: add new RFC5322 compliant parsers for header bodies 2020-09-10 20:36:25 +03:00
Manos Pitsidianakis 0de39cb658
melib/email/address: add constructors, and fix debug print 2020-09-10 20:36:25 +03:00
Manos Pitsidianakis 46c44ced96
line_break: check of eof in LB13 2020-09-10 20:36:25 +03:00
Manos Pitsidianakis f8f3f1817d
melib/notmuch: fix search
Search was not available, it had been left out of date
2020-08-28 14:27:46 +03:00
Manos Pitsidianakis b4fe34eacf
melib/imap: add ImapCache trait 2020-08-28 00:31:35 +03:00
Manos Pitsidianakis e878c50af5
tools/imapshell: actually send LOGOUT instead of just closing socket 2020-08-28 00:16:37 +03:00
Manos Pitsidianakis 8f46c4ebe7
Small fixes 2020-08-27 17:29:27 +03:00
Manos Pitsidianakis b94342c52b
themes/regexp: fix unwrap check on regexp match byte offsets 2020-08-27 17:27:45 +03:00
Manos Pitsidianakis 75f59ee726
melib/imap: split by lines when reading IDLE unsolicited responses 2020-08-27 17:26:39 +03:00
Manos Pitsidianakis be2d268a20
melib/imap: build uid<>msn cache in {select,examine}_mailbox() 2020-08-27 17:26:07 +03:00
Manos Pitsidianakis 209bd98814
melib/imap: fix cache not being updated in some events 2020-08-27 17:25:05 +03:00
Manos Pitsidianakis 6302d9d618
Rename testing crate to tools, and add README 2020-08-27 17:18:58 +03:00
Manos Pitsidianakis a37faf0bec
Fix imapconn IMAP shell binary
IMAP shell hasn't been working since updating IMAP to async. Now it
works by using block_on executor.
2020-08-27 17:07:19 +03:00
Manos Pitsidianakis e9a80b32ac
melib/imap: small cleanups 2020-08-26 20:08:44 +03:00
Manos Pitsidianakis f02dde46da
melib/error:Add ErrorKind::Timeout
Timeout errors lead to automatic restart of connections without
bothering the user about the details, compared to actual network errors.
2020-08-26 20:01:39 +03:00
Manos Pitsidianakis 25b325dbda
Keep bytes copy in SaveMessage job in case of failure 2020-08-26 20:00:25 +03:00
Manos Pitsidianakis ca0f37e1f3
Send AccountStatusChange event on receiving mailboxes 2020-08-26 19:59:27 +03:00
Manos Pitsidianakis 843616221e
Add logging level to Generic jobs
Not every job success should be shown to the user, for example updating
the sqlite3 database. So introduce a level to only show relevant
notifications.
2020-08-26 19:17:54 +03:00
Manos Pitsidianakis c6f11fb592
melib: update notify to 4.0.15 2020-08-26 19:17:54 +03:00
Manos Pitsidianakis e349882ea7
melib/email/parser: use SmallVec in encoded words 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 14663e46b9
Remove some old TODO comments 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 4217839155
melib/email: remove Envelope::from_token 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 9e9be0b5f3
Remove block_on from mailbox creation/deletion 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 1df25f36ef
melib/email: case insensitive match on charset from bytes 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 96a3da3d7b
melib/imap: fix deflate feature flags 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis f7ac1703e8
melib/notmuch: add watch/refresh methods to backend 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 974836776d
melib/email: trim raw input for some fields 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis b545a0b905
Show error if watch job fails 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 341ff9164b
melib/notmuch: add Message,TagIterator,Thread types 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 8c6c9806b5
Fix some clippy lints 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis fc25c7b165
Fix compiler warnings 2020-08-26 00:54:07 +03:00
Manos Pitsidianakis 629997397f
Allow toggle_help (default ?) remapping 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 53e924eb33
Add edit envelope action back as async 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis f7c9f21575
melib/imap: add CONDSTORE support
Closes #52
2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 1ca0bd0d96
sqlite3: add schema versioning
To potentially be used with automatic migrations on version update
2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 8d50e83a33
melib/email: add case-insensitive Header struct
- HeaderName is either 32 or less inlined bytes or heap-allocated vec for more than that.
- Equality and hashing is case-insensitive
- A HeaderMap is a hashmap from HeaderName to Strings that can be
indexed with &str, case insensitive. Insertion order is also preserved
2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 0f3bf858a3
melib/imap: impl UNSELECT via nonexistent mailbox 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 876e1bc510
melib/imap: turn ImapResponse From to TryFrom 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 94433cfc40
melib/backends: cleanup MailBackend trait definition 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 3eadaba34e
Replace old pseudo-async code with blocking rust async 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis a190805384
melib/backends: Add BackendEvent enum 2020-08-26 00:54:06 +03:00
Manos Pitsidianakis 9928ee78e7
Add Reply{ToAuthor,ToAll} actions
- previous Reply action now lets you select recipients by default
- ReplyToAuthor selects the Envelope author as recipient
- ReplyToAll selects all addresses
2020-08-26 00:54:05 +03:00
Manos Pitsidianakis d95aae1987
terminal/keys: add `Space` identifier in Key Display impl 2020-08-26 00:54:05 +03:00
Manos Pitsidianakis 9afbdd4887
Add insert_user_agent option in composing
Add option for automatically inserting a 'User-Agent' header in new
drafts.
2020-08-26 00:54:05 +03:00
Manos Pitsidianakis be31d35ff6
melib/line_break: fix missing Break on B2 class
Graphemes of B2 class, such as the Em dash can break before and after.
However this case wasn't handled in the line break iterator.
2020-08-26 00:54:05 +03:00
Manos Pitsidianakis bb4754e38a
themes/shortcuts: preserve order of keys 2020-08-26 00:54:05 +03:00
Manos Pitsidianakis 8a6bf3b217
Preserve Account order from configuration file
Use IndexMap to preserve the order of accounts in the UI from the
account definitions.
2020-08-26 00:54:05 +03:00
Manos Pitsidianakis dede8d2a9e
melib/imap: timeout when establishing connection 2020-08-16 19:57:28 +03:00
Manos Pitsidianakis 0b00f5dfbc
Update toml to 0.5.6, add preserve_order 2020-08-16 15:38:37 +03:00
Manos Pitsidianakis d1a9f4e28a
melib/collection: remove unnecessary mut references 2020-08-16 15:38:11 +03:00
Manos Pitsidianakis b9e53a7451
melib/smtp: add recipient argument in mail_transaction() 2020-08-16 15:16:27 +03:00
Manos Pitsidianakis 30c390443a
melib: Add native_tls behind feature
native_tls error conversion was held behind `imap_backend` feature, but
tls is also used in smtp.
2020-08-15 13:42:30 +03:00
Manos Pitsidianakis 1affee183a
melib/nntp: fetch all articles of group 2020-08-09 21:23:13 +03:00
Manos Pitsidianakis 92a9127758
melib/notmuch: don't read messages to String 2020-08-09 20:29:55 +03:00
Manos Pitsidianakis 79b2b38e32
melib: add supports_submission backend capability
To be used by NNTP, JMAP and some IMAP servers with BURL capability
2020-08-09 14:56:34 +03:00
Manos Pitsidianakis 560f9e5399
melib/email: parse empty attachments correctly 2020-08-09 09:50:20 +03:00
Manos Pitsidianakis c0f8bc1aed
melib/email/attachments: add Content-Disposition 2020-08-09 09:49:32 +03:00
Manos Pitsidianakis b2c14abd6e
melib/jmap: add {flag,tag} set support
Closes #61
2020-08-09 09:47:01 +03:00
Manos Pitsidianakis d413be02cd
Update sample-config.toml
Remove unknown options since they trigger an error now, and double #
comments
2020-08-07 13:54:29 +03:00
Manos Pitsidianakis a712bf6c3c
melib/jmap: make backend async
Replace reqwest with isahc which supports async IO
2020-08-07 13:51:44 +03:00
Manos Pitsidianakis fe4dae12df
listing/*: show MailboxEntry::status() when length is 0
Show the MailboxEntry::status() string when self.length == 0, instead of
"MAILBOX is empty".
2020-08-07 00:39:17 +03:00
Manos Pitsidianakis 6d61d0651c
melib/jmap: add special keywords to search 2020-08-06 21:13:20 +03:00
Manos Pitsidianakis c88eac1cc5
melib/jmap: implement search
Closes #59
2020-08-06 19:46:46 +03:00
Manos Pitsidianakis 52bcecfd4a
conf.rs: reject unknown configuration options
Closes #11
2020-08-03 22:53:06 +03:00
Manos Pitsidianakis 750e32c8e1
mail/listing: use mailbox count() total instead of loaded total 2020-08-02 16:52:19 +03:00
Manos Pitsidianakis 5db749c258
terminal/cells.rs: fix resize to grow actually making the grid smaller 2020-08-02 16:52:19 +03:00
Manos Pitsidianakis 5485e7b941
melib/notmuch: fetch mail in chunks
notmuch fetch took too much time on large mailboxes because it sent the
result as one big vec, instead of chunking it.
2020-08-02 16:52:19 +03:00
Manos Pitsidianakis e8a98f87e3
Change version to 0.6.1 2020-08-02 01:25:06 +03:00
Manos Pitsidianakis fb523c140a
terminal/cells: resize growable grid when exactly at bounds 2020-08-02 00:49:59 +03:00
Manos Pitsidianakis 890000bd0e
status page: trim extension name at 30 chars
NNTP has some long protocol extension names
2020-08-02 00:48:44 +03:00
Manos Pitsidianakis c5d0a6c3b6
conf/accounts.rs: don't retry connect on auth error 2020-08-02 00:46:37 +03:00
Manos Pitsidianakis 1bdecd62c7
melib/nntp: add AUTH support 2020-08-02 00:44:45 +03:00
Manos Pitsidianakis ce45cf5f17
melib/{imap,nntp}: flush after write_all
IMAP IDLE got stuck, because the IDLE connection used `send_raw` that
didn't flush output after `write_all`, *if* DEFLATE was on. DEFLATE
needs to flush output.
2020-08-02 00:22:15 +03:00
Manos Pitsidianakis ec0153e7b2
melib: add protocol extension info in MailBackendCapabilities 2020-08-02 00:22:15 +03:00
Manos Pitsidianakis 2b3949ddb2
melib: add missing cfg attribute for NNTP 2020-08-02 00:22:15 +03:00
Manos Pitsidianakis 522f667350
melib: add experimental NNTP backend
Closes #54
2020-07-30 20:58:53 +03:00
Manos Pitsidianakis 7b686ff38c
Fix README in Cargo.toml 2020-07-29 21:51:58 +03:00
Manos Pitsidianakis 93d9c195cc
Change version to 0.6.0 2020-07-29 20:17:59 +03:00
Manos Pitsidianakis 3ac2c12e7a
Small fixes 2020-07-29 14:33:09 +03:00
Manos Pitsidianakis 44fdc0765e
conf/accounts.rs: add 30s job timeout 2020-07-29 14:27:43 +03:00
Manos Pitsidianakis 5c038887db
melib/imap: add MOVE support 2020-07-29 01:19:08 +03:00
Manos Pitsidianakis 5ec7c59d8a
melib/threads: re-add to missing_message_ids on remove 2020-07-28 17:39:25 +03:00
Manos Pitsidianakis 9a29f4245f
melib/imap: add COMPRESS=DEFLATE support
Closes #53
2020-07-28 17:39:25 +03:00
Manos Pitsidianakis d8f2a08e7b
melib/smtp: add serde field default values 2020-07-27 15:06:57 +03:00
Manos Pitsidianakis 8ec0da4fbd
melib/imap: add conf toggle flags for IMAP extensions 2020-07-27 15:06:57 +03:00
Manos Pitsidianakis 7bbfd188ef
melib/imap: move current_mailbox to ImapStream
ImapStream holds the connection state (current command id), so it makes
sense to move current_mailbox state there. That way, when a connection
drops for whatever reason the old current_mailbox is dropped and not
carried over to new connections.
2020-07-27 15:06:56 +03:00
Manos Pitsidianakis 2db983ae1f
mail/view.rs: try restarting future if get bytes fails 2020-07-27 15:06:56 +03:00
Manos Pitsidianakis ce693904bf
samples/themes: add orca theme 2020-07-27 15:06:56 +03:00
Manos Pitsidianakis 32b4c30fee
melib/email.rs: use SmallVec for Address fields 2020-07-27 15:06:56 +03:00
Manos Pitsidianakis 52cec59215
melib/error: add From<&MeliError> for MeliError 2020-07-27 15:04:29 +03:00
Manos Pitsidianakis 3152411f22
Fix Makefile semantics
Makefile targets didn't correspond to the widely used ones:

- make should build meli instead of showing help
- make check should run tests

Closes #42
2020-07-26 16:09:41 +03:00
Manos Pitsidianakis 70a4409e59
mail/listing*: various theme color fixes 2020-07-26 16:09:41 +03:00
Manos Pitsidianakis 74673880e6
command.rs: add eof() parser to action parsers 2020-07-26 16:09:41 +03:00
Manos Pitsidianakis cc119c19b0
melib/maildir: send NewFlags events 2020-07-26 16:09:41 +03:00
Manos Pitsidianakis 031e81ac8f
imap: add UntaggedResponse::UIDFetch 2020-07-26 16:09:41 +03:00
Manos Pitsidianakis f41a1ffe3a
imap: remove FLAGS.SILENT from STOREs
Flag updates were not received, because FLAGS.SILENT was used.
2020-07-26 16:09:41 +03:00
Manos Pitsidianakis 26b327d86a
mail/listing*: clear selection after perform_action() 2020-07-26 16:09:41 +03:00
Manos Pitsidianakis b5530860d2
conf/DotAddressable: impls for more types 2020-07-26 16:09:35 +03:00
Manos Pitsidianakis 0d198dbb56
conf.rs: fix struct decl/impl order in file
Impls and type declarations were out of order
2020-07-26 15:38:11 +03:00
Manos Pitsidianakis 7fd511e149
conf/shortcuts.rs: implement DotAddressable for Shortcuts 2020-07-26 15:38:11 +03:00
Manos Pitsidianakis 1cc1b0604c
conf/accounts.rs: use QueryTrait when search_backend is None 2020-07-26 15:38:08 +03:00
Manos Pitsidianakis 3f8aa560f0
melib/MailBackend: add MailBackendCapabilities struct 2020-07-25 17:53:04 +03:00
Manos Pitsidianakis 4aaa784d8f
Fix panic on empty command history when browsing history 2020-07-25 16:34:53 +03:00
Manos Pitsidianakis 8b90c7fcb6
conf/shortcuts: add shortcut for COMMAND mode
Replace hardcoded Key value with customisable shortcut
"general.enter_command_mode"
2020-07-25 15:19:53 +03:00
Manos Pitsidianakis c2550f60b6
Rename EXECUTE mode to COMMAND
vim uses COMMAND, and we want to be consistent with vim when possible.
2020-07-25 15:19:53 +03:00
Manos Pitsidianakis b20bdea8f0
EXECUTE: cancel command with Esc 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 989cfcc877
conf/accounts.rs: use mailbox alias if available in MailboxEntry::name() 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 7744ef1462
conf/accounts.rs: make JobRequest::Generic name Cow<'_, str> 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis d6ef3567f4
conf/accounts.rs: add hash() method 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 688060ceb6
conf/accounts.rs: always load Inbox 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis ed3b2fa6c8
types.rs: add JobCanceled event 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 5a5408ecd5
imap: small fixes 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 00acba7717
melib/MailBackend: add copy_messages,set_flags,delete_messages methods 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis a049a83fe3
conf/accounts: add insert_job() method 2020-07-25 15:19:53 +03:00
Manos Pitsidianakis 246ac4b84a
Update smallvec dependency to 1.4.1 2020-07-25 15:19:52 +03:00
Manos Pitsidianakis 1b8529c59c
melib/imap: use LITERAL+ with APPEND
Closes #50
2020-07-25 15:17:35 +03:00
Manos Pitsidianakis f9efaea0ec
ConversationsListing: fix invalid update_line colors 2020-07-25 15:17:35 +03:00
Manos Pitsidianakis 99fbac3806
Remove unused variables/functions 2020-07-23 13:39:58 +03:00
Manos Pitsidianakis 0ee3a0bf79
imap: clear mesage totals when fetching entire mailbox
Totals might have been set after a STATUS response, meaning we know the
totals without knowing exactly what message UIDs are there. Clear the
totals, and start inserting UIDs instead.
2020-07-23 13:23:24 +03:00
Manos Pitsidianakis 6121f77853
imap: support LIST-STATUS 2020-07-23 13:23:24 +03:00
Manos Pitsidianakis 350c8033b1
imap: use ImapLineIterator in imap_mailboxes() 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis e49c293b01
imap: impl DoubleEndedIterator for ImapLineIterator 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis b9343dfb32
imap: update supported capabilities 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis 1bd89b3c96
themes: add mail.sidebar_account_name key 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis 44ffbe54e2
input_thread: add atomic refcount to check if thread is dead 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis 0882dbbad0
melib/Collection: put all fields behind a mutex 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis 1112ef4717
melib/Collection: remove unused fields 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis fadb3634e0
melib: take MailboxHash instead of &Mailbox in fetch*() 2020-07-23 13:23:23 +03:00
Manos Pitsidianakis 9103d05617
melib: s/get/fetch in MailBackend methods 2020-07-18 12:34:13 +03:00
Manos Pitsidianakis 0a7f283582
imap: prevent deadlock in watch::examine_updates
uid_store.mailboxes was locked before calling examine_updates, which
calls examine_mailbox() which also attempts to lock uid_store.mailboxes
2020-07-17 22:45:25 +03:00
Manos Pitsidianakis 996abd323f
Add print setting action
Add experimental print setting action. The command is of the form:

  print account_name listing.index_style

account_name is currently ignored.

The path, e.g. listing.index_style is split by "." and fed to
DotAddressable lookup trait method. The method checks the first segment
in the path if it matches any of the struct's fields, and then calls the
field's lookup method.
2020-07-17 13:33:40 +03:00
Manos Pitsidianakis c6c2865a54
melib/thread/iterators: remove recursion in favor of loops 2020-07-17 13:33:40 +03:00
Manos Pitsidianakis b4dadf20b6
ThreadListing: don't print previous link on root envelopes
If a thread root is missing (i.e. we never received that message or it
was deleted) threads rendered like this:

 ├─>Re: original subject
 ├─>Re: original subject
 └─>Re: original subject

This causes visual ambiguity if the parentless thread follows another:

 Another thread
 └─>Re: Another thread
 ├─>Re: original subject
 ├─>Re: original subject
 └─>Re: original subject

This commit removes the "previous link" from every first message in a group:

 ┬─>Re: original subject
 ├─>Re: original subject
 └─>Re: original subject
2020-07-17 13:33:40 +03:00
Manos Pitsidianakis 08d8c05a67
CompactListing: update self.rows{,_drawn} on row update
self.rows{,_drawn} were left unupdated, and stale envelope hashes could
result in panics
2020-07-17 00:04:59 +03:00
Manos Pitsidianakis 1bac926bdc
CompactListing: add row_attr macro
Add macro to calculate theme attribute for given thread row
2020-07-17 00:04:26 +03:00
Manos Pitsidianakis 5e1fa2d8d7
CompactListing: add select command
Select envelopes based on query
2020-07-17 00:03:35 +03:00
Manos Pitsidianakis 0d3fe288c5
sqlite3: make reindex operation async 2020-07-17 00:02:14 +03:00
Manos Pitsidianakis 32f196143e
melib: add supports_search() method to MailBackend 2020-07-17 00:02:02 +03:00
Manos Pitsidianakis 5ef62a39b8
conf: Rename cache_type to search_backend 2020-07-16 23:57:00 +03:00
Manos Pitsidianakis 017a45d5cd
conf/accounts: add JobRequest::Generic 2020-07-16 22:54:50 +03:00
Manos Pitsidianakis eb62463e7d
jobs: add spawn_blocking() method 2020-07-16 22:53:16 +03:00
Manos Pitsidianakis 1f9cdb8be5
conf/accounts: update mailbox status on payload delivery 2020-07-16 18:00:53 +03:00
Manos Pitsidianakis d3391e96c0
mbox: send envelope payload in chunks 2020-07-16 17:59:27 +03:00
Manos Pitsidianakis 15b15854bf
update documentation
Endless gratitude to WanderingBeekeper for editing the text.
2020-07-15 20:20:37 +03:00
Manos Pitsidianakis 587eaf7215
ThreadListing: add columns 2020-07-15 19:02:52 +03:00
Manos Pitsidianakis 349d2990c2
docs: add `send_mail` documentation 2020-07-15 15:37:00 +03:00
Manos Pitsidianakis 77dc1d74bf
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.

Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:

  [composing]
  send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }

For local smtp server:
  [composing]
  send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 15:24:01 +03:00
Manos Pitsidianakis ddafde7b37
jobs: save handle for each Job
If we save the JoinHandle for each task, we can cancel it in future
commits if we have to timeout network requests.
2020-07-15 15:22:33 +03:00
Manos Pitsidianakis 08c462801d
melib/mbox: fix not updating mailbox_index on new envelope 2020-07-15 15:22:33 +03:00
Manos Pitsidianakis e1c9967260
melib: Small documentation fixes for smtp, thread 2020-07-15 15:22:33 +03:00
Manos Pitsidianakis 4b27ae2b91
melib: Add experimental SMTP client 2020-07-15 15:22:33 +03:00
Manos Pitsidianakis 97c76cc6a1
melib/error: add ErrorKind struct 2020-07-13 21:36:55 +03:00
Manos Pitsidianakis c7bbf7ed7e
melib: move lookup_ipv4() to connection module 2020-07-13 21:36:55 +03:00
Manos Pitsidianakis 9db6b07b71
Remove some needless clones and stuff (thanks to Clippy) 2020-07-13 21:36:55 +03:00
Manos Pitsidianakis edfd2b1fef
conf.rs: accept default action "Y" when asking to create config
Reported by: bronsen
2020-07-10 15:55:15 +03:00
Manos Pitsidianakis d914f7afd9
MailView: send NewJob event on mail body request 2020-07-08 13:43:48 +03:00
Manos Pitsidianakis 899d497c9c
Rename _cmd options to _command for consistency 2020-07-08 12:12:15 +03:00
Manos Pitsidianakis 839d2f3d80
config_macros.rs: don't skip nonmatching attributes
config_macros.rs contains a macro that parses config structs and
generates a new "override" struct that contains the fields as Options.
The macro matches on each field's attributes and removes the serde
"default" attributes, since the override default is always None.
However, if an attribute contained a group of values and the first
wasn't `default` the attribute was skipped, so don't do that.
2020-07-08 12:10:14 +03:00
Manos Pitsidianakis bfc08f892d
Show account online error status in status tab 2020-07-08 12:10:14 +03:00
Manos Pitsidianakis 3a16dc6522
Show account online error status when offline 2020-07-08 12:10:14 +03:00
Manos Pitsidianakis 931863436d
imap: remove blocking imap backend, replace with async 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis 89dedbedb7
imap: launch async watch when connection comes online
Closes #38 Make async watch/refresh work in imap
2020-07-06 15:27:08 +03:00
Manos Pitsidianakis b5748c247a
MailBackend: remove connect() method 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis f48343ca89
conf/accounts: add is_{async,remote} fields 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis 231471fa8c
MailBackend: add is_{async,online} methods 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis 94e0aa4fe7
MailBackend: change get() ret type to Result<_> 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis a7e177586a
Fix clippy lints 2020-07-06 15:27:08 +03:00
Manos Pitsidianakis bbedeed3e3
More imap async fixes 2020-07-06 15:27:06 +03:00
Manos Pitsidianakis 391058a59c
BackendOp: add copy_to() method 2020-07-06 15:26:39 +03:00
Manos Pitsidianakis 5c204d3b69
rustfmt.toml: set edition = 2018 2020-07-06 15:26:39 +03:00
Manos Pitsidianakis b3876113aa
BackendOp: return future in as_bytes() 2020-07-06 15:26:39 +03:00
Manos Pitsidianakis 4721073bc3
Rename jobs1 to jobs 2020-07-06 15:26:39 +03:00
Manos Pitsidianakis 1ddde9ccba
BackendOp: change fetch_flags() retval to future 2020-07-06 15:26:35 +03:00
Manos Pitsidianakis ed3e66cedf
BackendOp: remove description() method 2020-07-06 15:26:03 +03:00
Manos Pitsidianakis e06308fed2
MailBackend: change more methods to Futures 2020-07-06 15:26:00 +03:00
Manos Pitsidianakis 03522c0298
melib: Fixup warnings in imap_async, maildir 2020-07-06 15:25:17 +03:00
Manos Pitsidianakis 6553d8ec44
imap_saync: fix max_uid invariant violation 2020-07-06 15:13:01 +03:00
Manos Pitsidianakis adb9061adc
imap_async: add force parameter to {examine,select}_mailbox() 2020-07-06 15:13:01 +03:00
Manos Pitsidianakis 21051fa862
JobRequest: add more variants 2020-07-06 15:13:01 +03:00
Manos Pitsidianakis 42419327f8
imap_async: add operations 2020-07-06 15:13:01 +03:00
Manos Pitsidianakis c82367e00d
BackendOp: Change set_{flag,tag} methods 2020-07-06 15:12:33 +03:00
Manos Pitsidianakis 8c1fc031e5
BackendOp: change fetch_flags retval to Result<Flag> 2020-07-06 15:12:11 +03:00
Manos Pitsidianakis ee10cdbcd5
Make get_async() return a Stream 2020-07-06 15:12:11 +03:00
Manos Pitsidianakis a38764f490
Add somewhat-working async IMAP backend 2020-07-06 15:12:05 +03:00
Manos Pitsidianakis b72a1ca6d8
WIP maildir async 2020-07-06 15:08:32 +03:00
Manos Pitsidianakis 4f3a98f90a
Add job executor 2020-07-06 15:07:44 +03:00
Manos Pitsidianakis de201b5d6c
imap: create message_sequence cache
Close #45 (hopefully)
2020-07-06 11:38:15 +03:00
Manos Pitsidianakis f8b84a192c
imap: add current_mailbox enum MailboxSelection
Add enum to track the currently selected Mailbox in the IMAP connection
2020-07-06 11:32:03 +03:00
Manos Pitsidianakis ca7bbd9de4
Fix pasted text not being registered immediately
Input thread reading from stdin should continue reading after receiving
the magic BRACKET START sequence until receiving the BRACKET END
sequence.
2020-06-26 21:12:57 +03:00
Manos Pitsidianakis 58aff83b95
Change "Draft saved" to "Message saved" 2020-06-26 21:12:57 +03:00
Manos Pitsidianakis c0c19268ee
Add ProgressSpinner widget 2020-06-26 21:12:57 +03:00
Manos Pitsidianakis 5e2576161a
meli.conf.5: update toml standard link 2020-06-26 21:12:57 +03:00
Manos Pitsidianakis def3997d6f
email/parser.rs: replace "FIXME" errors 2020-06-26 21:12:57 +03:00
Manos Pitsidianakis 91badc3960
imap: count message totals using HashSet
This way it's easy to know if a flag change in an envelope requires the
unseen total of a mailbox to change.
2020-06-26 21:12:56 +03:00
Manos Pitsidianakis c4bc7be5d1
Tabbed: correctly pass events to other children
When passing an event to the focused tab and it is not handled, the
other children weren't then each called to see if they handle the
event. That led to refresh events etc not being processed by the mail
list tab if it wasn't focused.
2020-06-23 20:11:05 +03:00
Manos Pitsidianakis 4ae7a57d45
Add save-draft command 2020-06-23 20:11:05 +03:00
Manos Pitsidianakis 64e5d4af4f
imap/untagged.rs: properly queue refresh events
RefreshEvents where added in self.uid_store.refresh_events queue though
ImapConnection has a method add_refresh_event() that drains the queue if
possible
2020-06-23 20:11:05 +03:00
Manos Pitsidianakis 2a0ad92374
imap: don't send CRLF twice when sending LITERAL
This results in BAD IMAP errors, as a CRLF results in an empty command.
2020-06-23 20:11:04 +03:00
Manos Pitsidianakis d7444a5b19
imap: recognize EXPUNGE events 2020-06-23 20:11:04 +03:00
Manos Pitsidianakis bfbaf3d617
Utilize EnvelopeRemove events
EnvelopeRemove events were not ever used in the UI
2020-06-23 20:11:04 +03:00
Manos Pitsidianakis efb06be09b
melib: return Result<_> from operation()
Envelope might have been deleted before main thread requests an
operation, which is a race condition.
2020-06-23 20:10:54 +03:00
Manos Pitsidianakis d827ea1001
imap/connection.rs: debug print NO/BAD responses 2020-06-23 17:31:25 +03:00
Manos Pitsidianakis fda947f8fb
imap.rs: fix two warnings 2020-06-23 17:31:25 +03:00
Manos Pitsidianakis b946b61cf1
terminal/cells.rs: remove unused variables 2020-06-23 17:31:25 +03:00
Manos Pitsidianakis 6f6f795fd5
imap: use uidnext for fetching all messages in get() 2020-06-23 12:37:27 +03:00
Manos Pitsidianakis c08ceae97c
imap: add status_response() parser 2020-06-23 12:36:42 +03:00
Manos Pitsidianakis c7835ccc13
imap: add mailbox_token() parser 2020-06-23 12:31:40 +03:00
Manos Pitsidianakis c2300e8ea0
imap: update is_online flag on successful read/write 2020-06-23 12:30:10 +03:00
Manos Pitsidianakis eca1921a8a
collection: add update_flags() method
On NewFlags events, the threads in Collection were not being updated, so
if an envelope's seen status was toggled the thread's unseen count was
  not updated, and thus not reflected in the UI even though the
  envelope's new flags event was registered properly.
2020-06-23 12:27:10 +03:00
Manos Pitsidianakis cac21a279b
melib: Remove dead dependencies 2020-06-22 19:20:38 +03:00
Manos Pitsidianakis a6a30f3adb
conf/accounts.rs return Result on init() 2020-06-22 17:32:51 +03:00
Manos Pitsidianakis 688a798fa2
XDGNotifications: increase rate limiting
3 notifications evenly spread per second did not make any sense.
Increase it to 1000 and see if it's ok
2020-06-22 17:31:18 +03:00
Manos Pitsidianakis 6bdd9b07bb
bin: remove unwrap from timer thread 2020-06-22 17:29:47 +03:00
Manos Pitsidianakis 01e1f4111c
imap: make hostname optional in ENVELOPE address parser 2020-06-22 17:27:48 +03:00
Manos Pitsidianakis 79b2e20557
imap: add message to Badcharset, Permanentflags responses 2020-06-22 17:26:20 +03:00
Manos Pitsidianakis 3703ae762e
imap: show reason for error on invalid uid fetch response 2020-06-22 17:25:49 +03:00
Manos Pitsidianakis 7d359624fe
imap: early return on empty mailbox in get() 2020-06-22 17:22:34 +03:00
Manos Pitsidianakis af4ad19169
imap: add chain_err_summary error descriptions 2020-06-22 17:21:46 +03:00
Manos Pitsidianakis ca11c8e474
Remove useless debug prints 2020-06-22 11:33:03 +03:00
Manos Pitsidianakis 34ed9e2014
conf: set mailbox autoload default to false 2020-06-22 11:31:43 +03:00
Manos Pitsidianakis 083732ed33
README.md: add explanations for features 2020-06-21 23:53:55 +03:00
Manos Pitsidianakis 9fb86ab2f2
components: create layouts module in utilities 2020-06-21 12:51:49 +03:00
Manos Pitsidianakis f8cef3290e
config_macros.rs: try rustfmt on generated module 2020-06-21 12:23:01 +03:00
Manos Pitsidianakis 0169025d50
build.rs: add proc-macro to generate Override structs for configuration 2020-06-20 23:58:53 +03:00
Manos Pitsidianakis 1db2c16f95
mbox: add support for multiple mbox mailboxes in config
Concerns #9
2020-06-20 14:49:02 +03:00
Manos Pitsidianakis 674073899d
mbox: Add different readers for mbox{o,rd,cl,cl2} 2020-06-20 13:14:40 +03:00
Manos Pitsidianakis 01d83d8088
email/parser: do not set has_colon newline
When parsing a field-name, and expecting a colon (:) if a newline is
first encountered do not set `has_colon` flag to true.
2020-06-20 13:14:40 +03:00
Manos Pitsidianakis 8bfdce6658
melib/error: do not discard old summary in set_summary 2020-06-20 13:14:40 +03:00
Manos Pitsidianakis 75f9256a50
email/parser: change Error type to include error location
Add ParsingError type that includes a string with the location and
possibly an explanation for the error.
2020-06-20 13:14:40 +03:00
Manos Pitsidianakis 02c881ac00
Add save-attachment option for entire message as eml 2020-06-15 01:07:50 +03:00
Manos Pitsidianakis d7e4bd9379
conf: set default override value to None 2020-06-13 12:48:15 +03:00
Manos Pitsidianakis cecd33eb5e
SVGScreenshotFilter: make svg smaller and fix grapheme cluster textLength inaccuracies 2020-06-13 01:15:24 +03:00
Manos Pitsidianakis 58ddfae9a7
execute.rs: fix missing space parsers 2020-06-12 01:46:21 +03:00
Manos Pitsidianakis fe655e679c
Fix rustfmt suggestions 2020-06-12 01:42:06 +03:00
Manos Pitsidianakis 0618e62ab6
Add optional feature to save SVG screenshot 2020-06-12 01:37:57 +03:00
Manos Pitsidianakis bc0189ffa1
Spawn workers on demand 2020-06-11 12:01:11 +03:00
Manos Pitsidianakis 40f66f3333
imap: modify connection timeouts 2020-06-11 12:00:07 +03:00
Manos Pitsidianakis 34d782f16f
imap: Remove panic from fetch_flags 2020-06-11 11:44:04 +03:00
Manos Pitsidianakis c7fbc5cafb
imap: remove redundant passing of AccountHash 2020-06-11 11:43:18 +03:00
Manos Pitsidianakis 2d862e39f4
imap: off by one error in iteration 2020-06-11 11:42:02 +03:00
Manos Pitsidianakis 2d3f49d64d
imap: index by (MailboxHash, UID) instead of just UID
Mailboxes can share UIDs.
2020-06-11 11:41:08 +03:00
Manos Pitsidianakis 55948dd7c2
Use BTreeSet instead of HashSet in copy_area()
I kind of forgot about BTreeSets, and kept a separate HashSet and sorted
index of the set's keys.
2020-06-10 19:02:54 +03:00
Manos Pitsidianakis e97cf98b3b
Add `view` subcommand
Add subcommand to view standalone e-mail files in meli's pager without
instantiating any accounts.
2020-06-10 18:07:56 +03:00
Manos Pitsidianakis 7dc8a87a62
Prevent sub overflow in EnvelopeView 2020-06-10 18:07:56 +03:00
Manos Pitsidianakis 05c6c19889
src/conf.rs: Remove debug! prints 2020-06-09 17:20:30 +03:00
Manos Pitsidianakis 9f30cd6bbc
state.rs: send AccountStatusChange 2020-06-09 15:39:53 +03:00
Manos Pitsidianakis 1241b6073f
Clear tags before applying new ones in NewFlags 2020-06-09 15:39:01 +03:00
Manos Pitsidianakis ca9d4fde58
Discard EnvelopeRename event if envelope is missing from Collection 2020-06-09 15:38:13 +03:00
Manos Pitsidianakis f3d5edfe14
Add copy/move to other account operations 2020-06-08 22:11:43 +03:00
Manos Pitsidianakis c07185a3aa
regexp: add priority field to regular expressions 2020-06-08 00:55:30 +03:00
Manos Pitsidianakis 465c78e903
Add Cell::keep_attrs() method 2020-06-08 00:55:29 +03:00
Manos Pitsidianakis 4bc8ff2ce9
Use structopt for command line parsing 2020-06-08 00:55:29 +03:00
Manos Pitsidianakis a17f0b4fd4
listing: rework MailListingTrait
split redraw_list() to redraw_threads_list() and redraw_envelope_list()
2020-06-07 14:35:41 +03:00
Manos Pitsidianakis 9edef4ecd2
ui: add attachment_tree() func in MailView
Split ascii attachment tree generation into a function in MailView
2020-06-07 14:35:41 +03:00
Manos Pitsidianakis 5435a4615e
imap: don't try to connect in is_online()
Attempting to connect to the server when calling imap's is_online()
blocks the UI process, so don't.
2020-06-07 14:35:41 +03:00
Manos Pitsidianakis b4dfc1f89d
imap: add experimental header caching with sqlite3
Add support for header caching. It is currently unstable and should not
be used. It can be turned on by specifying "X_header_caching" to true in
the IMAP account's configuration.

The header cache is saved in a sqlite3 database in your XDG_DATA_DIR,
for example:

  /home/epilys/.local/share/meli/17328072387188469646_header_cache.db

Concerns #31 https://git.meli.delivery/meli/meli/issues/31
2020-06-07 14:35:20 +03:00
Manos Pitsidianakis 6458ccb860
meli: update nom dependency to 5.1.1 2020-06-06 23:22:26 +03:00
Manos Pitsidianakis 6ec249dd7f
melib: update nom dependency from 3.2.0 to 5.1.1
That was hecking exhausting
2020-06-06 23:19:07 +03:00
Manos Pitsidianakis db4c401828
melib/error: add chain_err_summary() method 2020-06-06 12:27:02 +03:00
Manos Pitsidianakis e4d4cd55d3
melib: skip mbox `From ` header if present
mbox messages might end up in the parser by mistake, for example by
being present in a Maildir store.
2020-06-06 12:24:39 +03:00
Manos Pitsidianakis 3e31c46a74
Add "regexp" feature, format text with regexps
`regexp` feature uses the pcre2 library to enable the user to define
regular expressions for matching text and applying text formatting to
the matches. An example from the theme configuration I used to test
this:

  [terminal.themes.win95.text_format_regexps]
  "listing.subject" = { "\\[[^\\]]*\\]" = { attrs = "Bold" } }
  "listing.from" = { "\\<[^\\>]*\\>(?:(?:\\s*$)|(?=,))" = { attrs = "Italics" } }

  [terminal.themes.win95.text_format_regexps."pager.envelope.body"]
  "^>.*$" = { attrs = "Italics" }
  "\\d+\\s?(?:(?:[KkMmTtGg]?[Bb])|(?:[KkMmTtGg][Bb]?)(?=\\s))" = { attrs = "Bold | Underline" }
2020-06-05 10:56:36 +03:00
Manos Pitsidianakis ef0f269fbf
terminal: add FormatTag, text format tags
FormatTag describes a set of attributes, colors that exist in a
tag_table field of CellBuffer. The field of tag_associations contains
the hash of a tag and the {start,end} index of the cells that have this
attribute. A tag can thus be used many times.

An example of use is

    let t = self.pager.insert_tag(FormatTag {
        attrs: Attr::ITALICS,
        ..Default::default()
    });
    debug!("FormatTag hash = {}", t);
    let (width, height) = self.pager.size();
    for i in 0..height {
      if self.pager.content[(0, i)].ch() == '>' {
        self.pager.set_tag(t, (0, i), (width.saturating_sub(1), i));
      }
    }

This will set reply lines in text as italics.

This feature interface is not used anywhere yet.
2020-06-05 10:56:36 +03:00
Manos Pitsidianakis 8c1c628c2c
melib: fix non-unicode encode_header() char boundary issue 2020-06-05 10:56:35 +03:00
Manos Pitsidianakis 84976b1dc9
Update libloading dependency to 0.6.2 2020-06-05 10:56:35 +03:00
Manos Pitsidianakis 5366888dff
Add samples/ directory with config and themes 2020-06-02 18:31:07 +03:00
Manos Pitsidianakis d2cdd26127
docs: update meli-themes.5 2020-06-02 18:31:07 +03:00
Manos Pitsidianakis de03b106f3
themes: Add support for Color/Attribute aliases
Add aliases to avoid repetition of raw values when defining new themes.
Aliases are strings starting with "$" and must be defined in the
`color_aliases` and `attr_aliases` fields of a theme.
2020-06-02 18:31:07 +03:00
Manos Pitsidianakis eca8a30c3f
themes: Add Theme struct
Wrap HashMap<Cow<'static, str>, ThemeAttributeInner> into a struct, in
order to add more fields in the future.
2020-06-02 18:31:07 +03:00
Manos Pitsidianakis fa96a4e905
themes: add support for optional field theme value links
Theme attribute values can refer to another theme key instead of
defining a value. Add support for optionally defining the theme key's
field by appending a ".fg" or ".bg" suffix to the link's key.
2020-06-02 18:31:07 +03:00
Manos Pitsidianakis 9c0ee76ff4
themes: Rename Theme struct to Themes 2020-06-02 18:31:07 +03:00
Manos Pitsidianakis 5144fb6b6b
Add CHANGELOG.md file 2020-06-01 18:05:03 +03:00
Manos Pitsidianakis 049175e743
pager: fix filter invocation and ansi parsing 2020-05-31 22:37:06 +03:00
Manos Pitsidianakis bee1baedb2
themes: add indentation level color keys
Add theme keys for the indentation level colors in ThreadView
2020-05-31 16:44:39 +03:00
Manos Pitsidianakis b3b9563db0
LineBreakCandidateIter: make iter non-recursive
A line with lots of graphemes without any breaks can overflow the stack,
so make the recursion into a loop.
2020-05-31 01:08:22 +03:00
Manos Pitsidianakis 6ceed3cae9
sqlite3: move module to melib 2020-05-30 15:37:12 +03:00
Manos Pitsidianakis 815ff98acc
imap: add smarter untagged reply detection 2020-05-30 14:43:44 +03:00
Manos Pitsidianakis 2c45c39048
ShellExpandTrait: fix for non-linux targets 2020-05-30 14:09:54 +03:00
Manos Pitsidianakis 960d660786
Add #[ignore] to test_parser() 2020-05-29 22:21:12 +03:00
Manos Pitsidianakis 9703b39a40
Add execute command parser to improve suggestions
Add grammar for execute commands and parser to identify possible next
tokens for the user's execute command input.

The grammar is given as a sequence of Tokens in each command's
definition. The parser parses the user's input according to this
grammar, and returns the tokens that could come next, if any.
2020-05-29 20:43:40 +03:00
Manos Pitsidianakis fad8820868
Make serde default for manual_refresh = false 2020-05-29 20:43:39 +03:00
Manos Pitsidianakis 12feca9c97
terminal/ansi: add attribute support
Add attribute escape sequence support in terminal::ansi, which handles
converting strings with ansi escape sequences into meli's internal
terminal structures in order to incorporate them into the UI.
2020-05-29 20:43:39 +03:00
Manos Pitsidianakis e4a1ab8a09
Fix rustfmt suggestions 2020-05-29 20:43:39 +03:00
Manos Pitsidianakis 0a83b99e7c
Update nix, linkify, uuid dependencies 2020-05-29 15:59:47 +03:00
Manos Pitsidianakis b8261ee36a
Overhaul input thread
Remove raw/non raw distinction.

Use a pipe for input thread commands and poll stdin/pipe for events
2020-05-29 15:43:05 +03:00
Manos Pitsidianakis 839c1b1eb5
bin.rs: remove useless #[macro_use] 2020-05-28 21:02:49 +03:00
Manos Pitsidianakis bea0ca61f5
maildir: conditionally accept invalid subdirs
If directory is invalid (i.e. has no {cur,new,tmp} subfolders), accept
 it ONLY if it contains subdirs of any depth that are valid maildir
 paths.

For example, this change will accept the following directory tree:
```
  invalid_maildir
  └── valid_maildir
      ├── cur
      ├── new
      └── tmp
```
2020-05-28 21:02:49 +03:00
Manos Pitsidianakis bd404e6937
Execute user shell commands with /bin/sh
Execute user provided command invocations $CMD such as `editor_cmd` with
`/bin/sh` as `/bin/sh -c "$CMD"

Previously, user commands were split by whitespace which must trigger
erroneous behavior if quotes are involved.
2020-05-28 21:02:49 +03:00
Manos Pitsidianakis bfff0e4feb
conf: add options for logging
Add options for log file location and maximum log level. Also add
manpage entries for these options in `meli.conf.5`
2020-05-28 21:02:49 +03:00
Manos Pitsidianakis 608ef9a946
conf: warn on invalid mailbox name conf 2020-05-19 15:00:26 +03:00
Manos Pitsidianakis 671d473894
email/parser: avoid slice index panic if slice is empty 2020-05-19 13:01:09 +03:00
Manos Pitsidianakis f8961f493a
Makefile: expand paths
Makefile displays a warning if $MANDIR is not in your manpaths or
$BINDIR is not in your $PATH. Expand paths $PREFIX (and by association $BINDIR and $MANDIR) before doing that validation or otherwise paths like '~/.local' and '/home/user/.local' will be erroneously reported different
2020-05-19 12:57:09 +03:00
Manos Pitsidianakis fb2bb74c5c
Remove std::dbg! use 2020-05-19 12:55:22 +03:00
Manos Pitsidianakis ab30733ce7
SegmentTree: add update() method 2020-05-18 20:58:55 +03:00
Manos Pitsidianakis c2980f5dcf
RateLimit: add test 2020-05-18 20:58:20 +03:00
Manos Pitsidianakis 3573423169
PosixTimer: rearm timer only when calling rearm() 2020-05-18 20:57:17 +03:00
Manos Pitsidianakis 1717aa7845
bin: use self-pipe in signal handler
send_timeout() isn't signal safe, and might block.
2020-05-18 15:47:19 +03:00
Manos Pitsidianakis 7990b71c19
StatusBar: recognize readline shortcuts in Execute mode 2020-05-16 17:32:30 +03:00
Manos Pitsidianakis 3ce4772251
datetime: fix unupdated tests 2020-05-16 13:34:59 +03:00
Manos Pitsidianakis 38893a77bd
notmuch: fix invalid flag setting 2020-05-16 13:34:29 +03:00
Manos Pitsidianakis 595fa8ab95
notmuch: add total message count for mailboxes 2020-05-16 13:33:22 +03:00
Manos Pitsidianakis 68b1feb6c8
melib: add timestamp to debug trace logs 2020-05-16 12:46:01 +03:00
Manos Pitsidianakis 295577f9d7
Fix invalid theme keys in ThreadListing 2020-05-16 12:44:20 +03:00
Manos Pitsidianakis a86c1cbb26
listing: redraw on EnvelopeUpdate events 2020-05-11 21:01:40 +03:00
Manos Pitsidianakis c5fe511d95
notmuch: don't remove tags from tag_index
When removing a tag, we shouldn't also remove it from the tag index.
2020-05-11 21:01:40 +03:00
Manos Pitsidianakis a6af7fc0d3
listing.rs: don't create unnecessary operation 2020-05-11 21:01:40 +03:00
Manos Pitsidianakis b2857955e4
notmuch: add NewFlags, Remove and Create events 2020-05-11 21:01:40 +03:00
Manos Pitsidianakis 8648b229ad
Add AccountHash to RefreshEvent
Different accounts might have same inboxes with same MailboxHashes. Use
the hash of the account's name to differentiate.
2020-05-10 22:10:17 +03:00
Manos Pitsidianakis eb701695f7
Remove fnv crate 2020-05-10 21:18:56 +03:00
Manos Pitsidianakis b5b9982d9e
notmuch: cache messages by msg-id, not path 2020-05-09 14:32:30 +03:00
Manos Pitsidianakis 3ea1ce5454
errors: add `source` field to MeliError 2020-05-09 14:32:30 +03:00
Manos Pitsidianakis d915c4a7c8
text_processing: remove invalid unreachable!() 2020-05-08 14:58:59 +03:00
Manos Pitsidianakis d405aa9797
Show last worker thread heartbeat on status page 2020-05-08 11:07:10 +03:00
Manos Pitsidianakis c8391983ee
Refactor OfflineListing
Move offline status drawing to OfflineListing
2020-05-08 11:00:45 +03:00
Manos Pitsidianakis 2c549f5fcb
Refactor comments in notmuch/bindings.rs 2020-05-08 10:54:53 +03:00
Manos Pitsidianakis 2230e5705d
notmuch: LOCK database only when needed
Reported in https://git.meli.delivery/meli/meli/issues/24
2020-05-07 23:11:47 +03:00
Manos Pitsidianakis 0a34b082f6
Add cargo-fuzz targets 2020-05-07 22:52:50 +03:00
Manos Pitsidianakis b00d3c28c5
parser: fix panic on invalid encoded_word, display_addr
found by cargo-fuzz
2020-05-06 19:11:49 +03:00
Manos Pitsidianakis 5981f98f17
parser: fix panic on invalid message id 2020-05-06 18:58:00 +03:00
Manos Pitsidianakis f2ecb81612
parser: fix panic on invalid input
Found with cargo-fuzz
2020-05-06 18:47:37 +03:00
Manos Pitsidianakis 5d07a5147b
datetime: fix panic on invalid cstr conversion 2020-05-06 18:46:38 +03:00
Manos Pitsidianakis 330134af5a
maildir: update mailbox unread count on file rename event 2020-05-06 17:38:29 +03:00
Manos Pitsidianakis d580b25415
themes: overwrite only explicit key attributes
If user config file overwrites a single attribute and not the others,
for example only bg:

  "mail.listing.tag_default" = { bg = "Blue" }

The other attributes, in this case fg and attrs revert to the default
values of ThemeAttributeInner and not the default value for the key
"mail.listing.tag_default". As a result the above expands to:

  "mail.listing.tag_default" = { fg = Color::Default, bg = "Blue", attrs
  = Attr::Default }

This commit keeps the key value defaults, so the above should expand to:

  "mail.listing.tag_default" = { fg = default_theme["mail.listing.tag_default"].fg, bg = "Blue", attrs
  = default_theme["mail.listing.tag_default"].attrs }
2020-04-10 11:41:00 +03:00
Manos Pitsidianakis 18dcf15e1e
Add open_mailbox shortcut for sidebar 2020-04-05 21:35:36 +03:00
Manos Pitsidianakis d8135674df
themes: add {even,odd}_unseen, {even,odd}_selected, {even,odd}_highlighted
Suggested in #21
2020-04-05 15:57:05 +03:00
Manos Pitsidianakis e633434b93
themes: Fix invalid attribute links panic in is_cyclic
Attribute links are not checked for validity in theme validation, and an
invalid link would cause a panic in is_cyclic.

This commit improves the theme validation errors by printing if the
error lies in a theme key or a link.
2020-04-05 15:57:05 +03:00
Manos Pitsidianakis 4930d1b46c
Add Italics, Blink, Dim and Hidden text attributes
Text attributes have been rewritten as bit flags, so for example instead of
"BoldUnderline" you'd have to define "Bold | Underline" in your theme
settings.

Requested in #21
2020-04-05 15:57:05 +03:00
Manos Pitsidianakis e9a935dbf7
melib: add search method in mail backends 2020-04-05 15:57:05 +03:00
Manos Pitsidianakis 3d7b9ff7cb
Move Query to melib 2020-04-05 15:57:05 +03:00
Manos Pitsidianakis c37d8bd331
imap: add mutex timeout lock and remove unwraps 2020-04-05 15:56:59 +03:00
Manos Pitsidianakis 5842a63e37
melib: ignore Draft body if empty for multipart mail 2020-04-04 19:17:16 +03:00
Manos Pitsidianakis ad2a51891b
melib: print attachment name in Display for text/* 2020-04-04 19:16:35 +03:00
Manos Pitsidianakis fd60be482f
Open sidebar for mailbox navigation with Left/Right arrow keys
Left/Right arrow keys change focus between the sidebar and mailbox
listing. If focused on sidebar, move arrow keys to select mailbox and
open with 'Enter'. Press Right arrow key to return to mailbox listing.

- Mailbox focused:
  +--+-------------+
  |~ |=============|
  |~ |=============|
  |  |=============|
  |~ |=============|
  |~ |=============|
  +--+-------------+
- Press `Left` arrow key
- Menu focused:
  +--------+-------+
  |~~~~    |=======|
  |~~      |=======|
  |        |=======|
  |~~~     |=======|
  |~~~~    |=======|
  +--------+-------+
- Press `Right` arrow key to return
2020-04-04 19:15:58 +03:00
Manos Pitsidianakis 840005022c
themes: add default tag theme attribute
The theme attribute key is "mail.listing.tag_default"
2020-04-03 10:13:27 +03:00
Manos Pitsidianakis 6ccb9d3d75
melib/src/email/address.rs: Fix invalid UTF8 panic
In StrBuilder::display there's an assumption that the string is valid utf-8 but if an email contains an invalid string inside the MIME encoded word part the conversion panics. Change it to a lossy UTF-8 conversion instead. Fixes #19

Reported-By: cycomanic
2020-04-02 08:22:12 +03:00
Manos Pitsidianakis e034f4dd52
view.rs: fix redrawing errors 2020-03-28 11:46:10 +02:00
Manos Pitsidianakis a3903ea2cb
Show Cc in default headers in mail view 2020-03-28 11:45:31 +02:00
Manos Pitsidianakis 9afb636894
melib/email: fix whitespace duplication in mime encoding 2020-03-28 11:44:30 +02:00
Manos Pitsidianakis 8eca8b34ed
jmap: fix two error messages 2020-03-28 11:43:32 +02:00
Manos Pitsidianakis c77af98b26
imap: prevent deadlock in operations.rs
imap/operations.rs could deadlock with imap/watch.rs when both lock the
main IMAP connection but both also need to lock UIDStore
2020-03-25 13:12:18 +02:00
Manos Pitsidianakis 4c32bf450d
Add {un,}subscribe mailbox operations
Concerns #17
2020-03-24 21:05:06 +02:00
Manos Pitsidianakis 5c2b93ee18
jmap: add parser for rfc3339 dates
Reported-by:cycomanic
Concerns #18 https://git.meli.delivery/meli/meli/issues/18
2020-03-24 00:09:40 +02:00
Manos Pitsidianakis 61be6e4c96
notmuch: fix wrong mailbox path in save()
mailbox path was passed to save_to_mailbox() with a cur/ tail and
save_to_mailbox() added an extra cur/ tail
2020-03-18 19:22:17 +02:00
Manos Pitsidianakis 7a770c7f7b
imap: fetch RFC822 instead of RFC822.HEADER
RFC822.HEADER is not parsed in imap/protocol_parser.rs
2020-03-18 19:19:39 +02:00
Manos Pitsidianakis 9ff54f236b
Add conf_override! macro
conf_override! wraps struct definitions and defines a secondary Override
struct that wraps each field in an Option. The macro mailbox_settings!
is used to select settings from an account & mailbox index. If a user defines an overriding setting, the macro returns the override instead of the immediately next in the hierarchy setting.

The selection is done for a specific field as follows:

  if per-folder override is defined, return per-folder override
    else if per-account override is defined, return per-account override
      else return global setting field value.
2020-03-18 19:13:07 +02:00
Manos Pitsidianakis a8c1016f37
Add various logic checks 2020-03-12 09:47:39 +02:00
Manos Pitsidianakis 6ca8c3b964
imap: add server_password_command 2020-03-12 09:45:18 +02:00
Manos Pitsidianakis 1811fb51cb
Fix unused imports/code compiler warnings 2020-03-04 22:11:37 +02:00
Manos Pitsidianakis b7175c2400
Fix compiler error in --no-default-features build 2020-03-04 22:04:57 +02:00
Manos Pitsidianakis 84d7e4c034
Small documentation fixes 2020-03-04 14:11:00 +02:00
Manos Pitsidianakis 31d90e1d87
Add managesieve.rs 2020-03-04 14:09:55 +02:00
Manos Pitsidianakis 651dda67cf
Respect autoload mailbox setting 2020-03-02 12:06:19 +02:00
Manos Pitsidianakis 106dae3334
Add config overrides to mailbox filter
If per-folder config filter is defined, it overrides the app-wide
filter.
2020-03-01 22:51:58 +02:00
Manos Pitsidianakis c19b9ec181
Add auto_choose_multipart_alternative to manpage 2020-03-01 20:58:24 +02:00
Manos Pitsidianakis a3600c0cd2
Add `filter` option in mail list
Filter mail in mail list.

Example:
[listing]
filter = "not flags:seen" # show only unseen messages
2020-03-01 20:24:00 +02:00
Manos Pitsidianakis 9d20fd5576
Save forked processes for reaping 2020-03-01 17:56:58 +02:00
Manos Pitsidianakis 6c76db2063
Add delete, copy actions for envelopes 2020-03-01 17:48:10 +02:00
Manos Pitsidianakis 2a9059f9b4
Add add-attachment from pipe, default_header_values 2020-03-01 17:45:55 +02:00
Manos Pitsidianakis 6079909f9c
imap: add managesieve connection
So far only the connection is implemented, and using the
testing/manage_sieve binary you can get a shell to a managesieve server.

The managesieve interface will be used in the UI from a plugin, but the
plugin's interface isn't implemented yet.
2020-02-28 15:47:07 +02:00
Manos Pitsidianakis 63467a3c45
Check ComponentId equality on Composer::kill() 2020-02-28 09:18:31 +02:00
Manos Pitsidianakis 63af2a688a
Detect breaks on write_string_to_grid 2020-02-28 09:17:30 +02:00
Manos Pitsidianakis f10cc954e7
Don't dump mail on Account drop 2020-02-28 09:16:50 +02:00
Manos Pitsidianakis a94bb1e27a
Show float notification on refresh cmd 2020-02-28 09:16:19 +02:00
Manos Pitsidianakis 670485e8c7
compose: clear bounds of compose area properly 2020-02-28 09:15:11 +02:00
Manos Pitsidianakis 7b631beb0a
Don't panic in WorkController::drop 2020-02-28 09:12:36 +02:00
Manos Pitsidianakis 6b2a1f7757
imap: Don't fail on WouldBlock on ImapBlockingConnection 2020-02-28 09:11:41 +02:00
Manos Pitsidianakis ca51077f53
imap: Add support for untagged FETCH (FLAG.. messages
IDLE connection can get untagged "* FETCH (FLAGS ({flag_list))" messages
if any client has changed flags. Support this refresh event.
2020-02-28 09:09:43 +02:00
Manos Pitsidianakis c1a64d6c33
Add imports in tag_hash macro 2020-02-28 09:04:01 +02:00
Manos Pitsidianakis 53fa3d03da
Notify embedded terminal on embedded process exit
When an embedded process exits the main process receives a SIGCHLD. The
check on whether the embedded process is alive is done on input, so
forward an input of '\0' to get the embedded terminal to notice its
child is dead.
2020-02-27 16:46:47 +02:00
Manos Pitsidianakis 126b65817e
Forward input on input/rawinput switch
Input thread listens on stdin input and forwards the input to the main
process. When an embedded terminal is launched within the main process,
the input thread is asked to switch to raw input, that is to send the
parsed input and the raw bytes to the main process in order to get them
forwarded to the embedded terminal. The switch happens by calling
get_events and get_events_raw.

When the input thread receives an InputCommand::{No,}Raw, it has already
received an input event, since the `select!` is within the
stdin events for loop. (There's no way to `select` on blocking iterators
or raw fds, which is unfortunate.).

This commit forwards the input to the next function instead of dropping
it.
2020-02-27 16:41:58 +02:00
Manos Pitsidianakis 7807f565ec
Clear input thread channel on restore()
The channel may contain Kill commands that will cause the new thread to
exit immediately.
2020-02-27 16:40:03 +02:00
Manos Pitsidianakis 65666e6695
Fix double call of restore_input
restore_input is called in State::rcv_event on arrival of a fork
finished event:

```
            UIEvent::Fork(ForkType::Finished) => {
                self.switch_to_main_screen();
                self.switch_to_alternate_screen();
                self.context.restore_input();
                return;
            }
```

So there shouldn't be an extra call here.
2020-02-27 16:37:42 +02:00
Manos Pitsidianakis c43f3564d3
Update README on notmuch feature 2020-02-27 16:36:47 +02:00
Manos Pitsidianakis bae083cc8f
Rename Filter action to search 2020-02-26 18:36:52 +02:00
Manos Pitsidianakis 760c1e859d
Add search shortcut to shortcut map 2020-02-26 16:23:02 +02:00
Manos Pitsidianakis 33c1bf6558
Add consume newlines flag to phrase() 2020-02-26 15:53:46 +02:00
Manos Pitsidianakis 303c530488
Load libnotmuch dynamically 2020-02-26 14:18:00 +02:00
Manos Pitsidianakis ac71d627f1
Implement search for CellBuffer 2020-02-26 12:25:57 +02:00
Manos Pitsidianakis 4ac52d9d5b
Replace every use of Folder with Mailbox
Use Mailbox for consistency.
2020-02-26 10:54:10 +02:00
Manos Pitsidianakis 1245eae0be
Add Knuth–Morris–Pratt to pager 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis c9469f26ee
Remove duplicate function timer::arm()
arm() was a duplicate of set_value()
2020-02-25 22:15:13 +02:00
Manos Pitsidianakis 45c0160cb6
Fix ThreadListing
ThreadListing was broken after the ThreadGroup introduction
2020-02-25 22:15:13 +02:00
Manos Pitsidianakis 68007a0842
View decoded email source by default
Toggle between decoded/raw source with view_raw_source shortcut, default
M-r
2020-02-25 22:15:13 +02:00
Manos Pitsidianakis 44da24fc96
Add left/right cursor mvments to execute bar 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis c88d1cae51
Fix create_box boundary fg color 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis c4c11e4abc
Make Selector widget accept FnOnce 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis 499fd59c6e
melib/imap: implement refresh() 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis bbdc9d69b4
melib/imap: add ImapConnection::connect() 2020-02-25 22:15:13 +02:00
Manos Pitsidianakis f38d03e43a
melib: {create,delete}_folder returns updated folders
Potential parent folders will have their children fields updated, so
just return all folders.
2020-02-25 22:15:13 +02:00
Manos Pitsidianakis 9a46e58029
imap: don't retry command on reconnection
If a command fails and connection is restarted, don't try the command
again; it only made sense in the previous connection's context.
2020-02-19 17:06:26 +02:00
Manos Pitsidianakis e3abd458ce
Add ui_dialogs in State 2020-02-19 17:01:13 +02:00
Manos Pitsidianakis a806571322
Add UIDialog and UIConfirmationDialog widgets
They are just typedefs for the Selector widget. The API is kind of
messed up and this commit is part of the process of cleaning it up:
right now to use this, you check the is_done() method which if returns
true, the done() method executes the closure you defined when creating
the widget. The closure returns a UIEvent which you can forward
application-wide by context.replies.push_back(event) or handle it in
process_event() immediately.
2020-02-19 16:57:37 +02:00
Manos Pitsidianakis e22ab2b424
ui: fix shortcuts map title not showing up on resize 2020-02-15 17:21:45 +02:00
Manos Pitsidianakis d779a94279
Fix sent_folder not getting recorded if no explicit folder conf is set 2020-02-12 18:56:05 +02:00
Manos Pitsidianakis b6efb14824
melib: remove Mailbox
Refactor Collection from melib to hold what folders have what envelopes.

Frontend accounts will now have a FolderEntry for each logical folder
and will unify many Account fields into one and eliminate a lot of
duplicate/dead code.
2020-02-10 02:11:07 +02:00
Manos Pitsidianakis b50e770b5a
ui/accounts: remove Index<usize> impls 2020-02-10 00:41:06 +02:00
Manos Pitsidianakis aab6b02db2
ui: clear selection with Esc 2020-02-10 00:10:19 +02:00
Manos Pitsidianakis e26ed83331
Update native-tls to 0.2.3 2020-02-10 00:09:55 +02:00
Manos Pitsidianakis 4090eecd04
ui: Consume Esc input events only when necessary 2020-02-09 23:32:14 +02:00
Manos Pitsidianakis 9757e523bd
debian/: add build artifacts to .gitignore 2020-02-09 20:54:09 +02:00
Manos Pitsidianakis 14b0ef8f37
Respect use_color conf value as well as NO_COLOR 2020-02-09 20:47:36 +02:00
Manos Pitsidianakis a496de2794
build.rs: add rerun-if-changed 2020-02-09 20:46:39 +02:00
362 changed files with 123638 additions and 51155 deletions

View File

@ -0,0 +1,2 @@
[env]
PCRE2_SYS_STATIC = "1"

View File

@ -0,0 +1,2 @@
# Use cargo-derivefmt to sort derives alphabetically
f900dbea468e822c5a510a72ecc6367549443927

View File

@ -0,0 +1,25 @@
---
name: "Pull Request"
about: "Standard pull request template."
title: "WIP: "
ref: "master"
---
<!-- If your PR is ready to merge/review, remove the `WIP: ` prefix from the title. -->
### Summary of the PR
<!-- Changes introduced in this PR. -->
### Requirements
Before submitting your PR, please make sure you have addressed the following requirements:
* [ ] All commits in this PR are signed (with `git commit -s`), and the commit has a message describing the motivation behind the change, if appropriate.
* [ ] All added/changed public-facing functionality, especially configuration options, are documented in the manual pages.
* [ ] Any newly added `unsafe` code is properly documented.
* [ ] Each commit has been formatted with `rustfmt`. Run `make fmt` in the project root.
* [ ] Each commit has been linted with `clippy`. Run `make lint` in the project root.
* [ ] Each commit does not break any test. Run `make test` in the project root. If you have `cargo-nextest` installed, you can run `cargo nextest run --all --no-fail-fast --all-features --future-incompat-report` instead.

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: EUPL-1.2
name: Build .deb package
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
RUSTFLAGS: "-D warnings -W unreachable-pub -W rust-2021-compatibility"
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: short
on:
workflow_dispatch:
push:
tags:
- v*
jobs:
build:
name: Package for debian on ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: [linux-amd64, ]
include:
- build: linux-amd64
arch: amd64
os: ubuntu-latest
rust: stable
artifact_name: 'linux-amd64'
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- id: os-deps
name: install OS dependencies
run: |
apt-get update
apt-get install -y mandoc debhelper quilt build-essential
- id: rustup-setup
name: Install rustup and toolchains
shell: bash
run: |
if ! command -v rustup &>/dev/null; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
source "${HOME}/.cargo/env"
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
echo "CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}" >> $GITHUB_ENV
rustup toolchain install --profile minimal ${{ matrix.rust }} --target ${{ matrix.target }}
rustup default ${{ matrix.rust }}
fi
- name: Build binary
run: |
VERSION=$(grep -m1 version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1)
make deb-dist
mkdir artifacts
echo "VERSION=${VERSION}" >> $GITHUB_ENV
mv ../meli_*.deb artifacts/meli-${VERSION}-${{ matrix.artifact_name }}.deb
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: meli-${{env.VERSION}}-${{ matrix.artifact_name }}.deb
path: artifacts/meli-${{env.VERSION}}-${{ matrix.artifact_name }}.deb
if-no-files-found: error
retention-days: 30

View File

@ -0,0 +1,90 @@
# SPDX-License-Identifier: EUPL-1.2
name: Build release binary
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
RUSTFLAGS: "-D warnings -W unreachable-pub -W rust-2021-compatibility"
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: short
on:
workflow_dispatch:
push:
tags:
- v*
jobs:
build:
name: Build on ${{ matrix.build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: [linux-amd64, ]
include:
- build: linux-amd64
os: ubuntu-latest
rust: stable
artifact_name: 'meli-linux-amd64'
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- id: os-deps
name: install OS dependencies
run: |
apt-get update
apt-get install -y libdbus-1-dev pkg-config mandoc libssl-dev
#- id: cache-rustup
# name: Cache Rust toolchain
# uses: https://github.com/actions/cache@v3
# with:
# path: ~/.rustup
# key: toolchain-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-rustup.outputs.cache-hit != 'true' }}
- id: rustup-setup
name: Install rustup and toolchains
shell: bash
run: |
if ! command -v rustup &>/dev/null; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
source "${HOME}/.cargo/env"
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
rustup toolchain install --profile minimal ${{ matrix.rust }} --target ${{ matrix.target }}
fi
- name: Configure cargo data directory
# After this point, all cargo registry and crate data is stored in
# $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files
# that are needed during the build process. Additionally, this works
# around a bug in the 'cache' action that causes directories outside of
# the workspace dir to be saved/restored incorrectly.
run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
#- id: cache-cargo
# name: Cache cargo configuration and installations
# uses: https://github.com/actions/cache@v3
# with:
# path: ${{ env.CARGO_HOME }}
# key: cargo-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
- name: Setup Rust target
run: |
mkdir -p "${{ env.CARGO_HOME }}"
cat << EOF > "${{ env.CARGO_HOME }}"/config.toml
[build]
target = "${{ matrix.target }}"
EOF
- name: Build binary
run: |
make
mkdir artifacts
mv target/*/release/* target/ || true
mv target/release/* target/ || true
mv target/meli artifacts/
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.artifact_name }}
path: artifacts/meli
if-no-files-found: error
retention-days: 30

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: EUPL-1.2
name: Run cargo lints
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
RUSTFLAGS: "-D warnings -W unreachable-pub -W rust-2021-compatibility -C debuginfo=0"
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: short
on:
workflow_dispatch:
pull_request:
paths:
- '.gitea/**'
- 'melib/src/**'
- 'melib/Cargo.toml'
- 'meli/src/**'
- 'meli/Cargo.toml'
- 'Cargo.toml'
- 'Cargo.lock'
jobs:
test:
name: Lint on ${{ matrix.build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: [linux-amd64, ]
include:
- build: linux-amd64
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- id: os-deps
name: install OS dependencies
run: |
apt-get update
apt-get install -y libdbus-1-dev pkg-config mandoc libssl-dev
#- id: cache-rustup
# name: Cache Rust toolchain
# uses: https://github.com/actions/cache@v3
# with:
# path: ~/.rustup
# key: toolchain-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-rustup.outputs.cache-hit != 'true' }}
- id: rustup-setup
name: Install Rustup and toolchains
shell: bash
run: |
if ! command -v rustup &>/dev/null; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
source "${HOME}/.cargo/env"
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
rustup toolchain install --profile minimal --component clippy,rustfmt --target ${{ matrix.target }} -- "${{ matrix.rust }}"
rustup default ${{ matrix.rust }}
fi
- name: Configure cargo data directory
# After this point, all cargo registry and crate data is stored in
# $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files
# that are needed during the build process. Additionally, this works
# around a bug in the 'cache' action that causes directories outside of
# the workspace dir to be saved/restored incorrectly.
run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
#- id: cache-cargo
# name: Cache cargo configuration and installations
# uses: https://github.com/actions/cache@v3
# with:
# path: ${{ env.CARGO_HOME }}
# key: cargo-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
- name: Setup Rust target
run: |
mkdir -p "${{ env.CARGO_HOME }}"
cat << EOF > "${{ env.CARGO_HOME }}"/config.toml
[build]
target = "${{ matrix.target }}"
EOF
- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
name: Add lint dependencies
run: |
cargo install --quiet --version 1.0.9 --target "${{ matrix.target }}" cargo-sort
RUSTFLAGS="" cargo install --locked --target "${{ matrix.target }}" --git https://github.com/dcchut/cargo-derivefmt --rev 2ff93de7fb418180458dd1ba27e5655607c23ab6 --bin cargo-derivefmt
- name: rustfmt
if: success() || failure()
run: |
cargo fmt --check --all
- name: clippy
if: success() || failure()
run: |
cargo clippy --no-deps --all-features --all --tests --examples --benches --bins
- name: cargo-derivefmt melib
if: success() || failure()
run: |
cargo derivefmt --manifest-path ./melib/Cargo.toml
- name: cargo-derivefmt meli
if: success() || failure()
run: |
cargo derivefmt --manifest-path ./meli/Cargo.toml
- name: cargo-derivefmt fuzz
if: success() || failure()
run: |
cargo derivefmt --manifest-path ./fuzz/Cargo.toml
- name: cargo-derivefmt tools
if: success() || failure()
run: |
cargo derivefmt --manifest-path ./tools/Cargo.toml

View File

@ -0,0 +1,91 @@
# SPDX-License-Identifier: EUPL-1.2
name: Cargo manifest lints
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
RUSTFLAGS: "-D warnings -W unreachable-pub -W rust-2021-compatibility -C debuginfo=0"
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: short
on:
workflow_dispatch:
pull_request:
paths:
- '.gitea/**'
- 'melib/Cargo.toml'
- 'meli/Cargo.toml'
- 'fuzz/Cargo.toml'
- 'tool/Cargo.toml'
- 'Cargo.toml'
- 'Cargo.lock'
- '.cargo/config.toml'
jobs:
manifest_lint:
name: Lint Cargo manifests on ${{ matrix.build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: [linux-amd64, ]
include:
- build: linux-amd64
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- id: os-deps
name: install OS dependencies
run: |
apt-get update
apt-get install -y mandoc
- name: Find meli MSRV from meli/Cargo.toml.
run: echo MELI_MSRV=$(grep -m1 rust-version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1) >> $GITHUB_ENV
- id: rustup-setup
name: Install Rustup and toolchains
shell: bash
run: |
if ! command -v rustup &>/dev/null; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
source "${HOME}/.cargo/env"
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
echo "CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}" >> $GITHUB_ENV
rustup toolchain install --profile minimal --component "rustfmt" --target "${{ matrix.target }}" -- "${{ env.MELI_MSRV }}"
rustup component add rustfmt --toolchain ${{ env.MELI_MSRV }}-${{ matrix.target }}
rustup toolchain install --profile minimal --component "rustfmt" --target "${{ matrix.target }}" -- "${{ matrix.rust }}"
rustup component add rustfmt --toolchain ${{ matrix.rust }}-${{ matrix.target }}
rustup default ${{ matrix.rust }}
fi
- name: Setup Rust target
run: |
mkdir -p "${{ env.CARGO_HOME }}"
cat << EOF > "${{ env.CARGO_HOME }}"/config.toml
[build]
target = "${{ matrix.target }}"
EOF
- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
name: Add manifest lint dependencies
run: |
source "${HOME}/.cargo/env"
cargo install --quiet --version 1.0.9 --target "${{ matrix.target }}" cargo-sort
cargo install --quiet --version 0.15.1 --target "${{ matrix.target }}" cargo-msrv
- name: cargo-msrv verify melib MSRV
if: success() || failure()
run: |
source "${HOME}/.cargo/env"
cargo-msrv --output-format json --log-level trace --log-target stdout --path meli verify
cargo-msrv --output-format json --log-level trace --log-target stdout --path melib verify
- name: cargo-sort
if: success() || failure()
run: |
source "${HOME}/.cargo/env"
cargo-sort --check --check-format --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace fuzz
cargo-sort --check --check-format --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace tools
cargo-sort --check --check-format --grouped --order package,bin,lib,dependencies,features,build-dependencies,dev-dependencies,workspace --workspace
- name: Check debian/changelog is up-to-date.
if: success() || failure()
run: |
./scripts/check_debian_changelog.sh

View File

@ -0,0 +1,103 @@
# SPDX-License-Identifier: EUPL-1.2
name: Run Tests
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
RUSTFLAGS: "-D warnings -W unreachable-pub -W rust-2021-compatibility -C debuginfo=0"
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: short
on:
workflow_dispatch:
pull_request:
paths:
- '.gitea/**'
- 'melib/src/**'
- 'melib/Cargo.toml'
- 'meli/src/**'
- 'meli/Cargo.toml'
- 'Cargo.toml'
- 'Cargo.lock'
jobs:
test:
name: Test on ${{ matrix.build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: [linux-amd64, ]
include:
- build: linux-amd64
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- id: os-deps
name: install OS dependencies
run: |
apt-get update
apt-get install -y libdbus-1-dev pkg-config mandoc libssl-dev make
#- id: cache-rustup
# name: Cache Rust toolchain
# uses: https://github.com/actions/cache@v3
# with:
# path: ~/.rustup
# key: toolchain-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-rustup.outputs.cache-hit != 'true' }}
- id: rustup-setup
name: Install rustup and toolchains
shell: bash
run: |
if ! command -v rustup &>/dev/null; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
source "${HOME}/.cargo/env"
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
rustup toolchain install --profile minimal ${{ matrix.rust }} --target ${{ matrix.target }}
fi
- name: Configure cargo data directory
# After this point, all cargo registry and crate data is stored in
# $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files
# that are needed during the build process. Additionally, this works
# around a bug in the 'cache' action that causes directories outside of
# the workspace dir to be saved/restored incorrectly.
run: echo "CARGO_HOME=$(pwd)/.cargo_home" >> $GITHUB_ENV
#- id: cache-cargo
# name: Cache cargo configuration and installations
# uses: https://github.com/actions/cache@v3
# with:
# path: ${{ env.CARGO_HOME }}
# key: cargo-${{ matrix.os }}-${{ matrix.rust }}
#- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
- name: Setup Rust target
run: |
mkdir -p "${{ env.CARGO_HOME }}"
cat << EOF > "${{ env.CARGO_HOME }}"/config.toml
[build]
target = "${{ matrix.target }}"
EOF
- if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} && matrix.target
name: Add test dependencies
run: |
cargo install --quiet --version 0.9.54 --target "${{ matrix.target }}" cargo-nextest
- name: cargo-check
run: |
cargo check --all-features --all --tests --examples --benches --bins
- name: Compile
if: success() || failure()
run: cargo test --all --no-fail-fast --all-features --no-run --locked
- name: cargo test
run: |
cargo nextest run --all --no-fail-fast --all-features --future-incompat-report -E 'not (test(smtp::test::test_smtp))'
#cargo test --all --no-fail-fast --all-features -- --nocapture --quiet
- name: rustdoc build
if: success() || failure() # always run even if other steps fail, except when cancelled <https://stackoverflow.com/questions/58858429/how-to-run-a-github-actions-step-even-if-the-previous-step-fails-while-still-f>
run: |
make build-rustdoc
- name: rustdoc tests
if: success() || failure()
run: |
make test-docs

9
.gitignore vendored
View File

@ -9,3 +9,12 @@ target/
__pycache__/
*.py[cod]
debian/.debhelper/
debian/debhelper-build-stamp
debian/files
debian/meli.substvars
debian/meli/
# CLion IDE
.idea

76
BUILD.md 100644
View File

@ -0,0 +1,76 @@
# Build `meli`
For a quick start, build and install locally:
```sh
PREFIX=~/.local make install
```
Available subcommands for `make` are listed with `make help`.
The Makefile *should* be POSIX portable and not require a specific `make` version.
`meli` requires rust version 1.68.2 or later and rust's package manager, Cargo.
Information on how to get it on your system can be found here: <https://doc.rust-lang.org/cargo/getting-started/installation.html>
With Cargo available, the project can be built with `make` and the resulting binary will then be found under `target/release/meli`.
Run `make install` to install the binary and man pages.
This requires root, so I suggest you override the default paths and install it in your `$HOME`: `make PREFIX=${HOME}/.local install`.
You can build and run `meli` with one command: `cargo run --release`.
## Build features
Some functionality is held behind "feature gates", or compile-time flags. The following list explains each feature's purpose:
- `gpgme` enables GPG support via `libgpgme` (on by default)
- `dbus-notifications` enables showing notifications using `dbus` (on by default)
- `notmuch` provides support for using a notmuch database as a mail backend (on by default)
- `jmap` provides support for connecting to a jmap server and use it as a mail backend (on by default)
- `sqlite3` provides support for builting fast search indexes in local sqlite3 databases (on by default)
- `cli-docs` includes the manpage documentation compiled by either `mandoc` or `man` binary to plain text in `meli`'s command line. Embedded documentation can be viewed with the subcommand `meli man [PAGE]` (on by default).
- `regexp` provides experimental support for theming some e-mail fields based
on regular expressions.
It uses the `pcre2` library.
Since it's actual use in the code is very limited, it is not recommended to use this (off by default).
- `static` and `*-static` bundle C libraries in dependencies so that you don't need them installed in your system (on by default).
Though not a feature, the presence of the environment variable `UNICODE_REGENERATE_TABLES` in compile-time of the `melib` crate will force the regeneration of unicode tables.
Otherwise the tables are included with the source code, and there's no real reason to regenerate them unless you intend to modify the code or update to a new Unicode version.
## Build Debian package (*deb*)
Building with Debian's packaged cargo might require the installation of these two packages: `librust-openssl-sys-dev librust-libdbus-sys-dev`
A `*.deb` package can be built with `make deb-dist`
## Using notmuch
To use the optional notmuch backend feature, you must have `libnotmuch5` installed in your system.
In Debian-like systems, install the `libnotmuch5` packages.
`meli` detects the library's presence on runtime.
If it is not detected, you can use the `library_file_path` setting on your notmuch account to specify the absolute path of the library.
## Using GPG
To use the optional gpg feature, you must have `libgpgme` installed in your system.
In Debian-like systems, install the `libgpgme11` package.
`meli` detects the library's presence on runtime.
## Development
Development builds can be built and/or run with
```
cargo build
cargo run
```
There is a debug/tracing log feature that can be enabled by using the flag `--feature debug-tracing` after uncommenting the features in `Cargo.toml`.
The logs are printed in stderr when the env var `MELI_DEBUG_STDERR` is defined, thus you can run `meli` with a redirection (i.e `2> log`).
To trace network and protocol communications you can enable the following features:
- `imap-trace`
- `jmap-trace`
- `nntp-trace`
- `smtp-trace`

733
CHANGELOG.md 100644
View File

@ -0,0 +1,733 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- [0e3a0c4b](https://git.meli-email.org/meli/meli/commit/0e3a0c4b7049139994a65c6fe914dd3587c6713e) Add safe UI widget area drawing API
- [0114e695](https://git.meli-email.org/meli/meli/commit/0114e695428579ef4461b289d7372e3b392b5e62) Add next_search_result and previous_search_result shortcuts
- [c4344529](https://git.meli-email.org/meli/meli/commit/c4344529e30b3385149d6dc3c1c4b34306a85491) Add .git-blame-ignore-revs file
### Fixed
- [bcec745c](https://git.meli-email.org/meli/meli/commit/bcec745c241d7ed5d7d455ccdd65c6c95e1862b0) Fix command and status bar drawing
- [62b8465f](https://git.meli-email.org/meli/meli/commit/62b8465f2cd99789576d70008f1f321243b81fc3) Fix ThreadView for new TUI API
- [28fa66cc](https://git.meli-email.org/meli/meli/commit/28fa66cc2ad05e67708377fc99ffd65aa1b14386) Fix ThreadedListing for new TUI API
- [2c6f180d](https://git.meli-email.org/meli/meli/commit/2c6f180df987976c1f4cba7ceac878e697c73d27) Fix macos compilation
- [24971d19](https://git.meli-email.org/meli/meli/commit/24971d1960418bad92d89af9eb744933445baf99) Fix compilation with 1.70.0 cargo
### Changed
- [a1cbb198](https://git.meli-email.org/meli/meli/commit/a1cbb1988b34951046045f724f52bed2925b3880) Return Results instead of panicking
- [7645ff1b](https://git.meli-email.org/meli/meli/commit/7645ff1b875e3920389567eb5e61d800291e8a27) Rename write_string{to_grid,}
- [c2ae19d1](https://git.meli-email.org/meli/meli/commit/c2ae19d1208f2eb5cca341a04e019c3e285637a8) Return Option from current_pos
- [b61fc3ab](https://git.meli-email.org/meli/meli/commit/b61fc3ab6482dcef4f5cc1c09db3539b7e401f78) Add HelpView struct for shortcuts widget
- [ba7a97e9](https://git.meli-email.org/meli/meli/commit/ba7a97e90b4c474299a7b12fa74b7ea06c1535c8) Add x axis scroll support
- [3495ffd6](https://git.meli-email.org/meli/meli/commit/3495ffd61b5888f8538304ecb6e441819b373bdc) Change UIEvent::Notification structure
- [ccf6f9a2](https://git.meli-email.org/meli/meli/commit/ccf6f9a26e95437fb24464f90736c653e3f5dfed) Remember previous `set [index_style]]` preferences
- [23c15261](https://git.meli-email.org/meli/meli/commit/23c15261e79c63791c569f225c1745df1b90ce2d) Abstract envelope view filters away
- [031d0f7d](https://git.meli-email.org/meli/meli/commit/031d0f7dc76700ac938e1ee4a767fab8deebb9f2) Add area.is_empty() checks in cell iterators
- [e37997d6](https://git.meli-email.org/meli/meli/commit/e37997d697f1f0b8faaa56a36f43c9f1da4bbb41) Store Link URL value in Link type
### Refactoring
- [0500e451](https://git.meli-email.org/meli/meli/commit/0500e451dab5f129d71a9279913531e77981e868) Add missing EnvelopeRemove event handler
- [ab14f819](https://git.meli-email.org/meli/meli/commit/ab14f81900a03a07ef00a6b3232cb29d78e8edf5) Make write_string_to_grid a CellBuffer method
- [e0adcdfe](https://git.meli-email.org/meli/meli/commit/e0adcdfe15b8a78c333de199ba734a83181f53be) Move rest of methods under CellBuffer
- [0a74c7d0](https://git.meli-email.org/meli/meli/commit/0a74c7d0e5c318dd29c8ace01e588d441e0fcfb6) Overhaul refactor
- [3b4acc15](https://git.meli-email.org/meli/meli/commit/3b4acc15a535c9bfd084b2e33f2cd00b5b5d4eb0) Add tests
- [7eedd860](https://git.meli-email.org/meli/meli/commit/7eedd860518e3f7f5000a1888e4fa58ddbfb43bc) Remove address_list! macro
- [f3e85738](https://git.meli-email.org/meli/meli/commit/f3e85738e7981755e96468213c02af78432f8cdd) Move build.rs scripts to build directory
- [77325486](https://git.meli-email.org/meli/meli/commit/773254864bd8436712905eeb0c725d1d05277e60) Remove on-push hooks for actions w/ run on-pr
### Documentation
- [d018f07a](https://git.meli-email.org/meli/meli/commit/d018f07aa51fc293bf696fa7d7beff8e59ac91a8) Retouch manual pages
- [3adba40e](https://git.meli-email.org/meli/meli/commit/3adba40e32a8a66271ea2a8f5ddf27858744ecd6) Add macos manpage mirror url
### Packaging
- [cd2ba80f](https://git.meli-email.org/meli/meli/commit/cd2ba80f8e5424be08421b4dcc5113977418f240) Update metadata
- [5f8d7c80](https://git.meli-email.org/meli/meli/commit/5f8d7c8039c0623b3950fd1a8eb566f943fc309d) Update deb-dist target command with author metadata
- [59c99fdc](https://git.meli-email.org/meli/meli/commit/59c99fdc79bb31fb42cb99d4b95613022396a499) Update debian package metadata
### Miscellaneous Tasks
- [6506fffb](https://git.meli-email.org/meli/meli/commit/6506fffb9427ba13ba4368cd6b2c0dba12e5294c) Rewrite email flag modifications
- [23507932](https://git.meli-email.org/meli/meli/commit/23507932f94257a71f2ca8db23840ee0716072b6) Update cache on set_flags
- [470cae6b](https://git.meli-email.org/meli/meli/commit/470cae6b885c9b4851195fbb8274b1663bfa75cb) Update thread cache on email flag modifications
- [c1c41c91](https://git.meli-email.org/meli/meli/commit/c1c41c9126005266f00d4979777718463dddf7b2) Update README.md and add Codeberg mirror
- [84f3641e](https://git.meli-email.org/meli/meli/commit/84f3641ec1401a0522811add0ed87a131be449b9) Re-add on-screen message display
- [54d21f25](https://git.meli-email.org/meli/meli/commit/54d21f25fdb716d36fd3678dd149eb880e16698d) Re-add contact list and editor support
- [458258e1](https://git.meli-email.org/meli/meli/commit/458258e1aab91f3883d6a9201a175462511349e9) Re-enable compact listing style
- [1c1be7d6](https://git.meli-email.org/meli/meli/commit/1c1be7d6c9bfc9f14c3a62ce464e1e15f2e6c4ec) Add display_name(), display_slice(), display_name_slice() methods
- [5dd71ef1](https://git.meli-email.org/meli/meli/commit/5dd71ef1cd93aebaadb0554eac692d0a0fa4aecd) Upgrade JobsView component to new TUI API
- [b5cc2a09](https://git.meli-email.org/meli/meli/commit/b5cc2a095f0268bb90cab150e903b0bbaffe1479) Upgrade MailboxManager component to new TUI API
- [ed8a5de2](https://git.meli-email.org/meli/meli/commit/ed8a5de2cb4b93ad766803d3590f7041f28cc419) Re-enable EditAttachments component
- [77a8d9e2](https://git.meli-email.org/meli/meli/commit/77a8d9e2c2094e84e06f5d624cb6f8afda24a400) Make ModSequence publicly accessible
- [64898a05](https://git.meli-email.org/meli/meli/commit/64898a0583e348fef3cd266a7196425e7015a871) Make UIDStore constructor pub
- [10c3b0ea](https://git.meli-email.org/meli/meli/commit/10c3b0eabe1684699c775e03c4c58038ea7979af) Bump version to 0.8.5-rc.1
- [71f3ffe7](https://git.meli-email.org/meli/meli/commit/71f3ffe740276087f20d85d62440ef5d3fe426f6) Update Makefile
- [63a63253](https://git.meli-email.org/meli/meli/commit/63a63253d77f6e1b9a42ec55ecf0bbc45a011245) Use type alias for c_char
- [c751b2e8](https://git.meli-email.org/meli/meli/commit/c751b2e8450aa83b7a8f5e8afbeccadf333f74ba) Re-enable conversations listing style
- [d16afc7d](https://git.meli-email.org/meli/meli/commit/d16afc7d8d9e2eddb81664673e9a4ef82da2e303) Bump version to 0.8.5-rc.2
- [da251455](https://git.meli-email.org/meli/meli/commit/da251455a0185e207e0ec2d51273f6ddbdb572a8) Bump meli version to 0.8.5-rc.2
- [3a709794](https://git.meli-email.org/meli/meli/commit/3a7097948308981204132a0eed2d28338f9d6b33) Update minimum rust version from 1.65.0 to 1.68.2
- [f900dbea](https://git.meli-email.org/meli/meli/commit/f900dbea468e822c5a510a72ecc6367549443927) Use cargo-derivefmt to sort derives alphabetically
- [5ff4e8ae](https://git.meli-email.org/meli/meli/commit/5ff4e8ae68182db8d4535d8537d26a3f398c815b) Run builds.yaml when any manifest file changes
- [0a617410](https://git.meli-email.org/meli/meli/commit/0a617410ec1ce5f6fb43772e4ad43f45f58a7f4d) Split test.yaml to test.yaml and lints.yaml
- [3ba1603a](https://git.meli-email.org/meli/meli/commit/3ba1603af2a9e408659717b9c8dace7406a8b142) Add manifest file only lints workflow
- [1617212c](https://git.meli-email.org/meli/meli/commit/1617212c5b0948174155ece4a9d0584764bd7dac) Add scripts/check_debian_changelog.sh lint
- [e19f3e57](https://git.meli-email.org/meli/meli/commit/e19f3e572c0ac585a6c2023e50f8fd0bd2ea2dae) Cargo-sort all Cargo.toml files
- [c41f35fd](https://git.meli-email.org/meli/meli/commit/c41f35fdd55bf093656b68cc69eab4cf4b9a8ec4) Use actions/checkout@v3
- [876616d4](https://git.meli-email.org/meli/meli/commit/876616d45b7798131ecdda82bb90d1d481842f5c) Use actions/upload-artifact@v3
- [2419f4bd](https://git.meli-email.org/meli/meli/commit/2419f4bd40fb1a732cf1df42dde48ba8ca812072) Add debian package build workflow
## [v0.8.4](https://git.meli-email.org/meli/meli/releases/tag/v0.8.4) - 2023-11-22
### Fixed
- [ef30228e](https://git.meli-email.org/meli/meli/commit/ef30228e08efe6e36ab9858a5ba32876d6d8fdae) Fix failing test
### Miscellaneous Tasks
- [f81a1e23](https://git.meli-email.org/meli/meli/commit/f81a1e23382208390394be71e3aaa27ee505cb0f) Bump version to 0.8.4
## [v0.8.3](https://git.meli-email.org/meli/meli/releases/tag/v0.8.3) - 2023-11-22
### Added
- [3105a037](https://git.meli-email.org/meli/meli/commit/3105a0373b8754f37b326239c1cf7129fae06e1b) Add quit command
### Fixed
- [d3cbf184](https://git.meli-email.org/meli/meli/commit/d3cbf184e606d5b7ade9cfb125db01f45d7180ae) Add extra_submission_headers fields in composer form and autocomplete for Newsgroups
- [7aec5b8e](https://git.meli-email.org/meli/meli/commit/7aec5b8e78d80e7717a9aedd7344db6b108534f5) Fix SMTP example doc
- [f702dc22](https://git.meli-email.org/meli/meli/commit/f702dc220c9ab97ce0fddfae194d5e2935a20193) Fix new clippy lints.
- [688e39a6](https://git.meli-email.org/meli/meli/commit/688e39a67e6a467ca649acbe20b1f368fbc1e9f0) Fix clippy lints
### Changed
- [5a7919bb](https://git.meli-email.org/meli/meli/commit/5a7919bb03641be6d7bc5b9002d44e16ee358f12) Use ConversationsListing::format_date
- [0f3b5294](https://git.meli-email.org/meli/meli/commit/0f3b52945959b53c8d809eb434a91ec4c561b2d4) Hoist format_date() to ListingTrait method
### Refactoring
- [e1b55340](https://git.meli-email.org/meli/meli/commit/e1b55340fa258a2a7b118fd18c11614fb2b5e173) Show error description when TIOCGWINSZ ioctl fails
- [e95c275d](https://git.meli-email.org/meli/meli/commit/e95c275d68fe3dbd588046c110ae8b3fa966f6de) Remove duplicate end sequence
- [8a21be21](https://git.meli-email.org/meli/meli/commit/8a21be21775cb474a6b65e1c0bffd771c0df6f2f) Replace splice with truncate
- [2db021fa](https://git.meli-email.org/meli/meli/commit/2db021fa0a9a707cd7cdb6c8bf140bf5c8acf906) Remove regexp from default features
- [fa33a946](https://git.meli-email.org/meli/meli/commit/fa33a9468a16c50361353efa269fca79bd58e284) Move managesieve-client binary to tools/
### Miscellaneous Tasks
- [e88957ae](https://git.meli-email.org/meli/meli/commit/e88957ae6edfee7fabb41e9210f9d906866cda8d) Add extra_submission_headers field in MailBackendCapabilities struct
- [606f487f](https://git.meli-email.org/meli/meli/commit/606f487fc5e227f1727697a5911e27cbec174089) Add IRC channel badge
- [0e60bdf2](https://git.meli-email.org/meli/meli/commit/0e60bdf26eb842744f59257800ca8e30b1a43836) Add "iterator" feature to signal-hook
- [ac2a5dcd](https://git.meli-email.org/meli/meli/commit/ac2a5dcdd10d97f5ed9c8a8c83e1641b373dd31a) Add display() method for Address
- [43bfd413](https://git.meli-email.org/meli/meli/commit/43bfd4131d5cab39319d1943bcad46e929ec4d56) Update ahash dependency
- [af241d25](https://git.meli-email.org/meli/meli/commit/af241d25cbab20227a88ec4d557222cdeed98dde) Bump version to 0.8.3
- [7387b67e](https://git.meli-email.org/meli/meli/commit/7387b67eeee27aefbc4d20ca2a1d503aa0fb1838) Enable "static" build for C library dependencies by default
- [bfc78a08](https://git.meli-email.org/meli/meli/commit/bfc78a0803524e236bc883833838d3ad78918621) Replace CRLF with LF when editing
- [111a1160](https://git.meli-email.org/meli/meli/commit/111a1160adf2e0fef00a90350784307c859a198b) Bump version to 0.8.3
## [v0.8.2](https://git.meli-email.org/meli/meli/releases/tag/v0.8.2) - 2023-09-22
### Fixed
- [73b3ed55](https://git.meli-email.org/meli/meli/commit/73b3ed559d21dcc7cdee7f96119461e2447c1906) Fix forward dialog not workng
- [7888d8b2](https://git.meli-email.org/meli/meli/commit/7888d8b2a5dc977f0f18094a32dc73893a5cfc4f) Fix doc test compilation
### Changed
- [22525d40](https://git.meli-email.org/meli/meli/commit/22525d40fb48661f86657151e35fdf9c95c4b45e) Go to end when pressing next/page down for the second time
- [71474436](https://git.meli-email.org/meli/meli/commit/714744366f5e26fc1b6609e8e785d64489f9a68d) Revert 22525d40 behavior when sidebar not focused
### Miscellaneous Tasks
- [eb5d49c4](https://git.meli-email.org/meli/meli/commit/eb5d49c41ac58c5068011620c22e21b5fa115417) Use Self in self methods
- [3d85ca2e](https://git.meli-email.org/meli/meli/commit/3d85ca2edfca9abff4b3ffdd837b25e68c6586c2) Bump version to 0.8.2
## [v0.8.1](https://git.meli-email.org/meli/meli/releases/tag/v0.8.1) - 2023-09-13
### Added
- [6476985c](https://git.meli-email.org/meli/meli/commit/6476985ce6abbb9048ba5aec19f6c5144bfe89b7) Add Cross.toml for aarch64-unknown-linux-gnu builds
- [45d4f611](https://git.meli-email.org/meli/meli/commit/45d4f611b170d7b80afca5810c51fea1bf084c10) Add install-man cli subcommand to install manpages on your system
- [a4f0dbac](https://git.meli-email.org/meli/meli/commit/a4f0dbac26126c03886115e518b3cd2ede0b88cb) Add current working directory tracking to Context
### Fixed
- [49a38a23](https://git.meli-email.org/meli/meli/commit/49a38a23bf522a18e636385632cfe3533c4f525c) Fix invalid Type link references
- [85af5244](https://git.meli-email.org/meli/meli/commit/85af524458bc06421ac39689469474efb8164c1c) Fix invalid mailto() results when body field exists
- [c7825c76](https://git.meli-email.org/meli/meli/commit/c7825c76c3ac6be89f64f1f04afd9c0ca08bdf76) Handle dialog Esc in the parent component
- [dd4d0b79](https://git.meli-email.org/meli/meli/commit/dd4d0b79721d8cd5b29cdaca9cd01412974f2e13) Fix typo
- [c43aeb0e](https://git.meli-email.org/meli/meli/commit/c43aeb0eb103f2a8fd802f84eab56551c6e65418) Fix invalid address parse on folded values
- [7e3e9386](https://git.meli-email.org/meli/meli/commit/7e3e9386316ef344580d9e44edb3f8b0c196c3c5) Fix out-of-bounds draw when terminal is small
- [7e4ed2fa](https://git.meli-email.org/meli/meli/commit/7e4ed2fa107eca2ef309bcaa211440c315730b6c) Fix some out of bounds drawing.
### Changed
- [1b3bebe3](https://git.meli-email.org/meli/meli/commit/1b3bebe3049ae5c7cb2210ed95c355c9b5c709f8) Open earliest unread email instead of first in thread
- [49c36009](https://git.meli-email.org/meli/meli/commit/49c36009cec8c88d61d796162787990216bfeeab) Don't initialize entire thread at once
- [0a9c89b6](https://git.meli-email.org/meli/meli/commit/0a9c89b6b357fc3d002c3eb451fd67e7a49ce7f5) Add toggle_layout shortcut
- [64ba0459](https://git.meli-email.org/meli/meli/commit/64ba0459ee3652eaf451d10222853a898d85e337) Init cursor at To: header field
- [81974311](https://git.meli-email.org/meli/meli/commit/81974311c200b8ad66c0e626f8b8db6686e565ff) Show current number command buffer
### Refactoring
- [a337e226](https://git.meli-email.org/meli/meli/commit/a337e2269e584769314cdf325cdeb6e57cb0c622) Refactor module structure
- [b4f2f335](https://git.meli-email.org/meli/meli/commit/b4f2f3357613729e493e5f41a48def7610dc65aa) Remove deflate feature; make it a hard dependency
- [2dc29405](https://git.meli-email.org/meli/meli/commit/2dc29405868b9df0dfff25e341814526a478db00) Add feature to use cache instead of downloading unicode data
- [0132677f](https://git.meli-email.org/meli/meli/commit/0132677ff54a9618d3c59b08a188b73ae0c062c7) Introduce CommandError with context
- [3344a8db](https://git.meli-email.org/meli/meli/commit/3344a8dbf6b478a85d2b933fc1fa1a6001c600f4) Remove unnecessary Clone derives
- [b673af02](https://git.meli-email.org/meli/meli/commit/b673af02ac9e9d4be95daa2490ce24d0bc9b10d9) Move to crate root
- [54862f86](https://git.meli-email.org/meli/meli/commit/54862f8651cb7dfe3bca7f5924fe776b93ac6aee) Add hide_sidebar_on_launch option
### Miscellaneous Tasks
- [a615b470](https://git.meli-email.org/meli/meli/commit/a615b4701b7e852a9112b317e2e31997c6cbe82e) Embed xdg-utils crate
- [f0075b86](https://git.meli-email.org/meli/meli/commit/f0075b86cf636a3d39d4edf1ff6d58c112bbecf7) Show descriptive tab names for composer and threads
- [6d5ebb5b](https://git.meli-email.org/meli/meli/commit/6d5ebb5b04279fe6e4fbf598504cae2f012fa494) Split code into submodules, add better error reporting
- [63abf1e8](https://git.meli-email.org/meli/meli/commit/63abf1e890b93fcadf35f88b3dbea473c0d8f5cd) Update README.md
- [bb4d2000](https://git.meli-email.org/meli/meli/commit/bb4d20003690d72b62a66d46a1fc5ae914e2bf64) Unify toggle_* parsers
- [9b9c38f7](https://git.meli-email.org/meli/meli/commit/9b9c38f769abae0ff86e4b71e4db0ad65fdacfb4) Don't flood user with sqlite3 errors if db is corrupted
- [747e39bf](https://git.meli-email.org/meli/meli/commit/747e39bf55cfc19b6eeece3ca7c71bad98d92389) Add print-used-paths subcommand
- [39e99770](https://git.meli-email.org/meli/meli/commit/39e99770da4b51d0986a4b561fbe36b27d04565d) Use Context::current_dir() when saving files to relative paths
- [fe0a96f0](https://git.meli-email.org/meli/meli/commit/fe0a96f0855486207280430064a93cab94dffeb2) Update to 2021 edition
- [3944e4e6](https://git.meli-email.org/meli/meli/commit/3944e4e60e431247eefc0b3cf35af27fb011f37b) Update to 2021 edition
- [7eed8278](https://git.meli-email.org/meli/meli/commit/7eed82783a3dbac513e233be4f0bce06904fe8c8) Bump version to 0.8.1
## [v0.8.0](https://git.meli-email.org/meli/meli/releases/tag/v0.8.0) - 2023-08-29
### Added
- [36e29cb6](https://git.meli-email.org/meli/meli/commit/36e29cb6fd00c798ad83e3064e0ff78c8153dced) Add configurable mailbox sort order
- [81184b18](https://git.meli-email.org/meli/meli/commit/81184b182c5f5d65614653b817981fddc6a84ffa) Add extra_identities configuration flag
- [b716e438](https://git.meli-email.org/meli/meli/commit/b716e4383ea3163cabe760cd5512b7d70b218915) Add collapse option for mailboxes in sidebar menu
- [3d92b410](https://git.meli-email.org/meli/meli/commit/3d92b41075fc16214675cf141acd9c89fb6f5c49) Add cli-docs feature to the default set
- [104352e5](https://git.meli-email.org/meli/meli/commit/104352e5950598f4a659bd593d587910af8adc12) Add table UI widget
- [7d9cabb0](https://git.meli-email.org/meli/meli/commit/7d9cabb023b510e6175fd6b2523f0414a6da1f3f) Add mailbox manager tab
- [660bacb9](https://git.meli-email.org/meli/meli/commit/660bacb9262dac7457bd8c421cc70343a0db3cd5) Add `mailto` command to open composer with initial values from mailto template
- [3adf72ae](https://git.meli-email.org/meli/meli/commit/3adf72aed0772fea39fbd6cbaec680fb2995e92d) Add support for utf-7 encoding
- [d9c07def](https://git.meli-email.org/meli/meli/commit/d9c07def0f5db655aa11c5981d1419a336c3d91a) Add command to select charset encoding for email
- [8c671935](https://git.meli-email.org/meli/meli/commit/8c671935f9ad5bd2894c0ecdaec9c2f378e461ca) Add compose (pre-submission) hooks for validation/linting
- [96537e48](https://git.meli-email.org/meli/meli/commit/96537e48c5f5c8d54076ec5db76e94a499cbe1e6) Add {Timer,Component}Id wrapper types over Uuid
- [b5f205b7](https://git.meli-email.org/meli/meli/commit/b5f205b77b8911a1fb6019767bb026e5f4a7f79e) Add availability to use server_password_command in the nntp backend like in the IMAP backend
- [a5770c89](https://git.meli-email.org/meli/meli/commit/a5770c89f46b908d17d6eb4573c8337a952f99a8) Add Woodpecker-CI check pipeline
- [d4e605c0](https://git.meli-email.org/meli/meli/commit/d4e605c098ba13b8bc2d9f14d07ea45da38e9a2f) Add tagref source code annotations
- [cf9a04a5](https://git.meli-email.org/meli/meli/commit/cf9a04a5910c9d82e1acb10a2f4d40c2af0335ed) Add metadata to Jobs, and add JobManager tab
- [bb7e119a](https://git.meli-email.org/meli/meli/commit/bb7e119ade131e8fe1bcac39b616741af817808c) Add gitea CI workflows
- [1c79786e](https://git.meli-email.org/meli/meli/commit/1c79786ea210e53ee7d566455d83d74fe4699d28) Add scripts/make_html_manual_page.py
- [65e82d88](https://git.meli-email.org/meli/meli/commit/65e82d8896500e8ef586656e3bde4bc102b84aba) Add meli/README.md symbolic link
### Fixed
- [ce2068d3](https://git.meli-email.org/meli/meli/commit/ce2068d36bb5d8ad0bb8f886bc19cb4aab75c4e8) Fix background watch using JSON paths incorrectly
- [e9aaa7b0](https://git.meli-email.org/meli/meli/commit/e9aaa7b067903040acd7f3d7c685de94b3b98450) Use *const c_char instead of *const i8 for portability
- [aa3524dd](https://git.meli-email.org/meli/meli/commit/aa3524dd305f2cf293eaaf7120b812478255f79c) Fix tag not being removed in set_flags()
- [daa900ec](https://git.meli-email.org/meli/meli/commit/daa900ec9a566460833c020feba10933c0248162) Fix embed terminal in macos
- [7fca5f01](https://git.meli-email.org/meli/meli/commit/7fca5f01ef53069958403dd794ee0e5c310f4e45) Fix jmap build with isahc 1.7.2
- [ed3dbc85](https://git.meli-email.org/meli/meli/commit/ed3dbc85861ab61fee56077c7ba94306b0a96dc4) Fix crashes when listing is empty
- [824f614a](https://git.meli-email.org/meli/meli/commit/824f614a69e55a25d67832593cb8aadb9671e306) Fix HtmlView not being redrawn when parent is dirty
- [97ff3e78](https://git.meli-email.org/meli/meli/commit/97ff3e787fbfb5ff50e3ba787f067829509f7cd2) Only add toml files to the themes
- [9cb66ef8](https://git.meli-email.org/meli/meli/commit/9cb66ef818f6598eb779f931e201a8d38e86a484) Fix all clippy warnings in `meli` crate
- [0c0bee44](https://git.meli-email.org/meli/meli/commit/0c0bee4482d4fbfa675b97ca30405fdc77655936) Add missing .PHONY targets, fix missing tab indentation
- [a73885ac](https://git.meli-email.org/meli/meli/commit/a73885acb14cd94d4a6a54ebd5b39a001d7e21e1) Improve embed terminal
- [da9c80cc](https://git.meli-email.org/meli/meli/commit/da9c80ccfd7aa87842c2c3c089ba2b784a583ab6) Enhance SubjectPrefix with strip_prefixes_from_list() method
- [aa99b0d7](https://git.meli-email.org/meli/meli/commit/aa99b0d787463be4267913b801117bd4d2ea5003) Implement configurable subject prefix stripping when replying
- [cbe593cf](https://git.meli-email.org/meli/meli/commit/cbe593cf31308dcf549d7880eea2d82e5024dd73) Add configurable header preample suffix and prefix for editing
- [2de69d17](https://git.meli-email.org/meli/meli/commit/2de69d17f14e79ce2a35564d278b5e895d16a48f) Fix erroneous placement of newlnes for wrap_header_preamble suffix
- [94bd84b4](https://git.meli-email.org/meli/meli/commit/94bd84b45d53b0e0fae52198fbdc05179b87cccc) Fix clippy lints for `meli` crate
- [b138d9bc](https://git.meli-email.org/meli/meli/commit/b138d9bc6166b763febf035b50109d810e3c18c9) Fix some clippy lints
- [c6bdda03](https://git.meli-email.org/meli/meli/commit/c6bdda03cf451ab52a3d414cad1344bb32c82879) Fix notmuch error shown on any missing backend
- [16646976](https://git.meli-email.org/meli/meli/commit/16646976d75284665c1fa0d7b7e3e3cde3531d66) Fix reply subject prefixes stripping original prefix
- [88a1f0d4](https://git.meli-email.org/meli/meli/commit/88a1f0d4bc17b60f8f23ea71f33a81aee78f8769) Fix FETCH response parsing bug
- [59b95f83](https://git.meli-email.org/meli/meli/commit/59b95f83d2b388b30a3a855f68bf5952355597d7) Fix docs
- [282af86e](https://git.meli-email.org/meli/meli/commit/282af86e83807772f042b115af24ffe2e0575b9e) Fix NAME sections manual pages for correct whatis(1) parsing
- [bd22f986](https://git.meli-email.org/meli/meli/commit/bd22f986f0c06f6dae535733d484aa89f610ed46) Fix clippy lints
- [5ba7b2cd](https://git.meli-email.org/meli/meli/commit/5ba7b2cd7bb07abe8faafe5e45db6145b3f90bc9) Fix clippy lints for meli binary
- [7924aa8b](https://git.meli-email.org/meli/meli/commit/7924aa8bfe8f0fbcd557bb8bb3a9d3ebeab2220a) Fix compilation
- [b9030a68](https://git.meli-email.org/meli/meli/commit/b9030a684c0ad64951a388e49d5825c12b483fb4) Fix selection not appearing immediately and invalid motions
- [4f45b109](https://git.meli-email.org/meli/meli/commit/4f45b109745ebc29febc452b9bcb0cd88f131ffc) Fix tag updates not showing up right away
- [abc56eae](https://git.meli-email.org/meli/meli/commit/abc56eae431153d2e48f8b1eb3e0d2a140b600d8) Fix SEEN flag update hiding mail view momentarily
- [40c6647d](https://git.meli-email.org/meli/meli/commit/40c6647db83c5137b79c9bec233972a8a78aeb76) Fix multipart/related with main text/html part not displayed correctly
- [11140b4a](https://git.meli-email.org/meli/meli/commit/11140b4a76419a6f8c83db38823e83aeac8fbb98) Fix test output
- [3a10953f](https://git.meli-email.org/meli/meli/commit/3a10953f05ea4944a8a20c2c5d647d5862dca907) Update fix-prefix-for-debian.patch
- [939dc15e](https://git.meli-email.org/meli/meli/commit/939dc15e289e06a0fad72e44f9e91133892a4ec0) Fix melib tests
- [39d9c2af](https://git.meli-email.org/meli/meli/commit/39d9c2af3b7daf39c6aa7eab5f2d95f1b9c3a562) Fix test smtp server logic
- [34bb532e](https://git.meli-email.org/meli/meli/commit/34bb532e8d91c5f35bdc058821da63ac543ecfa6) Mention w3m dependency
- [b1a71887](https://git.meli-email.org/meli/meli/commit/b1a71887710153f0f98b25b2f224fbe37f7a6889) Clippy fixes
- [1f8ac228](https://git.meli-email.org/meli/meli/commit/1f8ac2287b960e0ed5c44dadbf68b924f035d321) Fix ftplugin location and add example mail.vim file
- [1eea8bab](https://git.meli-email.org/meli/meli/commit/1eea8bab77cc20fb911f13aa16322a217b36b06b) Fix `test_imap_fetch_response`.
- [daf42fd4](https://git.meli-email.org/meli/meli/commit/daf42fd456bad5ddf65ac515c2fb277896d1fea3) Fix build error with quote 1.0.28
- [6388bea9](https://git.meli-email.org/meli/meli/commit/6388bea9a063f776398ffc503fdb0789ce9af9f1) Fix &[u8] index in HeaderMap
- [c5ecacea](https://git.meli-email.org/meli/meli/commit/c5ecaceae1ab50a1c337f5cab9e97c0b061cb2d5) Fix some search criteria in Query type
- [27a4dcb9](https://git.meli-email.org/meli/meli/commit/27a4dcb916e0bed723490df9d82bfd7c83f10a83) Fix some rustdoc lints
- [fdc0861a](https://git.meli-email.org/meli/meli/commit/fdc0861ac0ac725e6e5031d120bd4682752c0267) Fix expanded_hash argument off by one error
- [0c0a678c](https://git.meli-email.org/meli/meli/commit/0c0a678cffec73940065923bb3837deb85075f9f) Fix overlay widgets not being reaped after Unrealize event
- [65179d48](https://git.meli-email.org/meli/meli/commit/65179d4816a39b0c92e9c6a981b491c60313634f) Fix cursor/widget focus scrolling logic
- [e64923ee](https://git.meli-email.org/meli/meli/commit/e64923eeaaf1fdf0ee485cceff0c57b2d43f165a) Fix debug_assert condition
- [5f29faa6](https://git.meli-email.org/meli/meli/commit/5f29faa640ebe7b14e76e56227a482207b8d952e) Clippy lint fixes
- [0b258a1f](https://git.meli-email.org/meli/meli/commit/0b258a1f058fa08b143a8e573883a4abe89dc7e1) Clippy lint fixes
- [ba7f5dce](https://git.meli-email.org/meli/meli/commit/ba7f5dce1c37c04768aa060b35f3803e6db3840e) Fix display of threaded conversations tree structure
- [1dc1d868](https://git.meli-email.org/meli/meli/commit/1dc1d86848eb6d187120bcaa00296f2b4e2025ca) Fix infinite loop bug
- [e8e49e74](https://git.meli-email.org/meli/meli/commit/e8e49e741b0f888d44da69f52aa3fff2e03e7ced) Fix wrong per message offset
- [e3dfeaad](https://git.meli-email.org/meli/meli/commit/e3dfeaad7e4f838af5fb2e6e398d3e1aa37fe511) Fix compilation error when building without `gpgme` feature
- [7998e1e7](https://git.meli-email.org/meli/meli/commit/7998e1e77ef057bab28434edefb79d7be6a4de33) Add missing LC libc constants for openbsd target_os
- [b5657201](https://git.meli-email.org/meli/meli/commit/b5657201db4828c6e61c52e7ce338ac1a6e6f9fc) Fix doctest compilation errors
- [c2ed3e28](https://git.meli-email.org/meli/meli/commit/c2ed3e283f6729ac7e112d00ae54dd99a2ada5e6) Fix Source::* view showing only envelope body
- [d93ee413](https://git.meli-email.org/meli/meli/commit/d93ee413a766f35a4ef88d9fc3ace9cf37d28dd1) Add timestamp_to_string_utc
- [6086a378](https://git.meli-email.org/meli/meli/commit/6086a3789d4d01818322dab1f1a9eb4c1f6a2b25) Fix libgpgme segfault error and re-enable gpg
- [ab418c1d](https://git.meli-email.org/meli/meli/commit/ab418c1d39d02840bc5c61996c1a5416e2f35464) Refresh documentation, fix encryption/signing
- [0219dc87](https://git.meli-email.org/meli/meli/commit/0219dc870798a16fd4d9f546d14c115f9e2c6bd8) Respect max_objects_in_get when fetching email
- [6280bc75](https://git.meli-email.org/meli/meli/commit/6280bc75e550332a73c1a51dd46475cd54cc0a34) Fix blob download URL formatting
- [2df73547](https://git.meli-email.org/meli/meli/commit/2df73547515fd3464e1fc2b88aa67462f583a8ec) Fix overflow substracts
- [8e698cab](https://git.meli-email.org/meli/meli/commit/8e698cabcfe58ddd566133ba2c33249c23180a74) Fix unreachable-pub and disjoint-capture lint errors
- [40d4ecef](https://git.meli-email.org/meli/meli/commit/40d4ecefa013caaa13af493233c693fb495360ca) Accept invalid (non-ascii) address comment text
- [4e654d2d](https://git.meli-email.org/meli/meli/commit/4e654d2d02044be7340b63f1250d37b2ca57b221) Limit LIST ACTIVE command length to 512 octets
- [84081f4e](https://git.meli-email.org/meli/meli/commit/84081f4ed7570dd8bcc23d90b9c4cbff55620636) Minor style fix
- [97d36868](https://git.meli-email.org/meli/meli/commit/97d3686815c011bb8f1d4e448f12b2294693730d) Use Happy Eyeballs algorithm Ꙭ
- [96f0b3e6](https://git.meli-email.org/meli/meli/commit/96f0b3e6b484c9cbb7eaddcaad2b59811b733545) Fix shortcut section order
- [64982b4c](https://git.meli-email.org/meli/meli/commit/64982b4cab0b0c2d396cb5dcf7add6f268fd4551) Fix page{up,down} event bubbling up
- [8551e1ba](https://git.meli-email.org/meli/meli/commit/8551e1ba0b4fa6d9587bbb249f11e9b80d24e4d3) Fix new 1.72 default clippy lints
### Changed
- [8563bccd](https://git.meli-email.org/meli/meli/commit/8563bccd1b6d48dc06dd521f77228c3cbecf7613) Don't cache CellBuffer, only row info
- [0f6f3e30](https://git.meli-email.org/meli/meli/commit/0f6f3e30c67f209e0b5e03d2dd2e1e48180d9855) Add IMAP config in config parse test
- [ce269c64](https://git.meli-email.org/meli/meli/commit/ce269c64e16db344f0e65461e56dbced2f1a4d64) Don't fail on `server_password_command`
- [9dc4d405](https://git.meli-email.org/meli/meli/commit/9dc4d4055cb2f854e835748315677bf4a2db2012) Add focus_{left,right} shortcuts to switch focus
- [4b96bd59](https://git.meli-email.org/meli/meli/commit/4b96bd591f18bf7c8a3c922d469b81072d1782a2) Add ColorCache constructor to deduplicate code
- [c06c3f58](https://git.meli-email.org/meli/meli/commit/c06c3f589315f017a412f31d80559a5a734d7b89) Draw gap between list and mail view
- [c9d26bb4](https://git.meli-email.org/meli/meli/commit/c9d26bb4158e2f423c795f82bcb2c91a0f0c46ec) Add configurable custom hooks with shell commands
- [02e86d1f](https://git.meli-email.org/meli/meli/commit/02e86d1fade9faefc14b890e3cec8ed2255bb839) Check for subject overflow on draw
- [8cab9d9d](https://git.meli-email.org/meli/meli/commit/8cab9d9da8710257f2b62832bfac802c2a35b368) Add option to hide consecutive identical From values inside a thread
- [363f4930](https://git.meli-email.org/meli/meli/commit/363f4930994d1d2e88220878b3848f176b8c5f97) Add {previous,next}_entry shortcuts to quickly open other mail entries
- [342df091](https://git.meli-email.org/meli/meli/commit/342df091a076bce1f8477dabbad193312d8cdd67) Don't set all thread to seen when opening a thread entry
- [74e15316](https://git.meli-email.org/meli/meli/commit/74e15316dbbf67254023e619924e522f80e77cb9) Open message/rfc822 attachments in subview instead of new tab
- [369c1dbd](https://git.meli-email.org/meli/meli/commit/369c1dbdac9842746270a3d3c5bf7ed2205cb644) Show `open` command in status bar
- [519257b0](https://git.meli-email.org/meli/meli/commit/519257b08f7029fe71efd2f61ab3a29a4b43b862) Add relative_menu_indices setting for menubar
- [8abc9358](https://git.meli-email.org/meli/meli/commit/8abc9358a70465b12a11168be1718ab06479d6e2) Add newline after Version: 1 header
- [561ba9c8](https://git.meli-email.org/meli/meli/commit/561ba9c87b57e1012ad89bde08506a2beacb7fff) Add relative_list_indices setting for thread listing
- [52874f9a](https://git.meli-email.org/meli/meli/commit/52874f9a97a4799fcff2e14c43cafe9692f21cb6) Cancel previous jobs on MailView drop/update
- [9037f084](https://git.meli-email.org/meli/meli/commit/9037f08495894c15a7817594ba91e0d5561c6e69) Replace hardcoded Key::{Home,End} values with shortcut values
- [31aa9ad2](https://git.meli-email.org/meli/meli/commit/31aa9ad29e33f285314d0d320a02f00071f61282) Autogen mbox filename when exporting mail to directories
### Refactoring
- [330a2b20](https://git.meli-email.org/meli/meli/commit/330a2b20ed492f6b6ea86c196d43d67430487faa) Flush stdout in Ask() after printing
- [340d6451](https://git.meli-email.org/meli/meli/commit/340d6451a330861af09fd02231c17ba4168d9654) Add config setting for sidebar ratio
- [d0de0485](https://git.meli-email.org/meli/meli/commit/d0de04854ec4770b54e4d8303a9b8ab9eb5d68b0) Add {in,de}crease_sidebar shortcuts
- [f5dc25ae](https://git.meli-email.org/meli/meli/commit/f5dc25ae0d5b8d6fb15a534fa49557385d6894d0) Check that all conf flags are recognized in validation
- [d3e62e3d](https://git.meli-email.org/meli/meli/commit/d3e62e3d74bdc55872bbdf92c01d18aa00b0affd) Use conf shortcuts for scroll {up, down}
- [23c23556](https://git.meli-email.org/meli/meli/commit/23c2355662d589c091dd3c86c8d91c7988eb941c) Fill and align shortcut table columns
- [5823178c](https://git.meli-email.org/meli/meli/commit/5823178cc26f66ba902a901522f0506b4348b22e) Add test that looks in source code for invalid theme key references
- [9205f3b8](https://git.meli-email.org/meli/meli/commit/9205f3b8afe28ef3a68959d590ed967946a5d622) Handle a per account mail order parameter
- [d921b3c3](https://git.meli-email.org/meli/meli/commit/d921b3c3209ff7fe865b5a3b90e20098b3ff211f) Use mail sorting parameters from config
- [f4e0970d](https://git.meli-email.org/meli/meli/commit/f4e0970d46e3ec73d684e2ddcc5011f61e87314d) Add ability to kill embed process
- [bde87af3](https://git.meli-email.org/meli/meli/commit/bde87af3877d4a0b071e331c93a07e0acf51bf7a) Refactor filter() method in Listing trait
- [a42a6ca8](https://git.meli-email.org/meli/meli/commit/a42a6ca868e4590a8b93560737173e80993ecaec) Show notifications in terminal if no alternative
- [eb5949dc](https://git.meli-email.org/meli/meli/commit/eb5949dc9bbcf05f86c58b3c93d1066204313e2a) Switch summary<->details identifiers
- [8c7b001a](https://git.meli-email.org/meli/meli/commit/8c7b001aa5d4cb6bbaf438f3f47cd91cc2fd6833) Add `thread_subject_pack` command to pack different inner thread subjects in entry title
- [388d4e35](https://git.meli-email.org/meli/meli/commit/388d4e35d65f8f770526c4c5f44767c55eda23f8) Add in-progress messages while connecting in IMAP
- [787c64c2](https://git.meli-email.org/meli/meli/commit/787c64c2da8af5cc0dafcb92c1d3bea6b54f3659) Remove expect()s from create_config_file()
- [b87d54ea](https://git.meli-email.org/meli/meli/commit/b87d54ea3f3f077b6330e798263be6a3d33b3b9c) Impl Into<BTreeSet<EnvelopeHash>> for EnvelopeHashBatch
- [e450ad0f](https://git.meli-email.org/meli/meli/commit/e450ad0f9cbc2d215a8f03d2d39260abe19fb5af) Remove unused struct
- [c54a31f7](https://git.meli-email.org/meli/meli/commit/c54a31f7cca728eec87f7cd670a4baec37dc919a) Break line for error messages
- [7935e49a](https://git.meli-email.org/meli/meli/commit/7935e49a00190cc7f2057abe353739c8dad4f74d) Check properly if mailbox request is an error
- [117d7fbe](https://git.meli-email.org/meli/meli/commit/117d7fbe046fe23c400a925ccba7317d8a1d3f08) Make private fields public
- [ffb12c6d](https://git.meli-email.org/meli/meli/commit/ffb12c6d1ae9a774de22a25d38bc6714a435c7ad) Make all public struct fields public
- [46a038dc](https://git.meli-email.org/meli/meli/commit/46a038dc68093b28b69c3af38de4dd09431efae2) Remove interactive messages when #[cfg(test)]
- [803d3414](https://git.meli-email.org/meli/meli/commit/803d3414fd73743ff5bfc0fefe5e3d76d88e58cb) Implement some rfc5804 commands
- [b776409d](https://git.meli-email.org/meli/meli/commit/b776409d6c9caec3732bada9e25637c2676af3b8) Add thread, env hash index fields
- [cc439b23](https://git.meli-email.org/meli/meli/commit/cc439b239ae27ae84fbcf50fbd82ec591c147c94) Add RowsState struct
- [db227dea](https://git.meli-email.org/meli/meli/commit/db227dea34caa747e136500356fddf95a91002e6) Add error messages if `mandoc`,`man` binaries are missing
- [ee9d458b](https://git.meli-email.org/meli/meli/commit/ee9d458b05ffa0214a4526daf1423916830526bc) Implement mailbox {un,}sub actions
- [7af89359](https://git.meli-email.org/meli/meli/commit/7af893597f5a3f3261bfff47dae0723bf1b17e53) Replace use of Self::DESCRIPTION with Shortcuts struct consts
- [eaecc5ea](https://git.meli-email.org/meli/meli/commit/eaecc5ea12f4a5ebe309d5654509c0771bbdc2f1) Remove hardcoded major .so version for non linux/macos target_os
- [f63ce388](https://git.meli-email.org/meli/meli/commit/f63ce388f7774ea015fdaa2362202c33f3ddacd4) Move ManageMailboxes to Tab Actions
- [3c847ad2](https://git.meli-email.org/meli/meli/commit/3c847ad26afcc4a4cdcfbdbf70f35be57d0da1ab) Add beginning of sieve parser
- [5443b7e8](https://git.meli-email.org/meli/meli/commit/5443b7e8f300a0084abde7354360ecbe909178bb) Remove literal_map() parse combinator
- [12cb717b](https://git.meli-email.org/meli/meli/commit/12cb717bda186b0ebdda18e2215e30b1426fb08a) Add server_password_command to jmap
- [428f752b](https://git.meli-email.org/meli/meli/commit/428f752b20cdb1c8ab01e7f3119001cfafca8ef1) Remove obsolete crate::components::mail::get_display_name()
- [91557c2c](https://git.meli-email.org/meli/meli/commit/91557c2c4366b481e80943e94f661c8b47150571) Prevent list blank when refreshing account
- [d332e457](https://git.meli-email.org/meli/meli/commit/d332e4578d69c4371418fb2bb3c0d75e1960e01f) Add proper Display impl for HeaderName
- [f537c249](https://git.meli-email.org/meli/meli/commit/f537c24909d13a53a95b43e265e4cb4c013334ac) Move text field to its own module
- [d33f9d54](https://git.meli-email.org/meli/meli/commit/d33f9d54c708699386a3f32e4056ccab6c68528b) Remove unreachable!() in Key::serialize
- [330887c4](https://git.meli-email.org/meli/meli/commit/330887c4f5bad5357508b9fa6f723e45ab307d2a) Introduce imap-codec.
- [4da53669](https://git.meli-email.org/meli/meli/commit/4da5366959145e166c40297abfdf1876e5addc50) Remove bincode dep, use serde_json for sqlite3 values
- [155fb41b](https://git.meli-email.org/meli/meli/commit/155fb41b93708ef8793250f9dea611bc317a86d5) Remove unused Component::set_id method
- [575509f1](https://git.meli-email.org/meli/meli/commit/575509f1edc756ad218bb76cf74460d83009c851) Move mail view to listing parent component
- [6858ee1f](https://git.meli-email.org/meli/meli/commit/6858ee1fab3bcddbda7335f49c30f36153e8d4b7) Move subcommand handling to its own module
- [b0e867eb](https://git.meli-email.org/meli/meli/commit/b0e867eb68dc3dba96de79f7481989187fa12df4) Move src to meli/src
- [48a10f72](https://git.meli-email.org/meli/meli/commit/48a10f724171bfae702b7b40438189adbbe75079) Remove unused BackendOp::fetch_flags() method
- [073d43b9](https://git.meli-email.org/meli/meli/commit/073d43b9b869fc9d46c5195c31ad6e7806cf486c) Move data files to data subdir
- [1e084c1d](https://git.meli-email.org/meli/meli/commit/1e084c1d854ed7efb2254f9e8d52ac13d8badffa) Move backends out of the backends module
- [a5446975](https://git.meli-email.org/meli/meli/commit/a5446975c2423654dea9551474a880e94ebdc006) Move braille and screen to their own module files
- [005bf388](https://git.meli-email.org/meli/meli/commit/005bf3881ec59d53e4f16473fb3b1857487dae23) Move components/utilities -> utilities
- [64ab65dd](https://git.meli-email.org/meli/meli/commit/64ab65ddffe3341bca775acb2289ee00e771fdb0) Move components/contacts -> contacts
- [7c9a4b4b](https://git.meli-email.org/meli/meli/commit/7c9a4b4b7c366c967a3378098d210124712fd293) Move components/mail -> mail
- [df638cce](https://git.meli-email.org/meli/meli/commit/df638cceec6016760037b650a77143a07cd1e738) Remove stale failing doc code example
- [da8e8104](https://git.meli-email.org/meli/meli/commit/da8e81044833975cadb08db836795a389c142e9c) Remove leftover debug prints
- [a1e70061](https://git.meli-email.org/meli/meli/commit/a1e7006186474f55cf4a14f53dbd32bdf8ca5993) Move Sort{Order,Field} to utils mod
- [66c21ab1](https://git.meli-email.org/meli/meli/commit/66c21ab1734bfbf4e604da505f6b6109008fd7c2) Move StandardHeader to its own module
- [946309c6](https://git.meli-email.org/meli/meli/commit/946309c6f3bbc59b53dc2b05732b40f3d445fd9f) Do some small parser refactoring
- [b95f7783](https://git.meli-email.org/meli/meli/commit/b95f778335bebd480f69fe66fabec4f8a6e2b587) Move JmapSession to its own module
### Documentation
- [a866b294](https://git.meli-email.org/meli/meli/commit/a866b29499b44032545df4941b6cfec4ee2db8bb) Update valid shortcut entries from src/conf/shortcuts.rs
- [f76f4ea3](https://git.meli-email.org/meli/meli/commit/f76f4ea3f7416a4a641d5891f19927aa354a3247) Add meli.7, a general tutorial document
- [5fa4b626](https://git.meli-email.org/meli/meli/commit/5fa4b6260c60409579fe964970719f9ab60482cc) Add more screenshots
- [7c711542](https://git.meli-email.org/meli/meli/commit/7c7115427dd5f6320a4305df3dc88a8567829720) Complete guide document
- [30cc5d3d](https://git.meli-email.org/meli/meli/commit/30cc5d3d0220452630780c3238f393b9e1f2b93a) Add edit-config in manpages
- [24103f33](https://git.meli-email.org/meli/meli/commit/24103f3310ca533791bdd07643fdb23a10c6031d) Add external-tools.md document
- [b6c93e49](https://git.meli-email.org/meli/meli/commit/b6c93e49f2af3001b206a288edea02c58e14aa5b) Add use_tls option in IMAP connection settings
- [34a54d3c](https://git.meli-email.org/meli/meli/commit/34a54d3c05efc3b56154179111c3e39e0f3fd8b1) Add some `TODO([#222](https://git.meli-email.org/meli/meli/issues/222))`s.
### Packaging
- [671ce9f6](https://git.meli-email.org/meli/meli/commit/671ce9f694a8e941826472caad8051998540bb1f) Add missing build dependencies
### Miscellaneous Tasks
- [25805229](https://git.meli-email.org/meli/meli/commit/2580522931fb29442598ac8932a13eaeb577bace) Log vcard parsing failures
- [5f003a31](https://git.meli-email.org/meli/meli/commit/5f003a31be95a3877d1006f8a22e424a1183163d) Parse vCards with just LF instead of CRLF line endings
- [d8e9a005](https://git.meli-email.org/meli/meli/commit/d8e9a00563c023abb0ff75aaa4ba3fa92626c5ce) Add quoted REFERENCES field in parsing of responses
- [81d12656](https://git.meli-email.org/meli/meli/commit/81d1265601c299dee6405f3f9b4e81f89d3cfe29) Escape IMAP passwords properly
- [0d8bedd2](https://git.meli-email.org/meli/meli/commit/0d8bedd2d5d3eb8eee831e75d1e14d45beefb847) Make is_online() await for connection
- [d4b690d5](https://git.meli-email.org/meli/meli/commit/d4b690d5d3a7f6a6b57afd7a6177db0db20a9c94) Send password as byte literal on LOGIN
- [2eb22a29](https://git.meli-email.org/meli/meli/commit/2eb22a290abb3f37bc77c3bc2771edfb60a1c314) Stop hardcoding certain component colors
- [2c23ca34](https://git.meli-email.org/meli/meli/commit/2c23ca34cdee769a0f78a0b0ef934e5f20dd9567) Update most Cargo dependencies
- [721891c2](https://git.meli-email.org/meli/meli/commit/721891c2955e9f5e223949bde2dd43604cec8390) Update nom dependency
- [4fdc90b3](https://git.meli-email.org/meli/meli/commit/4fdc90b31ea56c046dfe5bf9bee0a118f9c03db1) Use `open` instead of `xdg-open` in macos
- [9558b2ae](https://git.meli-email.org/meli/meli/commit/9558b2ae921aa35076f58d68b5898334a2797685) Parse Cp1253 as windows1253 encoding
- [6a843d49](https://git.meli-email.org/meli/meli/commit/6a843d49830f8c70f510c4232ea63eb204d35319) Export list_mail_in_maildir_fs() function
- [d6355a30](https://git.meli-email.org/meli/meli/commit/d6355a3043ec0b4b2a3e1c3fbb0ed66d2e87e7f4) Impl Debug for ParsingError
- [dc5afa13](https://git.meli-email.org/meli/meli/commit/dc5afa13dbea4da042c35e12291c5b5a2846c3ff) Use osascript/applescript for notifications on macos
- [e6d6e1f5](https://git.meli-email.org/meli/meli/commit/e6d6e1f588db9793e822cdbb1ce2edb2959170c6) Don't unwrap if pseudoterminal creation fails
- [ca84906d](https://git.meli-email.org/meli/meli/commit/ca84906d7ddb1351643998efaa56086e3ba9cf8e) Escape all quotes in applescript on macos
- [4a79b202](https://git.meli-email.org/meli/meli/commit/4a79b2021d2fb3edd046197b44b702bdb468fc5e) Update dependency versions
- [e29041f7](https://git.meli-email.org/meli/meli/commit/e29041f73354c59ef95916edd75e6ca7876e3c3a) Rename src/bin.rs to src/main.rs
- [7650805c](https://git.meli-email.org/meli/meli/commit/7650805c60cec2fe09cd2a59cb665731f5cca140) Bring stripped binary size down to 7MiB
- [ca488968](https://git.meli-email.org/meli/meli/commit/ca48896865778df2c79bc1d13f03b5f56136304c) Add strip option to profile.release
- [10497952](https://git.meli-email.org/meli/meli/commit/10497952f718b49f3a247741a64361f855b2d4f7) Wrap stdout in BufWriter
- [29042aba](https://git.meli-email.org/meli/meli/commit/29042aba593210f3be73010908d5092951b3b1a1) Add mbox date format parse
- [480000eb](https://git.meli-email.org/meli/meli/commit/480000ebbb67a80181fd27762ca649acf13df0f3) Show error if account directory does not contain ".notmuch" subdirectory
- [a484b397](https://git.meli-email.org/meli/meli/commit/a484b397c68fd126c17073ac9c9f02432c413341) Show informative error messages if libloading fails
- [4a20fc42](https://git.meli-email.org/meli/meli/commit/4a20fc42e1f5cad325d5aa439d1baab210aceed8) Update CHANGELOG.md
- [a72c96a2](https://git.meli-email.org/meli/meli/commit/a72c96a26afe9e54a0fcadb8c43448f1fdc09ce9) Add 8BITMIME support to smtp client
- [3c0f5d82](https://git.meli-email.org/meli/meli/commit/3c0f5d8274d8039b1a2c928f99194835bca7b83a) Add BINARYMIME support to smtp client
- [36883692](https://git.meli-email.org/meli/meli/commit/36883692782ed2355a0ec12ccf9f82aa2edcc8c1) Add smtp test
- [9cbbf71e](https://git.meli-email.org/meli/meli/commit/9cbbf71e0f8f9115e9e043982f20045cfc550eb7) Add DecodeOptions struct for decoding
- [0df46a63](https://git.meli-email.org/meli/meli/commit/0df46a63ec6e30983480f0eb50c8da3f74b4f0b3) Show error if sqlite3 search backend is set but doesn't exist
- [a7a50d30](https://git.meli-email.org/meli/meli/commit/a7a50d3078cb7466ab341ddfc30a80c7b1f8dfdb) Box<_> some large fields in biggest types
- [d8d43a16](https://git.meli-email.org/meli/meli/commit/d8d43a16fef045a2116ff126e7b6e27817b526fc) Add html_open config setting
- [0ed10711](https://git.meli-email.org/meli/meli/commit/0ed10711ef542cc13eaaef809fa557468b3d6696) Add new_mail_script option
- [c3fdafde](https://git.meli-email.org/meli/meli/commit/c3fdafde3b69c0abc78a62926e0c32fc3dd602d6) Documentation touchups
- [347be543](https://git.meli-email.org/meli/meli/commit/347be54305c60350b055a1da3a1abfa4d33d3f22) Add NetworkErrorKind enum
- [0c08cb73](https://git.meli-email.org/meli/meli/commit/0c08cb737ceaa5c738712905c7d57f956d449ed0) Mark mailboxes as subscribed on personal accounts
- [129573e0](https://git.meli-email.org/meli/meli/commit/129573e0fd9b42ebf14c2de176e65b92bf8479bd) Rename root_path to root_mailbox
- [7e09b180](https://git.meli-email.org/meli/meli/commit/7e09b1807ffa9bae54da35b02c83b5aaee455819) Replace _Ref deref unwraps with expect()
- [55ed9624](https://git.meli-email.org/meli/meli/commit/55ed962425ba25d2317946705ff6861a77eb770f) Use server_url instead of server_hostname + server_port in config
- [0ef4dde9](https://git.meli-email.org/meli/meli/commit/0ef4dde9392452f7cf7f18294f747fc6e0babb8d) Wrap serde_json deserialize errors in human readable errors
- [dd0baa82](https://git.meli-email.org/meli/meli/commit/dd0baa82e9789da23c8f9b06925776c7f80e2568) Spawn user-given command strings with sh -c ".."
- [3697b7d9](https://git.meli-email.org/meli/meli/commit/3697b7d960cc9dbe602fa84f861cea854b600b73) Don't use LC_ category in place of LC_ masks in libc calls
- [6d20abdd](https://git.meli-email.org/meli/meli/commit/6d20abdde7b4cec6ec1af7c097f01042ea05cfbb) Add #[allow(deref_nullptr)] in bindgen tests
- [17b42b1a](https://git.meli-email.org/meli/meli/commit/17b42b1a6c721fb2e369c2a300867c8db2beb959) Add json deserialization tests
- [64346dd3](https://git.meli-email.org/meli/meli/commit/64346dd3fe0ef40025ec6fdb01d18eb38f7e7f65) Add map_res, quoted_slice, is_a, alt, take, take_literal
- [56fc43bc](https://git.meli-email.org/meli/meli/commit/56fc43bcf869a867455b44d007b9d3d17422bc8d) Add As{Ref,Mut} impls for RwRef{,Mut}
- [63179841](https://git.meli-email.org/meli/meli/commit/631798413659a320dcd9574e0bca7b7d75cc8d6c) Add --bin flag to meli cargo build target
- [ded9adde](https://git.meli-email.org/meli/meli/commit/ded9adde614ac3d38045fa97a0f5144b80855fe7) More descriptive "Unimplemented" messages
- [2224a710](https://git.meli-email.org/meli/meli/commit/2224a7100f9bc6c44bc66117a88556003e74186e) Reset imap cache on init error
- [252d2bdf](https://git.meli-email.org/meli/meli/commit/252d2bdf2f12c8954f8b299000bbde6219d25335) Replace hardcoded /bin/false with 'false'
- [2427b097](https://git.meli-email.org/meli/meli/commit/2427b097c5c40f3212a105cb40f913c9860ae2a8) Make tag_default background lighter on light theme
- [7382e301](https://git.meli-email.org/meli/meli/commit/7382e30160a934ce97dd73c1be44640d5b4a4c75) Convert EnvelopeHash from typedef to wrapper struct
- [259aeb00](https://git.meli-email.org/meli/meli/commit/259aeb00877557ee85b5cc555d50e605b85b3109) Convert {Account,Mailbox}Hash from typedef to wrapper struct
- [5634f955](https://git.meli-email.org/meli/meli/commit/5634f9555315deb2d39ed8fce577a35f4d535ac1) Rename MeliError struct to Error
- [7606317f](https://git.meli-email.org/meli/meli/commit/7606317f24d076bdc7db873c2b15811728ed946a) Add support for virtual mailbox hierarchy
- [2878bbb8](https://git.meli-email.org/meli/meli/commit/2878bbb8c887275d26264bf7201a632161c4048a) Add parser for mutt alias file
- [de2f46fe](https://git.meli-email.org/meli/meli/commit/de2f46fe611726a445c1e06cbc35343e716aa335) Rustfmt changes
- [f9ac9b60](https://git.meli-email.org/meli/meli/commit/f9ac9b607a2bd01e42c81cfab3c933df28ff1676) Temporarily disable libgpgme functions because of a bug
- [256a3e25](https://git.meli-email.org/meli/meli/commit/256a3e252e2e4db9af9a04c7df1a52eeaf2bbfc9) Update minimum supported rust version
- [fbc1007f](https://git.meli-email.org/meli/meli/commit/fbc1007ff4f41bac888a1b53c156feec4f795403) Deserialize `null` to empty vec for messageId
- [d7ec97f0](https://git.meli-email.org/meli/meli/commit/d7ec97f03bc0e815e160a142f871dc764d416af1) Small rustfmt change
- [2447a2cb](https://git.meli-email.org/meli/meli/commit/2447a2cbfeaa8d6f7ec11a2a8a6f3be1ff2fea58) Avoid relying on hardcoded hash values
- [d679a744](https://git.meli-email.org/meli/meli/commit/d679a74450b35724301c81da1644bcedb1c54045) Implement Bearer token authentication
- [47e6d5d9](https://git.meli-email.org/meli/meli/commit/47e6d5d935a2b5124efbe847dac885b859200469) Add edit-config CLI subcommand that opens config files on EDITOR
- [3a02b6fb](https://git.meli-email.org/meli/meli/commit/3a02b6fb8024e6bb046fc167e7527aad1b192202) Mention how to override w3m with html_filter
- [85d4316a](https://git.meli-email.org/meli/meli/commit/85d4316a6a8703ac3e4923cf99ce8c4bb22bb4ae) Replace old logging module with the `log` create
- [1f1ea307](https://git.meli-email.org/meli/meli/commit/1f1ea307698a5a7f62f5ab2ea1594aef4d8f48a8) On draw() set dirty on return
- [77020e0c](https://git.meli-email.org/meli/meli/commit/77020e0c19873b8053321132ff5b58181c567fcd) Update CHANGELOG.md
- [682ea554](https://git.meli-email.org/meli/meli/commit/682ea5547e380deeb215503b39c8aa66c65b3cac) Add `.idea` (CLion) to `.gitignore`.
- [f63f6445](https://git.meli-email.org/meli/meli/commit/f63f6445addeccee1a6b830f1c101a043612ea4e) Improve error message when `m4` executable is missing.
- [cc27639f](https://git.meli-email.org/meli/meli/commit/cc27639fca0dcb3a5ff9fceef8666dbbf047adaa) Use Envelope attachments when editing and don't add already existing headers
- [30866f75](https://git.meli-email.org/meli/meli/commit/30866f752b21802b64ce7d2e02c9962c1091c9d8) Bypass rustfmt bug.
- [235fceaf](https://git.meli-email.org/meli/meli/commit/235fceaf2168af50c3804cecfbf69e64ff42598c) Add standard heeder constants in email::headers
- [aebff3d3](https://git.meli-email.org/meli/meli/commit/aebff3d3d9864b8854aba5e7f43a61d515e8057f) Implement mailto RFC properly
- [954329d8](https://git.meli-email.org/meli/meli/commit/954329d848a5b3e73fca50ed1db9859118bed6dd) Set file extensions to temp files, use `open` in macos
- [58889bca](https://git.meli-email.org/meli/meli/commit/58889bcadd44d6aec2eddd17cf5ecb1e07531cbe) Add show_extra_headers option
- [23d95973](https://git.meli-email.org/meli/meli/commit/23d95973d4f574fe431441df97ceaef0e3e4762f) Add search.rs module
- [6bf1756d](https://git.meli-email.org/meli/meli/commit/6bf1756de844386ba312d15109ae29951896147b) Implement more search criteria in Query type
- [299c8e0f](https://git.meli-email.org/meli/meli/commit/299c8e0f993c4ac88005a5c9e708d9e214b20ac1) Restructure pub use melib::* imports
- [f8623d4b](https://git.meli-email.org/meli/meli/commit/f8623d4b2c386f51f1d11a23900503d8165ac9f3) Implement more ResponseCode cases
- [b92a80a2](https://git.meli-email.org/meli/meli/commit/b92a80a23afb96fbd63031704e4656cc8a00526c) Resync even if UIDVALIDITY is missing from cache
- [bf615e7d](https://git.meli-email.org/meli/meli/commit/bf615e7d933b474942d421eafc1015aeb28f8516) Check for case when envelope has its own message id in References and In-Reply-To
- [e0257c9d](https://git.meli-email.org/meli/meli/commit/e0257c9d8d6f234f71852a0080d443b063d5e6d7) Run cargo-sort
- [d7e6b40b](https://git.meli-email.org/meli/meli/commit/d7e6b40b7e1f501fdaaba54880e9c7a4b0e01288) Auto re-index sqlite3 database if it's missing
- [cd85d833](https://git.meli-email.org/meli/meli/commit/cd85d83324a009ea4b86ac22af395145a9e999ab) Replace timestamp with Date value in message/rfc822 Display
- [579372b4](https://git.meli-email.org/meli/meli/commit/579372b4a75e39c9e84010de16d7d46294bed04a) Improve readability of `Envelope`.
- [6c6d9f4b](https://git.meli-email.org/meli/meli/commit/6c6d9f4b4e0d16b5a73ae8e2a2fb2a6f124df7e6) Improve ordering of `flag_impl!`s.
- [8f14a237](https://git.meli-email.org/meli/meli/commit/8f14a2373e16b9b4af22f9388fae84235dd08123) Put imap-codec logic under the imap_backend feature
- [fd0faade](https://git.meli-email.org/meli/meli/commit/fd0faade066a18466e683361211bba569956bf63) Add connection instance id string for debugging in logs
- [5c9b3fb0](https://git.meli-email.org/meli/meli/commit/5c9b3fb0448fa3689ff33faba3dde03c49347f61) Impl Component for Box<dyn Component>
- [45bac6eb](https://git.meli-email.org/meli/meli/commit/45bac6eb16a5a093193d5beb4d80040ce161304a) Tidy up use of debug!
- [5699baec](https://git.meli-email.org/meli/meli/commit/5699baecfba9cb15aac04a6b400cfb6bc881e2c5) Add utils::{futures, random}
- [b05d9299](https://git.meli-email.org/meli/meli/commit/b05d92997546e438b202d336fc581c2514c63b9f) Impl exponential backoff when retrying connection
- [f5cfbd32](https://git.meli-email.org/meli/meli/commit/f5cfbd32e6ebbe83ad7e84d048f1fbf2e51ca605) On set_flags, update {un,}seen sets in all mailboxes
- [f0d88005](https://git.meli-email.org/meli/meli/commit/f0d88005fbabcd552593ba0fe785e89a3560ac1c) Change message/rfc822 Display repr
- [f98e36ce](https://git.meli-email.org/meli/meli/commit/f98e36cee514f643e0fe256857cf31e2e0f24080) Replace old-style /*! module doc comments with //!
- [1bcc0bbe](https://git.meli-email.org/meli/meli/commit/1bcc0bbece2f479950e8811261befedc0199dab9) Add mbox parsing test
- [619fbef1](https://git.meli-email.org/meli/meli/commit/619fbef129e249489e64a26e1d0dfbd02db2516a) Recursively calculate update_show_subject()
- [957abf4e](https://git.meli-email.org/meli/meli/commit/957abf4e7238ec74b2194a21533b69dd1a58c0a8) Update cargo dependencies
- [9d51b6bd](https://git.meli-email.org/meli/meli/commit/9d51b6bd525784bc108959519c8dd21d30a8b020) Update `imap-codec`.
- [7c33f899](https://git.meli-email.org/meli/meli/commit/7c33f8999b6a5efd911680f2b83a3ff3a682a715) Use published imap-codec 0.10.0.
- [3803d788](https://git.meli-email.org/meli/meli/commit/3803d788abc5157b9cc6368da7e54aced9604aec) If auth is false checks if config has password entry
- [866166eb](https://git.meli-email.org/meli/meli/commit/866166eb8e8b994c8c87aad92a3303f9f6449b2d) Don't print parsing error for empty bytes
- [5b5869a2](https://git.meli-email.org/meli/meli/commit/5b5869a2ec3fce2fc69aa5c83fbda7a767f2a402) Re-enable print to stderr ifdef MELI_DEBUG_STDERR
- [13fe64a0](https://git.meli-email.org/meli/meli/commit/13fe64a027895780efdb6bfee246d562741a4be1) Cache pgp signature verification results
- [5ceddf41](https://git.meli-email.org/meli/meli/commit/5ceddf412e3b215b712e55aea8e18887d2d39f1a) Update CHANGELOG.md
- [4e55fbc9](https://git.meli-email.org/meli/meli/commit/4e55fbc90d8b105788c7c5998cb26b2829ac87a2) Add SEEN flag to all envs, since NNTP has no flags
- [e9cd800f](https://git.meli-email.org/meli/meli/commit/e9cd800f49e2d0e155d434ff8e91462e20b9d4f5) Add support for storing read status locally
- [53cba4be](https://git.meli-email.org/meli/meli/commit/53cba4beee4f774b548881c1a3f207ca391d3df3) Update README.md relative file paths
- [c4c245ee](https://git.meli-email.org/meli/meli/commit/c4c245ee19137f64d836401f7c1de17c9eb42b6e) Respect danger_accept_invalid_certs setting
- [29b43e2c](https://git.meli-email.org/meli/meli/commit/29b43e2c88edcfdecffd076fbb773c8547425f12) Replace mktime with timegm
- [4874e30f](https://git.meli-email.org/meli/meli/commit/4874e30f3ce9b186ac7cd427cba4a8542bd5048e) Add smtp-trace feature
- [51e9fbe8](https://git.meli-email.org/meli/meli/commit/51e9fbe8f2c380f3c9ee6a9ee65e638c169b43ef) Add account_name identifier to sqlite3 index database name
- [129f1091](https://git.meli-email.org/meli/meli/commit/129f10911b01641940801586bfa5286307e4342f) Rename `imap_backend` feature to `imap`
- [fe027fa3](https://git.meli-email.org/meli/meli/commit/fe027fa300a9882730a558fffe6000527ef08ff8) Rename `maildir_backend` feature to `maildir`
- [fe7dcc50](https://git.meli-email.org/meli/meli/commit/fe7dcc508ee51f492df2de3884147531fada6f4e) Rename `notmuch_backend` feature to `notmuch`
- [e9f09a15](https://git.meli-email.org/meli/meli/commit/e9f09a153ca0a1a023efe924b314ea977ccc3c25) Rename `mbox_backend` feature to `mbox`
- [7db930ca](https://git.meli-email.org/meli/meli/commit/7db930cabd295e888f4f106d5e7ea411521340ff) Rename `jmap_backend` feature to `jmap`
- [89c90f22](https://git.meli-email.org/meli/meli/commit/89c90f224a68ec524f7dc7033955ce7b8196f493) Add `nntp` feature
- [b65934fa](https://git.meli-email.org/meli/meli/commit/b65934facc7aeeb8ab30603e16cef2b747f9a0e5) Add nntp-trace feature
- [8ecdb6df](https://git.meli-email.org/meli/meli/commit/8ecdb6df3189cae4b6fa21a177bde756cc4407cf) Add imap-trace feature
- [9216e7bc](https://git.meli-email.org/meli/meli/commit/9216e7bc657738ae9861583a837c1326398197e4) Add opt id string for tracing
- [ae25ffba](https://git.meli-email.org/meli/meli/commit/ae25ffba430572efe73fde05eaf8111453f814cf) Don't do plain EHLO before starting Tls connection
- [8cb2a515](https://git.meli-email.org/meli/meli/commit/8cb2a515e1ba31efe914db67504993bc081ed7f3) Use localhost in lieu of 127.0.0.1 for CI
- [0ee1b6e0](https://git.meli-email.org/meli/meli/commit/0ee1b6e01830c01871e93e27d735a39792202325) Start background watch job in init
- [448e0635](https://git.meli-email.org/meli/meli/commit/448e0635e00b533a4d9dc15ba65982097649b397) Log error when command length exceeds 512 octets
- [bf543855](https://git.meli-email.org/meli/meli/commit/bf543855dc143b25344b79303f017380c9773793) Add PartialEq<str> for MessageID
- [7c7f6e19](https://git.meli-email.org/meli/meli/commit/7c7f6e1923e8b3127cf7cbd4b18f1db3ed9c6583) Don't increase Thread length for duplicates
- [5c2b0471](https://git.meli-email.org/meli/meli/commit/5c2b04719b953373c6a657f22db295d08b94685e) Normalize std::fmt::* imports
- [0f60009e](https://git.meli-email.org/meli/meli/commit/0f60009ea909adfb8f4e85d942decb8bc60f7539) Add RUSTFLAGS with -D warnings
- [6578a566](https://git.meli-email.org/meli/meli/commit/6578a5666889434ed6ca2f276e365633956fe3d3) Update cargo install directions
- [4f6081b6](https://git.meli-email.org/meli/meli/commit/4f6081b6633aed1eeafd99c24aa2dc64397043ca) Update to `imap-codec 1.0.0-beta`.
- [dc2b0044](https://git.meli-email.org/meli/meli/commit/dc2b00442b04c21455a6fda59b4729d0cbd04eff) Run rustfmt and cargo-sort
- [b3858de2](https://git.meli-email.org/meli/meli/commit/b3858de2f4e12723ee922174c79cc36062bed54e) Impl From<io::ErrorKind> for ErrorKind
- [f93adb68](https://git.meli-email.org/meli/meli/commit/f93adb683a562f25e40ffa03f80d04d5ad8ca34f) Replace change_color uses with change_theme
- [f193bdf6](https://git.meli-email.org/meli/meli/commit/f193bdf685e06652ab5b2da2a9a01fa56620cda6) Add column headers and sorting
- [095d24f9](https://git.meli-email.org/meli/meli/commit/095d24f91447a2ecab6d6bc78e1705ea4394e9bd) Add PULL_REQUEST_TEMPLATE.md
- [ab57e942](https://git.meli-email.org/meli/meli/commit/ab57e9420db29efd42773e970f33751b7b3f6f26) Add delete_contact shortcut
- [3963103d](https://git.meli-email.org/meli/meli/commit/3963103d55db28f789fe39f0dd80cd0d57792b5d) Prevent duplicate contact creation
- [f162239f](https://git.meli-email.org/meli/meli/commit/f162239fcc87d9c4f8aba8c33a9812a5e691c8d9) Change `on:` conditions for test.yaml
- [974b3a53](https://git.meli-email.org/meli/meli/commit/974b3a53058181e3df992a2105abcbf1c392fc19) Update bitflags, rusqlite dependencies
- [4d22b669](https://git.meli-email.org/meli/meli/commit/4d22b669bf330f8f3168fc2f704ad63c21c5e821) Update dependencies
- [ffba203a](https://git.meli-email.org/meli/meli/commit/ffba203a3b7070cc9e71d9444556e108ff0e18ea) Add support for Home and End key navigation
- [3433f7c4](https://git.meli-email.org/meli/meli/commit/3433f7c41e0d0cbb48af821280537da41b9e53d0) Update PULL_REQUEST_TEMPLATE.md
- [f7a4741b](https://git.meli-email.org/meli/meli/commit/f7a4741bf1622ae60042fb6ab0a906fe50fb1e06) Add jmap-trace feature
- [c875dda4](https://git.meli-email.org/meli/meli/commit/c875dda4960e5688b17176ba82ad1e5da38b883b) Add last_method_response field to Connection
- [37a787e6](https://git.meli-email.org/meli/meli/commit/37a787e6bb5abd34fae2888944537dec1ee3842f) Use IndexMap instead of HashMap
- [6ebdc7f9](https://git.meli-email.org/meli/meli/commit/6ebdc7f9aec5531c2b562a4e0cfd320ead6a4c01) Add Id<_>::empty() contructor
- [4f9b9773](https://git.meli-email.org/meli/meli/commit/4f9b97736a4af8b8b4ba0017ad1175a1c2352db6) Rename EmailImport to EmailImportObject
- [11432ba2](https://git.meli-email.org/meli/meli/commit/11432ba2c381b07bb540f7f92664b3c351e3cf62) Make `null` fields into Option<_>s
- [d9467d5f](https://git.meli-email.org/meli/meli/commit/d9467d5fcd9543611ec8a034eb7e25d12a3dcc45) Save all core capabilities to session store
- [31982931](https://git.meli-email.org/meli/meli/commit/31982931f5f472717b4c3d900f16c0588682f48e) Use Argument<OBJ> (value or resultreference) where appropriate
- [29fd8522](https://git.meli-email.org/meli/meli/commit/29fd8522e6bc2b0b6196cb97c8868dc34c2ba2f0) Implement Backend::create_mailbox()
- [5d8f07c8](https://git.meli-email.org/meli/meli/commit/5d8f07c8058261c7c251b3fb010ad866110e91df) Rename some objects better
- [38bc1369](https://git.meli-email.org/meli/meli/commit/38bc1369cc136c482f48d1ed3172b7f510ff7762) Add an Identity type.
- [59513b26](https://git.meli-email.org/meli/meli/commit/59513b267097cac8fe757c6198f26e0179014604) Implement Backend::submit(), server-side submission
- [5459a84f](https://git.meli-email.org/meli/meli/commit/5459a84f3d2b4c91a89252fba63f4ef12d965b9b) Update to imap-codec 1.0.0 (w/o `-beta`)
- [290cfb86](https://git.meli-email.org/meli/meli/commit/290cfb86c0c942690c48a0d3298e9d2de3ec4d94) Add a highlighted_selected theme key
- [46636d87](https://git.meli-email.org/meli/meli/commit/46636d8748f2779f38a10c6bf38c4e07acf16f8a) Bump version to 0.8.0
### Continuous Integration
- [1d0405ed](https://git.meli-email.org/meli/meli/commit/1d0405ed5b5cd76f4fe79e73fb30f4d4dce1d441) Add env vars
- [6e27edcb](https://git.meli-email.org/meli/meli/commit/6e27edcb775ce831b784d2040672f2d2af2c020f) Use cargo-nextest
- [67d2da0f](https://git.meli-email.org/meli/meli/commit/67d2da0f88b0e7b9b74c5d05c6c17a45057b094a) Disable smtp::test::test_smtp in test.yaml
## [alpha-0.7.2] - 2021-10-15
### Added
- Add forward mail option
- Add url_launcher config setting
- Add add_addresses_to_contacts command
- Add show_date_in_my_timezone pager config flag
- docs: add pager filter documentation
- mail/view: respect per-folder/account pager filter override
- pager: add filter command, esc to clear filter
- Show compile time features in with command argument
### Fixed
- melib/email/address: quote display_name if it contains ","
- melib/smtp: fix Cc and Bcc ignored when sending mail
- melib/email/address: quote display_name if it contains "."
## [alpha-0.7.1] - 2021-09-08
### Added
- Change all Down/Up shortcuts to j/k
- add 'GB18030' charset
- melib/nntp: implement refresh
- melib/nntp: update total/new counters on new articles
- melib/nntp: implement NNTP posting
- configs: throw error on extra unused conf flags in some imap/nntp
- configs: throw error on missing `composing` section with explanation
### Fixed
- Fix compilation for netbsd-9.2
- conf: fixed some boolean flag values requiring to be string e.g. "true"
## [alpha-0.7.0] - 2021-09-03
### Added
Notable changes:
- add import command to import email from files into accounts
- add add-attachment-file-picker command and `file_picker_command` setting to
use external commands to choose files when composing new mail
- ask confirm for delete
- add export-mbox command
- add export-mail command
- add TLS support with nntp
- add JMAP watch with polling
- add reload-config command
- add import-mail command
- imap: implement gmail XOAUTH2 authentication method
- imap: implement OAUTH2 authentication
- compose: treat inline message/rfc822 as attachments
- add gpg support via libgpgme
### Fixed
- Loading notmuch library on macos
- Limit dbus dependency to target_os = "linux"
- IMAP, notmuch, mbox backends: various performance fixes
## [alpha-0.6.2] - 2020-09-24
### Added
- Add customizable mailbox tree in sidebar
- Make `dbus` dependency opt-out (feature is `dbus-notifications`)
- Implemented JMAP async, search, tagging, syncing
- Preserve account order from configuration file
- Implemented IMAP `CONDSTORE` support for IMAP cache
- Add `timeout` setting for IMAP
- Implement TCP keepalive for IMAP
- Rewrote email address parsers.
- Implement `copy_messages` for maildir
- Implement selection with motions
### Fixed
- Fixed various problems with IMAP cache
- Fixed various problems with IMAP message counts
- Fixed various problems with IMAP connection hanging
- Fixed IMAP not reconnecting on dropped IDLE connections
- Fixed various problems with notmuch backend
## [alpha-0.6.1] - 2020-08-02
### Added
- added experimental NNTP backend
- added server extension support and use in account status tab
### Fixed
- imap: fixed IDLE connection getting stuck when using DEFLATE
## [alpha-0.6.0] - 2020-07-29
### Added
- Add `select` command to select threads that match search query
- Add support for mass copying/deleting/flagging/moving of messages
- IMAP: add support for COMPRESS=DEFLATE and others
Extension use can be configured with individual flags such as `use_deflate`
- Rename EXECUTE mode to COMMAND
- add async IMAP backend
- add in-app SMTP support
- ui: Show decoded source by default when viewing an Envelope's source
- ui: Add search in pagers
- Add managesieve REPL binary for managesieve script management
- imap: `add server_password_command`
- configuration: Add per-folder and per-account configuration overrides.
e.g. `accounts."imap.domain.tld".mailboxes."INBOX".index_style = "plain"`
The selection is done for a specific field as follows:
```text
if per-folder override is defined, return per-folder override
else if per-account override is defined, return per-account override
else return global setting field value.
```
- themes: Add Italics, Blink, Dim and Hidden text attributes
- ui: recognize readline shortcuts in Execute mode
- ui: hopefully smarter auto-completion in Execute mode
- demo NNTP python plugin
- ui: add `auto_choose_multipart_alternative`: Choose `text/html` alternative if `text/plain` is empty in `multipart/alternative` attachments.
- ui: custom date format strings
- ui: manual refresh for mailbox view
- ui: create mailbox command
- fs autocomplete
- ui: add support for [`NO_COLOR`](https://no-color.org/)
- enhanced, portable Makefile
- added Debian packaging
- added `default_header_values`: default header values used when creating a new draft
- ui: switch between sidebar and mailbox view with {left,right} keys for more intuitive navigation
- ui: add optional filter query for each mailbox view to view only the matching subset of messages (for example, you can hide all seen envelopes with `filter = "not flags:seen"`
### Changed
- Replace any use of 'folder' with 'mailbox' in user configuration
- Load libnotmuch dynamically
- Launch all user shell commands with `sh -c "..."`
### Fixed
- notmuch: add support for multiple accounts on same notmuch db
## [alpha-0.5.1] - 2020-02-09
### Added
- Added in-terminal floating notifications with history
- Added mailbox creation/deletion commands in IMAP accounts
- Added cli-docs compile time feature: Optionally build manpages to text with mandoc and print them from the command line.
- Added new theme keys
[unreleased]: #
[alpha-0.5.1]: https://github.com/meli/meli/releases/tag/alpha-0.5.1
[alpha-0.6.0]: https://github.com/meli/meli/releases/tag/alpha-0.6.0
[alpha-0.6.1]: https://github.com/meli/meli/releases/tag/alpha-0.6.1
[alpha-0.6.2]: https://github.com/meli/meli/releases/tag/alpha-0.6.2
[alpha-0.7.0]: https://github.com/meli/meli/releases/tag/alpha-0.7.0
[alpha-0.7.1]: https://github.com/meli/meli/releases/tag/alpha-0.7.1
[alpha-0.7.2]: https://github.com/meli/meli/releases/tag/alpha-0.7.2
[v0.8.0]: https://git.meli-email.org/meli/meli/releases/tag/v0.8.0
[v0.8.1]: https://git.meli-email.org/meli/meli/releases/tag/v0.8.1
[v0.8.2]: https://git.meli-email.org/meli/meli/releases/tag/v0.8.2
[v0.8.3]: https://git.meli-email.org/meli/meli/releases/tag/v0.8.3
[v0.8.4]: https://git.meli-email.org/meli/meli/releases/tag/v0.8.4

2985
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,66 +1,14 @@
[package]
name = "meli"
version = "0.5.1"
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
edition = "2018"
license = "GPL-3.0-or-later"
readme = "README"
description = "terminal mail client"
homepage = "https://meli.delivery"
repository = "https://git.meli.delivery/meli/meli.git"
keywords = ["mail", "mua", "maildir", "terminal", "imap"]
categories = ["command-line-utilities", "email"]
[[bin]]
name = "meli"
path = "src/bin.rs"
[dependencies]
xdg = "2.1.0"
crossbeam = "0.7.2"
signal-hook = "0.1.12"
signal-hook-registry = "1.2.0"
nix = "0.16.1"
melib = { path = "melib", version = "0.5.0" }
serde = "1.0.71"
serde_derive = "1.0.71"
serde_json = "1.0"
toml = "0.5.3"
fnv = "1.0.3" # >:c
linkify = "0.3.1" # >:c
xdg-utils = "0.3.0"
nom = "3.2.0"
notify = "4.0.1" # >:c
notify-rust = "^3" # >:c
termion = "1.5.1"
bincode = "1.2.0"
uuid = { version = "0.7.4", features = ["serde", "v4"] }
unicode-segmentation = "1.2.1" # >:c
libc = {version = "0.2.59", features = ["extra_traits",]}
rusqlite = {version = "0.20.0", optional =true }
rmp = "^0.8"
rmpv = { version = "^0.4.2", features=["with-serde",] }
rmp-serde = "^0.14.0"
smallvec = { version = "1.1.0", features = ["serde", ] }
[workspace]
resolver = "2"
members = [
"meli",
"melib",
]
[profile.release]
lto = true
opt-level = "z"
lto = "fat"
codegen-units = 1
opt-level = "s"
debug = false
[workspace]
members = ["melib", "testing", ]
[features]
default = ["sqlite3"]
notmuch = ["melib/notmuch_backend", ]
jmap = ["melib/jmap_backend",]
sqlite3 = ["rusqlite"]
cli-docs = []
# Print tracing logs as meli runs in stderr
# enable for debug tracing logs: build with --features=debug-tracing
debug-tracing = ["melib/debug-tracing", ]
strip = true

21
Cross.toml 100644
View File

@ -0,0 +1,21 @@
[target.aarch64-unknown-linux-gnu]
# Build with -static features.
pre-build = [
"export DEBIAN_FRONTEND=noninteractive ",
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update -y",
"""
apt-get install --assume-yes \
pkg-config \
libdbus-1-dev \
libdbus-1-dev:$CROSS_DEB_ARCH \
librust-libdbus-sys-dev \
librust-libdbus-sys-dev:$CROSS_DEB_ARCH \
librust-openssl-sys-dev \
librust-openssl-sys-dev:$CROSS_DEB_ARCH \
libsqlite3-dev:$CROSS_DEB_ARCH \
libssl-dev \
libssl-dev:$CROSS_DEB_ARCH \
sqlite3:$CROSS_DEB_ARCH
""",
]

67
DEVELOPMENT.md 100644
View File

@ -0,0 +1,67 @@
# Development
Code style follows the `rustfmt.toml` file.
## Trace logs
Enable trace logs to `stderr` with:
```sh
export MELI_DEBUG_STDERR=yes
```
This means you will have to to redirect `stderr` to a file like `meli 2> trace.log`.
Tracing is opt-in by build features:
```sh
cargo build --features=debug-tracing,imap-trace,smtp-trace
```
## use `.git-blame-ignore-revs` file _optional_
Use this file to ignore formatting commits from `git-blame`.
It needs to be set up per project because `git-blame` will fail if it's missing.
```sh
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
## Formatting with `rustfmt`
```sh
make fmt
```
## Linting with `clippy`
```sh
make lint
```
## Testing
```sh
make test
```
How to run specific tests:
```sh
cargo test -p {melib, meli} (-- --nocapture) (--test test_name)
```
## Profiling
```sh
perf record -g target/debug/meli
perf script | stackcollapse-perf | rust-unmangle | flamegraph > perf.svg
```
<!-- -->
<!-- ## Running fuzz targets -->
<!-- -->
<!-- Note: `cargo-fuzz` requires the nightly toolchain. -->
<!-- -->
<!-- ```sh -->
<!-- cargo +nightly fuzz run envelope_parse -- -dict=fuzz/envelope_tokens.dict -->
<!-- ``` -->

180
Makefile
View File

@ -16,22 +16,33 @@
#
# You should have received a copy of the GNU General Public License
# along with meli. If not, see <http://www.gnu.org/licenses/>.
.POSIX:
.SUFFIXES:
CARGO_TARGET_DIR ?= target
CARGO_BIN ?= cargo
TAGREF_BIN ?= tagref
CARGO_ARGS ?=
RUSTFLAGS ?= -D warnings -W unreachable-pub -W rust-2021-compatibility
CARGO_SORT_BIN = cargo-sort
CARGO_HACK_BIN = cargo-hack
PRINTF = /usr/bin/printf
# Options
PREFIX ?= /usr/local
BINDIR ?= ${PREFIX}/bin
MANDIR ?= ${PREFIX}/share/man
CARGO_TARGET_DIR ?= target
MIN_RUSTC ?= 1.39.0
CARGO_BIN ?= cargo
EXPANDED_PREFIX := `cd ${PREFIX} && pwd -P`
BINDIR ?= ${EXPANDED_PREFIX}/bin
MANDIR ?= ${EXPANDED_PREFIX}/share/man
# Installation parameters
MANPAGES ?= meli.1 meli.conf.5 meli-themes.5
DOCS_SUBDIR ?= meli/docs/
MANPAGES ?= meli.1 meli.conf.5 meli-themes.5 meli.7
FEATURES ?= --features "${MELI_FEATURES}"
MANPATHS := `manpath 2> /dev/null`
VERSION ?= `sed -n "s/^version\s*=\s*\"\(.*\)\"/\1/p" Cargo.toml`
MANPATHS != ACCUM="";for m in `manpath 2> /dev/null | tr ':' ' '`; do if [ -d "$${m}" ]; then REAL_PATH=`cd $${m} && pwd` ACCUM="$${ACCUM}:$${REAL_PATH}";fi;done;echo $${ACCUM}'\c' | sed 's/^://'
VERSION = `grep -m1 version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1`
MIN_RUSTC = `grep -m1 rust-version meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1`
GIT_COMMIT = `git show-ref -s --abbrev HEAD`
DATE = `date -I`
# Output parameters
BOLD ?= `[ -z $${TERM} ] && echo "" || tput bold`
@ -40,16 +51,20 @@ ANSI_RESET ?= `[ -z $${TERM} ] && echo "" || tput sgr0`
CARGO_COLOR ?= `[ -z $${NO_COLOR+x} ] && echo "" || echo "--color=never "`
RED ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 1) || echo ""`
GREEN ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 2) || echo ""`
YELLOW ?= `[ -z $${NO_COLOR+x} ] && ([ -z $${TERM} ] && echo "" || tput setaf 3) || echo ""`
.POSIX:
.SUFFIXES:
.PHONY: meli
meli: check-deps
@${CARGO_BIN} build ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --release --bin meli
.PHONY: help
help:
@echo "For a quick start, build and install locally:\n ${BOLD}${GREEN}PREFIX=~/.local make install${ANSI_RESET}\n"
@echo "For a quick start, build and install locally:\n\n${BOLD}${GREEN}make PREFIX=~/.local install${ANSI_RESET}\n"
@echo "Available subcommands:"
@echo " - ${BOLD}meli${ANSI_RESET} (builds meli with optimizations in \$$CARGO_TARGET_DIR)"
@echo " - ${BOLD}install${ANSI_RESET} (installs binary in \$$BINDIR and documentation to \$$MANDIR)"
@echo " - ${BOLD}uninstall${ANSI_RESET}"
@echo "Secondary subcommands:"
@echo "\nSecondary subcommands:"
@echo " - ${BOLD}clean${ANSI_RESET} (cleans build artifacts)"
@echo " - ${BOLD}check-deps${ANSI_RESET} (checks dependencies)"
@echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to \$$BINDIR)"
@ -59,32 +74,59 @@ help:
@echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz in this directory)"
@echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package in the parent directory)"
@echo " - ${BOLD}distclean${ANSI_RESET} (cleans distribution build artifacts)"
@echo " - ${BOLD}build-rustdoc${ANSI_RESET} (builds rustdoc documentation for all packages in \$$CARGO_TARGET_DIR)"
@echo "\nENVIRONMENT variables of interest:"
@echo "* PREFIX = ${UNDERLINE}${PREFIX}${ANSI_RESET}"
@echo -n "* MELI_FEATURES = ${UNDERLINE}"
@[ -z $${MELI_FEATURES+x} ] && echo -n "unset" || echo -n ${MELI_FEATURES}
@echo "* PREFIX = ${UNDERLINE}${EXPANDED_PREFIX}${ANSI_RESET}"
@echo "* MELI_FEATURES = ${UNDERLINE}\n"
@[ -z $${MELI_FEATURES+x} ] && echo "unset\c" || echo ${MELI_FEATURES}'\c'
@echo ${ANSI_RESET}
@echo "* BINDIR = ${UNDERLINE}${BINDIR}${ANSI_RESET}"
@echo "* MANDIR = ${UNDERLINE}${MANDIR}${ANSI_RESET}"
@echo -n "* MANPATH = ${UNDERLINE}"
@[ $${MANPATH+x} ] && echo -n $${MANPATH} || echo -n "unset"
@echo "* MANPATH = ${UNDERLINE}\c"
@[ $${MANPATH+x} ] && echo $${MANPATH}'\c' || echo "unset\c"
@echo ${ANSI_RESET}
@echo "* output of manpath(1) = ${UNDERLINE}${MANPATHS}${ANSI_RESET}"
@echo -n "* NO_MAN ${UNDERLINE}"
@[ $${NO_MAN+x} ] && echo -n "set" || echo -n "unset"
@echo "* (cleaned) output of manpath(1) = ${UNDERLINE}${MANPATHS}${ANSI_RESET}"
@echo "* NO_MAN ${UNDERLINE}\c"
@[ $${NO_MAN+x} ] && echo "set\c" || echo "unset\c"
@echo ${ANSI_RESET}
@echo -n "* NO_COLOR ${UNDERLINE}"
@[ $${NO_COLOR+x} ] && echo -n "set" || echo -n "unset"
@echo "* NO_COLOR ${UNDERLINE}\c"
@[ $${NO_COLOR+x} ] && echo "set\c" || echo "unset\c"
@echo ${ANSI_RESET}
@echo "* CARGO_BIN = ${UNDERLINE}${CARGO_BIN}${ANSI_RESET}"
@echo "* CARGO_ARGS = ${UNDERLINE}${CARGO_ARGS}${ANSI_RESET}"
@echo "* MIN_RUSTC = ${UNDERLINE}${MIN_RUSTC}${ANSI_RESET}"
@echo "* VERSION = ${UNDERLINE}${VERSION}${ANSI_RESET}"
@echo "* GIT_COMMIT = ${UNDERLINE}${GIT_COMMIT}${ANSI_RESET}"
@#@echo "* CARGO_COLOR = ${CARGO_COLOR}"
meli: check-deps
@${CARGO_BIN} build ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --release
.PHONY: check
check: check-tagrefs
@RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} check ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --all --tests --examples --benches --bins
.PHONY: fmt
fmt:
@$(CARGO_BIN) +nightly fmt --all || $(CARGO_BIN) fmt --all
@OUT=$$($(CARGO_SORT_BIN) -w 2>&1) || $(PRINTF) "WARN: %s cargo-sort failed or binary not found in PATH.\n" "$$OUT"
.PHONY: lint
lint:
@RUSTFLAGS='${RUSTFLAGS}' $(CARGO_BIN) clippy --no-deps --all-features --all --tests --examples --benches --bins
.PHONY: test
test: test-docs
@RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} test ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" --all --tests --examples --benches --bins
.PHONY: test-docs
test-docs:
@RUSTFLAGS='${RUSTFLAGS}' ${CARGO_BIN} test ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" --all --doc
.PHONY: test-feature-permutations
test-feature-permutations:
$(CARGO_HACK_BIN) hack --feature-powerset
.PHONY: check-deps
check-deps:
@(if ! echo ${MIN_RUSTC}\\n`${CARGO_BIN} --version | cut -d ' ' -f 2` | sort -CV; then echo "rust version >= ${RED}${MIN_RUSTC}${ANSI_RESET} required, found: `which ${CARGO_BIN}` `${CARGO_BIN} --version | cut -d ' ' -f 2`" \
@(if ! echo ${MIN_RUSTC}\\n`${CARGO_BIN} --version | grep ^cargo | cut -d ' ' -f 2` | sort -CV; then echo "rust version >= ${RED}${MIN_RUSTC}${ANSI_RESET} required, found: `which ${CARGO_BIN}` `${CARGO_BIN} --version | cut -d ' ' -f 2`" \
"\nYour options:\n - Set CARGO_BIN to a supported version\n - Install a supported version from your distribution's package manager\n - Install a supported version from ${UNDERLINE}https://rustup.rs/${ANSI_RESET}" ; exit 1; fi)
@ -93,30 +135,32 @@ clean:
-rm -rf ./${CARGO_TARGET_DIR}/
.PHONY: distclean
distclean: clean
distclean:
@rm -f meli-${VERSION}.tar.gz
@rm -rf .pc # rm debian stuff
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)${BINDIR}/meli
-rm $(DESTDIR)${MANDIR}/man1/meli.1.gz
-rm $(DESTDIR)${MANDIR}/man5/meli.conf.5.gz
-rm $(DESTDIR)${MANDIR}/man5/meli-themes.5.gz
for MANPAGE in ${MANPAGES}; do \
SECTION=`echo $${MANPAGE} | rev | cut -d "." -f 1`; \
MANPAGEPATH="${DESTDIR}${MANDIR}/man$${SECTION}/$${MANPAGE}.gz"; \
rm -f "$${MANAGEPATH}"
; done
.PHONY: install-doc
install-doc:
@(if [ -z $${NO_MAN+x} ]; then \
mkdir -p $(DESTDIR)${MANDIR}/man1 ; \
mkdir -p $(DESTDIR)${MANDIR}/man5 ; \
echo " - ${BOLD}Installing manpages to ${ANSI_RESET}${DESTDIR}${MANDIR}:" ; \
for MANPAGE in ${MANPAGES}; do \
SECTION=`echo $${MANPAGE} | rev | cut -d "." -f 1`; \
mkdir -p $(DESTDIR)${MANDIR}/man$${SECTION} ; \
MANPAGEPATH=${DESTDIR}${MANDIR}/man$${SECTION}/$${MANPAGE}.gz; \
echo " * installing $${MANPAGE} → ${GREEN}$${MANPAGEPATH}${ANSI_RESET}"; \
gzip -n < $${MANPAGE} > $${MANPAGEPATH} \
; done ; \
gzip -n < ${DOCS_SUBDIR}$${MANPAGE} > $${MANPAGEPATH} \
; done ; \
(case ":${MANPATHS}:" in \
*:${DESTDIR}${MANDIR}:*) echo -n "";; \
*:${DESTDIR}${MANDIR}:*) echo "\c";; \
*) echo "\n${RED}${BOLD}WARNING${ANSI_RESET}: ${UNDERLINE}Path ${DESTDIR}${MANDIR} is not contained in your MANPATH variable or the output of \`manpath\` command.${ANSI_RESET} \`man\` might fail finding the installed manpages. Consider adding it if necessary.\nMANPATH variable / output of \`manpath\`: ${MANPATHS}" ;; \
esac) ; \
else echo "NO_MAN is defined, so no documentation is going to be installed." ; fi)
@ -126,20 +170,24 @@ install-bin: meli
@mkdir -p $(DESTDIR)${BINDIR}
@echo " - ${BOLD}Installing binary to ${ANSI_RESET}${GREEN}${DESTDIR}${BINDIR}/meli${ANSI_RESET}"
@case ":${PATH}:" in \
*:${DESTDIR}${BINDIR}:*) echo -n "";; \
*:${DESTDIR}${BINDIR}:*) echo "\n";; \
*) echo "\n${RED}${BOLD}WARNING${ANSI_RESET}: ${UNDERLINE}Path ${DESTDIR}${BINDIR} is not contained in your PATH variable.${ANSI_RESET} Consider adding it if necessary.\nPATH variable: ${PATH}";; \
esac
@install -D ./${CARGO_TARGET_DIR}/release/meli $(DESTDIR)${BINDIR}/meli
@mkdir -p $(DESTDIR)${BINDIR}
@rm -f $(DESTDIR)${BINDIR}/meli
@cp ./${CARGO_TARGET_DIR}/release/meli $(DESTDIR)${BINDIR}/meli
@chmod 755 $(DESTDIR)${BINDIR}/meli
.PHONY: install
.NOTPARALLEL: yes
install: meli install-bin install-doc
@(if [ -z $${NO_MAN+x} ]; then \
echo "\n You're ready to go. You might want to read the \"STARTING WITH meli\" section in the manpage (\`man meli\`)" ;\
$(PRINTF) "\n You're ready to go. You might want to read the \"STARTING WITH meli\" section in the manpage (\`man meli\`)" ;\
$(PRINTF) "\n or the tutorial in meli(7) (\`man 7 meli\`).\n" ;\
fi)
@echo " - Report bugs in the mailing list or git issue tracker ${UNDERLINE}https://git.meli.delivery${ANSI_RESET}"
@echo " - If you have a specific feature or workflow you want to use, you can post in the mailing list or git issue tracker."
@$(PRINTF) " - Report bugs in the mailing list or git issue tracker ${UNDERLINE}https://git.meli-email.org${ANSI_RESET}\n"
@$(PRINTF) " - If you have a specific feature or workflow you want to use, you can post in the mailing list or git issue tracker.\n"
.PHONY: dist
dist:
@ -148,5 +196,53 @@ dist:
.PHONY: deb-dist
deb-dist:
@dpkg-buildpackage -b -rfakeroot -us -uc
@echo ${BOLD}${GREEN}Generated${ANSI_RESET} ../meli_${VERSION}-1_amd64.deb
@author=$(grep -m1 authors meli/Cargo.toml | head -n1 | cut -d'"' -f 2 | head -n1)
@dpkg-buildpackage -b -rfakeroot -us -uc --build-by="${author}" --release-by="${author}"
@echo ${BOLD}${GREEN}Generated${ANSI_RESET} ../meli_${VERSION}-1_`dpkg --print-architecture`.deb
.PHONY: build-rustdoc
build-rustdoc:
@RUSTDOCFLAGS="--crate-version ${VERSION}_${GIT_COMMIT}_${DATE}" ${CARGO_BIN} doc ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" --all-features --no-deps --workspace --document-private-items --open
.PHONY: check-tagrefs
check-tagrefs:
@(if ! command -v "$(TAGREF_BIN)" > /dev/null;\
then \
$(PRINTF) "Warning: tagref binary not in PATH.\n" 1>&2;\
exit;\
else \
$(TAGREF_BIN);\
fi)
.PHONY: test-makefile
test-makefile:
@$(PRINTF) "Checking that current version is detected. "
@([ ! -z "${VERSION}" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${RED}ERROR${ANSI_RESET}\nVERSION env var is empty, check its definition.\n" 1>&2
@$(PRINTF) "Checking that 'date -I' works on this platform. "
@export DATEVAL=$$(printf "%s" ${DATE} | wc -c | tr -d "[:blank:]" 2>&1); ([ "$${DATEVAL}" = "10" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${RED}ERROR${ANSI_RESET}\n'date -I' does not produce a YYYY-MM-DD output on this platform.\n" 1>&2
@$(PRINTF) "Checking that the git commit SHA can be detected. "
@([ ! -z "$(GIT_COMMIT)" ] && $(PRINTF) "${GREEN}OK${ANSI_RESET}\n") || $(PRINTF) "${YELLOW}WARN${ANSI_RESET}\nGIT_COMMIT env var is empty.\n" 1>&2
# Checking if mdoc changes produce new lint warnings from mandoc(1) compared to HEAD version:
#
# example invocation: `mandoc_lint meli.1`
#
# with diff(1)
# ============
#function mandoc_lint () {
#diff <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
#}
#
# with sdiff(1) (side by side)
# ============================
#
#function mandoc_lint () {
#sdiff <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
#}
#
# with delta(1)
# =============
#
#function mandoc_lint () {
#delta --side-by-side <(mandoc -T lint <(git show HEAD:./meli/docs/$1) 2> /dev/null | cut -d':' -f 3-) <(mandoc -T lint ./meli/docs/$1 2> /dev/null | cut -d':' -f 3-)
#}

244
README.md
View File

@ -1,133 +1,153 @@
# meli
For a quick start, build and install locally:
# meli ![Established, created in 2017](https://img.shields.io/badge/Est.-2017-blue) ![Minimum Supported Rust Version](https://img.shields.io/badge/MSRV-1.68.2-blue) [![GitHub license](https://img.shields.io/github/license/meli/meli)](https://github.com/meli/meli/blob/master/COPYING) [![Crates.io](https://img.shields.io/crates/v/meli)](https://crates.io/crates/meli) [![IRC channel](https://img.shields.io/badge/irc.oftc.net-%23meli-blue)](ircs://irc.oftc.net:6697/%23meli)
**BSD/Linux/macos terminal email client with support for multiple accounts and Maildir / mbox / notmuch / IMAP / JMAP / NNTP (Usenet).**
Try an [old online interactive web demo](https://meli-email.org/wasm2.html "online interactive web demo") powered by WebAssembly!
* `#meli` on OFTC IRC | [mailing lists](https://lists.meli-email.org/)
* Repository:
- Main <https://git.meli-email.org/meli/meli> Report bugs and/or feature requests in [meli's issue tracker](https://git.meli-email.org/meli/meli/issues "meli gitea issue tracker")
- Official mirror <https://codeberg.org/meli/meli>
- Official mirror <https://github.com/meli/meli>
**Table of contents**:
- [Install](#install)
- [Build](#build)
- [Quick start](#quick-start)
- [Supported E-mail backends](#supported-e-mail-backends)
- [E-mail submission backends](#e-mail-submission-backends)
- [Non-exhaustive list of features](#non-exhaustive-list-of-features)
- [HTML Rendering](#html-rendering)
- [Documentation](#documentation)
## Install
- [pkgsrc](https://pkgsrc.se/mail/meli)
- [openbsd ports](https://openports.pl/path/mail/meli)
- `cargo install meli` or `cargo install --git https://git.meli-email.org/meli/meli.git meli`
- [Pre-built debian package, static binaries](https://github.com/meli/meli/releases/ "github releases for meli")
- [Nix](https://search.nixos.org/packages?show=meli&query=meli&from=0&size=30&sort=relevance&channel=unstable#disabled "nixos package search results for 'meli'")
## Build
Run `cargo build --release --bin meli` or `make`.
For detailed building instructions, see [`BUILD.md`](./BUILD.md)
## Quick start
<table>
<tr><td>
```sh
PREFIX=~/.local make install
# Create configuration file in ${XDG_CONFIG_HOME}/meli/config.toml:
$ meli create-config
# Edit configuration in ${EDITOR} or ${VISUAL}:
$ meli edit-config
# Optionally, install manual pages if installed via cargo:
$ meli install-man
# Ready to go.
$ meli
```
Available subcommands:
- meli (builds meli with optimizations in `$CARGO_TARGET_DIR`)
- install (installs binary in `$BINDIR` and documentation to `$MANDIR`)
- uninstall
Secondary subcommands:
- clean (cleans build artifacts)
- check-deps (checks dependencies)
- install-bin (installs binary to `$BINDIR`)
- install-doc (installs manpages to `$MANDIR`)
- help (prints this information)
- dist (creates release tarball named `meli-VERSION.tar.gz` in this directory)
- deb-dist (builds debian package in the parent directory)
- distclean (cleans distribution build artifacts)
</td><td>
The Makefile *should* be portable and not require a specific `make` version.
See a comprehensive tour of `meli` in the manual page [`meli(7)`](./meli/docs/meli.7).
# Documentation
See also the [Quickstart tutorial](https://meli-email.org/documentation.html#quick-start) online.
After installing meli, see `meli(1)` and `meli.conf(5)` for documentation.
After installing `meli`, see `meli(1)`, `meli.conf(5)`, `meli(7)` and `meli-themes(5)` for documentation.
Sample configuration and theme files can be found in the `meli/docs/samples/` subdirectory.
Manual pages are also [hosted online](https://meli-email.org/documentation.html "meli documentation").
`meli` by default looks for a configuration file in this location: `${XDG_CONFIG_HOME}/meli/config.toml`.
# Building
meli requires rust 1.39 and rust's package manager, Cargo. Information on how
to get it on your system can be found here: <https://doc.rust-lang.org/cargo/getting-started/installation.html>
With Cargo available, the project can be built with
```sh
make meli
```
The resulting binary will then be found under `target/release/meli`
Run:
```sh
make install
```
to install the binary and man pages. This requires root, so I suggest you override the default paths and install it in your `$HOME`:
```sh
make PREFIX=$HOME/.local install
```
See `meli(1)` and `meli.conf(5)` for documentation.
You can build and run meli with one command:
```sh
cargo run --release
```
While the project is in early development, meli will only be developed for the
linux kernel and respected linux distributions. Support for more UNIX-like OSes
is on the roadmap.
# Building in Debian
Building with Debian's packaged cargo might require the installation of these
two packages: `librust-openssl-sys-dev librust-libdbus-sys-dev`
A `*.deb` package can be built with `make deb-dist`
# Building with notmuch
To use the optional notmuch backend feature, you must have `libnotmuch` installed in your system. In Debian-like systems, install the `libnotmuch5 libnotmuch-dev` packages.
To build with notmuch support, prepend the environment variable `MELI_FEATURES='notmuch'` to your make invocation:
```sh
MELI_FEATURES="notmuch" make
```
or if building directly with cargo, use the flag `--features="notmuch"'.
# Building with JMAP
To build with JMAP support, prepend the environment variable `MELI_FEATURES='jmap'` to your make invocation:
```sh
MELI_FEATURES="jmap" make
```
or if building directly with cargo, use the flag `--features="jmap"'.
# Development
Development builds can be built and/or run with
```
cargo build
cargo run
```
There is a debug/tracing log feature that can be enabled by using the flag
`--feature debug-tracing` after uncommenting the features in `Cargo.toml`. The logs
are printed in stderr, thus you can run meli with a redirection (i.e `2> log`)
Code style follows the default rustfmt profile.
# Configuration
meli by default looks for a configuration file in this location: `$XDG_CONFIG_HOME/meli/config.toml`
You can run meli with arbitrary configuration files by setting the `$MELI_CONFIG`
environment variable to their locations, ie:
You can run meli with arbitrary configuration files by setting the `${MELI_CONFIG}` environment variable to their locations, i.e.:
```sh
MELI_CONFIG=./test_config cargo run
```
# Testing
</td></tr>
</table>
How to run specific tests:
See [`meli(7)`](./meli/docs/meli.7) for an extensive tutorial and [`meli.conf(5)`](./meli/docs/meli.conf.5) for all configuration values.
| | | |
:---:|:---:|:---:
![Main view screenshot](./meli/docs/screenshots/main.webp "mail meli view screenshot") | ![Compact main view screenshot](./meli/docs/screenshots/compact.webp "compact main view screenshot") | ![Compose with embed terminal editor screenshot](./meli/docs/screenshots/compose.webp "composing view screenshot")
Main view | Compact main view | Compose with embed terminal editor
### Supported E-mail backends
| Protocol | Support |
|:------------:|:----------------|
| IMAP | full |
| Maildir | full |
| notmuch | full[^0] |
| mbox | read-only |
| JMAP | functional |
| NNTP / Usenet| functional |
[^0]: there's no support for searching through all email directly, you'd have to
create a mailbox with a notmuch query that returns everything and search
inside that mailbox.
### E-mail submission backends
- SMTP
- Pipe to shell script
- Server-side submission when supported
### Non-exhaustive list of features
- TLS
- email threading support
- multithreaded, async operation
- optionally run your editor of choice inside meli, with an embedded
xterm-compatible terminal emulator
- plain text configuration in TOML
- ability to open emails in UI tabs and switch to them
- optional sqlite3 index search
- override almost any setting per mailbox, per account
- contact list (+read-only vCard and mutt alias file support)
- forced UTF-8 (other encodings are read-only)
- configurable shortcuts
- theming
- `NO_COLOR` support
- ascii-only drawing characters option
- view text/html attachments through an html filter command (w3m by default)
- pipe attachments/mail to stuff
- use external attachment file picker instead of typing in an attachment's full path
- GPG signing, encryption, signing + encryption
- GPG signature verification
## HTML Rendering
HTML rendering is achieved using [w3m](https://github.com/tats/w3m) by default.
You can use the `pager.html_filter` setting to override this (for more details you can consult [`meli.conf(5)`](./meli/docs/meli.conf.5)).
## Documentation
See a comprehensive tour of `meli` in the manual page [`meli(7)`](./meli/docs/meli.7).
See also the [Quickstart tutorial](https://meli-email.org/documentation.html#quick-start) online.
After installing `meli`, see `meli(1)`, `meli.conf(5)`, `meli(7)` and `meli-themes(5)` for documentation.
Sample configuration and theme files can be found in the `meli/docs/samples/` subdirectory.
Manual pages are also [hosted online](https://meli-email.org/documentation.html "meli documentation").
`meli` by default looks for a configuration file in this location: `${XDG_CONFIG_HOME}/meli/config.toml`
You can run meli with arbitrary configuration files by setting the `${MELI_CONFIG}` environment variable to their locations, or use the `[-c, --config]` argument:
```sh
cargo test -p {melib, meli} (-- --nocapture) (--test test_name)
MELI_CONFIG=./test_config meli
```
# Profiling
or
```sh
perf record -g target/debug/bin
perf script | stackcollapse-perf | rust-unmangle | flamegraph > perf.svg
meli -c ./test_config
```

View File

@ -1,67 +0,0 @@
/*
* meli - build.rs
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
fn main() {
#[cfg(feature = "cli-docs")]
{
const MANDOC_OPTS: &[&'static str] = &["-T", "utf8", "-I", "os=Generated by mandoc(1)"];
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
let out_dir = env::var("OUT_DIR").unwrap();
let mut out_dir_path = Path::new(&out_dir).to_path_buf();
out_dir_path.push("meli.txt");
let output = Command::new("mandoc")
.args(MANDOC_OPTS)
.arg("meli.1")
.output()
.or_else(|_| Command::new("man").arg("-l").arg("meli.1").output())
.unwrap();
let mut file = File::create(&out_dir_path).unwrap();
file.write_all(&output.stdout).unwrap();
out_dir_path.pop();
out_dir_path.push("meli.conf.txt");
let output = Command::new("mandoc")
.args(MANDOC_OPTS)
.arg("meli.conf.5")
.output()
.or_else(|_| Command::new("man").arg("-l").arg("meli.conf.5").output())
.unwrap();
let mut file = File::create(&out_dir_path).unwrap();
file.write_all(&output.stdout).unwrap();
out_dir_path.pop();
out_dir_path.push("meli-themes.txt");
let output = Command::new("mandoc")
.args(MANDOC_OPTS)
.arg("meli-themes.5")
.output()
.or_else(|_| Command::new("man").arg("-l").arg("meli-themes.5").output())
.unwrap();
let mut file = File::create(&out_dir_path).unwrap();
file.write_all(&output.stdout).unwrap();
}
}

123
cliff.toml 100644
View File

@ -0,0 +1,123 @@
# configuration for https://github.com/orhun/git-cliff
[changelog]
# changelog header
header = """
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
# note that the - before / after the % controls whether whitespace is rendered between each line.
# Getting this right so that the markdown renders with the correct number of lines between headings
# code fences and list items is pretty finicky. Note also that the 4 backticks in the commit macro
# is intentional as this escapes any backticks in the commit body.
body = """
{% if not version %}
## [Unreleased]
{% else %}
## [{{ version }}](https://git.meli-email.org/meli/meli/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif %}
{% macro commit(commit) -%}
- [{{ commit.id | truncate(length=8, end="") }}]({{ "https://git.meli-email.org/meli/meli/commit/" ~ commit.id }}) {% if commit.scope %}*({{commit.scope | lower }})* {% endif %}{{ commit.message | split(pat="\n")| first | upper_first }}{% endmacro -%}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits | filter(attribute="scope") | sort(attribute="scope") %}
{{ self::commit(commit=commit) }}
{%- endfor -%}
{% for commit in commits %}
{%- if not commit.scope %}
{{ self::commit(commit=commit) }}
{%- endif -%}
{%- endfor -%}
{%- endfor %}
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff <https://git-cliff.org> -->
"""
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = false
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://git.meli-email.org/meli/meli/issues/${2}))" },
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "<!-- 00 -->Added" },
{ message = "^[aA]dd", group = "<!-- 00 -->Added" },
{ message = "[fF]ix", group = "<!-- 01 -->Bug Fixes" },
{ message = "[rR]efactor", group = "<!-- 02 -->Refactoring" },
{ message = "[mM]ove", group = "<!-- 02 -->Refactoring" },
{ message = "[rR]emove", group = "<!-- 02 -->Refactoring" },
{ message = "^refactor", group = "<!-- 02 -->Refactoring" },
{ message = "^[^.]*.rs:", group = "<!-- 02 -->Refactoring" },
{ message = "^meli", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^melib", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^imap", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^jmap", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^notmuch", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^mbox", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^smtp", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^mbox", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^doc", group = "<!-- 03 -->Documentation" },
{ message = "[mM]anual", group = "<!-- 03 -->Documentation" },
{ message = "[mM]anpage", group = "<!-- 03 -->Documentation" },
{ message = "[rR]eadme", group = "<!-- 03 -->Documentation" },
{ message = "^perf", group = "<!-- 04 -->Performance" },
{ message = "^style", group = "<!-- 05 -->Styling" },
{ message = "^test", group = "<!-- 06 -->Testing" },
{ message = "^debian", group = "<!-- 06 -->Packaging" },
{ message = "^mail/view", group = "<!-- 02 -->Changes" },
{ message = "^view", group = "<!-- 02 -->Changes" },
{ message = "^utilities", group = "<!-- 02 -->Changes" },
{ message = "^mail", group = "<!-- 02 -->Changes" },
{ message = "^listing", group = "<!-- 02 -->Changes" },
{ message = "^terminal", group = "<!-- 02 -->Changes" },
{ message = "^types", group = "<!-- 02 -->Changes" },
{ message = "^conf", group = "<!-- 02 -->Changes" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore\\(deps\\)", skip = true },
{ message = "^chore\\(changelog\\)", skip = true },
{ message = "^[cC]hore", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^scripts", group = "<!-- 07 -->Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 08 -->Security" },
{ message = "^build", group = "<!-- 09 -->Build" },
{ message = "^ci", group = "<!-- 10 -->Continuous Integration" },
{ message = "^revert", group = "<!-- 11 -->Reverted Commits" },
{ message = ".*", group = "<!-- 07 -->Miscellaneous Tasks" },
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]+|alpha-[0-9]+"
# regex for ignoring tags
ignore_tags = "v[^-]+-rc[.]?[0-9]+"
# regex for skipping tags
#skip_tags = "alpha"
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"

62
codemeta.json 100644
View File

@ -0,0 +1,62 @@
{
"@context": ["https://doi.org/10.5063/schema/codemeta-2.0", "http://schema.org/"],
"@type": "SoftwareSourceCode",
"applicationCategory": "E-mail client",
"author": [
{
"@id": "https://pitsidianak.is/",
"@type": "Person",
"name": "epilys",
"email": "manos@pitsidianak.is",
"familyName": "Pitsidianakis",
"givenName": "Manos",
"url": "https://pitsidianak.is/"
}
],
"codeRepository": "https://git.meli-email.org/meli/meli.git",
"dateCreated": "2016-04-25",
"dateModified": "2023-12-11",
"datePublished": "2017-07-23",
"description": "BSD/Linux/macos terminal email client with support for multiple accounts and Maildir / mbox / notmuch / IMAP / JMAP / NNTP (Usenet).",
"downloadUrl": "https://git.meli-email.org/meli/meli/archive/v0.8.5-rc.3.tar.gz",
"identifier": "https://meli-email.org/",
"isPartOf": "https://meli-email.org/",
"keywords": [
"e-mail",
"email",
"mail",
"terminal user interface",
"client",
"mua",
"mail user agent",
"smtp",
"imap",
"jmap",
"mbox",
"maildir",
"nntp"
],
"license": [
"https://spdx.org/licenses/EUPL-1.2",
"https://spdx.org/licenses/GPL-3.0-or-later"
],
"name": "meli",
"operatingSystem": [
"Linux",
"macOS",
"OpenBSD",
"NetBSD"
],
"programmingLanguage": "Rust",
"relatedLink": [
"https://codeberg.org/meli/meli",
"https://github.com/meli/meli",
"https://lists.meli-email.org/"
],
"version": "0.8.5-rc3",
"contIntegration": "https://git.meli-email.org/meli/meli/actions",
"developmentStatus": "active",
"issueTracker": "https://git.meli-email.org/meli/meli/issues",
"readme": "https://git.meli-email.org/meli/meli/raw/commit/dedee908d1e0b42773bade8e0604e94b14810e2d/README.md",
"buildInstructions": "https://git.meli-email.org/meli/meli/raw/commit/dedee908d1e0b42773bade8e0604e94b14810e2d/BUILD.md"
}

348
contrib/oauth2.py 100755
View File

@ -0,0 +1,348 @@
#!/usr/bin/env python3
#
# Copyright 2012 Google Inc.
# Copyright 2020 Manos Pitsidianakis
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Performs client tasks for testing IMAP OAuth2 authentication.
To use this script, you'll need to have registered with Google as an OAuth
application and obtained an OAuth client ID and client secret.
See https://developers.google.com/identity/protocols/OAuth2 for instructions on
registering and for documentation of the APIs invoked by this code.
This script has 3 modes of operation.
1. The first mode is used to generate and authorize an OAuth2 token, the
first step in logging in via OAuth2.
oauth2 --user=xxx@gmail.com \
--client_id=1038[...].apps.googleusercontent.com \
--client_secret=VWFn8LIKAMC-MsjBMhJeOplZ \
--generate_oauth2_token
The script will converse with Google and generate an oauth request
token, then present you with a URL you should visit in your browser to
authorize the token. Once you get the verification code from the Google
website, enter it into the script to get your OAuth access token. The output
from this command will contain the access token, a refresh token, and some
metadata about the tokens. The access token can be used until it expires, and
the refresh token lasts indefinitely, so you should record these values for
reuse.
2. The script will generate new access tokens using a refresh token.
oauth2 --user=xxx@gmail.com \
--client_id=1038[...].apps.googleusercontent.com \
--client_secret=VWFn8LIKAMC-MsjBMhJeOplZ \
--refresh_token=1/Yzm6MRy4q1xi7Dx2DuWXNgT6s37OrP_DW_IoyTum4YA
3. The script will generate an OAuth2 string that can be fed
directly to IMAP or SMTP. This is triggered with the --generate_oauth2_string
option.
oauth2 --generate_oauth2_string --user=xxx@gmail.com \
--access_token=ya29.AGy[...]ezLg
The output of this mode will be a base64-encoded string. To use it, connect to a
IMAPFE and pass it as the second argument to the AUTHENTICATE command.
a AUTHENTICATE XOAUTH2 a9sha9sfs[...]9dfja929dk==
"""
import base64
import imaplib
import json
from optparse import OptionParser
import smtplib
import sys
import urllib.request, urllib.parse, urllib.error
def SetupOptionParser():
# Usage message is the module's docstring.
parser = OptionParser(usage=__doc__)
parser.add_option('--generate_oauth2_token',
action='store_true',
dest='generate_oauth2_token',
help='generates an OAuth2 token for testing')
parser.add_option('--generate_oauth2_string',
action='store_true',
dest='generate_oauth2_string',
help='generates an initial client response string for '
'OAuth2')
parser.add_option('--client_id',
default=None,
help='Client ID of the application that is authenticating. '
'See OAuth2 documentation for details.')
parser.add_option('--client_secret',
default=None,
help='Client secret of the application that is '
'authenticating. See OAuth2 documentation for '
'details.')
parser.add_option('--access_token',
default=None,
help='OAuth2 access token')
parser.add_option('--refresh_token',
default=None,
help='OAuth2 refresh token')
parser.add_option('--scope',
default='https://mail.google.com/',
help='scope for the access token. Multiple scopes can be '
'listed separated by spaces with the whole argument '
'quoted.')
parser.add_option('--test_imap_authentication',
action='store_true',
dest='test_imap_authentication',
help='attempts to authenticate to IMAP')
parser.add_option('--test_smtp_authentication',
action='store_true',
dest='test_smtp_authentication',
help='attempts to authenticate to SMTP')
parser.add_option('--user',
default=None,
help='email address of user whose account is being '
'accessed')
parser.add_option('--quiet',
action='store_true',
default=False,
dest='quiet',
help='Omit verbose descriptions and only print '
'machine-readable outputs.')
return parser
# The URL root for accessing Google Accounts.
GOOGLE_ACCOUNTS_BASE_URL = 'https://accounts.google.com'
# Hardcoded dummy redirect URI for non-web apps.
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
def AccountsUrl(command):
"""Generates the Google Accounts URL.
Args:
command: The command to execute.
Returns:
A URL for the given command.
"""
return '%s/%s' % (GOOGLE_ACCOUNTS_BASE_URL, command)
def UrlEscape(text):
# See OAUTH 5.1 for a definition of which characters need to be escaped.
return urllib.parse.quote(text, safe='~-._')
def UrlUnescape(text):
# See OAUTH 5.1 for a definition of which characters need to be escaped.
return urllib.parse.unquote(text)
def FormatUrlParams(params):
"""Formats parameters into a URL query string.
Args:
params: A key-value map.
Returns:
A URL query string version of the given parameters.
"""
param_fragments = []
for param in sorted(iter(params.items()), key=lambda x: x[0]):
param_fragments.append('%s=%s' % (param[0], UrlEscape(param[1])))
return '&'.join(param_fragments)
def GeneratePermissionUrl(client_id, scope='https://mail.google.com/'):
"""Generates the URL for authorizing access.
This uses the "OAuth2 for Installed Applications" flow described at
https://developers.google.com/accounts/docs/OAuth2InstalledApp
Args:
client_id: Client ID obtained by registering your app.
scope: scope for access token, e.g. 'https://mail.google.com'
Returns:
A URL that the user should visit in their browser.
"""
params = {}
params['client_id'] = client_id
params['redirect_uri'] = REDIRECT_URI
params['scope'] = scope
params['response_type'] = 'code'
return '%s?%s' % (AccountsUrl('o/oauth2/auth'),
FormatUrlParams(params))
def AuthorizeTokens(client_id, client_secret, authorization_code):
"""Obtains OAuth access token and refresh token.
This uses the application portion of the "OAuth2 for Installed Applications"
flow at https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
Args:
client_id: Client ID obtained by registering your app.
client_secret: Client secret obtained by registering your app.
authorization_code: code generated by Google Accounts after user grants
permission.
Returns:
The decoded response from the Google Accounts server, as a dict. Expected
fields include 'access_token', 'expires_in', and 'refresh_token'.
"""
params = {}
params['client_id'] = client_id
params['client_secret'] = client_secret
params['code'] = authorization_code
params['redirect_uri'] = REDIRECT_URI
params['grant_type'] = 'authorization_code'
request_url = AccountsUrl('o/oauth2/token')
response = urllib.request.urlopen(request_url, urllib.parse.urlencode(params).encode()).read()
return json.loads(response)
def RefreshToken(client_id, client_secret, refresh_token):
"""Obtains a new token given a refresh token.
See https://developers.google.com/accounts/docs/OAuth2InstalledApp#refresh
Args:
client_id: Client ID obtained by registering your app.
client_secret: Client secret obtained by registering your app.
refresh_token: A previously-obtained refresh token.
Returns:
The decoded response from the Google Accounts server, as a dict. Expected
fields include 'access_token', 'expires_in', and 'refresh_token'.
"""
params = {}
params['client_id'] = client_id
params['client_secret'] = client_secret
params['refresh_token'] = refresh_token
params['grant_type'] = 'refresh_token'
request_url = AccountsUrl('o/oauth2/token')
response = urllib.request.urlopen(request_url, urllib.parse.urlencode(params).encode()).read()
return json.loads(response)
def GenerateOAuth2String(username, access_token, base64_encode=True):
"""Generates an IMAP OAuth2 authentication string.
See https://developers.google.com/google-apps/gmail/oauth2_overview
Args:
username: the username (email address) of the account to authenticate
access_token: An OAuth2 access token.
base64_encode: Whether to base64-encode the output.
Returns:
The SASL argument for the OAuth2 mechanism.
"""
auth_string = 'user=%s\1auth=Bearer %s\1\1' % (username, access_token)
if base64_encode:
auth_string = base64.b64encode(bytes(auth_string, 'utf-8'))
return auth_string
def TestImapAuthentication(user, auth_string):
"""Authenticates to IMAP with the given auth_string.
Prints a debug trace of the attempted IMAP connection.
Args:
user: The Gmail username (full email address)
auth_string: A valid OAuth2 string, as returned by GenerateOAuth2String.
Must not be base64-encoded, since imaplib does its own base64-encoding.
"""
print()
imap_conn = imaplib.IMAP4_SSL('imap.gmail.com')
imap_conn.debug = 4
imap_conn.authenticate('XOAUTH2', lambda x: auth_string)
imap_conn.select('INBOX')
def TestSmtpAuthentication(user, auth_string):
"""Authenticates to SMTP with the given auth_string.
Args:
user: The Gmail username (full email address)
auth_string: A valid OAuth2 string, not base64-encoded, as returned by
GenerateOAuth2String.
"""
print()
smtp_conn = smtplib.SMTP('smtp.gmail.com', 587)
smtp_conn.set_debuglevel(True)
smtp_conn.ehlo('test')
smtp_conn.starttls()
smtp_conn.docmd('AUTH', 'XOAUTH2 ' + base64.b64encode(auth_string))
def RequireOptions(options, *args):
missing = [arg for arg in args if getattr(options, arg) is None]
if missing:
print('Missing options: %s' % ' '.join(missing), file=sys.stderr)
sys.exit(-1)
def main(argv):
options_parser = SetupOptionParser()
(options, args) = options_parser.parse_args()
if options.refresh_token:
RequireOptions(options, 'client_id', 'client_secret')
response = RefreshToken(options.client_id, options.client_secret,
options.refresh_token)
if options.quiet:
print(response['access_token'])
else:
print('Access Token: %s' % response['access_token'])
print('Access Token Expiration Seconds: %s' % response['expires_in'])
elif options.generate_oauth2_string:
RequireOptions(options, 'user', 'access_token')
oauth2_string = GenerateOAuth2String(options.user, options.access_token)
if options.quiet:
print(oauth2_string.decode('utf-8'))
else:
print('OAuth2 argument:\n' + oauth2_string.decode('utf-8'))
elif options.generate_oauth2_token:
RequireOptions(options, 'client_id', 'client_secret')
print('To authorize token, visit this url and follow the directions:')
print(' %s' % GeneratePermissionUrl(options.client_id, options.scope))
authorization_code = input('Enter verification code: ')
response = AuthorizeTokens(options.client_id, options.client_secret,
authorization_code)
print('Refresh Token: %s' % response['refresh_token'])
print('Access Token: %s' % response['access_token'])
print('Access Token Expiration Seconds: %s' % response['expires_in'])
elif options.test_imap_authentication:
RequireOptions(options, 'user', 'access_token')
TestImapAuthentication(options.user,
GenerateOAuth2String(options.user, options.access_token,
base64_encode=False))
elif options.test_smtp_authentication:
RequireOptions(options, 'user', 'access_token')
TestSmtpAuthentication(options.user,
GenerateOAuth2String(options.user, options.access_token,
base64_encode=False))
else:
options_parser.print_help()
print('Nothing to do, exiting.')
return
if __name__ == '__main__':
main(sys.argv)

89
debian/changelog vendored
View File

@ -1,3 +1,92 @@
meli (0.8.5-rc.3-1) bookworm; urgency=low
* Update to 0.8.5-rc.3
-- Manos Pitsidianakis <manos@pitsidianak.is> Sun, 10 Dec 2023 15:22:18 +0000
meli (0.8.5-rc.2-1) bookworm; urgency=low
* Update to 0.8.5-rc.2
-- Manos Pitsidianakis <manos@pitsidianak.is> Mon, 4 Dec 2023 19:34:00 +0200
meli (0.8.4-1) bookworm; urgency=low
* Update to 0.8.4
-- Manos Pitsidianakis <manos@pitsidianak.is> Mon, 27 Nov 2023 19:34:00 +0200
meli (0.7.2-1) bullseye; urgency=low
Added
- Add forward mail option
- Add url_launcher config setting
- Add add_addresses_to_contacts command
- Add show_date_in_my_timezone pager config flag
- docs: add pager filter documentation
- mail/view: respect per-folder/account pager filter override
- pager: add filter command, esc to clear filter
- Show compile time features in with command argument
Fixed
- melib/email/address: quote display_name if it contains ","
- melib/smtp: fix Cc and Bcc ignored when sending mail
- melib/email/address: quote display_name if it contains "."
-- Manos Pitsidianakis <epilys@nessuent.xyz> Fri, 15 Oct 2021 12:34:00 +0200
meli (0.7.1-1) bullseye; urgency=low
Added
- Change all Down/Up shortcuts to j/k
- add 'GB18030' charset
- melib/nntp: implement refresh
- melib/nntp: update total/new counters on new articles
- melib/nntp: implement NNTP posting
- configs: throw error on extra unused conf flags in some imap/nntp
- configs: throw error on missing `composing` section with explanation
Fixed
- Fix compilation for netbsd-9.2
- conf: fixed some boolean flag values requiring to be string e.g. "true"
-- Manos Pitsidianakis <epilys@nessuent.xyz> Wed, 08 Sep 2021 18:14:00 +0200
meli (0.7.0-1) buster; urgency=low
-- Manos Pitsidianakis <epilys@nessuent.xyz> Fri, 03 Sep 2021 18:14:00 +0200
meli (0.6.2-1) buster; urgency=low
Added
- Add customizable mailbox tree in sidebar
- Make `dbus` dependency opt-out (feature is `dbus-notifications`)
- Implemented JMAP async, search, tagging, syncing
- Preserve account order from configuration file
- Implemented IMAP `CONDSTORE` support for IMAP cache
- Add `timeout` setting for IMAP
- Implement TCP keepalive for IMAP
- Rewrote email address parsers.
- Implement `copy_messages` for maildir
- Implement selection with motions
Fixed
- Fixed various problems with IMAP cache
- Fixed various problems with IMAP message counts
- Fixed various problems with IMAP connection hanging
- Fixed IMAP not reconnecting on dropped IDLE connections
- Fixed various problems with notmuch backend
-- Manos Pitsidianakis <epilys@nessuent.xyz> Thu, 24 Sep 2020 18:14:00 +0200
meli (0.6.1-1) buster; urgency=low
* added experimental NNTP backend
* added server extension support and use in account status tab
* imap: fixed IDLE connection getting stuck when using DEFLATE
-- Manos Pitsidianakis <epilys@nessuent.xyz> Sun, 02 Aug 2020 01:09:05 +0200
meli (0.6.0-1) buster; urgency=low
* Update to 0.6.0
-- Manos Pitsidianakis <epilys@nessuent.xyz> Wed, 29 Jul 2020 22:24:08 +0200
meli (0.5.1-1) buster; urgency=low
* Update to 0.5.1

1
debian/compat vendored
View File

@ -1 +0,0 @@
11

18
debian/control vendored
View File

@ -1,14 +1,20 @@
Source: meli
Section: mail
Priority: optional
Maintainer: Manos Pitsidianakis <epilys@nessuent.xyz>
Build-Depends: debhelper (>=11~), mandoc (>=1.14.4-1)
Maintainer: Manos Pitsidianakis <manos@pitsidianak.is>
Build-Depends: debhelper-compat (=13), mandoc (>=1.14.4-1), quilt, libsqlite3-dev
Standards-Version: 4.1.4
Homepage: https://meli.delivery
Rules-Requires-Root: no
Vcs-Git: https://git.meli-email.org/meli/meli.git
Vcs-Browser: https://git.meli-email.org/meli/meli
Homepage: https://meli-email.org
Package: meli
Architecture: any
Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Recommends: libnotmuch, xdg-utils (>=1.1.3-1)
Description: terminal mail client
Recommends: xdg-utils (>=1.1.3-1), w3m, mailcap
Suggests: libnotmuch5, notmuch, rss2email, xterm, neovim, msmtp
Provides: mail-reader, imap-client
Description: terminal mail client.
meli supports mbox, maildir, IMAP, JMAP, notmuch and NNTP (Usernet) with
TLS/SSL, SASL, GPG features.

4
debian/copyright vendored
View File

@ -1,11 +1,11 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: meli
Source: <https://git.meli.delivery/meli/meli>
Source: https://git.meli-email.org/meli/meli
#
# Please double check copyright with the licensecheck(1) command.
Files: *
Copyright: 2017-2020 Manos Pitsidianakis
Copyright: 2017-2023 Manos Pitsidianakis
License: GPL-3.0+
#----------------------------------------------------------------------------
# License file: COPYING

8
debian/extra/meli.desktop vendored 100644
View File

@ -0,0 +1,8 @@
[Desktop Entry]
Name=meli
Exec=meli
Categories=Office;Network;Email;
Comment=Terminal mail client
NoDisplay=false
Terminal=true
Type=Application

7
debian/meli.bug-presubj vendored 100644
View File

@ -0,0 +1,7 @@
WARNING: This package is not distributed by debian, it was generated from the source repository of meli.
Please do not report bugs to debian, but to the appropriate issue tracker for meli:
- https://git.meli-email.org/meli/meli/issues
- Send e-mail to the mailing list, "meli general" <meli-general@meli-email.org>
https://lists.meli-email.org/list/meli-general/

9
debian/meli.bug-script vendored 100755
View File

@ -0,0 +1,9 @@
#!/bin/sh
echo "Including output of \`meli -v\` and \`meli compiled-with\`..."
LC_ALL=C meli -v >&3
echo "\nEnabled compile-time features"
echo "-----------------------------"
LC_ALL=C meli compiled-with >&3 || true

5
debian/meli.doc-base vendored 100644
View File

@ -0,0 +1,5 @@
Document: meli
Title: meli E-mail Client Manual
Author: Various
Abstract: Manual for meli the terminal e-mail client.
Section: Network/Communication

7
debian/meli.docs vendored
View File

@ -1,3 +1,4 @@
meli.1
meli.conf.5
meli-themes.5
meli/docs/meli.1
meli/docs/meli.7
meli/docs/meli.conf.5
meli/docs/meli-themes.5

2
debian/meli.examples vendored 100644
View File

@ -0,0 +1,2 @@
meli/docs/samples/sample-config.toml
meli/docs/samples/themes

View File

@ -1,14 +1,16 @@
Description: Fix PREFIX env var in Makefile for use in Debian
Author: Manos Pitsidianakis <epilys@nessuent.xyz>
Last-Update: 2020-01-30
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@
# along with meli. If not, see <http://www.gnu.org/licenses/>.
Last-Update: 2023-03-06
Index: meli/Makefile
===================================================================
--- meli.orig/Makefile
+++ meli/Makefile
@@ -20,7 +20,7 @@
.SUFFIXES:
# Options
-PREFIX ?= /usr/local
+PREFIX ?= /usr
BINDIR ?= ${PREFIX}/bin
MANDIR ?= ${PREFIX}/share/man
EXPANDED_PREFIX := `cd ${PREFIX} && pwd -P`
BINDIR ?= ${EXPANDED_PREFIX}/bin
MANDIR ?= ${EXPANDED_PREFIX}/share/man

View File

@ -1 +1,2 @@
fix-prefix-for-debian.patch
usr_bin_editor.patch

View File

@ -0,0 +1,23 @@
From: Manos Pitsidianakis <manos@pitsidianak.is>
Date: Thu, 27 Feb 2014 16:06:15 +0100
Subject: usr_bin_editor
If EDITOR or VISUAL is not set, fall back to /usr/bin/editor,
which is set by update-alternatives.
---
meli/src/subcommands.rs | 1 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/meli/src/subcommands.rs
+++ b/meli/src/subcommands.rs
@@ -52,9 +52,7 @@
pub fn edit_config() -> Result<()> {
let editor = std::env::var("EDITOR")
.or_else(|_| std::env::var("VISUAL"))
- .map_err(|err| {
- format!("Could not find any value in environment variables EDITOR and VISUAL. {err}")
- })?;
+ .unwrap_or_else(|_| "/usr/bin/editor".into());
let config_path = crate::conf::get_config_file()?;
let mut cmd = Command::new(editor);

11
debian/rules vendored
View File

@ -1,14 +1,21 @@
#!/usr/bin/make -f
# You must remove unused comment lines for the released package.
#export DH_VERBOSE = 1
export RUSTUP_HOME=${HOME}/.rustup
export DH_VERBOSE = 1
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
export MELI_FEATURES = cli-docs sqlite3
#export MELI_FEATURES = cli-docs sqlite3
%:
dh $@ --with quilt
override_dh_auto_configure:
true
override_dh_auto_test:
true
#override_dh_auto_install:
# dh_auto_install -- prefix=/usr

4
fuzz/.gitignore vendored 100644
View File

@ -0,0 +1,4 @@
target
corpus
artifacts

1377
fuzz/Cargo.lock generated 100644

File diff suppressed because it is too large Load Diff

21
fuzz/Cargo.toml 100644
View File

@ -0,0 +1,21 @@
[package]
name = "melib-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[[bin]]
name = "envelope_parse"
path = "fuzz_targets/envelope_parse.rs"
[dependencies]
libfuzzer-sys = "0.3"
melib = { path = "../melib" }
# Prevent this from interfering with workspaces
[workspace]
members = ["."]

View File

@ -0,0 +1,25 @@
","
";"
"<"
">"
"@"
":"
# tab character
"\x09"
# new line character
"\x0A"
" "
"Subject: "
"Subject"
"To"
"To: "
"Date"
"Date: "
"Message-Id"
"Message-Id: "
"From"
"From: "
"Cc"
"Cc: "
"Bcc"
"Bcc: "

View File

@ -0,0 +1,11 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
extern crate melib;
use melib::Envelope;
fuzz_target!(|data: &[u8]| {
// fuzzed code goes here
let _envelope = Envelope::from_bytes(data, None);
});

520
meli.1
View File

@ -1,520 +0,0 @@
.\" meli - meli.1
.\"
.\" Copyright 2017-2019 Manos Pitsidianakis
.\"
.\" This file is part of meli.
.\"
.\" meli is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
.\"
.Dd July 29, 2019
.Dt MELI 1
.Os
.Sh NAME
.Nm meli
.Nd Meli Mail User Agent. meli is the Greek word for honey
.Sh SYNOPSIS
.Nm
.Op Fl -help | h
.Op Fl -version | v
.Op Fl -create-config Op Ar path
.Op Fl -test-config Op Ar path
.Op Fl -config Ar path
.Op Fl -print-default-theme
.Op Fl -print-loaded-themes
.Bl -tag -width flag -offset indent
.It Fl -help | h
Show help message and exit.
.It Fl -version | v
Show version and exit.
.It Fl -create-config Op Ar path
Create configuration file in
.Pa path
if given, or at
.Pa $XDG_CONFIG_HOME/meli/config.toml
.It Fl -test-config Op Ar path
Test a configuration file for syntax issues or missing options.
.It Fl -config Ar path
Start meli with given configuration file.
.It Fl -print-default-theme
Print default theme keys and values in TOML syntax, to be used as a blueprint.
.It Fl -print-loaded-themes
Print all loaded themes in TOML syntax.
.El
.Sh DESCRIPTION
.Nm
is a terminal mail client aiming for extensive and user-frendly configurability.
.Bd -literal
^^ .-=-=-=-. ^^
^^ (`-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^ ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
( `-=-=-=-(@)-=-=-` ) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`)
^^ (`-=-=-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-`)
`-=-=-=-=-` ^^
.Ed
.Sh STARTING WITH meli
When launched for the first time,
.Nm
will search for its configuration directory,
.Pa $XDG_CONFIG_HOME/meli/ Ns
\&.
If it doesn't exist, you will be asked if you want to create one along with a sample configuration.
The sample configuration
.Pa $XDG_CONFIG_HOME/meli/config.toml
includes comments with the basic settings required for setting up accounts allowing you to copy and edit right away.
See
.Xr meli.conf 5
for the available configuration options.
.Pp
At any time, you may press
.Cm \&?
to show a searchable list of all available actions and shortcuts, along with every possible setting and command that your version supports.
.Pp
The main visual navigation tool is the left-side sidebar.
The menu's visibility may be toggled with
.Cm `
(shortcuts.listing:
.Ic toggle_menu_visibility Ns
).
.Pp
The view into each folder has 4 modes: plain, threaded, conversations and compact.
Plain views each mail indvidually, threaded shows their thread relationship visually, and conversations includes one entry per thread of emails (compact is one row per thread).
.Pp
If you're using a light color palette in your terminal, you may set
.Em theme = "light"
in the
.Em terminal
section of your configuration.
See
.Xr meli-themes 5
for complete documentation on user themes.
.Sh VIEWING MAIL
Open attachments by typing their index in the attachments list and then
.Cm a Ns
\&.
.Nm
will attempt to open text inside its pager and other content via
.Cm xdg-open Ns
\&.
Press
.Cm m
instead to use the mailcap entry for the MIME type of the attachment, if any.
See
.Sx FILES
for the location of the mailcap files and
.Xr mailcap 5
for their syntax.
.Sh SEARCH
Each e-mail storage backend has its default search method.
.Em IMAP
uses the SEARCH command,
.Em notmuch
uses libnotmuch and
.Em Maildir/mbox
have to do a slow linear search.
Thus it is advised to use a cache on
.Em Maildir/mbox
accounts.
.Nm Ns
, if built with sqlite3, includes the ability to perform full text search on the following fields: From, To, Cc, Bcc, In-Reply-To, References, Subject and Date.
The message body (in plain text human readable form) and the flags can also be queried.
To enable sqlite3 indexing for an account set
.Em cache_type
to
.Em sqlite3
in the configuration file and to create the sqlite3 index issue command
.Cm index Ar ACCOUNT_NAME Ns \&.
.sp
To search in the message body type your keywords without any special formatting.
To search in specific fields, prepend your search keyword with "field:" like so:
.Pp
.D1 subject:helloooo or subject:\&"call for help\&" or \&"You remind me today of a small, Mexican chihuahua.\&"
.Pp
.D1 not ((from:unrealistic and (to:complex or not "query")) or flags:seen,draft)
.Pp
.D1 alladdresses:mailing@list.tld and cc:me@domain.tld
.Pp
Boolean operators are
.Em or Ns
,
.Em and
and
.Em not
.Po
alias:
.Em \&!
.Pc
String keywords with spaces must be quoted.
Quotes should always be escaped.
.sp
.Sy Important Notice about IMAP/JMAP
.sp
To prevent downloading all your messages from your IMAP/JMAP server, don't set
.Em cache_type
to
.Em sqlite3 Ns
\&.
.Nm
will relay your queries to the IMAP server.
Expect a delay between query and response.
Sqlite3 on the contrary at reasonable mailbox sizes should have a non noticable delay.
.Sh TAGS
.Nm
supports tagging in notmuch and IMAP/JMAP backends.
Tags can be searched with the `tags:` or `flags:` prefix in a search query, and can be modified by
.Cm tag add TAG
and
.Cm tag remove TAG
(see
.Xr meli.conf 5 TAGS Ns
, settings
.Ic colors
and
.Ic ignore_tags
for how to set tag colors and tag visiblity)
.Sh COMPOSING
To send mail, press
.Cm m
while viewing the appropriate account to open a new composing tab.
To reply to a mail, press
.Cm R Ns
\&.
You may edit some of the header fields from within the view, by selecting with the arrow keys and pressing
.Cm enter
to enter
.Ar INSERT
mode.
At any time you may press
.Cm e
to launch your editor (see
.Xr meli.conf 5 COMPOSING Ns
, setting
.Ic editor_cmd
for how to select which editor to launch).
Attachments may be handled with the
.Em add-attachment Ns
,
.Em remove-attachment
commands (see below).
Finally, pressing
.Cm s
will send your message by piping it into a binary of your choosing (see
.Xr meli.conf 5 COMPOSING Ns
, setting
.Ic mailer_cmd Ns
).
To save your draft without sending it, issue command
.Cm close
and select 'save as draft'.
.Pp
With no Draft or Sent folder,
.Nm
tries first saving mail in your INBOX and then at any other folder.
On complete failure to save your draft or sent message it will be saved in your
.Em tmp
directory instead and you will be notified of its location.
.Pp
To open a draft for editing later, select your draft in the mail listing and press
.Cm e Ns
\&.
.sp
Your editor can be used in
.Nm Ns
\&'s embed terminal emulator by setting
.Ic embed
to
.Em true
in your composing settings.
When launched, your editor captures all input until it exits or stops.
To stop your editor and return to
.Nm
issue Ctrl-z and to resume editing press the
.Ic edit_mail
command again (default
.Em e Ns
).
.Sh CONTACTS
.Nm
supports two kinds of contact backends:
.sp
.Bl -enum -compact -offset indent
.It
an internal format that gets saved under
.Pa $XDG_DATA_HOME/meli/account_name/addressbook Ns
\&.
.It
vCard files (v3, v4) through the
.Ic vcard_folder
option in the account section.
The path defined as
.Ic vcard_folder
can hold multiple vCards per file.
They are loaded read only.
.El
.sp
See
.Xr meli.conf 5 ACCOUNTS
for the complete account configuration values.
.Sh EXECUTE mode
Commands are issued in EXECUTE mode, by default started with Space and exited with Escape key.
.Pp
the following commands are valid in the mail listing context:
.Bl -tag -width 36n
.It Cm set Ar plain | threaded | compact | conversations
set the way mailboxes are displayed
.El
.TS
allbox tab(:);
lb l.
conversations:shows one entry per thread
compact:shows one row per thread
threaded:shows threads as a tree structure
plain:shows one row per mail, regardless of threading
.TE
.Bl -tag -width 36n
.It Cm sort Ar subject | date \ Ar asc | desc
sort mail listing
.It Cm subsort Ar subject | date \ Ar asc | desc
sorts only the first level of replies.
.It Cm go Ar n
where
.Ar n
is a mailbox prefixed with the
.Ar n
number in the side menu for the current account
.It Cm toggle_thread_snooze
don't issue notifications for thread under cursor in thread listing
.It Cm filter Ar STRING
filter mailbox with
.Ar STRING
key.
Escape exits filter results
.It Cm set read, set unread
.It Cm create-folder Ar ACCOUNT Ar FOLDER_PATH
create folder with given path.
Be careful with backends and separator sensitivity (eg IMAP)
.It Cm subscribe-folder Ar ACCOUNT Ar FOLDER_PATH
subscribe to folder with given path
.It Cm unsubscribe-folder Ar ACCOUNT Ar FOLDER_PATH
unsubscribe to folder with given path
.It Cm rename-folder Ar ACCOUNT Ar FOLDER_PATH_SRC Ar FOLDER_PATH_DEST
rename folder
.It Cm delete-folder Ar ACCOUNT Ar FOLDER_PATH
delete folder
.El
.Pp
envelope view commands:
.Bl -tag -width 36n
.It Cm pipe Ar EXECUTABLE Ar ARGS
pipe pager contents to binary
.It Cm list-post
post in list of viewed envelope
.It Cm list-unsubscribe
unsubscribe automatically from list of viewed envelope
.It Cm list-archive
open list archive with
.Cm xdg-open
.El
.Pp
composing mail commands:
.Bl -tag -width 36n
.It Cm add-attachment Ar PATH
in composer, add
.Ar PATH
as an attachment
.It Cm remove-attachment Ar INDEX
remove attachment with given index
.It Cm toggle sign
toggle between signing and not signing this message.
If the gpg invocation fails then the mail won't be sent.
.El
.Pp
generic commands:
.Bl -tag -width 36n
.It Cm open-in-tab
opens envelope view in new tab
.It Cm close
closes closeable tabs
.It Cm setenv Ar KEY=VALUE
set environment variable
.Ar KEY
to
.Ar VALUE
.It Cm printenv Ar KEY
print environment variable
.Ar KEY
.El
.Sh SHORTCUTS
Non-complete list of shortcuts and their default values.
.Bl -tag -width 36n
.It Ic open_thread
\&'\\n'
.It Ic exit_thread
\&'i'
.It Ic create_contact
\&'c'
.It Ic edit_contact
\&'e'
.It Ic prev_page
PageUp,
.It Ic next_page
PageDown
.It Ic prev_folder
\&'K'
.It Ic next_folder
\&'J'
.It Ic prev_account
\&'l'
.It Ic next_account
\&'h'
.It Ic new_mail
\&'m'
.It Ic scroll_up
\&'k'
.It Ic scroll_down
\&'j'
.It Ic page_up
PageUp
.It Ic page_down
PageDown
.It Ic toggle-menu-visibility
\&'`'
.It Ic select
\&'v'
.It Ic `
toggles hiding of sidebar in mail listings
.It Ic \&?
opens up a shortcut window that shows available actions in the current component you are using (eg mail listing, contact list, mail composing)
.It Ic m
starts a new mail composer
.It Ic R
replies to the viewed mail.
.It Ic u
displays numbers next to urls in the body text of an email and
.Ar n Ns Ic g
opens the
.Ar n Ns
th
url with xdg-open
.It Ar n Ns Ic a
opens the
.Ar n Ns
th
attachment.
.It Ar n Ns Ic m
opens the
.Ar n Ns
th
attachment according to its mailcap entry.
.It Ic v
(un)selects mail entries in mail listings
.El
.Sh EXIT STATUS
.Nm
exits with 0 on a successful run.
Other exit statuses are:
.Bl -tag -width 2n
.It 1
catchall for general errors
.El
.Sh ENVIRONMENT
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Ev EDITOR
Specifies the editor to use
.It Ev MELI_CONFIG
Override the configuration file
.It Ev NO_COLOR
When present (regardless of its value), prevents the addition of ANSI color.
The configuration value
.Ic use_color
overrides this.
.El
.Sh FILES
.Nm
uses the following parts of the XDG standard:
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Ev XDG_CONFIG_HOME
defaults to
.Pa ~/.config/
.It Ev XDG_CACHE_HOME
defaults to
.Pa ~/.cache/
.El
.Pp
and appropriates the following locations:
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Pa $XDG_CONFIG_HOME/meli/
User configuration directory.
.It Pa $XDG_CONFIG_HOME/meli/config.toml
User configuration file.
See
.Xr meli.conf 5
for its syntax and values.
.It Pa $XDG_CONFIG_HOME/meli/hooks/*
Reserved for event hooks.
.It Pa $XDG_CONFIG_HOME/meli/plugins/*
Reserved for plugin files.
.It Pa $XDG_CACHE_HOME/meli/*
Internal cached data used by meli.
.It Pa $XDG_DATA_HOME/meli/*
Internal data used by meli.
.It Pa $XDG_DATA_HOME/meli/meli.log
Operation log.
.It Pa /tmp/meli/*
Temporary files generated by
.Nm Ns
\&.
.El
.Pp
Mailcap entries are searched for in the following files, in this order:
.Pp
.Bl -enum -compact -offset indent
.It
.Pa $XDG_CONFIG_HOME/meli/mailcap
.It
.Pa $XDG_CONFIG_HOME/.mailcap
.It
.Pa $HOME/.mailcap
.It
.Pa /etc/mailcap
.It
.Pa /usr/etc/mailcap
.It
.Pa /usr/local/etc/mailcap
.El
.Sh SEE ALSO
.Xr meli.conf 5 ,
.Xr meli-themes 5 ,
.Xr xdg-open 1 ,
.Xr mailcap 5
.Sh CONFORMING TO
XDG Standard
.Aq https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html Ns
, maildir
.Aq https://cr.yp.to/proto/maildir.html Ns
, IMAPv4rev1 RFC3501, The JSON Meta Application Protocol (JMAP) RFC8620, The JSON Meta Application Protocol (JMAP) for Mail RFC8621.
.Sh AUTHORS
Copyright 2017-2019
.An Manos Pitsidianakis Aq epilys@nessuent.xyz
Released under the GPL, version 3 or greater.
This software carries no warranty of any kind.
(See COPYING for full copyright and warranty notices.)
.Pp
.Aq https://meli.delivery

View File

@ -1,697 +0,0 @@
.\" meli - meli.conf.5
.\"
.\" Copyright 2017-2019 Manos Pitsidianakis
.\"
.\" This file is part of meli.
.\"
.\" meli is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
.\"
.Dd September 16, 2019
.Dt MELI.CONF 5
.Os
.Sh NAME
.Nm meli.conf
.Nd configuration file for the Meli Mail User Agent
.Sh SYNOPSIS
.Pa $XDG_CONFIG_HOME/meli/config.toml
.Sh DESCRIPTION
Configuration for meli is written in TOML.
Few things to consider before writing TOML (quoting the spec):
.Pp
.Bl -bullet -compact
.It
TOML is case sensitive.
.It
A TOML file must be a valid UTF-8 encoded Unicode document.
.It
Whitespace means tab (0x09) or space (0x20).
.It
Newline means LF (0x0A) or CRLF (0x0D 0x0A).
.El
.Pp
Refer to TOML documentation for valid TOML syntax.
.sp
Though not part of TOML syntax,
.Nm
can have nested configuration files by using the following
.Xr m4 1
include macro:
.Dl include(\&"/path/to/file\&")
.Sh SECTIONS
The top level sections of the config are accounts, shortcuts, notifications, pager, listing, composing, pgp, terminal.
.Sh EXAMPLES
example configuration
.sp
.Bd -literal
# Setting up a Maildir account
[accounts.account-name]
root_folder = "/path/to/root/folder"
format = "Maildir"
index_style = "Compact"
identity="email@address.tld"
subscribed_folders = ["folder", "folder/Sent"] # or [ "*", ] for all folders
display_name = "Name"
# Set folder-specific settings
[accounts.account-name.folders]
"INBOX" = { alias="Inbox" } #inline table
"drafts" = { alias="Drafts" } #inline table
[accounts.account-name.folders."foobar-devel"] # or a regular table
ignore = true # don't show notifications for this folder
# Setting up an mbox account
[accounts.mbox]
root_folder = "/var/mail/username"
format = "mbox"
index_style = "Compact"
identity="username@hostname.local"
[pager]
filter = "/usr/bin/pygmentize"
html_filter = "w3m -I utf-8 -T text/html"
[notifications]
script = "notify-send"
[composing]
# required for sending e-mail
mailer_cmd = 'msmtp --read-recipients --read-envelope-from'
editor_cmd = 'vim +/^$'
[shortcuts]
[shortcuts.composing]
edit_mail = 'e'
[shortcuts.listing]
new_mail = 'm'
set_seen = 'n'
[terminal]
theme = "light"
.Ed
.Pp
available options are listed below.
.Sy default values are shown in parentheses.
.Sh ACCOUNTS
.Bl -tag -width 36n
.It Ic root_folder Ar String
the backend-specific path of the root_folder, usually INBOX.
.It Ic format Ar String Op maildir mbox imap notmuch jmap
the format of the mail backend.
.It Ic subscribed_folders Ar [String,]
an array of folder paths to display in the UI.
Paths are relative to the root folder (eg "INBOX/Sent", not "Sent").
The glob wildcard
.Em \&*
can be used to match every folder name and path.
.It Ic identity Ar String
your e-mail address that is inserted in the From: headers of outgoing mail
.It Ic index_style Ar String
set the way mailboxes are displayed
.El
.TS
allbox tab(:);
lb l.
conversations:shows one entry per thread
compact:shows one row per thread
threaded:shows threads as a tree structure
plain:shows one row per mail, regardless of threading
.TE
.Bl -tag -width 36n
.It Ic display_name Ar String
(optional) a name which can be combined with your address:
"Name <email@address.tld>"
.It Ic read_only Ar boolean
attempt to not make any changes to this account.
.Pq Em false
.It Ic manual_refresh Ar boolean
(optional) if true, do not monitor account for changes (shortcut listing.refresh)
.Pq Em false
.It Ic refresh_command Ar String
(optional) command to execute when manually refreshing (shortcut listing.refresh)
.Pq Em None
.It Ic cache_type Ar String
(optional) choose which cache backend to use.
Available options are 'none' and 'sqlite3'
.Pq Em "sqlite3"
.It Ic vcard_folder Ar String
(optional) Folder that contains .vcf files.
They are parsed and imported read-only.
.It Ic folders Ar folder_config
(optional) configuration for each folder.
Its format is described below in
.Sx FOLDERS Ns
\&.
.El
.Sh notmuch only
.Ic root_folder
points to the directory which contains the
.Pa .notmuch/
subdirectory.
notmuch folders are virtual, since they are defined by user-given notmuch queries.
Thus you have to explicitly state the folders you want in the
.Ic folders
field and set the
.Ar query
property to each of them.
Example:
.Bd -literal
[accounts.notmuch]
format = "notmuch"
\&...
[accounts.notmuch.folders]
"INBOX" = { query="tag:inbox", subscribe = true }
"Drafts" = { query="tag:draft", subscribe = true }
"Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
.Ed
.Sh IMAP only
IMAP specific options are:
.Bl -tag -width 36n
.It Ic server_hostname Ar String
example:
.Qq mail.example.tld
.It Ic server_username Ar String
.It Ic server_password Ar String
.It Ic server_port Ar number
(optional)
.\" default value
.Pq Em 143
.It Ic use_starttls Ar boolean
(optional) if port is 993 and use_starttls is unspecified, it becomes false by default.
.\" default value
.Pq Em true
.It Ic danger_accept_invalid_certs Ar boolean
(optional) do not validate TLS certificates.
.\" default value
.Pq Em false
.El
.Sh JMAP only
JMAP specific options are:
.Bl -tag -width 36n
.It Ic server_hostname Ar String
example:
.Qq mail.example.tld
.It Ic server_username Ar String
.It Ic server_password Ar String
.It Ic server_port Ar number
(optional)
.\" default value
.Pq Em 443
.It Ic danger_accept_invalid_certs Ar boolean
(optional) do not validate TLS certificates.
.\" default value
.Pq Em false
.El
.Sh FOLDERS
.Bl -tag -width 36n
.It Ic alias Ar String
(optional) show a different name for this folder in the UI
.It Ic autoload Ar boolean
(optional) load this folder on startup (not functional yet)
.It Ic subscribe Ar boolean
(optional) watch this folder for updates
.\" default value
.Pq Em true
.It Ic ignore Ar boolean
(optional) silently insert updates for this folder, if any
.\" default value
.Pq Em false
.It Ic usage Ar boolean
(optional) special usage of this folder.
Valid values are:
.Bl -bullet -compact
.It
.Ar Normal
.It
.Ar Inbox
.It
.Ar Archive
.It
.Ar Drafts
.It
.Ar Flagged
.It
.Ar Junk
.It
.Ar Sent
.It
.Ar Trash
.El
otherwise usage is inferred from the folder title.
.It Ic conf_override Ar boolean
(optional) override global settings for this folder.
Available sections to override are
.Em pager, notifications, shortcuts, composing
and the account options
.Em identity and index_style Ns
\&.
Example:
.Bd -literal
[accounts."imap.domain.tld".folders."INBOX"]
index_style = "plain"
[accounts."imap.domain.tld".folders."INBOX".pager]
filter = ""
.Ed
.El
.Sh COMPOSING
.Bl -tag -width 36n
.It Ic mailer_cmd Ar String
command to pipe new mail to, exit code must be 0 for success.
.It Ic editor_cmd Ar String
command to launch editor.
Can have arguments.
Draft filename is given as the last argument.
If it's missing, the environment variable $EDITOR is looked up.
.It Ic embed Ar boolean
(optional) embed editor within meli
.\" default value
.Pq Em false
.It Ic format_flowed Ar boolean
(optional) set format=flowed [RFC3676] in text/plain attachments.
.\" default value
.Pq Em true
.El
.Sh SHORTCUTS
Shortcuts can take the following values:
.Qq Em Backspace
.Qq Em Left
.Qq Em Right
.Qq Em Up
.Qq Em Down
.Qq Em Home
.Qq Em End
.Qq Em PageUp
.Qq Em PageDown
.Qq Em Delete
.Qq Em Insert
.Qq Em Enter
.Qq Em Tab
.Qq Em Esc
.Qq Em F1..F12
.Qq Em M-char
.Qq Em C-char
and
.Qq Em char Ns
, where char is a single character string.
.sp
The headings before each list indicate the map key of the shortcut list.
For example for the first list titled
.Em general
the configuration is typed as follows:
.Bd -literal
[shortcuts.general]
next_tab = 'T'
.Ed
.sp
and for
.Em compact-listing Ns
:
.Bd -literal
[shortcuts.compact-listing]
open_thread = "Enter"
exit_thread = 'i'
.Ed
.sp
.Pp
.Em general
.Bl -tag -width 36n
.It Ic next_tab
Go to next tab.
.\" default value
.Pq Em T
.It Ic go_to_tab
Go to the
.Em n Ns
th tab
.Pq Em cannot be redefined
.El
.sp
.Em listing
.Bl -tag -width 36n
.It Ic prev_page
Go to previous page.
.\" default value
.Pq Em PageUp
.It Ic next_page
Go to next page.
.\" default value
.Pq Em PageDown
.It Ic prev_folder
Go to previous folder.
.\" default value
.Pq Em K
.It Ic next_folder
Go to next folder.
.\" default value
.Pq Em J
.It Ic prev_account
Go to previous account.
.\" default value
.Pq Em l
.It Ic next_account
Go to next account.
.\" default value
.Pq Em h
.It Ic new_mail
Start new mail draft in new tab
.\" default value
.Pq Em m
.It Ic set_seen
Set thread as seen.
.\" default value
.Pq Em n
.It Ic refresh
Manually request a folder refresh.
.\" default value
.Pq Em F5
.It Ic search
Search within list of e-mails.
.\" default value
.Pq Em /
.It Ic toggle_menu_visibility
Toggle visibility of side menu in mail list.
.\" default value
.Pq Em `
.El
.sp
.Em compact-listing
.Bl -tag -width 36n
.It Ic exit_thread
Exit thread view
.\" default value
.Pq Em i
.It Ic open_thread
Open thread.
.\" default value
.Pq Em Enter
.It Ic select_entry
Select thread entry.
.\" default value
.Pq Em v
.El
.sp
.Em pager
.Bl -tag -width 36n
.It Ic scroll_up
Scroll up pager.
.\" default value
.Pq Em k
.It Ic scroll_down
Scroll down pager.
.\" default value
.Pq Em j
.It Ic page_up
Go to previous pager page
.\" default value
.Pq Em PageUp
.It Ic page_down
Go to next pager pag
.\" default value
.Pq Em PageDown
.El
.sp
.Em contact-list
.Bl -tag -width 36n
.It Ic create_contact
Create new contact.
.\" default value
.Pq Em c
.It Ic edit_contact
Edit contact under cursor
.\" default value
.Pq Em e
.It Ic mail_contact
Mail contact under cursor
.\" default value
.Pq Em m
.It Ic toggle_menu_visibility
Toggle visibility of side menu in mail list.
.\" default value
.Pq Em `
.El
.sp
.sp
.Em composing
.Bl -tag -width 36n
.It Ic send_mail
Deliver draft to mailer
.\" default value
.Pq Em s
.It Ic edit_mail
Edit mail.
.\" default value
.Pq Em e
.El
.sp
.Em envelope-view
.Pp
To "select" an attachment, type its index (you will see the typed result in the command buffer on your bottom right of the status line) and then issue the corresponding command.
.Bl -tag -width 36n
.It Ic add_addresses_to_contacts Ns
Select addresses from envelope to add to contacts.
.\" default value
.Pq Em c
.It Ic view_raw_source
View raw envelope source in a pager.
.\" default value
.Pq Em M-r
.It Ic reply
Reply to envelope.
.\" default value
.Pq Em R
.It Ic edit
Open envelope in composer.
.\" default value
.Pq Em e
.It Ic return_to_normal_view
Return to envelope if viewing raw source or attachment.
.\" default value
.Pq Em r
.It Ic open_attachment
Opens selected attachment with
.Cm xdg-open
.\" default value
.Pq Em a
.It Ic open_mailcap
Opens selected attachment according to its mailcap entry.
See
.Xr meli.1 FILES
for the mailcap file locations.
.\" default value
.Pq Em m
.It Ic go_to_url
Go to url of given index
.\" default value
.Pq Em g
.It Ic toggle_url_mode
Toggles url open mode.
When active, it prepends an index next to each url that you can select by typing and open by issuing
.Ic go_to_url
.\" default value
.Pq Em u
.It Ic toggle_expand_headers
Expand extra headers (References and others)
.\" default value
.Pq Em h
.El
.sp
.Em thread-view
.Bl -tag -width 36n
.It Ic reverse_thread_order
Reverse thread order.
.\" default value
.Pq Em r
.It Ic toggle_mailview
Toggle mail view visibility.
.\" default value
.Pq Em p
.It Ic toggle_threadview
Toggle thread view visibility.
.\" default value
.Pq Em t
.It Ic collapse_subtree
Collapse thread branches.
.\" default value
.Pq Em h
.It Ic prev_page
Go to previous page.
.\" default value
.Pq Em PageUp
.It Ic next_page
Go to next page.
.\" default value
.Pq Em PageDown
.El
.sp
.Sh NOTIFICATIONS
.Bl -tag -width 36n
.It Ic enable Ar boolean
enable freedesktop-spec notifications.
this is usually what you want
.\" default value
.Pq Em true
.It Ic script Ar String
(optional) script to pass notifications to, with title as 1st arg and body as 2nd
.\" default value
.Pq Em none
.It Ic xbiff_file_path Ar String
(optional) file that gets its size updated when new mail arrives
.Pq Em none
.\" default value
.It Ic play_sound Ar boolean
(optional) play theme sound in notifications if possible
.Pq Em false
.\" default value
.It Ic sound_file Ar String
(optional) play sound file in notifications if possible
.\" default value
.Pq Em none
.El
.Sh PAGER
.Bl -tag -width 36n
.It Ic pager_context Ar num
(optional) number of context lines when going to next page.
(Unimplemented)
.\" default value
.Pq Em 0
.It Ic headers_sticky Ar boolean
(optional) always show headers when scrolling.
.\" default value
.Pq Em true
.It Ic html_filter Ar String
(optional) pipe html attachments through this filter before display
.\" default value
.Pq Em none
.It Ic filter Ar String
(optional) a command to pipe mail output through for viewing in pager.
.\" default value
.Pq Em none
.It Ic format_flowed Ar bool
(optional) respect format=flowed
.\" default value
.Pq Em true
.It Ic split_long_lines Ar bool
(optional) Split long lines that would overflow on the x axis.
.\" default value
.Pq Em true
.It Ic minimum_width Ar num
(optional) Minimum text width in columns.
.\" default value
.Pq Em 80
.El
.Sh LISTING
.Bl -tag -width 36n
.It Ic context_lines Ar num
(optional) number of context lines when going to next page.
(Unimplemented)
.\" default value
.Pq Em 0
.It Ic datetime_fmt Ar String
(optional) datetime formatting passed verbatim to strftime(3).
.\" default value
.Pq Em \&%Y-\&%m-\&%d \&%T
.It Ic recent_dates Ar Boolean
(optional) Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
.\" default value
.Pq Em true
.El
.Sh TAGS
.Bl -tag -width 36n
.It Ic colours Ar hash table String[Color]
(optional) set UI colors for tags
.It Ic ignore_tags Ar Array String
(optional) hide tags (not the tagged messages themselves)
.El
.sp
example configuration:
.sp
.Bd -literal
[tags]
# valid inputs: #HHHHHH, #ABC -> #AABBCC, XTERM_NAME, 0-255 byte
colors = { signed="#Ff6600", replied="DeepSkyBlue4", draft="#f00", replied="8" }
[accounts.dummy]
\&...
[accounts.dummy.folders]
# per folder override:
"INBOX" = { tags.ignore_tags=["inbox", ] }
.Ed
.Sh PGP
.Bl -tag -width 36n
.It Ic auto_verify_signatures Ar boolean
auto verify signed e-mail according to RFC3156
.\" default value
.Pq Em true
.It Ic auto_sign Ar boolean
(optional) always sign sent messages
.\" default value
.Pq Em false
.It Ic key Ar String
(optional) key to be used when signing/encrypting (not functional yet)
.\" default value
.Pq Em none
.It Ic gpg_binary Ar String
(optional) gpg binary name or file location to use
.\" default value
.Pq Em "gpg2"
.El
.Sh TERMINAL
.Bl -tag -width 36n
.It Ic theme Ar String
(optional) select between these themes: light / dark
.\" default value
.Pq Em dark
.It Ic ascii_drawing Ar boolean
(optional) if true, box drawing will be done with ascii characters.
.\" default value
.Pq Em false
.It Ic use_color Ar boolean
(optional) if false, no ANSI colors are used
.\" default value
.Pq Em true
.It Ic window_title Ar String
(optional) set window title in xterm compatible terminals (empty string means no window title is set)
.\" default value
.Pq Em "meli"
.It Ic themes Ar hash table String[String[Attribute]]
define UI themes.
See
.Xr meli-themes 5
for details.
.Bd -literal
[terminal]
theme = "themeB"
[terminal.themes.themeA]
"mail.view.body" = {fg = "HotPink3", bg = "LightSalmon1"}
\&...
[terminal.themes.themeB]
"mail.view.body" = {fg = "CadetBlue", bg = "White"}
\&...
[terminal.themes.themeC]
\&...
.Ed
.El
.Sh SEE ALSO
.Xr meli 1 ,
.Xr meli-themes 5
.Sh CONFORMING TO
TOML Standard v.0.5.0 https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
.Sh AUTHORS
Copyright 2017-2019
.An Manos Pitsidianakis Aq epilys@nessuent.xyz
Released under the GPL, version 3 or greater.
This software carries no warranty of any kind.
(See COPYING for full copyright and warranty notices.)
.Pp
.Aq https://meli.delivery

92
meli/Cargo.toml 100644
View File

@ -0,0 +1,92 @@
[package]
name = "meli"
version = "0.8.5-rc.3"
authors = ["Manos Pitsidianakis <manos@pitsidianak.is>"]
edition = "2021"
rust-version = "1.68.2"
license = "GPL-3.0-or-later"
readme = "README.md"
description = "terminal e-mail client"
homepage = "https://meli-email.org"
repository = "https://git.meli-email.org/meli/meli.git"
keywords = ["mail", "mua", "maildir", "terminal", "imap"]
categories = ["command-line-utilities", "email"]
default-run = "meli"
[[bin]]
name = "meli"
path = "src/main.rs"
[lib]
name = "meli"
path = "src/lib.rs"
[dependencies]
async-task = "^4.2.0"
bitflags = { version = "2.4", features = ["serde"] }
crossbeam = { version = "^0.8" }
flate2 = { version = "1", optional = true }
futures = "0.3.5"
indexmap = { version = "^1.6", features = ["serde-1"] }
libc = { version = "0.2.125", default-features = false, features = ["extra_traits"] }
libz-sys = { version = "1.1", features = ["static"], optional = true }
linkify = { version = "^0.10", default-features = false }
melib = { path = "../melib", version = "0.8.5-rc.3", features = [] }
nix = { version = "0.27", default-features = false, features = ["signal", "poll", "term", "ioctl", "process"] }
num_cpus = "1.12.0"
serde = "1.0.71"
serde_derive = "1.0.71"
serde_json = "1.0"
signal-hook = { version = "^0.3", default-features = false, features = ["iterator"] }
signal-hook-registry = { version = "1.2.0", default-features = false }
smallvec = { version = "^1.5.0", features = ["serde"] }
structopt = { version = "0.3.14", default-features = false }
svg_crate = { version = "^0.13", optional = true, package = "svg" }
termion = { version = "1.5.1", default-features = false }
toml = { version = "0.8", default-features = false, features = ["display","preserve_order","parse"] }
xdg = "2.1.0"
[dependencies.pcre2]
# An [env] entry in .cargo/config.toml should force a static build instead of
# looking for a system library.
version = "0.2.3"
optional = true
[features]
default = ["sqlite3", "notmuch", "smtp", "dbus-notifications", "gpgme", "cli-docs", "jmap", "static"]
notmuch = ["melib/notmuch"]
jmap = ["melib/jmap"]
sqlite3 = ["melib/sqlite3"]
smtp = ["melib/smtp"]
smtp-trace = ["smtp", "melib/smtp-trace"]
regexp = ["dep:pcre2"]
dbus-notifications = ["dep:notify-rust"]
cli-docs = ["dep:flate2"]
svgscreenshot = ["dep:svg_crate"]
gpgme = ["melib/gpgme"]
# Static / vendoring features.
tls-static = ["melib/tls-static"]
http-static = ["melib/http-static"]
sqlite3-static = ["melib/sqlite3-static"]
dbus-static = ["dep:notify-rust", "notify-rust?/d_vendored"]
libz-static = ["dep:libz-sys", "libz-sys?/static"]
static = ["tls-static", "http-static", "sqlite3-static", "dbus-static", "libz-static"]
# Print tracing logs as meli runs in stderr
# enable for debug tracing logs: build with --features=debug-tracing and export MELI_DEBUG_STDERR
debug-tracing = ["melib/debug-tracing"]
[build-dependencies]
flate2 = { version = "1", optional = true }
proc-macro2 = "1.0.37"
quote = "^1.0"
regex = "1"
syn = { version = "1", features = [] }
[dev-dependencies]
flate2 = { version = "1" }
regex = "1"
tempfile = "3.3"
[target.'cfg(target_os="linux")'.dependencies]
notify-rust = { version = "^4", default-features = false, features = ["dbus"], optional = true }

1
meli/README.md 120000
View File

@ -0,0 +1 @@
../README.md

85
meli/build.rs 100644
View File

@ -0,0 +1,85 @@
/*
* meli - build.rs
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
extern crate proc_macro;
extern crate quote;
extern crate syn;
include!("config_macros.rs");
fn main() {
println!("cargo:rerun-if-changed=src/conf/.rebuild.overrides.rs");
override_derive(&[
("src/conf/pager.rs", "PagerSettings"),
("src/conf/listing.rs", "ListingSettings"),
("src/conf/notifications.rs", "NotificationsSettings"),
("src/conf/shortcuts.rs", "Shortcuts"),
("src/conf/composing.rs", "ComposingSettings"),
("src/conf/tags.rs", "TagsSettings"),
("src/conf/pgp.rs", "PGPSettings"),
]);
#[cfg(feature = "cli-docs")]
{
use flate2::{Compression, GzBuilder};
const MANDOC_OPTS: &[&str] = &["-T", "utf8", "-I", "os=Generated by mandoc(1)"];
use std::{env, io::prelude::*, path::Path};
let out_dir = env::var("OUT_DIR").unwrap();
let mut out_dir_path = Path::new(&out_dir).to_path_buf();
let mut cl = |filepath: &str, output: &str, source: bool| {
out_dir_path.push(output);
let output = if source {
std::fs::read_to_string(filepath).unwrap().into_bytes()
} else {
let output = Command::new("mandoc")
.args(MANDOC_OPTS)
.arg(filepath)
.output()
.or_else(|_| Command::new("man").arg("-l").arg(filepath).output())
.expect(
"could not execute `mandoc` or `man`. If the binaries are not available \
in the PATH, disable `cli-docs` feature to be able to continue \
compilation.",
);
output.stdout
};
let file = File::create(&out_dir_path).unwrap_or_else(|err| {
panic!("Could not create file {}: {}", out_dir_path.display(), err)
});
let mut gz = GzBuilder::new()
.comment(output.len().to_string().into_bytes())
.write(file, Compression::default());
gz.write_all(&output).unwrap();
gz.finish().unwrap();
out_dir_path.pop();
};
cl("docs/meli.1", "meli.txt.gz", false);
cl("docs/meli.conf.5", "meli.conf.txt.gz", false);
cl("docs/meli-themes.5", "meli-themes.txt.gz", false);
cl("docs/meli.7", "meli.7.txt.gz", false);
cl("docs/meli.1", "meli.mdoc.gz", true);
cl("docs/meli.conf.5", "meli.conf.mdoc.gz", true);
cl("docs/meli-themes.5", "meli-themes.mdoc.gz", true);
cl("docs/meli.7", "meli.7.mdoc.gz", true);
}
}

View File

@ -0,0 +1,245 @@
/*
* meli -
*
* Copyright Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::{
fs::File,
io::prelude::*,
process::{Command, Stdio},
};
use quote::{format_ident, quote};
use regex::Regex;
// Write ConfigStructOverride to overrides.rs
pub(crate) fn override_derive(filenames: &[(&str, &str)]) {
let mut output_file =
File::create("src/conf/overrides.rs").expect("Unable to open output file");
let mut output_string = r##"// @generated
/*
* meli - conf/overrides.rs
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
#![allow(clippy::derivable_impls)]
//! This module is automatically generated by `config_macros.rs`.
use super::*;
use melib::HeaderName;
"##
.to_string();
let cfg_attr_default_attr_regex = Regex::new(r"\s*default\s*[,]").unwrap();
let cfg_attr_default_val_attr_regex = Regex::new(r#"\s*default\s*=\s*"[^"]*"\s*,\s*"#).unwrap();
let cfg_attr_feature_regex = Regex::new(r"[(](?:not[(]\s*)?feature").unwrap();
'file_loop: for (filename, ident) in filenames {
println!("cargo:rerun-if-changed={}", filename);
let mut file = File::open(filename)
.unwrap_or_else(|err| panic!("Unable to open file `{}` {}", filename, err));
let mut src = String::new();
file.read_to_string(&mut src).expect("Unable to read file");
let syntax = syn::parse_file(&src).expect("Unable to parse file");
if syntax.items.iter().any(|item| {
if let syn::Item::Struct(s) = item {
if s.ident.to_string().ends_with("Override") {
println!("ident {} exists, skipping {}", ident, filename);
return true;
}
}
false
}) {
continue 'file_loop;
}
for item in syntax.items.iter() {
if let syn::Item::Struct(s) = item {
if s.ident != ident {
continue;
}
if s.ident.to_string().ends_with("Override") {
unreachable!();
}
let override_ident: syn::Ident = format_ident!("{}Override", s.ident);
let mut field_tokentrees = vec![];
let mut attrs_tokens = vec![];
for attr in &s.attrs {
if let Ok(syn::Meta::List(ml)) = attr.parse_meta() {
if ml.path.get_ident().is_some() && ml.path.get_ident().unwrap() == "cfg" {
attrs_tokens.push(attr);
}
}
}
let mut field_idents = vec![];
for f in &s.fields {
let ident = &f.ident;
let ty = &f.ty;
let attrs = f
.attrs
.iter()
.filter_map(|f| {
let mut new_attr = f.clone();
if let proc_macro2::TokenTree::Group(g) =
f.tokens.clone().into_iter().next().unwrap()
{
let mut attr_inner_value = f.tokens.to_string();
if cfg_attr_feature_regex.is_match(&attr_inner_value) {
attr_inner_value = cfg_attr_default_val_attr_regex
.replace_all(&attr_inner_value, "")
.to_string();
if attr_inner_value.contains("default") {
attr_inner_value = cfg_attr_default_attr_regex
.replace_all(&attr_inner_value, "")
.to_string();
}
let new_toks: proc_macro2::TokenStream =
attr_inner_value.parse().unwrap();
new_attr.tokens = quote! { #new_toks };
}
if !attr_inner_value.starts_with("( default")
&& !attr_inner_value.starts_with("( default =")
&& !attr_inner_value.starts_with("(default")
&& !attr_inner_value.starts_with("(default =")
{
return Some(new_attr);
}
if attr_inner_value.starts_with("( default =")
|| attr_inner_value.starts_with("(default =")
{
let rest = g.stream().into_iter().skip(4);
new_attr.tokens = quote! { ( #(#rest)*) };
match new_attr.tokens.to_string().as_str() {
"( )" | "()" => {
return None;
}
_ => {}
}
} else if attr_inner_value.starts_with("( default")
|| attr_inner_value.starts_with("(default")
{
let rest = g.stream().into_iter().skip(2);
new_attr.tokens = quote! { ( #(#rest)*) };
match new_attr.tokens.to_string().as_str() {
"( )" | "()" => {
return None;
}
_ => {}
}
}
}
Some(new_attr)
})
.collect::<Vec<_>>();
let t = quote! {
#(#attrs)*
#[serde(default)]
pub #ident : Option<#ty>
};
if !field_idents.contains(&ident) {
field_idents.push(ident);
}
field_tokentrees.push(t);
}
//let fields = &s.fields;
let literal_struct = quote! {
#(#attrs_tokens)*
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct #override_ident {
#(#field_tokentrees),*
}
#(#attrs_tokens)*
impl Default for #override_ident {
fn default() -> Self {
Self {
#(#field_idents: None),*
}
}
}
};
output_string.push_str(&literal_struct.to_string());
output_string.push_str("\n\n");
}
}
}
let rustfmt_closure = move |output_file: &mut File, output_string: &str| {
let mut rustfmt = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|err| format!("failed to execute rustfmt {}", err))?;
{
// limited borrow of stdin
let stdin = rustfmt
.stdin
.as_mut()
.ok_or("failed to get rustfmt stdin")?;
stdin
.write_all(output_string.as_bytes())
.map_err(|err| format!("failed to write to rustfmt stdin {}", err))?;
}
let output = rustfmt
.wait_with_output()
.map_err(|err| format!("failed to wait on rustfmt child {}", err))?;
if !output.stderr.is_empty() {
return Err(format!(
"rustfmt invocation replied with: `{}`",
String::from_utf8_lossy(&output.stderr)
));
}
output_file
.write_all(&output.stdout)
.expect("failed to write to src/conf/overrides.rs");
Ok(())
};
if let Err(err) = rustfmt_closure(&mut output_file, &output_string) {
println!("Tried rustfmt on overrides module, got error: {}", err);
output_file.write_all(output_string.as_bytes()).unwrap();
}
}

View File

@ -0,0 +1,122 @@
# Using other apps with `meli`
## Sending mail with a command line tool
`composing.send_mail` can use either settings for an SMTP server or a shell
command to which it pipes new mail to.
### `msmtp` and `send_mail`
[`msmtp`][msmtp] is a command line SMTP client that can be configured to work
with many SMTP servers. It supports queuing and other small useful features.
See [the documentation](https://marlam.de/msmtp/msmtp.html).
```toml
[composing]
send_mail = 'msmtp --logfile=/home/user/.mail/msmtp.log --read-recipients
--read-envelope-from'
```
[msmtp]: https://marlam.de/msmtp/
## Editor
Any editor you specify in `composing.editor_cmd` will be invoked with the
e-mail draft file path appended as an argument to it. For example, if your
setting is `editor_cmd = 'nano'`, `meli` will execute `nano /tmp/meli/...`.
### Configuration
#### `vim` / `neovim` command
The following command setting in your `meli` configuration file makes editing
start at the first empty line, that is, after the e-mail headers. This allows
you to start writing the e-mail body right away after opening the editor from
`meli`.
```toml
[composing]
editor_cmd = '~/.local/bin/vim +/^$'
```
In `vim`, the `+` argument positions the cursor at the first file argument. `/`
specifies a pattern position instead of a line number. `^` specifies the start
of a line, and `$` the end of the line. The pattern altogether matches an empty
line, which will be after the e-mail headers.
### Composing with `format=flowed`
`format=flowed` is a proposed IETF standard[^formatflowed] that lets you
preserve the structure of paragraphs by disambiguating a *hard* and a *soft*
line break. A line break that is preceded by a space character is *soft* and
does not terminate the paragraph, while a line break without a space is a
*hard* one and creates a new paragraph. This allows text to be re-flowed in
e-mail clients at different display widths and font sizes without messing up
the author's formatting.
#### `vim` / `neovim` and `format=flowed`
Create a `mail.vim` file type plugin in:
- `$HOME/.vim/after/ftplugin/mail.vim` for vim
- `$HOME/.config/nvim/after/ftplugin/mail.vim` for neovim
```vim
setlocal nomodeline
setlocal textwidth=72
setlocal formatoptions=aqtw2r
setlocal nojoinspaces
setlocal nosmartindent
setlocal comments+=nb:>
match ErrorMsg '\s\+$'
```
Also, don't forget that you can easily quote stuff with `MailQuote`.
From `:help ft-mail-plugin`:
> Local mappings:
> `<LocalLeader>q` or `\\MailQuote`
> Quotes the text selected in Visual mode, or from the cursor position
> to the end of the file in Normal mode.
> This means "> " is inserted in each line.
See the accompanying [`mail.vim`](./mail.vim) for comments for each setting.
## `xbiff`
[`xbiff(1)`][xbiff] manual page says:[^xbiffmanpage]
> The `xbiff` program displays a little image of a mailbox. When there is no
> mail, the flag on the mailbox is down. When mail arrives, the flag goes up
> and the mailbox beeps.
This tool is very outdated, but some users might still have use for it.
Therefore `meli` provides support (also, it's easy to support this feature).
Specify a file path in `notifications.xbiff_file_path` and `meli` will write to
it when new mail arrives. This file can the be used as input to `xbiff`.
```toml
[notifications]
xbiff_file_path = "/tmp/xbiff"
```
[xbiff]: https://en.wikipedia.org/wiki/Xbiff
[^xbiffmanpage]: https://www.x.org/releases/X11R7.0/doc/html/xbiff.1.html
## Viewing HTML e-mail
By default `meli` tries to render HTML e-mail with `w3m`. You can override this
by setting the `pager.html_filter` setting. The default setting corresponds to:
```toml
[pager]
html_filter = "w3m -I utf-8 -T text/html"
```
The HTML of the e-mail is piped into `html_filter`'s standard input.
## Externally refreshing e-mail accounts
If your account's syncing is handled by an external tool, you can use the
refresh shortcuts within `meli` to call this tool with
`accounts.refresh_command`.

View File

@ -0,0 +1,354 @@
'\" t
.\"<!-- Copyright 1998 - 2007 Double Precision, Inc. See COPYING for -->
.\"<!-- distribution information. -->
.\" Title: maildir
.\" Author: Sam Varshavchik
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\" Date: 07/24/2017
.\" Manual: Double Precision, Inc.
.\" Source: Courier Mail Server
.\" Language: English
.\"
.TH "MAILDIR" "5" "07/24/2017" "Courier Mail Server" "Double Precision, Inc\&."
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
maildir \- E\-mail directory
.SH "SYNOPSIS"
.sp
$HOME/Maildir
.SH "DESCRIPTION"
.PP
A
\(lqMaildir\(rq
is a structured directory that holds E\-mail messages\&. Maildirs were first implemented by the
Qmail
mail server\&. Qmail\*(Aqs maildirs were a simple data structure, nothing more than a single collection of E\-mail messages\&. The
Courier
mail server builds upon
Qmail\*(Aqs maildirs to provide extended functionality, such as folders and quotas\&. This document describes the
Courier
mail server\*(Aqs extended maildirs, without explicitly identifying The
Courier
mail server\-specific extensions\&. See
\fBmaildir\fR(5)
in Qmail\*(Aqs documentation for the original definition of maildirs\&.
.PP
Traditionally, E\-mail folders were saved as plain text files, called
\(lqmboxes\(rq\&. Mboxes have known limitations\&. Only one application can use an mbox at the same time\&. Locking is required in order to allow simultaneous concurrent access by different applications\&. Locking is often problematic, and not very reliable in network\-based filesystem requirements\&. Some network\-based filesystems don\*(Aqt offer any reliable locking mechanism at all\&. Furthermore, even bulletproof locking won\*(Aqt prevent occasional mbox corruption\&. A process can be killed or terminated in the middle of updating an mbox\&. This will likely result in corruption, and a loss of most messages in the mbox\&.
.PP
Maildirs allow multiple concurrent access by different applications\&. Maildirs do not require locking\&. Multiple applications can update a maildir at the same time, without stepping on each other\*(Aqs feet\&.
.SS "Maildir contents"
.PP
A
\(lqmaildir\(rq
is a directory that\*(Aqs created by
\m[blue]\fB\fBmaildirmake\fR(1)\fR\m[]\&\s-2\u[1]\d\s+2\&. Naturally, maildirs should not have any group or world permissions, unless you want other people to read your mail\&. A maildir contains three subdirectories:
tmp,
new, and
cur\&. These three subdirectories comprise the primary folder, where new mail is delivered by the system\&.
.PP
Folders are additional subdirectories in the maildir whose names begin with a period: such as
\&.Drafts
or
\&.Sent\&. Each folder itself contains the same three subdirectories,
tmp,
new, and
cur, and an additional zero\-length file named
maildirfolder, whose purpose is to inform any mail delivery agent that it\*(Aqs really delivering to a folder, and that the mail delivery agent should look in the parent directory for any maildir\-related information\&.
.PP
Folders are not physically nested\&. A folder subdirectory, such as
\&.Sent
does not itself contain any subfolders\&. The main maildir contains a single, flat list of subfolders\&. These folders are logically nested, and periods serve to separate folder hierarchies\&. For example,
\&.Sent\&.2002
is considered to be a subfolder called
\(lq2002\(rq
which is a subfolder of
\(lqSent\(rq\&.
.sp
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.br
.ps +1
\fBFolder name encoding\fR
.RS 4
.PP
Folder names can contain any Unicode character, except for control characters\&. US\-ASCII characters, U+0x0020 \- U+0x007F, except for the period, forward\-slash, and ampersand characters (U+0x002E, U+0x002F, and U+0x0026) represent themselves\&. The ampersand is represent by the two character sequence
\(lq&\-\(rq\&. The period, forward slash, and non US\-ASCII Unicode characters are represented using the UTF\-7 character set, and encoded with a modified form of base64\-encoding\&.
.PP
The
\(lq&\(rq
character starts the modified base64\-encoded sequence; the sequence is terminated by the
\(lq\-\(rq
character\&. The sequence of 16\-bit Unicode characters is written in big\-endian order, and encoded using the base64\-encoding method described in section 5\&.2 of
\m[blue]\fBRFC 1521\fR\m[]\&\s-2\u[2]\d\s+2, with the following modifications:
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
The
\(lq=\(rq
padding character is omitted\&. When decoding, an incomplete 16\-bit character is discarded\&.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
The comma character,
\(lq,\(rq
is used in place of the
\(lq/\(rq
character in the base64 alphabet\&.
.RE
.PP
For example, the word
\(lqResume\(rq
with both "e"s being the e\-acute character, U+0x00e9, is encoded as
\(lqR&AOk\-sum&AOk\-\(rq
(so a folder of that name would be a maildir subdirectory called
\(lq\&.R&AOk\-sum&AOk\-\(rq)\&.
.RE
.sp
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.br
.ps +1
\fBOther maildir contents\fR
.RS 4
.PP
Software that uses maildirs may also create additional files besides the
tmp,
new, and
cur
subdirectories \-\- in the main maildir or a subfolder \-\- for its own purposes\&.
.RE
.SS "Messages"
.PP
E\-mail messages are stored in separate, individual files, one E\-mail message per file\&. The
tmp
subdirectory temporarily stores E\-mail messages that are in the process of being delivered to this maildir\&.
tmp
may also store other kinds of temporary files, as long as they are created in the same way that message files are created in
tmp\&. The
new
subdirectory stores messages that have been delivered to this maildir, but have not yet been seen by any mail application\&. The
cur
subdirectory stores messages that have already been seen by mail applications\&.
.SS "Adding new mail to maildirs"
.PP
The following process delivers a new message to the maildir:
.PP
A new unique filename is created using one of two possible forms:
\(lqtime\&.MusecPpid\&.host\(rq, or
\(lqtime\&.MusecPpid_unique\&.host\(rq\&.
\(lqtime\(rq
and
\(lqusec\(rq
is the current system time, obtained from
\fBgettimeofday\fR(2)\&.
\(lqpid\(rq
is the process number of the process that is delivering this message to the maildir\&.
\(lqhost\(rq
is the name of the machine where the mail is being delivered\&. In the event that the same process creates multiple messages, a suffix unique to each message is appended to the process id; preferrably an underscore, followed by an increasing counter\&. This applies whether messages created by a process are all added to the same, or different, maildirs\&. This protocol allows multiple processes running on multiple machines on the same network to simultaneously create new messages without stomping on each other\&.
.PP
The filename created in the previous step is checked for existence by executing the
\fBstat\fR(2)
system call\&. If
\fBstat\fR(2)
results in ANYTHING OTHER than the system error
ENOENT, the process must sleep for two seconds, then go back and create another unique filename\&. This is an extra step to insure that each new message has a completely unique filename\&.
.PP
Other applications that wish to use
tmp
for temporary storage should observe the same protocol (but see READING MAIL FROM MAILDIRS below, because old files in
tmp
will be eventually deleted)\&.
.PP
If the
\fBstat\fR(2)
system call returned
ENOENT, the process may proceed to create the file in the
tmp
subdirectory, and save the entire message in the new file\&. The message saved MUST NOT have the
\(lqFrom_\(rq
header that is used to mboxes\&. The message also MUST NOT have any
\(lqFrom_\(rq
lines in the contents of the message prefixed by the
\(lq>\(rq
character\&.
.PP
When saving the message, the number of bytes returned by the
\fBwrite\fR(2)
system call must be checked, in order to make sure that the complete message has been written out\&.
.PP
After the message is saved, the file descriptor is
\fBfstat\fR(2)\-ed\&. The file\*(Aqs device number, inode number, and the its byte size, are saved\&. The file is closed and is then immediately moved/renamed into the
new
subdirectory\&. The name of the file in
new
should be
\(lqtime\&.MusecPpidVdevIino\&.host,S=\fIcnt\fR\(rq, or
\(lqtime\&.MusecPpidVdevIino_unique\&.host,S=\fIcnt\fR\(rq\&.
\(lqdev\(rq
is the message\*(Aqs device number,
\(lqino\(rq
is the message\*(Aqs inode number (from the previous
\fBfstat\fR(2)
call); and
\(lqcnt\(rq
is the message\*(Aqs size, in bytes\&.
.PP
The
\(lq,S=\fIcnt\fR\(rq
part optimizes the
\m[blue]\fBCourier\fR\m[]\&\s-2\u[3]\d\s+2
mail server\*(Aqs maildir quota enhancement; it allows the size of all the mail stored in the maildir to be added up without issuing the
\fBstat\fR(2)
system call for each individual message (this can be quite a performance drain with certain network filesystems)\&.
.SS "READING MAIL FROM MAILDIRS"
.PP
Applications that read mail from maildirs should do it in the following order:
.PP
When opening a maildir or a maildir folder, read the
tmp
subdirectory and delete any files in there that are at least 36 hours old\&.
.PP
Look for new messages in the
new
subdirectory\&. Rename
\fInew/filename\fR, as
\fIcur/filename:2,info\fR\&. Here,
\fIinfo\fR
represents the state of the message, and it consists of zero or more boolean flags chosen from the following:
\(lqD\(rq
\- this is a \*(Aqdraft\*(Aq message,
\(lqR\(rq
\- this message has been replied to,
\(lqS\(rq
\- this message has been viewed (seen),
\(lqT\(rq
\- this message has been marked to be deleted (trashed), but is not yet removed (messages are removed from maildirs simply by deleting their file),
\(lqF\(rq
\- this message has been marked by the user, for some purpose\&. These flags must be stored in alphabetical order\&. New messages contain only the
:2,
suffix, with no flags, indicating that the messages were not seen, replied, marked, or deleted\&.
.PP
Maildirs may have maximum size quotas defined, but these quotas are purely voluntary\&. If you need to implement mandatory quotas, you should use any quota facilities provided by the underlying filesystem that is used to store the maildirs\&. The maildir quota enhancement is designed to be used in certain situations where filesystem\-based quotas cannot be used for some reason\&. The implementation is designed to avoid the use of any locking\&. As such, at certain times the calculated quota may be imprecise, and certain anomalous situations may result in the maildir actually going over the stated quota\&. One such situation would be when applications create messages without updating the quota estimate for the maildir\&. Eventually it will be precisely recalculated, but wherever possible new messages should be created in compliance with the voluntary quota protocol\&.
.PP
The voluntary quota protocol involves some additional procedures that must be followed when creating or deleting messages within a given maildir or its subfolders\&. The
\m[blue]\fB\fBdeliverquota\fR(8)\fR\m[]\&\s-2\u[4]\d\s+2
command is a tiny application that delivers a single message to a maildir using the voluntary quota protocol, and hopefully it can be used as a measure of last resort\&. Alternatively, applications can use the
libmaildir\&.a
library to handle all the low\-level dirty details for them\&. The voluntary quota enhancement is described in the
\m[blue]\fB\fBmaildirquota\fR(7)\fR\m[]\&\s-2\u[5]\d\s+2
man page\&.
.SS "Maildir Quotas"
.PP
This is a voluntary mechanism for enforcing "loose" quotas on the maximum sizes of maildirs\&. This mechanism is enforced in software, and not by the operating system\&. Therefore it is only effective as long as the maildirs themselves are not directly accessible by their users, since this mechanism is trivially disabled\&.
.PP
If possible, operating system\-enforced quotas are preferrable\&. Where operating system quota enforcement is not available, or not possible, this voluntary quota enforcement mechanism might be an acceptable compromise\&. Since it\*(Aqs enforced in software, all software that modifies or accesses the maildirs is required to voluntary obey and enforce a quota\&. The voluntary quota implementation is flexible enough to allow non quota\-aware applications to also access the maildirs, without any drastic consequences\&. There will be some non\-drastic consequences, though\&. Of course, non quota\-aware applications will not enforce any defined quotas\&. Furthermore, this voluntary maildir quota mechanism works by estimating the current size of the maildir, with periodic exact recalculation\&. Obviously non quota\-aware maildir applications will not update the maildir size estimation, so the estimate will be thrown off for some period of time, until the next recalculation\&.
.PP
This voluntary quota mechanism is designed to be a reasonable compromise between effectiveness, and performance\&. The entire purpose of using maildir\-based mail storage is to avoid any kind of locking, and to permit parallel access to mail by multiple applications\&. In order to compute the exact size of a maildir, the maildir must be locked somehow to prevent any modifications while its contents are added up\&. Obviously something like that defeats the original purpose of using maildirs, therefore the voluntary quota mechanism does not use locking, and that\*(Aqs why the current recorded maildir size is always considered to be an estimate\&. Regular size recalculations will compensate for any occasional race conditions that result in the estimate to be thrown off\&.
.PP
A quota for an existing maildir is installed by running maildirmake with the
\-q
option, and naming an existing maildir\&. The
\-q
option takes a parameter,
\fIquota\fR, which is a comma\-separated list of quota specifications\&. A quota specification consists of a number followed by either \*(AqS\*(Aq, indicating the maximum message size in bytes, or \*(AqC\*(Aq, maximum number of messages\&. For example:
.sp
.if n \{\
.RS 4
.\}
.nf
\fBmaildirmake \-q 5000000S,1000C \&./Maildir\fR
.fi
.if n \{\
.RE
.\}
.PP
This sets the quota to 5,000,000 bytes or 1000 messages, whichever comes first\&.
.sp
.if n \{\
.RS 4
.\}
.nf
\fBmaildirmake \-q 1000000S \&./Maildir\fR
.fi
.if n \{\
.RE
.\}
.PP
This sets the quota to 1,000,000 bytes, without limiting the number of messages\&.
.PP
A quota of an existing maildir can be changed by rerunning the
\fBmaildirmake\fR
command with a new
\-q
option\&. To delete a quota entirely, delete the
\fIMaildir\fR/maildirsize
file\&.
.SH "SEE ALSO"
.PP
\m[blue]\fB\fBmaildirmake\fR(1)\fR\m[]\&\s-2\u[1]\d\s+2\&.
.SH "AUTHOR"
.PP
\fBSam Varshavchik\fR
.RS 4
Author
.RE
.SH "NOTES"
.IP " 1." 4
\fBmaildirmake\fR(1)
.RS 4
\%http://www.courier-mta.org/maildirmake.html
.RE
.IP " 2." 4
RFC 1521
.RS 4
\%http://www.rfc-editor.org/rfc/rfc1521.txt
.RE
.IP " 3." 4
Courier
.RS 4
\%http://www.courier-mta.org
.RE
.IP " 4." 4
\fBdeliverquota\fR(8)
.RS 4
\%http://www.courier-mta.org/deliverquota.html
.RE
.IP " 5." 4
\fBmaildirquota\fR(7)
.RS 4
\%http://www.courier-mta.org/maildirquota.html
.RE

View File

@ -0,0 +1,187 @@
'\" t
.\" -*-nroff-*-
.\"
.\" Copyright (C) 2000 Thomas Roessler <roessler@does-not-exist.org>
.\"
.\" This document is in the public domain and may be distributed and
.\" changed arbitrarily.
.\"
.TH mbox 5 "February 19th, 2002" Unix "User Manuals"
.\"
.SH NAME
mbox \- Format for mail message storage.
.\"
.SH DESCRIPTION
This document describes the format traditionally used by Unix hosts
to store mail messages locally.
.B mbox
files typically reside in the system's mail spool, under various
names in users' Mail directories, and under the name
.B mbox
in users' home directories.
.PP
An
.B mbox
is a text file containing an arbitrary number of e-mail messages.
Each message consists of a postmark, followed by an e-mail message
formatted according to \fBRFC822\fP, \fBRFC2822\fP. The file format
is line-oriented. Lines are separated by line feed characters (ASCII 10).
.PP
A postmark line consists of the four characters "From", followed by
a space character, followed by the message's envelope sender
address, followed by whitespace, and followed by a time stamp. This
line is often called From_ line.
.PP
The sender address is expected to be
.B addr-spec
as defined in \fBRFC2822\fP 3.4.1. The date is expected to be
.B date-time
as output by
.BR asctime (3).
For compatibility reasons with legacy software, two-digit years
greater than or equal to 70 should be interpreted as the years
1970+, while two-digit years less than 70 should be interpreted as
the years 2000-2069. Software reading files in this format should
also be prepared to accept non-numeric timezone information such as
"CET DST" for Central European Time, daylight saving time.
.PP
Example:
.IP "" 1
>From example@example.com Fri Jun 23 02:56:55 2000
.PP
In order to avoid misinterpretation of lines in message bodies
which begin with the four characters "From", followed by a space
character, the mail delivery agent must quote any occurrence
of "From " at the start of a body line.
.sp
There are two different quoting schemes, the first (\fBMBOXO\fP) only
quotes plain "From " lines in the body by prepending a '>' to the
line; the second (\fBMBOXRD\fP) also quotes already quoted "From "
lines by prepending a '>' (i.e. ">From ", ">>From ", ...). The later
has the advantage that lines like
.IP "" 1
>From the command line you can use the '\-p' option
.PP
aren't dequoted wrongly as a \fBMBOXRD\fP-MDA would turn the line
into
.IP "" 1
>>From the command line you can use the '\-p' option
.PP
before storing it. Besides \fBMBOXO\fP and \fBMBOXRD\fP there is also
\fBMBOXCL\fP which is \fBMBOXO\fP with a "Content-Length:"\-field with the
number of bytes in the message body; some MUAs (like
.BR mutt (1))
do automatically transform \fBMBOXO\fP mailboxes into \fBMBOXCL\fP ones when
ever they write them back as \fBMBOXCL\fP can be read by any \fBMBOXO\fP-MUA
without any problems.
.PP
If the modification-time (usually determined via
.BR stat (2))
of a nonempty
.B mbox
file is greater than the access-time the file has new mail. Many MUAs
place a Status: header in each message to indicate which messages have
already been read.
.\"
.SH LOCKING
Since
.B mbox
files are frequently accessed by multiple programs in parallel,
.B mbox
files should generally not be accessed without locking.
.PP
Three different locking mechanisms (and combinations thereof) are in
general use:
.IP "\(bu"
.BR fcntl (2)
locking is mostly used on recent, POSIX-compliant systems. Use of
this locking method is, in particular, advisable if
.B mbox
files are accessed through the Network File System (NFS), since it
seems the only way to reliably invalidate NFS clients' caches.
.IP "\(bu"
.BR flock (2)
locking is mostly used on BSD-based systems.
.IP "\(bu"
Dotlocking is used on all kinds of systems. In order to lock an
.B mbox
file named \fIfolder\fR, an application first creates a temporary file
with a unique name in the directory in which the
\fIfolder\fR resides. The application then tries to use the
.BR link (2)
system call to create a hard link named \fIfolder.lock\fR
to the temporary file. The success of the
.BR link (2)
system call should be additionally verified using
.BR stat (2)
calls. If the link has succeeded, the mail folder is considered
dotlocked. The temporary file can then safely be unlinked.
.IP ""
In order to release the lock, an application just unlinks the
\fIfolder.lock\fR file.
.PP
If multiple methods are combined, implementors should make sure to
use the non-blocking variants of the
.BR fcntl (2)
and
.BR flock (2)
system calls in order to avoid deadlocks.
.PP
If multiple methods are combined, an
.B mbox
file must not be considered to have been successfully locked before
all individual locks were obtained. When one of the individual
locking methods fails, an application should release all locks it
acquired successfully, and restart the entire locking procedure from
the beginning, after a suitable delay.
.PP
The locking mechanism used on a particular system is a matter of
local policy, and should be consistently used by all applications
installed on the system which access
.B mbox
files. Failure to do so may result in loss of e-mail data, and in
corrupted
.B mbox
files.
.\"
.SH FILES
.IR /var/spool/mail/$LOGNAME
.RS
\fB$LOGNAME\fP's incoming mail folder.
.RE
.PP
.IR $HOME/mbox
.RS
user's archived mail messages, in his \fB$HOME\fP directory.
.RE
.PP
.IR $HOME/Mail/
.RS
A directory in user's \fB$HOME\fP directory which is commonly used to hold
.B mbox
format folders.
.RE
.PP
.\"
.SH "SEE ALSO"
.BR mutt (1),
.BR fcntl (2),
.BR flock (2),
.BR link (2),
.BR stat (2),
.BR asctime (3),
.BR maildir (5),
.BR mmdf (5),
.BR RFC822 ,
.BR RFC976 ,
.BR RFC2822
.\"
.SH AUTHOR
Thomas Roessler <roessler@does-not-exist.org>, Urs Janssen <urs@tin.org>
.\"
.SH HISTORY
The
.B mbox
format occurred in Version 6 AT&T Unix.
.br
A variant of this format was documented in \fBRFC976\fP.

View File

@ -0,0 +1,235 @@
.TH mbox 5
.SH "NAME"
mbox \- file containing mail messages
.SH "INTRODUCTION"
The most common format for storage of mail messages is
.I mbox
format.
An
.I mbox
is a single file containing zero or more mail messages.
.SH "MESSAGE FORMAT"
A message encoded in
.I mbox
format begins with a
.B From_
line, continues with a series of
.B \fRnon-\fBFrom_
lines,
and ends with a blank line.
A
.B From_
line means any line that begins with the characters
F, r, o, m, space:
.EX
From god@heaven.af.mil Sat Jan 3 01:05:34 1996
.br
Return-Path: <god@heaven.af.mil>
.br
Delivered-To: djb@silverton.berkeley.edu
.br
Date: 3 Jan 1996 01:05:34 -0000
.br
From: God <god@heaven.af.mil>
.br
To: djb@silverton.berkeley.edu (D. J. Bernstein)
.br
.br
How's that mail system project coming along?
.br
.EE
The final line is a completely blank line (no spaces or tabs).
Notice that blank lines may also appear elsewhere in the message.
The
.B From_
line always looks like
.B From
.I envsender
.I date
.IR moreinfo .
.I envsender
is one word, without spaces or tabs;
it is usually the envelope sender of the message.
.I date
is the delivery date of the message.
It always contains exactly 24 characters in
.B asctime
format.
.I moreinfo
is optional; it may contain arbitrary information.
Between the
.B From_
line and the blank line is a message in RFC 822 format,
as described in
.BR qmail-header(5) ,
subject to
.B >From quoting
as described below.
.SH "HOW A MESSAGE IS DELIVERED"
Here is how a program appends a message to an
.I mbox
file.
It first creates a
.B From_
line given the message's envelope sender and the current date.
If the envelope sender is empty (i.e., if this is a bounce message),
the program uses
.B MAILER-DAEMON
instead.
If the envelope sender contains spaces, tabs, or newlines,
the program replaces them with hyphens.
The program then copies the message, applying
.B >From quoting
to each line.
.B >From quoting
ensures that the resulting lines are not
.B From_
lines:
the program prepends a
.B >
to any
.B From_
line,
.B >From_
line,
.B >>From_
line,
.B >>>From_
line,
etc.
Finally the program appends a blank line to the message.
If the last line of the message was a partial line,
it writes two newlines;
otherwise it writes one.
.SH "HOW A MESSAGE IS READ"
A reader scans through an
.I mbox
file looking for
.B From_
lines.
Any
.B From_
line marks the beginning of a message.
The reader should not attempt to take advantage of the fact that every
.B From_
line (past the beginning of the file)
is preceded by a blank line.
Once the reader finds a message,
it extracts a (possibly corrupted) envelope sender
and delivery date out of the
.B From_
line.
It then reads until the next
.B From_
line or end of file, whichever comes first.
It strips off the final blank line
and
deletes the
quoting of
.B >From_
lines and
.B >>From_
lines and so on.
The result is an RFC 822 message.
.SH "COMMON MBOX VARIANTS"
There are many variants of
.I mbox
format.
The variant described above is
.I mboxrd
format, popularized by Rahul Dhesi in June 1995.
The original
.I mboxo
format quotes only
.B From_
lines, not
.B >From_
lines.
As a result it is impossible to tell whether
.EX
From: djb@silverton.berkeley.edu (D. J. Bernstein)
.br
To: god@heaven.af.mil
.br
.br
>From now through August I'll be doing beta testing.
.br
Thanks for your interest.
.EE
was quoted in the original message.
An
.I mboxrd
reader will always strip off the quoting.
.I mboxcl
format is like
.I mboxo
format, but includes a Content-Length field with the
number of bytes in the message.
.I mboxcl2
format is like
.I mboxcl
but has no
.B >From
quoting.
These formats are used by SVR4 mailers.
.I mboxcl2
cannot be read safely by
.I mboxrd
readers.
.SH "UNSPECIFIED DETAILS"
There are many locking mechanisms for
.I mbox
files.
.B qmail-local
always uses
.B flock
on systems that have it, otherwise
.BR lockf .
The delivery date in a
.B From_
line does not specify a time zone.
.B qmail-local
always creates the delivery date in GMT
so that
.I mbox
files can be safely transported from one time zone to another.
If the mtime on a nonempty
.I mbox
file is greater than the atime,
the file has new mail.
If the mtime is smaller than the atime,
the new mail has been read.
If the atime equals the mtime,
there is no way to tell whether the file has new mail,
since
.B qmail-local
takes much less than a second to run.
One solution is for a mail reader to artificially set the
atime to the mtime plus 1.
Then the file has new mail if and only if the atime is
less than or equal to the mtime.
Some mail readers place
.B Status
fields in each message to indicate which messages have been read.
.SH "SEE ALSO"
maildir(5),
qmail-header(5),
qmail-local(8)

View File

@ -0,0 +1,239 @@
.TH maildir 5
.SH "NAME"
maildir \- directory for incoming mail messages
.SH "INTRODUCTION"
.I maildir
is a structure for
directories of incoming mail messages.
It solves the reliability problems that plague
.I mbox
files and
.I mh
folders.
.SH "RELIABILITY ISSUES"
A machine may crash while it is delivering a message.
For both
.I mbox
files and
.I mh
folders this means that the message will be silently truncated.
Even worse: for
.I mbox
format, if the message is truncated in the middle of a line,
it will be silently joined to the next message.
The mail transport agent will try again later to deliver the message,
but it is unacceptable that a corrupted message should show up at all.
In
.IR maildir ,
every message is guaranteed complete upon delivery.
A machine may have two programs simultaneously delivering mail
to the same user.
The
.I mbox
and
.I mh
formats require the programs to update a single central file.
If the programs do not use some locking mechanism,
the central file will be corrupted.
There are several
.I mbox
and
.I mh
locking mechanisms,
none of which work portably and reliably.
In contrast, in
.IR maildir ,
no locks are ever necessary.
Different delivery processes never touch the same file.
A user may try to delete messages from his mailbox at the same
moment that the machine delivers a new message.
For
.I mbox
and
.I mh
formats, the user's mail-reading program must know
what locking mechanism the mail-delivery programs use.
In contrast, in
.IR maildir ,
any delivered message
can be safely updated or deleted by a mail-reading program.
Many sites use Sun's
.B Network F\fPa\fBil\fPur\fBe System
(NFS),
presumably because the operating system vendor does not offer
anything else.
NFS exacerbates all of the above problems.
Some NFS implementations don't provide
.B any
reliable locking mechanism.
With
.I mbox
and
.I mh
formats,
if two machines deliver mail to the same user,
or if a user reads mail anywhere except the delivery machine,
the user's mail is at risk.
.I maildir
works without trouble over NFS.
.SH "THE MAILDIR STRUCTURE"
A directory in
.I maildir
format has three subdirectories,
all on the same filesystem:
.BR tmp ,
.BR new ,
and
.BR cur .
Each file in
.B new
is a newly delivered mail message.
The modification time of the file is the delivery date of the message.
The message is delivered
.I without
an extra UUCP-style
.B From_
line,
.I without
any
.B >From
quoting,
and
.I without
an extra blank line at the end.
The message is normally in RFC 822 format,
starting with a
.B Return-Path
line and a
.B Delivered-To
line,
but it could contain arbitrary binary data.
It might not even end with a newline.
Files in
.B cur
are just like files in
.BR new .
The big difference is that files in
.B cur
are no longer new mail:
they have been seen by the user's mail-reading program.
.SH "HOW A MESSAGE IS DELIVERED"
The
.B tmp
directory is used to ensure reliable delivery,
as discussed here.
A program delivers a mail message in six steps.
First, it
.B chdir()\fPs
to the
.I maildir
directory.
Second, it
.B stat()s
the name
.BR tmp/\fItime.pid.host ,
where
.I time
is the number of seconds since the beginning of 1970 GMT,
.I pid
is the program's process ID,
and
.I host
is the host name.
Third, if
.B stat()
returned anything other than ENOENT,
the program sleeps for two seconds, updates
.IR time ,
and tries the
.B stat()
again, a limited number of times.
Fourth, the program
creates
.BR tmp/\fItime.pid.host .
Fifth, the program
.I NFS-writes
the message to the file.
Sixth, the program
.BR link() s
the file to
.BR new/\fItime.pid.host .
At that instant the message has been successfully delivered.
The delivery program is required to start a 24-hour timer before
creating
.BR tmp/\fItime.pid.host ,
and to abort the delivery
if the timer expires.
Upon error, timeout, or normal completion,
the delivery program may attempt to
.B unlink()
.BR tmp/\fItime.pid.host .
.I NFS-writing
means
(1) as usual, checking the number of bytes returned from each
.B write()
call;
(2) calling
.B fsync()
and checking its return value;
(3) calling
.B close()
and checking its return value.
(Standard NFS implementations handle
.B fsync()
incorrectly
but make up for it by abusing
.BR close() .)
.SH "HOW A MESSAGE IS READ"
A mail reader operates as follows.
It looks through the
.B new
directory for new messages.
Say there is a new message,
.BR new/\fIunique .
The reader may freely display the contents of
.BR new/\fIunique ,
delete
.BR new/\fIunique ,
or rename
.B new/\fIunique
as
.BR cur/\fIunique:info .
See
.B http://pobox.com/~djb/proto/maildir.html
for the meaning of
.IR info .
The reader is also expected to look through the
.B tmp
directory and to clean up any old files found there.
A file in
.B tmp
may be safely removed if it
has not been accessed in 36 hours.
It is a good idea for readers to skip all filenames in
.B new
and
.B cur
starting with a dot.
Other than this, readers should not attempt to parse filenames.
.SH "ENVIRONMENT VARIABLES"
Mail readers supporting
.I maildir
use the
.B MAILDIR
environment variable
as the name of the user's primary mail directory.
.SH "SEE ALSO"
mbox(5),
qmail-local(8)

87
meli/docs/mail.vim 100644
View File

@ -0,0 +1,87 @@
" Place this plugin in
"
" `$HOME/.vim/after/ftplugin/mail.vim` for vim
" `$HOME/.config/nvim/after/ftplugin/mail.vim` for neovim
" Don't use modelines in e-mail messages
setlocal nomodeline
setlocal textwidth=72
" *fo-a*
" a Automatic formatting of paragraphs.
" Every time text is inserted or deleted the paragraph will be reformatted.
" *fo-w*
" w Trailing white space indicates a paragraph continues in the next line.
" A line that ends in a non-white character ends a paragraph.
" *fo-q*
" q Allow formatting of comments with "gq".
" *fo-t*
" t Auto-wrap text using textwidth
" *fo-r*
" r Automatically insert the current comment leader after hitting <Enter> in
" Insert mode.
" *fo-c*
" c Auto-wrap comments using textwidth, inserting the current comment leader
" automatically.
" *fo-2*
" 2 When formatting text, use the indent of the second line of a paragraph for
" the rest of the paragraph, instead of the indent of the first line.
" This supports paragraphs in which the first line has a different indent than
" the rest.
" Note that 'autoindent' must be set too.
" Example:
" first line of a paragraph
" second line of the same paragraph
" third line.
" This also works inside comments, ignoring the comment leader.
setlocal formatoptions=aqtw2r
" Disable adding two spaces after '.', '?' and '!' with a join command.
setlocal nojoinspaces
" Disable smartident (meant for source code)
setlocal nosmartindent
" *'comments'* *'com'* *E524* *E525*
" A comma-separated list of strings that can start a comment line.
" See |format-comments|.
" See |option-backslash| about using backslashes to insert a space.
"
"
" The 'comments' option is a comma-separated list of parts.
" Each part defines a type of comment string.
" A part consists of: {flags}:{string}
"
" {string} is the literal text that must appear.
"
" {flags}:
" n Nested comment.
" Nesting with mixed parts is allowed.
" If 'comments' is "n:),n:>" a line starting with "> ) >" is a comment.
"
" b Blank (<Space>, <Tab> or <EOL>) required after {string}.
setlocal comments+=nb:>
" Highlight trailing whitespace as errors.
match ErrorMsg '\s\+$'
" MAIL *mail.vim* *ft-mail.vim*
" By default mail.vim synchronises syntax to 100 lines before the first
" displayed line.
" If you have a slow machine, and generally deal with emails with short
" headers, you can change this to a smaller value:
let mail_minlines = 30
" *no_mail_maps* *g:no_mail_maps*
" Disable defining mappings for a specific filetype by setting a variable,
" which contains the name of the filetype.
" For the "mail" filetype this would be:
let no_mail_maps = 1
" Local mappings:
" <LocalLeader>q or \\MailQuote
" Quotes the text selected in Visual mode, or from the cursor position
" to the end of the file in Normal mode.
" This means "> " is inserted in each line.

View File

@ -17,29 +17,30 @@
.\" You should have received a copy of the GNU General Public License
.\" along with meli. If not, see <http://www.gnu.org/licenses/>.
.\"
.Dd January 23, 2020
.\".Dd November 11, 2022
.Dd March 10, 2024
.Dt MELI-THEMES 5
.Os
.Sh NAME
.Nm meli-themes
.Nd themes for the
.Nm meli
mail client
.Xr meli 1
terminal e-mail client
.Sh SYNOPSIS
.Nm meli
comes with two themes,
.Ic dark
(the default) and
(default) and
.Ic light .
.sp
Custom themes can be defined as lists of key-values in the configuration files:
.Bl -bullet -compact
.Pp
Custom themes are defined as lists of key-values in the configuration files:
.Bl -item -compact -offset 2
.It
.Pa $XDG_CONFIG_HOME/meli/config.toml
.It
.Pa $XDG_CONFIG_HOME/meli/themes/*.toml
.El
.sp
.Pp
The application theme is defined in the configuration as follows:
.Bd -literal
[terminal]
@ -48,22 +49,35 @@ theme = "dark"
.Sh DESCRIPTION
Themes for
.Nm meli
are described in the configuration language TOML.
They are key-value tables defined in the TERMINAL section of the configuration file.
are described in the configuration language TOML, as they are key-value tables defined in the TERMINAL section of the configuration file.
Each key defines the semantic location of the theme attribute within the application.
For example, "mail.listing.compact.* keys are settings for the `compact` mail listing style.
For example,
.Ic mail.listing.compact.*
keys are settings for the
.Ic compact
mail listing style.
A setting contains three fields: fg for foreground color, bg for background color, and attrs for text attribute.
.sp
.Pp
.Dl \&"widget.key.label\&" = { fg = \&"Default\&", bg = \&"Default\&", attrs = \&"Default\&" }
.sp
Each field contains a value, and each value can either be a color/attribute, or a key name.
In the latter case the setting's real value depends on the value of the key.
.Pp
Each field contains a value, which may be either a color/attribute, a link (key name) or a valid alias.
An alias is a string starting with the \&"\&$\&" character and must be declared in advance in the
.Ic color_aliases
or
.Ic attr_aliases
fields of a theme.
An alias' value can be any valid value, including links and other aliases, as long as they are valid.
In the case of a link the setting's real value depends on the value of the referred key.
This allows for defaults within a group of associated values.
Cyclic references in a theme results in an error:
.sp
.Pp
.Dl spooky theme contains a cycle: fg: mail.listing.compact.even -> mail.listing.compact.highlighted -> mail.listing.compact.odd -> mail.listing.compact.even
.Pp
Two themes are included by default, `light` and `dark`.
Two themes are included by default,
.Ql light
and
.Ql dark Ns
\&.
.Sh EXAMPLES
Specific settings from already defined themes can be overwritten:
.Bd -literal
@ -76,10 +90,14 @@ theme = "dark"
"mail.view.headers" = { fg = "30" }
"mail.view.body" = {fg = "HotPink3", bg = "LightSalmon1"}
# Linked value keys can be whatever key:
"mail.listing.compact.unseen" = { bg = "mail.sidebar_highlighted_account" }
"mail.listing.compact.even_unseen" = { bg = "mail.sidebar_highlighted_account" }
# Linked color value keys can optionally refer to another field:
"mail.listing.compact.odd_unseen" = { bg = "mail.sidebar_highlighted_account.fg" }
.sp
# define new theme. Undefined settings will inherit from the default "dark" theme.
[terminal.themes."hunter2"]
color_aliases= { "Jebediah" = "#b4da55" }
"mail.listing.tag_default" = { fg = "$Jebediah" }
"mail.view.headers" = { fg = "White", bg = "Black" }
.Ed
.Sh CUSTOM THEMES
@ -87,43 +105,45 @@ Custom themes can be included in your configuration files or be saved independen
.Pa $XDG_CONFIG_HOME/meli/themes/
directory as TOML files.
To start creating a theme right away, you can begin by editing the default theme keys and values:
.sp
.Dl meli --print-default-theme > ~/.config/meli/themes/new_theme.toml
.sp
.Pp
.Dl meli print-default-theme > ~/.config/meli/themes/new_theme.toml
.Pp
.Pa new_theme.toml
will now include all keys and values of the "dark" theme.
.sp
.Dl meli --print-loaded-themes
.sp
.Pp
.Dl meli print-loaded-themes
.Pp
will print all loaded themes with the links resolved.
.Sh VALID ATTRIBUTE VALUES
Case-sensitive.
.Bl -bullet -compact
.Bl -dash -compact
.It
"Default"
.It
"Bold"
.It
"Dim"
.It
"Italics"
.It
"Underline"
.It
"BoldUnderline"
"Blink"
.It
"Reverse"
.It
"BoldReverse"
"Hidden"
.It
"UnderlineReverse"
.It
"BoldReverseUnderline"
Any combo of the above separated by a bitwise XOR "\&|" eg "Dim | Italics"
.El
.Sh VALID COLOR VALUES
Color values are of type String with the following valid contents:
.Bl -bullet -compact
.Bl -dash -compact
.It
"Default" is the terminal default. (Case-sensitive)
.It
Hex triplet eg #FFFFFF for RGB colors
(three letter shorthand also valid, eg #09c → #0099cc) (Case-insensitive)
Hex triplet e.g. #FFFFFF for RGB colors.
Three character shorthand is also valid, e.g. #09c → #0099cc (Case-insensitive)
.It
0-255 byte for 256 colors.
.It
@ -131,8 +151,10 @@ Hex triplet eg #FFFFFF for RGB colors
name but with some modifications (for a full table see COLOR NAMES addendum) (Case-sensitive)
.El
.Sh NO COLOR
To completely disable ANSI colors, there are two options:
.Bl -bullet -compact
To completely disable
.Tn ANSI
colors, there are two options:
.Bl -dash -compact
.It
Set the
.Ic use_color
@ -142,76 +164,167 @@ option (section
.It
The
.Ev NO_COLOR
environmental variable, when present (regardless of its value), prevents the addition of ANSI color.
environmental variable, when present (regardless of its value), prevents the addition of
.Tn ANSI
color.
When the configuration value
.Ic use_color
is explicitly set to true by the user,
.Ev NO_COLOR
is ignored.
.El
.sp
In this mode, cursor locations (i.e. currently selected entries/items) will use the "reverse video" ANSI attribute to inverse the terminal's default foreground/background colors.
.Pp
In this mode, cursor locations (i.e., currently selected entries/items) will use the
.Ql reverse video
.Tn ANSI
attribute to invert the terminal's default foreground/background colors.
.Sh VALID KEYS
.TS
allbox tab(:);
lb
l.
key name
.Bl -dash -compact
.It
theme_default
.It
error_message
.It
highlight
.It
status.bar
.It
status.command_bar
.It
status.history
.It
status.history.hints
.It
status.notification
.It
tab.focused
.It
tab.unfocused
.It
tab.bar
.It
widgets.list.header
.It
widgets.form.label
.It
widgets.form.field
.It
widgets.form.highlighted
.It
widgets.options.highlighted
.It
mail.sidebar
.It
mail.sidebar_divider
.It
mail.sidebar_unread_count
.It
mail.sidebar_index
.It
mail.sidebar_highlighted
.It
mail.sidebar_highlighted_unread_count
.It
mail.sidebar_highlighted_index
.It
mail.sidebar_highlighted_account
.It
mail.sidebar_highlighted_account_unread_count
.It
mail.sidebar_highlighted_account_index
.It
mail.listing.compact.even
.It
mail.listing.compact.odd
mail.listing.compact.unseen
mail.listing.compact.selected
mail.listing.compact.highlighted
.It
mail.listing.compact.even_unseen
.It
mail.listing.compact.odd_unseen
.It
mail.listing.compact.even_selected
.It
mail.listing.compact.odd_selected
.It
mail.listing.compact.even_highlighted
.It
mail.listing.compact.odd_highlighted
.It
mail.listing.compact.even_highlighted_selected
.It
mail.listing.compact.odd_highlighted_selected
.It
mail.listing.plain.even
.It
mail.listing.plain.odd
mail.listing.plain.unseen
mail.listing.plain.selected
mail.listing.plain.highlighted
.TE
.TS
allbox tab(:);
lb
l.
key name
.It
mail.listing.plain.even_unseen
.It
mail.listing.plain.odd_unseen
.It
mail.listing.plain.even_selected
.It
mail.listing.plain.odd_selected
.It
mail.listing.plain.even_highlighted
.It
mail.listing.plain.odd_highlighted
.It
mail.listing.plain.even_highlighted_selected
.It
mail.listing.plain.odd_highlighted_selected
.It
mail.listing.conversations
.It
mail.listing.conversations.subject
.It
mail.listing.conversations.from
.It
mail.listing.conversations.date
mail.listing.conversations.padding
.It
mail.listing.conversations.unseen
mail.listing.conversations.unseen_padding
.It
mail.listing.conversations.highlighted
.It
mail.listing.conversations.selected
.It
mail.listing.conversations.highlighted_selected
.It
mail.view.headers
.It
mail.view.headers_names
.It
mail.view.headers_area
.It
mail.view.body
.It
mail.view.thread.indentation.a
.It
mail.view.thread.indentation.b
.It
mail.view.thread.indentation.c
.It
mail.view.thread.indentation.d
.It
mail.view.thread.indentation.e
.It
mail.view.thread.indentation.f
.It
mail.listing.attachment_flag
.It
mail.listing.thread_snooze_flag
.TE
.It
mail.listing.tag_default
.It
pager.highlight_search
.It
pager.highlight_search_current
.El
.Sh COLOR NAMES
.TS
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
Aqua:14:_:Black:0
Aquamarine1:122:_:Maroon:1
Aquamarine2:86:_:Green:2
@ -247,7 +360,7 @@ DarkMagenta1:91:_:SpringGreen6:29
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
DarkOliveGreen1:192:_:Turquoise4:30
DarkOliveGreen2:155:_:DeepSkyBlue3:31
DarkOliveGreen3:191:_:DeepSkyBlue4:32
@ -283,7 +396,7 @@ DeepPink4:125:_:Grey37:59
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
DeepPink6:162:_:MediumPurple6:60
DeepPink7:89:_:SlateBlue2:61
DeepPink8:53:_:SlateBlue3:62
@ -319,7 +432,7 @@ Grey19:236:_:DeepPink7:89
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
Grey23:237:_:DarkMagenta:90
Grey27:238:_:DarkMagenta1:91
Grey3:232:_:DarkViolet1:92
@ -355,7 +468,7 @@ HotPink2:169:_:LightGreen:119
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
HotPink3:132:_:LightGreen1:120
HotPink4:168:_:PaleGreen1:121
IndianRed:131:_:Aquamarine1:122
@ -391,7 +504,7 @@ LightSlateGrey:103:_:DarkOliveGreen6:149
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
LightSteelBlue:147:_:DarkSeaGreen6:150
LightSteelBlue1:189:_:DarkSeaGreen3:151
LightSteelBlue3:146:_:LightCyan3:152
@ -427,7 +540,7 @@ NavajoWhite3:144:_:LightGoldenrod3:179
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
Navy:4:_:Tan:180
NavyBlue:17:_:MistyRose3:181
Olive:3:_:Thistle3:182
@ -463,7 +576,7 @@ Purple5:55:_:Salmon1:209
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
Red:9:_:LightCoral:210
Red1:196:_:PaleVioletRed1:211
Red2:124:_:Orchid2:212
@ -499,7 +612,7 @@ Tan:180:_:Grey30:239
allbox tab(:);
lb|lb|l|lb|lb
l l|l|l l.
name ↓:byte:_:name:byte ↓
name \(da:byte:_:name:byte \(da
Teal:6:_:Grey35:240
Thistle1:225:_:Grey39:241
Thistle3:182:_:Grey42:242
@ -520,15 +633,34 @@ Yellow6:148:_:Grey93:255
.Sh SEE ALSO
.Xr meli 1 ,
.Xr meli.conf 5
.Sh CONFORMING TO
TOML Standard v.0.5.0 https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
.sp
https://no-color.org/
.Sh STANDARDS
.Bl -item -compact
.It
.Lk https://toml.io/en/v0.5.0 "TOML Standard v.0.5.0"
.It
.Lk https://no\-color.org/ "NO_COLOR: disabling ANSI color output by default"
.El
.Sh AUTHORS
Copyright 2017-2019
.An Manos Pitsidianakis Aq epilys@nessuent.xyz
Copyright 2017\(en2024
.An Manos Pitsidianakis Aq Mt manos@pitsidianak.is
.Pp
Released under the GPL, version 3 or greater.
This software carries no warranty of any kind.
(See COPYING for full copyright and warranty notices.)
.Pp
.Aq https://meli.delivery
.Po
See
.Pa COPYING
for full copyright and warranty notices.
.Pc
.Ss Links
.Bl -item -compact
.It
.Lk https://meli\-email.org "Website"
.It
.Lk https://git.meli\-email.org/meli/meli "Main\ git\ repository\ and\ issue\ tracker"
.It
.Lk https://codeberg.org/meli/meli "Official\ read-only\ git\ mirror\ on\ codeberg.org"
.It
.Lk https://github.com/meli/meli "Official\ read-only\ git\ mirror\ on\ github.com"
.It
.Lk https://crates.io/crates/meli "meli\ crate\ on\ crates.io"
.El

939
meli/docs/meli.1 100644
View File

@ -0,0 +1,939 @@
.\" meli - meli.1
.\"
.\" Copyright 2017-2019 Manos Pitsidianakis
.\"
.\" This file is part of meli.
.\"
.\" meli is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
.\"
.de HorizontalRule
.\"\l'\n(.l\(ru1.25'
.sp
..
.de Shortcut
.Sm
.Aq \\$1
\
.Po
.Em shortcuts.\\$2\&. Ns
.Em \\$3
.Pc
.Sm
..
.de ShortcutPeriod
.Aq \\$1
.Po
.Em shortcuts.\\$2\&. Ns
.Em \\$3
.Pc Ns
..
.de Command
.Bd -ragged
.Cm \\$*
.Ed
.sp
..
.\".Dd November 11, 2022
.Dd March 10, 2024
.Dt MELI 1
.Os
.Sh NAME
.Nm meli
.Nd terminal e\-mail client
.Em μέλι
is the Greek word for honey
.Sh SYNOPSIS
.Nm
.Op Fl -help | h
.Op Fl -version | v
.Op Fl -config Ar path
.Bl -tag -width flag -offset indent
.It Fl -help | h
Show help message and exit.
.It Fl -version | v
Show version and exit.
.It Fl -config Ar path
Start meli with given configuration file.
.It Cm create-config Op Ar path
Create configuration file in
.Pa path
if given, or at
.Pa $XDG_CONFIG_HOME/meli/config.toml
.It Cm test-config Op Ar path
Test a configuration file for syntax issues or missing options.
.It Cm man Op Ar page
Print documentation page and exit (Piping to a pager is recommended).
.It Cm install-man Op Ar path
Install manual pages to the first location provided by
.Ev MANPATH
or
.Xr manpath 1 ,
unless you specify the directory as an argument.
.It Cm compiled-with
Print compile time feature flags of this binary.
.It Cm edit-config
Edit configuration files with
.Ev EDITOR
or
.Ev VISUAL Ns
\&.
.It Cm help
Prints help information or the help of the given subcommand(s).
.It Cm print-app-directories
Print all directories that
.Ns Nm
creates and uses.
.It Cm print-config-path
Print location of configuration file that will be loaded on normal app startup.
.It Cm print-default-theme
Print default theme keys and values in TOML syntax, to be used as a blueprint.
.It Cm print-loaded-themes
Print all loaded themes in TOML syntax.
.It Cm print-log-path
Print log file location.
.It Cm view
View mail from input file.
.El
.Sh DESCRIPTION
.Nm
is a terminal mail client aiming for extensive and user-friendly configurability.
.Bd -literal
^^ .-=-=-=-. ^^
^^ (`-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^ ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
( `-=-=-=-(@)-=-=-` ) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`)
^^ (`-=-=-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-`)
`-=-=-=-=-` ^^
.Ed
.Sh STARTING WITH meli
When launched for the first time,
.Nm
will search for its configuration directory,
.Pa $XDG_CONFIG_HOME/meli/ Ns
\&.
If it doesn't exist, you will be asked if you want to create one and presented with a sample configuration file
.Pq Pa $XDG_CONFIG_HOME/meli/config.toml
that includes the basic settings required for setting up accounts allowing you to copy and edit right away.
See
.Xr meli.conf 5
for the available configuration options.
.Pp
At any time, you may press
.Shortcut \&? general toggle_help
for a searchable list of all available actions and shortcuts, along with every possible setting and command that your version supports.
.Pp
The main visual navigation tool, the left-side sidebar may be toggled with
.ShortcutPeriod \(ga listing toggle_menu_visibility
\&.
.Pp
Each mailbox may be viewed in 4 modes:
.Bl -dash -compact
.It
.Tg index-style-plain
.Em Plain
views each mail individually,
.It
.Tg index-style-threaded
.Em Threaded
shows their thread relationship visually,
.It
.Tg index-style-conversations
.Em Conversations
collapses each thread of e\-mails into a single entry,
.It
.Tg index-style-compact
.Em Compact
shows one row per thread.
.El
.Pp
If you're using a light color palette in your terminal, you should set
.Em theme = "light"
in the
.Em terminal
section of your configuration.
See
.Xr meli-themes 5
for complete documentation on user themes.
.Pp
See
.Xr meli 7
for a more detailed tutorial on using
.Nm Ns
\&.
.Sh SHORTCUTS
See
.Xr meli.conf 5 SHORTCUTS
for shortcuts and their default values.
.Sh VIEWING MAIL
Open attachments by typing their index in the attachments list and then
.ShortcutPeriod a envelope_view open_attachment
\&.
.Nm
will attempt to open text inside its pager, and other content via
.Cm xdg-open Ns
\&.
Press
.Shortcut m envelope_view open_mailcap
instead to use the mailcap entry for the MIME type of the attachment, if any.
See
.Sx FILES
for the location of the mailcap files and
.Xr mailcap 5
for their syntax.
You can save individual attachments with the
.Command save-attachment Ar INDEX Ar path-to-file
command.
.Ar INDEX
is the attachment's index in the listing.
If the path provided is a directory, the attachment is saved with its filename set to the filename in the attachment, if any.
If the 0th index is provided, the entire message is saved.
If the path provided is a directory, the message is saved as an eml file with its filename set to the messages message-id.
.Sh SEARCH
Each e\-mail storage backend has a default search method assigned.
.Em IMAP
uses the SEARCH command,
.Em notmuch
uses libnotmuch and
.Em Maildir/mbox
performs a slow linear search.
It is advised to use a search backend on
.Em Maildir/mbox
accounts.
.Nm Ns
, if built with sqlite3, includes the ability to perform full text search on the following fields:
.Em From ,
.Em To ,
.Em Cc ,
.Em Bcc ,
.Em In-Reply-To ,
.Em References ,
.Em Subject
and
.Em Date .
The message body (in plain text human readable form) and the flags can also be queried.
To enable sqlite3 indexing for an account set
.Em search_backend
to
.Em sqlite3
in the configuration file and to create the sqlite3 index issue command:
.Command index Ar ACCOUNT_NAME Ns
To search in the message body type your keywords without any special formatting.
To search in specific fields, prepend your search keyword with "field:" like so:
.Pp
.D1 subject:helloooo or subject:\&"call for help\&" or \&"You remind me today of a small, Mexican chihuahua.\&"
.Pp
.D1 not ((from:unrealistic and (to:complex or not "query")) or flags:seen,draft)
.Pp
.D1 alladdresses:mailing@example.com and cc:me@example.com
.Pp
Boolean operators are
.Em or Ns
,
.Em and
and
.Em not
.Po
alias:
.Em \&!
.Pc
String keywords with spaces must be quoted.
Quotes should always be escaped.
.Ss Important Notice about IMAP/JMAP
.HorizontalRule
To prevent downloading all your messages from your IMAP/JMAP server, don't set
.Em search_backend
to
.Em sqlite3 Ns
\&.
.Nm
will relay your queries to the IMAP server.
Expect a delay between query and response.
Sqlite3 on the contrary at reasonable mailbox sizes should have a non noticeable delay.
.Ss QUERY ABNF SYNTAX
.HorizontalRule
.Bl -dash -compact
.It
.Li query = \&"(\&" query \&")\&" | from | to | cc | bcc | alladdresses | subject | flags | has_attachments | query \&"or\&" query | query \&"and\&" query | not query
.It
.Li not = \&"not\&" | \&"!\&"
.It
.Li quoted = ALPHA / SP *(ALPHA / DIGIT / SP)
.It
.Li term = ALPHA *(ALPHA / DIGIT) | DQUOTE quoted DQUOTE
.It
.Li tagname = term
.It
.Li flagval = \&"passed\&" | \&"replied\&" | \&"seen\&" | \&"read\&" | \&"junk\&" | \&"trash\&" | \&"trashed\&" | \&"draft\&" | \&"flagged\&" | tagname
.It
.Li flagterm = flagval | flagval \&",\&" flagterm
.It
.Li from = \&"from:\&" term
.It
.Li to = \&"to:\&" term
.It
.Li cc = \&"cc:\&" term
.It
.Li bcc = \&"bcc:\&" term
.It
.Li alladdresses = \&"alladdresses:\&" term
.It
.Li subject = \&"subject:\&" term
.It
.Li flags = \&"flags:\&" flag | \&"tags:\&" flag | \&"is:\&" flag
.El
.Sh FLAGS
.Nm
supports the basic maildir flags: passed, replied, seen, trashed, draft and flagged.
Flags can be searched with the
.Ns Ql flags:
prefix in a search query, and can be modified by
.Command flag set FLAG
and
.Command flag unset FLAG
.Sh TAGS
.Nm
supports tagging in notmuch and IMAP/JMAP backends.
Tags can be searched with the
.Ns Ql tags:
or
.Ns Ql flags:
prefix in a search query, and can be modified by
.Command tag add TAG
and
.Command tag remove TAG
(see
.Xr meli.conf 5 TAGS Ns
, settings
.Ic colors
and
.Ic ignore_tags
for how to set tag colors and tag visibility)
.Sh COMPOSING
.Ss Opening the message Composer tab
To create a new mail message, press
.Shortcut m listing new_mail
while viewing a mailbox.
To reply to a mail, press
.ShortcutPeriod R envelope_view reply
\&.
Both these actions open the mail composer view in a new tab.
.Ss Editing text
.HorizontalRule
.Bl -dash -compact
.It
Edit the header fields by selecting with the arrow keys and pressing
.Shortcut Enter general focus_in_text_field
to enter
.Em INSERT
mode and
.Cm Esc
key to exit.
.It
At any time you may press
.Shortcut e composing edit Ns
to launch your editor (see
.Xr meli.conf 5 COMPOSING Ns
, setting
.Ic editor_command
for how to select which editor to launch).
.It
Your editor can be used in
.Nm Ns
\&'s embed terminal emulator by setting
.Ic embed
to
.Em true
in your composing settings
.Po
You can return to
.Nm
at any time by pressing
.Aq Ctrl-Z
.Pc
.It
When launched, your editor captures all input until it exits or stops.
.It
To stop your editor and return to
.Nm
press
.Aq Ctrl-z
and to resume editing press the
.Ic edit
command again.
.El
.Ss Attachments
.HorizontalRule
Attachments may be handled with the
.Cm add-attachment Ns
,
.Cm remove-attachment
commands (see below).
.Ss Sending
.HorizontalRule
Finally, pressing
.Shortcut s composing send_mail
will send your message according to your settings
.Po
see
.Xr meli.conf 5 COMPOSING Ns
, setting name
.Ic send_mail
.Pc Ns
\&.
With no Draft or Sent mailbox,
.Nm
tries first saving mail in your INBOX and then at any other mailbox.
On complete failure to save your draft or sent message it will be saved in your
.Em tmp
directory instead and you will be notified of its location.
.Ss Drafts
.HorizontalRule
To save your draft without sending it, issue
.Em COMMAND
.Cm close
and select 'save as draft'.
.sp
To open a draft for further editing, select your draft in the mail listing and press
.Ic edit Ns
\&.
.Sh CONTACTS
.Nm
supports three kinds of contact backends:
.Bl -enum -compact
.It
an internal format that gets saved under
.Pa $XDG_DATA_HOME/meli/account_name/addressbook Ns
\&.
.It
vCard files (v3, v4) through the
.Ic vcard_folder
option in the account section.
The path defined as
.Ic vcard_folder
can hold multiple vCards per file.
They are loaded read only.
.It
a
.Xr mutt 1
compatible alias file in the option
.Ic mutt_alias_file
.El
.sp
See
.Xr meli.conf 5 ACCOUNTS
for the complete account contact configuration values.
.Sh MODES
.Bl -tag -compact -width 8n
.It NORMAL
is the default mode
.It COMMAND
commands are issued in
.Em COMMAND
mode, by default started with
.Shortcut \&: general enter_command_mode
and exited with
.Aq Esc
key.
.It EMBED
is the mode of the embed terminal emulator
.It INSERT
captures all input as text input, and is exited with
.Cm Esc
key.
.El
.Sh COMMAND
.Ss Mail listing commands
.HorizontalRule
.Bl -tag -width 36n
.It Cm set Ar plain | threaded | compact | conversations
set the way mailboxes are displayed
.El
.TS
allbox tab(:);
lb l.
conversations:shows one entry per thread
compact:shows one row per thread
threaded:shows threads as a tree structure
plain:shows one row per mail, regardless of threading
.TE
.Bl -tag -width 36n
.It Cm sort Ar subject | date \ Ar asc | desc
sort mail listing
.It Cm subsort Ar subject | date \ Ar asc | desc
sorts only the first level of replies.
.It Cm go Ar n
where
.Ar n
is a mailbox prefixed with the
.Ar n
number in the side menu for the current account
.It Cm toggle thread_snooze
don't issue notifications for thread under cursor in thread listing
.It Cm search Ar STRING
search mailbox with
.Ar STRING
query.
Escape exits search results.
.It Cm select Ar STRING
select threads matching
.Ar STRING
query.
.It Cm clear-selection
Clear current selection.
.It Cm set seen, set unseen
Set seen status of message.
.It Cm import Ar FILEPATH Ar MAILBOX_PATH
Import mail from file into given mailbox.
.It Cm copyto, moveto Ar MAILBOX_PATH
Copy or move to other mailbox.
.It Cm copyto, moveto Ar ACCOUNT Ar MAILBOX_PATH
Copy or move to another account's mailbox.
.It Cm delete
Delete selected threads.
.It Cm export-mbox Ar FILEPATH
Export selected threads to mboxcl2 file.
.It Cm create\-mailbox Ar ACCOUNT Ar MAILBOX_PATH
create mailbox with given path.
Be careful with backends and separator sensitivity (eg IMAP)
.It Cm subscribe\-mailbox Ar ACCOUNT Ar MAILBOX_PATH
subscribe to mailbox with given path
.It Cm unsubscribe\-mailbox Ar ACCOUNT Ar MAILBOX_PATH
unsubscribe to mailbox with given path
.It Cm rename\-mailbox Ar ACCOUNT Ar MAILBOX_PATH_SRC Ar MAILBOX_PATH_DEST
rename mailbox
.It Cm delete\-mailbox Ar ACCOUNT Ar MAILBOX_PATH
deletes mailbox in the mail backend.
This action is irreversible.
.El
.Ss Mail view commands
.HorizontalRule
.Bl -tag -width 36n
.It Cm pipe Ar EXECUTABLE Ar ARGS
pipe pager contents to binary
.It Cm filter Ar EXECUTABLE Ar ARGS
filter and display pager contents through command
.It Cm list-post
post in list of viewed envelope
.It Cm list-unsubscribe
unsubscribe automatically from list of viewed envelope
.It Cm list-archive
open list archive with
.Cm xdg-open
.El
.Ss Composing mail commands
.HorizontalRule
.Bl -tag -width 36n
.It Cm mailto Ar MAILTO_ADDRESS
Opens a composer tab with initial values parsed from the
.Li mailto:
address.
.It Cm add-attachment Ar PATH
in composer, add
.Ar PATH
as an attachment
.It Cm add-attachment < Ar CMD Ar ARGS
in composer, pipe
.Ar CMD Ar ARGS
output into an attachment
.It Cm add-attachment-file-picker
Launch command defined in the configuration value
.Ic file_picker_command
in
.Xr meli.conf 5 TERMINAL
.It Cm add-attachment-file-picker < Ar CMD Ar ARGS
Launch command
.Ar CMD Ar ARGS Ns
\&.
The command should print file paths in stderr, separated by NULL bytes.
.It Cm remove-attachment Ar INDEX
remove attachment with given index
.It Cm toggle sign
toggle between signing and not signing this message.
If the gpg invocation fails then the mail won't be sent.
See
.Xr meli.conf 5 PGP
for PGP configuration.
.It Cm save-draft
saves a copy of the draft in the Draft folder
.El
.Ss Generic commands
.HorizontalRule
.Bl -tag -width 36n
.It Cm open-in-tab
opens envelope view in new tab
.It Cm close
closes closeable tabs
.It Cm setenv Ar KEY=VALUE
set environment variable
.Ar KEY
to
.Ar VALUE
.It Cm printenv Ar KEY
print environment variable
.Ar KEY
.It Cm quit
Quits
.Nm Ns
\&.
.It Cm reload-config
Reloads configuration but only if account configuration is unchanged.
Useful if you want to reload some settings without restarting
.Nm Ns
\&.
.El
.Sh EXIT STATUS
.Nm
exits with 0 on a successful run.
Other exit statuses are:
.Bl -tag -width 5n
.It 1
catchall for general errors
.It 101
process panic
.El
.Sh ENVIRONMENT
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Ev EDITOR
Specifies the editor to use
.It Ev MELI_CONFIG
Override the configuration file
.It Ev NO_COLOR
When defined (regardless of its value), prevents the addition of
.Tn ANSI
color.
The configuration value
.Ic use_color
overrides this.
.El
.Sh FILES
.Nm
uses the following parts of the XDG standard:
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Ev XDG_CONFIG_HOME
defaults to
.Pa ~/.config/
.It Ev XDG_CACHE_HOME
defaults to
.Pa ~/.cache/
.El
.Pp
and appropriates the following locations:
.Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
.It Pa $XDG_CONFIG_HOME/meli/
User configuration directory
.It Pa $XDG_CONFIG_HOME/meli/config.toml
User configuration file, see
.Xr meli.conf 5
for its syntax and values.
.It Pa $XDG_CONFIG_HOME/meli/hooks/*
Reserved for event hooks.
.It Pa $XDG_CONFIG_HOME/meli/plugins/*
Reserved for plugin files.
.It Pa $XDG_CACHE_HOME/meli/*
Internal cached data used by meli.
.It Pa $XDG_DATA_HOME/meli/*
Internal data used by meli.
.It Pa $XDG_DATA_HOME/meli/meli.log
Operation log.
.It Pa /tmp/meli/*
Temporary files generated by
.Nm Ns
\&.
.El
.Pp
Mailcap entries are searched for in the following files, in this order:
.Pp
.Bl -enum -compact -offset indent
.It
.Pa $XDG_CONFIG_HOME/meli/mailcap
.It
.Pa $XDG_CONFIG_HOME/.mailcap
.It
.Pa $HOME/.mailcap
.It
.Pa /etc/mailcap
.It
.Pa /usr/etc/mailcap
.It
.Pa /usr/local/etc/mailcap
.El
.Sh STANDARDS
.Bl -dash -compact
.It
.Rs
.%B XDG Base Directory Specification
.%O Version 0.8
.%A Waldo Bastian
.%A Allison Karlitskaya
.%A Lennart Poettering
.%A Johannes Löthberg
.%U https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
.%D May 08, 2021
.Re
.It
.Rs
.%B maildir
.%A Daniel J. Bernstein
.%U https://cr.yp.to/proto/maildir.html
.%D 1995
.Re
.It
.Rs
.%B RFC1524 A User Agent Configuration Mechanism For Multimedia Mail Format Information
.%O mailcap file
.%I Legacy
.%D September 01, 1993
.%A Dr. Nathaniel S. Borenstein
.%U https://datatracker.ietf.org/doc/rfc1524/
.Re
.It
.Rs
.%B RFC2047 MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
.%I IETF
.%D November 01, 1996
.%A Keith Moore
.%U https://datatracker.ietf.org/doc/rfc2047/
.Re
.It
.Rs
.%B RFC2183 Communicating Presentation Information in Internet Messages: The Content-Disposition Header Field
.%I Legacy
.%D August 01, 1997
.%A Rens Troost
.%A Steve Dorner
.%A Keith Moore
.%U https://datatracker.ietf.org/doc/rfc2183/
.Re
.It
.Rs
.%B RFC2369 The Use of URLs as Meta-Syntax for Core Mail List Commands and their Transport through Message Header Fields
.%I Legacy
.%D July 01, 1998
.%A Joshua D. Baer
.%A Grant Neufeld
.%U https://datatracker.ietf.org/doc/rfc2369/
.Re
.It
.Rs
.%B RFC2426 vCard MIME Directory Profile
.%O vCard Version 3
.%I IETF
.%D September 01, 1998
.%A Frank Dawson
.%A Tim Howes
.%U https://datatracker.ietf.org/doc/rfc2426/
.Re
.It
.Rs
.%B RFC3156 MIME Security with OpenPGP
.%I IETF
.%D August 01, 2001
.%A Thomas Roessler
.%A Michael Elkins
.%A Raph Levien
.%A Dave Del Torto
.%U https://datatracker.ietf.org/doc/rfc3156/
.Re
.It
.Rs
.%B RFC3461 Simple Mail Transfer Protocol (SMTP) Service Extension for Delivery Status Notifications (DSNs)
.%I IETF
.%D January 23, 2003
.%A Keith Moore
.%U https://datatracker.ietf.org/doc/rfc3461/
.Re
.It
.Rs
.%B RFC3501 INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
.%I IETF
.%D March 18, 2003
.%A Mark Crispin
.%U https://datatracker.ietf.org/doc/rfc3501/
.Re
.It
.Rs
.%B RFC3676 The Text/Plain Format and DelSp Parameters
.%I IETF
.%D February 19, 2004
.%A Randall Gellens
.%U https://datatracker.ietf.org/doc/rfc3676/
.Re
.It
.Rs
.%B RFC3691 Internet Message Access Protocol (IMAP) UNSELECT command
.%I IETF
.%D February 20, 2004
.%A Alexey Melnikov
.%U https://datatracker.ietf.org/doc/rfc3691/
.Re
.It
.Rs
.%B RFC3977 Network News Transfer Protocol (NNTP)
.%I IETF
.%D October 26, 2006
.%A Clive Feather
.%U https://datatracker.ietf.org/doc/rfc3977/
.Re
.It
.Rs
.%B RFC4549 Synchronization Operations for Disconnected IMAP4 Clients
.%I IETF
.%D June 16, 2006
.%A Alexey Melnikov
.%U https://datatracker.ietf.org/doc/rfc4549/
.Re
.It
.Rs
.%B RFC4616 The PLAIN Simple Authentication and Security Layer (SASL) Mechanism
.%I IETF
.%D August 31, 2006
.%A Kurt Zeilenga
.%U https://datatracker.ietf.org/doc/rfc4616/
.Re
.It
.Rs
.%B RFC4954 SMTP Service Extension for Authentication
.%I IETF
.%D July 23, 2007
.%A Rob Siemborski
.%A Alexey Melnikov
.%U https://datatracker.ietf.org/doc/rfc4954/
.Re
.It
.Rs
.%B RFC5321 Simple Mail Transfer Protocol
.%I IETF
.%D October 01, 2008
.%A Dr. John C. Klensin
.%U https://datatracker.ietf.org/doc/rfc5321/
.Re
.It
.Rs
.%B RFC5322 Internet Message Format
.%I IETF
.%D October 01, 2008
.%A Pete Resnick
.%U https://datatracker.ietf.org/doc/rfc5322/
.Re
.It
.Rs
.%B RFC6048 Network News Transfer Protocol (NNTP) Additions to LIST Command
.%I IETF
.%D November 22, 2010
.%A Julien ÉLIE
.%U https://datatracker.ietf.org/doc/rfc6048/
.Re
.It
.Rs
.%B RFC6152 SMTP Service Extension for 8-bit MIME Transport
.%I IETF
.%D March 07, 2011
.%A Dave Crocker
.%A Dr. John C. Klensin
.%A Dr. Marshall T. Rose
.%A Ned Freed
.%U https://datatracker.ietf.org/doc/rfc6152/
.Re
.It
.Rs
.%B RFC6350 vCard Format Specification
.%O vCard Version 4
.%I IETF
.%D August 31, 2011
.%A Simon Perreault
.%U https://datatracker.ietf.org/doc/rfc6350/
.Re
.It
.Rs
.%B RFC6532 Internationalized Email Headers
.%I IETF
.%D February 17, 2012
.%A Abel Yang
.%A Shawn Steele
.%A Ned Freed
.%U https://datatracker.ietf.org/doc/rfc6532/
.Re
.It
.Rs
.%B RFC6868 Parameter Value Encoding in iCalendar and vCard
.%I IETF
.%D February 14, 2013
.%A Cyrus Daboo
.%U https://datatracker.ietf.org/doc/rfc6868/
.Re
.It
.Rs
.%B RFC7162 IMAP Extensions: Quick Flag Changes Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC)
.%I IETF
.%D May 23, 2014
.%A Alexey Melnikov
.%A Dave Cridland
.%U https://datatracker.ietf.org/doc/rfc7162/
.Re
.It
.Rs
.%B RFC8620 The JSON Meta Application Protocol (JMAP)
.%I IETF
.%D July 18, 2019
.%A Neil Jenkins
.%A Chris Newman
.%U https://datatracker.ietf.org/doc/rfc8620/
.Re
.It
.Rs
.%B RFC8621 The JSON Meta Application Protocol (JMAP) for Mail
.%I IETF
.%D August 08, 2019
.%A Neil Jenkins
.%A Chris Newman
.%U https://datatracker.ietf.org/doc/rfc8621/
.Re
.El
.Sh SEE ALSO
.Xr meli.conf 5 ,
.Xr meli-themes 5 ,
.Xr meli 7 ,
.Xr xdg-open 1 ,
.Xr mailcap 5
.Sh AUTHORS
Copyright 2017\(en2024
.An Manos Pitsidianakis Aq Mt manos@pitsidianak.is
.Pp
Released under the GPL, version 3 or greater.
This software carries no warranty of any kind.
.Po
See
.Pa COPYING
for full copyright and warranty notices.
.Pc
.Ss Links
.Bl -item -compact
.It
.Lk https://meli\-email.org "Website"
.It
.Lk https://git.meli\-email.org/meli/meli "Main\ git\ repository\ and\ issue\ tracker"
.It
.Lk https://codeberg.org/meli/meli "Official\ read-only\ git\ mirror\ on\ codeberg.org"
.It
.Lk https://github.com/meli/meli "Official\ read-only\ git\ mirror\ on\ github.com"
.It
.Lk https://crates.io/crates/meli "meli\ crate\ on\ crates.io"
.El

764
meli/docs/meli.7 100644
View File

@ -0,0 +1,764 @@
.\" meli - meli.7
.\"
.\" Copyright 2017-2022 Manos Pitsidianakis
.\"
.\" This file is part of meli.
.\"
.\" meli is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
.\"
.\".de Hr
.\".Bd -literal -offset center
.\"╌╍─────────────────────────────────────────────────────────╍╌
.\".Ed
.\"..
.de Shortcut
.Sm
.Aq \\$1
\
.Po
.Em shortcuts.\\$2\&. Ns
.Em \\$3
.Pc
.Sm
..
.de ShortcutPeriod
.Aq \\$1
.Po
.Em shortcuts.\\$2\&. Ns
.Em \\$3
.Pc Ns
..
.de Command
.Bd -ragged -offset 1n
.Cm \\$*
.Ed
..
.\".Dd November 11, 2022
.Dd March 10, 2024
.Dt MELI 7
.Os
.Sh NAME
.Nm meli
.Nd Tutorial for the meli terminal e\-mail client
.Sh SYNOPSIS
.Nm
.Op ...
.Sh DESCRIPTION
.Nm
is a terminal mail client aiming for extensive and user\-friendly configurability.
.Bd -literal -offset center
^^ .-=-=-=-. ^^
^^ (`-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^ ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
( `-=-=-=-(@)-=-=-` ) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-=-=-`)
^^ (`-=-=-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-=-=-=-`) ^^
(`-=-=-=-=-=-=-`) ^^
^^ (`-=-=-=-=-`)
`-=-=-=-=-` ^^
.Ed
.Sh INTRODUCTION
To quit
.Nm
press
.Shortcut q general quit
at any time.
When launched for the first time,
.Nm
will search for its configuration directory,
.Pa $XDG_CONFIG_HOME/meli/ Ns
\&.
If it doesn't exist, you will be asked if you want to create one and presented with a sample configuration file
.Pq Pa $XDG_CONFIG_HOME/meli/config.toml
that includes the basic settings required for setting up accounts allowing you to copy and edit right away.
See
.Xr meli.conf 5
for the available configuration options.
.Pp
At any time, you may press
.Shortcut \&? general toggle_help
for a searchable list of all available actions and shortcuts, along with every possible setting and command that your version supports.
.Pp
Each time a shortcut is mentioned in this document, you will find a parenthesis next to it with the name of the shortcut setting along with its section in the configuration settings so that you can modify it if you wish.
.Pp
For example, to set the
.Em toggle_help
shortcut mentioned in the previous paragraph, add the following to your configuration:
.Bd -literal -offset center
[shortcuts]
general.toggle_help = 'F1'
.Ed
.sp
Or alternatively:
.Bd -literal -offset center
[shortcuts.general]
toggle_help = 'F1'
.Ed
.Pp
To go to the next tab on the right, press
.ShortcutPeriod T general next_tab
\&.
.Sh INTERACTING WITH Nm
You will be interacting with
.Nm
in four primary ways:
.Bl -column
.It 1.
keyboard shortcuts in
.Sy NORMAL
mode.
.It 2.
commands with arguments in
.Sy COMMAND
mode.
.It 3.
regular text input in text input widgets in
.Sy INSERT
mode.
.It 4.
any kind of input that gets passed directly into an embedded terminal in
.Sy EMBED
mode.
.El
.Sh MODES
.Nm
is a modal application, just like
.Xr vi 1 Ns
\&.
This means that pressing the same keys in different modes would yield different results.
This allows you to separate how the input is interpreted without the need to focus your input with a mouse.
.Bl -tag -width 8n
.It NORMAL
This is the default mode of
.Nm Ns
\&.
All keyboard shortcuts work in this mode.
.It COMMAND
Commands are issued in
.Sy COMMAND
mode, by default started with
.Shortcut \&: general enter_command_mode
and exited with
.Aq Esc
key.
.It EMBED
This is the mode of the embed terminal emulator.
To exit an embedded application, issue
.Aq Ctrl\-C
to kill it or
.Aq Ctrl\-Z
to stop the program and follow the instructions on
.Nm
to exit.
.It INSERT
This mode is entered when pressing
.Aq Enter
on a cursor selected text input field, and it captures all input as text input.
It is exited with the
.Aq Esc
key.
.El
.Sh ACTIVE SHORTCUTS POPUP
By pressing
.Shortcut \&? general toggle_help
at any time, the shortcuts popup display status gets toggled.
You can find all valid shortcuts for the current UI state you are in.
.Bd -literal -offset center
┌─shortcuts──Press ? to close────────────────────────────────┐
│ ▀│
│ use COMMAND "search" to find shortcuts █│
│ Use Up, Down, Left, Right to scroll. █│
│ █│
│ pager █│
│ █│
│ PageDown page_down █│
│ PageUp page_up │
│ j scroll_down │
│ k scroll_up │
│ │
│ view mail │
│ │
│ c add_addresses_to_contacts │
│ e edit │
│ u toggle_url_mode │
│ a open_attachment │
│ m open_mailcap │
│ R reply │
│ C-r reply_to_author │
│ C-g reply_to_all │
│ C-f forward │
│ M-r view_raw_source │
│ h toggle_expand_headers ▄│
└────────────────────────────────────────────────────────────┘
.Ed
.Bd -ragged -offset 3n
.Em Shows\ active\ shortcuts\ in\ order\ of\ the\ widget\ hierarchy\&.
.Ed
.Sh MAIN VIEW
.Bd -literal -offset center
┌───────────────────────┐
├────┼──────────────────┤
│___ │ ___________ │
│ _ │ _______________ │
│ _ │__________________│
│ _ │ ___________ │
│ │ _____ │
│ │ │
└────┴──────────────────┘
.Ed
.Bd -ragged -offset 3n
.Em The\ main\ view's\ layout\&.
.Ed
.sp
This is the view you will spend more time with in
.Nm Ns
\&.
.Pp
Press
.Shortcut \(ga listing toggle_menu_visibility
to toggle the sidebars visibility.
.Pp
Press
.Shortcut Left listing focus_right
to switch focus on the sidebar menu.
Press
.Shortcut Right listing focus_left
to switch focus on the e\-mail list.
.Pp
On the e\-mail list, press
.Shortcut k listing scroll_up
to scroll up, and
.Shortcut j listing scroll_down
to scroll down.
Press
.Shortcut Enter listing open_entry
to open an e\-mail entry and
.Shortcut i listing exit_entry
to exit it.
.Bd -ragged
.Sy The sidebar\&.
.Ed
.Bd -literal -offset center
┌─────────────┉┉┉┉┉✂
│ mail▐ contact li✂
│personal account ✂
│ 0 INBOX ✂
│ 1 ┣━Sent ✂
│ 2 ┣━Lists ✂
│ 3 ┃ ┣━meli-dev ✂
│ 4 ┃ ┗━meli ✂
│ 5 ┣━Drafts ✂
│ 6 ┣━Trash ✂
│ 7 ┗━foobar ✂
┇ 8 Trash ✂
✂ ✂ ✂ ✂ ✂ ✂ ✂ ✂ ✂ ✂
.Ed
.sp
Press
.Shortcut k listing scroll_up
to scroll up, and
.Shortcut j listing scroll_down
to scroll down.
.Pp
Press
.Shortcut Enter listing open_mailbox
to open an entry (either a mailbox or an account name).
Entering an account name will show you a page with details about the account and its network connection, depending on the backend.
.Pp
While focused in the sidebar, you can
.Dq collapse
a mailbox tree, if it has children, and you can open it with
.ShortcutPeriod Space listing toggle_mailbox_collapse
\&.
You can have mailbox trees collapsed on startup by default by setting a mailbox's
.Ic collapsed
setting to
.Em true Ns
\&.
See
.Xr meli.conf 5 section MAILBOXES
for details.
.Pp
You can increase the sidebar's width with
.Shortcut Ctrl\-p listing increase_sidebar
and decrease with
.ShortcutPeriod Ctrl\-o listing decrease_sidebar
\&.
.Bd -ragged
.Sy The status bar.
.Ed
.Bd -literal -offset center
┌────────────────────────────────────────────────────┈┈
│NORMAL | Mailbox: Inbox, Messages: 25772, New: 3006
└────────────────────────────────────────────────────┈┈
.Ed
.Pp
The status bar shows which mode you are, and the status message of the current view.
In the pictured example, it shows the status of a mailbox called
.Dq Inbox
with lots of e\-mails.
.Bd -ragged
.Sy The number modifier buffer.
.Ed
.Bd -literal -offset center
┈┈────────────┐
12 │
┈┈────────────┘
.Ed
.Pp
Some commands may accept a number modifier.
.Tg number-modifier
For example, scroll down commands can receive a multiplier
.Em n
to scroll down
.Em n
entries.
Another use of the number buffer is opening URLs inside the pager.
See
.Sx PAGER
for an explanation of interacting with URLs in e\-mails.
.Pp
Pressing numbers in
.Sy NORMAL
mode will populate this buffer.
To erase it, press the
.Aq Esc
key.
.Sh MAIL LIST
There are four different list styles:
.Bl -hyphen -compact
.It
.Qq plain
which shows one line per e\-mail.
.It
.Qq threaded
which shows a threaded view with drawn tree structure.
.It
.Qq compact
which shows one line per thread which can include multiple e\-mails.
.It
.Qq conversations
which shows more than one line per thread which can include multiple e\-mails with more details about the thread.
.El
.Bd -ragged
.Sy Plain view\&.
.Ed
.Bd -literal -offset center
│42 Fri, 02 Sep 2022 19:51 xxxxxxxxxxxxx < [PATCH 3/8] │
│43 Fri, 02 Sep 2022 19:51 xxxxxxxxxxxxx < [PATCH 2/8] │
│44 Fri, 02 Sep 2022 19:51 xxxxxxxxxxxxx < [PATCH 1/8] │
|45 Fri, 02 Sep 2022 19:51 xxxxxxxxxxxxx < [PATCH 0/8] |
│46 Fri, 02 Sep 2022 18:18 xxxxxxxx <xxxxx Re: [PATCH 3│
.Ed
.Bd -ragged
.Sy Threaded view\&.
.Ed
.Bd -literal -offset center
│12 9 hours ago xxxxxxxxxxxxxxx [PATCH v3 0│
│13 9 hours ago xxxxxxxxxxxxxxx ├─>[PATCH │
│14 9 hours ago xxxxxxxxxxxxxxx ├─>[PATCH │
|15 9 hours ago xxxxxxxxxxxxxxx ├─>[PATCH |
│16 9 hours ago xxxxxxxxxxxxxxx ├─>[PATCH │
│17 9 hours ago xxxxxxxxxxxxxxx └─>[PATCH │
│18 2022-08-23 01:23:51 xxxxxxxxxxxxxxx [RFC v4 00/│
│19 2022-08-23 01:23:52 xxxxxxxxxxxxxxx ├─>[RFC v4│
|20 2022-08-30 10:30:16 xxxxxxxxxxxxxxx │ └─> |
│21 6 days ago xxxxxxxxxxxxxxx │ └─> │
│22 2022-08-23 01:23:53 xxxxxxxxxxxxxxx ├─>[RFC v4│
.Ed
.Bd -ragged
.Sy Compact view\&.
.Ed
.Bd -literal -offset center
│18 2022-…:38 xxxxxxxxxxxxxxx [PATCH v3 3/3] u…_l() (2) │
|19 2022-…:49 xxxxxxxxxxxxxxx [PATCH v8 0/7] A…e (3) |
│20 2022-…:10 xxxxxxxxxxxxxxx [PATCH v8 2/7] f…s (2) │
│21 2022-…:38 xxxxxxxxxxxxxxx [PATCH v8 3/7] b…s (2) │
│22 2022-…:53 xxxxxxxxxxxxxxx [PATCH v6 00/10] p…g (31) │
.Ed
.Bd -ragged
.Sy Conversations view\&.
.Ed
.Bd -literal -offset center
│[PATCH v2] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (5) │
|1 day ago▁▁▁▁xxxxxxxxxxxxx <xxxxxxxxxxxxx@xxxxxxxxxx>, xxxxx│
│ |
│[PATCH v2 0/8] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx│
│1 day ago▁▁▁▁xxxxxxxxxxxxxxx <xxxxxxxxxx@xxxxxxxxxxxxxx>, xx│
| │
│[PATCH 0/2] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (4) |
│2 days ago▁▁▁▁xxxxxxxxxxxxxxxx <xxxxxxxx@xxxxxxxxxxx>, xxxxx│
│ │
│[PATCH 0/8] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (12) │
│2 days ago▁▁▁▁xxxxxxxxxxxxx <xxxxxxxx@xxxxxxxxxx>, xxxxxxxxx│
.Ed
.sp
.sp
.Sy Performing actions on entries and/or selections\&.
.Pp
Press
.Shortcut v listing select_entry
to toggle the selection of a single entry.
.Qq select_entry
can be prefixed by a number modifier and affixed by a scrolling motion (up or down) to select multiple entries.
.Tg number-modifier
Simple set operations can be performed on a selection with these shortcut modifiers:
.sp
.Bl -hyphen -compact
.It
Union modifier:
.Shortcut Ctrl\-u listing union_modifier
.It
Difference modifier:
.Shortcut Ctrl\-d listing diff_modifier
.It
Intersection modifier:
.Shortcut Ctrl\-i listing intersection_modifier
.El
.Pp
To set an entry as
.Qq read
\&, use the
.Shortcut n listing set_seen
shortcut.
To set an entry as
.Qq unread
\&, use the command
.Command set unseen
.sp
which also has its complement
.Command set seen
.sp
action.
.Pp
For e\-mail backends that support flags you can use the following commands on entries and selections to modify them:
.Command flag set FLAG
.Command flag unset FLAG
.Pp
For e\-mail backends that support tags
.Po
like
.Qq IMAP
or
.Qq notmuch Ns
.Pc
you can use the following commands on entries and selections to modify them:
.Command tag add TAG
.Command tag remove TAG
.sp
(see
.Xr meli.conf 5 TAGS Ns
, settings
.Ic colors
and
.Ic ignore_tags
for how to set tag colors and tag visibility)
You can clear the selection with the
.Aq Esc
key.
.Sh PAGER
You can open an e\-mail entry by pressing
.ShortcutPeriod Enter listing open_entry
\&. This brings up the e\-mail view with the e\-mail content inside a pager.
.Bd -literal -offset center
┌────────────────────────────────────────────────────────────┐
│Date: Sat, 21 May 2022 16:16:11 +0300 ▀│
│From: Narrator <narrator@example.com> █│
│To: Stanley <427@example.com> █│
│Subject: The e-mail ending █│
│Message-ID: <gambheerata@example.com> █│
│ █│
│The story, and the choices, or what have you, and therefore█│
│by becoming it is! So on and so forth, until inevitably, we │
│all until the end of time. At which time, everything all at │
│once, so now you see? Blah, blah, blah, rah, rah, rah... │
│We've eaten too much and it can't be just yet. No, no! │
│Until two-hundred and forty-five! But the logic of │
│elimination, working backwards, the deduction therefore │
│becomes impossible to manufacture. It went on for nearly │
│ten thousand years, until just yesterday. Here and there, │
│forward and back, and never a moment before lunchtime. It │
│can't be! It's the only thing there is! How many billions │
│left until so much more than forever ago! Which is why I │
│say: │
│ │
│The story, and the choices, or what have you, and therefore │
│by becoming it is! So on and so forth, until inevitably, we▄│
└────────────────────────────────────────────────────────────┘
.Ed
.Bd -ragged -offset 3n
.Em The\ pager\ displaying\ an\ e\-mail\&.
.Ed
.Pp
The pager is simple to use.
Scroll with the following:
.Bl -hang -width 27n
.It Go to next pager page
.Shortcut PageDown pager page_down
.It Go to previous pager page
.Shortcut PageUp pager page_up
.It Scroll down pager.
.Shortcut j pager scroll_down
.It Scroll up pager.
.Shortcut k pager scroll_up
.El
.sp
All scrolling shortcuts can be prefixed with a number modifier
.Tg number-modifier
which will act as a multiplier.
.Pp
The pager can enter a special
.Em url
mode which will prefix all detected hyperlinks and e\-mail addresses with a number inside square brackets
.ShortcutPeriod u pager toggle_url_mode
\&.
Writing down a chosen number as a number modifier
.Tg number-modifier
and pressing
.Shortcut g envelope_view go_to_url
will attempt to open the link with the system's default open command
.Po
.Xr xdg-open 1
in supported OSes,
and
.Xr open 1
on MacOS
.Pc Ns
\&.
To override with a custom launcher, see
.Qo
.Li pager
.Qc
configuration setting
.Qo
.Li url_launcher
.Qc
.Po
see
.Xr meli.conf 5 PAGER
for more details
.Pc Ns
\&.
.Sh MAIL VIEW
Other things you can do when viewing e\-mail:
.Bl -dash -compact
.It
Most importantly, you can exit the mail view with:
.Shortcut i listing exit_entry
.It
Add addresses from the e\-mail headers to contacts:
.Shortcut c envelope_view add_addresses_to_contacts
.It
Open an attachment by entering its index as a number modifier and pressing:
.Tg number-modifier
.Shortcut a envelope_view open_attachment
.It
Open an attachment by its
.Xr mailcap 4
entry by entering its index as a number modifier and pressing:
.Shortcut m envelope_view open_mailcap
.It
Reply to envelope:
.Shortcut R envelope_view reply
.It
Reply to author:
.Shortcut Ctrl\-r envelope_view reply_to_author
.It
Reply to all/Reply to list/Follow up:
.Shortcut Ctrl\-g envelope_view reply_to_all
.It
Forward e\-mail:
.Shortcut Ctrl\-f envelope_view forward
.It
Expand extra headers: (References and others)
.Shortcut h envelope_view toggle_expand_headerk
.It
View envelope source in a pager: (toggles between raw and decoded source)
.Shortcut M\-r envelope_view view_raw_source
.It
Return to envelope_view if viewing raw source or attachment:
.Shortcut r envelope_view return_to_normal_view
.El
.Sh COMPOSING
To compose an e\-mail, you can either start with an empty draft by pressing
.Shortcut m listing new_mail
which opens a composer view in a new tab.
To reply to a specific e\-mail, when in envelope view you can select the specific action you want to take:
.sp
.Bl -dash -compact
.It
Reply to envelope.
.Shortcut R envelope_view reply
.It
Reply to author.
.Shortcut Ctrl\-r envelope_view reply_to_author
.It
Reply to all.
.Shortcut Ctrl\-g envelope_view reply_to_all
.El
.sp
To launch your editor, press
.ShortcutPeriod e composing edit
\&.
To send your draft, press
.ShortcutPeriod s composing send_mail
\&.
To save the draft without submission, enter the command
.Command close
.sp
and select
.Qq save as draft Ns
\&.
You can return to the draft by going to your
.Qq Drafts
mailbox and selecting
.ShortcutPeriod e envelope_view edit
\&.
.Bd -literal -offset center
┌────────────────────────────────────────────────────────────┐
│ mail▐ contact list ▐ composing ▍███████████████████████│
│ COMPOSING MESSAGE │
│ Date Mon, 05 Sep 2022 17:49:19 +0300 │
│ From myself <myself@example.com>░░░░ │
│ To friend <myfriend@example.com>░░ │
│ Cc ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ Bcc ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ Subject This is my subject!░░░░░░░░░░░░ │
│ │
│ Hello friend!░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ ☐ don't sign │
│ ☐ don't encrypt │
│ no attachments │
│ │
│NORMAL | Mailbox: Inbox, Messages: 25772, New: 3006 │
└────────────────────────────────────────────────────────────┘
.Ed
.Bd -ragged -offset 3n
.Em The\ lightly\ highlighted\ cells\ represent\ text\ input\ fields\&.
.Ed
.sp
If you enable the embed terminal option, you can launch your terminal editor of choice when you press
.Ic edit Ns
\&.
.Bd -literal -offset center
┌────────────────────────────────────────────────────────────┐
│ mail▐ contact list ▐ composing ▍███████████████████████│
│ ╓COMPOSING MESSAGE┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╖ │
│ ║ p/v/f/h/5/T/m/07f56b6e-ec09-49d9-b8d8-f0c5a81e7826 ║ │
│ ║ 7 Date: Mon, 05 Sep 2022 18:43:10 +0300 ║ │
│ ║ 6 From: Mister Cardholder <mrholder@example.com> ║ │
│ ║ 5 To: ║ │
│ ║ 4 Cc: ║ │
│ ║ 3 Bcc: ║ │
│ ║ 2 Subject: ║ │
│ ║ 1 User-Agent: meli 0.7.2 ║ │
│ ║8 █ ║ │
│ ║~ ║ │
│ ║~ ║ │
│ ║~ ║ │
│ ║~ ║ │
│ ║ N… <6e-ec09-49d9-b8d8-f0c5a81e7826 100% ㏑:8 ℅:1║ │
│ ╚════════════════════════════════════════════════════╝ │
│ │
│ │
│ ☐ don't sign │
│ ☐ don't encrypt │
│ no attachments │
│ │
│EMBED | Mailbox: Inbox, Messages: 25772, New: 3006 │
└────────────────────────────────────────────────────────────┘
.Ed
.Bd -ragged -offset 3n
.Bf -emphasis
.Xr nvim 1 Ns
\ running\ inside\ the\ composing\ tab\&.
.Ef
The\ double\ line\ border\ annotates\ the\ area\ of\ the\ embedded\ terminal,
the\ actual\ embedding\ is\ seamless\&.
.Ed
.Ss composing mail commands
.Bl -tag -width 36n
.It Cm add\-attachment Ar PATH
in composer, add
.Ar PATH
as an attachment
.It Cm add\-attachment < Ar CMD Ar ARGS
in composer, pipe
.Ar CMD Ar ARGS
output into an attachment
.It Cm add\-attachment\-file\-picker
Launch command defined in the configuration value
.Ic file_picker_command
in
.Xr meli.conf 5 TERMINAL
.It Cm add\-attachment\-file\-picker < Ar CMD Ar ARGS
Launch command
.Ar CMD Ar ARGS Ns
\&.
The command should print file paths in stderr, separated by NULL bytes.
.It Cm remove\-attachment Ar INDEX
remove attachment with given index
.It Cm toggle sign
toggle between signing and not signing this message.
If the gpg invocation fails then the mail won't be sent.
See
.Xr meli.conf 5 PGP
for PGP configuration.
.It Cm save\-draft
saves a copy of the draft in the Draft folder
.El
.\" [ref:TODO]: add contacts section
.Sh THEMES
See
.Xr meli-themes 5
for documentation on how to theme
.Nm Ns
\&.
.Sh SEE ALSO
.Xr meli 1 ,
.Xr meli.conf 5 ,
.Xr meli-themes 5 ,
.Xr xdg-open 1 ,
.Xr mailcap 5
.Sh AUTHORS
Copyright 2017\(en2024
.An Manos Pitsidianakis Aq Mt manos@pitsidianak.is
.Pp
Released under the GPL, version 3 or greater.
This software carries no warranty of any kind.
.Po
See
.Pa COPYING
for full copyright and warranty notices.
.Pc
.Ss Links
.Bl -item -compact
.It
.Lk https://meli\-email.org "Website"
.It
.Lk https://git.meli\-email.org/meli/meli "Main\ git\ repository\ and\ issue\ tracker"
.It
.Lk https://codeberg.org/meli/meli "Official\ read-only\ git\ mirror\ on\ codeberg.org"
.It
.Lk https://github.com/meli/meli "Official\ read-only\ git\ mirror\ on\ github.com"
.It
.Lk https://crates.io/crates/meli "meli\ crate\ on\ crates.io"
.El

2277
meli/docs/meli.conf.5 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
## Look into meli.conf(5) for all valid configuration options, their
## descriptions and default values
##
## The syntax for including other configuration files is enclosed in `:
##`include("account_one")`
##`include("./account_two")`
##`include("/home/absolute/path/to/shortcuts/config.toml")`
##
##
## Setting up a Maildir account
#[accounts.account-name]
#root_mailbox = "/path/to/root/mailbox"
#format = "Maildir"
#listing.index_style = "Conversations" # or [plain, threaded, compact]
#identity="email@example.com"
#display_name = "Name"
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
#
## Set mailbox-specific settings
# [accounts.account-name.mailboxes]
# "INBOX" = { rename="Inbox" }
# "drafts" = { rename="Drafts" }
# "foobar-devel" = { ignore = true } # don't show notifications for this mailbox
#
## Setting up an mbox account
#[accounts.mbox]
#root_mailbox = "/var/mail/username"
#format = "mbox"
#listing.index_style = "Compact"
#identity="username@hostname.local"
#
## Setting up an IMAP account
#[accounts."imap"]
#root_mailbox = "INBOX"
#format = "imap"
#server_hostname="mail.example.com"
#server_password="pha2hiLohs2eeeish2phaii1We3ood4chakaiv0hien2ahie3m"
#server_username="username@example.com"
##server_port="993" # imaps
#server_port="143" # STARTTLS
#use_starttls=true #optional
#listing.index_style = "Conversations"
#identity = "username@example.com"
#display_name = "Name Name"
### match every mailbox:
#subscribed_mailboxes = ["*" ]
### match specific mailboxes:
##subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
#
## Setting up an account for an already existing notmuch database
##[accounts.notmuch]
##root_mailbox = "/path/to/folder" # where .notmuch/ directory is located
##format = "notmuch"
##listing.index_style = "conversations"
##identity="username@example.com"
##display_name = "Name Name"
## # notmuch mailboxes are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
## [accounts.notmuch.mailboxes]
## "INBOX" = { query="tag:inbox", subscribe = true }
## "Drafts" = { query="tag:draft", subscribe = true }
## "Sent" = { query="from:username@example.com from:username2@example.com", subscribe = true }
##
## Setting up a Gmail account
#[accounts."gmail"]
#root_mailbox = '[Gmail]'
#format = "imap"
#server_hostname='imap.gmail.com'
#server_password="password"
#server_username="username@gmail.com"
#server_port="993"
#listing.index_style = "Conversations"
#identity = "username@gmail.com"
#display_name = "Name Name"
### match every mailbox:
#subscribed_mailboxes = ["*" ]
#composing.send_mail = 'msmtp --read-recipients --read-envelope-from'
### Gmail auto saves sent mail to Sent folder, so don't duplicate the effort:
#composing.store_sent_mail = false
#
##[accounts."jmap account"]
##root_mailbox = "INBOX"
##format = "jmap"
##server_url="http://localhost:8080"
##server_username="user@hostname.local"
##server_password="changeme"
##listing.index_style = "Conversations"
##identity = "user@hostname.local"
##subscribed_mailboxes = ["*", ]
##composing.send_mail = 'server_submission'
#
#[pager]
#filter = "COLUMNS=72 /usr/local/bin/pygmentize -l email"
#pager_context = 0 # default, optional
#sticky_headers = true # default, optional
#
#[notifications]
#script = "notify-send"
#xbiff_file_path = "path" # for use with xbiff(1)
#play_sound = true # default, optional
#sound_file = "path" # optional
#
###shortcuts
#[shortcuts.composing]
#edit = 'e'
#
#[shortcuts.contact-list]
#create_contact = 'c'
#edit_contact = 'e'
#
##Mail listing defaults
#[shortcuts.listing]
#prev_page = "PageUp"
#next_page = "PageDown"
#prev_mailbox = 'K'
#next_mailbox = 'J'
#prev_account = 'l'
#next_account = 'h'
#new_mail = 'm'
#set_seen = 'n'
#exit_entry = 'i'
#
##Pager defaults
#
#[shortcuts.pager]
#scroll_up = 'k'
#scroll_down = 'j'
#page_up = "PageUp"
#page_down = "PageDown"
#
#[composing]
##required for sending e-mail
#send_mail = 'msmtp --read-recipients --read-envelope-from'
##send_mail = { hostname = "smtp.example.com", port = 587, auth = { type = "auto", username = "user", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/user.gpg" } }, security = { type = "STARTTLS" } }
#editor_command = 'vim +/^$' # optional, by default $EDITOR is used.
#
#
#[pgp]
#auto_sign = false # always sign sent messages
#auto_verify_signatures = true # always verify signatures when reading signed e-mails
#
#[terminal]
#theme = "dark" # or "light"

View File

@ -0,0 +1,70 @@
[terminal.themes.nord]
"theme_default" = { fg = "$nord6", bg = "$nord0", attrs = "Default" }
"mail.listing.compact.even" = { fg = "theme_default", bg = "$nord1", attrs = "theme_default" }
"mail.listing.compact.odd" = { fg = "theme_default", bg = "$nord2", attrs = "theme_default" }
"mail.listing.plain.even" = { fg = "theme_default", bg = "$nord1", attrs = "theme_default" }
"mail.listing.plain.odd" = { fg = "theme_default", bg = "$nord2", attrs = "theme_default" }
"mail.listing.compact.even_highlighted" = { fg = "$nord0", bg = "$focused_bg", attrs = "theme_default" }
"mail.listing.compact.odd_highlighted" = { fg = "$nord0", bg = "$focused_bg", attrs = "theme_default" }
"mail.listing.conversations.highlighted" = { fg = "$nord0", bg = "$focused_bg", attrs = "theme_default" }
"mail.listing.conversations.selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.subject" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.unseen" = { fg = "theme_default", bg = "$nord2", attrs = "theme_default" }
"mail.listing.plain.even_highlighted" = { fg = "$nord0", bg = "$focused_bg", attrs = "theme_default" }
"mail.listing.plain.odd_highlighted" = { fg = "$nord0", bg = "$focused_bg", attrs = "theme_default" }
"mail.listing.tag_default" = { fg = "theme_default", bg = "$nord8", attrs = "theme_default" }
"mail.sidebar_highlighted" = { fg = "$nord1", bg = "$focused_bg", attrs = "theme_default" }
"mail.sidebar_highlighted_account" = { fg = "$nord5", bg = "$nord1", attrs = "theme_default" }
"mail.sidebar_highlighted_account_name" = { fg = "mail.sidebar_highlighted_account", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_index" = { fg = "mail.sidebar_highlighted_account", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_unread_count" = { fg = "mail.sidebar_highlighted_account", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_highlighted_unread_count" = { fg = "mail.sidebar_highlighted", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar" = { fg = "$nord5", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_account_name" = { fg = "$nord5", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_index" = { fg = "$nord1", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_unread_count" = { fg = "$nord1", bg = "theme_default", attrs = "theme_default" }
"mail.view.body" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers" = { fg = "$nord9", bg = "theme_default", attrs = "theme_default" }
"mail.view.thread.indentation.a" = { fg = "theme_default", bg = "$nord11", attrs = "theme_default" }
"mail.view.thread.indentation.b" = { fg = "theme_default", bg = "$nord12", attrs = "theme_default" }
"mail.view.thread.indentation.c" = { fg = "theme_default", bg = "$nord13", attrs = "theme_default" }
"mail.view.thread.indentation.d" = { fg = "theme_default", bg = "$nord14", attrs = "theme_default" }
"mail.view.thread.indentation.e" = { fg = "theme_default", bg = "$nord15", attrs = "theme_default" }
"mail.view.thread.indentation.f" = { fg = "theme_default", bg = "$nord13", attrs = "theme_default" }
"pager.highlight_search" = { fg = "$nord5", bg = "$nord7", attrs = "Bold" }
"pager.highlight_search_current" = { fg = "$nord7", bg = "$nord10", attrs = "Bold" }
"status.bar" = { fg = "$nord5", bg = "$nord3", attrs = "theme_default" }
"status.notification" = { fg = "$nord5", bg = "$nord3", attrs = "theme_default" }
"tab.bar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"tab.focused" = { fg = "$nord1", bg = "$focused_bg", attrs = "theme_default" }
"tab.unfocused" = { fg = "$nord4", bg = "$unfocused_bg", attrs = "theme_default" }
"widgets.form.field" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"widgets.form.highlighted" = { fg = "theme_default", bg = "$nord2", attrs = "theme_default" }
"widgets.form.label" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"widgets.list.header" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"widgets.options.highlighted" = { fg = "theme_default", bg = "$nord2", attrs = "theme_default" }
[terminal.themes.nord.color_aliases]
nord0 = "#2e3440"
nord1 = "#3b4252"
nord2 = "#434c5e"
nord3 = "#4c566a"
# snow storm
nord4 = "#d8dee9"
nord5 = "#e5e9f0"
nord6 = "#eceff4"
# frost
nord7 = "#8fbcbb"
nord8 = "#88c0d0"
nord9 = "#81a1c1"
nord10 = "#5e81ac"
# aurora
nord11 = "#bf616a"
nord12 = "#d08770"
nord13 = "#ebcb8b"
nord14 = "#a3be8c"
nord15 = "#b48ead"
# semantics
focused_bg = "$nord8"
unfocused_bg = "$nord3"

View File

@ -0,0 +1,60 @@
[terminal.themes.orca]
color_aliases = { "neon_green" = "#6ef9d4", "darkgrey" = "#4a4a4a", "neon_purple" = "#df2f94" }
"theme_default" = { fg = "White", bg = "Black", attrs = "Default" }
"mail.listing.attachment_flag" = { fg = "$neon_green", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.even" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.even_highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.compact.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.even_unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.compact.odd" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.odd_highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.compact.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.odd_unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.conversations" = { fg = "$darkgrey", bg = "theme_default", attrs = "Default" }
"mail.listing.conversations.date" = { fg = "$neon_purple", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.from" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.conversations.selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.subject" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.plain.even" = { fg = "theme_default", bg = "Grey19", attrs = "theme_default" }
"mail.listing.plain.even_highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.plain.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.plain.even_unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.plain.odd" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.odd_highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.plain.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.plain.odd_unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.tag_default" = { fg = "Black", bg = "$neon_green", attrs = "theme_default" }
"mail.listing.thread_snooze_flag" = { fg = "Red", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted" = { fg = "Grey7", bg = "White", attrs = "theme_default" }
"mail.sidebar_highlighted_account" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted_account_name" = { fg = "White", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_account_name" = { fg = "$darkgrey", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted_account_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_unread_count" = { fg = "mail.sidebar_unread_count", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_highlighted_unread_count" = { fg = "mail.sidebar_highlighted", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_index" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_unread_count" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.view.body" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers" = { fg = "DodgerBlue1", bg = "theme_default", attrs = "theme_default" }
"mail.view.thread.indentation.a" = { fg = "theme_default", bg = "#EC4436", attrs = "theme_default" }
"mail.view.thread.indentation.b" = { fg = "theme_default", bg = "#D301F9", attrs = "theme_default" }
"mail.view.thread.indentation.c" = { fg = "theme_default", bg = "#314EFB", attrs = "theme_default" }
"mail.view.thread.indentation.d" = { fg = "theme_default", bg = "#068ACD", attrs = "theme_default" }
"mail.view.thread.indentation.e" = { fg = "theme_default", bg = "#019589", attrs = "theme_default" }
"mail.view.thread.indentation.f" = { fg = "theme_default", bg = "#68A033", attrs = "theme_default" }
"pager.highlight_search" = { fg = "White", bg = "Teal", attrs = "Bold" }
"pager.highlight_search_current" = { fg = "White", bg = "NavyBlue", attrs = "Bold" }
"status.bar" = { fg = "White", bg = "Black", attrs = "theme_default" }
"status.notification" = { fg = "Plum1", bg = "theme_default", attrs = "theme_default" }
"tab.bar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"tab.focused" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"tab.unfocused" = { fg = "$darkgrey", bg = "Black", attrs = "theme_default" }
"widgets.form.field" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"widgets.form.highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"widgets.form.label" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"widgets.list.header" = { fg = "Black", bg = "White", attrs = "Bold" }
"widgets.options.highlighted" = { fg = "theme_default", bg = "Grey", attrs = "theme_default" }

View File

@ -0,0 +1,69 @@
[terminal.themes.sail]
color_aliases = { "unseen_fg" = "theme_default", "unseen_bg" = "theme_default", "sea" = "#91C7FF", "dimmed_text" = "#afbec5", "dimmed_bg" = "Grey78", "header" = "#edeff1" }
"theme_default" = { fg = "#37474f", bg = "White", attrs = "Default" }
"mail.listing.attachment_flag" = { fg = "Blue", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.even" = { fg = "$dimmed_text", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.even_highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }
"mail.listing.compact.even_selected" = { fg = "$dimmed_text", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.even_unseen" = { fg = "$unseen_fg", bg = "$unseen_bg", attrs = "theme_default" }
"mail.listing.compact.odd" = { fg = "$dimmed_text", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.odd_highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }
"mail.listing.compact.odd_selected" = { fg = "$dimmed_text", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.odd_unseen" = { fg = "$unseen_fg", bg = "$unseen_bg", attrs = "theme_default" }
"mail.listing.plain.even" = { fg = "mail.listing.compact.even", bg = "mail.listing.compact.even", attrs = "theme_default" }
"mail.listing.plain.even_highlighted" = { fg = "mail.listing.compact.even_highlighted", bg = "mail.listing.compact.even_highlighted", attrs = "theme_default" }
"mail.listing.plain.even_selected" = { fg = "mail.listing.compact.even_selected", bg = "mail.listing.compact.even_selected", attrs = "theme_default" }
"mail.listing.plain.even_unseen" = { fg = "mail.listing.compact.even_unseen", bg = "mail.listing.compact.even_unseen", attrs = "theme_default" }
"mail.listing.plain.odd" = { fg = "mail.listing.compact.odd", bg = "mail.listing.compact.odd", attrs = "theme_default" }
"mail.listing.plain.odd_highlighted" = { fg = "mail.listing.compact.odd_highlighted", bg = "mail.listing.compact.odd_highlighted", attrs = "theme_default" }
"mail.listing.plain.odd_selected" = { fg = "mail.listing.compact.odd_selected", bg = "mail.listing.compact.odd_selected", attrs = "theme_default" }
"mail.listing.plain.odd_unseen" = { fg = "mail.listing.compact.odd_unseen", bg = "mail.listing.compact.odd_unseen", attrs = "theme_default" }
"mail.listing.conversations" = { fg = "$dimmed_text", bg = "theme_default", attrs = "Default" }
"mail.listing.conversations.date" = { fg = "mail.listing.conversations", bg = "mail.listing.conversations", attrs = "theme_default" }
"mail.listing.conversations.from" = { fg = "mail.listing.conversations", bg = "mail.listing.conversations", attrs = "theme_default" }
"mail.listing.conversations.subject" = { fg = "mail.listing.conversations", bg = "mail.listing.conversations", attrs = "theme_default" }
"mail.listing.conversations.highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }
"mail.listing.conversations.selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.unseen" = { fg = "$unseen_fg", bg = "$unseen_bg", attrs = "theme_default" }
"mail.listing.tag_default" = { fg = "Black", bg = "$dimmed_text", attrs = "theme_default" }
"mail.listing.thread_snooze_flag" = { fg = "Red", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar" = { fg = "$dimmed_text", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }
"mail.sidebar_highlighted_account" = { fg = "$dimmed_text", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted_account_name" = { fg = "theme_default", bg = "$header", attrs = "Bold" }
"mail.sidebar_account_name" = { fg = "mail.sidebar", bg = "mail.sidebar", attrs = "theme_default" }
"mail.sidebar_highlighted_account_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_unread_count" = { fg = "mail.sidebar_unread_count", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_highlighted_unread_count" = { fg = "mail.sidebar_highlighted", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_index" = { fg = "mail.sidebar", bg = "mail.sidebar", attrs = "theme_default" }
"mail.sidebar_unread_count" = { fg = "$dimmed_text", bg = "theme_default", attrs = "theme_default" }
"mail.view.body" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers_names" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"mail.view.thread.indentation.a" = { fg = "theme_default", bg = "#EC633D", attrs = "theme_default" }
"mail.view.thread.indentation.b" = { fg = "theme_default", bg = "#D347F9", attrs = "theme_default" }
"mail.view.thread.indentation.c" = { fg = "theme_default", bg = "#317EFB", attrs = "theme_default" }
"mail.view.thread.indentation.d" = { fg = "theme_default", bg = "#06B8CD", attrs = "theme_default" }
"mail.view.thread.indentation.e" = { fg = "theme_default", bg = "#93DDB6", attrs = "theme_default" }
"mail.view.thread.indentation.f" = { fg = "theme_default", bg = "#68A033", attrs = "theme_default" }
"pager.highlight_search" = { fg = "White", bg = "Teal", attrs = "Bold" }
"pager.highlight_search_current" = { fg = "White", bg = "NavyBlue", attrs = "Bold" }
"status.bar" = { fg = "theme_default", bg = "$sea", attrs = "theme_default" }
"status.notification" = { fg = "Plum1", bg = "theme_default", attrs = "theme_default" }
"tab.bar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"tab.focused" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"tab.unfocused" = { fg = "White", bg = "$sea", attrs = "Bold" }
"widgets.form.field" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"widgets.form.highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }
"widgets.form.label" = { fg = "theme_default", bg = "theme_default", attrs = "Bold" }
"widgets.list.header" = { fg = "Black", bg = "White", attrs = "Bold" }
"widgets.options.highlighted" = { fg = "theme_default", bg = "$dimmed_bg", attrs = "theme_default" }

View File

@ -0,0 +1,42 @@
[terminal.themes.spooky]
"theme_default" = { fg = "#333", bg = "#fe9b13", attrs = "Default" }
"mail.listing.attachment_flag" = { fg = "LightSlateGrey", bg = "theme_default", attrs = "theme_default" }
"mail.listing.compact.even" = { fg = "theme_default", bg = "#bf200e", attrs = "theme_default" }
"mail.listing.compact.odd" = { fg = "theme_default", bg = "#fa4113", attrs = "theme_default" }
"mail.listing.compact.even_highlighted" = { fg = "theme_default", bg = "Yellow6", attrs = "theme_default" }
"mail.listing.compact.odd_highlighted" = { fg = "theme_default", bg = "Yellow6", attrs = "theme_default" }
"mail.listing.compact.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.even_unseen" = { fg = "Black", bg = "Orange3", attrs = "theme_default" }
"mail.listing.compact.odd_unseen" = { fg = "Black", bg = "Orange3", attrs = "theme_default" }
"mail.listing.conversations.date" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.from" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.highlighted" = { fg = "theme_default", bg = "Grey58", attrs = "theme_default" }
"mail.listing.conversations.selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.subject" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.unseen" = { fg = "Black", bg = "Grey78", attrs = "theme_default" }
"mail.listing.plain.even" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.odd" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.even_unseen" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.odd_unseen" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.even_highlighted" = { fg = "theme_default", bg = "Yellow6", attrs = "theme_default" }
"mail.listing.plain.odd_highlighted" = { fg = "theme_default", bg = "Yellow6", attrs = "theme_default" }
"mail.listing.plain.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.plain.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.thread_snooze_flag" = { fg = "Red", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted_account" = { fg = "White", bg = "Orange3", attrs = "theme_default" }
"mail.sidebar_highlighted_account_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_unread_count" = { fg = "mail.sidebar_unread_count", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted" = { fg = "Grey7", bg = "White", attrs = "theme_default" }
"mail.sidebar_highlighted_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_highlighted_unread_count" = { fg = "mail.sidebar_highlighted", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_index" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_unread_count" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.view.body" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers" = { fg = "DodgerBlue1", bg = "theme_default", attrs = "theme_default" }
"status.bar" = { fg = "White", bg = "#A21500", attrs = "theme_default" }
"tab.bar" = { fg = "theme_default", bg = "#332300", attrs = "theme_default" }
"tab.unfocused" = { fg = "theme_default", bg = "#A26F00", attrs = "theme_default" }
"tab.focused" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }

View File

@ -0,0 +1,46 @@
[terminal.themes.watermelon]
color_aliases = { "JewelGreen" = "#157241", "PinkLace" = "#FFD5FD", "TorchRed" = "#F50431", "ChelseaCucumber" = "#6CA94A", "ScreaminGreen" = "#8FFF52", "SunsetOrange" = "#f74b41", "Melon" = "#fdbcb4", "BlueStone" = "#005F5F", "HotPink" = "#FF74D7" }
"theme_default" = { fg = "$TorchRed", bg = "$PinkLace", attrs = "Default" }
"widgets.list.header" = { fg = "$PinkLace", bg = "$TorchRed", attrs = "Bold" }
"mail.listing.attachment_flag" = { fg = "LightSlateGrey", bg = "theme_default", attrs = "theme_default" }
"mail.listing.tag_default" = { bg = "$Melon", attrs = "Bold" }
"mail.listing.compact.even" = { fg = "White", bg = "$ChelseaCucumber", attrs = "Bold" }
"mail.listing.compact.odd" = { fg = "$PinkLace", bg = "$JewelGreen", attrs = "theme_default" }
"mail.listing.compact.even_unseen" = { fg = "$JewelGreen", bg = "$ScreaminGreen", attrs = "theme_default" }
"mail.listing.compact.odd_unseen" = { fg = "$JewelGreen", bg = "$ScreaminGreen", attrs = "theme_default" }
"mail.listing.compact.even_highlighted" = { fg = "$JewelGreen", bg = "$SunsetOrange", attrs = "theme_default" }
"mail.listing.compact.odd_highlighted" = { fg = "$JewelGreen", bg = "$SunsetOrange", attrs = "theme_default" }
"mail.listing.compact.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.compact.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.date" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.from" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.highlighted" = { fg = "$JewelGreen", bg = "$SunsetOrange", attrs = "theme_default" }
"mail.listing.conversations.selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.conversations.subject" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.conversations.unseen" = { fg = "Black", bg = "mail.listing.compact.even_unseen", attrs = "theme_default" }
"mail.listing.plain.even" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.odd" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.listing.plain.even_unseen" = { fg = "theme_default", bg = "mail.listing.compact.even_unseen", attrs = "theme_default" }
"mail.listing.plain.odd_unseen" = { fg = "theme_default", bg = "mail.listing.compact.odd_unseen", attrs = "theme_default" }
"mail.listing.plain.even_highlighted" = { fg = "$JewelGreen", bg = "$SunsetOrange", attrs = "theme_default" }
"mail.listing.plain.odd_highlighted" = { fg = "$JewelGreen", bg = "$SunsetOrange", attrs = "theme_default" }
"mail.listing.plain.even_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.plain.odd_selected" = { fg = "theme_default", bg = "LightCoral", attrs = "theme_default" }
"mail.listing.thread_snooze_flag" = { fg = "Red", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_highlighted_account" = { fg = "White", bg = "$TorchRed", attrs = "theme_default" }
"mail.sidebar_highlighted_account_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted_account_unread_count" = { fg = "mail.sidebar_unread_count", bg = "mail.sidebar_highlighted_account", attrs = "theme_default" }
"mail.sidebar_highlighted" = { fg = "Grey7", bg = "White", attrs = "theme_default" }
"mail.sidebar_highlighted_index" = { fg = "mail.sidebar_index", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_highlighted_unread_count" = { fg = "mail.sidebar_highlighted", bg = "mail.sidebar_highlighted", attrs = "theme_default" }
"mail.sidebar_index" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.sidebar_unread_count" = { fg = "Grey46", bg = "theme_default", attrs = "theme_default" }
"mail.view.body" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"mail.view.headers" = { fg = "DodgerBlue1", bg = "theme_default", attrs = "theme_default" }
"status.bar" = { fg = "$PinkLace", bg = "$TorchRed", attrs = "theme_default" }
"status.notification" = { fg = "theme_default", bg = "theme_default", attrs = "Default" }
"tab.bar" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }
"tab.unfocused" = { fg = "$PinkLace", bg = "$HotPink", attrs = "theme_default" }
"tab.focused" = { fg = "theme_default", bg = "theme_default", attrs = "theme_default" }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

2179
meli/src/accounts.rs 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
/*
* meli - accounts module.
*
* Copyright 2023 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Account mail backend operations.
use super::*;
impl Account {
pub fn set_flags(
&mut self,
env_hashes: EnvelopeHashBatch,
mailbox_hash: MailboxHash,
flags: SmallVec<[FlagOp; 8]>,
) -> Result<JobId> {
let fut = self.backend.write().unwrap().set_flags(
env_hashes.clone(),
mailbox_hash,
flags.clone(),
)?;
let handle = self
.main_loop_handler
.job_executor
.spawn_specialized("set_flags".into(), fut);
let job_id = handle.job_id;
self.insert_job(
job_id,
JobRequest::SetFlags {
env_hashes,
mailbox_hash,
flags,
handle,
},
);
Ok(job_id)
}
#[cfg(not(feature = "sqlite3"))]
pub(super) fn update_cached_env(&mut self, _: Envelope, _: Option<EnvelopeHash>) {}
#[cfg(feature = "sqlite3")]
pub(super) fn update_cached_env(&mut self, env: Envelope, old_hash: Option<EnvelopeHash>) {
if self.settings.conf.search_backend == crate::conf::SearchBackend::Sqlite3 {
let msg_id = env.message_id_display().to_string();
let name = self.name.clone();
let backend = self.backend.clone();
let fut = async move {
crate::sqlite3::AccountCache::remove(
name.clone(),
old_hash.unwrap_or_else(|| env.hash()),
)
.await?;
crate::sqlite3::AccountCache::insert(env, backend, name).await?;
Ok(())
};
let handle = self
.main_loop_handler
.job_executor
.spawn_specialized("sqlite3::remove".into(), fut);
self.insert_job(
handle.job_id,
JobRequest::Generic {
name: format!("Update envelope {} in sqlite3 cache", msg_id).into(),
handle,
log_level: LogLevel::TRACE,
on_finish: None,
},
);
}
}
}

View File

@ -0,0 +1,222 @@
//
// meli - accounts module.
//
// Copyright 2017 Emmanouil Pitsidianakis <manos@pitsidianak.is>
//
// This file is part of meli.
//
// meli is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later
use std::{borrow::Cow, collections::HashMap, pin::Pin};
use futures::stream::Stream;
use melib::{backends::*, email::*, error::Result, LogLevel};
use smallvec::SmallVec;
use crate::{is_variant, jobs::JoinHandle};
pub enum JobRequest {
Mailboxes {
handle: JoinHandle<Result<HashMap<MailboxHash, Mailbox>>>,
},
Fetch {
mailbox_hash: MailboxHash,
#[allow(clippy::type_complexity)]
handle: JoinHandle<(
Option<Result<Vec<Envelope>>>,
Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>,
)>,
},
Generic {
name: Cow<'static, str>,
log_level: LogLevel,
handle: JoinHandle<Result<()>>,
on_finish: Option<crate::types::CallbackFn>,
},
IsOnline {
handle: JoinHandle<Result<()>>,
},
Refresh {
mailbox_hash: MailboxHash,
handle: JoinHandle<Result<()>>,
},
SetFlags {
env_hashes: EnvelopeHashBatch,
mailbox_hash: MailboxHash,
flags: SmallVec<[FlagOp; 8]>,
handle: JoinHandle<Result<()>>,
},
SaveMessage {
bytes: Vec<u8>,
mailbox_hash: MailboxHash,
handle: JoinHandle<Result<()>>,
},
SendMessage,
SendMessageBackground {
handle: JoinHandle<Result<()>>,
},
DeleteMessages {
env_hashes: EnvelopeHashBatch,
handle: JoinHandle<Result<()>>,
},
CreateMailbox {
path: String,
handle: JoinHandle<Result<(MailboxHash, HashMap<MailboxHash, Mailbox>)>>,
},
DeleteMailbox {
mailbox_hash: MailboxHash,
handle: JoinHandle<Result<HashMap<MailboxHash, Mailbox>>>,
},
//RenameMailbox,
SetMailboxPermissions {
mailbox_hash: MailboxHash,
handle: JoinHandle<Result<()>>,
},
SetMailboxSubscription {
mailbox_hash: MailboxHash,
new_value: bool,
handle: JoinHandle<Result<()>>,
},
Watch {
handle: JoinHandle<Result<()>>,
},
}
impl Drop for JobRequest {
fn drop(&mut self) {
match self {
Self::Generic { handle, .. } |
Self::IsOnline { handle, .. } |
Self::Refresh { handle, .. } |
Self::SetFlags { handle, .. } |
Self::SaveMessage { handle, .. } |
//JobRequest::RenameMailbox,
Self::SetMailboxPermissions { handle, .. } |
Self::SetMailboxSubscription { handle, .. } |
Self::Watch { handle, .. } |
Self::SendMessageBackground { handle, .. } => {
handle.cancel();
}
Self::DeleteMessages { handle, .. } => {
handle.cancel();
}
Self::CreateMailbox { handle, .. } => {
handle.cancel();
}
Self::DeleteMailbox { handle, .. } => {
handle.cancel();
}
Self::Fetch { handle, .. } => {
handle.cancel();
}
Self::Mailboxes { handle, .. } => {
handle.cancel();
}
Self::SendMessage => {}
}
}
}
impl std::fmt::Debug for JobRequest {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Generic { name, .. } => write!(f, "JobRequest::Generic({})", name),
Self::Mailboxes { .. } => write!(f, "JobRequest::Mailboxes"),
Self::Fetch { mailbox_hash, .. } => {
write!(f, "JobRequest::Fetch({})", mailbox_hash)
}
Self::IsOnline { .. } => write!(f, "JobRequest::IsOnline"),
Self::Refresh { .. } => write!(f, "JobRequest::Refresh"),
Self::SetFlags {
env_hashes,
mailbox_hash,
flags,
..
} => f
.debug_struct(stringify!(JobRequest::SetFlags))
.field("env_hashes", &env_hashes)
.field("mailbox_hash", &mailbox_hash)
.field("flags", &flags)
.finish(),
Self::SaveMessage { .. } => write!(f, "JobRequest::SaveMessage"),
Self::DeleteMessages { .. } => write!(f, "JobRequest::DeleteMessages"),
Self::CreateMailbox { .. } => write!(f, "JobRequest::CreateMailbox"),
Self::DeleteMailbox { mailbox_hash, .. } => {
write!(f, "JobRequest::DeleteMailbox({})", mailbox_hash)
}
//JobRequest::RenameMailbox,
Self::SetMailboxPermissions { .. } => {
write!(f, "JobRequest::SetMailboxPermissions")
}
Self::SetMailboxSubscription { .. } => {
write!(f, "JobRequest::SetMailboxSubscription")
}
Self::Watch { .. } => write!(f, "JobRequest::Watch"),
Self::SendMessage => write!(f, "JobRequest::SendMessage"),
Self::SendMessageBackground { .. } => {
write!(f, "JobRequest::SendMessageBackground")
}
}
}
}
impl std::fmt::Display for JobRequest {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Generic { name, .. } => write!(f, "{}", name),
Self::Mailboxes { .. } => write!(f, "Get mailbox list"),
Self::Fetch { .. } => write!(f, "Mailbox fetch"),
Self::IsOnline { .. } => write!(f, "Online status check"),
Self::Refresh { .. } => write!(f, "Refresh mailbox"),
Self::SetFlags {
env_hashes, flags, ..
} => write!(
f,
"Set flags for {} message{}: {:?}",
env_hashes.len(),
if env_hashes.len() == 1 { "" } else { "s" },
flags
),
Self::SaveMessage { .. } => write!(f, "Save message"),
Self::DeleteMessages { env_hashes, .. } => write!(
f,
"Delete {} message{}",
env_hashes.len(),
if env_hashes.len() == 1 { "" } else { "s" }
),
Self::CreateMailbox { path, .. } => write!(f, "Create mailbox {}", path),
Self::DeleteMailbox { .. } => write!(f, "Delete mailbox"),
//JobRequest::RenameMailbox,
Self::SetMailboxPermissions { .. } => write!(f, "Set mailbox permissions"),
Self::SetMailboxSubscription { .. } => write!(f, "Set mailbox subscription"),
Self::Watch { .. } => write!(f, "Background watch"),
Self::SendMessageBackground { .. } | Self::SendMessage => {
write!(f, "Sending message")
}
}
}
}
impl JobRequest {
is_variant! { is_watch, Watch { .. } }
is_variant! { is_online, IsOnline { .. } }
pub fn is_fetch(&self, mailbox_hash: MailboxHash) -> bool {
matches!(self, Self::Fetch {
mailbox_hash: h, ..
} if *h == mailbox_hash)
}
}

View File

@ -0,0 +1,347 @@
//
// meli - accounts module.
//
// Copyright 2017 Emmanouil Pitsidianakis <manos@pitsidianak.is>
//
// This file is part of meli.
//
// meli is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later
use indexmap::IndexMap;
use melib::{
backends::{Mailbox, MailboxHash},
error::Error,
log,
};
use smallvec::SmallVec;
use crate::{conf::FileMailboxConf, is_variant};
#[derive(Clone, Debug, Default)]
pub enum MailboxStatus {
Available,
Failed(Error),
/// first argument is done work, and second is total work
Parsing(usize, usize),
#[default]
None,
}
impl MailboxStatus {
is_variant! { is_available, Available }
is_variant! { is_parsing, Parsing(_, _) }
}
#[derive(Clone, Debug)]
pub struct MailboxEntry {
pub status: MailboxStatus,
pub name: String,
pub path: String,
pub ref_mailbox: Mailbox,
pub conf: FileMailboxConf,
}
impl MailboxEntry {
pub fn new(
status: MailboxStatus,
name: String,
ref_mailbox: Mailbox,
conf: FileMailboxConf,
) -> Self {
let mut ret = Self {
status,
name,
path: ref_mailbox.path().into(),
ref_mailbox,
conf,
};
match ret.conf.mailbox_conf.extra.get("encoding") {
None => {}
Some(v) if ["utf-8", "utf8"].iter().any(|e| v.eq_ignore_ascii_case(e)) => {}
Some(v) if ["utf-7", "utf7"].iter().any(|e| v.eq_ignore_ascii_case(e)) => {
ret.name = melib::backends::utf7::decode_utf7_imap(&ret.name);
ret.path = melib::backends::utf7::decode_utf7_imap(&ret.path);
}
Some(other) => {
log::warn!(
"mailbox `{}`: unrecognized mailbox name charset: {}",
&ret.name,
other
);
}
}
ret
}
pub fn status(&self) -> String {
match self.status {
MailboxStatus::Available => format!(
"{} [{} messages]",
self.name(),
self.ref_mailbox.count().ok().unwrap_or((0, 0)).1
),
MailboxStatus::Failed(ref e) => e.to_string(),
MailboxStatus::None => "Retrieving mailbox.".to_string(),
MailboxStatus::Parsing(done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
}
}
pub fn name(&self) -> &str {
if let Some(name) = self.conf.mailbox_conf.alias.as_ref() {
name
} else {
self.ref_mailbox.name()
}
}
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct MailboxNode {
pub hash: MailboxHash,
pub depth: usize,
pub indentation: u32,
pub has_sibling: bool,
pub children: Vec<MailboxNode>,
}
pub fn build_mailboxes_order(
tree: &mut Vec<MailboxNode>,
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
mailboxes_order: &mut Vec<MailboxHash>,
) {
tree.clear();
mailboxes_order.clear();
for (h, f) in mailbox_entries.iter() {
if f.ref_mailbox.parent().is_none() {
fn rec(
h: MailboxHash,
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
depth: usize,
) -> MailboxNode {
let mut node = MailboxNode {
hash: h,
children: Vec::new(),
depth,
indentation: 0,
has_sibling: false,
};
for &c in mailbox_entries[&h].ref_mailbox.children() {
if mailbox_entries.contains_key(&c) {
node.children.push(rec(c, mailbox_entries, depth + 1));
}
}
node
}
tree.push(rec(*h, mailbox_entries, 0));
}
}
macro_rules! mailbox_eq_key {
($mailbox:expr) => {{
if let Some(sort_order) = $mailbox.conf.mailbox_conf.sort_order {
(0, sort_order, $mailbox.ref_mailbox.path())
} else {
(1, 0, $mailbox.ref_mailbox.path())
}
}};
}
tree.sort_unstable_by(|a, b| {
if mailbox_entries[&b.hash]
.conf
.mailbox_conf
.sort_order
.is_none()
&& mailbox_entries[&b.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if mailbox_entries[&a.hash]
.conf
.mailbox_conf
.sort_order
.is_none()
&& mailbox_entries[&a.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
mailbox_eq_key!(mailbox_entries[&a.hash])
.cmp(&mailbox_eq_key!(mailbox_entries[&b.hash]))
}
});
let mut stack: SmallVec<[Option<&MailboxNode>; 16]> = SmallVec::new();
for n in tree.iter_mut() {
mailboxes_order.push(n.hash);
n.children.sort_unstable_by(|a, b| {
if mailbox_entries[&b.hash]
.conf
.mailbox_conf
.sort_order
.is_none()
&& mailbox_entries[&b.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Greater
} else if mailbox_entries[&a.hash]
.conf
.mailbox_conf
.sort_order
.is_none()
&& mailbox_entries[&a.hash]
.ref_mailbox
.path()
.eq_ignore_ascii_case("INBOX")
{
std::cmp::Ordering::Less
} else {
mailbox_eq_key!(mailbox_entries[&a.hash])
.cmp(&mailbox_eq_key!(mailbox_entries[&b.hash]))
}
});
stack.extend(n.children.iter().rev().map(Some));
while let Some(Some(next)) = stack.pop() {
mailboxes_order.push(next.hash);
stack.extend(next.children.iter().rev().map(Some));
}
}
drop(stack);
for node in tree.iter_mut() {
fn rec(
node: &mut MailboxNode,
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
mut indentation: u32,
has_sibling: bool,
) {
node.indentation = indentation;
node.has_sibling = has_sibling;
let mut iter = (0..node.children.len())
.filter(|i| {
mailbox_entries[&node.children[*i].hash]
.ref_mailbox
.is_subscribed()
})
.collect::<SmallVec<[_; 8]>>()
.into_iter()
.peekable();
indentation <<= 1;
if has_sibling {
indentation |= 1;
}
while let Some(i) = iter.next() {
let c = &mut node.children[i];
rec(c, mailbox_entries, indentation, iter.peek().is_some());
}
}
rec(node, mailbox_entries, 0, false);
}
}
#[cfg(test)]
mod tests {
use melib::{
backends::{Mailbox, MailboxHash},
error::Result,
MailboxPermissions, SpecialUsageMailbox,
};
use crate::accounts::{FileMailboxConf, MailboxEntry, MailboxStatus};
#[test]
fn test_mailbox_utf7() {
#[derive(Debug)]
struct TestMailbox(String);
impl melib::BackendMailbox for TestMailbox {
fn hash(&self) -> MailboxHash {
unimplemented!()
}
fn name(&self) -> &str {
&self.0
}
fn path(&self) -> &str {
&self.0
}
fn children(&self) -> &[MailboxHash] {
unimplemented!()
}
fn clone(&self) -> Mailbox {
unimplemented!()
}
fn special_usage(&self) -> SpecialUsageMailbox {
unimplemented!()
}
fn parent(&self) -> Option<MailboxHash> {
unimplemented!()
}
fn permissions(&self) -> MailboxPermissions {
unimplemented!()
}
fn is_subscribed(&self) -> bool {
unimplemented!()
}
fn set_is_subscribed(&mut self, _: bool) -> Result<()> {
unimplemented!()
}
fn set_special_usage(&mut self, _: SpecialUsageMailbox) -> Result<()> {
unimplemented!()
}
fn count(&self) -> Result<(usize, usize)> {
unimplemented!()
}
}
for (n, d) in [
("~peter/mail/&U,BTFw-/&ZeVnLIqe-", "~peter/mail/台北/日本語"),
("&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-", "Отправленные"),
] {
let ref_mbox = TestMailbox(n.to_string());
let mut conf: melib::MailboxConf = Default::default();
conf.extra.insert("encoding".to_string(), "utf7".into());
let entry = MailboxEntry::new(
MailboxStatus::None,
n.to_string(),
Box::new(ref_mbox),
FileMailboxConf {
mailbox_conf: conf,
..Default::default()
},
);
assert_eq!(&entry.path, d);
}
}
}

229
meli/src/args.rs 100644
View File

@ -0,0 +1,229 @@
/*
* meli - args.rs
*
* Copyright 2017-2023 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Command line arguments.
use super::*;
#[derive(Debug, StructOpt)]
#[structopt(name = "meli", about = "terminal mail client", version_short = "v")]
pub struct Opt {
/// use specified configuration file
#[structopt(short, long, parse(from_os_str))]
pub config: Option<PathBuf>,
#[structopt(subcommand)]
pub subcommand: Option<SubCommand>,
}
#[derive(Debug, StructOpt)]
pub enum SubCommand {
/// print default theme in full to stdout and exit.
PrintDefaultTheme,
/// print loaded themes in full to stdout and exit.
PrintLoadedThemes,
/// print all directories that meli creates/uses.
PrintAppDirectories,
/// print location of configuration file that will be loaded on normal app
/// startup.
PrintConfigPath,
/// edit configuration files with `$EDITOR`/`$VISUAL`.
EditConfig,
/// create a sample configuration file with available configuration options.
/// If PATH is not specified, meli will try to create it in
/// $XDG_CONFIG_HOME/meli/config.toml
#[structopt(display_order = 1)]
CreateConfig {
#[structopt(value_name = "NEW_CONFIG_PATH", parse(from_os_str))]
path: Option<PathBuf>,
},
/// test a configuration file for syntax issues or missing options.
#[structopt(display_order = 2)]
TestConfig {
#[structopt(value_name = "CONFIG_PATH", parse(from_os_str))]
path: Option<PathBuf>,
},
#[structopt(visible_alias="docs", aliases=&["docs", "manpage", "manpages"])]
#[structopt(display_order = 3)]
/// print documentation page and exit (Piping to a pager is recommended.).
Man(ManOpt),
#[structopt(display_order = 4)]
/// Install manual pages to the first location provided by $MANPATH /
/// manpath(1), unless you specify the directory as an argument.
InstallMan {
#[structopt(value_name = "DESTINATION_PATH", parse(from_os_str))]
destination_path: Option<PathBuf>,
},
#[structopt(display_order = 5)]
/// Print compile time feature flags of this binary
CompiledWith,
/// Print log file location.
PrintLogPath,
/// View mail from input file.
View {
#[structopt(value_name = "INPUT", parse(from_os_str))]
path: PathBuf,
},
}
#[derive(Debug, StructOpt)]
pub struct ManOpt {
#[cfg(feature = "cli-docs")]
#[cfg_attr(feature = "cli-docs", structopt(default_value = "meli", possible_values=manpages::POSSIBLE_VALUES, value_name="PAGE", parse(try_from_str = manpages::parse_manpage)))]
/// Name of manual page.
pub page: manpages::ManPages,
/// If true, output text in stdout instead of spawning $PAGER.
#[cfg(feature = "cli-docs")]
#[cfg_attr(
feature = "cli-docs",
structopt(long = "no-raw", alias = "no-raw", value_name = "bool")
)]
pub no_raw: Option<Option<bool>>,
}
#[cfg(feature = "cli-docs")]
pub mod manpages {
use std::{
env, fs,
path::{Path, PathBuf},
sync::Arc,
};
use melib::log;
use crate::{Error, Result};
pub const POSSIBLE_VALUES: &[&str] = &[
"meli",
"meli.1",
"conf",
"meli.conf",
"meli.conf.5",
"themes",
"meli-themes",
"meli-themes.5",
"guide",
"meli.7",
];
pub fn parse_manpage(src: &str) -> Result<ManPages> {
match src {
"" | "meli" | "meli.1" | "main" => Ok(ManPages::Main),
"meli.7" | "guide" => Ok(ManPages::Guide),
"meli.conf" | "meli.conf.5" | "conf" | "config" | "configuration" => Ok(ManPages::Conf),
"meli-themes" | "meli-themes.5" | "themes" | "theming" | "theme" => {
Ok(ManPages::Themes)
}
_ => Err(Error::new(format!("Invalid documentation page: {src}",))),
}
}
#[derive(Clone, Copy, Debug)]
/// Choose manpage
pub enum ManPages {
/// meli(1)
Main = 0,
/// meli.conf(5)
Conf = 1,
/// meli-themes(5)
Themes = 2,
/// meli(7)
Guide = 3,
}
impl std::fmt::Display for ManPages {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
fmt,
"{}",
match self {
Self::Main => "meli.1",
Self::Conf => "meli.conf.5",
Self::Themes => "meli-themes.5",
Self::Guide => "meli.7",
}
)
}
}
impl ManPages {
pub fn install(destination: Option<PathBuf>) -> Result<PathBuf> {
fn path_valid(p: &Path, tries: &mut Vec<PathBuf>) -> bool {
tries.push(p.into());
p.exists()
&& p.is_dir()
&& fs::metadata(p)
.ok()
.map(|m| !m.permissions().readonly())
.unwrap_or(false)
}
let mut tries = vec![];
let Some(mut path) = destination
.filter(|p| path_valid(p, &mut tries))
.or_else(|| {
if let Some(paths) = env::var_os("MANPATH") {
if let Some(path) =
env::split_paths(&paths).find(|p| path_valid(p, &mut tries))
{
return Some(path);
}
}
None
})
.or_else(|| {
#[allow(deprecated)]
env::home_dir()
.map(|p| p.join(".local").join("share").join("man"))
.filter(|p| path_valid(p, &mut tries))
})
else {
return Err(format!("Could not write to any of these paths: {:?}", tries).into());
};
for (p, dir) in [
(Self::Main, "man1"),
(Self::Conf, "man5"),
(Self::Themes, "man5"),
(Self::Guide, "man7"),
] {
let text = crate::subcommands::man(p, true)?;
path.push(dir);
std::fs::create_dir_all(&path).map_err(|err| {
Error::new(format!("Could not create {} directory.", path.display()))
.set_source(Some(Arc::new(err)))
})?;
path.push(&p.to_string());
fs::write(&path, text.as_bytes()).map_err(|err| {
Error::new(format!("Could not write to {}", path.display()))
.set_source(Some(Arc::new(err)))
})?;
log::trace!("Installed {} to {}", p, path.display());
path.pop();
path.pop();
}
Ok(path)
}
}
}

688
meli/src/command.rs 100644
View File

@ -0,0 +1,688 @@
/*
* meli
*
* Copyright 2017-2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! A parser module for user commands passed through
//! [`Command`](crate::types::UIMode::Command) mode.
use std::{borrow::Cow, collections::HashSet, str::FromStr};
use melib::{
nom::{
self,
branch::alt,
bytes::complete::{is_a, is_not, tag, take_until},
character::complete::{digit1, not_line_ending},
combinator::{map, map_res},
error::Error as NomError,
multi::separated_list1,
sequence::{pair, preceded, separated_pair},
IResult,
},
parser::BytesExt,
SortField, SortOrder,
};
pub mod actions;
#[macro_use]
pub mod error;
#[macro_use]
pub mod argcheck;
pub mod history;
pub mod parser;
use actions::MailboxOperation;
use error::CommandError;
pub use parser::parse_command;
pub use crate::actions::{
AccountAction::{self, *},
Action::{self, *},
ComposeAction::{self, *},
FlagAction,
ListingAction::{self, *},
MailingListAction::{self, *},
TabAction::{self, *},
TagAction,
ViewAction::{self, *},
};
/// Helper macro to convert an array of tokens into a `TokenStream`
macro_rules! to_stream {
($token: expr) => {
TokenStream {
tokens: &[$token],
}
};
($($tokens:expr),*) => {
TokenStream {
tokens: &[$($tokens),*],
}
};
}
/// Macro to create a const table with every command part that can be
/// auto-completed and its description
macro_rules! define_commands {
( [$({ tags: [$( $tags:literal),*], desc: $desc:literal, tokens: $tokens:expr, parser: $parser:path}),*]) => {
pub const COMMAND_COMPLETION: &[(&str, &str, TokenStream, fn(&[u8]) -> IResult<&[u8], Result<Action, CommandError>>)] = &[$($( ($tags, $desc, TokenStream { tokens: $tokens }, $parser) ),*),* ];
};
}
pub fn quoted_argument(input: &[u8]) -> IResult<&[u8], &str> {
if input.is_empty() {
return Err(nom::Err::Error(NomError {
input,
code: nom::error::ErrorKind::Tag,
}));
}
if input[0] == b'"' {
let mut i = 1;
while i < input.len() {
if input[i] == b'\"' && input[i - 1] != b'\\' {
return Ok((&input[i + 1..], unsafe {
std::str::from_utf8_unchecked(&input[1..i])
}));
}
i += 1;
}
Err(nom::Err::Error(NomError {
input,
code: nom::error::ErrorKind::Tag,
}))
} else {
map_res(is_not(" "), std::str::from_utf8)(input)
}
}
#[derive(Clone, Copy, Debug)]
pub struct TokenStream {
tokens: &'static [TokenAdicity],
}
use Token::*;
use TokenAdicity::*;
impl TokenStream {
fn matches<'s>(&self, s: &mut &'s str, sugg: &mut HashSet<String>) -> Vec<(&'s str, Token)> {
let mut tokens = vec![];
for t in self.tokens.iter() {
let mut ptr = 0;
while ptr + 1 < s.len() && s.as_bytes()[ptr].is_ascii_whitespace() {
ptr += 1;
}
*s = &s[ptr..];
//println!("\t before s.is_empty() {:?} {:?}", t, s);
if s.is_empty() || *s == " " {
match t.inner() {
Literal(lit) => {
sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, lit));
}
Alternatives(v) => {
for t in v.iter() {
//println!("adding empty suggestions for {:?}", t);
let mut _s = *s;
let mut m = t.matches(&mut _s, sugg);
tokens.append(&mut m);
}
}
Seq(_s) => {}
RestOfStringValue => {
sugg.insert(String::new());
}
t @ AttachmentIndexValue
| t @ MailboxIndexValue
| t @ IndexValue
| t @ Filepath
| t @ AccountName
| t @ MailboxPath
| t @ QuotedStringValue
| t @ AlphanumericStringValue => {
let _t = t;
//sugg.insert(format!("{}{:?}", if s.is_empty() { " " }
// else { "" }, t));
}
}
tokens.push((*s, *t.inner()));
return tokens;
}
match t.inner() {
Literal(lit) => {
if lit.starts_with(*s) && lit.len() != s.len() {
sugg.insert(lit[s.len()..].to_string());
tokens.push((s, *t.inner()));
return tokens;
} else if s.starts_with(lit) {
tokens.push((&s[..lit.len()], *t.inner()));
*s = &s[lit.len()..];
} else {
return vec![];
}
}
Alternatives(v) => {
let mut cont = true;
for t in v.iter() {
let mut _s = *s;
let mut m = t.matches(&mut _s, sugg);
if !m.is_empty() {
tokens.append(&mut m);
//println!("_s is empty {}", _s.is_empty());
cont = !_s.is_empty();
*s = _s;
break;
}
}
if tokens.is_empty() {
return tokens;
}
if !cont {
*s = "";
}
}
Seq(_s) => {
return vec![];
}
RestOfStringValue => {
tokens.push((*s, *t.inner()));
return tokens;
}
AttachmentIndexValue
| MailboxIndexValue
| IndexValue
| Filepath
| AccountName
| MailboxPath
| QuotedStringValue
| AlphanumericStringValue => {
let mut ptr = 0;
while ptr + 1 < s.len() && !s.as_bytes()[ptr].is_ascii_whitespace() {
ptr += 1;
}
tokens.push((&s[..ptr + 1], *t.inner()));
*s = &s[ptr + 1..];
}
}
}
tokens
}
}
/// `Token` wrapper that defines how many times a token is expected to be
/// repeated
#[derive(Clone, Copy, Debug)]
pub enum TokenAdicity {
ZeroOrOne(Token),
ZeroOrMore(Token),
One(Token),
OneOrMore(Token),
}
impl TokenAdicity {
fn inner(&self) -> &Token {
match self {
ZeroOrOne(ref t) => t,
ZeroOrMore(ref t) => t,
One(ref t) => t,
OneOrMore(ref t) => t,
}
}
}
/// A token encountered in the UI's command execution bar
#[derive(Clone, Copy, Debug)]
pub enum Token {
Literal(&'static str),
Filepath,
Alternatives(&'static [TokenStream]),
Seq(&'static [TokenAdicity]),
AccountName,
MailboxPath,
QuotedStringValue,
RestOfStringValue,
AlphanumericStringValue,
AttachmentIndexValue,
MailboxIndexValue,
IndexValue,
}
fn eof(input: &[u8]) -> IResult<&[u8], ()> {
if input.is_empty() {
Ok((input, ()))
} else {
Err(nom::Err::Error(NomError {
input,
code: nom::error::ErrorKind::Tag,
}))
}
}
define_commands!([
{ tags: ["set", "set seen", "set unseen", "set plain", "set threaded", "set compact"],
desc: "set [seen/unseen], toggles message's Seen flag. set [plain/threaded/compact/conversations] changes the mail listing view",
tokens: &[One(Literal("set")),
One(
Alternatives(&[
to_stream!(One(Literal("seen"))),
to_stream!(One(Literal("unseen"))),
to_stream!(One(Literal("plain"))),
to_stream!(One(Literal("threaded"))),
to_stream!(One(Literal("compact"))),
to_stream!(One(Literal("conversations")))
])
)
],
parser: parser::set
},
{ tags: ["delete"],
desc: "delete message",
tokens: &[One(Literal("delete"))],
parser: parser::delete_message
},
{ tags: ["copyto", "moveto"],
desc: "copy/move message",
tokens: &[One(Alternatives(&[to_stream!(One(Literal("copyto"))), to_stream!(One(Literal("moveto")))])), ZeroOrOne(AccountName), One(MailboxPath)],
parser: parser::copymove
},
{ tags: ["import "],
desc: "import FILESYSTEM_PATH MAILBOX_PATH",
tokens: &[One(Literal("import")), One(Filepath), One(MailboxPath)],
parser: parser::import
},
{ tags: ["close"],
desc: "close non-sticky tabs",
tokens: &[One(Literal("close"))],
parser: parser::close
},
{ tags: ["go"],
desc: "go <n>, switch to nth mailbox in this account",
tokens: &[One(Literal("goto")), One(MailboxIndexValue)],
parser: parser::goto
},
{ tags: ["subsort"],
desc: "subsort [date/subject] [asc/desc], sorts first level replies in threads.",
tokens: &[One(Literal("subsort")), One(Alternatives(&[to_stream!(One(Literal("date"))), to_stream!(One(Literal("subject")))])), One(Alternatives(&[to_stream!(One(Literal("asc"))), to_stream!(One(Literal("desc")))])) ],
parser: parser::subsort
},
{ tags: ["sort"],
desc: "sort [date/subject] [asc/desc], sorts threads.",
tokens: &[One(Literal("sort")), One(Alternatives(&[to_stream!(One(Literal("date"))), to_stream!(One(Literal("subject")))])), One(Alternatives(&[to_stream!(One(Literal("asc"))), to_stream!(One(Literal("desc")))])) ],
parser: parser::sort
},
{ tags: ["sort"],
desc: "sort <column index> [asc/desc], sorts table columns.",
tokens: &[One(Literal("sort")), One(IndexValue), ZeroOrOne(Alternatives(&[to_stream!(One(Literal("asc"))), to_stream!(One(Literal("desc")))])) ],
parser: parser::sort_column
},
{ tags: ["toggle thread_snooze"],
desc: "turn off new notifications for this thread",
tokens: &[One(Literal("toggle")), One(Literal("thread_snooze"))],
parser: parser::toggle
},
{ tags: ["search"],
desc: "search <TERM>, searches list with given term",
tokens: &[One(Literal("search")), One(RestOfStringValue)],
parser: parser::search
},
{ tags: ["clear-selection"],
desc: "clear-selection",
tokens: &[One(Literal("clear-selection"))],
parser: parser::select
},
{ tags: ["select"],
desc: "select <TERM>, selects envelopes matching with given term",
tokens: &[One(Literal("select")), One(RestOfStringValue)],
parser: parser::select
},
{ tags: ["export-mbox "],
desc: "export-mbox PATH",
tokens: &[One(Literal("export-mbox")), One(Filepath)],
parser: parser::export_mbox
},
{ tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
desc: "list-[unsubscribe/post/archive]",
tokens: &[One(Alternatives(&[to_stream!(One(Literal("list-archive"))), to_stream!(One(Literal("list-post"))), to_stream!(One(Literal("list-unsubscribe")))]))],
parser: parser::mailinglist
},
{ tags: ["setenv "],
desc: "setenv VAR=VALUE",
tokens: &[One(Literal("setenv")), OneOrMore(Seq(&[One(AlphanumericStringValue), One(Literal("=")), One(QuotedStringValue)]))],
parser: parser::setenv
},
{ tags: ["printenv "],
desc: "printenv VAR",
tokens: &[],
parser: parser::printenv
},
{ tags: ["mailto "],
desc: "mailto MAILTO_ADDRESS",
tokens: &[One(Literal("mailto")), One(QuotedStringValue)],
parser: parser::mailto
},
/* Pipe pager contents to binary */
{ tags: ["pipe "],
desc: "pipe EXECUTABLE ARGS",
tokens: &[One(Literal("pipe")), One(Filepath), ZeroOrMore(QuotedStringValue)],
parser: parser::pipe
},
/* Filter pager contents through binary */
{ tags: ["filter "],
desc: "filter EXECUTABLE ARGS",
tokens: &[One(Literal("filter")), One(Filepath), ZeroOrMore(QuotedStringValue)],
parser: parser::filter
},
{ tags: ["add-attachment ", "add-attachment-file-picker "],
desc: "add-attachment PATH",
tokens: &[One(
Alternatives(&[to_stream!(One(Literal("add-attachment")), One(Filepath)), to_stream!(One(Literal("add-attachment-file-picker")))]))],
parser: parser::add_attachment
},
{ tags: ["remove-attachment "],
desc: "remove-attachment INDEX",
tokens: &[One(Literal("remove-attachment")), One(IndexValue)],
parser: parser::remove_attachment
},
{ tags: ["save-draft"],
desc: "save draft",
tokens: &[One(Literal("save-draft"))],
parser: parser::save_draft
},
{ tags: ["toggle sign "],
desc: "switch between sign/unsign for this draft",
tokens: &[One(Literal("toggle")), One(Literal("sign"))],
parser: parser::toggle
},
{ tags: ["toggle encrypt"],
desc: "toggle encryption for this draft",
tokens: &[One(Literal("toggle")), One(Literal("encrypt"))],
parser: parser::toggle
},
{ tags: ["create-mailbox "],
desc: "create-mailbox ACCOUNT MAILBOX_PATH",
tokens: &[One(Literal("create-mailbox")), One(AccountName), One(MailboxPath)],
parser: parser::create_mailbox
},
{ tags: ["subscribe-mailbox "],
desc: "subscribe-mailbox ACCOUNT MAILBOX_PATH",
tokens: &[One(Literal("subscribe-mailbox")), One(AccountName), One(MailboxPath)],
parser: parser::sub_mailbox
},
{ tags: ["unsubscribe-mailbox "],
desc: "unsubscribe-mailbox ACCOUNT MAILBOX_PATH",
tokens: &[One(Literal("unsubscribe-mailbox")), One(AccountName), One(MailboxPath)],
parser: parser::unsub_mailbox
},
{ tags: ["rename-mailbox "],
desc: "rename-mailbox ACCOUNT MAILBOX_PATH_SRC MAILBOX_PATH_DEST",
tokens: &[One(Literal("rename-mailbox")), One(AccountName), One(MailboxPath), One(MailboxPath)],
parser: parser::rename_mailbox
},
{ tags: ["delete-mailbox "],
desc: "delete-mailbox ACCOUNT MAILBOX_PATH",
tokens: &[One(Literal("delete-mailbox")), One(AccountName), One(MailboxPath)],
parser: parser::delete_mailbox
},
{ tags: ["reindex "],
desc: "reindex ACCOUNT, rebuild account cache in the background",
tokens: &[One(Literal("reindex")), One(AccountName)],
parser: parser::reindex
},
{ tags: ["open-in-tab"],
desc: "opens envelope view in new tab",
tokens: &[One(Literal("open-in-tab"))],
parser: parser::open_in_new_tab
},
{ tags: ["save-attachment "],
desc: "save-attachment INDEX PATH",
tokens: &[One(Literal("save-attachment")), One(AttachmentIndexValue), One(Filepath)],
parser: parser::save_attachment
},
{ tags: ["export-mail "],
desc: "export-mail PATH",
tokens: &[One(Literal("export-mail")), One(Filepath)],
parser: parser::export_mail
},
{ tags: ["add-addresses-to-contacts "],
desc: "add-addresses-to-contacts",
tokens: &[One(Literal("add-addresses-to-contacts"))],
parser: parser::add_addresses_to_contacts
},
{ tags: ["tag", "tag add", "tag remove"],
desc: "tag [add/remove], edits message's tags.",
tokens: &[One(Literal("tag")), One(Alternatives(&[to_stream!(One(Literal("add"))), to_stream!(One(Literal("remove")))]))],
parser: parser::_tag
},
{ tags: ["print "],
desc: "print ACCOUNT SETTING",
tokens: &[One(Literal("print")), One(AccountName), One(QuotedStringValue)],
parser: parser::print_account_setting
},
{ tags: ["print "],
desc: "print SETTING",
tokens: &[One(Literal("print")), One(QuotedStringValue)],
parser: parser::print_setting
},
{ tags: ["toggle mouse"],
desc: "toggle mouse support",
tokens: &[One(Literal("toggle")), One(Literal("mouse"))],
parser: parser::toggle
},
{ tags: ["manage-mailboxes"],
desc: "view and manage mailbox preferences",
tokens: &[One(Literal("manage-mailboxes"))],
parser: parser::manage_mailboxes
},
{ tags: ["manage-jobs"],
desc: "view and manage jobs",
tokens: &[One(Literal("manage-jobs"))],
parser: parser::manage_jobs
},
{ tags: ["quit"],
desc: "quit meli",
tokens: &[One(Literal("quit"))],
parser: parser::quit
},
{ tags: ["reload-config"],
desc: "reload configuration file",
tokens: &[One(Literal("reload-config"))],
parser: parser::reload_config
}
]);
/// Get command suggestions for input
pub fn command_completion_suggestions(input: &str) -> Vec<String> {
use crate::melib::ShellExpandTrait;
let mut sugg: HashSet<String> = Default::default();
for (_tags, _desc, tokens, _) in COMMAND_COMPLETION.iter() {
let _m = tokens.matches(&mut &(*input), &mut sugg);
if _m.is_empty() {
continue;
}
if let Some((s, Filepath)) = _m.last() {
let p = std::path::Path::new(s);
sugg.extend(p.complete(true).into_iter());
}
}
sugg.into_iter()
.map(|s| format!("{}{}", input, s.as_str()))
.collect::<Vec<String>>()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_parser() {
let mut input = "sort".to_string();
macro_rules! match_input {
($input:expr) => {{
let mut sugg: HashSet<String> = Default::default();
//print!("{}", $input);
for (_tags, _desc, tokens, _) in COMMAND_COMPLETION.iter() {
// //println!("{:?}, {:?}, {:?}", _tags, _desc, tokens);
let _ = tokens.matches(&mut $input.as_str(), &mut sugg);
// if !m.is_empty() {
// //print!("{:?} ", desc);
// //println!(" result = {:#?}\n\n", m);
// }
}
//println!("suggestions = {:#?}", sugg);
sugg.into_iter()
.map(|s| format!("{}{}", $input.as_str(), s.as_str()))
.collect::<HashSet<String>>()
}};
}
assert_eq!(
&match_input!(input),
&IntoIterator::into_iter(["sort date".to_string(), "sort subject".to_string()])
.collect(),
);
input = "so".to_string();
assert_eq!(
&match_input!(input),
&IntoIterator::into_iter(["sort".to_string()]).collect(),
);
input = "so ".to_string();
assert_eq!(&match_input!(input), &HashSet::default(),);
input = "to".to_string();
assert_eq!(
&match_input!(input),
&IntoIterator::into_iter(["toggle".to_string()]).collect(),
);
input = "toggle ".to_string();
assert_eq!(
&match_input!(input),
&IntoIterator::into_iter([
"toggle mouse".to_string(),
"toggle sign".to_string(),
"toggle encrypt".to_string(),
"toggle thread_snooze".to_string()
])
.collect(),
);
}
#[test]
#[ignore]
fn test_parser_interactive() {
use std::io;
let mut input = String::new();
loop {
input.clear();
print!("> ");
match io::stdin().read_line(&mut input) {
Ok(_n) => {
println!("Input is {:?}", input.as_str().trim());
let mut sugg: HashSet<String> = Default::default();
let mut vec = vec![];
//print!("{}", input);
for (_tags, _desc, tokens, _) in COMMAND_COMPLETION.iter() {
//println!("{:?}, {:?}, {:?}", _tags, _desc, tokens);
let m = tokens.matches(&mut input.as_str().trim(), &mut sugg);
if !m.is_empty() {
vec.push(tokens);
//print!("{:?} ", desc);
//println!(" result = {:#?}\n\n", m);
}
}
println!(
"suggestions = {:#?}",
sugg.into_iter()
.zip(vec.into_iter())
.map(|(s, v)| format!(
"{}{} {:?}",
input.as_str().trim(),
if input.trim().is_empty() {
s.trim()
} else {
s.as_str()
},
v
))
.collect::<Vec<String>>()
);
if input.trim() == "quit" {
break;
}
}
Err(error) => println!("error: {}", error),
}
}
println!("alright");
}
#[test]
fn test_command_parser_all() {
use CommandError::*;
for cmd in [
"set unseen",
"set seen",
"delete",
"copyto somewhere",
"moveto somewhere",
"import fpath mpath",
"close ",
"go 5",
] {
parse_command(cmd.as_bytes()).unwrap_or_else(|err| panic!("{} failed {}", cmd, err));
}
assert_eq!(
parse_command(b"setfafsfoo").unwrap_err().to_string(),
Parsing {
inner: "setfafsfoo".into(),
kind: "".into(),
}
.to_string(),
);
assert_eq!(
parse_command(b"set foo").unwrap_err().to_string(),
BadValue {
inner: "Bad argument for `set`. Accepted arguments are [seen, unseen, plain, \
threaded, compact, conversations]."
.into(),
}
.to_string(),
);
assert_eq!(
parse_command(b"moveto ").unwrap_err().to_string(),
WrongNumberOfArguments {
too_many: false,
takes: (1, Some(1)),
given: 0,
__func__: "moveto",
inner: "".into(),
}
.to_string(),
);
assert_eq!(
parse_command(b"reindex 1 2 3").unwrap_err().to_string(),
WrongNumberOfArguments {
too_many: true,
takes: (1, Some(1)),
given: 2,
__func__: "reindex",
inner: "".into(),
}
.to_string(),
);
}
}

View File

@ -0,0 +1,192 @@
/*
* meli
*
* Copyright 2017-2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! User actions that need to be handled by the UI
use std::path::PathBuf;
use melib::{email::mailto::Mailto, Flag, SortField, SortOrder};
use crate::components::{Component, ComponentId};
#[derive(Debug)]
pub enum FlagAction {
Set(Flag),
Unset(Flag),
}
#[derive(Debug)]
pub enum TagAction {
Add(String),
Remove(String),
}
#[derive(Debug)]
pub enum ListingAction {
SetPlain,
SetThreaded,
SetCompact,
SetConversations,
Search(String),
Select(String),
SetSeen,
SetUnseen,
CopyTo(MailboxPath),
CopyToOtherAccount(AccountName, MailboxPath),
MoveTo(MailboxPath),
MoveToOtherAccount(AccountName, MailboxPath),
Import(PathBuf, MailboxPath),
ExportMbox(Option<melib::mbox::MboxFormat>, PathBuf),
Delete,
OpenInNewTab,
Tag(TagAction),
Flag(FlagAction),
ClearSelection,
ToggleThreadSnooze,
}
#[derive(Debug)]
pub enum TabAction {
Close,
Kill(ComponentId),
New(Option<Box<dyn Component>>),
ManageMailboxes,
ManageJobs,
}
#[derive(Debug)]
pub enum MailingListAction {
ListPost,
ListArchive,
ListUnsubscribe,
}
#[derive(Debug)]
pub enum ViewAction {
Pipe(String, Vec<String>),
Filter(String),
SaveAttachment(usize, String),
ExportMail(String),
AddAddressesToContacts,
}
#[derive(Debug)]
pub enum ComposeAction {
AddAttachment(String),
AddAttachmentFilePicker(Option<String>),
AddAttachmentPipe(String),
RemoveAttachment(usize),
SaveDraft,
ToggleSign,
ToggleEncrypt,
Mailto(Mailto),
}
#[derive(Debug)]
pub enum AccountAction {
ReIndex,
PrintAccountSetting(String),
}
#[derive(Debug)]
pub enum MailboxOperation {
Create(NewMailboxPath),
Delete(MailboxPath),
Subscribe(MailboxPath),
Unsubscribe(MailboxPath),
Rename(MailboxPath, NewMailboxPath),
// Placeholder
SetPermissions(MailboxPath),
}
#[derive(Debug)]
pub enum Action {
Listing(ListingAction),
ViewMailbox(usize),
Sort(SortField, SortOrder),
SortColumn(usize, SortOrder),
SubSort(SortField, SortOrder),
Tab(TabAction),
MailingListAction(MailingListAction),
View(ViewAction),
SetEnv(String, String),
PrintEnv(String),
CurrentDirectory,
ChangeCurrentDirectory(PathBuf),
Compose(ComposeAction),
Mailbox(AccountName, MailboxOperation),
AccountAction(AccountName, AccountAction),
PrintSetting(String),
ReloadConfiguration,
ToggleMouse,
Quit,
}
impl Action {
pub fn needs_confirmation(&self) -> bool {
matches!(
self,
Self::Listing(ListingAction::Delete)
| Self::MailingListAction(_)
| Self::Mailbox(_, _)
| Self::Quit
)
}
}
type AccountName = String;
type MailboxPath = String;
type NewMailboxPath = String;
macro_rules! impl_into_action {
($({$t:ty => $var:tt}),*) => {
$(
impl From<$t> for Action {
fn from(v: $t) -> Self {
Self::$var(v)
}
}
)*
};
}
macro_rules! impl_tuple_into_action {
($({$a:ty,$b:ty => $var:tt}),*) => {
$(
impl From<($a,$b)> for Action {
fn from((a, b): ($a,$b)) -> Self {
Self::$var(a, b)
}
}
)*
};
}
impl_into_action!(
{ ListingAction => Listing },
{ TabAction => Tab },
{ MailingListAction => MailingListAction },
{ ViewAction => View },
{ ComposeAction => Compose }
);
impl_tuple_into_action!(
{ AccountName, MailboxOperation => Mailbox },
{ AccountName, AccountAction => AccountAction }
);

View File

@ -0,0 +1,180 @@
/*
* meli
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Helper type for showing the exact reason why a command was invalid.
use super::*;
pub enum ArgCheck<const MIN: u8, const MAX: u8> {
Start { __func__: &'static str },
BeforeArgument { so_far: u8, __func__: &'static str },
Eof { so_far: u8, __func__: &'static str },
}
impl<const MIN: u8, const MAX: u8> ArgCheck<MIN, MAX> {
#[inline]
pub fn new(__func__: &'static str) -> Self {
Self::Start { __func__ }
}
#[inline]
pub fn start(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::Start { __func__ } = *self else {
unreachable!(
"ArgCheck::start called with invalid variant: {}",
if matches!(self, Self::BeforeArgument { .. }) {
"BeforeArgument"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
if is_empty && MIN > 0 {
return Err(CommandError::WrongNumberOfArguments {
too_many: false,
takes: (MIN, MAX.into()),
given: 0,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::BeforeArgument {
so_far: 0,
__func__,
};
Ok(())
}
#[inline]
pub fn inc(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::BeforeArgument { __func__, so_far } = *self else {
unreachable!(
"ArgCheck::inc called with invalid variant: {}",
if matches!(self, Self::Start { .. }) {
"Start"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
let new_value = so_far + 1;
if is_empty && new_value > MAX {
return Err(CommandError::WrongNumberOfArguments {
too_many: true,
takes: (MIN, MAX.into()),
given: new_value,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::BeforeArgument {
so_far: new_value,
__func__,
};
Ok(())
}
#[inline]
pub fn finish(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::BeforeArgument { __func__, so_far } = *self else {
unreachable!(
"ArgCheck::finish called with invalid variant: {}",
if matches!(self, Self::Start { .. }) {
"Start"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
if !is_empty {
assert!(so_far <= MAX);
assert!(so_far >= MIN);
return Err(CommandError::WrongNumberOfArguments {
too_many: true,
takes: (MIN, MAX.into()),
given: so_far + 1,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::Eof { so_far, __func__ };
Ok(())
}
}
macro_rules! arg_init {
(min_arg: $n:expr, max_arg: $x:expr, $func:tt) => {{
ArgCheck::<$n, $x>::new(stringify!($func))
}};
}
//macro_rules! arg_value_check {
// ($tag:literal, $input:expr) => {{
// if tag::<&'_ str, &'_ [u8],
// melib::nom::error::Error<&[u8]>>($tag)($input).is_err() { return
// Ok(( $input,
// Err(CommandError::BadValue {
// inner: $tag.to_string().into(),
// }),
// ));
// }
// tag($tag)($input)
// }};
//}
macro_rules! arg_chk {
(start $check:ident, $input:expr) => {{
if let Err(err) = $check.start($input) {
return Ok(($input, Err(err)));
};
}};
(inc $check:ident, $input:expr) => {{
if let Err(err) = $check.inc($input) {
return Ok(($input, Err(err)));
};
}};
(finish $check:ident, $input:expr) => {{
if let Err(err) = $check.finish($input) {
return Ok(($input, Err(err)));
};
}};
}

View File

@ -0,0 +1,123 @@
/*
* meli
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::*;
#[derive(Clone, Debug)]
pub enum CommandError {
Parsing {
inner: Cow<'static, str>,
kind: Cow<'static, str>,
},
BadValue {
inner: Cow<'static, str>,
},
WrongNumberOfArguments {
too_many: bool,
takes: (u8, Option<u8>),
given: u8,
__func__: &'static str,
inner: Cow<'static, str>,
},
Other {
inner: Cow<'static, str>,
},
}
impl<'a> From<nom::Err<melib::nom::error::Error<&'a [u8]>>> for CommandError {
fn from(res: nom::Err<melib::nom::error::Error<&'a [u8]>>) -> Self {
match res {
nom::Err::Incomplete(_) => Self::Parsing {
inner: res.to_string().into(),
kind: "".into(),
},
nom::Err::Error(e) | nom::Err::Failure(e) => Self::Parsing {
inner: String::from_utf8_lossy(e.input).to_string().into(),
kind: format!("{:?}", e.code).into(),
},
}
}
}
impl std::fmt::Display for CommandError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Parsing { inner, kind: _ } => {
write!(fmt, "Could not parse command: {}", inner)
}
Self::BadValue { inner } => {
write!(fmt, "Bad value/argument: {}", inner)
}
Self::WrongNumberOfArguments {
too_many,
takes,
given,
__func__,
inner: _,
} => {
if *too_many {
match takes {
(min, None) => {
write!(
fmt,
"{}: Too many arguments. Command takes {} arguments, but {} were \
given.",
__func__, min, given
)
}
(min, Some(max)) => {
write!(
fmt,
"{}: Too many arguments. Command takes from {} to {} arguments, \
but {} were given.",
__func__, min, max, given
)
}
}
} else {
match takes {
(min, None) => {
write!(
fmt,
"{}: Not enough arguments. Command takes {} arguments, but {} \
were given.",
__func__, min, given
)
}
(min, Some(max)) => {
write!(
fmt,
"{}: Not enough arguments. Command takes from {} to {} arguments, \
but {} were given.",
__func__, min, max, given
)
}
}
}
}
Self::Other { inner } => {
write!(fmt, "Error: {}", inner)
}
}
}
}
impl std::error::Error for CommandError {}

View File

@ -19,16 +19,20 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};
use std::{
fs::OpenOptions,
io::{Read, Write},
sync::{Arc, Mutex},
};
thread_local!(static CMD_HISTORY_FILE: Arc<Mutex<std::fs::File>> = Arc::new(Mutex::new({
let data_dir = xdg::BaseDirectories::with_prefix("meli").unwrap();
OpenOptions::new().append(true) /* writes will append to a file instead of overwriting previous contents */
.create(true) /* a new file will be created if the file does not yet already exist.*/
.read(true)
.open(data_dir.place_data_file("cmd_history").unwrap()).unwrap()
OpenOptions::new()
.append(true) /* writes will append to a file instead of overwriting previous contents */
.create(true) /* a new file will be created if the file does not yet already exist. */
.read(true)
.open(data_dir.place_data_file("cmd_history").unwrap())
.unwrap()
})));
pub fn log_cmd(mut cmd: String) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
/*
* meli
*
* Copyright 2017-2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Components visual and logical separations of application interfaces.
use indexmap::IndexMap;
///
/// They can draw on the terminal and receive events, but also do other stuff
/// as well.
/// For an example, see the [`notifications`] module.
/// See also the [`Component`] trait for more details.
use smallvec::SmallVec;
use uuid::Uuid;
use super::*;
#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[repr(transparent)]
pub struct ComponentId(Uuid);
impl AsRef<Uuid> for ComponentId {
fn as_ref(&self) -> &Uuid {
&self.0
}
}
impl std::fmt::Display for ComponentId {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0.as_simple(), fmt)
}
}
impl std::fmt::Debug for ComponentId {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0.as_simple(), fmt)
}
}
impl Default for ComponentId {
fn default() -> Self {
Self(Uuid::new_v4())
}
}
impl std::fmt::LowerHex for ComponentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::LowerHex::fmt(self.0.as_hyphenated(), f)
}
}
impl std::fmt::UpperHex for ComponentId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::UpperHex::fmt(self.0.as_hyphenated(), f)
}
}
pub type ShortcutMap = IndexMap<&'static str, Key>;
pub type ShortcutMaps = IndexMap<&'static str, ShortcutMap>;
mod private {
pub trait Sealed {}
}
impl private::Sealed for ShortcutMaps {}
pub trait ExtendShortcutsMaps: private::Sealed {
fn extend_shortcuts(&mut self, other: Self);
}
impl ExtendShortcutsMaps for ShortcutMaps {
fn extend_shortcuts(&mut self, mut other: Self) {
other.retain(|k, v| {
if let Some(m) = self.get_mut(k) {
m.extend(v.iter().map(|(k, v)| (*k, v.clone())));
false
} else {
true
}
});
self.extend(other);
}
}
#[derive(Clone, Copy, Debug)]
pub enum PageMovement {
Up(usize),
Right(usize),
Left(usize),
Down(usize),
PageUp(usize),
PageDown(usize),
Home,
End,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ScrollContext {
pub shown_lines: usize,
pub total_lines: usize,
pub has_more_lines: bool,
}
#[derive(Clone, Copy, Debug)]
pub enum ScrollUpdate {
End(ComponentId),
Update {
id: ComponentId,
context: ScrollContext,
},
}
/// Types implementing this Trait can draw on the terminal and receive events.
/// If a type wants to skip drawing if it has not changed anything, it can hold
/// some flag in its fields (eg `self.dirty = false`) and act upon that in their
/// [`draw`](Component::draw) implementation.
pub trait Component: std::fmt::Display + std::fmt::Debug + Send + Sync {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool;
fn is_dirty(&self) -> bool;
/// If the component is meant to be currently visible to the user.
fn is_visible(&self) -> bool {
true
}
/// If the component can quit right away without any unsaved or ongoing
/// operations.
fn can_quit_cleanly(&mut self, _context: &Context) -> bool {
true
}
fn set_dirty(&mut self, value: bool);
fn kill(&mut self, _id: ComponentId, _context: &mut Context) {}
fn id(&self) -> ComponentId;
fn shortcuts(&self, _context: &Context) -> ShortcutMaps {
Default::default()
}
/// Get status message for the status line.
fn status(&self, _context: &Context) -> String {
String::new()
}
fn attributes(&self) -> &'static ComponentAttr {
&ComponentAttr::DEFAULT
}
fn children(&self) -> IndexMap<ComponentId, &dyn Component> {
IndexMap::default()
}
fn children_mut(&mut self) -> IndexMap<ComponentId, &mut dyn Component> {
IndexMap::default()
}
fn realize(&self, parent: Option<ComponentId>, context: &mut Context) {
// log::trace!("Realizing id {} w/ parent {:?}", self.id(), &parent);
context.realized.insert(self.id(), parent);
}
fn unrealize(&self, context: &mut Context) {
// log::trace!("Unrealizing id {}", self.id());
context.unrealized.insert(self.id());
context
.replies
.push_back(UIEvent::ComponentUnrealize(self.id()));
}
}
impl Component for Box<dyn Component> {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
(**self).draw(grid, area, context)
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
(**self).process_event(event, context)
}
fn is_dirty(&self) -> bool {
(**self).is_dirty()
}
fn is_visible(&self) -> bool {
(**self).is_visible()
}
fn can_quit_cleanly(&mut self, context: &Context) -> bool {
(**self).can_quit_cleanly(context)
}
fn set_dirty(&mut self, value: bool) {
(**self).set_dirty(value)
}
fn kill(&mut self, id: ComponentId, context: &mut Context) {
(**self).kill(id, context)
}
fn id(&self) -> ComponentId {
(**self).id()
}
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
(**self).shortcuts(context)
}
fn status(&self, context: &Context) -> String {
(**self).status(context)
}
fn attributes(&self) -> &'static ComponentAttr {
(**self).attributes()
}
fn children(&self) -> IndexMap<ComponentId, &dyn Component> {
(**self).children()
}
fn children_mut(&mut self) -> IndexMap<ComponentId, &mut dyn Component> {
(**self).children_mut()
}
fn realize(&self, parent: Option<ComponentId>, context: &mut Context) {
(**self).realize(parent, context)
}
fn unrealize(&self, context: &mut Context) {
(**self).unrealize(context)
}
}
bitflags::bitflags! {
/// Attributes of a [`Component`] widget.
///
/// `ComponentAttr::DEFAULT` represents no attribute.
pub struct ComponentAttr: u8 {
/// Nothing special going on.
const DEFAULT = 0;
const HAS_ANIMATIONS = 1;
const CONTAINER = 1 << 1;
}
}
impl Default for ComponentAttr {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ComponentPath {
pub id: ComponentId,
pub tail: SmallVec<[ComponentId; 8]>,
}
impl ComponentPath {
pub fn new(id: ComponentId) -> Self {
Self {
id,
tail: SmallVec::default(),
}
}
pub fn push_front(&mut self, id: ComponentId) {
self.tail.insert(0, self.id);
self.id = id;
}
pub fn push_back(&mut self, id: ComponentId) {
self.tail.push(id);
}
pub fn resolve<'c>(&self, root: &'c dyn Component) -> Option<&'c dyn Component> {
let mut cursor = root;
for id in self.tail.iter().rev().chain(std::iter::once(&self.id)) {
// log::trace!("resolve cursor = {} next id is {}", cursor.id(), &id);
if *id == cursor.id() {
// log::trace!("continue;");
continue;
}
cursor = cursor.children().remove(id)?;
}
Some(cursor)
}
#[inline]
pub fn parent(&self) -> Option<&ComponentId> {
self.tail.first()
}
#[inline]
pub fn root(&self) -> Option<&ComponentId> {
self.tail.last()
}
}

1427
meli/src/conf.rs 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,305 @@
/*
* meli - conf module
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Configuration for composing email.
use std::collections::HashMap;
use melib::{conf::ActionFlag, email::HeaderName};
use serde::{de, Deserialize, Deserializer};
use super::{
default_vals::{ask, false_val, none, true_val},
deserializers::non_empty_string,
};
/// Settings for writing and sending new e-mail
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ComposingSettings {
/// A command to pipe new emails to
/// Required
pub send_mail: SendMail,
/// Command to launch editor. Can have arguments. Draft filename is given as
/// the last argument. If it's missing, the environment variable $EDITOR is
/// looked up.
#[serde(
default = "none",
alias = "editor-command",
alias = "editor-cmd",
alias = "editor_cmd"
)]
pub editor_command: Option<String>,
/// Embedded editor (for terminal interfaces) instead of forking and
/// waiting.
#[serde(default = "false_val", alias = "embed")]
pub embedded_pty: bool,
/// Set "format=flowed" in plain text attachments.
/// Default: true
#[serde(default = "true_val", alias = "format-flowed")]
pub format_flowed: bool,
/// Set User-Agent
/// Default: empty
#[serde(default = "true_val", alias = "insert_user_agent")]
pub insert_user_agent: bool,
/// Set default header values for new drafts
/// Default: empty
#[serde(default, alias = "default-header-values")]
pub default_header_values: HashMap<HeaderName, String>,
/// Wrap header preamble when editing a draft in an editor. This allows you
/// to write non-plain text email without the preamble creating syntax
/// errors. They are stripped when you return from the editor. The
/// values should be a two element array of strings, a prefix and suffix.
/// Default: None
#[serde(default, alias = "wrap-header-preamble")]
pub wrap_header_preamble: Option<(String, String)>,
/// Store sent mail after successful submission. This setting is meant to be
/// disabled for non-standard behaviour in gmail, which auto-saves sent
/// mail on its own. Default: true
#[serde(default = "true_val")]
pub store_sent_mail: bool,
/// The attribution line appears above the quoted reply text.
/// The format specifiers for the replied address are:
/// - `%+f` — the sender's name and email address.
/// - `%+n` — the sender's name (or email address, if no name is included).
/// - `%+a` — the sender's email address.
/// The format string is passed to strftime(3) with the replied envelope's
/// date. Default: "On %a, %0e %b %Y %H:%M, %+f wrote:%n"
#[serde(default = "none")]
pub attribution_format_string: Option<String>,
/// Whether the strftime call for the attribution string uses the POSIX
/// locale instead of the user's active locale
/// Default: true
#[serde(default = "true_val")]
pub attribution_use_posix_locale: bool,
/// Forward emails as attachment? (Alternative is inline)
/// Default: ask
#[serde(default = "ask", alias = "forward-as-attachment")]
pub forward_as_attachment: ActionFlag,
/// Alternative lists of reply prefixes (etc. ["Re:", "RE:", ...]) to strip
/// Default: `["Re:", "RE:", "Fwd:", "Fw:", "回复:", "回覆:", "SV:", "Sv:",
/// "VS:", "Antw:", "Doorst:", "VS:", "VL:", "REF:", "TR:", "TR:", "AW:",
/// "WG:", "ΑΠ:", "Απ:", "απ:", "ΠΡΘ:", "Πρθ:", "πρθ:", "ΣΧΕΤ:", "Σχετ:",
/// "σχετ:", "ΠΡΘ:", "Πρθ:", "πρθ:", "Vá:", "Továbbítás:", "R:", "I:",
/// "RIF:", "FS:", "BLS:", "TRS:", "VS:", "VB:", "RV:", "RES:", "Res",
/// "ENC:", "Odp:", "PD:", "YNT:", "İLT:", "ATB:", "YML:"]`
#[serde(default, alias = "reply-prefix-list-to-strip")]
pub reply_prefix_list_to_strip: Option<Vec<String>>,
/// The prefix to use in reply subjects. The de facto prefix is "Re:".
#[serde(default = "res", alias = "reply-prefix")]
pub reply_prefix: String,
/// Custom `compose-hooks`.
#[serde(default, alias = "custom-compose-hooks")]
pub custom_compose_hooks: Vec<ComposeHook>,
/// Disabled `compose-hooks`.
#[serde(default, alias = "disabled-compose-hooks")]
pub disabled_compose_hooks: Vec<String>,
}
impl Default for ComposingSettings {
fn default() -> Self {
Self {
send_mail: SendMail::ShellCommand("false".into()),
editor_command: None,
embedded_pty: false,
format_flowed: true,
insert_user_agent: true,
default_header_values: HashMap::default(),
store_sent_mail: true,
wrap_header_preamble: None,
attribution_format_string: None,
attribution_use_posix_locale: true,
forward_as_attachment: ActionFlag::Ask,
reply_prefix_list_to_strip: None,
reply_prefix: res(),
custom_compose_hooks: vec![],
disabled_compose_hooks: vec![],
}
}
}
fn res() -> String {
"Re:".to_string()
}
macro_rules! named_unit_variant {
($variant:ident) => {
pub mod $variant {
pub fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(stringify!($variant))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(concat!("\"", stringify!($variant), "\""))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value == stringify!($variant) {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
}
}
}
deserializer.deserialize_str(V)
}
}
};
}
pub mod strings {
named_unit_variant!(server_submission);
}
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum SendMail {
#[cfg(feature = "smtp")]
Smtp(melib::smtp::SmtpServerConf),
#[serde(with = "strings::server_submission")]
ServerSubmission,
ShellCommand(String),
}
/// Shell command compose hooks (See
/// [`crate::mail::compose::hooks::Hook`])
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ComposeHook {
#[serde(deserialize_with = "non_empty_string")]
name: String,
#[serde(deserialize_with = "non_empty_string")]
command: String,
}
impl From<ComposeHook> for crate::mail::hooks::Hook {
fn from(c: ComposeHook) -> Self {
Self::new_shell_command(c.name.into(), c.command)
}
}
const SENDMAIL_ERR_HELP: &str = r#"Invalid `send_mail` value.
Here are some valid examples:
Use server submission in protocols that support it (JMAP, NNTP)
===============================================================
send_mail = "server_submission"
Using a shell script
====================
send_mail = "msmtp --read-recipients --read-envelope-from"
Direct SMTP connection
======================
send_mail = { hostname = "mail.example.com", port = 587, auth = { type = "auto", password = { type = "raw", value = "hunter2" } }, security = { type = "STARTTLS" } }
[composing.send_mail]
hostname = "mail.example.com"
port = 587
auth = { type = "auto", password = { type = "command_eval", value = "/path/to/password_script.sh" } }
security = { type = "TLS", danger_accept_invalid_certs = true } }
`send_mail` direct SMTP connection fields:
- hostname: text
- port: valid port number
- envelope_from: text (optional, default is empty),
- auth: ...
- security: ... (optional, default is "auto")
- extensions: ... (optional, default is PIPELINING, CHUNKING, PRDR, 8BITMIME, BINARYMIME, SMTPUTF8, AUTH and DSN_NOTIFY)
Possible values for `send_mail.auth`:
No authentication:
auth = { type = "none" }
Regular authentication:
Note: `require_auth` and `auth_type` are optional and can be skipped.
auth = { type = "auto", username = "...", password = "...", require_auth = true, auth_type = ... }
password can be:
password = { type = "raw", value = "..." }
password = { type = "command_eval", value = "/path/to/password_script.sh" }
XOAuth2 authentication:
Note: `require_auth` is optional and can be skipped.
auth = { type = "xoauth2", token_command = "...", require_auth = true }
Possible values for `send_mail.auth.auth_type` when `auth.type` is "auto":
auth_type = { plain = false, login = true }
Possible values for `send_mail.security`:
Note that in all cases field `danger_accept_invalid_certs` is optional and its default value is false.
security = "none"
security = { type = "auto", danger_accept_invalid_certs = false }
security = { type = "STARTTLS", danger_accept_invalid_certs = false }
security = { type = "TLS", danger_accept_invalid_certs = false }
Possible values for `send_mail.extensions` (All optional and have default values `true`:
pipelining
chunking
8bitmime
prdr
binarymime
smtputf8
auth
dsn_notify: Array of options e.g. ["FAILURE"]
"#;
impl<'de> Deserialize<'de> for SendMail {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum SendMailInner {
#[cfg(feature = "smtp")]
Smtp(melib::smtp::SmtpServerConf),
#[serde(with = "strings::server_submission")]
ServerSubmission,
ShellCommand(String),
}
match <SendMailInner>::deserialize(deserializer) {
#[cfg(feature = "smtp")]
Ok(SendMailInner::Smtp(v)) => Ok(Self::Smtp(v)),
Ok(SendMailInner::ServerSubmission) => Ok(Self::ServerSubmission),
Ok(SendMailInner::ShellCommand(v)) => Ok(Self::ShellCommand(v)),
Err(_err) => Err(de::Error::custom(SENDMAIL_ERR_HELP)),
}
}
}

View File

@ -0,0 +1,258 @@
/*
* meli - listing conf module
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::{search::Query, Error, Result, ToggleFlag};
use super::{default_vals::*, DotAddressable, IndexStyle};
/// Settings for mail listings
///
///
/// Tree decoration examples:
///
///```no_run
/// const HAS_SIBLING: &str = " ┃";
/// const NO_SIBLING: &str = " ";
/// const HAS_SIBLING_LEAF: &str = " ┣━";
/// const NO_SIBLING_LEAF: &str = " ┗━";
/// ```
///
///```no_run
/// const HAS_SIBLING: &str = " |";
/// const NO_SIBLING: &str = " ";
/// const HAS_SIBLING_LEAF: &str = " |\\_";
/// const NO_SIBLING_LEAF: &str = " \\_";
/// ```
///
///```no_run
/// const HAS_SIBLING: &str = " ";
/// const NO_SIBLING: &str = " ";
/// const HAS_SIBLING_LEAF: &str = " ";
/// const NO_SIBLING_LEAF: &str = " ";
/// ```
///
///```no_run
/// const HAS_SIBLING: &str = " │";
/// const NO_SIBLING: &str = " ";
/// const HAS_SIBLING_LEAF: &str = " ├─";
/// const NO_SIBLING_LEAF: &str = " ╰─";
/// ```
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ListingSettings {
/// Number of context lines when going to next page.
/// Default: 0
#[serde(default = "zero_val", alias = "context-lines")]
pub context_lines: usize,
/// Show auto-hiding scrollbar in accounts sidebar menu.
/// Default: True
#[serde(default = "true_val")]
pub show_menu_scrollbar: bool,
/// Datetime formatting passed verbatim to strftime(3).
/// Default: %Y-%m-%d %T
#[serde(default = "none", alias = "datetime-fmt")]
pub datetime_fmt: Option<String>,
/// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
/// Default: true
#[serde(default = "true_val", alias = "recent-dates")]
pub recent_dates: bool,
/// Show only envelopes that match this query
/// Default: None
#[serde(default = "none")]
pub filter: Option<Query>,
#[serde(default, alias = "index-style")]
pub index_style: IndexStyle,
/// Default: " "
#[serde(default = "none")]
pub sidebar_mailbox_tree_has_sibling: Option<String>,
/// Default: " "
#[serde(default)]
pub sidebar_mailbox_tree_no_sibling: Option<String>,
/// Default: " "
#[serde(default)]
pub sidebar_mailbox_tree_has_sibling_leaf: Option<String>,
/// Default: " "
#[serde(default)]
pub sidebar_mailbox_tree_no_sibling_leaf: Option<String>,
/// Default: ' '
#[serde(default = "default_divider")]
pub sidebar_divider: char,
/// Default: 90
#[serde(default = "default_ratio")]
pub sidebar_ratio: usize,
/// Flag to show if thread entry contains unseen mail.
/// Default: "●"
#[serde(default)]
pub unseen_flag: Option<String>,
/// Flag to show if thread has been snoozed.
/// Default: "💤"
#[serde(default)]
pub thread_snoozed_flag: Option<String>,
/// Flag to show if thread entry has been selected.
/// Default: "☑️"
#[serde(default)]
pub selected_flag: Option<String>,
/// Flag to show if thread entry contains attachments.
/// Default: "📎"
#[serde(default)]
pub attachment_flag: Option<String>,
/// Flag to show if any thread entry contains your address as a receiver.
/// Useful to make mailing list threads that CC you stand out.
/// Default: "✸"
#[serde(default)]
pub highlight_self_flag: Option<String>,
/// Show `highlight_self_flag` or not.
/// Default: false
#[serde(default)]
pub highlight_self: ToggleFlag,
/// Should threads with different Subjects show a list of those
/// subjects on the entry title?
/// Default: "true"
#[serde(default = "true_val")]
pub thread_subject_pack: bool,
/// In threaded listing style, repeat identical From column values within a
/// thread. Not repeating adds empty space in the From column which
/// might result in less visual clutter.
/// Default: "false"
#[serde(default = "false_val")]
pub threaded_repeat_identical_from_values: bool,
/// Show relative indices in menu mailboxes to quickly help with jumping to
/// them. Default: "true"
#[serde(default = "true_val", alias = "relative-menu-indices")]
pub relative_menu_indices: bool,
/// Show relative indices in listings to quickly help with jumping to
/// them. Default: "true"
#[serde(default = "true_val", alias = "relative-list-indices")]
pub relative_list_indices: bool,
/// Hide sidebar on launch. Default: "false"
#[serde(default = "false_val", alias = "hide-sidebar-on-launch")]
pub hide_sidebar_on_launch: bool,
}
const fn default_divider() -> char {
' '
}
const fn default_ratio() -> usize {
90
}
impl Default for ListingSettings {
fn default() -> Self {
Self {
context_lines: 0,
show_menu_scrollbar: true,
datetime_fmt: None,
recent_dates: true,
filter: None,
index_style: IndexStyle::default(),
sidebar_mailbox_tree_has_sibling: None,
sidebar_mailbox_tree_no_sibling: None,
sidebar_mailbox_tree_has_sibling_leaf: None,
sidebar_mailbox_tree_no_sibling_leaf: None,
sidebar_divider: default_divider(),
sidebar_ratio: 90,
unseen_flag: None,
thread_snoozed_flag: None,
selected_flag: None,
attachment_flag: None,
highlight_self_flag: None,
highlight_self: ToggleFlag::Unset,
thread_subject_pack: true,
threaded_repeat_identical_from_values: false,
relative_menu_indices: true,
relative_list_indices: true,
hide_sidebar_on_launch: false,
}
}
}
impl DotAddressable for ListingSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"context_lines" => self.context_lines.lookup(field, tail),
"show_menu_scrollbar" => self.show_menu_scrollbar.lookup(field, tail),
"datetime_fmt" => self.datetime_fmt.lookup(field, tail),
"recent_dates" => self.recent_dates.lookup(field, tail),
"filter" => self.filter.lookup(field, tail),
"index_style" => self.index_style.lookup(field, tail),
"sidebar_mailbox_tree_has_sibling" => {
self.sidebar_mailbox_tree_has_sibling.lookup(field, tail)
}
"sidebar_mailbox_tree_no_sibling" => {
self.sidebar_mailbox_tree_no_sibling.lookup(field, tail)
}
"sidebar_mailbox_tree_has_sibling_leaf" => self
.sidebar_mailbox_tree_has_sibling_leaf
.lookup(field, tail),
"sidebar_mailbox_tree_no_sibling_leaf" => self
.sidebar_mailbox_tree_no_sibling_leaf
.lookup(field, tail),
"sidebar_divider" => self.sidebar_divider.lookup(field, tail),
"sidebar_ratio" => self.sidebar_ratio.lookup(field, tail),
"unseen_flag" => self.unseen_flag.lookup(field, tail),
"thread_snoozed_flag" => self.thread_snoozed_flag.lookup(field, tail),
"selected_flag" => self.selected_flag.lookup(field, tail),
"attachment_flag" => self.attachment_flag.lookup(field, tail),
"highlight_self_flag" => self.highlight_self_flag.lookup(field, tail),
"highlight_self" => self.highlight_self.lookup(field, tail),
"thread_subject_pack" => self.thread_subject_pack.lookup(field, tail),
"threaded_repeat_identical_from_values" => self
.threaded_repeat_identical_from_values
.lookup(field, tail),
"relative_menu_indices" => self.relative_menu_indices.lookup(field, tail),
"relative_list_indices" => self.relative_list_indices.lookup(field, tail),
"hide_sidebar_on_launch" => self.hide_sidebar_on_launch.lookup(field, tail),
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}

View File

@ -0,0 +1,94 @@
/*
* meli - notifications conf module
*
* Copyright 2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::{Error, Result, ToggleFlag};
use super::{
default_vals::{internal_value_false, none, true_val},
DotAddressable,
};
/// Settings for the notifications function.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NotificationsSettings {
/// Enable notifications.
/// Default: True
#[serde(default = "true_val")]
pub enable: bool,
/// A command to pipe notifications through.
/// Default: None
#[serde(default = "none")]
pub script: Option<String>,
/// A command to pipe new mail notifications through (preferred over
/// `script`). Default: None
#[serde(default = "none")]
pub new_mail_script: Option<String>,
/// A file location which has its size changed when new mail arrives (max
/// 128 bytes). Can be used to trigger new mail notifications eg with
/// `xbiff(1)`. Default: None
#[serde(default = "none", alias = "xbiff-file-path")]
pub xbiff_file_path: Option<String>,
#[serde(default = "internal_value_false", alias = "play-sound")]
pub play_sound: ToggleFlag,
#[serde(default = "none", alias = "sound-file")]
pub sound_file: Option<String>,
}
impl Default for NotificationsSettings {
fn default() -> Self {
Self {
enable: true,
script: None,
new_mail_script: None,
xbiff_file_path: None,
play_sound: ToggleFlag::InternalVal(false),
sound_file: None,
}
}
}
impl DotAddressable for NotificationsSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"enable" => self.enable.lookup(field, tail),
"script" => self.script.lookup(field, tail),
"new_mail_script" => self.new_mail_script.lookup(field, tail),
"xbiff_file_path" => self.xbiff_file_path.lookup(field, tail),
"play_sound" => self.play_sound.lookup(field, tail),
"sound_file" => self.sound_file.lookup(field, tail),
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}

View File

@ -0,0 +1,43 @@
// @generated
/*
* meli - conf/overrides.rs
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
#![allow(clippy::derivable_impls)]
//! This module is automatically generated by `config_macros.rs`.
use super::*;
use melib::HeaderName;
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PagerSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "pager-context")] # [serde (default)] pub pager_context : Option < usize > , # [doc = " Stop at the end instead of displaying next mail."] # [doc = " Default: false"] # [serde (alias = "pager-stop")] # [serde (default)] pub pager_stop : Option < bool > , # [doc = " Always show headers when scrolling."] # [doc = " Default: true"] # [serde (alias = "sticky-headers" , alias = "headers-sticky" , alias = "headers_sticky")] # [serde (default)] pub sticky_headers : Option < bool > , # [doc = " The height of the pager in mail view, in percent."] # [doc = " Default: 80"] # [serde (alias = "pager-ratio")] # [serde (default)] pub pager_ratio : Option < usize > , # [doc = " A command to pipe mail output through for viewing in pager."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub filter : Option < Option < String > > , # [doc = " A command to pipe html output before displaying it in a pager"] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-filter")] # [serde (default)] pub html_filter : Option < Option < String > > , # [doc = " Respect \"format=flowed\""] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Split long lines that would overflow on the x axis."] # [doc = " Default: true"] # [serde (alias = "split-long-lines")] # [serde (default)] pub split_long_lines : Option < bool > , # [doc = " Minimum text width in columns."] # [doc = " Default: 80"] # [serde (alias = "minimum-width")] # [serde (default)] pub minimum_width : Option < usize > , # [doc = " Choose `text/html` alternative if `text/plain` is empty in"] # [doc = " `multipart/alternative` attachments."] # [doc = " Default: true"] # [serde (alias = "auto-choose-multipart-alternative")] # [serde (default)] pub auto_choose_multipart_alternative : Option < ToggleFlag > , # [doc = " Show Date: in my timezone"] # [doc = " Default: true"] # [serde (alias = "show-date-in-my-timezone")] # [serde (default)] pub show_date_in_my_timezone : Option < ToggleFlag > , # [doc = " A command to launch URLs with. The URL will be given as the first"] # [doc = " argument of the command. Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub url_launcher : Option < Option < String > > , # [doc = " A command to open html files."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-open")] # [serde (default)] pub html_open : Option < Option < String > > , # [doc = " Extra headers to display, if present, in the default header preamble."] # [doc = " Default: []"] # [serde (alias = "show-extra-headers")] # [serde (default)] pub show_extra_headers : Option < Vec < String > > } impl Default for PagerSettingsOverride { fn default () -> Self { Self { pager_context : None , pager_stop : None , sticky_headers : None , pager_ratio : None , filter : None , html_filter : None , format_flowed : None , split_long_lines : None , minimum_width : None , auto_choose_multipart_alternative : None , show_date_in_my_timezone : None , url_launcher : None , html_open : None , show_extra_headers : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ListingSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "context-lines")] # [serde (default)] pub context_lines : Option < usize > , # [doc = " Show auto-hiding scrollbar in accounts sidebar menu."] # [doc = " Default: True"] # [serde (default)] pub show_menu_scrollbar : Option < bool > , # [doc = " Datetime formatting passed verbatim to strftime(3)."] # [doc = " Default: %Y-%m-%d %T"] # [serde (alias = "datetime-fmt")] # [serde (default)] pub datetime_fmt : Option < Option < String > > , # [doc = " Show recent dates as `X {minutes,hours,days} ago`, up to 7 days."] # [doc = " Default: true"] # [serde (alias = "recent-dates")] # [serde (default)] pub recent_dates : Option < bool > , # [doc = " Show only envelopes that match this query"] # [doc = " Default: None"] # [serde (default)] pub filter : Option < Option < Query > > , # [serde (alias = "index-style")] # [serde (default)] pub index_style : Option < IndexStyle > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling_leaf : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling_leaf : Option < Option < String > > , # [doc = " Default: ' '"] # [serde (default)] pub sidebar_divider : Option < char > , # [doc = " Default: 90"] # [serde (default)] pub sidebar_ratio : Option < usize > , # [doc = " Flag to show if thread entry contains unseen mail."] # [doc = " Default: \"\""] # [serde (default)] pub unseen_flag : Option < Option < String > > , # [doc = " Flag to show if thread has been snoozed."] # [doc = " Default: \"💤\""] # [serde (default)] pub thread_snoozed_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry has been selected."] # [doc = " Default: \"\u{fe0f}\""] # [serde (default)] pub selected_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry contains attachments."] # [doc = " Default: \"📎\""] # [serde (default)] pub attachment_flag : Option < Option < String > > , # [doc = " Flag to show if any thread entry contains your address as a receiver."] # [doc = " Useful to make mailing list threads that CC you stand out."] # [doc = " Default: \"\""] # [serde (default)] pub highlight_self_flag : Option < Option < String > > , # [doc = " Show `highlight_self_flag` or not."] # [doc = " Default: false"] # [serde (default)] pub highlight_self : Option < ToggleFlag > , # [doc = " Should threads with different Subjects show a list of those"] # [doc = " subjects on the entry title?"] # [doc = " Default: \"true\""] # [serde (default)] pub thread_subject_pack : Option < bool > , # [doc = " In threaded listing style, repeat identical From column values within a"] # [doc = " thread. Not repeating adds empty space in the From column which"] # [doc = " might result in less visual clutter."] # [doc = " Default: \"false\""] # [serde (default)] pub threaded_repeat_identical_from_values : Option < bool > , # [doc = " Show relative indices in menu mailboxes to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-menu-indices")] # [serde (default)] pub relative_menu_indices : Option < bool > , # [doc = " Show relative indices in listings to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-list-indices")] # [serde (default)] pub relative_list_indices : Option < bool > , # [doc = " Hide sidebar on launch. Default: \"false\""] # [serde (alias = "hide-sidebar-on-launch")] # [serde (default)] pub hide_sidebar_on_launch : Option < bool > } impl Default for ListingSettingsOverride { fn default () -> Self { Self { context_lines : None , show_menu_scrollbar : None , datetime_fmt : None , recent_dates : None , filter : None , index_style : None , sidebar_mailbox_tree_has_sibling : None , sidebar_mailbox_tree_no_sibling : None , sidebar_mailbox_tree_has_sibling_leaf : None , sidebar_mailbox_tree_no_sibling_leaf : None , sidebar_divider : None , sidebar_ratio : None , unseen_flag : None , thread_snoozed_flag : None , selected_flag : None , attachment_flag : None , highlight_self_flag : None , highlight_self : None , thread_subject_pack : None , threaded_repeat_identical_from_values : None , relative_menu_indices : None , relative_list_indices : None , hide_sidebar_on_launch : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct NotificationsSettingsOverride { # [doc = " Enable notifications."] # [doc = " Default: True"] # [serde (default)] pub enable : Option < bool > , # [doc = " A command to pipe notifications through."] # [doc = " Default: None"] # [serde (default)] pub script : Option < Option < String > > , # [doc = " A command to pipe new mail notifications through (preferred over"] # [doc = " `script`). Default: None"] # [serde (default)] pub new_mail_script : Option < Option < String > > , # [doc = " A file location which has its size changed when new mail arrives (max"] # [doc = " 128 bytes). Can be used to trigger new mail notifications eg with"] # [doc = " `xbiff(1)`. Default: None"] # [serde (alias = "xbiff-file-path")] # [serde (default)] pub xbiff_file_path : Option < Option < String > > , # [serde (alias = "play-sound")] # [serde (default)] pub play_sound : Option < ToggleFlag > , # [serde (alias = "sound-file")] # [serde (default)] pub sound_file : Option < Option < String > > } impl Default for NotificationsSettingsOverride { fn default () -> Self { Self { enable : None , script : None , new_mail_script : None , xbiff_file_path : None , play_sound : None , sound_file : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ShortcutsOverride { # [serde (default)] pub general : Option < GeneralShortcuts > , # [serde (default)] pub listing : Option < ListingShortcuts > , # [serde (default)] pub composing : Option < ComposingShortcuts > , # [serde (alias = "contact-list")] # [serde (default)] pub contact_list : Option < ContactListShortcuts > , # [serde (alias = "envelope-view")] # [serde (default)] pub envelope_view : Option < EnvelopeViewShortcuts > , # [serde (alias = "thread-view")] # [serde (default)] pub thread_view : Option < ThreadViewShortcuts > , # [serde (default)] pub pager : Option < PagerShortcuts > } impl Default for ShortcutsOverride { fn default () -> Self { Self { general : None , listing : None , composing : None , contact_list : None , envelope_view : None , thread_view : None , pager : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ComposingSettingsOverride { # [doc = " A command to pipe new emails to"] # [doc = " Required"] # [serde (default)] pub send_mail : Option < SendMail > , # [doc = " Command to launch editor. Can have arguments. Draft filename is given as"] # [doc = " the last argument. If it's missing, the environment variable $EDITOR is"] # [doc = " looked up."] # [serde (alias = "editor-command" , alias = "editor-cmd" , alias = "editor_cmd")] # [serde (default)] pub editor_command : Option < Option < String > > , # [doc = " Embedded editor (for terminal interfaces) instead of forking and"] # [doc = " waiting."] # [serde (alias = "embed")] # [serde (default)] pub embedded_pty : Option < bool > , # [doc = " Set \"format=flowed\" in plain text attachments."] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Set User-Agent"] # [doc = " Default: empty"] # [serde (alias = "insert_user_agent")] # [serde (default)] pub insert_user_agent : Option < bool > , # [doc = " Set default header values for new drafts"] # [doc = " Default: empty"] # [serde (alias = "default-header-values")] # [serde (default)] pub default_header_values : Option < HashMap < HeaderName , String > > , # [doc = " Wrap header preamble when editing a draft in an editor. This allows you"] # [doc = " to write non-plain text email without the preamble creating syntax"] # [doc = " errors. They are stripped when you return from the editor. The"] # [doc = " values should be a two element array of strings, a prefix and suffix."] # [doc = " Default: None"] # [serde (alias = "wrap-header-preamble")] # [serde (default)] pub wrap_header_preamble : Option < Option < (String , String) > > , # [doc = " Store sent mail after successful submission. This setting is meant to be"] # [doc = " disabled for non-standard behaviour in gmail, which auto-saves sent"] # [doc = " mail on its own. Default: true"] # [serde (default)] pub store_sent_mail : Option < bool > , # [doc = " The attribution line appears above the quoted reply text."] # [doc = " The format specifiers for the replied address are:"] # [doc = " - `%+f` — the sender's name and email address."] # [doc = " - `%+n` — the sender's name (or email address, if no name is included)."] # [doc = " - `%+a` — the sender's email address."] # [doc = " The format string is passed to strftime(3) with the replied envelope's"] # [doc = " date. Default: \"On %a, %0e %b %Y %H:%M, %+f wrote:%n\""] # [serde (default)] pub attribution_format_string : Option < Option < String > > , # [doc = " Whether the strftime call for the attribution string uses the POSIX"] # [doc = " locale instead of the user's active locale"] # [doc = " Default: true"] # [serde (default)] pub attribution_use_posix_locale : Option < bool > , # [doc = " Forward emails as attachment? (Alternative is inline)"] # [doc = " Default: ask"] # [serde (alias = "forward-as-attachment")] # [serde (default)] pub forward_as_attachment : Option < ActionFlag > , # [doc = " Alternative lists of reply prefixes (etc. [\"Re:\", \"RE:\", ...]) to strip"] # [doc = " Default: `[\"Re:\", \"RE:\", \"Fwd:\", \"Fw:\", \"回复:\", \"回覆:\", \"SV:\", \"Sv:\","] # [doc = " \"VS:\", \"Antw:\", \"Doorst:\", \"VS:\", \"VL:\", \"REF:\", \"TR:\", \"TR:\", \"AW:\","] # [doc = " \"WG:\", \"ΑΠ:\", \"Απ:\", \"απ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"ΣΧΕΤ:\", \"Σχετ:\","] # [doc = " \"σχετ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"Vá:\", \"Továbbítás:\", \"R:\", \"I:\","] # [doc = " \"RIF:\", \"FS:\", \"BLS:\", \"TRS:\", \"VS:\", \"VB:\", \"RV:\", \"RES:\", \"Res\","] # [doc = " \"ENC:\", \"Odp:\", \"PD:\", \"YNT:\", \"İLT:\", \"ATB:\", \"YML:\"]`"] # [serde (alias = "reply-prefix-list-to-strip")] # [serde (default)] pub reply_prefix_list_to_strip : Option < Option < Vec < String > > > , # [doc = " The prefix to use in reply subjects. The de facto prefix is \"Re:\"."] # [serde (alias = "reply-prefix")] # [serde (default)] pub reply_prefix : Option < String > , # [doc = " Custom `compose-hooks`."] # [serde (alias = "custom-compose-hooks")] # [serde (default)] pub custom_compose_hooks : Option < Vec < ComposeHook > > , # [doc = " Disabled `compose-hooks`."] # [serde (alias = "disabled-compose-hooks")] # [serde (default)] pub disabled_compose_hooks : Option < Vec < String > > } impl Default for ComposingSettingsOverride { fn default () -> Self { Self { send_mail : None , editor_command : None , embedded_pty : None , format_flowed : None , insert_user_agent : None , default_header_values : None , wrap_header_preamble : None , store_sent_mail : None , attribution_format_string : None , attribution_use_posix_locale : None , forward_as_attachment : None , reply_prefix_list_to_strip : None , reply_prefix : None , custom_compose_hooks : None , disabled_compose_hooks : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct TagsSettingsOverride { # [serde (deserialize_with = "tag_color_de")] # [serde (default)] pub colors : Option < HashMap < TagHash , Color > > , # [serde (deserialize_with = "tag_set_de" , alias = "ignore-tags")] # [serde (default)] pub ignore_tags : Option < HashSet < TagHash > > } impl Default for TagsSettingsOverride { fn default () -> Self { Self { colors : None , ignore_tags : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PGPSettingsOverride { # [doc = " auto verify signed e-mail according to RFC3156"] # [doc = " Default: true"] # [serde (alias = "auto-verify-signatures")] # [serde (default)] pub auto_verify_signatures : Option < ActionFlag > , # [doc = " auto decrypt encrypted e-mail"] # [doc = " Default: true"] # [serde (alias = "auto-decrypt")] # [serde (default)] pub auto_decrypt : Option < ActionFlag > , # [doc = " always sign sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-sign")] # [serde (default)] pub auto_sign : Option < ActionFlag > , # [doc = " Auto encrypt sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-encrypt")] # [serde (default)] pub auto_encrypt : Option < ActionFlag > , # [doc = " Default: None"] # [serde (alias = "sign-key")] # [serde (default)] pub sign_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "decrypt-key")] # [serde (default)] pub decrypt_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "encrypt-key")] # [serde (default)] pub encrypt_key : Option < Option < String > > , # [doc = " Allow remote lookups"] # [doc = " Default: False"] # [serde (alias = "allow-remote-lookups")] # [serde (default)] pub allow_remote_lookup : Option < ActionFlag > , # [doc = " Remote lookup mechanisms."] # [doc = " Default: \"local,wkd\""] # [cfg_attr (feature = "gpgme" , serde (alias = "remote-lookup-mechanisms"))] # [cfg (feature = "gpgme")] # [serde (default)] pub remote_lookup_mechanisms : Option < melib :: gpgme :: LocateKey > , # [cfg (not (feature = "gpgme"))] # [cfg_attr (not (feature = "gpgme") , serde (alias = "remote-lookup-mechanisms"))] # [serde (default)] pub remote_lookup_mechanisms : Option < String > } impl Default for PGPSettingsOverride { fn default () -> Self { Self { auto_verify_signatures : None , auto_decrypt : None , auto_sign : None , auto_encrypt : None , sign_key : None , decrypt_key : None , encrypt_key : None , allow_remote_lookup : None , remote_lookup_mechanisms : None } } }

View File

@ -0,0 +1,173 @@
/*
* meli - pager conf module
*
* Copyright 2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Settings for the pager function.
use melib::{Error, Result, ToggleFlag};
use super::{default_vals::*, deserializers::*, DotAddressable};
/// Settings for the pager function.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PagerSettings {
/// Number of context lines when going to next page.
/// Default: 0
#[serde(default = "zero_val", alias = "pager-context")]
pub pager_context: usize,
/// Stop at the end instead of displaying next mail.
/// Default: false
#[serde(default = "false_val", alias = "pager-stop")]
pub pager_stop: bool,
/// Always show headers when scrolling.
/// Default: true
#[serde(
default = "false_val",
alias = "sticky-headers",
/* deprecated names */
alias = "headers-sticky",
alias = "headers_sticky"
)]
pub sticky_headers: bool,
/// The height of the pager in mail view, in percent.
/// Default: 80
#[serde(default = "eighty_val", alias = "pager-ratio")]
pub pager_ratio: usize,
/// A command to pipe mail output through for viewing in pager.
/// Default: None
#[serde(default = "none", deserialize_with = "non_empty_opt_string")]
pub filter: Option<String>,
/// A command to pipe html output before displaying it in a pager
/// Default: None
#[serde(
default = "none",
deserialize_with = "non_empty_opt_string",
alias = "html-filter"
)]
pub html_filter: Option<String>,
/// Respect "format=flowed"
/// Default: true
#[serde(default = "true_val", alias = "format-flowed")]
pub format_flowed: bool,
/// Split long lines that would overflow on the x axis.
/// Default: true
#[serde(default = "true_val", alias = "split-long-lines")]
pub split_long_lines: bool,
/// Minimum text width in columns.
/// Default: 80
#[serde(default = "eighty_val", alias = "minimum-width")]
pub minimum_width: usize,
/// Choose `text/html` alternative if `text/plain` is empty in
/// `multipart/alternative` attachments.
/// Default: true
#[serde(
default = "internal_value_true",
alias = "auto-choose-multipart-alternative"
)]
pub auto_choose_multipart_alternative: ToggleFlag,
/// Show Date: in my timezone
/// Default: true
#[serde(default = "internal_value_true", alias = "show-date-in-my-timezone")]
pub show_date_in_my_timezone: ToggleFlag,
/// A command to launch URLs with. The URL will be given as the first
/// argument of the command. Default: None
#[serde(default = "none", deserialize_with = "non_empty_opt_string")]
pub url_launcher: Option<String>,
/// A command to open html files.
/// Default: None
#[serde(
default = "none",
deserialize_with = "non_empty_opt_string",
alias = "html-open"
)]
pub html_open: Option<String>,
/// Extra headers to display, if present, in the default header preamble.
/// Default: []
#[serde(default = "Vec::new", alias = "show-extra-headers")]
pub show_extra_headers: Vec<String>,
}
impl Default for PagerSettings {
fn default() -> Self {
Self {
pager_context: 0,
pager_stop: false,
sticky_headers: false,
pager_ratio: 80,
filter: None,
html_filter: None,
html_open: None,
format_flowed: true,
split_long_lines: true,
minimum_width: 80,
auto_choose_multipart_alternative: ToggleFlag::InternalVal(true),
show_date_in_my_timezone: ToggleFlag::InternalVal(true),
url_launcher: None,
show_extra_headers: vec![],
}
}
}
impl DotAddressable for PagerSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"pager_context" => self.pager_context.lookup(field, tail),
"pager_stop" => self.pager_stop.lookup(field, tail),
"sticky_headers" => self.sticky_headers.lookup(field, tail),
"pager_ratio" => self.pager_ratio.lookup(field, tail),
"filter" => self.filter.lookup(field, tail),
"html_filter" => self.html_filter.lookup(field, tail),
"html_open" => self.html_open.lookup(field, tail),
"format_flowed" => self.format_flowed.lookup(field, tail),
"split_long_lines" => self.split_long_lines.lookup(field, tail),
"minimum_width" => self.minimum_width.lookup(field, tail),
"auto_choose_multipart_alternative" => {
self.auto_choose_multipart_alternative.lookup(field, tail)
}
"show_date_in_my_timezone" => self.show_date_in_my_timezone.lookup(field, tail),
"url_launcher" => self.html_filter.lookup(field, tail),
"show_extra_headers" => self.show_extra_headers.lookup(field, tail),
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}

View File

@ -0,0 +1,112 @@
/*
* meli - configuration module.
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::conf::ActionFlag;
use super::default_vals::*;
/// Settings for digital signing and encryption
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PGPSettings {
/// auto verify signed e-mail according to RFC3156
/// Default: true
#[serde(default = "true_val", alias = "auto-verify-signatures")]
pub auto_verify_signatures: ActionFlag,
/// auto decrypt encrypted e-mail
/// Default: true
#[serde(default = "true_val", alias = "auto-decrypt")]
pub auto_decrypt: ActionFlag,
/// always sign sent e-mail
/// Default: false
#[serde(default = "false_val", alias = "auto-sign")]
pub auto_sign: ActionFlag,
/// Auto encrypt sent e-mail
/// Default: false
#[serde(default = "false_val", alias = "auto-encrypt")]
pub auto_encrypt: ActionFlag,
// https://tools.ietf.org/html/rfc4880#section-12.2
/// Default: None
#[serde(default = "none", alias = "sign-key")]
pub sign_key: Option<String>,
/// Default: None
#[serde(default = "none", alias = "decrypt-key")]
pub decrypt_key: Option<String>,
/// Default: None
#[serde(default = "none", alias = "encrypt-key")]
pub encrypt_key: Option<String>,
/// Allow remote lookups
/// Default: False
#[serde(
default = "action_internal_value_false",
alias = "allow-remote-lookups"
)]
pub allow_remote_lookup: ActionFlag,
/// Remote lookup mechanisms.
/// Default: "local,wkd"
#[cfg_attr(
feature = "gpgme",
serde(
default = "default_lookup_mechanism",
alias = "remote-lookup-mechanisms"
)
)]
#[cfg(feature = "gpgme")]
pub remote_lookup_mechanisms: melib::gpgme::LocateKey,
#[cfg(not(feature = "gpgme"))]
#[cfg_attr(
not(feature = "gpgme"),
serde(default, alias = "remote-lookup-mechanisms")
)]
pub remote_lookup_mechanisms: String,
}
#[cfg(feature = "gpgme")]
fn default_lookup_mechanism() -> melib::gpgme::LocateKey {
melib::gpgme::LocateKey::LOCAL | melib::gpgme::LocateKey::WKD
}
impl Default for PGPSettings {
fn default() -> Self {
Self {
auto_verify_signatures: true.into(),
auto_decrypt: true.into(),
auto_sign: false.into(),
auto_encrypt: false.into(),
sign_key: None,
decrypt_key: None,
encrypt_key: None,
allow_remote_lookup: action_internal_value_false::<ActionFlag>(),
#[cfg(feature = "gpgme")]
remote_lookup_mechanisms: default_lookup_mechanism(),
#[cfg(not(feature = "gpgme"))]
remote_lookup_mechanisms: String::new(),
}
}
}

View File

@ -0,0 +1,281 @@
/*
* meli - configuration module.
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use indexmap::IndexMap;
use melib::{Error, Result};
use super::DotAddressable;
use crate::terminal::Key;
#[macro_export]
macro_rules! shortcut {
($key:ident == $shortcuts:ident[$section:expr][$val:literal]) => {
$shortcuts
.get($section)
.and_then(|s| s.get($val).map(|v| v == $key))
.unwrap_or(false)
};
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Shortcuts {
#[serde(default)]
pub general: GeneralShortcuts,
#[serde(default)]
pub listing: ListingShortcuts,
#[serde(default)]
pub composing: ComposingShortcuts,
#[serde(default, alias = "contact-list")]
pub contact_list: ContactListShortcuts,
#[serde(default, alias = "envelope-view")]
pub envelope_view: EnvelopeViewShortcuts,
#[serde(default, alias = "thread-view")]
pub thread_view: ThreadViewShortcuts,
#[serde(default)]
pub pager: PagerShortcuts,
}
impl Shortcuts {
pub const GENERAL: &'static str = "general";
pub const LISTING: &'static str = "listing";
pub const COMPOSING: &'static str = "composing";
pub const CONTACT_LIST: &'static str = "contact_list";
pub const ENVELOPE_VIEW: &'static str = "envelope_view";
pub const THREAD_VIEW: &'static str = "thread_view";
pub const PAGER: &'static str = "pager";
}
impl DotAddressable for Shortcuts {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"general" => self.general.lookup(field, tail),
"listing" => self.listing.lookup(field, tail),
"composing" => self.composing.lookup(field, tail),
"contact_list" | "contact-list" => self.contact_list.lookup(field, tail),
"envelope_view" | "envelope-view" => self.envelope_view.lookup(field, tail),
"thread_view" | "thread-view" => self.thread_view.lookup(field, tail),
"pager" => self.pager.lookup(field, tail),
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandShortcut {
pub shortcut: Key,
pub command: Vec<String>,
}
/// Create a struct holding all of a Component's shortcuts.
#[macro_export]
macro_rules! shortcut_key_values {
(
$cname:expr,
$(#[$outer:meta])*
pub struct $name:ident { $($fname:ident |> $fdesc:literal |> $default:expr),* }) => {
$(#[$outer])*
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
#[serde(rename = $cname)]
pub struct $name {
pub commands: Vec<CommandShortcut>,
$(pub $fname : Key),*
}
impl $name {
/// Returns a shortcut's description
pub fn key_desc(&self, key: &str) -> &'static str {
match key {
$(stringify!($fname) => $fdesc),*,
_ => unreachable!()
}
}
/// Returns a hashmap of all shortcuts and their values
pub fn key_values(&self) -> IndexMap<&'static str, Key> {
[
$((stringify!($fname),(self.$fname).clone()),)*
].iter().cloned().collect()
}
}
impl Default for $name {
fn default() -> Self {
Self {
commands : vec![],
$($fname: $default),*
}
}
}
impl DotAddressable for $name {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
$(stringify!($fname) => self.$fname.lookup(field, tail),)*
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}
}
}
shortcut_key_values! { "listing",
/// Shortcut listing for a mail listing.
pub struct ListingShortcuts {
scroll_up |> "Scroll up list." |> Key::Char('k'),
scroll_down |> "Scroll down list." |> Key::Char('j'),
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
next_account |> "Go to next account." |> Key::Char('H'),
next_mailbox |> "Go to next mailbox." |> Key::Char('J'),
next_page |> "Go to next page." |> Key::PageDown,
prev_account |> "Go to previous account." |> Key::Char('L'),
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
open_mailbox |> "Open selected mailbox." |> Key::Char('\n'),
toggle_mailbox_collapse |> "Toggle mailbox collapse in menu." |> Key::Char(' '),
prev_page |> "Go to previous page." |> Key::PageUp,
search |> "Search within list of e-mails." |> Key::Char('/'),
refresh |> "Manually request a mailbox refresh." |> Key::F(5),
set_seen |> "Set thread as seen." |> Key::Char('n'),
union_modifier |> "Union modifier." |> Key::Ctrl('u'),
diff_modifier |> "Difference modifier." |> Key::Ctrl('d'),
intersection_modifier |> "Intersection modifier." |> Key::Ctrl('i'),
select_entry |> "Select thread entry." |> Key::Char('v'),
increase_sidebar |> "Increase sidebar width." |> Key::Ctrl('f'),
decrease_sidebar |> "Decrease sidebar width." |> Key::Ctrl('d'),
next_entry |> "Focus on next entry." |> Key::Ctrl('n'),
previous_entry |> "Focus on previous entry." |> Key::Ctrl('p'),
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`'),
focus_left |> "Switch focus on the left." |> Key::Left,
focus_right |> "Switch focus on the right." |> Key::Right,
exit_entry |> "Exit e-mail entry." |> Key::Char('i'),
open_entry |> "Open e-mail entry." |> Key::Char('\n')
}
}
shortcut_key_values! { "contact-list",
/// Shortcut listing for the contact list view
pub struct ContactListShortcuts {
scroll_up |> "Scroll up list." |> Key::Char('k'),
scroll_down |> "Scroll down list." |> Key::Char('j'),
create_contact |> "Create new contact." |> Key::Char('c'),
edit_contact |> "Edit contact under cursor." |> Key::Char('e'),
delete_contact |> "Delete contact under cursor." |> Key::Char('d'),
mail_contact |> "Mail contact under cursor." |> Key::Char('m'),
next_account |> "Go to next account." |> Key::Char('H'),
prev_account |> "Go to previous account." |> Key::Char('L'),
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`')
}
}
shortcut_key_values! { "pager",
/// Shortcut listing for the text pager
pub struct PagerShortcuts {
page_down |> "Go to next pager page." |> Key::PageDown,
page_up |> "Go to previous pager page." |> Key::PageUp,
scroll_down |> "Scroll down pager." |> Key::Char('j'),
scroll_up |> "Scroll up pager." |> Key::Char('k')
}
}
shortcut_key_values! { "general",
pub struct GeneralShortcuts {
toggle_help |> "Toggle help and shortcuts view." |> Key::Char('?'),
enter_command_mode |> "Enter COMMAND mode." |> Key::Char(':'),
quit |> "Quit meli." |> Key::Char('q'),
go_to_tab |> "Go to the nth tab." |> Key::Alt('n'),
next_tab |> "Go to the next tab." |> Key::Char('T'),
scroll_right |> "Generic scroll right (catch-all setting)" |> Key::Char('l'),
scroll_left |> "Generic scroll left (catch-all setting)" |>Key::Char('h'),
scroll_up |> "Generic scroll up (catch-all setting)" |> Key::Char('k'),
scroll_down |> "Generic scroll down (catch-all setting)" |> Key::Char('j'),
next_page |> "Go to next page. (catch-all setting)" |> Key::PageDown,
prev_page |> "Go to previous page. (catch-all setting)" |> Key::PageUp,
home_page |> "Go to first page. (catch-all setting)" |> Key::Home,
end_page |> "Go to last page. (catch-all setting)" |> Key::End,
open_entry |> "Open list entry. (catch-all setting)" |> Key::Char('\n'),
info_message_next |> "Show next info message, if any." |> Key::Alt('>'),
info_message_previous |> "Show previous info message, if any." |> Key::Alt('<'),
focus_in_text_field |> "Focus on a text field." |> Key::Char('\n'),
next_search_result |> "Scroll to next search result." |> Key::Char('n'),
previous_search_result |> "Scroll to previous search result." |> Key::Char('N')
}
}
shortcut_key_values! { "composing",
pub struct ComposingShortcuts {
edit |> "Edit." |> Key::Char('e'),
send_mail |> "Deliver draft to mailer." |> Key::Char('s'),
scroll_up |> "Change field focus." |> Key::Char('k'),
scroll_down |> "Change field focus." |> Key::Char('j')
}
}
shortcut_key_values! { "envelope-view",
pub struct EnvelopeViewShortcuts {
add_addresses_to_contacts |> "Select addresses from envelope to add to contacts." |> Key::Char('c'),
edit |> "Open envelope in composer." |> Key::Char('e'),
go_to_url |> "Go to url of given index." |> Key::Char('g'),
open_attachment |> "Opens selected attachment with xdg-open." |> Key::Char('a'),
open_mailcap |> "Opens selected attachment according to its mailcap entry." |> Key::Char('m'),
open_html |> "Opens html attachment in the default browser." |> Key::Char('v'),
reply |> "Reply to envelope." |> Key::Char('R'),
reply_to_author |> "Reply to author." |> Key::Ctrl('r'),
reply_to_all |> "Reply to all/Reply to list/Follow up." |> Key::Ctrl('g'),
forward |> "Forward email." |> Key::Ctrl('f'),
return_to_normal_view |> "Return to envelope if viewing raw source or attachment." |> Key::Char('r'),
toggle_expand_headers |> "Expand extra headers (References and others)." |> Key::Char('h'),
toggle_url_mode |> "Toggles url open mode." |> Key::Char('u'),
view_raw_source |> "View envelope source in a pager. (toggles between raw and decoded source)" |> Key::Alt('r'),
change_charset |> "Force attachment charset for decoding." |> Key::Char('d')
}
}
shortcut_key_values! { "thread-view",
pub struct ThreadViewShortcuts {
scroll_up |> "Scroll up list." |> Key::Char('k'),
scroll_down |> "Scroll down list." |> Key::Char('j'),
collapse_subtree |> "collapse thread branches." |> Key::Char('h'),
next_page |> "Go to next page." |> Key::PageDown,
prev_page |> "Go to previous page." |> Key::PageUp,
reverse_thread_order |> "reverse thread order." |> Key::Ctrl('r'),
toggle_mailview |> "toggle mail view visibility." |> Key::Char('p'),
toggle_threadview |> "toggle thread view visibility." |> Key::Char('t'),
toggle_layout |> "Toggle between horizontal and vertical layout." |> Key::Char(' ')
}
}

View File

@ -0,0 +1,99 @@
/*
* meli - configuration module.
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! E-mail tag configuration and {de,}serializing.
use std::collections::{HashMap, HashSet};
use melib::{Error, Result, TagHash};
use serde::{Deserialize, Deserializer};
use super::DotAddressable;
use crate::terminal::Color;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TagsSettings {
#[serde(default, deserialize_with = "tag_color_de")]
pub colors: HashMap<TagHash, Color>,
#[serde(default, deserialize_with = "tag_set_de", alias = "ignore-tags")]
pub ignore_tags: HashSet<TagHash>,
}
pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<TagHash>>>(
deserializer: D,
) -> std::result::Result<T, D::Error>
where
D: Deserializer<'de>,
{
Ok(<Vec<String>>::deserialize(deserializer)?
.into_iter()
.map(|tag| TagHash::from_bytes(tag.as_bytes()))
.collect::<HashSet<TagHash>>()
.into())
}
pub fn tag_color_de<'de, D, T: std::convert::From<HashMap<TagHash, Color>>>(
deserializer: D,
) -> std::result::Result<T, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum _Color {
B(u8),
C(Color),
}
Ok(<HashMap<String, _Color>>::deserialize(deserializer)?
.into_iter()
.map(|(tag, color)| {
(
TagHash::from_bytes(tag.as_bytes()),
match color {
_Color::B(b) => Color::Byte(b),
_Color::C(c) => c,
},
)
})
.collect::<HashMap<TagHash, Color>>()
.into())
}
impl DotAddressable for TagsSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"colors" => self.colors.lookup(field, tail),
"ignore_tags" => self.ignore_tags.lookup(field, tail),
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}

View File

@ -0,0 +1,127 @@
/*
* meli - configuration module.
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Settings for terminal display
use melib::{Error, Result, ToggleFlag};
use super::{deserializers::non_empty_opt_string, DotAddressable, Themes};
/// Settings for terminal display
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TerminalSettings {
/// light, dark
pub theme: String,
pub themes: Themes,
pub ascii_drawing: bool,
pub use_color: ToggleFlag,
/// Use mouse events. This will disable text selection, but you will be able
/// to resize some widgets.
/// Default: False
pub use_mouse: ToggleFlag,
/// String to show in status bar if mouse is active.
/// Default: "🖱️ "
#[serde(deserialize_with = "non_empty_opt_string")]
pub mouse_flag: Option<String>,
#[serde(deserialize_with = "non_empty_opt_string")]
pub window_title: Option<String>,
#[serde(deserialize_with = "non_empty_opt_string")]
pub file_picker_command: Option<String>,
/// Choose between 30-something built in sequences (integers between 0-30)
/// or define your own list of strings for the progress spinner
/// animation. Default: 0
#[serde(default)]
pub progress_spinner_sequence: Option<ProgressSpinnerSequence>,
}
impl Default for TerminalSettings {
fn default() -> Self {
Self {
theme: "dark".to_string(),
themes: Themes::default(),
ascii_drawing: false,
use_color: ToggleFlag::InternalVal(true),
use_mouse: ToggleFlag::InternalVal(false),
mouse_flag: Some("🖱️ ".to_string()),
window_title: Some("meli".to_string()),
file_picker_command: None,
progress_spinner_sequence: None,
}
}
}
impl TerminalSettings {
pub fn use_color(&self) -> bool {
/* Don't use color if
* - Either NO_COLOR is set and user hasn't explicitly set use_colors or
* - User has explicitly set use_colors to false
*/
!((std::env::var("NO_COLOR").is_ok()
&& (self.use_color.is_false() || self.use_color.is_internal()))
|| (self.use_color.is_false() && !self.use_color.is_internal()))
}
}
impl DotAddressable for TerminalSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() {
Some(field) => {
let tail = &path[1..];
match *field {
"theme" => self.theme.lookup(field, tail),
"themes" => Err(Error::new("unimplemented")),
"ascii_drawing" => self.ascii_drawing.lookup(field, tail),
"use_color" => self.use_color.lookup(field, tail),
"use_mouse" => self.use_mouse.lookup(field, tail),
"mouse_flag" => self.mouse_flag.lookup(field, tail),
"window_title" => self.window_title.lookup(field, tail),
"file_picker_command" => self.file_picker_command.lookup(field, tail),
"progress_spinner_sequence" => {
self.progress_spinner_sequence.lookup(field, tail)
}
other => Err(Error::new(format!(
"{} has no field named {}",
parent_field, other
))),
}
}
None => Ok(toml::to_string(self).map_err(|err| err.to_string())?),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ProgressSpinnerSequence {
Integer(usize),
Custom {
frames: Vec<String>,
#[serde(default = "interval_ms_val")]
interval_ms: u64,
},
}
const fn interval_ms_val() -> u64 {
crate::utilities::ProgressSpinner::INTERVAL_MS
}
impl DotAddressable for ProgressSpinnerSequence {}

File diff suppressed because it is too large Load Diff

View File

@ -19,61 +19,53 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::*;
use fnv::FnvHashMap;
use indexmap::IndexMap;
use melib::Card;
mod contact_list;
pub use self::contact_list::*;
use crate::{
terminal::*, CellBuffer, Component, ComponentId, Context, Field, FormWidget, Key, StatusEvent,
ThemeAttribute, UIDialog, UIEvent,
};
#[derive(Debug)]
enum ViewMode {
ReadOnly,
Discard(Selector<char>),
Discard(Box<UIDialog<char>>),
Edit,
//New,
}
#[derive(Debug)]
pub struct ContactManager {
id: ComponentId,
parent_id: ComponentId,
parent_id: Option<ComponentId>,
pub card: Card,
mode: ViewMode,
form: FormWidget,
account_pos: usize,
content: CellBuffer,
form: FormWidget<bool>,
pub account_pos: usize,
content: Screen<Virtual>,
theme_default: ThemeAttribute,
dirty: bool,
has_changes: bool,
initialized: bool,
}
impl fmt::Display for ContactManager {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "contacts")
impl std::fmt::Display for ContactManager {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.card)
}
}
impl ContactManager {
fn new(context: &Context) -> Self {
pub fn new(context: &Context) -> Self {
let theme_default: ThemeAttribute = crate::conf::value(context, "theme_default");
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(theme_default.fg)
.set_bg(theme_default.bg)
.set_attrs(theme_default.attrs);
ret
};
ContactManager {
id: Uuid::nil(),
parent_id: Uuid::nil(),
Self {
id: ComponentId::default(),
parent_id: None,
card: Card::new(),
mode: ViewMode::Edit,
form: FormWidget::default(),
account_pos: 0,
content: CellBuffer::new(100, 1, default_cell),
content: Screen::<Virtual>::new(),
theme_default,
dirty: true,
has_changes: false,
@ -81,44 +73,49 @@ impl ContactManager {
}
}
fn initialize(&mut self) {
let (width, _) = self.content.size();
fn initialize(&mut self, context: &Context) {
if !self.content.resize_with_context(100, 1, context) {
return;
}
let mut area = self.content.area();
let (x, _) = write_string_to_grid(
let (x, _) = self.content.grid_mut().write_string(
"Last edited: ",
&mut self.content,
Color::Byte(250),
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
((0, 0), (width - 1, 0)),
area,
None,
);
let (x, y) = write_string_to_grid(
area = area.skip_cols(x);
let (x, y) = self.content.grid_mut().write_string(
&self.card.last_edited(),
&mut self.content,
Color::Byte(250),
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
((x, 0), (width - 1, 0)),
area,
None,
);
area = area.skip(x, y);
if self.card.external_resource() {
self.mode = ViewMode::ReadOnly;
self.content
.resize(self.content.size().0, 2, Cell::default());
write_string_to_grid(
self.content.grid_mut().write_string(
"This contact's origin is external and cannot be edited within meli.",
&mut self.content,
Color::Byte(250),
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
((x, y), (width - 1, y)),
area,
None,
);
}
self.form = FormWidget::new("Save".into());
self.form = FormWidget::new(
("Save".into(), true),
/* cursor_up_shortcut */ context.settings.shortcuts.general.scroll_up.clone(),
/* cursor_down_shortcut */
context.settings.shortcuts.general.scroll_down.clone(),
);
self.form.add_button(("Cancel(Esc)".into(), false));
self.form
.push(("NAME".into(), self.card.name().to_string()));
@ -135,84 +132,65 @@ impl ContactManager {
self.form.push(("URL".into(), self.card.url().to_string()));
self.form.push(("KEY".into(), self.card.key().to_string()));
for (k, v) in self.card.extra_properties() {
self.form.push((k.to_string(), v.to_string()));
self.form.push((k.to_string().into(), v.to_string()));
}
}
pub fn set_parent_id(&mut self, new_val: ComponentId) {
self.parent_id = new_val;
self.parent_id = Some(new_val);
}
}
impl Component for ContactManager {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.initialized {
self.initialize();
self.initialize(context);
self.initialized = true;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
if self.dirty {
let (width, _height) = self.content.size();
clear_area(
grid,
(upper_left, set_y(bottom_right, get_y(upper_left) + 1)),
self.theme_default,
);
copy_area_with_break(grid, &self.content, area, ((0, 0), (width - 1, 0)));
if self.is_dirty() {
grid.clear_area(area, self.theme_default);
grid.copy_area(self.content.grid(), area.skip_rows(2), self.content.area());
self.dirty = false;
}
self.form.draw(
grid,
(set_y(upper_left, get_y(upper_left) + 2), bottom_right),
area.skip_rows(2 + self.content.area().height()),
context,
);
match self.mode {
ViewMode::Discard(ref mut selector) => {
/* Let user choose whether to quit with/without saving or cancel */
selector.draw(grid, center_area(area, selector.content.size()), context);
}
_ => {}
if let ViewMode::Discard(ref mut selector) = self.mode {
/* Let user choose whether to quit with/without saving or cancel */
selector.draw(grid, area, context);
}
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::ConfigReload { old_settings: _ } = event {
self.theme_default = crate::conf::value(context, "theme_default");
self.content.grid_mut().empty();
self.initialized = false;
self.set_dirty(true);
}
match self.mode {
ViewMode::Discard(ref mut selector) => {
if matches!(event, UIEvent::ComponentUnrealize(ref id) if *id == selector.id()) {
selector.unrealize(context);
self.mode = ViewMode::Edit;
self.set_dirty(true);
return true;
}
if selector.process_event(event, context) {
if selector.is_done() {
let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) {
ViewMode::Discard(s) => s,
_ => unreachable!(),
};
let key = s.collect()[0] as char;
match key {
'x' => {
context
.replies
.push_back(UIEvent::Action(Tab(Kill(self.parent_id))));
return true;
}
'n' => {}
'y' => {}
_ => {}
}
}
self.set_dirty(true);
return true;
}
}
ViewMode::Edit => {
if let &mut UIEvent::Input(Key::Esc) = event {
if matches!(event, UIEvent::Input(Key::Esc)) {
if self.can_quit_cleanly(context) {
context
.replies
.push_back(UIEvent::Action(Tab(Kill(self.parent_id))));
self.unrealize(context);
}
return true;
}
@ -220,17 +198,15 @@ impl Component for ContactManager {
match self.form.buttons_result() {
None => {}
Some(true) => {
let fields = std::mem::replace(&mut self.form, FormWidget::default())
.collect()
.unwrap();
let fields: FnvHashMap<String, String> = fields
let fields = std::mem::take(&mut self.form).collect().unwrap();
let fields: IndexMap<String, String> = fields
.into_iter()
.map(|(s, v)| {
(
s,
s.to_string(),
match v {
Field::Text(v, _) => v.as_str().to_string(),
Field::Choice(mut v, c) => v.remove(c),
Field::Text(v) => v.as_str().to_string(),
Field::Choice(mut v, c, _) => v.remove(c).to_string(),
},
)
})
@ -243,23 +219,23 @@ impl Component for ContactManager {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage("Saved.".into()),
));
context.replies.push_back(UIEvent::ComponentKill(self.id));
self.unrealize(context);
}
Some(false) => {
context.replies.push_back(UIEvent::ComponentKill(self.id));
self.unrealize(context);
}
}
self.set_dirty(true);
if let UIEvent::InsertInput(_) = event {
if matches!(event, UIEvent::InsertInput(_)) {
self.has_changes = true;
}
return true;
}
}
ViewMode::ReadOnly => {
if let &mut UIEvent::Input(Key::Esc) = event {
if matches!(event, UIEvent::Input(Key::Esc)) {
if self.can_quit_cleanly(context) {
context.replies.push_back(UIEvent::ComponentKill(self.id));
self.unrealize(context);
}
return true;
}
@ -271,11 +247,7 @@ impl Component for ContactManager {
fn is_dirty(&self) -> bool {
self.dirty
|| self.form.is_dirty()
|| if let ViewMode::Discard(ref selector) = self.mode {
selector.is_dirty()
} else {
false
}
|| matches!(self.mode, ViewMode::Discard(ref selector) if selector.is_dirty())
}
fn set_dirty(&mut self, value: bool) {
@ -290,27 +262,36 @@ impl Component for ContactManager {
self.id
}
fn set_id(&mut self, id: ComponentId) {
self.id = id;
}
fn can_quit_cleanly(&mut self, context: &Context) -> bool {
if !self.has_changes {
return true;
}
/* Play it safe and ask user for confirmation */
self.mode = ViewMode::Discard(Selector::new(
"this contact has unsaved changes",
vec![
('x', "quit without saving".to_string()),
('y', "save draft and quit".to_string()),
('n', "cancel".to_string()),
],
true,
context,
));
self.set_dirty(true);
false
if matches!(self.mode, ViewMode::Discard(_)) {
true
} else {
let Some(parent_id) = self.parent_id else {
return true;
};
/* Play it safe and ask user for confirmation */
self.mode = ViewMode::Discard(Box::new(UIDialog::new(
"this contact has unsaved changes",
vec![
('y', "quit without saving".to_string()),
('n', "cancel".to_string()),
],
true,
Some(Box::new(move |id, results: &[char]| {
if matches!(results.first(), Some(&'y')) {
Some(UIEvent::ComponentUnrealize(parent_id))
} else {
Some(UIEvent::ComponentUnrealize(id))
}
})),
context,
)));
self.set_dirty(true);
false
}
}
}

View File

@ -0,0 +1,23 @@
/*
* meli
*
* Copyright 2023 - Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
pub mod editor;
pub mod list;

460
meli/src/jobs.rs 100644
View File

@ -0,0 +1,460 @@
/*
* meli - jobs executor
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Async job executor thread pool
use std::{
borrow::Cow,
future::Future,
iter,
panic::catch_unwind,
sync::{Arc, Mutex},
thread,
time::Duration,
};
use crossbeam::{
channel::Sender,
deque::{Injector, Stealer, Worker},
sync::{Parker, Unparker},
};
pub use futures::channel::oneshot;
use indexmap::IndexMap;
use melib::{log, smol, utils::datetime, uuid::Uuid, UnixTimestamp};
use crate::types::{StatusEvent, ThreadEvent, UIEvent};
type AsyncTask = async_task::Runnable;
fn find_task(
local: &Worker<MeliTask>,
global: &Injector<MeliTask>,
stealers: &[Stealer<MeliTask>],
) -> Option<MeliTask> {
// Pop a task from the local queue, if not empty.
local.pop().or_else(|| {
// Otherwise, we need to look for a task elsewhere.
iter::repeat_with(|| {
// Try stealing a batch of tasks from the global queue.
global
.steal_batch_and_pop(local)
// Or try stealing a task from one of the other threads.
.or_else(|| stealers.iter().map(|s| s.steal()).collect())
})
// Loop while no task was stolen and any steal operation needs to be retried.
.find(|s| !s.is_retry())
// Extract the stolen task, if there is one.
.and_then(|s| s.success())
})
}
macro_rules! uuid_hash_type {
($n:ident) => {
#[derive(PartialEq, Hash, Eq, Copy, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub struct $n(Uuid);
impl std::fmt::Debug for $n {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
}
impl std::fmt::Display for $n {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
}
impl Default for $n {
fn default() -> Self {
Self::new()
}
}
impl $n {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
pub fn null() -> Self {
Self(Uuid::nil())
}
}
};
}
uuid_hash_type!(JobId);
uuid_hash_type!(TimerId);
/// A spawned future and its current state.
pub struct MeliTask {
task: AsyncTask,
id: JobId,
desc: Cow<'static, str>,
timer: bool,
}
#[derive(Clone, Debug)]
/// A spawned future's metadata for book-keeping.
pub struct JobMetadata {
pub id: JobId,
pub desc: Cow<'static, str>,
pub timer: bool,
pub started: UnixTimestamp,
pub finished: Option<UnixTimestamp>,
pub succeeded: bool,
}
#[derive(Debug)]
pub struct JobExecutor {
global_queue: Arc<Injector<MeliTask>>,
workers: Vec<Stealer<MeliTask>>,
sender: Sender<ThreadEvent>,
parkers: Vec<Unparker>,
timers: Arc<Mutex<IndexMap<TimerId, TimerPrivate>>>,
pub jobs: Arc<Mutex<IndexMap<JobId, JobMetadata>>>,
}
#[derive(Debug, Default)]
struct TimerPrivate {
/// Interval for periodic timer.
interval: Duration,
/// Time until next expiration.
value: Duration,
active: bool,
handle: Option<async_task::Task<()>>,
cancel: Arc<Mutex<bool>>,
}
#[derive(Debug)]
pub struct Timer {
id: TimerId,
job_executor: Arc<JobExecutor>,
}
impl Timer {
pub fn id(&self) -> TimerId {
self.id
}
pub fn rearm(&self) {
self.job_executor.rearm(self.id);
}
pub fn disable(&self) {
self.job_executor.disable_timer(self.id);
}
pub fn set_interval(&self, new_val: Duration) {
self.job_executor.set_interval(self.id, new_val);
}
}
impl Drop for Timer {
fn drop(&mut self) {
self.disable();
}
}
impl JobExecutor {
/// A queue that holds scheduled tasks.
pub fn new(sender: Sender<ThreadEvent>) -> Self {
// Create a queue.
let mut ret = Self {
global_queue: Arc::new(Injector::new()),
workers: vec![],
parkers: vec![],
sender,
timers: Arc::new(Mutex::new(IndexMap::default())),
jobs: Arc::new(Mutex::new(IndexMap::default())),
};
let mut workers = vec![];
for _ in 0..num_cpus::get().max(1) {
let new_worker = Worker::new_fifo();
ret.workers.push(new_worker.stealer());
let p = Parker::new();
ret.parkers.push(p.unparker().clone());
workers.push((new_worker, p));
}
// Reactor thread
thread::Builder::new()
.name("meli-reactor".to_string())
.spawn(move || {
let ex = smol::Executor::new();
futures::executor::block_on(ex.run(futures::future::pending::<()>()));
})
.unwrap();
// Spawn executor threads the first time the queue is created.
for (i, (local, parker)) in workers.into_iter().enumerate() {
let global = ret.global_queue.clone();
let stealers = ret.workers.clone();
thread::Builder::new()
.name(format!("meli-executor-{}", i))
.spawn(move || loop {
parker.park_timeout(Duration::from_millis(100));
let task = find_task(&local, &global, stealers.as_slice());
if let Some(meli_task) = task {
let MeliTask {
task,
id,
timer,
desc,
} = meli_task;
if !timer {
log::trace!("Worker {} got task {:?} {:?}", i, desc, id);
}
let _ = catch_unwind(|| task.run());
if !timer {
log::trace!("Worker {} returned after {:?} {:?}", i, desc, id);
}
}
})
.unwrap();
}
ret
}
/// Spawns a future with a generic return value `R`
#[inline(always)]
pub fn spawn_specialized<F, R>(&self, desc: Cow<'static, str>, future: F) -> JoinHandle<R>
where
F: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
let (sender, receiver) = oneshot::channel();
let finished_sender = self.sender.clone();
let job_id = JobId::new();
let injector = self.global_queue.clone();
let cancel = Arc::new(Mutex::new(false));
let cancel2 = cancel.clone();
self.jobs.lock().unwrap().insert(
job_id,
JobMetadata {
id: job_id,
desc: desc.clone(),
started: datetime::now(),
finished: None,
succeeded: true,
timer: false,
},
);
// Create a task and schedule it for execution.
let (handle, task) = async_task::spawn(
async move {
let res = future.await;
let _ = sender.send(res);
finished_sender
.send(ThreadEvent::JobFinished(job_id))
.unwrap();
},
move |task| {
if *cancel.lock().unwrap() {
return;
}
let desc = desc.clone();
injector.push(MeliTask {
task,
id: job_id,
desc,
timer: false,
})
},
);
handle.schedule();
for unparker in self.parkers.iter() {
unparker.unpark();
}
JoinHandle {
task: Arc::new(Mutex::new(Some(task))),
cancel: cancel2,
chan: receiver,
job_id,
}
}
/// Spawns a future with a generic return value `R` that might block on a
/// new thread
#[inline(always)]
pub fn spawn_blocking<F, R>(&self, desc: Cow<'static, str>, future: F) -> JoinHandle<R>
where
F: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
self.spawn_specialized(
desc,
smol::unblock(move || futures::executor::block_on(future)),
)
}
pub fn create_timer(self: Arc<Self>, interval: Duration, value: Duration) -> Timer {
let timer = TimerPrivate {
interval,
cancel: Arc::new(Mutex::new(false)),
value,
active: true,
handle: None,
};
let id = TimerId::default();
self.timers.lock().unwrap().insert(id, timer);
self.arm_timer(id, value);
Timer {
id,
job_executor: self,
}
}
pub fn rearm(&self, timer_id: TimerId) {
let mut timers_lck = self.timers.lock().unwrap();
if let Some(timer) = timers_lck.get_mut(&timer_id) {
let value = timer.value;
drop(timers_lck);
self.arm_timer(timer_id, value);
}
}
fn arm_timer(&self, id: TimerId, value: Duration) {
let job_id = JobId::new();
let sender = self.sender.clone();
let injector = self.global_queue.clone();
let timers = self.timers.clone();
let cancel = Arc::new(Mutex::new(false));
let cancel2 = cancel.clone();
let (task, handle) = async_task::spawn(
async move {
let mut value = value;
loop {
smol::Timer::after(value).await;
sender
.send(ThreadEvent::UIEvent(UIEvent::Timer(id)))
.unwrap();
if let Some(interval) = timers.lock().unwrap().get(&id).and_then(|timer| {
if timer.interval.as_millis() == 0 && timer.interval.as_secs() == 0 {
None
} else if timer.active {
Some(timer.interval)
} else {
None
}
}) {
value = interval;
} else {
break;
}
}
},
move |task| {
if *cancel.lock().unwrap() {
return;
}
injector.push(MeliTask {
task,
id: job_id,
desc: "timer".into(),
timer: true,
})
},
);
self.timers.lock().unwrap().entry(id).and_modify(|timer| {
timer.handle = Some(handle);
timer.cancel = cancel2;
timer.active = true;
});
task.schedule();
for unparker in self.parkers.iter() {
unparker.unpark();
}
}
fn disable_timer(&self, id: TimerId) {
let mut timers_lck = self.timers.lock().unwrap();
if let Some(timer) = timers_lck.get_mut(&id) {
timer.active = false;
*timer.cancel.lock().unwrap() = true;
}
}
fn set_interval(&self, id: TimerId, new_val: Duration) {
let mut timers_lck = self.timers.lock().unwrap();
if let Some(timer) = timers_lck.get_mut(&id) {
timer.interval = new_val;
}
}
pub fn set_job_finished(&self, id: JobId) {
self.jobs.lock().unwrap().entry(id).and_modify(|entry| {
entry.finished = Some(datetime::now());
});
}
pub fn set_job_success(&self, id: JobId, value: bool) {
self.jobs.lock().unwrap().entry(id).and_modify(|entry| {
entry.succeeded = value;
});
}
}
pub type JobChannel<T> = oneshot::Receiver<T>;
/// `JoinHandle` for the future that allows us to cancel the task.
#[derive(Debug)]
pub struct JoinHandle<T> {
pub task: Arc<Mutex<Option<async_task::Task<()>>>>,
pub chan: JobChannel<T>,
pub cancel: Arc<Mutex<bool>>,
pub job_id: JobId,
}
impl<T> JoinHandle<T> {
pub fn cancel(&self) -> Option<StatusEvent> {
let mut lck = self.cancel.lock().unwrap();
if !*lck {
*lck = true;
Some(StatusEvent::JobCanceled(self.job_id))
} else {
None
}
}
}
impl<T> std::cmp::PartialEq<JobId> for JoinHandle<T> {
fn eq(&self, other: &JobId) -> bool {
self.job_id == *other
}
}
/*
use std::pin::Pin;
use std::task::{Context, Poll};
impl Future for JoinHandle {
type Output = Result<()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.inner).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(output) => Poll::Ready(output.expect("task failed")),
}
}
}
*/

View File

@ -0,0 +1,638 @@
/*
* meli
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::borrow::Cow;
use indexmap::IndexMap;
use super::*;
use crate::{
jobs::{JobId, JobMetadata},
melib::{
utils::datetime::{self, formats::RFC3339_DATETIME_AND_SPACE},
SortOrder,
},
};
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
enum Column {
_0 = 0,
_1,
_2,
_3,
_4,
}
const fn _assert_len() {
if JobManager::HEADERS.len() != Column::_4 as usize + 1 {
panic!("JobManager::HEADERS length changed, please update Column enum accordingly.");
}
}
const _: () = _assert_len();
#[derive(Debug)]
pub struct JobManager {
cursor_pos: usize,
new_cursor_pos: usize,
length: usize,
data_columns: DataColumns<5>,
min_width: [usize; 5],
sort_col: Column,
sort_order: SortOrder,
entries: IndexMap<JobId, JobMetadata>,
initialized: bool,
theme_default: ThemeAttribute,
highlight_theme: ThemeAttribute,
dirty: bool,
movement: Option<PageMovement>,
id: ComponentId,
}
impl std::fmt::Display for JobManager {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "jobs")
}
}
impl JobManager {
const HEADERS: [&'static str; 5] = ["id", "desc", "started", "finished", "succeeded"];
pub fn new(context: &Context) -> Self {
let theme_default = crate::conf::value(context, "theme_default");
let highlight_theme = if context.settings.terminal.use_color() {
crate::conf::value(context, "highlight")
} else {
ThemeAttribute {
attrs: Attr::REVERSE,
..ThemeAttribute::default()
}
};
let mut data_columns = DataColumns::default();
data_columns.theme_config.set_single_theme(theme_default);
Self {
cursor_pos: 0,
new_cursor_pos: 0,
entries: IndexMap::default(),
length: 0,
data_columns,
min_width: [0; 5],
sort_col: Column::_2,
sort_order: SortOrder::Desc,
theme_default,
highlight_theme,
initialized: false,
dirty: true,
movement: None,
id: ComponentId::default(),
}
}
fn initialize(&mut self, context: &Context) {
self.set_dirty(true);
let mut entries = (*context.main_loop_handler.job_executor.jobs.lock().unwrap()).clone();
self.length = entries.len();
entries.sort_by(|_, a, _, b| match (self.sort_col, self.sort_order) {
(Column::_0, SortOrder::Asc) => a.id.cmp(&b.id),
(Column::_0, SortOrder::Desc) => b.id.cmp(&b.id),
(Column::_1, SortOrder::Asc) => a.desc.cmp(&b.desc),
(Column::_1, SortOrder::Desc) => b.desc.cmp(&a.desc),
(Column::_2, SortOrder::Asc) => a.started.cmp(&b.started),
(Column::_2, SortOrder::Desc) => b.started.cmp(&a.started),
(Column::_3, SortOrder::Asc) => a.finished.cmp(&b.finished),
(Column::_3, SortOrder::Desc) => b.finished.cmp(&a.finished),
(Column::_4, SortOrder::Asc) if a.finished.is_some() && b.finished.is_some() => {
a.succeeded.cmp(&b.succeeded)
}
(Column::_4, SortOrder::Desc) if a.finished.is_some() && b.finished.is_some() => {
b.succeeded.cmp(&a.succeeded)
}
(Column::_4, SortOrder::Asc) if a.finished.is_none() => std::cmp::Ordering::Less,
(Column::_4, SortOrder::Asc) => std::cmp::Ordering::Greater,
(Column::_4, SortOrder::Desc) if a.finished.is_none() => std::cmp::Ordering::Greater,
(Column::_4, SortOrder::Desc) => std::cmp::Ordering::Less,
});
self.entries = entries;
macro_rules! hdr {
($idx:literal) => {{
Self::HEADERS[$idx].len() + if self.sort_col as u8 == $idx { 1 } else { 0 }
}};
}
self.min_width = [hdr!(0), hdr!(1), hdr!(2), hdr!(3), hdr!(4)];
for c in self.entries.values() {
// title
self.min_width[0] = self.min_width[0].max(c.id.to_string().len());
// desc
self.min_width[1] = self.min_width[1].max(c.desc.len());
}
self.min_width[2] = "1970-01-01 00:00:00".len();
self.min_width[3] = self.min_width[2];
// name column
_ = self.data_columns.columns[0].resize_with_context(
self.min_width[0],
self.length,
context,
);
self.data_columns.columns[0].grid_mut().clear(None);
// path column
_ = self.data_columns.columns[1].resize_with_context(
self.min_width[1],
self.length,
context,
);
self.data_columns.columns[1].grid_mut().clear(None);
// size column
_ = self.data_columns.columns[2].resize_with_context(
self.min_width[2],
self.length,
context,
);
self.data_columns.columns[2].grid_mut().clear(None);
// subscribed column
_ = self.data_columns.columns[3].resize_with_context(
self.min_width[3],
self.length,
context,
);
self.data_columns.columns[3].grid_mut().clear(None);
_ = self.data_columns.columns[4].resize_with_context(
self.min_width[4],
self.length,
context,
);
self.data_columns.columns[4].grid_mut().clear(None);
for (idx, e) in self.entries.values().enumerate() {
{
let area = self.data_columns.columns[0].area().nth_row(idx);
self.data_columns.columns[0].grid_mut().write_string(
&e.id.to_string(),
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
{
let area = self.data_columns.columns[1].area().nth_row(idx);
self.data_columns.columns[1].grid_mut().write_string(
&e.desc,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
{
let area = self.data_columns.columns[2].area().nth_row(idx);
self.data_columns.columns[2].grid_mut().write_string(
&datetime::timestamp_to_string(
e.started,
Some(RFC3339_DATETIME_AND_SPACE),
true,
),
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
{
let area = self.data_columns.columns[3].area().nth_row(idx);
self.data_columns.columns[3].grid_mut().write_string(
&if let Some(t) = e.finished {
Cow::Owned(datetime::timestamp_to_string(
t,
Some(RFC3339_DATETIME_AND_SPACE),
true,
))
} else {
Cow::Borrowed("null")
},
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
{
let area = self.data_columns.columns[4].area().nth_row(idx);
self.data_columns.columns[4].grid_mut().write_string(
&if e.finished.is_some() {
Cow::Owned(format!("{:?}", e.succeeded))
} else {
Cow::Borrowed("-")
},
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
}
if self.length == 0 {
let message = "No jobs.".to_string();
if self.data_columns.columns[0].resize_with_context(message.len(), self.length, context)
{
let area = self.data_columns.columns[0].area();
self.data_columns.columns[0].grid_mut().write_string(
&message,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area,
None,
);
}
}
}
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
let rows = area.height();
if rows == 0 {
return;
}
if self.length == 0 {
grid.clear_area(area, self.theme_default);
grid.copy_area(
self.data_columns.columns[0].grid(),
area,
self.data_columns.columns[0].area(),
);
context.dirty_areas.push_back(area);
return;
}
if let Some(mvm) = self.movement.take() {
match mvm {
PageMovement::Up(amount) => {
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(amount);
}
PageMovement::PageUp(multiplier) => {
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(rows * multiplier);
}
PageMovement::Down(amount) => {
if self.new_cursor_pos + amount < self.length {
self.new_cursor_pos += amount;
} else {
self.new_cursor_pos = self.length - 1;
}
}
PageMovement::PageDown(multiplier) => {
#[allow(clippy::comparison_chain)]
if self.new_cursor_pos + rows * multiplier < self.length {
self.new_cursor_pos += rows * multiplier;
} else if self.new_cursor_pos + rows * multiplier > self.length {
self.new_cursor_pos = self.length - 1;
} else {
self.new_cursor_pos = (self.length / rows) * rows;
}
}
PageMovement::Right(amount) => {
self.data_columns.x_offset += amount;
self.data_columns.x_offset = self.data_columns.x_offset.min(
self.data_columns
.widths
.iter()
.map(|w| w + 2)
.sum::<usize>()
.saturating_sub(2),
);
}
PageMovement::Left(amount) => {
self.data_columns.x_offset = self.data_columns.x_offset.saturating_sub(amount);
}
PageMovement::Home => {
self.new_cursor_pos = 0;
}
PageMovement::End => {
self.new_cursor_pos = self.length - 1;
}
}
}
let prev_page_no = (self.cursor_pos).wrapping_div(rows);
let page_no = (self.new_cursor_pos).wrapping_div(rows);
let top_idx = page_no * rows;
if self.length >= rows {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
ScrollUpdate::Update {
id: self.id,
context: ScrollContext {
shown_lines: top_idx + rows,
total_lines: self.length,
has_more_lines: false,
},
},
)));
} else {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
ScrollUpdate::End(self.id),
)));
}
// If cursor position has changed, remove the highlight from the previous
// position and apply it in the new one.
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
let old_cursor_pos = self.cursor_pos;
self.cursor_pos = self.new_cursor_pos;
for &(idx, highlight) in &[(old_cursor_pos, false), (self.new_cursor_pos, true)] {
if idx >= self.length {
continue; //bounds check
}
let new_area = area.nth_row(idx % rows);
self.data_columns
.draw(grid, idx, self.cursor_pos, grid.bounds_iter(new_area));
let row_attr = if highlight {
self.highlight_theme
} else {
self.theme_default
};
grid.change_theme(new_area, row_attr);
context.dirty_areas.push_back(new_area);
}
return;
} else if self.cursor_pos != self.new_cursor_pos {
self.cursor_pos = self.new_cursor_pos;
}
if self.new_cursor_pos >= self.length {
self.new_cursor_pos = self.length - 1;
self.cursor_pos = self.new_cursor_pos;
}
// Page_no has changed, so draw new page
_ = self
.data_columns
.recalc_widths((area.width(), area.height()), top_idx);
grid.clear_area(area, self.theme_default);
// copy table columns
self.data_columns
.draw(grid, top_idx, self.cursor_pos, grid.bounds_iter(area));
// highlight cursor
grid.change_theme(area.nth_row(self.cursor_pos % rows), self.highlight_theme);
// clear gap if available height is more than count of entries
if top_idx + rows > self.length {
grid.change_theme(area.skip_rows(self.length - top_idx), self.theme_default);
}
context.dirty_areas.push_back(area);
}
}
impl Component for JobManager {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() {
return;
}
if !self.initialized {
self.initialize(context);
}
if self.dirty {
let area = area.nth_row(0);
// Draw column headers.
grid.clear_area(area, self.theme_default);
let mut x_offset = 0;
for (i, (h, w)) in Self::HEADERS.iter().zip(self.min_width).enumerate() {
grid.write_string(
h,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs | Attr::BOLD,
area.skip_cols(x_offset),
None,
);
if self.sort_col as usize == i {
use SortOrder::*;
let arrow = match (grid.ascii_drawing, self.sort_order) {
(true, Asc) => DataColumns::<5>::ARROW_UP_ASCII,
(true, Desc) => DataColumns::<5>::ARROW_DOWN_ASCII,
(false, Asc) => DataColumns::<5>::ARROW_UP,
(false, Desc) => DataColumns::<5>::ARROW_DOWN,
};
grid.write_string(
arrow,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
area.skip_cols(x_offset + h.len()),
None,
);
}
x_offset += w + 2;
}
context.dirty_areas.push_back(area);
}
self.draw_list(grid, area.skip_rows(1), context);
self.dirty = false;
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::ConfigReload { old_settings: _ } = event {
self.theme_default = crate::conf::value(context, "theme_default");
self.initialized = false;
self.set_dirty(true);
}
let shortcuts = self.shortcuts(context);
match event {
UIEvent::StatusEvent(
StatusEvent::JobFinished(_) | StatusEvent::JobCanceled(_) | StatusEvent::NewJob(_),
) => {
self.initialized = false;
self.set_dirty(true);
return false;
}
UIEvent::Action(Action::SortColumn(column, order)) => {
let column = match *column {
0 => Column::_0,
1 => Column::_1,
2 => Column::_2,
3 => Column::_3,
4 => Column::_4,
other => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Invalid column index `{}`: there are {} columns.",
other,
Self::HEADERS.len()
)),
));
return true;
}
};
if (self.sort_col, self.sort_order) != (column, *order) {
self.sort_col = column;
self.sort_order = *order;
self.initialized = false;
self.set_dirty(true);
}
return true;
}
UIEvent::Input(Key::Char(ref c)) if c.is_ascii_digit() => {
let n = *c as u8 - b'0'; // safe cast because of is_ascii_digit() check;
let column = match n {
1 => Column::_0,
2 => Column::_1,
3 => Column::_2,
4 => Column::_3,
5 => Column::_4,
_ => {
return false;
}
};
if self.sort_col == column {
self.sort_order = !self.sort_order;
} else {
self.sort_col = column;
self.sort_order = SortOrder::default();
}
self.initialized = false;
self.set_dirty(true);
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_up"]) =>
{
let amount = 1;
self.movement = Some(PageMovement::Up(amount));
self.set_dirty(true);
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_down"])
&& self.cursor_pos < self.length.saturating_sub(1) =>
{
let amount = 1;
self.set_dirty(true);
self.movement = Some(PageMovement::Down(amount));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["prev_page"]) =>
{
let mult = 1;
self.set_dirty(true);
self.movement = Some(PageMovement::PageUp(mult));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["next_page"]) =>
{
let mult = 1;
self.set_dirty(true);
self.movement = Some(PageMovement::PageDown(mult));
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["home_page"]) =>
{
self.set_dirty(true);
self.movement = Some(PageMovement::Home);
return true;
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["end_page"]) =>
{
self.set_dirty(true);
self.movement = Some(PageMovement::End);
return true;
}
UIEvent::Resize => {
self.set_dirty(true);
}
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;
}
fn kill(&mut self, uuid: ComponentId, context: &mut Context) {
debug_assert!(uuid == self.id);
context.replies.push_back(UIEvent::Action(Tab(Kill(uuid))));
}
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
let mut map = ShortcutMaps::default();
map.insert(
Shortcuts::GENERAL,
context.settings.shortcuts.general.key_values(),
);
map[Shortcuts::GENERAL].insert("sort by 1st column", Key::Char('1'));
map[Shortcuts::GENERAL].insert("sort by 2nd column", Key::Char('2'));
map[Shortcuts::GENERAL].insert("sort by 3rd column", Key::Char('3'));
map[Shortcuts::GENERAL].insert("sort by 4th column", Key::Char('4'));
map[Shortcuts::GENERAL].insert("sort by 5th column", Key::Char('5'));
map
}
fn id(&self) -> ComponentId {
self.id
}
fn can_quit_cleanly(&mut self, _context: &Context) -> bool {
true
}
fn status(&self, _context: &Context) -> String {
format!(
"{} entries. Use `sort <n> [asc/desc]` command or press column index number key \
(twice to toggle asc/desc) to sort",
self.entries.len()
)
}
}

161
meli/src/lib.rs 100644
View File

@ -0,0 +1,161 @@
/*
* meli - lib.rs
*
* Copyright 2017-2022 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 meli. If not, see <http://www.gnu.org/licenses/>.
*/
#![deny(
rustdoc::redundant_explicit_links,
/* groups */
clippy::correctness,
clippy::suspicious,
clippy::complexity,
clippy::perf,
clippy::cargo,
clippy::nursery,
clippy::style,
/* restriction */
clippy::dbg_macro,
clippy::rc_buffer,
clippy::as_underscore,
clippy::assertions_on_result_states,
/* rustdoc */
rustdoc::broken_intra_doc_links,
/* pedantic */
//clippy::cast_lossless,
//clippy::cast_possible_wrap,
//clippy::ptr_as_ptr,
clippy::doc_markdown,
clippy::expect_fun_call,
clippy::bool_to_int_with_if,
clippy::borrow_as_ptr,
clippy::cast_ptr_alignment,
clippy::large_futures,
clippy::waker_clone_wake,
clippy::unused_enumerate_index,
clippy::unnecessary_fallible_conversions,
clippy::struct_field_names,
clippy::manual_hash_one,
clippy::into_iter_without_iter,
)]
#![allow(
clippy::option_if_let_else,
clippy::missing_const_for_fn,
clippy::significant_drop_tightening,
clippy::multiple_crate_versions,
clippy::significant_drop_in_scrutinee,
clippy::cognitive_complexity,
clippy::manual_clamp
)]
/* Source Code Annotation Tags:
*
* Global tags (in tagref format <https://github.com/stepchowfun/tagref>) for source code
* annotation:
*
* - tags from melib/src/lib.rs.
* - [tag:hardcoded_color_value] Replace hardcoded color values with user configurable ones.
*/
//!
//! This crate contains the frontend stuff of the application. The application
//! entry way on `src/bin.rs` creates an event loop and passes input to a
//! thread.
//!
//! The mail handling stuff is done in the `melib` crate which includes all
//! backend needs. The split is done to theoretically be able to create
//! different frontends with the same innards.
use std::alloc::System;
pub use std::{collections::VecDeque, path::PathBuf};
#[macro_use]
extern crate serde_derive;
extern crate linkify;
pub use melib::uuid;
pub extern crate bitflags;
pub extern crate serde_json;
pub extern crate smallvec;
pub extern crate termion;
pub use structopt::StructOpt;
#[global_allocator]
static GLOBAL: System = System;
pub extern crate melib;
pub use melib::{
error::*, log, AccountHash, ActionFlag, Envelope, EnvelopeHash, EnvelopeRef, Flag, LogLevel,
Mail, Mailbox, MailboxHash, ThreadHash, ToggleFlag,
};
pub mod args;
pub mod subcommands;
#[macro_use]
pub mod types;
pub use crate::types::*;
#[macro_use]
pub mod terminal;
pub use crate::terminal::*;
#[macro_use]
pub mod command;
pub use crate::command::*;
pub mod state;
pub use crate::state::*;
pub mod components;
pub use crate::components::*;
pub mod utilities;
pub use crate::utilities::*;
pub mod contacts;
pub use crate::contacts::*;
pub mod mail;
pub use crate::mail::*;
pub mod notifications;
pub mod mailbox_management;
pub use mailbox_management::*;
pub mod jobs_view;
pub use jobs_view::*;
#[cfg(feature = "svgscreenshot")]
pub mod svg;
#[macro_use]
pub mod conf;
pub use crate::conf::{
DotAddressable, IndexStyle, SearchBackend, Settings, Shortcuts, ThemeAttribute,
};
#[cfg(feature = "sqlite3")]
pub mod sqlite3;
pub mod jobs;
pub mod mailcap;
pub mod accounts;
pub use self::accounts::Account;

View File

@ -19,30 +19,30 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Entities that handle Mail specific functions.
*/
//! Entities that handle Mail specific functions.
use indexmap::IndexMap;
use melib::{
backends::{AccountHash, Mailbox, MailboxHash},
email::{attachment_types::*, attachments::*},
thread::ThreadNodeHash,
};
use super::*;
use melib::backends::Folder;
use melib::backends::FolderHash;
use melib::thread::ThreadNodeHash;
use crate::{
melib::text::{TextProcessing, Truncate},
uuid::Uuid,
};
pub mod listing;
pub use crate::listing::*;
pub mod view;
pub use crate::view::*;
mod compose;
pub mod compose;
pub use self::compose::*;
#[cfg(feature = "gpgme")]
pub mod pgp;
mod status;
pub mod status;
pub use self::status::*;
fn get_display_name(context: &Context, idx: usize) -> String {
let settings = context.accounts[idx].runtime_settings.account();
if let Some(d) = settings.display_name.as_ref() {
format!("{} <{}>", d, settings.identity)
} else {
settings.identity.to_string()
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More