Forum Moderators: coopster & phranque

Message Too Old, No Replies

help with cgi script

won't send attachments

         

webmstr

2:57 pm on Jan 10, 2006 (gmt 0)

10+ Year Member



My client paid their hosting server/isp to write a script so that the email form on their website enables them to receive attachments from people wanting to submit resumes. Well, it hasn't worked from the get go. My client won't deal with the hosting service, only me, so I am stuck being the middle man here. I have emailed them so many times about this and keep getting different "reasons" why the script isn't working. They get the email, but no attachment....they say they fixed it, now the client gets the attachment but it is all in "greek" as they put it. Another strange thing....when the form sends a copy of my test submission, I look at the attachment that comes with it, and it has the title of my computer's directory path instead of just the name of the document. I have emailed the host (guy who wrote the script) about this and they are clueless. Can I post the script here and see if anyone can find the problem? Is that allowed? Thanks!

perl_diver

4:14 pm on Jan 10, 2006 (gmt 0)

10+ Year Member



advise yuour client to seek a refund. There are many scripts already written that can send attachments to emails that you could customize for your clients needs. Search in a script archive like www.hotscripts.com for whatever server side script you prefer: perl, php, asp, other.

rocknbil

11:23 am on Jan 11, 2006 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



By all means post the script if it's not War and Peace. :-)

I have a couple suspicions about your script that your post reveals. First, I'm going under the assumption that this is your process: A web-based form with a file upload object on it allows the user to compose a message, attach a file. The file is uploaded to the server at least temporarily, then the script attaches the file to the outgoing mail and sends an email to the recipient(s).

Both of these processes require use and knowledge of multipart messages, and by your post it sounds like someone's a little shy on the "knowledge." part. :-)

now the client gets the attachment but it is all in "greek" as they put it.

So the binary image data is getting stuffed into the email inline instead of as an attachment. From this you can tell that the data is not being read and parsed correctly.

Data sent with a multi-part content is just that: some of the data is plain text, some of it is binary data that can be anything from an image to an .exe. In the multipart data stream there are boundaries defining where one ends and the other begins, and modules like CGI.pm are built to find these and split up the bits. A standard read/parse on multipart data will not correctly separate the parts and deliver them as you're seeing, a dump of binary gobblydey-gook directly in the email.

The second clue that this is possibly going on:

when the form sends a copy of my test submission, I look at the attachment that comes with it, and it has the title of my computer's directory path instead of just the name of the document

This is because the BROWSER is sending that path as the file name. If you want just the document name, you'll need to use a different variable to attach:

$path= $query->param('file'); #C:\wherever\yourfile.doc
@path = split(/\\/,$path); # ('C:','wherever','yourfile.doc');
$filename = $path[$#path]; #yourfile.doc

Now you read in the image data from $query->param('file'); and store it in $filename. When you go to attach it to the email, you'll be attaching $filename (yourfile.doc) not the full path.

I've already mentioned cgi.pm, the standard modue for many web data in/out functions, especially parsing multipart data. The other module you should look into is MIME::Lite, as it makes composing emails with or without attachments a breeze.

webmstr

1:05 pm on Jan 11, 2006 (gmt 0)

10+ Year Member



Thank you so much for taking the time to help me figure this out. There are three files that I can see on the server...a ".pm" file and two ".cgi"....(the second .cgi is labeled original for the old form that they used to use on their site so I don't think it is active, but he just left it there and renamed it...I am not a programmer and this is all new to me, but here is the new .cgi that he wrote first and the .pm second: (I changed the name to "website" for privacy purposes only)

#!/usr/bin/perl

use strict;
use CGI_LIB;
use MIME::Lite;

my $body = '';
my($cgi) = new CGI_LIB;

&cgi_header;

#### Make sure all necessary variables for email message are filled in

($cgi->{'subject'}) ¦¦ ($cgi->{'subject'} = "website Resume Submission [WWW]");
($cgi->{'email'}) ¦¦ ($cgi->{'email'} = "anonymous\@website.com");

#### Create the text for the email (not bothering with HTML MIME part)

$body = "\n";
$body .= "I am interested in a career with so and so Inc. Please find \n";
$body .= "attached a copy of my resume.\n";
$body .= "\n";
$body .= "My mailing address:\n";
$body .= "\n";
$body .= "Address: $cgi->{'street'}\n" if $cgi->{'street'};
$body .= "City: $cgi->{'city'}\n";
$body .= "State: $cgi->{'state'}\n";
$body .= "Zip: $cgi->{'zipcode'}\n";
$body .= "Phone: $cgi->{'phone'}\n";
$body .= "Extension: $cgi->{'extention'}\n" if $cgi->{'extention'};
$body .= "Email: $cgi->{'email'}\n";
$body .= "\n";
$body .= "\n" if $cgi->{'comments'};
$body .= "Additional Comments:\n" if $cgi->{'comments'};
$body .= "\n" if $cgi->{'comments'};
$body .= "$cgi->{'comments'}\n" if $cgi->{'comments'};
$body .= "\n";
$body .= "=============================================================\n";
$body .= "This mail was generated automatically. Please report any\n";
$body .= "problems to webmaster\@website.com. Requester was:\n";
$body .= "=============================================================\n";
$body .= "REMOTE HOST: $ENV{'REMOTE_HOST'}\n";
$body .= "REMOTE ADDRESS: $ENV{'REMOTE_ADDR'}\n";
$body .= "HTTP_USER_AGENT: $ENV{'HTTP_USER_AGENT'}\n";
$body .= "=============================================================\n";
$body .= "\n";

#### Create the email

my $message = MIME::Lite->new(
From =>"$cgi->{'name'} <$cgi->{'email'}>",
To =>"website
<electric\@website.com>",
Cc =>"$cgi->{'email'}",
Subject =>$cgi->{'subject'},
Type =>'multipart/alternative'
);

$message->attach(Type => 'text/plain',
Data => $body
);

if ($cgi->{'file'}) {
$message->attach(Type => "$cgi->{'file'}->{'content-type'}",
Data => $cgi->{'file'}->{'contents'},
Filename =>"$cgi->{'file'}->{'filename'}",
Disposition => 'attachment'
);
}

$message->send;

#### Now, redirect if "next-url" is included
if ($cgi->{'next-url'}) {
print "Location: $cgi->{'next-url'}\n";
print "\n";
exit;
}

#### Output confirmation message ####
print qq¦<HTML><HEAD><TITLE>Thank You</TITLE></HEAD><BODY BGCOLOR="#FFFAE4" text="#3366cc" link="#0000E8" vlink="#FF0000">\n¦;
print qq¦<H1><center>Thank You For Your Submission</center></H1>\n¦;
print qq¦<center>Your resume has been sent to Davis Electric.<p>¦;
print qq¦If you included an email address a copy will be sent to you.¦;
print "<p>\n";
print qq¦<A HREF="/">Return to website Inc</A></center>¦;
print "</BODY>\n";
exit;

#####################################################################
#### SUBROUTINES ####################################################

sub error_blank_field {
my $variable = @_;
print "\n" if ($cgi->{'next-url'});
print "<HTML><HEAD><TITLE>Comments Form Error</TITLE></HEAD><BODY>\n";
print "<H1>Error Processing Your Comments!</H1>\n";
print "You did not fill in $variable.\n";
print "Please go back to the form and do so.\n";
print "</BODY>\n";
exit;
}

sub cgi_header {
print "Content-type: text/html\n";
print "\n" unless ($cgi->{'next-url'});
}

#
# CGI_LIB package.
#
package CGI_LIB;

#===============================================================================
#
# Constructor:
# $obj = new CGI_LIB;
#
# Methods:
# $obj->getFormMethod();
# $obj->getSubmittedData();
# $obj->processData();
# $obj->processURLEncodedData();
# $obj->processMultiPartData();
#
# *p/s: For more details, please refer to the description below.
#
#===============================================================================

use strict;
use vars qw($FormMethod $VERSION);

$VERSION = '1.01';

#===============================================================================
#
# CONSTRUCTOR: $obj = new CGI_LIB;
#
# DESCRIPTION: This is the constructor of the CGI_LIB object.
# The constructor will process the data according to the form
# method used, and set the data into the object.
#
#===============================================================================
sub new {
my($pkg) = shift;
my($self) = {};

bless $self, $pkg;

$FormMethod = $self->getFormMethod();
$self->processData();
return $self;
}

#===============================================================================
#
# METHOD: $obj->getFormMethod();
#
# DESCRIPTION: This method will return the form method that has been used.
#
#===============================================================================
sub getFormMethod {
my($self) = shift;

return $ENV{'REQUEST_METHOD'};
}

#===============================================================================
#
# METHOD: $obj->getSubmittedData();
#
# DESCRIPTION: This method will return the submitted form data.
#
#===============================================================================
sub getSubmittedData {
my($self) = shift;

if ($FormMethod eq "POST") {
my($buffer);
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
return $buffer;
}
elsif ($FormMethod eq "GET") {
return $ENV{'QUERY_STRING'};
}
else {
return $ENV{'QUERY_STRING'};
}
}

#===============================================================================
#
# METHOD: $obj->processData();
#
# DESCRIPTION: According to different type of submitted data, this method will
# call different subroutine to process the data and set them into
# the object.
#
#===============================================================================
sub processData {
my($self) = shift;

if ($ENV{'CONTENT_TYPE'} =~ /^multipart\/form-data;/) {
$self->processMultiPartData();
}
elsif ($ENV{'CONTENT_TYPE'} =~ /^application\/x-www-form-urlencoded$/) {
$self->processURLEncodedData();
}
else {
$self->processURLEncodedData();
}
if ($FormMethod ne "GET" && $ENV{'QUERY_STRING'} ne "") {
$FormMethod = "GET";
$self->processURLEncodedData();
}
}

#===============================================================================
#
# METHOD: $obj->processURLEncodedData();
#
# DESCRIPTION: The submitted data is in application/x-www-form-urlencoded
# format.
#
#===============================================================================
sub processURLEncodedData {
my($self) = shift;

my($submittedData) = $self->getSubmittedData();

my(@fields) = split('&', $submittedData);

for (@fields) {
tr/+/ /;

my($fieldName, $fieldValue) = split('=', $_, 2);

# The %xx hex numbers are converted to alphanumeric.
$fieldName =~ s/%(..)/pack("C", hex($1))/eg;
$fieldValue =~ s/%(..)/pack("C", hex($1))/eg;

if (exists $self->{$fieldName}) {
if (ref($self->{$fieldName}) eq "ARRAY") {
push(@{$self->{$fieldName}}, $fieldValue);
}
else {
my($tempValue) = $self->{$fieldName};
delete $self->{$fieldName};
push(@{$self->{$fieldName}}, $tempValue);
push(@{$self->{$fieldName}}, $fieldValue);
}
}
else {
$self->{$fieldName} = $fieldValue;
}
}
}

#===============================================================================
#
# METHOD: $obj->processMultiPartData();
#
# DESCRIPTION: The submitted data is in multipart/form-data format.
# We only using this format when we want to do HTTP file upload.
#
#===============================================================================
sub processMultiPartData {
my($self) = shift;
my($submittedData) = $self->getSubmittedData();

my($boundary) = $ENV{'CONTENT_TYPE'} =~ /^.*boundary=(.*)$/;

my(@partsArray) = split(/--$boundary/, $submittedData);

@partsArray = splice(@partsArray, 1, (scalar(@partsArray) - 2));

my($aPart);
foreach $aPart (@partsArray) {
$aPart =~ s/(\r¦)\n$//g;
my($dump, $firstline, $fieldValue) = split(/[\r]\n/, $aPart, 3);
next if $firstline =~ /filename=\"\"/;
$firstline =~ s/^Content-Disposition: form-data; //;
my(@columns) = split(/;\s+/, $firstline);
my($fieldName) = $columns[0] =~ /^name=\"([^\"]+)\"$/;
my(%dataHash);
if (scalar(@columns) > 1) {
my($contentType, $blankline);
($contentType, $blankline, $fieldValue) = split(/[\r]\n/, $fieldValue, 3);
($dataHash{'content-type'}) = $contentType =~ /^Content-Type: ([^\s]+)$/;
}
else {
my($blankline);
($blankline, $fieldValue) = split(/[\r]\n/, $fieldValue, 2);
if (exists $self->{$fieldName}) {
if (ref($self->{$fieldName}) eq "ARRAY") {
push(@{$self->{$fieldName}}, $fieldValue);
}
else {
my($tempValue) = $self->{$fieldName};
delete $self->{$fieldName};
push(@{$self->{$fieldName}}, $tempValue);
push(@{$self->{$fieldName}}, $fieldValue);
}
}
else {
next if $fieldValue =~ /^\s*$/;
$self->{$fieldName} = $fieldValue;
}
next;
}
my($currentColumn);
for $currentColumn (@columns) {
my($currentHeader, $currentValue) = $currentColumn =~ /^([^=]+)="([^"]+)"$/;
$dataHash{"$currentHeader"} = $currentValue;
}
$dataHash{'contents'} = $fieldValue;
$dataHash{'size'} = length($fieldValue);
$self->{$fieldName} = \%dataHash;
}
}

1;
__END__

=head1 NAME

CGI_LIB -- This is a perl module which will help you to manipulate the CGI input.

=head1 SYNOPSIS

use CGI_LIB;
$obj = new CGI_LIB();

=head1 DESCRIPTION

For more details about this module, please visit to
[tneoh.zoneit.com...]

=head1 AUTHOR

Simon Tneoh Chee-Boon tneohcb@pc.jaring.my

Copyright (c) 1998 Simon Tneoh Chee-Boon. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=head1 VERSION

Version 1.01 21 August 1999

rocknbil

1:26 am on Jan 12, 2006 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm just going to throw a couple comments out here because by what I can see at first glance, this should work. Some of the real wizards here will be able to help more.

CGI.pm is now a standard distribution of perl and you don't need to do anything but include it:

use CGI;

But with this script you can't just add that and all your problems will go away. It has it's own methods and you'd have to swap out all the references to CGI_LIB to make it work. The only reason I mention it is I've no clue as to whether the CGI_LIB module is correctly splitting the multipart data.

Second, I see you're using Mime:Lite and everything there looks like it should work also, basically if the file exists it adds it as an attachment.

The only unseen item here: are you absolutely sure your submitting form is a multipart enctype?

<form method="post" action="thisScript" enctype="multipart/form-data">

webmstr

3:47 am on Jan 12, 2006 (gmt 0)

10+ Year Member



The only unseen item here: are you absolutely sure your submitting form is a multipart enctype?

<form method="post" action="thisScript" enctype="multipart/form-data">

I guess what you mean is what is in the .html code? If so, here is what I have there...

<form action="http://website.com/cgi-bin/webrequest.cgi" method="post" enctype="multipart/form-data" onsubmit="MM_validateForm('name','','R','email','','R');return document.MM_returnValue">
<p><strong>Your Name:</strong><br />
<input type="text" name="name" size="50" />
<br />
<strong>Your e-mail address:</strong><br />
<input type="text" name="email" size="50" />
<br />
<strong>Your mailing address (optional):</strong><br />
P.O. Box:<br />
<input type="text" name="pobox" size="10" />
<br />
Street:<br />
<input type="text" name="street" size="50" />
<br />
City:<br />
<input type="text" name="city" size="50" />
<br />
State:<br />
<input type="text" name="state" size="10" />
<br />
Zip Code:<br />
<input type="text" name="zipcode" size="10" />
<br />
Phone:<br />
<input type="text" name="phone" size="20" />
Extension:
<input type="text" name="extension" size="5" />
<br />
<br />
<strong>Questions or Comments :</strong><br />
<textarea name="comments" rows="10" cols="45"></textarea>
<br />
<img src="images/dotclear.gif" alt="" width="1" height="20" /><br />
Please upload your resume here:</p>
<p>
<input type="file" name="file" />
<br />
</p>
<center>
<input type="submit" value="Send" />
<img src="images/dotclear.gif" alt="" width="30" height="8" />
<input type="reset" value="Clear" />
<br />
</center>
</form>

webmstr

6:04 pm on Jan 16, 2006 (gmt 0)

10+ Year Member



bumping for further help! thanks