home *** CD-ROM | disk | FTP | other *** search
/ Datatid 2000 #2 / Datatid-2000-02.iso / internet / ecpro / setup.exe / CCLIB.PM < prev    next >
Encoding:
Perl POD Document  |  1997-12-03  |  22.5 KB  |  821 lines

  1. # --*-PERL-*--
  2. #####################################################################
  3. #
  4. # Copyright 1996, CyberCash Inc. All rights reserved.
  5. # Written by Brian Boesch and Judy Grass
  6. # Modified by Gene Gotimer
  7. #
  8. # These routines are intended to be used exclusively with the 
  9. # CyberCash secure payment system.
  10. #
  11. # RCSID: $Id: CCLib.pm,v 1.5 1997/07/14 11:53:30 gotimer Exp $
  12. #
  13. # Do not redistribute without written permission from CyberCash Inc.
  14. ######################################################################
  15.  
  16. # This CCLib.pm is part of the  distribution.
  17.  
  18. package CCLib;
  19.  
  20. require Exporter;
  21. @ISA = (Exporter);
  22. @EXPORT = qw();
  23. @EXPORT_OK = qw( sendmserver   urlencode     urldecode 
  24.                  GetQuery      SplitMessage  Clean 
  25.                  SetServer     Browser       GetHash 
  26.                  Now           Seconds       GMT 
  27.                  HumanSeconds  encode        decode );
  28.  
  29. $computehash = '/usr/local/bin/computehash';
  30. $tmpdir = $ENV{'TMP'}; # NT only
  31.  
  32. # UNIX or NT version
  33. $OS = 'NT';
  34.  
  35. # Set the CyberCash CashRegister version number
  36. $version = '';
  37.  
  38. use Socket;
  39. use Time::Local;
  40.  
  41. #
  42. # Debugging: Modify the variables below to enable debugging to a log file
  43. #
  44. # Be aware that performance is greatly decreased with debugging turned on
  45. #
  46.  
  47. # Debugging is on if $debugging is not 0
  48. # Set to 1 for API level debugging: only what comes into sendmserver
  49. #    and GetQuery and what goes out of sendmserver and GetQuery is
  50. #    logged
  51. # Set to 2 for debugging every step of the way
  52. $debugging = 0;
  53.  
  54. # Debug messages will be written to this file
  55. $debuglog = '/tmp/CCLib.log';
  56.  
  57. # Leave this variable alone, it is used to control the level of 
  58. # indent for debugging messages
  59. $indent = '';
  60.  
  61.  
  62. ##########################################################
  63. # sendmserver(url,
  64. #      attribute, value,
  65. #      attribute2, value2,
  66. #      ...);
  67. # Returns set of attribute values appropriate for an assoc array
  68. #
  69. sub sendmserver {
  70.  
  71. # Get the url and name-value pairs passed as parameters
  72.     local($url, @pairs) = @_;
  73.  
  74. # set up some variables
  75.     local($encoded_pairs);
  76.     local($payment_server_addr,@payment_server_addrs,$payment_server_socket);
  77.     local($name,$aliases,$protocol,$addrtype,$length);
  78.     local($pairs_length,$message,$message_length,$write_result);
  79.     local($timeout,$TimedOut,$socket_data,@socket_data);
  80.     local(%result) = ();
  81.  
  82. # For backwards compatibility
  83.     $paymentserverhost = $main::paymentserverhost || $paymentserverhost;
  84.     $paymentserverport = $main::paymentserverport || $paymentserverport;
  85.     $paymentserversecret = $main::paymentserversecret || $paymentserversecret;
  86.  
  87.     if ($debugging) {
  88.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  89.     print DEBUG "\n${indent}== Entering sendmserver ==\n";
  90.     $indent .= '  ';
  91.     print DEBUG "${indent}POST to host: $paymentserverhost, port: $paymentserverport\n"; 
  92.     print DEBUG "${indent}secret: $paymentserversecret, command: $url\n\n";
  93.  
  94.     print DEBUG "${indent}## Name-Value pairs to send ##\n";
  95.     my($i,$j);
  96.     for ($i=0; $i<@pairs; $i+=2) {
  97.         $j = $i + 1;
  98.         print DEBUG "${indent}$pairs[$i] = [[$pairs[$j]]]\n";
  99.     }
  100.         print DEBUG "\n";
  101.     close(DEBUG);
  102.     }
  103.  
  104. # turn the name-value pairs into a url encoded message
  105.     $encoded_pairs = &urlencode(@pairs);
  106.  
  107.     if ($debugging > 1) {
  108.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  109.     print DEBUG "${indent}Encoded name-value pairs: $encoded_pairs\n";
  110.     close(DEBUG);
  111.     }
  112.  
  113. # set up the socket to talk to the payment server
  114.     if ($OS eq 'UNIX') {
  115.     $SOCK_STREAM = SOCK_STREAM;
  116.     $AF_INET = AF_INET;
  117.     } else {
  118.     $SOCK_STREAM = 1;
  119.     $AF_INET = 2;
  120.     }
  121.  
  122. # find the TCP protocol information
  123.     ($name,$aliases,$protocol) = getprotobyname('tcp');
  124.  
  125. # find the address for the payment server
  126.    if ($paymentserverhost =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
  127.       $payment_server_addr = pack("C4", $1, $2, $3, $4);
  128.    } else {
  129.       ($name,$aliases,$addrtype,$length,@payment_server_addrs) = gethostbyname($paymentserverhost);
  130.       $payment_server_addr = $payment_server_addrs[0];
  131.    }
  132.  
  133. # Put the socket info into the correct format for the connect
  134.     $payment_server_socket = pack('S n a4 x8',$AF_INET,$paymentserverport,$payment_server_addr);
  135.  
  136. # This is a better version, but sockaddr_in is undefined in Perl 5.001m
  137. #    $payment_server_socket = sockaddr_in($paymentserverport,$payment_server_addr);
  138.  
  139. # make the socket filehandle and connect to the socket
  140. # if any fail, reply and return with an appropriate error message
  141. # "S" is the socket filehandle
  142.     socket(S,$AF_INET,$SOCK_STREAM,$protocol) || (&SocketFailed,return %result);
  143.     if ($debugging > 1) {
  144.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  145.     print DEBUG "\n${indent}Opened socket.\n";
  146.     close(DEBUG);
  147.     }
  148.  
  149.     connect(S,$payment_server_socket) || (&ConnectFailed,return %result);
  150.     if ($debugging > 1) {
  151.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  152.     print DEBUG "${indent}Connected to socket.\n\n";
  153.     close(DEBUG);
  154.     }
  155.  
  156. # Now that the socket is open, create a message to send
  157.     $pairs_length = length($encoded_pairs);
  158.     $message = <<"END";
  159. POST /$paymentserversecret/$url HTTP/1.0
  160. User-Agent: CyberCashMerchant-$version
  161. Content-type: application/x-www-form-urlencoded
  162. Content-length: $pairs_length
  163.  
  164. $encoded_pairs
  165. END
  166.     $message_length = length($message);
  167.  
  168.     if ($debugging > 1) {
  169.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  170.     print DEBUG "${indent}Encoded name-value pairs length = $pairs_length\n";
  171.     print DEBUG "${indent}Message:\n$message\n";
  172.     print DEBUG "${indent}Message length = $message_length\n";
  173.     close(DEBUG);
  174.     }
  175.  
  176.     if ($OS eq 'UNIX') { # alarms are not implemented in NT Perl
  177. # set up a handler for response timeouts
  178.     $SIG{'ALRM'} = \&Timeout;
  179. # SIGALRM will now call the &CCLib::Timeout routine
  180.  
  181. # set a flag so we can see if we timed out
  182.     $TimedOut = 0;
  183.  
  184. # set the timeout value for sending a message    
  185. # WARNING: The payment server may wait up to 90 seconds for
  186. #          a response from the CyberCash server. If you don't 
  187. #          wait at least 90 seconds, you may be giving up 
  188. #          too early. Be patient.
  189.     $timeout = 120;
  190.     alarm($timeout);
  191.     }
  192.  
  193. # force flush the output buffer
  194.    select(S); $| = 1; print ''; select(STDOUT);
  195.  
  196. # send the message over the socket
  197.     if ($OS eq 'UNIX') {
  198. # $write_result will be undef if there was an error
  199.     $write_result = syswrite(S,$message,$message_length);
  200.     unless ( defined($write_result) ) { &SyswriteFailed; return %result; }
  201.     } else {
  202.     print S $message;
  203.     }
  204.  
  205. # reinstate buffered I/O
  206.     $| = 0;
  207.  
  208.     if ($debugging > 1) {
  209.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  210.     print DEBUG "${indent}Wrote message to socket.\n";
  211.     print DEBUG "${indent}Reading response.\n";
  212.     close(DEBUG);
  213.     }
  214.  
  215.     if ($OS eq 'UNIX') {
  216. # reset the alarm while we wait for a response
  217.     alarm($timeout);
  218.     }
  219.  
  220. # read the socket line-by-line
  221.     while (<S>) {
  222.  
  223.     if ($OS eq 'UNIX') {
  224. # if we timed out, we should return. %results is already filled out.
  225.         return %result if ($TimedOut);
  226.     }
  227.  
  228.     if ($debugging > 1) {
  229.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  230.         print DEBUG "${indent}Read single line: $_\n";
  231.         close(DEBUG);
  232.     }
  233.  
  234.     if ($OS eq 'UNIX') {
  235. # reset the alarm for the next line
  236.         alarm($timeout);
  237.     }
  238.  
  239. # if we are told to go away, then we should do just that
  240.     if (/x-go-away-message/i) { &GoAway; return %result;}
  241.  
  242. # read until a blank line
  243.     last if (/^\s*$/);
  244.     }
  245.  
  246.     if ($OS eq 'UNIX') {
  247. # reset the alarm for the all-at-once read
  248.     alarm($timeout);
  249.     }
  250.  
  251. # we want to read all the rest of the data, all at once,
  252. # then convert to a single scalar value
  253.     @socket_data = <S>;
  254.     $socket_data = join('',@socket_data);
  255.  
  256.     if ($debugging > 1) {
  257.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  258.     print DEBUG "${indent}Read all at once:\n@socket_data\n";
  259.     print DEBUG "${indent}Finished reading response.\n";
  260.     close(DEBUG);
  261.     }
  262.  
  263.     if ($OS eq 'UNIX') {
  264. # turn off the alarm and reset the default signal handler
  265.     alarm(0);
  266.     $SIG{'ALRM'} = 'DEFAULT';
  267.     }
  268.  
  269. # decode the url encoded response, and send back the result
  270. # as an associative array (%result)
  271.     %result = &urldecode($socket_data);
  272.  
  273.     if ($debugging) {
  274.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  275.     print DEBUG "${indent}## Decoded response Name-Value pairs ##\n";
  276.     foreach (keys %result) {
  277.         print DEBUG "${indent}$_ = [[$result{$_}]]\n";
  278.     }
  279.     $indent =~ s/  //;
  280.     print DEBUG "\n\n${indent}== Exiting sendmserver ==\n\n\n";
  281.     close(DEBUG);
  282.     }
  283.  
  284.     return %result;
  285. }
  286.  
  287.  
  288. #
  289. # This routine is called when a SIGALRM is received.
  290. # That means that we have timed-out waiting for a 
  291. # response from the payment server.
  292. sub Timeout {
  293.  
  294.     $result{'MStatus'} = 'failure-hard';
  295.     $result{'MErrLoc'} = 'CCLib';
  296.     $result{'MErrMsg'} = "No response in $timeout seconds from the Merchant Payment Server.";
  297.  
  298.     $TimedOut = 1;
  299. }
  300.  
  301.  
  302. ##########################################################
  303. # Set the server information
  304. #
  305. sub SetServer {
  306.     local(%server) = @_;
  307.  
  308.     $paymentserverhost = $server{'host'};
  309.     $paymentserverport = $server{'port'};
  310.     $paymentserversecret = $server{'secret'};
  311.  
  312. }
  313.  
  314.  
  315. ##########################################################
  316. #
  317. # Strip any unwanted characters from a given string,
  318. # in this case, anything but a-z, A-Z, 0-9, plus, minus,
  319. # space, tab, slash, at, period, percent, or backslash
  320. # Most importantly, semicolons and dollar signs are removed
  321. # so they won't get passed to the command line.
  322. #
  323. sub Clean {
  324.  
  325.     local($string) = @_;
  326.  
  327. # check that all characters are legal, 
  328. # if not, delete illegals
  329.     $string =~ tr/a-zA-Z0-9_\-+ \t\/\@\.%\\//cd;
  330.  
  331.     return $string;
  332. }
  333.  
  334.  
  335. ##########################################################
  336. #
  337. # URL encode the message and place into "cybermessage"
  338. # urlencode takes a list elements 0,2,4,... are fieldnames
  339. #                        elements 1,3,5,... are values
  340. #
  341. sub urlencode {
  342.     my(@msglist) = @_;
  343.     my($msg) = '';
  344.     my($name,$value);
  345.  
  346.     if ($debugging > 1) {
  347.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  348.         print DEBUG "\n${indent}== Entering urlencode ==\n";
  349.         $indent .= '  ';
  350.         close(DEBUG);
  351.     }
  352.  
  353. # step through the list
  354.     while (@msglist > 0) {
  355.  
  356. # grab the next name-value pair
  357.     $name = shift(@msglist);
  358.     $value = shift(@msglist);
  359.  
  360.         if ($debugging > 1) {
  361.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  362.         print DEBUG "${indent}Raw (Encoded) name-value pair: $name -> $value";
  363.         close(DEBUG);
  364.         }
  365.  
  366. # encode the name and value
  367.     $name = &encode($name);
  368.     $value = &encode($value);
  369.  
  370.         if ($debugging > 1) {
  371.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  372.         print DEBUG " ($name -> $value)\n";
  373.         close(DEBUG);
  374.         }
  375.  
  376. # add the now-encoded name-value pair to the message
  377.     $msg .= "$name=$value";
  378.  
  379. # add an ampersand (as a name-value pair delimiter) unless
  380. # we have reached the end
  381.     $msg .= '&' unless (@msglist <= 0);
  382.  
  383.         if ($debugging > 1) {
  384.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  385.         print DEBUG "${indent}Message is now: $msg\n\n";
  386.         close(DEBUG);
  387.         }
  388.  
  389.     }
  390.  
  391. # everything is url encoded, so send the message back
  392.     if ($debugging > 1) {
  393.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  394.     $indent =~ s/  //;
  395.     print DEBUG "${indent}== Exiting urlencode ==\n\n";
  396.     close(DEBUG);
  397.     }
  398.  
  399.     return $msg;
  400. }
  401.  
  402.  
  403. ##########################################################
  404. #
  405. # URL decode the message
  406. #
  407. sub urldecode {
  408.     my($encoded) = @_;
  409.     my(@pairs) = split(/&/,$encoded);
  410.     my(%pairs,$name,$value);
  411.  
  412.     if ($debugging > 1) {
  413.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  414.     print DEBUG "\n${indent}== Entering urldecode ==\n";
  415.     $indent .= '  ';
  416.     close(DEBUG);
  417.     }
  418.  
  419.     foreach (@pairs) {
  420.  
  421. # Split into name and value
  422.     ($name,$value) = split(/=/,$_,2);
  423.  
  424.         if ($debugging > 1) {
  425.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  426.         print DEBUG "${indent}Raw name-value pair: $_\n";
  427.         close(DEBUG);
  428.         }
  429.  
  430. # Convert plus signs to spaces, and %XX from hex numbers to alphanumeric
  431.     $name = &decode($name);
  432.     $value = &decode($value);
  433.  
  434. # If the name has already been seen, just tack the value on the end
  435. # with a \0 delimeter      
  436.     if ( defined($pairs{$name}) ) {
  437.         $pairs{$name} .= "\0$value";
  438.     } else {
  439.         $pairs{$name} = $value;
  440.     }
  441.  
  442.         if ($debugging > 1) {
  443.             open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  444.             print DEBUG "${indent}Decoded name-value pair: $name -> $pairs{$name}\n\n";
  445.             close(DEBUG);
  446.         }
  447.     }
  448.  
  449.     if ($debugging > 1) {
  450.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  451.     $indent =~ s/  //;
  452.     print DEBUG "${indent}== Exiting urldecode ==\n\n";
  453.     close(DEBUG);
  454.     }
  455.  
  456.     return %pairs;
  457. }
  458.  
  459.  
  460. # URL encode a string
  461. sub encode {
  462.     my($text) = @_;
  463.  
  464.     # only alphanumerics and underscore (\w), dash, period, and star
  465.     # can go through UNencoded
  466.     $text =~ s/([^ \w\-.*])/sprintf("%%%2.2x",ord($1))/ge;
  467.  
  468.     # spaces get converted to plus signs (+)
  469.     # plus signs were already converted to %2B (ascii 43)
  470.     $text =~ s/ /+/g;
  471.  
  472.     return $text;
  473. }
  474.  
  475.  
  476. # URL decode a string 
  477. sub decode {
  478.     my($text) = @_;
  479.  
  480.     # Convert plus signs to spaces, and %XX from hex numbers to alphanumeric
  481.     $text =~ s/\+/ /g;
  482.     $text =~ s/%([a-fA-F0-9]{2})/pack("c",hex($1))/ge;
  483.  
  484.     return $text;
  485. }
  486.  
  487.  
  488.  
  489. ##########################################################
  490. #
  491. # Get the query from the HTTP message
  492. #
  493. sub GetQuery {
  494.     my($query,%pairs) = '';
  495.  
  496.     if ($debugging) {
  497.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  498.     print DEBUG "\n${indent}== Entering GetQuery ==\n";
  499.     $indent .= '  ';
  500.     print DEBUG "${indent}$ENV{'REQUEST_METHOD'} request:\n";
  501.     close(DEBUG);
  502.     }
  503.  
  504. # Read in text
  505.     if ($ENV{'REQUEST_METHOD'} =~ /GET/i) {
  506.         $query = $ENV{'QUERY_STRING'};
  507.  
  508.     if ($debugging > 1) {
  509.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  510.         print DEBUG "$query\n";
  511.         close(DEBUG);
  512.     }
  513.  
  514.     } elsif ($ENV{'REQUEST_METHOD'} =~ /POST/i) {
  515.     read(STDIN,$query,$ENV{'CONTENT_LENGTH'}) if ($ENV{'CONTENT_LENGTH'} > 0);
  516.  
  517.     if ($debugging > 1) {
  518.         open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  519.         print DEBUG "${indent}Content-Length = $ENV{'CONTENT_LENGTH'}\n";
  520.         print DEBUG "$query\n";
  521.         close(DEBUG);
  522.     }
  523.     }
  524.  
  525. # Decode the results
  526.     %pairs = &urldecode($query);
  527.  
  528.     if ($debugging) {
  529.     open(DEBUG, ">>$debuglog") || die "\nCCLib.pm debugging: Cannot append to $debuglog: $!";
  530.     print DEBUG "${indent}## Decoded Name-Value pairs ##\n";
  531.         foreach (keys %pairs) {
  532.             print DEBUG "${indent}$_ = [[$pairs{$_}]]\n";
  533.         }
  534.         $indent =~ s/  //;
  535.         print DEBUG "\n\n${indent}== Exiting GetQuery ==\n\n\n";
  536.     close(DEBUG);
  537.     }
  538.  
  539.     return %pairs;
  540. }
  541.  
  542.  
  543. ##########################################################
  544. #
  545. # Split up a MIME message into an associative array
  546. #
  547. sub SplitMessage {
  548.     local($message,$ccmessage) = @_;
  549.     local(@message,%message,$field,$colon,$data);
  550.  
  551.     @message = split(/\n/,$message);
  552.     if ($ccmessage) {
  553.         # take the header and footer off
  554.         $message{'header'} = shift(@message);
  555.         $message{'footer'} = pop(@message);
  556.     }
  557.  
  558.     # parse the message
  559.     $field = '';
  560.     $colon = '';
  561.     foreach (@message) {
  562.         if (/^\s+/) {   # continuation line
  563.             if ($field) {
  564.                 ($data) = /^\s+(.+)/;
  565.                 $data .= "\n";
  566.                 if ($colon eq ':') {
  567.                     $data =~ s/\s*$//; # only trim trailing whitespace if colon
  568.                 }
  569.                 $message{$field} .= $data;
  570.             }
  571.         } else {   # new field
  572.             ($field,$colon,$data) = /^\s*(.+?)\s*([:;])\s*(.+)/;
  573.             $data .= "\n";
  574.             if ($colon eq ':') {
  575.                 $data =~ s/\s*$//; # only trim trailing whitespace if colon
  576.             }
  577.             $message{$field} = $data;
  578.         }
  579.     }
  580.  
  581.     return(%message);
  582. }
  583.  
  584.  
  585. ##########################################################
  586. #
  587. # Find the browser type
  588. #
  589. sub Browser {
  590.     local(%browser) = ();
  591.     local($platform,$type,$version);
  592.  
  593.     $_ = $ENV{'HTTP_USER_AGENT'};
  594.  
  595.     # get the platform
  596.     if (/win(dows)?\s*(32|95|nt)/i) {
  597.         $platform = 'win32';
  598.     } elsif (/win/i) {
  599.         $platform = 'win';
  600.     } elsif (/mac/i) {
  601.         $platform = 'mac';
  602.     } else {
  603.         $platform = 'other';
  604.     }
  605.  
  606.     # get the browser type and version
  607.     # First, check explicitly for MSIE
  608.     if (/MSIE/) {
  609.         $type = 'MSIE';
  610.         ($version) = /MSIE\s+(.+?);/;
  611.     } else {
  612.         ($type,$version) = /^(.*?)\/\s*(.+?)\s/;
  613.     }
  614.  
  615.     # put it into the assoc array
  616.     %browser = ( 'platform' => $platform,
  617.                  'type'     => $type,
  618.                  'version'  => $version );
  619.  
  620.     return(%browser);
  621. }
  622.  
  623.  
  624. ##########################################################
  625. #
  626. # Find the browser type
  627. #
  628. sub GetHash {
  629.     local($text,$secret) = @_;
  630.     local($signThis) = "${secret}${text}${secret}";
  631.  
  632.     if ($OS eq 'NT') {
  633.         # can't use command line to pass text on NT, so
  634.         # write the message to a temporary file
  635.         local($tempfile) = "hash$$";
  636.         $tempfile =~ tr/A-Za-z0-9_//cd;
  637.         $tempfile = "$tmpdir/$tempfile.tmp";
  638.         open(TEMP,">$tempfile");
  639.         binmode TEMP;
  640.         print TEMP $signThis;
  641.         close(TEMP);
  642.  
  643.         # get the base64-encoded MD5 hash
  644.         open(HASH,"$computehash -f $tempfile|");
  645.  
  646.     } else {
  647.         # get the base64-encoded MD5 hash
  648.         open(HASH,"$computehash '$signThis'|");
  649.     }
  650.  
  651.     $hash = <HASH>;
  652.     chomp($hash);
  653.     close(HASH);
  654.  
  655.     # strip leading and trailing whitespace
  656.     $hash =~ s/^\s*|\s*$//g;
  657.  
  658.     return($hash);
  659.  
  660.     if ($OS eq 'NT') {
  661.         # clean up the temporary file
  662.         unlink "$tempfile";
  663.     }
  664. }
  665.  
  666.  
  667. ##########################################################
  668. #
  669. # Routines for manipulating time formats
  670. #
  671.  
  672. # return the current date + an offset in YYYYMMDDhhmmss.sss UTC
  673. sub Now {
  674.     my($offset) = @_;
  675.     my($ss,$mm,$hh,$DD,$MM,$YYYY,$wday,$yday,$isdst) = 
  676.         gmtime(time + $offset);
  677.     $MM++;
  678.     $YYYY += 1900 if ($year < 100);
  679.  
  680.     my($now) = sprintf('%4d%02d%02d%02d%02d%02d.000',$YYYY,$MM,$DD,$hh,$mm,$ss);
  681.     return($now);
  682. }
  683.  
  684.  
  685. # Convert YYYYMMDDhhmmss.sss to seconds
  686. sub Seconds {
  687.     my($machine) = @_;
  688.     my($year,$yr,$month,$date,$hour,$minute,$second,$frac) =
  689.         $machine =~ /A?(\d\d(\d\d))(\d\d)(\d\d)(\d\d)(\d\d)(\d\d).*(\d\d\d)*/i;
  690.  
  691.     my($seconds) = timegm($second,$minute,$hour,$date,($month-1),($year-1900)) + $frac/1000; 
  692.  
  693.     return $seconds;
  694. }
  695.  
  696.  
  697. # Convert seconds to human-readable time
  698. sub GMT {
  699.     my($seconds) = @_;
  700.     my($ss,$mm,$hh,$DD,$MM,$YYYY,$wday,$yday,$isdst) = 
  701.         gmtime($seconds);
  702.     my($nss,$nmm,$nhh,$nDD,$nMM,$nYYYY,$nwday,$nyday,$nisdst) = 
  703.         gmtime(time);
  704.     $MM++;
  705.  
  706.     my($human) = sprintf('%d/%d/%d at %d:%02d:%02d GMT',$MM,$DD,$YYYY,$hh,$mm,$ss);
  707.  
  708.     if ($yday == $nyday) {
  709.         $human = sprintf('%d:%02d:%02d GMT',$hh,$mm,$ss);
  710.     } elsif ( ($yday-$nyday) == 1) {
  711.         $human = sprintf('tommorrow at %d:%02d:%02d GMT',$hh,$mm,$ss);
  712.     }
  713.  
  714.     return $human;
  715. }
  716.  
  717.  
  718. # convert seconds to a more human-readable string
  719. sub HumanSeconds {
  720.     use integer;
  721.  
  722.     my($seconds) = @_;
  723.     my(@parts) = ();
  724.  
  725.     my($weeks) = $seconds / (7 * 24 * 60 * 60);
  726.     $seconds %= (7 * 24 * 60 * 60);
  727.     my($days) = $seconds / (24 * 60 * 60);
  728.     $seconds %= (24 * 60 * 60);
  729.     my($hours) = $seconds / (60 * 60);
  730.     $seconds %= (60 * 60);
  731.     my($minutes) = $seconds / 60;
  732.     $seconds %= 60;
  733.  
  734.     if ($weeks) {
  735.         if ($weeks == 1) {
  736.             push(@parts,'1 week');
  737.         } else {
  738.             push(@parts,"$weeks weeks");
  739.         }
  740.     }
  741.     if ($days) {
  742.         if ($days == 1) {
  743.             push(@parts,'1 day');
  744.         } else {
  745.             push(@parts,"$days days");
  746.         }
  747.     }
  748.     if ($hours) {
  749.         if ($hours == 1) {
  750.             push(@parts,'1 hour');
  751.         } else {
  752.             push(@parts,"$hours hours");
  753.         }
  754.     }
  755.     if ($minutes) {
  756.         if ($minutes == 1) {
  757.             push(@parts,'1 minute');
  758.         } else {
  759.             push(@parts,"$minutes minutes");
  760.         }
  761.     }
  762.     if ($seconds) {
  763.         if ($seconds == 1) {
  764.             push(@parts,'1 second');
  765.         } else {
  766.             push(@parts,"$seconds seconds");
  767.         }
  768.     }
  769.  
  770.     my($lastpart) = pop(@parts);
  771.     my($human) = '';
  772.     if (@parts) {
  773.         if (@parts == 1) {
  774.             $human = "$parts[0] and ";
  775.         } else {
  776.             $human = join(', ',@parts) . ', and ';
  777.         }
  778.     }
  779.     $human .= $lastpart;
  780.  
  781.     return $human;
  782. }
  783.  
  784.  
  785. ##########################################################
  786. #
  787. # Error reporting routines
  788. #
  789. sub SocketFailed {
  790.     $result{'MStatus'} = 'failure-hard';
  791.     $result{'MErrLoc'} = 'CCLib';
  792.     $result{'MErrMsg'} = 'Could not open socket to connect to Merchant Payment Server.';
  793. }
  794.  
  795. sub ConnectFailed {
  796.     $result{'MStatus'} = 'failure-hard';
  797.     $result{'MErrLoc'} = 'CCLib';
  798.     $result{'MErrMsg'} = <<'END';
  799. Could not connect socket to the Merchant Payment Server. 
  800. It may not be running or your configuration may be incorrect.
  801. END
  802. }
  803.  
  804. sub SyswriteFailed {
  805.     $result{'MStatus'} = 'failure-hard';
  806.     $result{'MErrLoc'} = 'CCLib';
  807.     $result{'MErrMsg'} = 'Could not write to the socket.';
  808. }
  809.  
  810. sub GoAway {
  811.     $result{'MStatus'} = 'failure-hard';
  812.     $result{'MErrLoc'} = 'MPMT';
  813.     $result{'MErrMsg'} = 'Misconfigured server/CGI-script secret.';
  814. }
  815.  
  816.  
  817. 1;
  818.