duplicity 3.0.8.dev2__tar.gz → 3.0.8.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {duplicity-3.0.8.dev2/duplicity.egg-info → duplicity-3.0.8.dev3}/PKG-INFO +1 -1
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/__init__.py +1 -1
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/dup_main.py +30 -29
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/util.py +0 -89
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3/duplicity.egg-info}/PKG-INFO +1 -1
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/man/duplicity.1 +1 -1
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/pyproject.toml +1 -4
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/setup.py +1 -1
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/AUTHORS.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/CHANGELOG.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/COPYING +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/README-LOG.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/README-REPO.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/README-TESTING.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/README.md +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/__main__.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/_librsyncmodule.c +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/argparse311.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backend_pool.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/__init__.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/_cf_cloudfiles.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/_cf_pyrax.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/_testbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/adbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/azurebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/b2backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/boxbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/cfbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/dpbxbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/gdocsbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/gdrivebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/giobackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/hsibackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/hubicbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/idrivedbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/imapbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/jottacloudbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/lftpbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/localbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/mediafirebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/megabackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/megav2backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/megav3backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/multibackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/ncftpbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/onedrivebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/par2backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/pcabackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/pydrivebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/pyrax_identity/__init__.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/pyrax_identity/hubic.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/rclonebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/rsyncbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/s3_boto3_backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/slatebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/ssh_paramiko_backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/ssh_pexpect_backend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/swiftbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/sxbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/tahoebackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/webdavbackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/backends/xorrisobackend.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/cached_ops.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/cli_data.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/cli_main.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/cli_util.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/config.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/diffdir.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/dup_collections.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/dup_tarfile.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/dup_temp.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/dup_time.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/errors.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/file_naming.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/filechunkio.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/globmatch.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/gpg.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/gpginterface.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/lazy.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/librsync.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/log.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/manifest.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/patchdir.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/path.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/progress.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/robust.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/selection.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/statistics.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity/tempdir.py +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity.egg-info/SOURCES.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity.egg-info/dependency_links.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity.egg-info/entry_points.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity.egg-info/requires.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/duplicity.egg-info/top_level.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/af_ZA/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ar_SA/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ca_ES/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/cs_CZ/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/da_DK/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/de_AT/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/de_DE/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/el_GR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/en_AU/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/en_GB/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/en_PR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/en_US/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/es_EM/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/es_ES/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/es_MX/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/es_PR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/es_US/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/fi_FI/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/fr_FR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/he_IL/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/hu_HU/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/it_IT/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ja_JP/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ko_KR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/nl_BE/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/nl_NL/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/nl_SR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/no_NO/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/pl_PL/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/pt_BR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/pt_PT/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ro_RO/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ru_BY/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ru_MD/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ru_RU/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/ru_UA/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/sr_SP/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/sv_SE/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/tr_TR/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/uk_UA/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/vi_VN/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/zh_CN/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/zh_HK/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/zh_MO/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/zh_SG/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/po/zh_TW/duplicity.mo +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/requirements.txt +0 -0
- {duplicity-3.0.8.dev2 → duplicity-3.0.8.dev3}/setup.cfg +0 -0
|
@@ -132,46 +132,47 @@ def get_passphrase(n, action, for_signing=False):
|
|
|
132
132
|
log.Notice(_("Reuse configured SIGN_PASSPHRASE as PASSPHRASE"))
|
|
133
133
|
return os.environ["SIGN_PASSPHRASE"]
|
|
134
134
|
|
|
135
|
+
# Next, verify we need to ask the user
|
|
136
|
+
|
|
137
|
+
# Assumptions:
|
|
138
|
+
# - encrypt-key has no passphrase
|
|
139
|
+
# - sign-key requires passphrase
|
|
140
|
+
# - gpg-agent supplies all, no user interaction
|
|
141
|
+
|
|
135
142
|
# no passphrase if --no-encryption or --use-agent
|
|
136
143
|
if not config.encryption or config.use_agent:
|
|
137
144
|
return ""
|
|
138
145
|
|
|
139
|
-
#
|
|
140
|
-
|
|
146
|
+
# these commands don't need a password
|
|
147
|
+
elif action in [
|
|
148
|
+
"collection-status",
|
|
149
|
+
"list-current-files",
|
|
150
|
+
"remove-all-but-n-full",
|
|
151
|
+
"remove-all-inc-of-but-n-full",
|
|
152
|
+
"remove-older-than",
|
|
153
|
+
]:
|
|
141
154
|
return ""
|
|
142
155
|
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if encrypt_keys and config.check_remote:
|
|
151
|
-
asymmetric = True
|
|
152
|
-
for key in encrypt_keys:
|
|
153
|
-
if util.key_needs_passphrase(config.gpg_binary, key):
|
|
154
|
-
log.Notice(f"Key {key} needs passphrase.")
|
|
155
|
-
need_passphrase = True
|
|
156
|
-
break
|
|
157
|
-
else:
|
|
158
|
-
log.Notice("No encryption keys need passphrase.")
|
|
159
|
-
else:
|
|
160
|
-
need_passphrase = True
|
|
161
|
-
log.Notice("No encryption keys configured.")
|
|
162
|
-
|
|
163
|
-
skips = copy.copy(skips_sync_archive)
|
|
164
|
-
skips.remove("full")
|
|
165
|
-
if (action == "full" and asymmetric) or config.restart or action in skips:
|
|
166
|
-
log.Notice(f"Skipping passphrase request for action {action}")
|
|
156
|
+
# for a full, inc, verify, we don't need a password if
|
|
157
|
+
# there is no sign_key and there are recipients
|
|
158
|
+
elif (
|
|
159
|
+
action in ("full", "inc", "verify")
|
|
160
|
+
and (config.gpg_profile.recipients or config.gpg_profile.hidden_recipients)
|
|
161
|
+
and (not config.gpg_profile.sign_key or (not config.restart and not for_signing))
|
|
162
|
+
):
|
|
167
163
|
return ""
|
|
168
164
|
|
|
169
|
-
elif
|
|
170
|
-
|
|
165
|
+
elif (
|
|
166
|
+
(config.gpg_profile.recipients or config.gpg_profile.hidden_recipients)
|
|
167
|
+
and config.metadata_sync_mode == "partial"
|
|
168
|
+
and action in ["full"]
|
|
169
|
+
):
|
|
170
|
+
log.Info(_("Skipping passphrase input for full backup with encryption keys."))
|
|
171
171
|
return ""
|
|
172
172
|
|
|
173
|
+
# Finally, ask the user for the passphrase
|
|
173
174
|
else:
|
|
174
|
-
log.
|
|
175
|
+
log.Info(_("PASSPHRASE variable not set, asking user."))
|
|
175
176
|
use_cache = True
|
|
176
177
|
while True:
|
|
177
178
|
# ask the user to enter a new passphrase to avoid an infinite loop
|
|
@@ -205,95 +205,6 @@ def release_lockfile():
|
|
|
205
205
|
pass
|
|
206
206
|
|
|
207
207
|
|
|
208
|
-
def key_needs_passphrase(gpgbin, key, logfile=None):
|
|
209
|
-
"""
|
|
210
|
-
Determine whether a GnuPG key requires a passphrase.
|
|
211
|
-
|
|
212
|
-
This helper invokes the specified GnuPG frontend in a non‑destructive
|
|
213
|
-
way to discover if the secret key is protected by a passphrase. It uses
|
|
214
|
-
`pexpect` to spawn the command and watch for prompts or agent errors,
|
|
215
|
-
never changing the key material itself.
|
|
216
|
-
|
|
217
|
-
How it works
|
|
218
|
-
- Runs: ``<gpgbin> --pinentry-mode cancel --dry-run --change-passphrase <key>``
|
|
219
|
-
with a C UTF‑8 locale to ensure predictable output.
|
|
220
|
-
- Interprets the interaction:
|
|
221
|
-
- If the process reaches EOF without a passphrase prompt, the key is
|
|
222
|
-
considered not to need a passphrase.
|
|
223
|
-
- If a passphrase prompt appears (matches ``passphrase.*:``), the key
|
|
224
|
-
is considered to need a passphrase.
|
|
225
|
-
- If ``gpg-agent`` fails to start or ignores an inquiry, we log an
|
|
226
|
-
error and return ``None`` to signal an indeterminate result.
|
|
227
|
-
|
|
228
|
-
Parameters
|
|
229
|
-
- gpgbin: str
|
|
230
|
-
The GnuPG command to execute, e.g. ``"gpg"`` or ``"gpgsm"``.
|
|
231
|
-
- key: str
|
|
232
|
-
The key identifier understood by the given binary. Examples:
|
|
233
|
-
- For ``gpg`` (OpenPGP): a key ID or fingerprint, e.g. ``"56538CCF"``.
|
|
234
|
-
- For ``gpgsm`` (S/MIME): a certificate keyref, e.g.
|
|
235
|
-
``"\\&165F2FB4F58D..."``.
|
|
236
|
-
- logfile: a file-like object or ``None``
|
|
237
|
-
If provided, raw pexpect I/O is mirrored to this stream for debugging
|
|
238
|
-
(e.g. ``sys.stdout``). Defaults to ``None``.
|
|
239
|
-
|
|
240
|
-
Returns
|
|
241
|
-
- ``True`` if the key requires a passphrase.
|
|
242
|
-
- ``False`` if the key does not require a passphrase.
|
|
243
|
-
- ``None`` if the status cannot be determined due to a runtime error
|
|
244
|
-
(e.g., agent failed to start or pexpect raised an exception).
|
|
245
|
-
|
|
246
|
-
Notes
|
|
247
|
-
- The check is read‑only: ``--dry-run`` and ``--pinentry-mode cancel`` are
|
|
248
|
-
used to avoid modifying the key or prompting the user.
|
|
249
|
-
- Environment variables ``LANG`` and ``LC_ALL`` are forced to ``C.utf8``
|
|
250
|
-
to make output matching stable across locales.
|
|
251
|
-
- For end‑to‑end manual verification with the repository’s test keyring,
|
|
252
|
-
see ``testing/manual/needspass.py``.
|
|
253
|
-
"""
|
|
254
|
-
|
|
255
|
-
environ = {**os.environ, "LANG": "C.utf8", "LC_ALL": "C.utf8"}
|
|
256
|
-
cmd = f"{gpgbin} --pinentry-mode cancel --dry-run --change-passphrase {key} "
|
|
257
|
-
|
|
258
|
-
log.Debug(f"{cmd=}")
|
|
259
|
-
|
|
260
|
-
try:
|
|
261
|
-
child = pexpect.spawn(cmd, encoding="utf-8", env=environ)
|
|
262
|
-
child.logfile = logfile
|
|
263
|
-
except pexpect.ExceptionPexpect as e:
|
|
264
|
-
log.Error(f"An unexpected error occurred: {e}")
|
|
265
|
-
return None
|
|
266
|
-
|
|
267
|
-
try:
|
|
268
|
-
got = child.expect(
|
|
269
|
-
[
|
|
270
|
-
pexpect.EOF,
|
|
271
|
-
"passphrase.*:",
|
|
272
|
-
"failed to start gpg-agent",
|
|
273
|
-
"ignoring gpg-agent inquiry",
|
|
274
|
-
]
|
|
275
|
-
)
|
|
276
|
-
except pexpect.ExceptionPexpect as e:
|
|
277
|
-
log.Error(f"Exception while checking if passphrase needed for: {key}:\n{e}")
|
|
278
|
-
return None
|
|
279
|
-
|
|
280
|
-
child.close()
|
|
281
|
-
log.Debug(f"{child.exitstatus=}, {child.signalstatus=}, {got=}, {child.after=}")
|
|
282
|
-
|
|
283
|
-
if got == 0:
|
|
284
|
-
log.Debug(f"Key {key} does not need passphrase")
|
|
285
|
-
return False
|
|
286
|
-
elif got == 1:
|
|
287
|
-
log.Debug(f"Key {key} needs passphrase")
|
|
288
|
-
return True
|
|
289
|
-
elif got == 2:
|
|
290
|
-
log.Error(f"gpg-agent failed to start.")
|
|
291
|
-
return None
|
|
292
|
-
elif got == 3:
|
|
293
|
-
log.Error(f"gpg-agent failed inquiry ignored.")
|
|
294
|
-
return None
|
|
295
|
-
|
|
296
|
-
|
|
297
208
|
def copyfileobj(infp, outfp, byte_count=-1):
|
|
298
209
|
"""
|
|
299
210
|
Copy byte_count bytes from infp to outfp, or all if byte_count < 0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH DUPLICITY 1 "January 14, 2026" "Version 3.0.8.
|
|
1
|
+
.TH DUPLICITY 1 "January 14, 2026" "Version 3.0.8.dev3" "User Manuals" \" -*- nroff -*-
|
|
2
2
|
.\" disable justification (adjust text to left margin only)
|
|
3
3
|
.\" command line examples stay readable through that
|
|
4
4
|
.ad l
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "duplicity"
|
|
3
|
-
version = "3.0.8.
|
|
3
|
+
version = "3.0.8.dev3"
|
|
4
4
|
dynamic = ["dependencies"]
|
|
5
5
|
description = "Encrypted backup using rsync algorithm"
|
|
6
6
|
authors = [
|
|
@@ -126,9 +126,6 @@ addopts = [
|
|
|
126
126
|
"-p no:randomly",
|
|
127
127
|
"-p no:xdist",
|
|
128
128
|
]
|
|
129
|
-
markers = [
|
|
130
|
-
"slow: test runs >= 10 secs",
|
|
131
|
-
]
|
|
132
129
|
testpaths = [
|
|
133
130
|
"testing/test_code.py",
|
|
134
131
|
"testing/unit",
|
|
@@ -42,7 +42,7 @@ elif not ((3, 9) <= sys.version_info[:2] <= (3, 14)):
|
|
|
42
42
|
print("Sorry, duplicity requires version 3.9 thru 3.14 of Python.", file=sys.stderr)
|
|
43
43
|
sys.exit(1)
|
|
44
44
|
|
|
45
|
-
Version: str = "3.0.8.
|
|
45
|
+
Version: str = "3.0.8.dev3"
|
|
46
46
|
|
|
47
47
|
# READTHEDOCS uses setup.py sdist but can't handle extensions
|
|
48
48
|
ext_modules = list()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|