Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20064516
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
21 KB
Subscribers
None
View Options
diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm
index ebe1be5c6..272881757 100644
--- a/tests/gpgscm/tests.scm
+++ b/tests/gpgscm/tests.scm
@@ -1,428 +1,440 @@
;; Common definitions for writing tests.
;;
;; Copyright (C) 2016 g10 Code GmbH
;;
;; This file is part of GnuPG.
;;
;; GnuPG 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.
;;
;; GnuPG is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, see <http://www.gnu.org/licenses/>.
;; Trace displays and returns the given value. A debugging aid.
(define (trace x)
(display x)
(newline)
x)
;; Stringification.
(define (stringify expression)
(let ((p (open-output-string)))
(write expression p)
(get-output-string p)))
;; Reporting.
(define (echo . msg)
(for-each (lambda (x) (display x) (display " ")) msg)
(newline))
(define (info . msg)
(apply echo msg)
(flush-stdio))
(define (error . msg)
(apply info msg)
(exit 1))
(define (skip . msg)
(apply info msg)
(exit 77))
(define (make-counter)
(let ((c 0))
(lambda ()
(let ((r c))
(set! c (+ 1 c))
r))))
(define *progress-nesting* 0)
(define (call-with-progress msg what)
(set! *progress-nesting* (+ 1 *progress-nesting*))
(if (= 1 *progress-nesting*)
(begin
(info msg)
(display " > ")
(flush-stdio)
(what (lambda (item)
(display item)
(display " ")
(flush-stdio)))
(info "< "))
(begin
(what (lambda (item) (display ".") (flush-stdio)))
(display " ")
(flush-stdio)))
(set! *progress-nesting* (- *progress-nesting* 1)))
(define (for-each-p msg proc lst)
(for-each-p' msg proc (lambda (x) x) lst))
(define (for-each-p' msg proc fmt lst)
(call-with-progress
msg
(lambda (progress)
(for-each (lambda (a)
(progress (fmt a))
(proc a))
lst))))
;; Process management.
(define CLOSED_FD -1)
(define (call-with-fds what infd outfd errfd)
(wait-process (stringify what) (spawn-process-fd what infd outfd errfd) #t))
(define (call what)
(call-with-fds what
CLOSED_FD
(if (< *verbose* 0) STDOUT_FILENO CLOSED_FD)
(if (< *verbose* 0) STDERR_FILENO CLOSED_FD)))
(define (call-check what)
(if (not (= 0 (call what)))
(throw (list what "failed"))))
;; Accessor functions for the results of 'spawn-process'.
(define :stdin car)
(define :stdout cadr)
(define :stderr caddr)
(define :pid cadddr)
(define (call-with-io what in)
(let ((h (spawn-process what 0)))
(es-write (:stdin h) in)
(es-fclose (:stdin h))
(let* ((out (es-read-all (:stdout h)))
(err (es-read-all (:stderr h)))
(result (wait-process (car what) (:pid h) #t)))
(es-fclose (:stdout h))
(es-fclose (:stderr h))
(list result out err))))
;; Accessor function for the results of 'call-with-io'. ':stdout' and
;; ':stderr' can also be used.
(define :retcode car)
(define (call-popen command input-string)
(let ((result (call-with-io command input-string)))
(if (= 0 (:retcode result))
(:stdout result)
(throw (:stderr result)))))
;;
;; estream helpers.
;;
(define (es-read-all stream)
(let loop
((acc ""))
(if (es-feof stream)
acc
(loop (string-append acc (es-read stream 4096))))))
;;
;; File management.
;;
(define (file-exists? name)
(call-with-input-file name (lambda (port) #t)))
(define (file=? a b)
(file-equal a b #t))
(define (text-file=? a b)
(file-equal a b #f))
(define (file-copy from to)
(catch '() (unlink to))
(letfd ((source (open from (logior O_RDONLY O_BINARY)))
(sink (open to (logior O_WRONLY O_CREAT O_BINARY) #o600)))
(splice source sink)))
(define (text-file-copy from to)
(catch '() (unlink to))
(letfd ((source (open from O_RDONLY))
(sink (open to (logior O_WRONLY O_CREAT) #o600)))
(splice source sink)))
+(define (path-join . components)
+ (let loop ((acc #f) (rest (filter (lambda (s)
+ (not (string=? "" s))) components)))
+ (if (null? rest)
+ acc
+ (loop (if (string? acc)
+ (string-append acc "/" (car rest))
+ (car rest))
+ (cdr rest)))))
+(assert (string=? (path-join "foo" "bar" "baz") "foo/bar/baz"))
+(assert (string=? (path-join "" "bar" "baz") "bar/baz"))
+
(define (canonical-path path)
(if (char=? #\/ (string-ref path 0))
path
(string-append (getcwd) "/" path)))
(define (in-srcdir what)
(canonical-path (string-append (getenv "srcdir") "/" what)))
(define (with-path name)
(let loop ((path (string-split (getenv "GPGSCM_PATH") #\:)))
(if (null? path)
name
(let* ((qualified-name (string-append (car path) "/" name))
(file-exists (call-with-input-file qualified-name
(lambda (x) #t))))
(if file-exists
qualified-name
(loop (cdr path)))))))
(define (basename path)
(let ((i (string-index path #\/)))
(if (equal? i #f)
path
(basename (substring path (+ 1 i) (string-length path))))))
(define (basename-suffix path suffix)
(basename
(if (string-suffix? path suffix)
(substring path 0 (- (string-length path) (string-length suffix)))
path)))
;; Helper for (pipe).
(define :read-end car)
(define :write-end cadr)
;; let-like macro that manages file descriptors.
;;
;; (letfd <bindings> <body>)
;;
;; Bind all variables given in <bindings> and initialize each of them
;; to the given initial value, and close them after evaluting <body>.
(macro (letfd form)
(let ((result-sym (gensym)))
`((lambda (,(caaadr form))
(let ((,result-sym
,(if (= 1 (length (cadr form)))
`(begin ,@(cddr form))
`(letfd ,(cdadr form) ,@(cddr form)))))
(close ,(caaadr form))
,result-sym)) ,@(cdaadr form))))
(macro (with-working-directory form)
(let ((result-sym (gensym)) (cwd-sym (gensym)))
`(let* ((,cwd-sym (getcwd))
(_ (if ,(cadr form) (chdir ,(cadr form))))
(,result-sym (begin ,@(cddr form))))
(chdir ,cwd-sym)
,result-sym)))
(macro (with-temporary-working-directory form)
(let ((result-sym (gensym)) (cwd-sym (gensym)) (tmp-sym (gensym)))
`(let* ((,cwd-sym (getcwd))
- (,tmp-sym (mkdtemp "gpgscm-XXXXXX"))
+ (,tmp-sym (mkdtemp (path-join (getenv "TMP") "gpgscm-XXXXXX")))
(_ (chdir ,tmp-sym))
(,result-sym (begin ,@(cdr form))))
(chdir ,cwd-sym)
(unlink-recursively ,tmp-sym)
,result-sym)))
(define (make-temporary-file . args)
- (canonical-path (string-append (mkdtemp "gpgscm-XXXXXX")
- "/"
- (if (null? args) "a" (car args)))))
+ (canonical-path (path-join
+ (mkdtemp (path-join (getenv "TMP") "gpgscm-XXXXXX"))
+ (if (null? args) "a" (car args)))))
(define (remove-temporary-file filename)
(catch '()
(unlink filename))
(let ((dirname (substring filename 0 (string-rindex filename #\/))))
(catch (echo "removing temporary directory" dirname "failed")
(rmdir dirname))))
;; let-like macro that manages temporary files.
;;
;; (lettmp <bindings> <body>)
;;
;; Bind all variables given in <bindings>, initialize each of them to
;; a string representing an unique path in the filesystem, and delete
;; them after evaluting <body>.
(macro (lettmp form)
(let ((result-sym (gensym)))
`((lambda (,(caadr form))
(let ((,result-sym
,(if (= 1 (length (cadr form)))
`(begin ,@(cddr form))
`(lettmp ,(cdadr form) ,@(cddr form)))))
(remove-temporary-file ,(caadr form))
,result-sym)) (make-temporary-file ,(symbol->string (caadr form))))))
(define (check-execution source transformer)
(lettmp (sink)
(transformer source sink)))
(define (check-identity source transformer)
(lettmp (sink)
(transformer source sink)
(if (not (file=? source sink))
(error "mismatch"))))
;;
;; Monadic pipe support.
;;
(define pipeM
(package
(define (new procs source sink producer)
(package
(define (dump)
(write (list procs source sink producer))
(newline))
(define (add-proc command pid)
(new (cons (list command pid) procs) source sink producer))
(define (commands)
(map car procs))
(define (pids)
(map cadr procs))
(define (set-source source')
(new procs source' sink producer))
(define (set-sink sink')
(new procs source sink' producer))
(define (set-producer producer')
(if producer
(throw "producer already set"))
(new procs source sink producer'))))))
(define (pipe:do . commands)
(let loop ((M (pipeM::new '() CLOSED_FD CLOSED_FD #f)) (cmds commands))
(if (null? cmds)
(begin
(if M::producer (M::producer))
(if (not (null? M::procs))
(let* ((retcodes (wait-processes (map stringify (M::commands))
(M::pids) #t))
(results (map (lambda (p r) (append p (list r)))
M::procs retcodes))
(failed (filter (lambda (x) (not (= 0 (caddr x))))
results)))
(if (not (null? failed))
(throw failed))))) ; xxx nicer reporting
(if (and (= 2 (length cmds)) (number? (cadr cmds)))
;; hack: if it's an fd, use it as sink
(let ((M' ((car cmds) (M::set-sink (cadr cmds)))))
(if (> M::source 2) (close M::source))
(if (> (cadr cmds) 2) (close (cadr cmds)))
(loop M' '()))
(let ((M' ((car cmds) M)))
(if (> M::source 2) (close M::source))
(loop M' (cdr cmds)))))))
(define (pipe:open pathname flags)
(lambda (M)
(M::set-source (open pathname flags))))
(define (pipe:defer producer)
(lambda (M)
(let* ((p (outbound-pipe))
(M' (M::set-source (:read-end p))))
(M'::set-producer (lambda ()
(producer (:write-end p))
(close (:write-end p)))))))
(define (pipe:echo data)
(pipe:defer (lambda (sink) (display data (fdopen sink "wb")))))
(define (pipe:spawn command)
(lambda (M)
(define (do-spawn M new-source)
(let ((pid (spawn-process-fd command M::source M::sink
(if (> *verbose* 0)
STDERR_FILENO CLOSED_FD)))
(M' (M::set-source new-source)))
(M'::add-proc command pid)))
(if (= CLOSED_FD M::sink)
(let* ((p (pipe))
(M' (do-spawn (M::set-sink (:write-end p)) (:read-end p))))
(close (:write-end p))
(M'::set-sink CLOSED_FD))
(do-spawn M CLOSED_FD))))
(define (pipe:splice sink)
(lambda (M)
(splice M::source sink)
(M::set-source CLOSED_FD)))
(define (pipe:write-to pathname flags mode)
(open pathname flags mode))
;;
;; Monadic transformer support.
;;
(define (tr:do . commands)
(let loop ((tmpfiles '()) (source #f) (cmds commands))
(if (null? cmds)
(for-each remove-temporary-file tmpfiles)
(let* ((v ((car cmds) tmpfiles source))
(tmpfiles' (car v))
(sink (cadr v))
(error (caddr v)))
(if error
(begin
(for-each remove-temporary-file tmpfiles')
(throw error)))
(loop tmpfiles' sink (cdr cmds))))))
(define (tr:open pathname)
(lambda (tmpfiles source)
(list tmpfiles pathname #f)))
(define (tr:spawn input command)
(lambda (tmpfiles source)
(if (and (member '**in** command) (not source))
(error (string-append (stringify cmd) " needs an input")))
(let* ((t (make-temporary-file))
(cmd (map (lambda (x)
(cond
((equal? '**in** x) source)
((equal? '**out** x) t)
(else x))) command)))
(catch (list (cons t tmpfiles) t *error*)
(call-popen cmd input)
(if (and (member '**out** command) (not (file-exists? t)))
(error (string-append (stringify cmd)
" did not produce '" t "'.")))
(list (cons t tmpfiles) t #f)))))
(define (tr:write-to pathname)
(lambda (tmpfiles source)
(rename source pathname)
(list tmpfiles pathname #f)))
(define (tr:pipe-do . commands)
(lambda (tmpfiles source)
(let ((t (make-temporary-file)))
(apply pipe:do
`(,@(if source `(,(pipe:open source (logior O_RDONLY O_BINARY))) '())
,@commands
,(pipe:write-to t (logior O_WRONLY O_BINARY O_CREAT) #o600)))
(list (cons t tmpfiles) t #f))))
(define (tr:assert-identity reference)
(lambda (tmpfiles source)
(if (not (file=? source reference))
(error "mismatch"))
(list tmpfiles source #f)))
(define (tr:assert-weak-identity reference)
(lambda (tmpfiles source)
(if (not (text-file=? source reference))
(error "mismatch"))
(list tmpfiles source #f)))
(define (tr:call-with-content function . args)
(lambda (tmpfiles source)
(catch (list tmpfiles source *error*)
(apply function `(,(call-with-input-file source read-all) ,@args)))
(list tmpfiles source #f)))
diff --git a/tests/migrations/Makefile.am b/tests/migrations/Makefile.am
index 0377b23d6..9c82d66ee 100644
--- a/tests/migrations/Makefile.am
+++ b/tests/migrations/Makefile.am
@@ -1,53 +1,56 @@
# Makefile.am - For tests/openpgp
# Copyright (C) 2016 g10 Code GmbH
#
# This file is part of GnuPG.
#
# GnuPG 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.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# Process this file with automake to create Makefile.in
# Programs required before we can run these tests.
required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT)
AM_CPPFLAGS = -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS =
+TMP ?= /tmp
+
TESTS_ENVIRONMENT = GPG_AGENT_INFO= LC_ALL=C \
PATH=../gpgscm:$(PATH) \
+ TMP=$(TMP) \
GPGSCM_PATH=$(top_srcdir)/tests/gpgscm:$(top_srcdir)/tests/migrations
TESTS = from-classic.scm \
extended-pkf.scm
TEST_FILES = from-classic.tar.asc \
extended-pkf.tar.asc
EXTRA_DIST = common.scm $(TESTS) $(TEST_FILES)
CLEANFILES = prepared.stamp x y yy z out err $(data_files) \
plain-1 plain-2 plain-3 trustdb.gpg *.lock .\#lk* \
*.test.log gpg_dearmor gpg.conf gpg-agent.conf S.gpg-agent \
pubring.gpg pubring.gpg~ pubring.kbx pubring.kbx~ \
secring.gpg pubring.pkr secring.skr \
gnupg-test.stop random_seed gpg-agent.log tofu.db
clean-local:
-rm -rf from-classic.gpghome/*.gpg
# We need to depend on a couple of programs so that the tests don't
# start before all programs are built.
all-local: $(required_pgms)
diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am
index 4137018d1..012a3f20c 100644
--- a/tests/openpgp/Makefile.am
+++ b/tests/openpgp/Makefile.am
@@ -1,164 +1,167 @@
# Makefile.am - For tests/openpgp
# Copyright (C) 1998, 1999, 2000, 2001, 2003,
# 2010 Free Software Foundation, Inc.
#
# This file is part of GnuPG.
#
# GnuPG 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.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# Process this file with automake to create Makefile.in
# Programs required before we can run these tests.
required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT) \
../../tools/gpg-connect-agent$(EXEEXT) \
../../tools/mk-tdata$(EXEEXT) \
../gpgscm/gpgscm$(EXEEXT)
AM_CPPFLAGS = -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS =
noinst_PROGRAMS = fake-pinentry
fake_pinentry_SOURCES = fake-pinentry.c
+TMP ?= /tmp
+
TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= LC_ALL=C \
EXEEXT=$(EXEEXT) \
PATH=../gpgscm:$(PATH) \
+ TMP=$(TMP) \
objdir=$(abs_top_builddir) \
GPGSCM_PATH=$(top_srcdir)/tests/gpgscm:$(top_srcdir)/tests/openpgp
# Note: setup.scm needs to be the first test to run and finish.scm
# the last one
TESTS = setup.scm \
version.scm \
mds.scm \
decrypt.scm \
decrypt-dsa.scm \
sigs.scm \
sigs-dsa.scm \
encrypt.scm \
encrypt-dsa.scm \
seat.scm \
clearsig.scm \
encryptp.scm \
detach.scm \
detachm.scm \
armsigs.scm \
armencrypt.scm \
armencryptp.scm \
signencrypt.scm \
signencrypt-dsa.scm \
armsignencrypt.scm \
armdetach.scm \
armdetachm.scm \
genkey1024.scm \
conventional.scm \
conventional-mdc.scm \
multisig.scm \
verify.scm \
armor.scm \
import.scm \
ecc.scm \
4gb-packet.scm \
tofu.scm \
gpgtar.scm \
use-exact-key.scm \
default-key.scm \
export.scm \
finish.scm
TEST_FILES = pubring.asc secring.asc plain-1o.asc plain-2o.asc plain-3o.asc \
plain-1.asc plain-2.asc plain-3.asc plain-1-pgp.asc \
plain-largeo.asc \
pubring.pkr.asc secring.skr.asc secdemo.asc pubdemo.asc \
gpg.conf.tmpl gpg-agent.conf.tmpl \
bug537-test.data.asc bug894-test.asc \
bug1223-good.asc bug1223-bogus.asc 4gb-packet.asc \
tofu-keys.asc tofu-keys-secret.asc \
tofu-2183839A-1.txt tofu-BC15C85A-1.txt tofu-EE37CF96-1.txt
data_files = data-500 data-9000 data-32000 data-80000 plain-large
priv_keys = privkeys/50B2D4FA4122C212611048BC5FC31BD44393626E.asc \
privkeys/7E201E28B6FEB2927B321F443205F4724EBE637E.asc \
privkeys/13FDB8809B17C5547779F9D205C45F47CE0217CE.asc \
privkeys/343D8AF79796EE107D645A2787A9D9252F924E6F.asc \
privkeys/8B5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34.asc \
privkeys/0D6F6AD4C4C803B25470F9104E9F4E6A4CA64255.asc \
privkeys/FD692BD59D6640A84C8422573D469F84F3B98E53.asc \
privkeys/76F7E2B35832976B50A27A282D9B87E44577EB66.asc \
privkeys/A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD.asc \
privkeys/0DD40284FF992CD24DC4AAC367037E066FCEE26A.asc \
privkeys/2BC997C0B8691D41D29A4EC81CCBCF08454E4961.asc \
privkeys/3C9D5ECA70130C2DBB1FC6AC0076BEEEC197716F.asc \
privkeys/449E644892C951A37525654730DD32C202079926.asc \
privkeys/58FFE844087634E62440224908BDE44BEA7EB730.asc \
privkeys/4DF9172D6FF428C97A0E9AA96F03E8BCE3B2F188.asc \
privkeys/9D7CD8F53F2F14C3E2177D1E9D1D11F39513A4A4.asc \
privkeys/6E6B7ED0BD4425018FFC54F3921D5467A3AE00EB.asc \
privkeys/C905D0AB6AE9655C5A35975939997BBF3325D6DD.asc \
privkeys/B2BAA7144303DF19BB6FDE23781DD3FDD97918D4.asc \
privkeys/CF60965BF51F67CF80DECE853E0D2D343468571D.asc \
privkeys/DF00E361D34F80868D06879AC21D7A7D4E4FAD76.asc \
privkeys/00FE67F28A52A8AA08FFAED20AF832DA916D1985.asc \
privkeys/1DF48228FEFF3EC2481B106E0ACA8C465C662CC5.asc \
privkeys/A2832820DC9F40751BDCD375BB0945BA33EC6B4C.asc \
privkeys/ADE710D74409777B7729A7653373D820F67892E0.asc \
privkeys/CEFC51AF91F68A2904FBFF62C4F075A4785B803F.asc \
privkeys/1E28F20E41B54C2D1234D896096495FF57E08D18.asc \
privkeys/EB33B687EB8581AB64D04852A54453E85F3DF62D.asc \
privkeys/C6A6390E9388CDBAD71EAEA698233FE5E04F001E.asc \
privkeys/D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc
sample_keys = samplekeys/README \
samplekeys/ecc-sample-1-pub.asc \
samplekeys/ecc-sample-2-pub.asc \
samplekeys/ecc-sample-3-pub.asc \
samplekeys/ecc-sample-1-sec.asc \
samplekeys/ecc-sample-2-sec.asc \
samplekeys/ecc-sample-3-sec.asc \
samplekeys/eddsa-sample-1-pub.asc \
samplekeys/eddsa-sample-1-sec.asc \
samplekeys/dda252ebb8ebe1af-1.asc \
samplekeys/dda252ebb8ebe1af-2.asc \
samplekeys/whats-new-in-2.1.asc \
samplekeys/e2e-p256-1-clr.asc \
samplekeys/e2e-p256-1-prt.asc \
samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc \
samplekeys/rsa-rsa-sample-1.asc \
samplekeys/ed25519-cv25519-sample-1.asc \
samplekeys/silent-running.asc
EXTRA_DIST = defs.inc defs.scm pinentry.sh $(TESTS) $(TEST_FILES) \
mkdemodirs signdemokey $(priv_keys) $(sample_keys) \
ChangeLog-2011
CLEANFILES = prepared.stamp x y yy z out err $(data_files) \
plain-1 plain-2 plain-3 trustdb.gpg *.lock .\#lk* \
*.test.log gpg_dearmor gpg.conf gpg-agent.conf S.gpg-agent \
pubring.gpg pubring.gpg~ pubring.kbx pubring.kbx~ \
secring.gpg pubring.pkr secring.skr \
gnupg-test.stop random_seed gpg-agent.log tofu.db \
passphrases
clean-local:
-rm -rf private-keys-v1.d openpgp-revocs.d tofu.d gpgtar.d
# We need to depend on a couple of programs so that the tests don't
# start before all programs are built.
all-local: $(required_pgms)
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 7:17 PM (1 h, 1 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
28/b6/f0f17304c879064c0e5069fad3a3
Attached To
rG GnuPG
Event Timeline
Log In to Comment