pax_global_header00006660000000000000000000000064123342063200014505gustar00rootroot0000000000000052 comment=cbec6648b1574ab5e208714ffd417c8f418bcc37 perl-Config-Identity-0.0018/000075500000000000000000000000001233420632000155275ustar00rootroot00000000000000perl-Config-Identity-0.0018/Changes000064400000000000000000000015131233420632000170220ustar00rootroot00000000000000TODO: - Trying GPG on empty file 0.0018 Saturday March 01 13:40:31 PST 2014: - Be more sane with how we interact with gpg 0.0017 Saturday March 01 11:12:09 PST 2014: - Use IPC::Run instead of IPC::Open3 0.0016 Wednesday June 16 23:26:25 PDT 2010: - Added CI_PAUSE_STUB/CI_GITHUB_STUB override 0.0015 Tuesday June 08 15:57:29 PDT 2010: - Added username/user aliasing to the PAUSE format (and loader) 0.0014 Wednesday June 02 20:23:51 PDT 2010: - Changed load_best to try_best and added a stricter load_best 0.0013 2010-06-12: - Surpress secmem warning during testing - Added documentation about CI_{GPG/CI_GPG_ARGUMENTS} - Put in information about using gpg-agent 0.0012 Tuesday May 11 23:59:01 PDT 2010: - Updated documentation 0.0010 Tuesday May 11 18:32:09 PDT 2010: - Initial release perl-Config-Identity-0.0018/MANIFEST000064400000000000000000000010651233420632000166620ustar00rootroot00000000000000# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.013. Changes MANIFEST META.yml Makefile.PL README lib/Config/Identity.pm lib/Config/Identity/GitHub.pm lib/Config/Identity/PAUSE.pm t/.01-basic.t.swp t/01-basic.t t/assets/github/.github t/assets/gpg/pubring.gpg t/assets/gpg/random_seed t/assets/gpg/secring.gpg t/assets/gpg/trustdb.gpg t/assets/h0/.test t/assets/h0/.test-identity t/assets/h1/.test t/assets/pause-username/.pause-identity t/assets/pause/.pause-alternate t/assets/pause/.pause-identity t/assets/test.asc t/assets/test.gpg perl-Config-Identity-0.0018/META.yml000064400000000000000000000010321233420632000167740ustar00rootroot00000000000000--- abstract: 'Load (and optionally decrypt via GnuPG) user/pass identity information ' author: - 'Robert Krimen ' build_requires: Test::Most: 0 configure_requires: ExtUtils::MakeMaker: 6.30 dynamic_config: 0 generated_by: 'Dist::Zilla version 5.013, CPAN::Meta::Converter version 2.130880' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Config-Identity requires: File::HomeDir: 0 File::Spec: 0 File::Which: 0 IPC::Run: 0 version: 0.0018 perl-Config-Identity-0.0018/Makefile.PL000064400000000000000000000023321233420632000175010ustar00rootroot00000000000000 # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.013. use strict; use warnings; use ExtUtils::MakeMaker 6.30; my %WriteMakefileArgs = ( "ABSTRACT" => "Load (and optionally decrypt via GnuPG) user/pass identity information ", "AUTHOR" => "Robert Krimen ", "BUILD_REQUIRES" => {}, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.30" }, "DISTNAME" => "Config-Identity", "EXE_FILES" => [], "LICENSE" => "perl", "NAME" => "Config::Identity", "PREREQ_PM" => { "File::HomeDir" => 0, "File::Spec" => 0, "File::Which" => 0, "IPC::Run" => 0 }, "TEST_REQUIRES" => { "Test::Most" => 0 }, "VERSION" => "0.0018", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "File::HomeDir" => 0, "File::Spec" => 0, "File::Which" => 0, "IPC::Run" => 0, "Test::Most" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); perl-Config-Identity-0.0018/README000064400000000000000000000063741233420632000164210ustar00rootroot00000000000000NAME Config::Identity - Load (and optionally decrypt via GnuPG) user/pass identity information VERSION version 0.0018 SYNOPSIS PAUSE: use Config::Identity::PAUSE; # 1. Find either $HOME/.pause-identity or $HOME/.pause # 2. Decrypt the found file (if necessary), read, and parse it # 3. Throw an exception unless %identity has 'user' and 'password' defined my %identity = Config::Identity::PAUSE->load; print "user: $identity{user} password: $identity{password}\n"; GitHub API: use Config::Identity::GitHub; # 1. Find either $HOME/.github-identity or $HOME/.github # 2. Decrypt the found file (if necessary) read, and parse it # 3. Throw an exception unless %identity has 'login' and 'token' defined my %identity = Config::Identity::PAUSE->load; print "login: $identity{login} token: $identity{token}\n"; DESCRIPTION Config::Identity is a tool for loadiing (and optionally decrypting via GnuPG) user/pass identity information For GitHub API access, an identity is a "login"/"token" pair For PAUSE access, an identity is a "user"/"password" pair USAGE %identity = Config::Identity->load_best( ) First attempt to load an identity from $HOME/.-identity If that file does not exist, then attempt to load an identity from $HOME/. The file may be optionally GnuPG encrypted %identity will be populated like so: For example: username alice password hunter2 Using a custom "gpg" or passing custom arguments You can specify a custom "gpg" executable by setting the CI_GPG environment variable export CI_GPG="$HOME/bin/gpg" You can pass custom arguments by setting the CI_GPG_ARGUMENTS environment variable export CI_GPG_ARGUMENTS="--no-secmem-warning" Encrypting your identity information with GnuPG If you've never used GnuPG before, first initialize it: # Follow the prompts to create a new key for yourself gpg --gen-key To encrypt your GitHub identity with GnuPG using the above key: # Follow the prompts, using the above key as the "recipient" # Use ^D once you've finished typing out your authentication information gpg -ea > $HOME/.github Caching your GnuPG secret key via gpg-agent Put the following in your .*rc if which gpg-agent 1>/dev/null then if test -f $HOME/.gpg-agent-info && \ kill -0 `cut -d: -f 2 $HOME/.gpg-agent-info` 2>/dev/null then . "${HOME}/.gpg-agent-info" export GPG_AGENT_INFO else eval `gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info"` fi else fi PAUSE identity format user password "username" can also be used as alias for "user" GitHub identity format login token AUTHOR Robert Krimen COPYRIGHT AND LICENSE This software is copyright (c) 2014 by Robert Krimen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. perl-Config-Identity-0.0018/lib/000075500000000000000000000000001233420632000162755ustar00rootroot00000000000000perl-Config-Identity-0.0018/lib/Config/000075500000000000000000000000001233420632000175025ustar00rootroot00000000000000perl-Config-Identity-0.0018/lib/Config/Identity.pm000064400000000000000000000142201233420632000216300ustar00rootroot00000000000000package Config::Identity; # ABSTRACT: Load (and optionally decrypt via GnuPG) user/pass identity information $Config::Identity::VERSION = '0.0018'; use strict; use warnings; use Carp; use IPC::Run qw/ start finish /; use File::HomeDir(); use File::Spec; our $home = File::HomeDir->home; { my $gpg; sub GPG() { $ENV{CI_GPG} || ( $gpg ||= do { require File::Which; $gpg = File::Which::which( $_ ) and last for qw/ gpg gpg2 /; $gpg; } ) } } sub GPG_ARGUMENTS() { $ENV{CI_GPG_ARGUMENTS} || '' } # TODO Do not even need to do this, since the file is on disk already... sub decrypt { my $self = shift; my $file = shift; my $gpg = GPG or croak "Missing gpg"; my $gpg_arguments = GPG_ARGUMENTS; my $run; # Old versions, please ignore #$run = "$gpg $gpg_arguments -qd --no-tty --command-fd 0 --status-fd 1"; #$run = "$gpg $gpg_arguments -qd --no-tty --command-fd 0"; $run = "$gpg $gpg_arguments -qd --no-tty"; my @run = split m/\s+/, $run; push @run, $file; my $process = start( \@run, '>pipe', \*OUT, '2>pipe', \*ERR ); my $output = join '', ; my $_error = join '', ; finish $process; return ( $output, $_error ); } sub best { my $self = shift; my $stub = shift; my $base = shift; $base = $home unless defined $base; croak "Missing stub" unless defined $stub && length $stub; for my $i0 ( ".$stub-identity", ".$stub" ) { for my $i1 ( "." ) { my $path = File::Spec->catfile( $base, $i1, $i0 ); return $path if -f $path; } } return ''; } sub read { my $self = shift; my $file = shift; croak "Missing file" unless -f $file; croak "Cannot read file ($file)" unless -r $file; my $binary = -B $file; open my $handle, $file or croak $!; binmode $handle if $binary; local $/ = undef; my $content = <$handle>; close $handle or warn $!; if ( $binary || $content =~ m/----BEGIN PGP MESSAGE----/ ) { my ( $_content, $error ) = $self->decrypt( $file ); if ( $error ) { carp "Error during decryption of content" . $binary ? '' : "\n$content"; croak "Error during decryption of $file:\n$error"; } $content = $_content; } return $content; } sub parse { my $self = shift; my $content = shift; return unless $content; my %content; for ( split m/\n/, $content ) { next if /^\s*#/; next unless m/\S/; next unless my ($key, $value) = /^\s*(\w+)\s+(.+)$/; $content{$key} = $value; } return %content; } sub load_best { my $self = shift; my $stub = shift; die "Unable to find .$stub-identity or .$stub" unless my $path = $self->best( $stub ); return $self->load( $path ); } sub try_best { my $self = shift; my $stub = shift; return unless my $path = $self->best( $stub ); return $self->load( $path ); } sub load { my $self = shift; my $file = shift; return $self->parse( $self->read( $file ) ); } 1; __END__ =pod =head1 NAME Config::Identity - Load (and optionally decrypt via GnuPG) user/pass identity information =head1 VERSION version 0.0018 =head1 SYNOPSIS PAUSE: use Config::Identity::PAUSE; # 1. Find either $HOME/.pause-identity or $HOME/.pause # 2. Decrypt the found file (if necessary), read, and parse it # 3. Throw an exception unless %identity has 'user' and 'password' defined my %identity = Config::Identity::PAUSE->load; print "user: $identity{user} password: $identity{password}\n"; GitHub API: use Config::Identity::GitHub; # 1. Find either $HOME/.github-identity or $HOME/.github # 2. Decrypt the found file (if necessary) read, and parse it # 3. Throw an exception unless %identity has 'login' and 'token' defined my %identity = Config::Identity::PAUSE->load; print "login: $identity{login} token: $identity{token}\n"; =head1 DESCRIPTION Config::Identity is a tool for loadiing (and optionally decrypting via GnuPG) user/pass identity information For GitHub API access, an identity is a C/C pair For PAUSE access, an identity is a C/C pair =head1 USAGE =head2 %identity = Config::Identity->load_best( ) First attempt to load an identity from $HOME/.-identity If that file does not exist, then attempt to load an identity from $HOME/. The file may be optionally GnuPG encrypted %identity will be populated like so: For example: username alice password hunter2 =head1 Using a custom C or passing custom arguments You can specify a custom C executable by setting the CI_GPG environment variable export CI_GPG="$HOME/bin/gpg" You can pass custom arguments by setting the CI_GPG_ARGUMENTS environment variable export CI_GPG_ARGUMENTS="--no-secmem-warning" =head1 Encrypting your identity information with GnuPG If you've never used GnuPG before, first initialize it: # Follow the prompts to create a new key for yourself gpg --gen-key To encrypt your GitHub identity with GnuPG using the above key: # Follow the prompts, using the above key as the "recipient" # Use ^D once you've finished typing out your authentication information gpg -ea > $HOME/.github =head1 Caching your GnuPG secret key via gpg-agent Put the following in your .*rc if which gpg-agent 1>/dev/null then if test -f $HOME/.gpg-agent-info && \ kill -0 `cut -d: -f 2 $HOME/.gpg-agent-info` 2>/dev/null then . "${HOME}/.gpg-agent-info" export GPG_AGENT_INFO else eval `gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info"` fi else fi =head1 PAUSE identity format user password C can also be used as alias for C =head1 GitHub identity format login token =head1 AUTHOR Robert Krimen =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2014 by Robert Krimen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut perl-Config-Identity-0.0018/lib/Config/Identity/000075500000000000000000000000001233420632000212735ustar00rootroot00000000000000perl-Config-Identity-0.0018/lib/Config/Identity/GitHub.pm000064400000000000000000000012061233420632000230120ustar00rootroot00000000000000package Config::Identity::GitHub; use strict; use warnings; use Config::Identity; use Carp; our $STUB = 'github'; sub STUB { defined $_ and return $_ for $ENV{CI_GITHUB_STUB}, $STUB } sub load { my $self = shift; return Config::Identity->try_best( $self->STUB ); } sub check { my $self = shift; my %identity = @_; my @missing; defined $identity{$_} && length $identity{$_} or push @missing, $_ for qw/ login token /; croak "Missing ", join ' and ', @missing if @missing; } sub load_check { my $self = shift; my %identity = $self->load; $self->check( %identity ); return %identity; } 1; perl-Config-Identity-0.0018/lib/Config/Identity/PAUSE.pm000064400000000000000000000015551233420632000225140ustar00rootroot00000000000000package Config::Identity::PAUSE; use strict; use warnings; use Config::Identity; use Carp; our $STUB = 'pause'; sub STUB { defined $_ and return $_ for $ENV{CI_PAUSE_STUB}, $STUB } sub load { my $self = shift; my %identity = Config::Identity->try_best( $self->STUB ); $identity{user} = $identity{username} if exists $identity{username} && ! exists $identity{user}; $identity{username} = $identity{user} if exists $identity{user} && ! exists $identity{username}; return %identity; } sub check { my $self = shift; my %identity = @_; my @missing; defined $identity{$_} && length $identity{$_} or push @missing, $_ for qw/ user password /; croak "Missing ", join ' and ', @missing if @missing; } sub load_check { my $self = shift; my %identity = $self->load; $self->check( %identity ); return %identity; } 1; perl-Config-Identity-0.0018/t/000075500000000000000000000000001233420632000157725ustar00rootroot00000000000000perl-Config-Identity-0.0018/t/.01-basic.t.swp000064400000000000000000000300001233420632000203350ustar00rootroot00000000000000b0VIM 7.3SS>sDzxlf90c32fa.local~zxl/develop/Config-Identity/t/01-basic.tutf-8 3210#"! UtpTadZT{^]-KJ=< { , T S 6  O ? > 8 2 1  ~ x r q O I s m l K E `ZTf`^]Z1;1;} } cmp_deeply( \%identity, {qw/ username alice user alice password hunter3 /} ); my %identity = Config::Identity::PAUSE->load_check; local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause-username /); { } cmp_deeply( \%identity, {qw/ username alice user alice password hunter2 /} ); my %identity = Config::Identity::PAUSE->load_check; local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause /); { use Config::Identity::PAUSE; } cmp_deeply( \%identity, {qw/ login alice token hunter2 /} ); my %identity = Config::Identity::GitHub->load_check; local $Config::Identity::home = File::Spec->catfile(qw/ t assets github /); { use Config::Identity::GitHub; }_END_.1 2 3 4 5 6 7 8 9 0ABCDEFGHIJKLMNOPQRSTUVWXYZ is( Config::Identity->read( File::Spec->catfile(qw/ t assets test.gpg /) ), <<_END_ ); if ($ENV{RELEASE_TESTING}) {_END_# 1231234567890xyzzy is( Config::Identity->read( File::Spec->catfile(qw/ t assets test.asc /) ), <<_END_ ); ; '--homedir ' . File::Spec->catfile(qw/ t assets gpg /) '--no-permission-warning ' . '--no-secmem-warning ' . $ENV{CI_GPG_ARGUMENTS} = skip 'GnuPG not available' unless Config::Identity->GPG;SKIP: {} cmp_deeply( \%identity, {qw/ username alternate user alternate password xyzzy /} ); my %identity = Config::Identity::PAUSE->load_check; local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause /); local $ENV{CI_PAUSE_STUB} = 'pause-alternate';{cmp_deeply( \%cfg, {qw/ apple a1 banana b2 /} );_END_ banana b2apple a1 # Xyzzy%cfg = Config::Identity->parse( <<'_END_' );my ( %cfg );is( Config::Identity->best( 'test' => $h1 ), File::Spec->catfile( $h1, '.test' ) );is( Config::Identity->best( 'test' => $h0 ), File::Spec->catfile( $h0, '.test-identity' ) );my $h1 = File::Spec->catdir(qw/ t assets h1 /);my $h0 = File::Spec->catdir(qw/ t assets h0 /);use Config::Identity::PAUSE;use Config::Identity::GitHub;use Config::Identity;plan 'no_plan';use Test::Most;use warnings;use strict;#!/usr/bin/env perlperl-Config-Identity-0.0018/t/01-basic.t000064400000000000000000000042461233420632000174640ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; use Test::Most; plan 'no_plan'; use Config::Identity; use Config::Identity::GitHub; use Config::Identity::PAUSE; my $h0 = File::Spec->catdir(qw/ t assets h0 /); my $h1 = File::Spec->catdir(qw/ t assets h1 /); is( Config::Identity->best( 'test' => $h0 ), File::Spec->catfile( $h0, '.test-identity' ) ); is( Config::Identity->best( 'test' => $h1 ), File::Spec->catfile( $h1, '.test' ) ); my ( %cfg ); %cfg = Config::Identity->parse( <<'_END_' ); # Xyzzy apple a1 banana b2 _END_ cmp_deeply( \%cfg, {qw/ apple a1 banana b2 /} ); { local $ENV{CI_PAUSE_STUB} = 'pause-alternate'; local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause /); my %identity = Config::Identity::PAUSE->load_check; cmp_deeply( \%identity, {qw/ username alternate user alternate password xyzzy /} ); } SKIP: { skip 'GnuPG not available' unless Config::Identity->GPG; $ENV{CI_GPG_ARGUMENTS} = '--no-secmem-warning ' . '--no-permission-warning ' . '--homedir ' . File::Spec->catfile(qw/ t assets gpg /) ; is( Config::Identity->read( File::Spec->catfile(qw/ t assets test.asc /) ), <<_END_ ); 1234567890xyzzy # 123 _END_ if ($ENV{RELEASE_TESTING}) { is( Config::Identity->read( File::Spec->catfile(qw/ t assets test.gpg /) ), <<_END_ ); ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 2 3 4 5 6 7 8 9 0 . _END_ } use Config::Identity::GitHub; { local $Config::Identity::home = File::Spec->catfile(qw/ t assets github /); my %identity = Config::Identity::GitHub->load_check; cmp_deeply( \%identity, {qw/ login alice token hunter2 /} ); } use Config::Identity::PAUSE; { local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause /); my %identity = Config::Identity::PAUSE->load_check; cmp_deeply( \%identity, {qw/ username alice user alice password hunter2 /} ); } { local $Config::Identity::home = File::Spec->catfile(qw/ t assets pause-username /); my %identity = Config::Identity::PAUSE->load_check; cmp_deeply( \%identity, {qw/ username alice user alice password hunter3 /} ); } } 1; perl-Config-Identity-0.0018/t/assets/000075500000000000000000000000001233420632000172745ustar00rootroot00000000000000perl-Config-Identity-0.0018/t/assets/github/000075500000000000000000000000001233420632000205565ustar00rootroot00000000000000perl-Config-Identity-0.0018/t/assets/github/.github000064400000000000000000000011501233420632000220360ustar00rootroot000000000000002y^rhB𖮔4p?Y |LP5 :m&WPOR(0gs 7=TN[8jF2])!V﬋-57R( f'1b9OL,uP7څfp=GHB쏁6 CT/>z-* NE> p|D0Ֆ̠úx+b?X]h|Q쌇㯶QǤ/7sP7PJRإ$;yOw//K1q[a~>g(.0֍2+A5gjUp{pkvwiƿsd5 6f\ҫ:;,!nQ!9 c(yvΓć8C=;,R`Ydي+bapE8'aV@p G挝8+|8R)B/rB?tfg".jG9uBv [kjm\zRN" CJKo@,UEQX|nGglr&(PDzWp\ZǪ%dG$Ϥ~qQZ.?/B! perl-Config-Identity-0.0018/t/assets/gpg/000075500000000000000000000000001233420632000200515ustar00rootroot00000000000000perl-Config-Identity-0.0018/t/assets/gpg/pubring.gpg000064400000000000000000000021721233420632000222200ustar00rootroot00000000000000Kc ["Һ5P_ ">u%򾕇:YAvlR mi%Ub >k x"$h%U29ЇGf>{W6o5]pVnIn9bp-kꪞFƶ7}M,H+֣"D`Ӣ`_5'-d.əi例p`-.)CiffTꑙ ǜj4ޒfaE13DG~l[W>kbǝ1H\ʙFl4@I#H_nK(0&Hʱ 'h4Vif׷Z0:fa FYI{*sLۭ ?}e-4uYuSG,q/V\\}f0H0TcF1 rqGRI4NL,֒ | [ZT>qVؔ ; X"ߕlmc1}{"_(qzw@H1atҬ+XOdEz 'yIJ4l:eXKCmoj:֩f^0g,X4*Wkm^-a5Q-uD{j+ERg;"t@؂#AUU=G+ \?A`;  #*I772MɜvKI Kc T':=D(J%r519-eǑaUcF ;)9ưperl-Config-Identity-0.0018/t/assets/gpg/random_seed000064400000000000000000000011301233420632000222470ustar00rootroot00000000000000LiL[k.&reC9hg"R b_ǙsE@a]zvìWHbFҳN l@׌wzoyrLxY#$96}0 e?Siwd/Us{[2 apT+ʷ,up$A!?Xz=*0 A~Ey#xD .z" Mے~rt]Cu-w^r:3?S3]eFRR[V-6M-\ "7sSLOc[+LnEh}{yxܖلZ<T+fFp;*D80[c;޳1W4X:S+%:A+r5CfEDIv|q3lB%O3]OK2>4@<[8+ZK;G*L(rperl-Config-Identity-0.0018/t/assets/gpg/secring.gpg000064400000000000000000000023031233420632000222000ustar00rootroot00000000000000Kc ["Һ5P_ ">u%򾕇:YAvlR mi%Ub >k x"$h%U29ЇGf>{W6o5]pVnIn9bp-kꪞFƶ7}M,H+֣"D`Ӣ`_5'-d.əi例p`-.)CiffTꑙ ǜj4ޒfaE13DG~l[W>kbǝ1H\ʙFl4@I#H_nK(0&Hʱ 'h4Vif׷Z0:fa FYI{*sLۭ ?}e-4uYuSG,q/V\\}f0H0TcF1 rqGRI4NL,֒ | [ZT>qVؔ ; X"ߕlmc1}{"_(qzw@H1atҬ+XOdEz 'yIJ4l:eXKCmoj:֩f^0g,X4*Wkm^-a5Q-uD{j+ERg;"t@؂#AUU=G+ \?A`;  #*I772MɜvKSQ.YCSb/&`Rbr)3%I Kc T':=\ Rj|Y WEoϣb[L&l|FwU>E pe YaV|֦4%`[0ciRm1dRD% 'ehAe"j}J$*gdim64)nH2c:u˳|v9!Y$ _8Gi|U1?.B=Glܼ`ydk>t[3T˕CwzUg>&n3Ob9ts8+U-qpQW(5oߪ;o{a  ϰ4\B ǀA&jϞ-II_(N}+j_OI.y{F [4ycNm`2 &[&MHm# 9R=xEn P, OjɊTƐYFN2qfcl\c7)