The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Mail::Toaster::Utility</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:root@tenten-slave.macports.org" />
</head>

<body style="background-color: white">



<ul id="index">
  <li><a href="#SYNOPSIS">SYNOPSIS</a></li>
  <li><a href="#DESCRIPTION">DESCRIPTION</a></li>
  <li><a href="#DIAGNOSTICS">DIAGNOSTICS</a></li>
  <li><a href="#DEPENDENCIES">DEPENDENCIES</a></li>
  <li><a href="#METHODS">METHODS</a></li>
  <li><a href="#TODO">TODO</a></li>
  <li><a href="#SEE-ALSO">SEE ALSO</a></li>
</ul>

<h1 id="SYNOPSIS">SYNOPSIS</h1>

<pre><code>  use Mail::Toaster::Utility;
  my $toaster = Mail::Toaster::Utility-&gt;new;

  $util-&gt;file_write($file, lines=&gt; @lines);</code></pre>

<p>This is just one of the many handy little methods I have amassed here. Rather than try to remember all of the best ways to code certain functions and then attempt to remember them, I have consolidated years of experience and countless references from Learning Perl, Programming Perl, Perl Best Practices, and many other sources into these subroutines.</p>

<h1 id="DESCRIPTION">DESCRIPTION</h1>

<p>This Mail::Toaster::Utility package is my most frequently used one. Each method has its own documentation but in general, all methods accept as input a hashref with at least one required argument and a number of optional arguments.</p>

<h1 id="DIAGNOSTICS">DIAGNOSTICS</h1>

<p>All methods set and return error codes (0 = fail, 1 = success) unless otherwise stated.</p>

<p>Unless otherwise mentioned, all methods accept two additional parameters:</p>

<pre><code>  verbose - to print status and verbose error messages, set verbose=&gt;1.
  fatal - die on errors. This is the default, set fatal=&gt;0 to override.</code></pre>

<h1 id="DEPENDENCIES">DEPENDENCIES</h1>

<pre><code>  Perl.
  Scalar::Util -  built-in as of perl 5.8</code></pre>

<p>Almost nothing else. A few of the methods do require certian things, like extract_archive requires tar and file. But in general, this package (Mail::Toaster::Utility) should run flawlessly on any UNIX-like system. Because I recycle this package in other places (not just Mail::Toaster), I avoid creating dependencies here.</p>

<h1 id="METHODS">METHODS</h1>

<dl>

<dt id="new">new</dt>
<dd>

<p>To use any of the methods below, you must first create a utility object. The methods can be accessed via the utility object.</p>

<pre><code>  ############################################
  # Usage      : use Mail::Toaster::Utility;
  #            : my $util = Mail::Toaster::Utility-&gt;new;
  # Purpose    : create a new Mail::Toaster::Utility object
  # Returns    : a bona fide object
  # Parameters : none
  ############################################</code></pre>

</dd>
<dt id="ask">ask</dt>
<dd>

<p>Get a response from the user. If the user responds, their response is returned. If not, then the default response is returned. If no default was supplied, 0 is returned.</p>

<pre><code>  ############################################
  # Usage      :  my $ask = $util-&gt;ask( &quot;Would you like fries with that&quot;,
  #                        default  =&gt; &quot;SuperSized!&quot;,
  #                        timeout  =&gt; 30
  #               );
  # Purpose    : prompt the user for information
  #
  # Returns    : S - the users response (if not empty) or
  #            : S - the default ask or
  #            : S - an empty string
  #
  # Parameters
  #   Required : S - question - what to ask
  #   Optional : S - default  - a default answer
  #            : I - timeout  - how long to wait for a response
  # Throws     : no exceptions
  # See Also   : yes_or_no</code></pre>

</dd>
<dt id="extract_archive">extract_archive</dt>
<dd>

<p>Decompresses a variety of archive formats using your systems built in tools.</p>

<pre><code>  ############### extract_archive ##################
  # Usage      : $util-&gt;extract_archive( &#39;example.tar.bz2&#39; );
  # Purpose    : test the archiver, determine its contents, and then
  #              use the best available means to expand it.
  # Returns    : 0 - failure, 1 - success
  # Parameters : S - archive - a bz2, gz, or tgz file to decompress</code></pre>

</dd>
<dt id="cwd_source_dir">cwd_source_dir</dt>
<dd>

<p>Changes the current working directory to the supplied one. Creates it if it does not exist. Tries to create the directory using perl&#39;s builtin mkdir, then the system mkdir, and finally the system mkdir with sudo.</p>

<pre><code>  ############ cwd_source_dir ###################
  # Usage      : $util-&gt;cwd_source_dir( &quot;/usr/local/src&quot; );
  # Purpose    : prepare a location to build source files in
  # Returns    : 0 - failure,  1 - success
  # Parameters : S - dir - a directory to build programs in</code></pre>

</dd>
<dt id="check_homedir_ownership">check_homedir_ownership</dt>
<dd>

<p>Checks the ownership on all home directories to see if they are owned by their respective users in /etc/password. Offers to repair the permissions on incorrectly owned directories. This is useful when someone that knows better does something like &quot;chown -R user /home /user&quot; and fouls things up.</p>

<pre><code>  ######### check_homedir_ownership ############
  # Usage      : $util-&gt;check_homedir_ownership();
  # Purpose    : repair user homedir ownership
  # Returns    : 0 - failure,  1 - success
  # Parameters :
  #   Optional : I - auto - no prompts, just fix everything
  # See Also   : sysadmin</code></pre>

<p>Comments: Auto mode should be run with great caution. Run it first to see the results and then, if everything looks good, run in auto mode to do the actual repairs.</p>

</dd>
<dt id="chown_system">chown_system</dt>
<dd>

<p>The advantage this sub has over a Pure Perl implementation is that it can utilize sudo to gain elevated permissions that we might not otherwise have.</p>

<pre><code>  ############### chown_system #################
  # Usage      : $util-&gt;chown_system( dir=&gt;&quot;/tmp/example&quot;, user=&gt;&#39;matt&#39; );
  # Purpose    : change the ownership of a file or directory
  # Returns    : 0 - failure,  1 - success
  # Parameters : S - dir    - the directory to chown
  #            : S - user   - a system username
  #   Optional : S - group  - a sytem group name
  #            : I - recurse - include all files/folders in directory?
  # Comments   : Uses the system chown binary
  # See Also   : n/a</code></pre>

</dd>
<dt id="clean_tmp_dir">clean_tmp_dir</dt>
<dd>

<pre><code>  ############## clean_tmp_dir ################
  # Usage      : $util-&gt;clean_tmp_dir( $dir );
  # Purpose    : clean up old build stuff before rebuilding
  # Returns    : 0 - failure,  1 - success
  # Parameters : S - $dir - a directory or file.
  # Throws     : die on failure
  # Comments   : Running this will delete its contents. Be careful!</code></pre>

</dd>
<dt id="get_mounted_drives">get_mounted_drives</dt>
<dd>

<pre><code>  ############# get_mounted_drives ############
  # Usage      : my $mounts = $util-&gt;get_mounted_drives();
  # Purpose    : Uses mount to fetch a list of mounted drive/partitions
  # Returns    : a hashref of mounted slices and their mount points.</code></pre>

</dd>
<dt id="archive_file">archive_file</dt>
<dd>

<pre><code>  ############### archive_file #################
  # Purpose    : Make a backup copy of a file by copying the file to $file.timestamp.
  # Usage      : my $archived_file = $util-&gt;archive_file( $file );
  # Returns    : the filename of the backup file, or 0 on failure.
  # Parameters : S - file - the filname to be backed up
  # Comments   : none</code></pre>

</dd>
<dt id="chmod">chmod</dt>
<dd>

<p>Set the permissions (ugo-rwx) of a file. Will use the native perl methods (by default) but can also use system calls and prepend sudo if additional permissions are needed.</p>

<pre><code>  $util-&gt;chmod(
                file_or_dir =&gt; &#39;/etc/resolv.conf&#39;,
                mode =&gt; &#39;0755&#39;,
                sudo =&gt; $sudo
  )

 arguments required:
   file_or_dir - a file or directory to alter permission on
   mode   - the permissions (numeric)

 arguments optional:
   sudo  - the output of $util-&gt;sudo

 result:
   0 - failure
   1 - success</code></pre>

</dd>
<dt id="chown">chown</dt>
<dd>

<p>Set the ownership (user and group) of a file. Will use the native perl methods (by default) but can also use system calls and prepend sudo if additional permissions are needed.</p>

<pre><code>  $util-&gt;chown(
                file_or_dir =&gt; &#39;/etc/resolv.conf&#39;,
                uid =&gt; &#39;root&#39;,
                gid =&gt; &#39;wheel&#39;,
                sudo =&gt; 1
  );

 arguments required:
   file_or_dir - a file or directory to alter permission on
   uid   - the uid or user name
   gid   - the gid or group name

 arguments optional:
   file  - alias for file_or_dir
   dir   - alias for file_or_dir
   sudo  - the output of $util-&gt;sudo

 result:
   0 - failure
   1 - success</code></pre>

</dd>
<dt id="file_delete">file_delete</dt>
<dd>

<pre><code>  ############################################
  # Usage      : $util-&gt;file_delete( $file );
  # Purpose    : Deletes a file.
  # Returns    : 0 - failure, 1 - success
  # Parameters
  #   Required : file - a file path
  # Comments   : none
  # See Also   :

 Uses unlink if we have appropriate permissions, otherwise uses a system rm call, using sudo if it is not being run as root. This sub will try very hard to delete the file!</code></pre>

</dd>
<dt id="get_url">get_url</dt>
<dd>

<pre><code>   $util-&gt;get_url( $url, verbose=&gt;1 );</code></pre>

<p>Use the standard URL fetching utility (fetch, curl, wget) for your OS to download a file from the $url handed to us.</p>

<pre><code> arguments required:
   url - the fully qualified URL

 arguments optional:
   timeout - the maximum amount of time to try

 result:
   1 - success
   0 - failure</code></pre>

</dd>
<dt id="file_is_newer">file_is_newer</dt>
<dd>

<p>compares the mtime on two files to determine if one is newer than another.</p>

</dd>
<dt id="file_mode">file_mode</dt>
<dd>

<pre><code> usage:
   my @lines = &quot;1&quot;, &quot;2&quot;, &quot;3&quot;;  # named array
   $util-&gt;file_write ( &quot;/tmp/foo&quot;, lines=&gt;\@lines );
        or
   $util-&gt;file_write ( &quot;/tmp/foo&quot;, lines=&gt;[&#39;1&#39;,&#39;2&#39;,&#39;3&#39;] );  # anon arrayref

 required arguments:
   mode - the files permissions mode

 result:
   0 - failure
   1 - success</code></pre>

</dd>
<dt id="file_read">file_read</dt>
<dd>

<p>Reads in a file, and returns it in an array. All lines in the array are chomped.</p>

<pre><code>   my @lines = $util-&gt;file_read( $file, max_lines=&gt;100 )

 arguments required:
   file - the file to read in

 arguments optional:
   max_lines  - integer - max number of lines
   max_length - integer - maximum length of a line

 result:
   0 - failure
   success - returns an array with the files contents, one line per array element</code></pre>

</dd>
<dt id="file_write">file_write</dt>
<dd>

<pre><code> usage:
   my @lines = &quot;1&quot;, &quot;2&quot;, &quot;3&quot;;  # named array
   $util-&gt;file_write ( &quot;/tmp/foo&quot;, lines=&gt;\@lines );
        or
   $util-&gt;file_write ( &quot;/tmp/foo&quot;, lines=&gt;[&#39;1&#39;,&#39;2&#39;,&#39;3&#39;] );  # anon arrayref

 required arguments:
   file - the file path you want to write to
   lines - an arrayref. Each array element will be a line in the file

 result:
   0 - failure
   1 - success</code></pre>

</dd>
<dt id="files_diff">files_diff</dt>
<dd>

<p>Determine if the files are different. $type is assumed to be text unless you set it otherwise. For anthing but text files, we do a MD5 checksum on the files to determine if they are different or not.</p>

<pre><code>   $util-&gt;files_diff( f1=&gt;$file1,f2=&gt;$file2,type=&gt;&#39;text&#39;,verbose=&gt;1 );

   if ( $util-&gt;files_diff( f1=&gt;&quot;foo&quot;, f2=&gt;&quot;bar&quot; ) )
   {
       print &quot;different!\n&quot;;
   };

 required arguments:
   f1 - the first file to compare
   f2 - the second file to compare

 arguments optional:
   type - the type of file (text or binary)

 result:
   0 - files are the same
   1 - files are different
  -1 - error.</code></pre>

</dd>
<dt id="find_bin">find_bin</dt>
<dd>

<p>Check all the &quot;normal&quot; locations for a binary that should be on the system and returns the full path to the binary.</p>

<pre><code>   $util-&gt;find_bin( &#39;dos2unix&#39;, dir=&gt;&#39;/opt/local/bin&#39; );</code></pre>

<p>Example:</p>

<pre><code>   my $sudo = $util-&gt;find_bin( &quot;sudo&quot;, dir =&gt; &quot;/usr/local/sbin&quot; );


 arguments required:
   bin - the name of the program (its filename)

 arguments optional:
   dir - a directory to check first

 results:
   0 - failure
   success will return the full path to the binary.</code></pre>

</dd>
<dt id="find_config">find_config</dt>
<dd>

<p>This sub is called by several others to determine which configuration file to use. The general logic is as follows:</p>

<pre><code>  If the etc dir and file name are provided and the file exists, use it.</code></pre>

<p>If that fails, then go prowling around the drive and look in all the usual places, in order of preference:</p>

<pre><code>  /opt/local/etc/
  /usr/local/etc/
  /etc</code></pre>

<p>Finally, if none of those work, then check the working directory for the named .conf file, or a .conf-dist.</p>

<p>Example: my $twconf = $util-&gt;find_config ( &#39;toaster-watcher.conf&#39;, etcdir =&gt; &#39;/usr/local/etc&#39;, )</p>

<pre><code> arguments required:
   file - the .conf file to read in

 arguments optional:
   etcdir - the etc directory to prefer

 result:
   0 - failure
   the path to $file</code></pre>

</dd>
<dt id="get_my_ips">get_my_ips</dt>
<dd>

<p>returns an arrayref of IP addresses on local interfaces.</p>

</dd>
<dt id="is_process_running">is_process_running</dt>
<dd>

<p>Verify if a process is running or not.</p>

<pre><code>   $util-&gt;is_process_running($process) ? print &quot;yes&quot; : print &quot;no&quot;;</code></pre>

<p>$process is the name as it would appear in the process table.</p>

</dd>
<dt id="is_readable">is_readable</dt>
<dd>

<pre><code>  ############################################
  # Usage      : $util-&gt;is_readable( file=&gt;$file );
  # Purpose    : ????
  # Returns    : 0 = no (not reabable), 1 = yes
  # Parameters : S - file - a path name to a file
  # Throws     : no exceptions
  # Comments   : none
  # See Also   : n/a

  result:
     0 - no (file is not readable)
     1 - yes (file is readable)</code></pre>

</dd>
<dt id="is_writable">is_writable</dt>
<dd>

<p>If the file exists, it checks to see if it is writable. If the file does not exist, it checks to see if the enclosing directory is writable.</p>

<pre><code>  ############################################
  # Usage      : $util-&gt;is_writable( &quot;/tmp/boogers&quot;);
  # Purpose    : make sure a file is writable
  # Returns    : 0 - no (not writable), 1 - yes (is writeable)
  # Parameters : S - file - a path name to a file
  # Throws     : no exceptions</code></pre>

</dd>
<dt id="fstab_list">fstab_list</dt>
<dd>

<pre><code>  ############ fstab_list ###################
  # Usage      : $util-&gt;fstab_list;
  # Purpose    : Fetch a list of drives that are mountable from /etc/fstab.
  # Returns    : an arrayref
  # Comments   : used in backup.pl
  # See Also   : n/a</code></pre>

</dd>
<dt id="get_dir_files">get_dir_files</dt>
<dd>

<pre><code>   $util-&gt;get_dir_files( $dir, verbose=&gt;1 )

 required arguments:
   dir - a directory

 result:
   an array of files names contained in that directory.
   0 - failure</code></pre>

</dd>
<dt id="get_the_date">get_the_date</dt>
<dd>

<p>Returns the date split into a easy to work with set of strings.</p>

<pre><code>   $util-&gt;get_the_date( bump=&gt;$bump, verbose=&gt;$verbose )

 required arguments:
   none

 optional arguments:
   bump - the offset (in days) to subtract from the date.

 result: (array with the following elements)
        $dd = day
        $mm = month
        $yy = year
        $lm = last month
        $hh = hours
        $mn = minutes
        $ss = seconds

        my ($dd, $mm, $yy, $lm, $hh, $mn, $ss) = $util-&gt;get_the_date();</code></pre>

</dd>
<dt id="install_from_source">install_from_source</dt>
<dd>

<pre><code>  usage:

        $util-&gt;install_from_source(
                package =&gt; &#39;simscan-1.07&#39;,
            site    =&gt; &#39;http://www.inter7.com&#39;,
                url     =&gt; &#39;/simscan/&#39;,
                targets =&gt; [&#39;./configure&#39;, &#39;make&#39;, &#39;make install&#39;],
                patches =&gt; &#39;&#39;,
                verbose =&gt; 1,
        );</code></pre>

<p>Downloads and installs a program from sources.</p>

<pre><code> required arguments:
    conf    - hashref - mail-toaster.conf settings.
    site    -
    url     -
    package -

 optional arguments:
    targets - arrayref - defaults to [./configure, make, make install].
    patches - arrayref - patch(es) to apply to the sources before compiling
    patch_args -
    source_sub_dir - a subdirectory within the sources build directory
    bintest - check the usual places for an executable binary. If found, it will assume the software is already installed and require confirmation before re-installing.

 result:
   1 - success
   0 - failure</code></pre>

</dd>
<dt id="install_from_source_php">install_from_source_php</dt>
<dd>

<p>Downloads a PHP program and installs it. This function is not completed due to lack o interest.</p>

</dd>
<dt id="is_interactive">is_interactive</dt>
<dd>

<p>tests to determine if the running process is attached to a terminal.</p>

</dd>
<dt id="logfile_append">logfile_append</dt>
<dd>

<pre><code>   $util-&gt;logfile_append( $file, lines=&gt;\@lines )</code></pre>

<p>Pass a filename and an array ref and it will append a timestamp and the array contents to the file. Here&#39;s a working example:</p>

<pre><code>   $util-&gt;logfile_append( $file, prog=&gt;&quot;proggy&quot;, lines=&gt;[&quot;Starting up&quot;, &quot;Shutting down&quot;] )</code></pre>

<p>That will append a line like this to the log file:</p>

<pre><code>   2004-11-12 23:20:06 proggy Starting up
   2004-11-12 23:20:06 proggy Shutting down

 arguments required:
   file  - the log file to append to
   prog  - the name of the application
   lines - arrayref - elements are events to log.

 result:
   1 - success
   0 - failure</code></pre>

</dd>
<dt id="mailtoaster">mailtoaster</dt>
<dd>

<pre><code>   $util-&gt;mailtoaster();</code></pre>

<p>Downloads and installs Mail::Toaster.</p>

</dd>
<dt id="mkdir_system">mkdir_system</dt>
<dd>

<pre><code>   $util-&gt;mkdir_system( dir =&gt; $dir, verbose=&gt;$verbose );</code></pre>

<p>creates a directory using the system mkdir binary. Can also make levels of directories (-p) and utilize sudo if necessary to escalate.</p>

</dd>
<dt id="check_pidfile">check_pidfile</dt>
<dd>

<p>check_pidfile is a process management method. It will check to make sure an existing pidfile does not exist and if not, it will create the pidfile.</p>

<pre><code>   $pidfile = $util-&gt;check_pidfile( &quot;/var/run/program.pid&quot; );</code></pre>

<p>The above example is all you need to do to add process checking (avoiding multiple daemons running at the same time) to a program or script. This is used in toaster-watcher.pl. toaster-watcher normally completes a run in a few seconds and is run every 5 minutes.</p>

<p>However, toaster-watcher can be configured to do things like expire old messages from maildirs and feed spam through a processor like sa-learn. This can take a long time on a large mail system so we don&#39;t want multiple instances of toaster-watcher running.</p>

<pre><code> result:
   the path to the pidfile (on success).</code></pre>

<p>Example:</p>

<pre><code>        my $pidfile = $util-&gt;check_pidfile( &quot;/var/run/changeme.pid&quot; );
        unless ($pidfile) {
                warn &quot;WARNING: couldn&#39;t create a process id file!: $!\n&quot;;
                exit 0;
        };

        do_a_bunch_of_cool_stuff;
        unlink $pidfile;</code></pre>

</dd>
<dt id="regexp_test">regexp_test</dt>
<dd>

<p>Prints out a string with the regexp match bracketed. Credit to Damien Conway from Perl Best Practices.</p>

<pre><code> Example:
    $util-&gt;regexp_test(
                exp    =&gt; &#39;toast&#39;,
                string =&gt; &#39;mailtoaster rocks&#39;,
        );

 arguments required:
   exp    - the regular expression
   string - the string you are applying the regexp to

 result:
   printed string highlighting the regexp match</code></pre>

</dd>
<dt id="source_warning">source_warning</dt>
<dd>

<p>Checks to see if the old build sources are present. If they are, offer to remove them.</p>

<pre><code> Usage:

   $util-&gt;source_warning(
                package =&gt; &quot;Mail-Toaster-5.26&quot;,
                clean   =&gt; 1,
                src     =&gt; &quot;/usr/local/src&quot;
   );

 arguments required:
   package - the name of the packages directory

 arguments optional:
   src     - the source directory to build in (/usr/local/src)
   clean   - do we try removing the existing sources? (enabled)
   timeout - how long to wait for an answer (60 seconds)

 result:
   1 - removed
   0 - failure, package exists and needs to be removed.</code></pre>

</dd>
<dt id="sources_get">sources_get</dt>
<dd>

<p>Tries to download a set of sources files from the site and url provided. It will try first fetching a gzipped tarball and if that files, a bzipped tarball. As new formats are introduced, I will expand the support for them here.</p>

<pre><code>  usage:
        $self-&gt;sources_get(
                package =&gt; &#39;simscan-1.07&#39;,
                site    =&gt; &#39;http://www.inter7.com&#39;,
                path    =&gt; &#39;/simscan/&#39;,
        )

 arguments required:
   package - the software package name
   site    - the host to fetch it from
   url     - the path to the package on $site

 arguments optional:
   conf    - hashref - values from toaster-watcher.conf</code></pre>

<p>This sub proved quite useful during 2005 as many packages began to be distributed in bzip format instead of the traditional gzip.</p>

</dd>
<dt id="sudo">sudo</dt>
<dd>

<pre><code>   my $sudo = $util-&gt;sudo();

   $util-&gt;syscmd( &quot;$sudo rm /etc/root-owned-file&quot; );</code></pre>

<p>Often you want to run a script as an unprivileged user. However, the script may need elevated privileges for a plethora of reasons. Rather than running the script suid, or as root, configure sudo allowing the script to run system commands with appropriate permissions.</p>

<p>If sudo is not installed and you&#39;re running as root, it&#39;ll offer to install sudo for you. This is recommended, as is properly configuring sudo.</p>

<pre><code> arguments required:

 result:
   0 - failure
   on success, the full path to the sudo binary</code></pre>

</dd>
<dt id="syscmd">syscmd</dt>
<dd>

<pre><code>   Just a little wrapper around system calls, that returns any failure codes and prints out the error(s) if present. A bit of sanity testing is also done to make sure the command to execute is safe.

      my $r = $util-&gt;syscmd( &quot;gzip /tmp/example.txt&quot; );
      $r ? print &quot;ok!\n&quot; : print &quot;not ok.\n&quot;;

    arguments required:
      cmd     - the command to execute

    result
      the exit status of the program you called.</code></pre>

</dd>
<dt id="try_mkdir">_try_mkdir</dt>
<dd>

<p>try creating a directory using perl&#39;s builtin mkdir.</p>

</dd>
<dt id="yes_or_no">yes_or_no</dt>
<dd>

<pre><code>  my $r = $util-&gt;yes_or_no(
      &quot;Would you like fries with that?&quot;,
      timeout  =&gt; 30
  );

        $r ? print &quot;fries are in the bag\n&quot; : print &quot;no fries!\n&quot;;

 arguments required:
   none.

 arguments optional:
   question - the question to ask
   timeout  - how long to wait for an answer (in seconds)

 result:
   0 - negative (or null)
   1 - success (affirmative)</code></pre>

</dd>
</dl>

<h1 id="TODO">TODO</h1>

<pre><code>  make all errors raise exceptions
  write test cases for every method</code></pre>

<h1 id="SEE-ALSO">SEE ALSO</h1>

<p>The following are all man/perldoc pages:</p>

<pre><code> Mail::Toaster</code></pre>


</body>

</html>