Commit Graph

171 Commits

Author SHA1 Message Date
Werner Koch 6b9f772914
common: Prepare for parsing mail sub-addresses.
* common/mbox-util.c (mailbox_from_userid): Add arg subaddress and
implement.  Change all callers to pass false for it.

* common/t-mbox-util.c (run_mbox_no_sub_test): New.
(run_filter): Add arg no_sub.
(main): Call new test and add option --no-sub.
--

Some stats: In the about 5300000 keys on the SKS servers we found 3055
unique mailboxes with a '+' in it.  After removing leading and
trailing '+' as well as multiple '+' (e.g. "c++" or "foo+bar+baz")
2697 were left which seem to be valid sub-addresses.

To filter mailboxes out from a line delimited list with
user-ids (e.g. an SQL output), the command

   t-mbox-util --verbose --filter

can be used; to output w/o sub-addresses add --no-sub.

GnuPG-bug-id: 4200
Signed-off-by: Werner Koch <wk@gnupg.org>
2018-11-12 07:44:33 +01:00
Werner Koch c817e75028
Merge branch 'STABLE-BRANCH-2-2' into master
--

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-12-18 16:42:59 +01:00
Werner Koch cd26c5482b
gpg: Return an error from hexfingerprint on malloc error.
* g10/keyid.c (hexfingerprint): Return NULL on malloc failure.  Chnage
all callers.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-12-13 10:52:34 +01:00
Werner Koch 34defc9bce
Adjust for changed macro names in libgpg-error master.
* common/logging.h (GPGRT_LOGLVL_): New replacement macros for older
libgpg-error versions.

--

Updates-commit: b56dfdfc18
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-12-11 10:42:23 +01:00
Werner Koch 18e5946aef
gpg: Fix wrong Tofu DB consistency check.
* g10/tofu.c (build_conflict_set): Do not assume MAX_FINGERPRINT_LEN
is the size of the fingerprint.
--

This problem was exhibited by
commit ecbbafb88d.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-10-18 13:56:12 +02:00
Werner Koch ecbbafb88d
gpg: Prepare for a longer fingerprint
* g10/card-util.c (change_cafpr): Use MAX_FINGERPRINT_LEN.
* g10/cipher.c (write_header): Use snprintf.
* g10/gpg.h (MAX_FINGERPRINT_LEN): Change to 32.
(MAX_FORMATTED_FINGERPRINT_LEN): Change to 59
* g10/keyid.c (format_hexfingerprint): Add v5 fingerprint format.
* g10/tofu.c (get_policy): Use MAX_FINGERPRINT_LEN for the buffer but
keep the raw length for now.
--

Note that this patch only increases the size of the buffer and adds a
new formatting for v5 fingerprints.  Moe work is required to fix
internal data structures like those in trustdb.gpg and the tofu
tables.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-09-27 09:42:13 +02:00
Marcus Brinkmann d245949766 tofu: Compare squares instead of square roots.
* g10/Makefile.am (tofu_source) [USE_TOFU]: Remove sqrtu32.h and
sqrtu32.c.
* g10/sqrtu32.h, g10/sqrtu32.c: Removed files.
* g10/tofu.c: Compare squares instead of square roots.
--
The original code is a factor 11.5 slower than using libm's sqrt(),
which in turn is a factor 3.5 slower than using one multiplication
on the other side of the comparison.  Also, it's much simpler now.

Signed-off-by: Marcus Brinkmann <mb@g10code.com>
2017-07-10 18:09:42 +02:00
Werner Koch 2d381b0f0b
gpg: Do not mark ", " translatable.
* g10/tofu.c (ask_about_binding): Remove useless translation markers.
--

Translation and in particular punctuation marks can only be translated
with context.  Thus making a sole comma translatable is useless.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-05-15 15:56:46 +02:00
Werner Koch 00b7767bc6
common: Let format_text return an error.
* common/stringhelp.c (format_text): Return NULL on error.
* common/t-stringhelp.c (test_format_text): Adjust for change.
* g10/gpgcompose.c (show_help): Abort on out of core.
* g10/tofu.c (ask_about_binding): Abort on format_text error.
(show_statistics): Ditto.
(show_warning): Ditto.
--

For better re-usability function in common/ shot  better not use
xmalloc functions.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-05-15 09:58:27 +02:00
NIIBE Yutaka 5c8fe54809 Spelling fixes in docs and comments.
--

In addition, fix trailing spaces in tests/inittests.

GnuPG-bug-id: 3121
Reported-by: ka7 (klemens)
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-28 10:06:33 +09:00
NIIBE Yutaka 0dec0cc281 g10: Minor fixes.
* g10/export.c (cleartext_secret_key_to_openpgp): No initialization.
(do_export_one_keyblock): Initialize with GPG_ERR_NOT_FOUND.
* g10/getkey.c (get_best_pubkey_byname): Add non-null check.
* g10/tofu.c (tofu_set_policy): ERR initialize to 0.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-17 09:15:13 +09:00
NIIBE Yutaka 7b4edf14bb common: Simplify format_text.
* common/stringhelp.c (format_text): Don't allow IN_PLACE formatting.
* common/stringhelp.h: Change the API with no IN_PLACE.
* common/t-stringhelp.c (test_format_text): Follow the change.
* g10/gpgcompose.c (show_help): Likewise.
* g10/tofu.c (format_conflict_msg_part1, ask_about_binding)
(show_statistics, show_warning): Likewise.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-12 14:47:30 +09:00
NIIBE Yutaka f079822b2c g10: Minor clean up for TOFU.
* g10/tofu.c (ask_about_binding): Fix for qualifier.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-11 12:51:47 +09:00
Werner Koch 8f2671d2cc
gpg: Pass CTRL to many more functions.
--

For proper operations as a server we need to avoid global variables.
Thus we need to pass the session state CTRL to most functions.  Quite
a lot of changes but fortunately straightforward to do.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-31 20:07:20 +02:00
Neal H. Walfield b1106b4d64 gpg: Make sure the conflict set includes the current key.
* g10/tofu.c (get_trust): Sanity check CONFLICT_SET after calling
get_policy.  If POLICY is 'auto' and the default policy is 'ask', make
sure CONFLICT_SET includes the current key.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 2959
Debian-bug-id: 854829

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2017-03-17 14:35:00 +01:00
Justus Winter 2649fdfff5
g10: Move more flags into the flag bitfield.
* g10/packet.h (PKT_user_id): Move 'is_primary', 'is_revoked', and
'is_expired' into the flags bitfield, and drop the prefix.
* g10/call-dirmngr.c: Adapt accordingly.
* g10/export.c: Likewise.
* g10/getkey.c: Likewise.
* g10/import.c: Likewise.
* g10/kbnode.c: Likewise.
* g10/keyedit.c: Likewise.
* g10/keylist.c: Likewise.
* g10/keyserver.c: Likewise.
* g10/mainproc.c: Likewise.
* g10/pkclist.c: Likewise.
* g10/pubkey-enc.c: Likewise.
* g10/tofu.c: Likewise.
* g10/trust.c: Likewise.
* g10/trustdb.c: Likewise.
--

This patch has been created by applying the following semantic patch:

    @@
    expression E;
    @@
    -E->is_expired
    +E->flags.expired

    @@
    expression E;
    @@
    -E->is_primary
    +E->flags.primary

    @@
    expression E;
    @@
    -E->is_revoked
    +E->flags.revoked

Signed-off-by: Justus Winter <justus@g10code.com>
2017-03-08 13:31:10 +01:00
NIIBE Yutaka 70aca95d68 Remove -I option to common.
* dirmngr/Makefile.am (AM_CPPFLAGS): Remove -I$(top_srcdir)/common.
* g10/Makefile.am (AM_CPPFLAGS): Ditto.
* g13/Makefile.am (AM_CPPFLAGS): Ditto.
* kbx/Makefile.am (AM_CPPFLAGS): Ditto.
* scd/Makefile.am (AM_CPPFLAGS): Ditto.
* sm/Makefile.am (AM_CPPFLAGS): Ditto.
* tools/Makefile.am (AM_CPPFLAGS): Ditto.
* Throughout: Follow the change.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-03-07 20:25:54 +09:00
Yuri Chornoivan 24cf0606b4 Clean up word replication.
--

This fixes extra word repetitions (like "the the" or "is is") in the
code and docs.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2017-02-21 13:11:46 -05:00
Neal H. Walfield 407f5f9bae gpg: Don't assume that strtoul interprets "" as 0.
* g10/tofu.c (show_statistics): If there are not records, return 0
instead of NULL.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 2853

According to SUSv3:

  If the subject sequence is empty or does not have the expected form,
  no conversion is performed
  ...
  If no conversion could be performed, 0 is returned and errno may be
  set to [EINVAL].

  http://pubs.opengroup.org/onlinepubs/007908799/xsh/strtol.html

It appears that MacOS X sets errno to EINVAL, but glibc doesn't.
Hence, we map NULL to 0 explicitly.
2017-02-02 15:48:45 +01:00
Neal H. Walfield 769272ba87 gpg: Ensure TOFU bindings associated with UTKs are registered as usual
* g10/tofu.c (get_trust): Call get_policy before short-circuiting the
policy lookup for ultimately trusted keys to make sure the binding is
added to the bindings table, if necessary.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 2929
2017-02-02 14:24:38 +01:00
Neal H. Walfield a08c781739 gpg: If there is a TOFU conflict, elide the too few message warning.
* g10/tofu.c (tofu_get_validity): If there was a conflict, don't also
print out a warning about too few messages.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2017-02-02 13:26:42 +01:00
Neal H. Walfield 027b81b35f gpg: Only print out TOFU statistics for conflicts in interactive mode
* g10/tofu.c (get_trust): Add arguments POLICYP and CONFLICT_SETP.  If
they are not NULL, return the policy and conflict set (if there is
one), respectively.  Update callers.  If MAY_ASK is FALSE, don't print
out the statistics.
(tofu_register_encryption): If there is a conflict and we haven't yet
printed the statistics about the conflicting bindings, do so now.
(tofu_get_validity): Likewise.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 2914
2017-02-02 13:26:42 +01:00
Neal H. Walfield 74268180e5 gpg: Add newline to output.
* g10/tofu.c (ask_about_binding): Add newline to output.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2017-02-02 13:26:41 +01:00
Neal H. Walfield 6f9d8a956b gpg: Remove period at end of warning.
* g10/tofu.c (tofu_register_encryption): Remove period at end of
warning.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2017-02-02 13:26:41 +01:00
Werner Koch 6b84ecbf31
Replace use of variable-length-arrays.
* common/t-iobuf.c (main): Replace variable-length-array.
* g10/gpgcompose.c (mksubpkt_callback): Ditto.
(encrypted): Ditto.
* g10/t-stutter.c (log_hexdump): Ditto.
(oracle_test): Ditto.
* g10/tofu.c (get_policy): Ditto.  Use "%zu" for size_t.
* scd/app-openpgp.c (ecc_writekey): Replace variable-length-array.
Check for zero length OID_LEN.

Signed-off-by: Werner Koch <wk@gnupg.org>
2017-01-02 13:29:18 +01:00
Neal H. Walfield 4a2c210b75 g10: Use total days, not total messages to compute TOFU validity
* g10/tofu.c (write_stats_status): Use the number of days with
signatures / encryptions to compute the validity, not the total number
of signatures / encryptions.
(BASIC_TRUST_THRESHOLD): Adjust given the new semantics.
(FULL_TRUST_THRESHOLD): Likewise.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-16 14:51:15 +01:00
Neal H. Walfield 94f6b9010d g10: Extend TOFU_STATS to emit <sign-days> and <encyrption-days>
* doc/DETAILS: Add SIGN-DAYS and ENCRYPT-DAYS to the TOFU_STATS status
line.
* g10/tofu.c (write_stats_status): Take additional parameters
signature_days and encryption_days.  Update callers.  Include them in
the tfs record and TOFU status lines.
(show_statistics): Compute the number of days on which we saw a
message signed by FINGERPRINT, and the number of days on which we
encrypted a message to it.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-16 14:32:39 +01:00
Neal H. Walfield fea9da4a8a g10: On a TOFU conflict, write the conflicting keys to the status fd
* g10/tofu.c (ask_about_binding): Emit all of the conflicting keys and
their statistics on the status fd.
(get_trust): Likewise, if we don't call ask_about_binding.
(show_statistics): Have the caller pass the policy as returned by
get_policy.  Add argument only_status_fd and don't emit any output on
stdout if it is set.  Update callers.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-16 11:11:17 +01:00
Neal H. Walfield 6caa2d0ba2 g10: Add missing space.
* g10/tofu.c (tofu_register_encryption): Add missing space.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-16 11:11:16 +01:00
Werner Koch a75790b740
gpg: Fix the fix out-of-bounds access.
* g10/tofu.c (build_conflict_set): Revert to int* and fix calloc.
--

The original code used an int array and thus better keep that and do
not limit it to 128 entries.

Fixes-commit: c3008bffac
Fixes-commit: 3b5b94ceab
Signed-off-by: Werner Koch <wk@gnupg.org>
2016-12-08 17:03:26 +01:00
Justus Winter 3b5b94ceab g10: Fix out-of-bounds access.
* g10/tofu.c (build_conflict_set): Use 'char'.

Fixes-commit: c3008bffac
Signed-off-by: Justus Winter <justus@g10code.com>
2016-12-08 14:19:09 +01:00
Werner Koch c3008bffac
gpg: Fix portability problem.
* g10/tofu.c (build_conflict_set): Replace variable dynamic array.
--

Note that clang complained that CONFLICT_SET_COUNT could be negative.
Thus I added an assert.

Signed-off-by: Werner Koch <wk@gnupg.org>
2016-12-08 13:08:02 +01:00
Neal H. Walfield bd9ebe1404 g10: Remove dead code.
* g10/tofu.c (tofu_set_policy_by_keyid): Remove function.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-06 12:16:56 +01:00
Neal H. Walfield cd532bb7b8 g10: Improve debugging output.
* g10/tofu.c (string_to_long): Improve debugging output.
(string_to_ulong): Likewise.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-02 11:07:04 +01:00
Neal H. Walfield bd1a1d8582 g10: In the TOFU module, make strings easier to translate.
* g10/tofu.c: Remove dead code.
(time_ago_str): Simplify implementation since we only want the most
significant unit.
(format_conflict_msg_part1): Use ngettext.
(ask_about_binding): Likewise and only emit full sentences.
(show_statistics): Likewise.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-12-01 22:12:56 +01:00
Neal H. Walfield 522f74f7e3 Improve some comments. 2016-11-29 14:56:00 +01:00
Neal H. Walfield 2f27cb12e3 g10: Extend TOFU_STATS to always show the validity
* doc/DETAILS (TOFU_STATS): Rename the VALIDITY field to SUMMARY.  Add
a new field called VALIDITY.
* g10/tofu.c (write_stats_status): Update output accordingly.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

Change TOFU_STATS as discussed offline with Werner, Justus and Andre.
2016-11-29 14:55:59 +01:00
Neal H. Walfield bfeafe2d3f g10: Use es_fopen instead of open.
* g10/tofu.c: Don't include <utime.h>, <fcntl.h> or <unistd.h>.
(busy_handler): Replace use of open with es_fopen.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

Thanks for Werner for pointing this out: es_fopen is more portable
than open.
2016-11-22 17:12:38 +01:00
Neal H. Walfield 44c17bcb00 g10: If the set of UTKs changes, invalidate any changed policies.
* g10/trustdb.c (tdb_utks): New function.
* g10/tofu.c (check_utks): New function.
(initdb): Call it.
* tests/openpgp/tofu.scm: Modify test to check the effective policy of
keys whose effective policy changes when we change the set of UTKs.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

If the set of ultimately trusted keys changes, then it is possible
that a binding's effective policy changes.  To deal with this, we
detect when the set of ultimately trusted keys changes and invalidate
all cached policies.
2016-11-22 15:24:05 +01:00
Justus Winter 6ce14a805f g10: Fix memory leak.
* g10/tofu.c (tofu_notice_key_changed): Remove spurious duplicate call
to 'hexfingerprint'.

Fixes-commit: 037f9de092
Signed-off-by: Justus Winter <justus@g10code.com>
2016-11-22 12:09:47 +01:00
Neal H. Walfield 037f9de092 g10: Cache the effective policy. Recompute it when required.
* g10/tofu.c (initdb): Add column effective_policy to the bindings
table.
(record_binding): New parameters effective_policy and set_conflict.
Save the effective policy.  If SET_CONFLICT is set, then set conflict
according to CONFLICT.  Otherwise, preserve the current value of
conflict.  Update callers.
(get_trust): Don't compute the effective policy here...
(get_policy): ... do it here, if it was not cached.  Take new
parameters, PK, the public key, and NOW, the time that the operation
started.  Update callers.
(show_statistics): New parameter PK.  Pass it to get_policy.  Update
callers.
(tofu_notice_key_changed): New function.
* g10/gpgv.c (tofu_notice_key_changed): New stub.
* g10/import.c (import_revoke_cert): Take additional argument CTRL.
Pass it to keydb_update_keyblock.
* g10/keydb.c (keydb_update_keyblock): Take additional argument CTRL.
Update callers.
[USE_TOFU]: Call tofu_notice_key_changed.
* g10/test-stubs.c (tofu_notice_key_changed): New stub.
* tests/openpgp/tofu.scm: Assume that manually setting a binding's
policy to auto does not cause the tofu engine to forget about any
conflict.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

We now store the computed policy in the tofu DB (in the
effective_policy column of the bindings table) to avoid computing it
every time, which is expensive.  Further, policy is never overridden
in case of a conflict.  Instead, we detect a conflict if CONFLICT is
not empty.

This change is backwards compatible to existing DBs.  The only minor
incompatibility is that unresolved conflicts won't be automatically
resolved in case we import a direct signature, or cross signatures.
2016-11-21 22:47:30 +01:00
Neal H. Walfield 182efc5b5d g10: Correctly parameterize ngettext.
* g10/tofu.c (ask_about_binding): Correctly parameterize ngettext.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-11-21 21:22:02 +01:00
Neal H. Walfield 7142b293c8 g10: Don't use the same variable for multiple SQL compiled statements
* g10/tofu.c (struct tofu_dbs_s): Remove unused field
record_binding_update2.  Replace register_insert with
register_signature and register_encryption.
(tofu_register_signature): Don't use dbs->s.register_insert, but
dbs->s.register_signature.
(tofu_register_encryption): Don't use dbs->s.register_insert, but
dbs->s.register_encryption.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-11-21 21:13:15 +01:00
Neal H. Walfield 91a0483c5d g10: Add a convenience function for checking if a key is a primary key
* g10/keydb.h (pk_is_primary): New function.
* g10/tofu.c (get_trust): Use it.
(tofu_register_signature): Likewise.
(tofu_register_encryption): Likewise.
(tofu_set_policy): Likewise.
(tofu_get_policy): Likewise.

Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-11-21 16:39:53 +01:00
NIIBE Yutaka 1c0b140ccc g10: Fix flags to open for lock of ToFU.
* g10/tofu.c (busy_handler): Fix the flags and utime is not needed.

--

The argument flags must include one of O_RDONLY, O_WRONLY, or O_RDWR.
Adding O_TRUNC, the file is updated.  So, utime is not needed.

Fixes-commit: b2e1b17efa
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2016-11-18 20:32:22 +09:00
NIIBE Yutaka b2e1b17efa g10: Fix creating a lock for ToFU.
* g10/tofu.c (busy_handler): Add third argument which is mandatory for
O_CREATE flag.

--

Reported-by: Kristian Fiskerstrand
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2016-11-18 09:32:34 +09:00
Werner Koch 4d7dc432b5
Change all http://www.gnu.org in license notices to https://
--
2016-11-05 12:02:19 +01:00
Justus Winter 5d4f1408d0 g10,w32: Fix build on Windows.
* g10/tofu.c (begin_transaction): Use the new 'gnupg_usleep'.

Signed-off-by: Justus Winter <justus@g10code.com>
2016-11-02 12:45:18 +01:00
Neal H. Walfield 7a634e48b1 g10: Avoid gratuitious SQLite aborts and starving writers.
* g10/tofu.c: Include <time.h>, <utime.h>, <fcntl.h> and <unistd.h>.
(tofu_dbs_s): Add fields want_lock_file and want_lock_file_ctime.
(begin_transaction): Only yield if DBS->WANT_LOCK_FILE_CTIME has
changed since we took the lock.  Don't use gpgrt_yield to yield, but
sleep for 100ms.  After taking the batch lock, update
DBS->WANT_LOCK_FILE_CTIME.  Also take the batch lock the first time we
take the real lock.  When taking the real lock, use immediate not
deferred mode to avoid gratuitious aborts.
(end_transaction): When dropping the outermost real lock, drop the
batch lock.
(busy_handler): New function.
(opendbs): Set the busy handler to it when opening the DB.  Initialize
CTRL->TOFU.DBS->WANT_LOCK_FILE.
(tofu_closedbs): Free DBS->WANT_LOCK_FILE.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

By default, SQLite defers transactions until they are actually needed.
A consequence of this is that if we have two readers and both decide
to do a write, then one has to abort.  To avoid this problem, we can
make the outermost transaction an immediate transaction.  This has the
disadvantage that we only allow a single reader at a time, but at
least we don't have gratuitous aborts anymore.

A second problem is that SQLite apparently doesn't actually create a
queue of waiters.  The result is that doing a sched_yield between
dropping and retaking the batch transaction is not enough to allow the
other process to make progress.  Instead, we need to wait a
while (emperically: 100ms seems reasonable).  To avoid waiting when
there is no contention, we use a new file's timestamp to signal that
there is a waiter.
2016-10-30 19:10:42 -07:00
Neal H. Walfield eec365a02b g10: Avoid reading in keys when possible.
* g10/tofu.c (build_conflict_set): If CONFLICT_SET contains a single
element, don't bother to check for cross sigs.  Add parameter PK.
Update callers.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
2016-10-30 11:03:51 -07:00