home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / CMCD0404.ISO / Software / Freeware / Programare / dotproject / includes / gateway.pl < prev    next >
Perl Script  |  2004-01-29  |  17KB  |  561 lines

  1. #!c:\programme\perl\bin\perl.exe -w
  2. # You may have to edit the above line to reflect your system
  3. # E.g. the typical UNIX/Linux system will require #!/usr/bin/perl
  4.  
  5. # $Id: gateway.pl,v 1.15 2004/01/29 02:42:48 ajdonnison Exp $ #
  6.  
  7. # send email report upon receipt (1 = yes, 0 = no)
  8. $send_email_report = 1;
  9.  
  10. # Send aknowlegment back to lodger (1 = yes, 0 = no)
  11. $send_acknowledge = 1;
  12.  
  13. # Save attachments as files in project 0 (1 = yes, 0 = no, just mark them as removed)
  14. $save_attachments = 0;
  15.  
  16. # Skip non-MIME component of MIME emails (usually a warning about non-MIME compliant readers)
  17. $skip_mime_preface = 1;
  18.  
  19. # NOTE:  Email addresses should escape the @ symbol as it is
  20. # a PERL array identifier and will cause this script to break.
  21. # Alternatively change the double quotes to single quotes, which
  22. # also escapes the string.
  23.  
  24. # NOTE 2: If your dotProject PHP environment is correctly set up
  25. # you don't need to add the @ and domain, it will get it from
  26. # dPconfig[site_domain] key.
  27.  
  28. # address to send report to
  29. $report_to_address = "admin";
  30.  
  31. # report from address
  32. $report_from_address = "support";
  33.  
  34. # location of sendmail
  35. $mailprog = "/usr/sbin/sendmail";
  36.  
  37. ######################## </CONFIGURATION SECTION> ##############################
  38.  
  39. ## First phase, check to see we can configure ourselves based upon
  40. ## the PHP environment.
  41. die ("Gateway.pl requires the full path to the dotproject config.php file as its only argument") if ($#ARGV != 0);
  42. %config = ();
  43. &check_config($ARGV[0]);
  44.  
  45. # Shortcuts for the email code
  46. $app_root = $config{'base_url'};
  47. $dp_root = $config{'root_dir'};
  48.  
  49. # If no domain portion, add the domain from the configuration file.
  50. if ( $report_to_address !~ /\@/ ) {
  51.   $report_to_address .= '@' . $config{'site_domain'};
  52. }
  53. if ( $report_from_address !~ /\@/ ) {
  54.   $report_from_address .= '@' . $config{'site_domain'};
  55. }
  56.  
  57. # database bindings
  58. use DBI;
  59.  
  60. # read in message
  61. while (<STDIN>) {
  62.     push @message, $_;
  63. }
  64.  
  65. # main program
  66. &get_headers();
  67. &check_attachments();
  68. &get_body();
  69. &insert_message();
  70. &insert_attachments() if ($save_attachments);
  71. &mail_report() if ($send_email_report);
  72. &mail_acknowledgement() if ($send_acknowledge);
  73.  
  74. exit();
  75.  
  76. ################################################################################
  77.  
  78. sub check_config() {
  79.   $dp_conf = $_[0];
  80.   open (PHPCONFIG, "<$dp_conf")
  81.     or die ("Cannot find dotProject configuration file!");
  82.   while (<PHPCONFIG>) {
  83.     if (/^\s*\$dpconfig\[/i) {
  84.       s/\s*;.*$//;
  85.       # Now split the conf line up.
  86.       @confs = split /\s*=\s*/;
  87.       # First part is the name
  88.       $confs[0] =~ s/^.*\[['"](.*)['"]\]/$1/;
  89.       $confs[1] =~ s/['"\r\n]//g;
  90.       # add to the config array
  91.       $config{$confs[0]} = $confs[1];
  92.     }
  93.   }
  94. }
  95.  
  96. sub get_headers {
  97.  
  98.     # read in headers
  99.     # First pass, fix up split headers.
  100.     $first_message_line = 0;
  101.     foreach (@message) {
  102.         last if (/^\s$/ || /^$/);
  103.         if (/^[\s\t]+/) {
  104.             $last_hdr = pop @headers;
  105.             $last_hdr =~ s/[\s\t]*$//;
  106.             s/[\s\t]*//;
  107.             $last_hdr .= $_;
  108.             push @headers, $last_hdr;
  109.         } else {
  110.             push @headers, $_;
  111.         }
  112.         $first_message_line++;
  113.     }
  114.     # Second pass, split out the required headers
  115.     $attachment = 0;
  116.     foreach (@headers) {
  117.         if (/oundary=/) {
  118.             $attachment_info = $_;
  119.             if ($save_attachments) {
  120.                 $attachment = 2;
  121.             } else {
  122.                 $attachment = 1;
  123.             }
  124.         }
  125.         $_ =~ s/:\s/:/g;
  126.         if (/:/) {
  127.             @vars = split(':', $_, 2);
  128.             if (@vars) {
  129.                 chop($header{$vars[0]} = $vars[1]);
  130.             }
  131.         }
  132.     }
  133.  
  134.     # strip out Re:'s in subject
  135.     $header{'Subject'} =~ s/\s*Re:\s*//gi;
  136.  
  137.     # put a nice Re: back in
  138.     $header{'Subject'} =~ s/(\[\#\d+\])(.*)/$1 Re: $2/;
  139.  
  140.     # initialize Cc: header
  141.     $header{'Cc'} = "" if (!$header{'Cc'});
  142.  
  143.     # Allow the use of Reply-To to insert tickets on behalf of another
  144.     if ($header{'Reply-To'}) {
  145.     $header{'From'} = $header{'Reply-To'};
  146.     }
  147.  
  148.     # fix quoting in email headers
  149.     $header{'From'} =~ s/"/\"/g;
  150.     $header{'Cc'} =~ s/"/\"/g;
  151.  
  152.     # determine ticket number
  153.     $parent = $header{'Subject'};
  154.     if ($parent =~ /\[\#(\d+)\]/) {
  155.         $parent =~ s/.*\[\#(\d+)\].*/$1/;
  156.     }
  157.     else {
  158.         $parent = 0;
  159.     }
  160.  
  161. }
  162.  
  163. ################################################################################
  164.  
  165. sub check_attachments {
  166.  
  167.     # check for attachment
  168.     return if (!$attachment_info);
  169.  
  170.     # determine attachment delimiter
  171.     ($i, $boundary) = split(/"/, $attachment_info);
  172.     return if (!$boundary);
  173.  
  174.     if ($attachment_info =~ /multipart\/alternative/i) {
  175.         $mime_alternative = 1;
  176.     } else {
  177.         $mime_alternative = 0;
  178.     }
  179.     # pull out attachments
  180.     $in_attach_hdrs = 0;
  181.     $attach_count = 0;
  182.     for ($i = $first_message_line; $i <= $#message; $i++) {
  183.         if ($message[$i] =~ /$boundary/) {
  184.         $in_attach_hdrs = 1;
  185.             push @boundary_lines, $i;
  186.         push @attach_disposition, "";
  187.         push @attach_type, "text/plain";
  188.         push @attach_encoding, "7bit";
  189.         push @attach_realname, "";
  190.         $attach_count += 1;
  191.     } else {
  192.         if ($in_attach_hdrs) {
  193.         if ($message[$i] =~ /^\s*$/) {
  194.             $last = pop @boundary_lines;
  195.             push @boundary_lines, $i;
  196.             push @boundary_end, $last;
  197.             $in_attach_hdrs = 0;
  198.         } else {
  199.             @attach_hdr = split(/[:;]/, $message[$i]);
  200.             if ($attach_hdr[0] =~ m/content-disposition/i) {
  201.                 $last = pop @attach_disposition;
  202.                 push @attach_disposition, $attach_hdr[1];
  203.             }
  204.             if ($attach_hdr[0] =~ m/content-type/i) {
  205.                 pop @attach_type;
  206.                 push @attach_type, $attach_hdr[1];
  207.             }
  208.             if ($attach_hdr[0] =~ m/content-transfer-encoding/i) {
  209.                 pop @attach_encoding;
  210.                 push @attach_encoding, $attach_hdr[1];
  211.             }
  212.             if ($message[$i] =~ m/name=/i) {
  213.                 ($x, $f) = split(/"/, $message[$i]);
  214.                 $x = "";
  215.                 pop @attach_realname;
  216.                 push @attach_realname, $f;
  217.             }
  218.         }
  219.         }
  220.     }
  221.     }
  222.     push @boundary_end, $#message;
  223. }
  224.  
  225. ################################################################################
  226.  
  227. sub get_body {
  228.  
  229.     # read in message body
  230.     if (!$attachment_info) {
  231.         for ($i = $first_message_line + 1; $i <= $#message; $i++) {
  232.             $body .= $message[$i];
  233.         }
  234.     }
  235.     else {
  236.         # Look for the attachment that doesn't have a disposition
  237.         if ($skip_mime_preface) {
  238.             $i = 1;
  239.         } else {
  240.             $i = 0;
  241.         }
  242.         for (; $i < $#attach_disposition; $i++) {
  243.             if ( ($mime_alternative == 1 && $attach_type[$i] =~ /text\/plain/i) || ($mime_alternative == 0 && $attach_disposition[$i] =~ /^$/ )) {
  244.                 for ($j = $boundary_lines[$i] + 1; $j < $boundary_end[$i+1]; $j++) {
  245.                     $body .= $message[$j];
  246.                 }
  247.             }
  248.         }
  249.     }
  250.     $body =~ s/^\n//;
  251.     $body =~ s/\r\n$/\n/;
  252.  
  253. }
  254.  
  255. ################################################################################
  256.  
  257. sub insert_message {
  258.  
  259.     # connect to database
  260.     $dbh = DBI->connect("DBI:mysql:$config{'dbname'}:$config{'dbhost'}", $config{'dbuser'}, $config{'dbpass'});
  261.  
  262.     # update parent activity
  263.     if ($parent) {
  264.         $activity_query = "UPDATE tickets SET type = 'Open', activity = UNIX_TIMESTAMP() WHERE ticket = '$parent'";
  265.         $sth = $dbh->prepare($activity_query);
  266.         $sth->execute();
  267.         $sth->finish();
  268.         $type = "Client Followup";
  269.         $assignment = "9999";
  270.     }
  271.     else {
  272.         $type = "Open";
  273.         $assignment = "0";
  274.     }
  275.  
  276.     # quote all fields
  277.     $db_parent = $dbh->quote($parent);
  278.     $attachment = $dbh->quote($attachment);
  279.     $author = $dbh->quote($header{'From'});
  280.     $subject = $dbh->quote($header{'Subject'});
  281.     $body = $dbh->quote($body);
  282.     $type = $dbh->quote($type);
  283.     $cc = $dbh->quote($header{'Cc'});
  284.     $assignment = $dbh->quote($assignment);
  285.  
  286.     # do insertion
  287.     $insert_query = "INSERT INTO tickets (parent, attachment, timestamp, author, subject, body, type, cc, assignment) ";
  288.     $insert_query .= "VALUES ($db_parent, $attachment, UNIX_TIMESTAMP(), $author, $subject, $body, $type, $cc, $assignment)";
  289.     $sth = $dbh->prepare($insert_query);
  290.     $sth->execute();
  291.     $ticket = $sth->{'mysql_insertid'};
  292.     $sth->finish();
  293.     $dbh->disconnect();
  294.  
  295. }
  296.  
  297. sub insert_attachments {
  298.     return if (!$attachment_info);
  299.  
  300.     $dbh = DBI->connect("DBI:mysql:$config{'dbname'}:$config{'dbhost'}", $config{'dbuser'}, $config{'dbpass'});
  301.     if ($skip_mime_preface) {
  302.         $i = 1;
  303.     } else {
  304.         $i = 0;
  305.     }
  306.     for ($i = 0; $i < $#attach_disposition; $i++) {
  307.         if ( ( $mime_alternative == 0 && $attach_disposition[$i] !~ /^$/) || ($mime_alternative == 1 && $attach_type[$i] !~ /text\/plain/) ) {
  308.             insert_attachment($i, $dbh);
  309.         }
  310.     }
  311.     $dbh->disconnect();
  312. }
  313.  
  314. sub insert_attachment($) {
  315.  
  316.     $att = $_[0];
  317.     $dbh = $_[1];
  318.  
  319.     # Check that we can write to the required directory and that we know who the
  320.     # web owner is.
  321.     $files_dir = $dp_root . "/files";
  322.     $file_repository = $files_dir . "/0";
  323.  
  324.     @st = stat $files_dir
  325.         or    die ("Cannot find file repository");
  326.     $web_owner = $st[4];
  327.  
  328.     # If the repository doesn't exist, create it.
  329.     stat $file_repository
  330.         or mkdir $file_repository, 0777;
  331.  
  332.     # Extract the file using mimencode if necessary.
  333.     $fid = sprintf("%x_%d", time(), $att);
  334.     # If content encoding is not 7bit, try and determine what it is
  335.     $fname = $file_repository . "/" . $fid;
  336.     $freal = ">";
  337.     $freal = "| mimencode -u -o " if ($attach_encoding[$att] =~ m/base64/i);
  338.     $freal = "| mimencode -u -q -o " if ($attach_encoding[$att] =~ m/quoted/i);
  339.     $fout = $freal . $fname;
  340.     open(FH, $fout);
  341.     for ($j = $boundary_lines[$att] + 1; $j < $boundary_end[$att+1]; $j++) {
  342.         print FH $message[$j];
  343.     }
  344.     close(FH);
  345.  
  346.     # Determine the files size
  347.     open(FH, $fname);
  348.     seek FH, 0, 2;
  349.     $filesize = tell FH;
  350.     close(FH);
  351.  
  352.     # Change ownership to the web server owner - assumes the files directory is correctly owned
  353.     chown  $fname, $web_owner 
  354.      or chmod 0666, $fname;
  355.  
  356.     # insert the file as user Admin (id=1), Project = 0
  357.     $sql_stmt = "INSERT into files (file_real_filename, file_name, file_type, file_size, file_date, file_description, file_task)  values (";
  358.     $sql_stmt .= " '" . $fid . "',";
  359.     $sql_stmt .= " '" . $attach_realname[$att] . "',";
  360.     $sql_stmt .= " '" . $attach_type[$att] . "', ";
  361.     $sql_stmt .= sprintf("%d", $filesize);
  362.     $sql_stmt .= ", NOW() , ";
  363.     $desc = "File attachment from: " . $header{'From'} . "\nTicket #" . $ticket . "\nSubject: " . $header{'Subject'};
  364.     $sql_stmt .= $dbh->quote($desc);
  365.     $sql_stmt .= ", ";
  366.     $sql_stmt .= $ticket;
  367.     $sql_stmt .= " )";
  368.     $sth = $dbh->prepare($sql_stmt);
  369.     $sth->execute();
  370.     $sth->finish();
  371. }
  372.  
  373.  
  374. ################################################################################
  375.  
  376. sub mail_report {
  377.  
  378.     # unquote necessary fields
  379.     $author =~ s/^\'(.*)\'$/$1/;
  380.     $author =~ s/\\\'/'/g;
  381.         $subject =~ s/^\'(.*)\'$/$1/;
  382.     $subject =~ s/\\\'/'/g;
  383.  
  384.     # try to strip off \r
  385.     $author =~ s/\\r//g;
  386.     $subject =~ s/\\r//g;
  387.  
  388.     # remove ticket number
  389.     $subject =~ s/\[\#\d+\](.*)/$1/;
  390.     $boundary = "_lkqwkASDHASK89271893712893"; 
  391.  
  392.     # mail the report
  393.     open(MAIL, "|$mailprog -t");
  394.     print MAIL "To: $report_to_address\n";
  395.     print MAIL "From: $report_from_address\n";
  396.     if ($parent) {
  397.         print MAIL "Subject: Client followup to trouble ticket #$parent\n";
  398.     } else {
  399.         print MAIL "Subject: New support ticket #$ticket\n";
  400.     }
  401.     print MAIL "Content-type: multipart/alternative; boundary=\"$boundary\"\n";
  402.     print MAIL "Mime-Version: 1.0\n\n";
  403.     print MAIL "--$boundary\n";
  404.     print MAIL "Content-disposition: inline\n";
  405.     print MAIL "Content-type: text/plain\n\n";
  406.     if ($parent) {
  407.       print MAIL "Followup Trouble ticket to ticket #$parent\n\n";
  408.     } else {
  409.       print MAIL "New Trouble Ticket\n\n";
  410.     }
  411.     print MAIL "Ticket ID: $ticket\n";
  412.     print MAIL "Author   : $author\n";
  413.     print MAIL "Subject  : $subject\n";
  414.     print MAIL "View     : $app_root/index.php?m=ticketsmith&a=view&ticket=$ticket\n";
  415.     print MAIL "\n--$boundary\n";
  416.     print MAIL "Content-disposition: inline\n";
  417.     print MAIL "Content-type: text/html\n\n";
  418.     print MAIL "<html>\n";
  419.     print MAIL "<head>\n";
  420.     print MAIL "<style>\n";
  421.     print MAIL ".title {\n";
  422.     print MAIL "    FONT-SIZE: 18pt; SIZE: 18pt;\n";
  423.     print MAIL "}\n";
  424.     print MAIL ".td {\n";
  425.         print MAIL "    font: 9pt arial, san-serif;\n";
  426.         print MAIL "}\n";
  427.     print MAIL "</style>\n";
  428.     if ($parent) {
  429.         print MAIL "<title>Followup Trouble ticket to ticket #$parent</title>\n";
  430.     } else {
  431.         print MAIL "<title>New Trouble ticket</title>\n";
  432.     }
  433.     print MAIL "</head>\n";
  434.     print MAIL "<body>\n";
  435.     print MAIL "\n";
  436.     print MAIL "<TABLE border=0 cellpadding=4 cellspacing=1>\n";
  437.     print MAIL "    <TR>\n";
  438.     print MAIL "        <TD nowrap><span class=title>Trouble Ticket Management</span></td>\n";
  439.     print MAIL "        <TD valign=top align=right width=100%> </td>\n";
  440.     print MAIL "    </tr>\n";
  441.     print MAIL "</TABLE>\n";
  442.     print MAIL "<TABLE width=600 border=0 cellpadding=4 cellspacing=1 bgcolor=#878676>\n";
  443.     print MAIL "    <TR>\n";
  444.     if ($parent) {
  445.         print MAIL "        <TD colspan=2><font face=arial,san-serif size=2 color=white>Followup Ticket Entered</font></TD>\n";
  446.     } else {
  447.         print MAIL "        <TD colspan=2><font face=arial,san-serif size=2 color=white>New Ticket Entered</font></TD>\n";
  448.     }
  449.     print MAIL "    </tr>\n";
  450.     print MAIL "    <TR>\n";
  451.     print MAIL "        <TD bgcolor=white nowrap class=td>Ticket ID:</TD>\n";
  452.     print MAIL "        <TD bgcolor=white nowrap class=td>$ticket</TD>\n";
  453.     print MAIL "    </tr>\n";
  454.     print MAIL "    <TR>\n";
  455.     print MAIL "        <TD bgcolor=white class=td>Author:</TD>\n";
  456.     print MAIL "        <TD bgcolor=white class=td>$author</TD>\n";
  457.     print MAIL "    </tr>\n";
  458.     print MAIL "    <TR>\n";
  459.     print MAIL "        <TD bgcolor=white class=td>Subject:</TD>\n";
  460.     print MAIL "        <TD bgcolor=white><font face=arial,san-serif size=2>$subject</font></TD>";
  461.     print MAIL "    </tr>\n";
  462.     print MAIL "    <TR>\n";
  463.     print MAIL "        <TD bgcolor=white nowrap class=td>View:</TD>\n";
  464.     print MAIL "        <TD bgcolor=white nowrap class=td><a href=$app_root/index.php?m=ticketsmith&a=view&ticket=$ticket>$app_root/index.php?m=ticketsmith&a=view&ticket=$ticket</a></TD>\n";
  465.     print MAIL "    </tr>\n";
  466.     print MAIL "</TABLE>\n";
  467.     print MAIL "</body>\n";
  468.     print MAIL "</html>\n";
  469.     print MAIL "\n--$boundary--\n";
  470.     close(MAIL);
  471.  
  472. }
  473.  
  474. ################################################################################
  475.  
  476. sub mail_acknowledgement {
  477.  
  478.     # unquote necessary fields
  479.     $author =~ s/^\'(.*)\'$/$1/;
  480.     $author =~ s/\\\'/'/g;
  481.     $subject =~ s/^\'(.*)\'$/$1/;
  482.     $subject =~ s/\\\'/'/g;
  483.  
  484.     # remove ticket number
  485.     $subject =~ s/\[\#\d+\](.*)/$1/;
  486.     $boundary = "_lkqwkASDHASK89271893712893"; 
  487.  
  488.     # mail the report
  489.     open(MAIL, "|$mailprog -t");
  490.     print MAIL "To: $author\n";
  491.     print MAIL "From: $report_from_address\n";
  492.     print MAIL "Subject: [#$ticket] Your Support Request\n";
  493.     print MAIL "Content-type: multipart/alternative; boundary=\"$boundary\"\n";
  494.     print MAIL "Mime-Version: 1.0\n\n";
  495.     print MAIL "--$boundary\n";
  496.     print MAIL "Content-disposition: inline\n";
  497.     print MAIL "Content-type: text/plain\n\n";
  498.     print MAIL "This is an acknowledgement that your support request has been logged\n";
  499.     print MAIL "by an automated support tracking system. It will be assigned to a\n";
  500.     print MAIL "support representative who will be in touch in due course.\n\n";
  501.     print MAIL "Details of support request:\n";
  502.     print MAIL "Ticket ID: $ticket\n";
  503.     print MAIL "Author   : $author\n";
  504.     print MAIL "Subject  : $subject\n";
  505.     print MAIL "\n--$boundary\n";
  506.     print MAIL "Content-disposition: inline\n";
  507.     print MAIL "Content-type: text/html\n\n";
  508.     print MAIL "<html>\n";
  509.     print MAIL "<head>\n";
  510.     print MAIL "<style>\n";
  511.     print MAIL ".title {\n";
  512.     print MAIL "    FONT-SIZE: 18pt; SIZE: 18pt;\n";
  513.     print MAIL "}\n";
  514.     print MAIL ".td {\n";
  515.         print MAIL "    font: 9pt arial, san-serif;\n";
  516.         print MAIL "}\n";
  517.     print MAIL "</style>\n";
  518.     print MAIL "<title>Your Support Request</title>\n";
  519.     print MAIL "</head>\n";
  520.     print MAIL "<body>\n";
  521.     print MAIL "\n";
  522.     print MAIL "<TABLE border=0 cellpadding=4 cellspacing=1>\n";
  523.     print MAIL "    <TR>\n";
  524.     print MAIL "        <TD nowrap><span class=title>Trouble Ticket Management</span></td>\n";
  525.     print MAIL "        <TD valign=top align=right width=100%> </td>\n";
  526.     print MAIL "    </tr>\n";
  527.     print MAIL "</TABLE>\n";
  528.     print MAIL "<TABLE width=600 border=0 cellpadding=4 cellspacing=1 bgcolor=#878676>\n";
  529.     print MAIL "    <TR>\n";
  530.     print MAIL "        <TD colspan=2><font face=arial,san-serif size=2 color=white>New Ticket Entered</font></TD>\n";
  531.     print MAIL "    </tr>\n";
  532.     print MAIL "    <TR>\n";
  533.     print MAIL "        <TD bgcolor=white nowrap class=td>Ticket ID:</TD>\n";
  534.     print MAIL "        <TD bgcolor=white nowrap class=td>$ticket</TD>\n";
  535.     print MAIL "    </tr>\n";
  536.     print MAIL "    <TR>\n";
  537.     print MAIL "        <TD bgcolor=white class=td>Author:</TD>\n";
  538.     print MAIL "        <TD bgcolor=white class=td>$author</TD>\n";
  539.     print MAIL "    </tr>\n";
  540.     print MAIL "    <TR>\n";
  541.     print MAIL "        <TD bgcolor=white class=td>Subject:</TD>\n";
  542.     print MAIL "        <TD bgcolor=white class=td>$subject</TD>\n";
  543.     print MAIL "    </tr>\n";
  544.     print MAIL "    <TR>\n";
  545.     print MAIL "        <TD bgcolor=white nowrap class=td> </TD>\n";
  546.     print MAIL "        <TD bgcolor=white nowrap class=td>\n";
  547.     print MAIL "This is an acknowledgement that your support request has been logged<br />\n";
  548.     print MAIL "by an automated support tracking system. It will be assigned to a<br />\n";
  549.     print MAIL "support representative who will be in touch in due course.\n";
  550.         print MAIL "            </font></TD>\n";
  551.     print MAIL "    </tr>\n";
  552.     print MAIL "</TABLE>\n";
  553.     print MAIL "</body>\n";
  554.     print MAIL "</html>\n";
  555.     print MAIL "\n--$boundary--\n";
  556.     close(MAIL);
  557.  
  558. }
  559.  
  560.  
  561.