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;"?


In reply to A mystery of conditional loading by Llew_Llaw_Gyffes

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.