NAWA 0.9
Web Application Framework for C++
contactform.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2022 Tobias Flaig.
3 *
4 * This file is part of nawa.
5 *
6 * nawa is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License,
8 * version 3, as published by the Free Software Foundation.
9 *
10 * nawa is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with nawa. If not, see <https://www.gnu.org/licenses/>.
17 */
18
24#include <nawa/Exception.h>
25#include <nawa/application.h>
26#include <nawa/logging/Log.h>
29#include <nawa/util/encoding.h>
30#include <random>
31
32using namespace std;
33using namespace nawa;
34using namespace nawa::mail;
35
36namespace {
37 Log logger;
38}
39
40int init(AppInit& appInit) {
41 // set up logging
42 logger.setAppname("Contact form app");
43
44 return 0;
45}
46
47int handleRequest(Connection& connection) {
48
49 // make sure cookies are a bit more secure
50 connection.setCookiePolicy(Cookie().httpOnly(true));
51
52 // we use session variables for a basic spam protection
53 connection.session().start();
54 // randVal is a random value that could be present from a previous request
55 auto randVal = connection.session()["randVal"];
56
57 // we will work with POST data quite often now, so we'll create a shortcut
58 auto const& post = connection.request().post();
59
60 // get a shortcut for the response stream
61 auto& resp = connection.responseStream();
62
63 // HTML header
64 resp << "<!DOCTYPE html><head><title>Contact Form</title></head><body>";
65
66 // if there is POST data, and randVal is not empty, process it
67 if (randVal.has_value() && post.count("rand_val") == 1) {
68
69 // check for correct rand_val
70 bool randValid = false;
71 try {
72 if (any_cast<unsigned int>(randVal) == stoul(post["rand_val"])) {
73 randValid = true;
74 }
75 } catch (...) {}
76
77 if (!randValid) {
78 resp << "<p>No spamming, please!</p>";
79 return 0;
80 }
81
82 // check whether the user filled in all required fields
83 if (post["name"].empty() || post["email"].empty() || post["subject"].empty() || post["message"].empty()) {
84 resp << "<p>Please go back and fill in all required fields!</p></body></html>";
85 return 0;
86 }
87
88 // create a SimpleEmail object from the form entries
89 SimpleEmail email;
90 EmailAddress from(post["name"], "contactform@example.com");
91 EmailAddress to("The Admin", "admin@example.com");
92 EmailAddress replyTo(post["email"]);
93 email.headers()["From"] = from.get();
94 email.headers()["To"] = to.get();
95 email.headers()["Content-Type"] = "text/plain; charset=UTF-8";
96 // apply Q-encoding to the header, just in case the user used special chars in the subject
97 email.headers()["Subject"] = encoding::makeEncodedWord("[Contact Form] " + post["subject"]);
98 email.quotedPrintableEncode(true);
99 email.text() = "This contact form was sent via an example nawa application!\r\n\r\n"
100 "Name of the sender: " +
101 post["name"] + "\r\n"
102 "Email of the sender: " +
103 post["email"] + "\r\n\r\n" +
104 post["message"];
105
106 // now use SmtpMailer to send the email to your mailbox
107 mail::SmtpMailer smtp("example.com", 587, mail::SmtpMailer::TlsMode::REQUIRE_STARTTLS,
108 true, "test@example.com", "12345");
109 smtp.enqueue(std::make_shared<SimpleEmail>(email), to, std::make_shared<EmailAddress>(from));
110 try {
111 smtp.processQueue();
112 resp << "<p>Message sent successfully!</p>";
113 } catch (const Exception& e) {
114 resp << "<p>Message could not be sent due to a technical problem :(</p>";
115 NLOG_ERROR(logger, "Error sending email: " << e.getMessage())
116 NLOG_DEBUG(logger, "Debug info: " << e.getDebugMessage())
117 }
118
119 resp << "</body></html>";
120
121 return 0;
122 }
123
124 // generate a new random value for inclusion in our form, so we can check it later
125 random_device rd;
126 randVal = rd();
127 connection.session().set("randVal", randVal);
128
129 // and show the form!
130 resp << "<p>Please fill in the following form in order to contact us! All fields are required.</p>\r\n"
131 "<form name=\"contact\" method=\"post\" action=\"?\">"
132 "<input type=\"hidden\" name=\"rand_val\" value=\""
133 << any_cast<unsigned int>(randVal)
134 << "\" />"
135 "<p>Your name: <input type=\"text\" name=\"name\" /></p>"
136 "<p>Email address: <input type=\"email\" name=\"email\" /></p>"
137 "<p>Subject: <input type=\"text\" name=\"subject\" /></p>"
138 "<p>Message: <textarea name=\"message\" rows=\"5\" cols=\"30\"></textarea></p>"
139 "<p><input type=\"submit\" name=\"go\" value=\"Submit\" /></p>"
140 "</form></body></html>";
141
142 return 0;
143}
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Simple class for (not (yet) thread-safe) logging to stderr or to any other output stream.
#define NLOG_DEBUG(Logger, Message)
Definition: Log.h:201
#define NLOG_ERROR(Logger, Message)
Definition: Log.h:183
Structure representing a basic email.
For establishing a connection to an SMTP server and sending emails.
This file will be configured by CMake and contains the necessary properties to ensure that a loaded a...
nawa::Session & session() noexcept
Definition: Connection.cpp:361
void setCookiePolicy(Cookie policy)
Definition: Connection.cpp:349
std::ostream & responseStream() noexcept
Definition: Connection.cpp:377
nawa::Request const & request() const noexcept
Definition: Connection.cpp:357
virtual std::string getMessage() const noexcept
Definition: Exception.h:71
virtual std::string getDebugMessage() const noexcept
Definition: Exception.h:79
Definition: Log.h:38
request::Post const & post() const noexcept
Definition: Request.cpp:56
void set(std::string key, const std::any &value)
Definition: Session.cpp:297
std::string start(std::string sessionId, std::optional< unsigned long > keepalive=std::nullopt)
Definition: Session.cpp:127
std::string get(bool includeName=true, bool applyPunycode=true) const
HeadersMap & headers() noexcept
bool & quotedPrintableEncode() noexcept
std::string & text() noexcept
int handleRequest(Connection &connection)
Definition: contactform.cpp:47
int init(AppInit &appInit)
Definition: contactform.cpp:40
Namespace containing functions for text encoding and decoding.
std::string makeEncodedWord(std::string const &input, bool base64=false, bool onlyIfNecessary=true)
Definition: encoding.cpp:339
Definition: AppInit.h:31