Llew_Llaw_Gyffes has asked for the wisdom of the Perl Monks concerning the following question:
Brethren, I seek the benefit of your experience.
I have a Perl application, a multithreaded client for the ICB chat service, to which I recently added client-to-client encryption as a feature.
Now, the base client requires only about two modules that are not normally part of the default Perl5 install. This, I considered acceptable. But adding encryption required the addition of about another eight modules. Since I did not know whether users would wish to install the additional eight modules for a feature they might not use, I sought a way to make use of those modules optional. And indeed, eventually I found such a means: Module::Load::Conditional.
After some study, I wrote the following code:
use Module::Load::Conditional qw[can_load check_install requires]; $Module::Load::Conditional::VERBOSE = 1; my $crypt_mods = {'Crypt::CBC' => undef, 'Crypt::Blowfish' => undef, 'Digest::SHA1' => undef, 'MIME::Base64' => undef, 'Compress::Zlib' => undef, 'Crypt::DH' => undef}; if (( can_load(modules=> {'Math::BigInt::GMP' => undef}, verbose => 1 +) || can_load(modules=> {'Math::BigInt' => undef}, verbose => 1)) && can_load(modules => $crypt_mods, verbose => 1)) { $encryption_avail = 1; } print $encryption_avail ? "All required crypto modules found and loaded: Encryption avai +lable\n" : "Encryption disabled: required modules not found\n";
And lo, this code would reportedly load all the necessary modules if available, or, if one or more modules were not available, it would disable the encryption feature - but continue execution anyway.
Except that later on, a call to decode_base64() would fail.
Thread 3 terminated abnormally: Undefined subroutine &main::decode_base64 called at ./icbm.conditional-load line 2071.That line is in the middle of the function below:
sub decrypt { my ($message, $cipher, $key) = @_; my ($plaintext, $comptext, $ciphertext, $armortext, $cbc); $cbc = Crypt::CBC->new( -key => $key, -cipher => $cipher); $armortext = $$message; $ciphertext = decode_base64($armortext) || out ("Unarmor failed!") +; $comptext = $cbc->decrypt($ciphertext) || out ("Decrypt failed!"); $plaintext = Compress::Zlib::memGunzip($comptext) || out ("Decompr +ess failed!"); $$message = $plaintext; return (1); }
But lo, did not Module::Load::Conditional say all modules had loaded? Doubting its word, I sought a means to verify that they were indeed loaded. I discovered that Module::Loaded, while it did not contain any function to directly tell me whether a module was loaded, would — if I instructed it to mark those modules as loaded — carp if it found them already loaded.
Thus I added the following code, right after the Module::Load::Conditional invocation above:
use Module::Loaded; foreach my $mk ('Compress::Zlib', 'Crypt::Blowfish', 'Crypt::CBC', 'Cr +ypt::DH', 'Digest::SHA1', 'Math::BigInt::GMP', 'Math::BigInt', ' +MIME::Base64') { mark_as_loaded($mk); }
And lo, it reported all these modules were, indeed, already loaded, just as Module::Load::Conditional had said:
All required crypto modules found and loaded: Encryption available 'Compress::Zlib' already marked as loaded ('/usr/lib/perl5/5.10.0/i686 +-linux-thread-multi-64int-ld/Compress/Zlib.pm') at ./icbm.conditional +-load line 225 'Crypt::Blowfish' already marked as loaded ('/usr/lib/perl5/site_perl/ +5.10.0/i686-linux-thread-multi-64int-ld/Crypt/Blowfish.pm') at ./icbm +.conditional-load line 225 'Crypt::CBC' already marked as loaded ('/usr/lib/perl5/site_perl/5.10. +0/Crypt/CBC.pm') at ./icbm.conditional-load line 225 'Crypt::DH' already marked as loaded ('/usr/lib/perl5/site_perl/5.10.0 +/Crypt/DH.pm') at ./icbm.conditional-load line 225 'Digest::SHA1' already marked as loaded ('/usr/lib/perl5/site_perl/5.1 +0.0/i686-linux-thread-multi-64int-ld/Digest/SHA1.pm') at ./icbm.condi +tional-load line 225 'Math::BigInt::GMP' already marked as loaded ('/usr/lib/perl5/site_per +l/5.10.0/i686-linux-thread-multi-64int-ld/Math/BigInt/GMP.pm') at ./i +cbm.conditional-load line 225 'Math::BigInt' already marked as loaded ('/usr/lib/perl5/5.10.0/Math/B +igInt.pm') at ./icbm.conditional-load line 225 'MIME::Base64' already marked as loaded ('/usr/lib/perl5/5.10.0/i686-l +inux-thread-multi-64int-ld/MIME/Base64.pm') at ./icbm.conditional-loa +d line 225
Brothers, I am perplexed. I am assured, doubly, that MIME::Base64 is loaded. And the other modules loaded with it are clearly loaded, or my code would fail much sooner (not least at the invocation of Crypt::CBC two lines earlier). Indeed, this is not even the first use of MIME::Base64 in the code.
So why, then, can I not call decode_base64(), when that code worked perfectly when I was loading it with a simple "Use MIME::Base64;"?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: A mystery of conditional loading
by bellaire (Hermit) on Mar 23, 2009 at 01:31 UTC | |
by Llew_Llaw_Gyffes (Scribe) on Mar 23, 2009 at 02:37 UTC |