C-Scene Issue #07 Jürgen Hermann |
CGI Bits & Pieces #2 |
This is the second installment of CGI Bits & Pieces, which shows you how to parse
data submitted from a HTML form and send it as a mail to a predefined email account.
If you are a CGI novice,
see the references at the end of this article. Having read
Brent's article is the bare minimum to understand anything that follows.
All examples are implemented in C++ and usually compiled and tested on both
Windows NT4 using Visual C++ 5.0 and FreeBSD/linux using egcs.
|
Form handling |
The data a user inputs into the fields of a form is sent to the URL that
is contained in the ACTION property of the FORM tag. But you do not get the data
as it is entered, but in a coded form that allows you to dissect control information from
user data. The control information is the name of the form fields, so you are able to
associate the data with the field it was entered into. The fields are sent as a string of name=value pairs, and if there are several
fields, those pairs are separated by an ampersand "&". Certain chars in the name and value part
are www-url-encoded, which means spaces are sent as a "+" character, and all other special
characters are sent as a per-cent sign "%" followed by the code of the character as two hex-digits.
Obviously, the characters "%&+=" have to be coded in that form; which characters are encoded exactly
is the decision of the browser.
The following code echos this encoded form information,
in clear text form, into a mail. Additionally, relevant information from the cgi-bin environment
is also included in the mail. To prevent people from sending to any email address
(i.e. using our server to spam), we have a config file that defines valid destination addresses; this also allows
us to define a follow-up URL that is showed to the user after he clicked on the submit button.
Both these configuration values are accessed by an alias name that is appended to the URL in the form tag and thus
can be found in the QUERY_STRING variable.
Below, you find the HTML code of a sample form, the C++ source code and a sample configuration file.
Finally, you can see a mail generated by the program compiled for RedHat Linux 2.0.36 using
egcs-2.91.60.
|
A sample FORM |
<form method=post action="/cgi-bin/wwwmail?jh">
<input type=text name=topic><br> <textarea name=memo cols=60 rows=3></textarea><br> <input type=submit name=button value=" Send it! "> </form> |
wwwmail.cpp |
// Send form data to an email account // // Copyright (c) 1999 by Jürgen Hermann, All Rights Reserved. #include <stdio.h> #include <time.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fstream> #include <sstream> // this is the return address put into the mail (should be in the config file) const char* POSTMASTER = "David Stiles <pingme@home.com>"; // get delimited substring from "str" and remove it from "str" std::string strParse(std::string& str, const char* delim) { std::string result; std::string::size_type pos = str.find_first_of(delim); if (pos == std::string::npos) { result = str; str = ""; } else { result = str.substr(0, pos); str.erase(0, pos+1); } return result; } // send an environment value to a stream, if it actually exists void echoEnv(ostream& out, const char* envvar) { char* val = getenv(envvar); if (val) out << envvar << '=' << val << '\n'; } // the main program int main() { // init mode_t oldmask = umask(0); umask(oldmask & ~mode_t(0770)); // scan the .cfg file and check for allowed destinations char* query = getenv("QUERY_STRING"); string mailto; string jumpto; if (query) { ifstream in("wwwmail.cfg"); string line; while (!in.eof()) { getline(in, line); if (!in.good() || in.eof()) break; string alias = strParse(line, ";"); if (alias == query) { mailto = strParse(line, ";"); jumpto = strParse(line, ";"); break; } } } if (!query || mailto.empty() || jumpto.empty()) { cout << "Content-Type: text/plain\n" << "\n" << "Internal error!\n"; exit(666); } // build the mail header char* formpage = getenv("HTTP_REFERER"); ostringstream mail; mail << "To: " << mailto << '\n' << "From: " << POSTMASTER << '\n' << "Subject: Form submit from '" << (formpage ? formpage : "Unknown URL") << "'\n" << "\n"; // send identification of the remote user echoEnv(mail, "HTTP_USER_AGENT"); echoEnv(mail, "REMOTE_ADDR"); echoEnv(mail, "REMOTE_IDENT"); echoEnv(mail, "REMOTE_HOST"); echoEnv(mail, "REMOTE_USER"); // send form data int ch; while ((ch = cin.get(), !cin.eof())) { switch (ch) { default: mail << char(ch); break; case '+': mail << ' '; break; case '&': mail << '\n'; break; case '%': { char buf[3]; buf[0] = cin.get(); buf[1] = cin.get(); buf[2] = '\0'; mail << char(strtol(buf, 0, 16)); break; } // case } // switch } // while mail << '\n'; // send the mail FILE* sendmail = popen("/usr/lib/sendmail -i -t", "w"); if (sendmail) { fputs(mail.str().c_str(), sendmail); pclose(sendmail); } // redir to follow-up page cout << "Location: " << jumpto << "\n\n"; return 0; } |
wwwmail.cfg |
jh;Juergen Hermann <jh@schiele-ct.de>;http://megaton.cscene.org/~snibril/
xgc;Jon Armstrong <armstron@eznet.net>;http://megaton.cscene.org/c/thankyou.html |
A sample mail |
Date: Thu, 25 Mar 1999 18:31:08 -0800
Message-Id: <199903260231.SAA19302@cx505299-a.fed1.sdca.home.com> To: Juergen Hermann <jh@schiele-ct.de> From: David Stiles <pingme@home.com> Subject: Form submit from 'http://megaton.cscene.org/~snibril/' X-UIDL: e7cc1b08bd03fcdfc5273bf7d79d9b44 HTTP_USER_AGENT=Mozilla/4.03 [en] (WinNT; I ;Nav) REMOTE_ADDR=193.141.27.190 REMOTE_HOST=193.141.27.190 topic=test memo=abc def button= Send it! |
References |
Gundavaram, Shishir, «CGI Programming on the World Wide Web».
Hermann, Jürgen, «Sending image files via CGI».
York, Brent, «CGI in C a starters tutorial». This page is Copyright © 1999 by Jürgen Hermann and Copyright © 1999 by C Scene. All Rights Reserved. |