This article is Copyright © 1999 by Jürgen Hermann and Copyright © 1999 by C-Scene. All Rights Reserved.

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.

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 encoded 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



]]>

wwwmail.cpp

#include #include #include #include #include #include // this is the return address put into the mail (should be in the config file) const char* POSTMASTER = "David Stiles "; // 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

;http://megaton.cscene.org/~snibril/ xgc;Jon Armstrong ;http://megaton.cscene.org/c/thankyou.html ]]>

A sample mail

To: Juergen Hermann From: David Stiles 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! ]]>

Gundavaram, Shishir, CGI Programming on the World Wide Web.
O'Reilly, 1996, 433 p., covers CGI basics as well as hot topics like data base interfacing, details of the HTTP protocol and their use, forms, server side includes, hyper-media, client-pull and server-push (examples in Perl), ISBN 1-56592-168-2.

Hermann, Jürgen, Sending image files via CGI.
C-Scene, 1998.

York, Brent, CGI in C - A starter's tutorial.
C-Scene, 1997.