summaryrefslogtreecommitdiff
path: root/src/mail.cpp
blob: edd892bd0cfef1ce713be9fe036e46a59e0ec53b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "services.h"

MailThread::MailThread(const Anope::string &mailto, const Anope::string &addr, const Anope::string &subject, const Anope::string &message) : Thread(), MailTo(mailto), Addr(addr), Subject(subject), Message(message), DontQuoteAddresses(Config->DontQuoteAddresses), Success(false)
{
}

MailThread::~MailThread()
{
	if (Success)
		Log(LOG_NORMAL, "mail") << "Successfully delivered mail for " << MailTo << " (" << Addr << ")";
	else
		Log(LOG_NORMAL, "mail") << "Error delivering mail for " << MailTo << " (" << Addr << ")";
}

void MailThread::Run()
{
	FILE *pipe = popen(Config->SendMailPath.c_str(), "w");

	if (!pipe)
	{
		SetExitState();
		return;
	}

	fprintf(pipe, "From: %s\n", Config->SendFrom.c_str());
	if (this->DontQuoteAddresses)
		fprintf(pipe, "To: %s <%s>\n", MailTo.c_str(), Addr.c_str());
	else
		fprintf(pipe, "To: \"%s\" <%s>\n", MailTo.c_str(), Addr.c_str());
	fprintf(pipe, "Subject: %s\n", Subject.c_str());
	fprintf(pipe, "%s", Message.c_str());
	fprintf(pipe, "\n.\n");

	pclose(pipe);

	Success = true;
	SetExitState();
}

bool Mail(User *u, NickCore *nc, BotInfo *service, const Anope::string &subject, const Anope::string &message)
{
	if (!u || !nc || !service || subject.empty() || message.empty())
		return false;

	if (!Config->UseMail)
		u->SendMessage(service, _("Services have been configured to not send mail."));
	else if (Anope::CurTime - u->lastmail < Config->MailDelay)
		u->SendMessage(service, _("Please wait \002%d\002 seconds and retry."), Config->MailDelay - (Anope::CurTime - u->lastmail));
	else if (nc->email.empty())
		u->SendMessage(service, _("E-mail for \002%s\002 is invalid."), nc->display.c_str());
	else
	{
		u->lastmail = nc->lastmail = Anope::CurTime;
		threadEngine.Start(new MailThread(nc->display, nc->email, subject, message));
		return true;
	}

	return false;
}

bool Mail(NickCore *nc, const Anope::string &subject, const Anope::string &message)
{
	if (!Config->UseMail || !nc || nc->email.empty() || subject.empty() || message.empty())
		return false;

	nc->lastmail = Anope::CurTime;
	threadEngine.Start(new MailThread(nc->display, nc->email, subject, message));

	return true;
}

/**
 * Checks whether we have a valid, common e-mail address.
 * This is NOT entirely RFC compliant, and won't be so, because I said
 * *common* cases. ;) It is very unlikely that e-mail addresses that
 * are really being used will fail the check.
 *
 * @param email Email to Validate
 * @return bool
 */
bool MailValidate(const Anope::string &email)
{
	bool has_period = false;

	static char specials[] = {'(', ')', '<', '>', '@', ',', ';', ':', '\\', '\"', '[', ']', ' '};

	if (email.empty())
		return false;
	Anope::string copy = email;

	size_t at = copy.find('@');
	if (at == Anope::string::npos)
		return false;
	Anope::string domain = copy.substr(at + 1);
	copy = copy.substr(0, at);

	/* Don't accept empty copy or domain. */
	if (copy.empty() || domain.empty())
		return false;

	/* Check for forbidden characters in the name */
	for (unsigned i = 0, end = copy.length(); i < end; ++i)
	{
		if (copy[i] <= 31 || copy[i] >= 127)
			return false;
		for (unsigned int j = 0; j < 13; ++j)
			if (copy[i] == specials[j])
				return false;
	}

	/* Check for forbidden characters in the domain */
	for (unsigned i = 0, end = domain.length(); i < end; ++i)
	{
		if (domain[i] <= 31 || domain[i] >= 127)
			return false;
		for (unsigned int j = 0; j < 13; ++j)
			if (domain[i] == specials[j])
				return false;
		if (domain[i] == '.')
		{
			if (!i || i == end - 1)
				return false;
			has_period = true;
		}
	}

	return has_period;
}