]>
Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Loader.pm
209a2a6976aaa4da24e30108d08ddbdaecb7561c
1 package File
::KDBX
::Loader
;
2 # ABSTRACT: Load KDBX files
7 use File
::KDBX
::Constants
qw(:magic :header :version);
9 use File
::KDBX
::Util
qw(:class :io);
13 use Ref
::Util
qw(is_ref is_scalarref);
14 use Scalar
::Util
qw(looks_like_number openhandle);
17 our $VERSION = '0.800'; # VERSION
22 my $self = bless {}, $class;
31 @$self{keys %args} = values %args;
38 my $format = shift // $self->format;
40 my $sig2 = $self->kdbx->sig2;
41 my $version = $self->kdbx->version;
45 if (defined $format) {
48 elsif (defined $sig2 && $sig2 == KDBX_SIG2_1
) {
51 elsif (looks_like_number
($version)) {
52 my $major = $version & KDBX_VERSION_MAJOR_MASK
;
54 KDBX_VERSION_2_0
() => 'V3',
55 KDBX_VERSION_3_0
() => 'V3',
56 KDBX_VERSION_4_0
() => 'V4',
58 $subclass = $subclasses{$major}
59 or throw
sprintf('Unsupported KDBX file version: %x', $version), version
=> $version;
62 throw
sprintf('Unknown file version: %s', $version), version
=> $version;
65 Module
::Load
::load
"File::KDBX::Loader::$subclass";
66 bless $self, "File::KDBX::Loader::$subclass";
80 return $self->load_handle($src, @_) if openhandle
($src) || $src eq '-';
81 return $self->load_string($src, @_) if is_scalarref
($src);
82 return $self->load_file($src, @_) if !is_ref
($src) && defined $src;
83 throw
'Programmer error: Must pass a stringref, filepath or IO handle to read';
89 my $str = shift or throw
'Expected string to load';
90 my %args = @_ % 2 == 0 ? @_ : (key
=> shift, @_);
92 my $key = delete $args{key
};
93 $args{kdbx
} //= $self->kdbx;
95 my $ref = is_scalarref
($str) ? $str : \
$str;
97 open(my $fh, '<', $ref) or throw
"Failed to open string buffer: $!";
99 $self = $self->new if !ref $self;
100 $self->init(%args, fh
=> $fh)->_read($fh, $key);
107 my $filepath = shift;
108 my %args = @_ % 2 == 0 ? @_ : (key
=> shift, @_);
110 my $key = delete $args{key
};
111 $args{kdbx
} //= $self->kdbx;
113 open(my $fh, '<:raw', $filepath) or throw
'Open file failed', filepath
=> $filepath;
115 $self = $self->new if !ref $self;
116 $self->init(%args, fh
=> $fh, filepath
=> $filepath)->_read($fh, $key);
124 my %args = @_ % 2 == 0 ? @_ : (key
=> shift, @_);
126 $fh = *STDIN
if $fh eq '-';
128 my $key = delete $args{key
};
129 $args{kdbx
} //= $self->kdbx;
131 $self = $self->new if !ref $self;
132 $self->init(%args, fh
=> $fh)->_read($fh, $key);
139 return File
::KDBX-
>new if !ref $self;
140 $self->{kdbx
} = shift if @_;
141 $self->{kdbx
} //= File
::KDBX-
>new;
145 has format
=> undef, is => 'ro';
146 has inner_format
=> 'XML', is => 'ro';
149 sub min_version
{ KDBX_VERSION_OLDEST
}
152 sub read_magic_numbers
{
155 my $kdbx = shift // $self->kdbx;
157 read_all
$fh, my $magic, 12 or throw
'Failed to read file signature';
159 my ($sig1, $sig2, $version) = unpack('L<3', $magic);
164 $kdbx->version($version);
165 $self->_rebless if ref $self;
168 return wantarray ? ($sig1, $sig2, $version, $magic) : $magic;
171 sub _fh
{ $_[0]->{fh
} or throw
'IO handle not set' }
178 my $kdbx = $self->kdbx;
179 $key //= $kdbx->key ? $kdbx->key->reload : undef;
182 read_all
$fh, my $buf, 1 or throw
'Failed to read the first byte', type
=> 'parser';
183 my $first = ord($buf);
185 if ($first != KDBX_SIG1_FIRST_BYTE
) {
186 # not a KDBX file... try skipping the outer layer
187 return $self->_read_inner_body($fh);
190 my $magic = $self->read_magic_numbers($fh, $kdbx);
191 $kdbx->sig1 == KDBX_SIG1
or throw
'Invalid file signature', type
=> 'parser', sig1
=> $kdbx->sig1;
193 if (ref($self) =~ /::(?:KDB|V[34])$/) {
194 defined $key or throw
'Must provide a master key', type
=> 'key.missing';
197 my $headers = $self->_read_headers($fh);
200 $self->_read_body($fh, $key, "$magic$headers");
203 throw
"Failed to load KDBX file: $err",
205 compression_error
=> $IO::Uncompress
::Gunzip
::GunzipError
,
206 crypt_error
=> $File::KDBX
::IO
::Crypt
::ERROR
,
207 hash_error
=> $File::KDBX
::IO
::HashBLock
::ERROR
,
208 hmac_error
=> $File::KDBX
::IO
::HmacBLock
::ERROR
;
216 my $headers = $self->kdbx->headers;
219 while (my ($type, $val, $raw) = $self->_read_header($fh)) {
221 last if $type == HEADER_END
;
222 $headers->{$type} = $val;
228 sub _read_body
{ die "Not implemented" }
230 sub _read_inner_body
{
233 my $current_pkg = ref $self;
234 require Scope
::Guard
;
235 my $guard = Scope
::Guard-
>new(sub { bless $self, $current_pkg });
237 $self->_rebless($self->inner_format);
238 $self->_read_inner_body(@_);
251 File::KDBX::Loader - Load KDBX files
263 $kdbx = $loader->kdbx;
264 $loader->kdbx($kdbx);
266 Get or set the L<File::KDBX> instance for storing the loaded data into.
270 Get the file format used for reading the database. Normally the format is auto-detected from the data stream.
271 This auto-detection works well, so there's not really a good reason to explicitly specify the format.
300 Get the format of the data inside the KDBX envelope. This only applies to C<V3> and C<V4> formats. Possible
307 C<XML> - Read the database groups and entries as XML (default)
311 C<Raw> - Read parsing and store the result in L<File::KDBX/raw>
319 $loader = File::KDBX::Loader->new(%attributes);
321 Construct a new L<File::KDBX::Loader>.
325 $loader = $loader->init(%attributes);
327 Initialize a L<File::KDBX::Loader> with a new set of attributes.
329 This is called by L</new>.
333 $loader = $loader->reset;
335 Set a L<File::KDBX::Loader> to a blank state, ready to load another KDBX file.
339 $kdbx = File::KDBX::Loader->load(\$string, $key);
340 $kdbx = File::KDBX::Loader->load(*IO, $key);
341 $kdbx = File::KDBX::Loader->load($filepath, $key);
342 $kdbx = $loader->load(...); # also instance method
346 The C<$key> is either a L<File::KDBX::Key> or a primitive that can be converted to a Key object.
350 $kdbx = File::KDBX::Loader->load_string($string, $key);
351 $kdbx = File::KDBX::Loader->load_string(\$string, $key);
352 $kdbx = $loader->load_string(...); # also instance method
354 Load a KDBX file from a string / memory buffer.
358 $kdbx = File::KDBX::Loader->load_file($filepath, $key);
359 $kdbx = $loader->load_file(...); # also instance method
361 Read a KDBX file from a filesystem.
365 $kdbx = File::KDBX::Loader->load_handle($fh, $key);
366 $kdbx = File::KDBX::Loader->load_handle(*IO, $key);
367 $kdbx->load_handle(...); # also instance method
369 Read a KDBX file from an input stream / file handle.
373 $min_version = File::KDBX::Loader->min_version;
375 Get the minimum KDBX file version supported, which is 3.0 or C<0x00030000> as
378 To read older KDBX files unsupported by this module, try L<File::KeePass>.
380 =head2 read_magic_numbers
382 $magic = File::KDBX::Loader->read_magic_numbers($fh);
383 ($sig1, $sig2, $version, $magic) = File::KDBX::Loader->read_magic_numbers($fh);
385 $magic = $loader->read_magic_numbers($fh);
386 ($sig1, $sig2, $version, $magic) = $loader->read_magic_numbers($fh);
388 Read exactly 12 bytes from an IO handle and parse them into the three magic numbers that begin
389 a KDBX file. This is a quick way to determine if a file is actually a KDBX file.
391 C<$sig1> should always be C<KDBX_SIG1> if reading an actual KDB or KDBX file.
393 C<$sig2> should be C<KDBX_SIG2_1> for KeePass 1 files and C<KDBX_SIG2_2> for KeePass 2 files.
395 C<$version> is the file version (e.g. C<0x00040001>).
397 C<$magic> is the raw 12 bytes read from the IO handle.
399 If called on an instance, the C<sig1>, C<sig2> and C<version> attributes will be set in the L</kdbx>
400 and the instance will be blessed into the correct loader subclass.
404 Please report any bugs or feature requests on the bugtracker website
405 L<https://github.com/chazmcgarvey/File-KDBX/issues>
407 When submitting a bug or request, please include a test-file or a
408 patch to an existing test-file that illustrates the bug or desired
413 Charles McGarvey <ccm@cpan.org>
415 =head1 COPYRIGHT AND LICENSE
417 This software is copyright (c) 2022 by Charles McGarvey.
419 This is free software; you can redistribute it and/or modify it under
420 the same terms as the Perl 5 programming language system itself.
This page took 0.055223 seconds and 3 git commands to generate.