NAWA  0.8
Web Application Framework for C++
contactform.cpp
Go to the documentation of this file.
1 
6 /*
7  * Copyright (C) 2019-2021 Tobias Flaig.
8  *
9  * This file is part of nawa.
10  *
11  * nawa is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License,
13  * version 3, as published by the Free Software Foundation.
14  *
15  * nawa is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with nawa. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <nawa/Exception.h>
25 #include <nawa/application.h>
26 #include <nawa/logging/Log.h>
28 #include <nawa/mail/SmtpMailer.h>
29 #include <nawa/util/encoding.h>
30 #include <random>
31 
32 using namespace std;
33 using namespace nawa;
34 using namespace nawa::mail;
35 
36 namespace {
37  Log logger;
38 }
39 
40 int init(AppInit& appInit) {
41  // set up logging
42  logger.setAppname("Contact form app");
43 
44  return 0;
45 }
46 
47 int 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.
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 start(nawa::Cookie properties=Cookie())
Definition: Session.cpp:127
void set(std::string key, const std::any &value)
Definition: Session.cpp:269
std::string get(bool includeName=true, bool applyPunycode=true) const
HeadersMap & headers() noexcept
std::string & text() noexcept
bool & quotedPrintableEncode() 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