-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
55 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,10 +65,7 @@ DEFAULT="$HOME/Mail/backup" | |
EOF | ||
# backup all your mail from GMail | ||
read password | ||
echo "$password" > ./password.txt | ||
imaparms fetch --host imap.gmail.com --user [email protected] --passfile ./password.txt --mda maildrop --all-folders --all | ||
rm ./password.txt | ||
imaparms fetch --host imap.gmail.com --user [email protected] --pass-pinentry --mda maildrop --all-folders --all | ||
``` | ||
|
||
For GMail you will have to create and use application-specific password, which requires enabling 2FA, [see below for more info](#gmail). | ||
|
@@ -81,21 +78,21 @@ To make the above efficient you have to sacrifice either `SEEN` or `FLAGGED` IMA | |
|
||
``` | ||
# mark all messages as UNSEEN | ||
imaparms mark --host imap.gmail.com --user [email protected] --passfile ./password.txt --all-folders unseen | ||
imaparms mark --host imap.gmail.com --user [email protected] --pass-pinentry --all-folders unseen | ||
# fetch UNSEEN and mark as SEEN as you go | ||
# this can be interrrupted and restarted and it will continue from where it left off | ||
imaparms mark --host imap.gmail.com --user [email protected] --passfile ./password.txt --all-folders --unseen | ||
imaparms mark --host imap.gmail.com --user [email protected] --pass-pinentry --all-folders --unseen | ||
``` | ||
|
||
or | ||
|
||
``` | ||
# mark all messages as UNFLAGGED | ||
imaparms mark --host imap.gmail.com --user [email protected] --passfile ./password.txt --all-folders unflagged | ||
imaparms mark --host imap.gmail.com --user [email protected] --pass-pinentry --all-folders unflagged | ||
# similarly | ||
imaparms mark --host imap.gmail.com --user [email protected] --passfile ./password.txt --all-folders --unflagged | ||
imaparms mark --host imap.gmail.com --user [email protected] --pass-pinentry --all-folders --unflagged | ||
``` | ||
|
||
This, of course, means that if you open or "mark as read" a message in GMail's web-mail UI while using `imaparms --unseen`, or flag (star) it there while using `imaparms --unflagged`, `imaparms` will ignore the message on the next `fetch`. | ||
|
@@ -300,7 +297,7 @@ Logins to a specified server, performs specified actions on all messages matchin | |
- `delete (expire)` | ||
: delete matching messages from specified folders | ||
|
||
### imaparms list [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] | ||
### imaparms list [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--pass-pinentry | --passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] | ||
|
||
Login, perform IMAP `LIST` command to get all folders, print them one per line. | ||
|
||
|
@@ -327,6 +324,8 @@ Login, perform IMAP `LIST` command to get all folders, print them one per line. | |
|
||
- `--user USER` | ||
: username on the server (required) | ||
- `--pass-pinentry` | ||
: read the password via `pinentry` | ||
- `--passfile PASSFILE, --pass-file PASSFILE` | ||
: file containing the password on its first line | ||
- `--passcmd PASSCMD, --pass-cmd PASSCMD` | ||
|
@@ -354,7 +353,7 @@ Login, perform IMAP `LIST` command to get all folders, print them one per line. | |
if you set in large enough to cover the longest single-server `fetch`, it will prevent any of the servers learning anything about the data on other servers; | ||
if you run `imaparms` on a machine that disconnects from the Internet when you go to sleep and you set it large enough, it will help in preventing the servers from collecting data about your sleep cycle | ||
|
||
### imaparms count [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] [--all-folders | --folder NAME] [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--porcelain] | ||
### imaparms count [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--pass-pinentry | --passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] [--all-folders | --folder NAME] [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--porcelain] | ||
|
||
Login, (optionally) perform IMAP `LIST` command to get all folders, perform IMAP `SEARCH` command with specified filters in each folder, print message counts for each folder one per line. | ||
|
||
|
@@ -385,6 +384,8 @@ Login, (optionally) perform IMAP `LIST` command to get all folders, perform IMAP | |
|
||
- `--user USER` | ||
: username on the server (required) | ||
- `--pass-pinentry` | ||
: read the password via `pinentry` | ||
- `--passfile PASSFILE, --pass-file PASSFILE` | ||
: file containing the password on its first line | ||
- `--passcmd PASSCMD, --pass-cmd PASSCMD` | ||
|
@@ -450,7 +451,7 @@ Login, (optionally) perform IMAP `LIST` command to get all folders, perform IMAP | |
- `--unflagged` | ||
: operate on messages not marked as `FLAGGED` | ||
|
||
### imaparms mark [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] (--all-folders | --folder NAME) [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] {seen,unseen,flagged,unflagged} | ||
### imaparms mark [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--pass-pinentry | --passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] (--all-folders | --folder NAME) [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] {seen,unseen,flagged,unflagged} | ||
|
||
Login, perform IMAP `SEARCH` command with specified filters for each folder, mark resulting messages in specified way by issuing IMAP `STORE` commands. | ||
|
||
|
@@ -477,6 +478,8 @@ Login, perform IMAP `SEARCH` command with specified filters for each folder, mar | |
|
||
- `--user USER` | ||
: username on the server (required) | ||
- `--pass-pinentry` | ||
: read the password via `pinentry` | ||
- `--passfile PASSFILE, --pass-file PASSFILE` | ||
: file containing the password on its first line | ||
- `--passcmd PASSCMD, --pass-cmd PASSCMD` | ||
|
@@ -550,7 +553,7 @@ Login, perform IMAP `SEARCH` command with specified filters for each folder, mar | |
- `flag`: add `FLAGGED` flag, sets `--unflagged` if no message search filter is specified | ||
- `unflag`: remove `FLAGGED` flag, sets `--flagged` if no message search filter is specified | ||
|
||
### imaparms fetch [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] --mda COMMAND [--new-mail-cmd NEW_MAIL_CMD] [--all-folders | --folder NAME] [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--mark {auto,noop,seen,unseen,flagged,unflagged}] | ||
### imaparms fetch [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--pass-pinentry | --passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] --mda COMMAND [--new-mail-cmd NEW_MAIL_CMD] [--all-folders | --folder NAME] [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--mark {auto,noop,seen,unseen,flagged,unflagged}] | ||
|
||
Login, perform IMAP `SEARCH` command with specified filters for each folder, fetch resulting messages in (configurable) batches, feed each batch of messages to an MDA, mark each message for which MDA succeded in a specified way by issuing IMAP `STORE` commands. | ||
|
||
|
@@ -577,6 +580,8 @@ Login, perform IMAP `SEARCH` command with specified filters for each folder, fet | |
|
||
- `--user USER` | ||
: username on the server (required) | ||
- `--pass-pinentry` | ||
: read the password via `pinentry` | ||
- `--passfile PASSFILE, --pass-file PASSFILE` | ||
: file containing the password on its first line | ||
- `--passcmd PASSCMD, --pass-cmd PASSCMD` | ||
|
@@ -660,7 +665,7 @@ Login, perform IMAP `SEARCH` command with specified filters for each folder, fet | |
- `flagged`: add `FLAGGED` flag | ||
- `unflagged`: remove `FLAGGED` flag | ||
|
||
### imaparms delete [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] (--all-folders | --folder NAME) [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--method {auto,delete,delete-noexpunge,gmail-trash}] | ||
### imaparms delete [--debug] [--dry-run] [--plain | --ssl | --starttls] [--host HOST] [--port PORT] [--user USER] [--pass-pinentry | --passfile PASSFILE | --passcmd PASSCMD] [--store-number INT] [--fetch-number INT] [--batch-number INT] [--batch-size INT] [--every SECONDS] [--every-add-random ADD] (--all-folders | --folder NAME) [--not-folder NAME] [--all | [--seen | --unseen |] [--flagged | --unflagged]] [--older-than DAYS] [--newer-than DAYS] [--older-than-timestamp-in PATH] [--newer-than-timestamp-in PATH] [--older-than-mtime-of PATH] [--newer-than-mtime-of PATH] [--from ADDRESS] [--not-from ADDRESS] [--method {auto,delete,delete-noexpunge,gmail-trash}] | ||
|
||
Login, perform IMAP `SEARCH` command with specified filters for each folder, delete them from the server using a specified method. | ||
|
||
|
@@ -695,6 +700,8 @@ Login, perform IMAP `SEARCH` command with specified filters for each folder, del | |
|
||
- `--user USER` | ||
: username on the server (required) | ||
- `--pass-pinentry` | ||
: read the password via `pinentry` | ||
- `--passfile PASSFILE, --pass-file PASSFILE` | ||
: file containing the password on its first line | ||
- `--passcmd PASSCMD, --pass-cmd PASSCMD` | ||
|
@@ -772,6 +779,11 @@ Specifying `--folder` multiple times will perform the specified action on all sp | |
|
||
- List all available IMAP folders and count how many messages they contain: | ||
|
||
- with the password taken from `pinentry`: | ||
``` | ||
imaparms count --ssl --host imap.example.com --user [email protected] --pass-pinentry | ||
``` | ||
- with the password taken from the first line of the given file: | ||
``` | ||
imaparms count --ssl --host imap.example.com --user [email protected] --passfile /path/to/file/containing/[email protected] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,23 @@ def handle_signals() -> None: | |
signal.signal(signal.SIGTERM, sig_handler) | ||
signal.signal(signal.SIGUSR1, sig_unsleep) | ||
|
||
def pinentry(host : str, user : str) -> str: | ||
with subprocess.Popen(["pinentry"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: | ||
def check(beginning : str) -> str: | ||
res = p.stdout.readline().decode(defenc) # type: ignore | ||
if not res.endswith("\n") or not res.startswith(beginning): | ||
raise Failure("pinentry conversation failed") | ||
return res[len(beginning):-1] | ||
check("OK ") | ||
def opt(what : str, beginning : str) -> str: | ||
p.stdin.write(what.encode(defenc) + b"\n") # type: ignore | ||
p.stdin.flush() # type: ignore | ||
return check(beginning) | ||
opt("SETDESC " + gettext("Please enter the passphrase for user %s on host %s") % (user, host), "OK") | ||
opt("SETPROMPT " + gettext("Passphrase:"), "OK") | ||
pin = opt("GETPIN", "D ") | ||
return pin | ||
|
||
def imap_parse_data(data : bytes, literals : _t.List[bytes] = [], top_level : bool = True) -> _t.Tuple[_t.Any, bytes]: | ||
"Parse IMAP response string into a tree of strings." | ||
acc : _t.List[bytes] = [] | ||
|
@@ -766,6 +783,10 @@ def add_examples(fmt : _t.Any) -> None: | |
|
||
fmt.start_section(_("List all available IMAP folders and count how many messages they contain")) | ||
|
||
fmt.start_section(_("with the password taken from `pinentry`")) | ||
fmt.add_code(f'{__package__} count --ssl --host imap.example.com --user [email protected] --pass-pinentry') | ||
fmt.end_section() | ||
|
||
fmt.start_section(_("with the password taken from the first line of the given file")) | ||
fmt.add_code(f'{__package__} count --ssl --host imap.example.com --user [email protected] --passfile /path/to/file/containing/[email protected]') | ||
fmt.end_section() | ||
|
@@ -921,7 +942,9 @@ def __call__(self, parser : _t.Any, cfg : _t.Any, value : _t.Any, option_string | |
user = cfg.user | ||
cfg.user = None | ||
|
||
if self.ptype == "file": | ||
if self.ptype == "pinentry": | ||
password = pinentry(host, user) | ||
elif self.ptype == "file": | ||
with open(value, "rb") as f: | ||
password = f.readline().decode(defenc) | ||
elif self.ptype == "cmd": | ||
|
@@ -962,6 +985,7 @@ def add_common(cmd : _t.Any) -> None: | |
agrp.add_argument("--user", type=str, help=_("username on the server (required)")) | ||
|
||
grp = agrp.add_mutually_exclusive_group() | ||
grp.add_argument("--pass-pinentry", nargs=0, action=EmitAccount, default="pinentry", help=_("read the password via `pinentry`")) | ||
grp.add_argument("--passfile", "--pass-file", action=EmitAccount, default="file", help=_("file containing the password on its first line")) | ||
grp.add_argument("--passcmd", "--pass-cmd", action=EmitAccount, default="cmd", help=_("shell command that returns the password as the first line of its stdout")) | ||
grp.set_defaults(password = None) | ||
|
@@ -1114,7 +1138,11 @@ def no_cmd(args : _t.Any) -> None: | |
cmd.set_defaults(func=cmd_action) | ||
cmd.set_defaults(command="delete") | ||
|
||
args = parser.parse_args(sys.argv[1:]) | ||
try: | ||
args = parser.parse_args(sys.argv[1:]) | ||
except CatastrophicFailure as exc: | ||
error(exc.show()) | ||
sys.exit(1) | ||
|
||
if args.help_markdown: | ||
parser.set_formatter_class(argparse.MarkdownBetterHelpFormatter) | ||
|