Blame


1 84c190b6 2021-12-17 jrmu #!/usr/bin/perl
2 84c190b6 2021-12-17 jrmu
3 84c190b6 2021-12-17 jrmu package Mail;
4 84c190b6 2021-12-17 jrmu
5 84c190b6 2021-12-17 jrmu use strict;
6 84c190b6 2021-12-17 jrmu use warnings;
7 84c190b6 2021-12-17 jrmu use OpenBSD::Pledge;
8 84c190b6 2021-12-17 jrmu use OpenBSD::Unveil;
9 8de79cd0 2023-03-06 jrmu use File::Copy qw(copy);
10 84c190b6 2021-12-17 jrmu use MIME::Base64;
11 84c190b6 2021-12-17 jrmu use Digest::SHA qw(sha256_hex);
12 84c190b6 2021-12-17 jrmu
13 84c190b6 2021-12-17 jrmu my %conf = %main::conf;
14 8de79cd0 2023-03-06 jrmu my $chans = $conf{chans};
15 84c190b6 2021-12-17 jrmu my $staff = $conf{staff};
16 f8e21ca0 2023-03-07 jrmu my $mailhostname = $conf{mailhostname};
17 84c190b6 2021-12-17 jrmu my $mailfrom = $conf{mailfrom};
18 84c190b6 2021-12-17 jrmu my $mailname = $conf{mailname};
19 176d5d5d 2023-03-06 jrmu my $imapport = $conf{imapport};
20 176d5d5d 2023-03-06 jrmu my $smtpport = $conf{smtpport};
21 176d5d5d 2023-03-06 jrmu my $teamchans = $conf{teamchans};
22 176d5d5d 2023-03-06 jrmu my @teamchans = split /[,\s]+/m, $teamchans;
23 cae514a3 2023-03-07 jrmu my $webmail = $conf{webmail};
24 8de79cd0 2023-03-06 jrmu my $approval = $conf{approval};
25 8de79cd0 2023-03-06 jrmu my $expires = $conf{expires};
26 176d5d5d 2023-03-06 jrmu my $passwdpath = "/etc/mail/passwd";
27 176d5d5d 2023-03-06 jrmu my $virtualspath = "/etc/mail/virtuals";
28 176d5d5d 2023-03-06 jrmu my $senderspath = "/etc/mail/users";
29 8de79cd0 2023-03-06 jrmu my @users;
30 176d5d5d 2023-03-06 jrmu
31 b3cb5043 2023-03-06 jrmu main::cbind("msg", "-", "mail", \&mmail);
32 84c190b6 2021-12-17 jrmu
33 84c190b6 2021-12-17 jrmu sub init {
34 84c190b6 2021-12-17 jrmu #dependencies for encrypt
35 84c190b6 2021-12-17 jrmu unveil("/usr/bin/encrypt", "rx") or die "Unable to unveil $!";
36 84c190b6 2021-12-17 jrmu #dependencies for mail
37 84c190b6 2021-12-17 jrmu unveil("/usr/sbin/sendmail", "rx") or die "Unable to unveil $!";
38 8de79cd0 2023-03-06 jrmu unveil($passwdpath, "rwc") or die "Unable to unveil $!";
39 8de79cd0 2023-03-06 jrmu unveil($virtualspath, "rwc") or die "Unable to unveil $!";
40 8de79cd0 2023-03-06 jrmu unveil($senderspath, "rwc") or die "Unable to unveil $!";
41 f0f29976 2023-03-06 jrmu unveil("$passwdpath.bak", "rwc") or die "Unable to unveil $!";
42 f0f29976 2023-03-06 jrmu unveil("$virtualspath.bak", "rwc") or die "Unable to unveil $!";
43 f0f29976 2023-03-06 jrmu unveil("$senderspath.bak", "rwc") or die "Unable to unveil $!";
44 84c190b6 2021-12-17 jrmu unveil("/usr/lib/libutil.so.13.1", "r") or die "Unable to unveil $!";
45 84c190b6 2021-12-17 jrmu unveil("/bin/sh", "rx") or die "Unable to unveil $!";
46 84c190b6 2021-12-17 jrmu }
47 3a155d60 2023-03-05 jrmu
48 84c190b6 2021-12-17 jrmu sub mmail {
49 3a155d60 2023-03-05 jrmu my ($bot, $nick, $host, $hand, @args) = @_;
50 3a155d60 2023-03-05 jrmu my ($chan, $text);
51 3a155d60 2023-03-05 jrmu if (@args == 2) {
52 3a155d60 2023-03-05 jrmu ($chan, $text) = ($args[0], $args[1]);
53 3a155d60 2023-03-05 jrmu } else { $text = $args[0]; }
54 3a155d60 2023-03-05 jrmu my $hostmask = "$nick!$host";
55 3a155d60 2023-03-05 jrmu if (defined($chan) && $chans =~ /$chan/) {
56 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $chan :$nick: Please check private message");
57 3a155d60 2023-03-05 jrmu }
58 3a155d60 2023-03-05 jrmu if ($text =~ /^$/) {
59 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Type !help for new instructions");
60 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
61 12a821c6 2023-03-06 jrmu main::putservlocalnet($bot, "PRIVMSG $chan :$staff: Help *$nick* on network ".$bot->{name}." with mail");
62 84c190b6 2021-12-17 jrmu }
63 3a155d60 2023-03-05 jrmu return;
64 3a155d60 2023-03-05 jrmu } elsif (main::isstaff($bot, $nick) && $text =~ /^delete\s+([[:ascii:]]+)/) {
65 3a155d60 2023-03-05 jrmu my $username = $1;
66 3a155d60 2023-03-05 jrmu if (SQLite::deleterows("mail", "username", $username)) {
67 3a155d60 2023-03-05 jrmu deletemail($username);
68 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
69 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $chan :$username email deleted");
70 3a155d60 2023-03-05 jrmu }
71 3a155d60 2023-03-05 jrmu }
72 3a155d60 2023-03-05 jrmu return;
73 3a155d60 2023-03-05 jrmu } elsif (main::isstaff($bot, $nick) && $text =~ /^approve\s+([[:ascii:]]+)/) {
74 3a155d60 2023-03-05 jrmu my $username = $1;
75 8de79cd0 2023-03-06 jrmu my @passwd = main::readarray($passwdpath);
76 8de79cd0 2023-03-06 jrmu foreach my $line (@passwd) {
77 f8e21ca0 2023-03-07 jrmu $line =~ s/^#(${username}\@${mailhostname}.*)/$1/;
78 8de79cd0 2023-03-06 jrmu }
79 8de79cd0 2023-03-06 jrmu # trailing newline necessary
80 77bde4fd 2023-03-06 jrmu `doas touch $passwdpath.bak`;
81 77bde4fd 2023-03-06 jrmu `doas chmod g+w $passwdpath.bak`;
82 a03f914c 2023-03-06 jrmu main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
83 a03f914c 2023-03-06 jrmu copy "${passwdpath}.bak", $passwdpath;
84 8de79cd0 2023-03-06 jrmu
85 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
86 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $chan :$username mail approved");
87 3a155d60 2023-03-05 jrmu }
88 3a155d60 2023-03-05 jrmu return;
89 84c190b6 2021-12-17 jrmu }
90 3a155d60 2023-03-05 jrmu ### Check duplicate hostmasks ###
91 3a155d60 2023-03-05 jrmu my @rows = SQLite::selectrows("irc", "hostmask", $hostmask);
92 3a155d60 2023-03-05 jrmu foreach my $row (@rows) {
93 3a155d60 2023-03-05 jrmu my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
94 3a155d60 2023-03-05 jrmu if (defined($password)) {
95 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
96 3a155d60 2023-03-05 jrmu return;
97 3a155d60 2023-03-05 jrmu }
98 3a155d60 2023-03-05 jrmu }
99 3a155d60 2023-03-05 jrmu
100 3a155d60 2023-03-05 jrmu if ($text =~ /^captcha\s+([[:alnum:]]+)/) {
101 3a155d60 2023-03-05 jrmu my $text = $1;
102 3a155d60 2023-03-05 jrmu # TODO avoid using host mask because cloaking can cause problems
103 3a155d60 2023-03-05 jrmu my $ircid = SQLite::id("irc", "nick", $nick, $expires);
104 3a155d60 2023-03-05 jrmu my $captcha = SQLite::get("mail", "ircid", $ircid, "captcha");
105 3a155d60 2023-03-05 jrmu if ($text ne $captcha) {
106 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Wrong captcha. To get a new captcha, type !mail <username> <email>");
107 3a155d60 2023-03-05 jrmu return;
108 3a155d60 2023-03-05 jrmu }
109 3a155d60 2023-03-05 jrmu my $pass = Hash::newpass();
110 3a155d60 2023-03-05 jrmu chomp(my $encrypted = `encrypt $pass`);
111 3a155d60 2023-03-05 jrmu my $username = SQLite::get("mail", "ircid", $ircid, "username");
112 3a155d60 2023-03-05 jrmu my $email = SQLite::get("mail", "ircid", $ircid, "email");
113 3a155d60 2023-03-05 jrmu my $hashirc = SQLite::get("irc", "id", $ircid, "hashid");
114 3a155d60 2023-03-05 jrmu SQLite::set("mail", "ircid", $ircid, "password", $encrypted);
115 3a155d60 2023-03-05 jrmu sleep(2);
116 3a155d60 2023-03-05 jrmu createmail($pass, $username);
117 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Check your email!");
118 02d84e0f 2023-03-06 jrmu sleep(5);
119 3a155d60 2023-03-05 jrmu mailmail($username, $pass, $email);
120 06fd50e5 2023-03-07 jrmu if ($approval) {
121 8de79cd0 2023-03-06 jrmu my @passwd = main::readarray($passwdpath);
122 8de79cd0 2023-03-06 jrmu foreach my $line (@passwd) {
123 f8e21ca0 2023-03-07 jrmu $line =~ s/^(${username}\@${mailhostname}.*)/#$1/;
124 8de79cd0 2023-03-06 jrmu }
125 8de79cd0 2023-03-06 jrmu # trailing newline necessary
126 77bde4fd 2023-03-06 jrmu `doas touch $passwdpath.bak`;
127 77bde4fd 2023-03-06 jrmu `doas chmod g+w $passwdpath.bak`;
128 a03f914c 2023-03-06 jrmu main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
129 a03f914c 2023-03-06 jrmu copy "${passwdpath}.bak", $passwdpath;
130 8de79cd0 2023-03-06 jrmu
131 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Your account has been created but must be manually approved by your admins ($staff) before it can be used.");
132 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
133 3a155d60 2023-03-05 jrmu main::putservlocalnet($bot, "PRIVMSG $chan :$staff: $nick\'s account $username must be manually unblocked before it can be used.");
134 3a155d60 2023-03-05 jrmu }
135 3a155d60 2023-03-05 jrmu }
136 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
137 f8e21ca0 2023-03-07 jrmu main::putservlocalnet($bot, "PRIVMSG $chan :$staff: $nick\'s mail registration of $username\@$mailhostname on $bot->{name} was successful, but you *must* help him to connect. Most users are unable to connect. Show him https://wiki.ircnow.org/?n=Email.Email");
138 3a155d60 2023-03-05 jrmu }
139 3a155d60 2023-03-05 jrmu #www($newnick, $reply, $password, "bouncer");
140 3a155d60 2023-03-05 jrmu return;
141 3a155d60 2023-03-05 jrmu } elsif ($text =~ /^([[:alnum:]]+)\s+([[:ascii:]]+)/) {
142 3a155d60 2023-03-05 jrmu my ($username, $email) = ($1, $2);
143 3a155d60 2023-03-05 jrmu my @userrows = SQLite::selectrows("mail", "username", $username);
144 3a155d60 2023-03-05 jrmu foreach my $row (@userrows) {
145 3a155d60 2023-03-05 jrmu my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
146 3a155d60 2023-03-05 jrmu if (defined($password)) {
147 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
148 3a155d60 2023-03-05 jrmu return;
149 3a155d60 2023-03-05 jrmu }
150 3a155d60 2023-03-05 jrmu }
151 3a155d60 2023-03-05 jrmu my @emailrows = SQLite::selectrows("mail", "email", $email);
152 3a155d60 2023-03-05 jrmu foreach my $row (@userrows) {
153 3a155d60 2023-03-05 jrmu my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
154 3a155d60 2023-03-05 jrmu if (defined($password)) {
155 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
156 3a155d60 2023-03-05 jrmu return;
157 3a155d60 2023-03-05 jrmu }
158 3a155d60 2023-03-05 jrmu }
159 3a155d60 2023-03-05 jrmu
160 3a155d60 2023-03-05 jrmu # my @users = treeget($znctree, "User", "Node");
161 3a155d60 2023-03-05 jrmu foreach my $user (@users) {
162 3a155d60 2023-03-05 jrmu if ($user eq $username) {
163 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Sorry, username taken. Please contact staff if you need help.");
164 3a155d60 2023-03-05 jrmu return;
165 3a155d60 2023-03-05 jrmu }
166 3a155d60 2023-03-05 jrmu }
167 3a155d60 2023-03-05 jrmu
168 3a155d60 2023-03-05 jrmu #my $captcha = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..4;
169 3a155d60 2023-03-05 jrmu my $captcha = int(rand(999));
170 3a155d60 2023-03-05 jrmu my $ircid = int(rand(9223372036854775807));
171 3a155d60 2023-03-05 jrmu my $hashid = sha256_hex("$ircid");
172 3a155d60 2023-03-05 jrmu SQLite::set("irc", "id", $ircid, "localtime", time());
173 3a155d60 2023-03-05 jrmu SQLite::set("irc", "id", $ircid, "hashid", sha256_hex($ircid));
174 3a155d60 2023-03-05 jrmu SQLite::set("irc", "id", $ircid, "date", main::date());
175 3a155d60 2023-03-05 jrmu SQLite::set("irc", "id", $ircid, "hostmask", $hostmask);
176 3a155d60 2023-03-05 jrmu SQLite::set("irc", "id", $ircid, "nick", $nick);
177 3a155d60 2023-03-05 jrmu SQLite::set("mail", "ircid", $ircid, "username", $username);
178 3a155d60 2023-03-05 jrmu SQLite::set("mail", "ircid", $ircid, "email", $email);
179 3a155d60 2023-03-05 jrmu SQLite::set("mail", "ircid", $ircid, "captcha", $captcha);
180 3a155d60 2023-03-05 jrmu SQLite::set("mail", "ircid", $ircid, "hashid", $hashid);
181 bcee9bd5 2023-08-01 jrmu main::whois($bot, $nick);
182 bcee9bd5 2023-08-01 jrmu main::ctcp($bot, $nick);
183 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :".`figlet $captcha`);
184 3a155d60 2023-03-05 jrmu #main::putserv($bot, "PRIVMSG $nick :https://$hostname/$hashid/captcha.png");
185 3a155d60 2023-03-05 jrmu #main::putserv($bot, "PRIVMSG $nick :https://$hostname/register.php?hashirc=$hashid");
186 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Type !mail captcha <text>");
187 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
188 3a155d60 2023-03-05 jrmu main::putservlocalnet($bot, "PRIVMSG $chan :$nick\'s on $bot->{name} mail captcha is $captcha");
189 3a155d60 2023-03-05 jrmu }
190 3a155d60 2023-03-05 jrmu } else {
191 3a155d60 2023-03-05 jrmu main::putserv($bot, "PRIVMSG $nick :Invalid username or email. Type !mail <username> <email> to try again.");
192 3a155d60 2023-03-05 jrmu foreach my $chan (@teamchans) {
193 12a821c6 2023-03-06 jrmu main::putservlocalnet($bot, "PRIVMSG $chan :$staff: Help *$nick* on network ".$bot->{name}." with mail");
194 3a155d60 2023-03-05 jrmu }
195 3a155d60 2023-03-05 jrmu }
196 84c190b6 2021-12-17 jrmu }
197 84c190b6 2021-12-17 jrmu
198 3a155d60 2023-03-05 jrmu sub mailmail {
199 3a155d60 2023-03-05 jrmu my( $username, $password, $email )=@_;
200 176d5d5d 2023-03-06 jrmu my $approvemsg;
201 176d5d5d 2023-03-06 jrmu if ($approval eq "true") {
202 176d5d5d 2023-03-06 jrmu $approvemsg = <<"EOF";
203 176d5d5d 2023-03-06 jrmu
204 176d5d5d 2023-03-06 jrmu *IMPORTANT*: Your account has been created but it has not yet been
205 8de79cd0 2023-03-06 jrmu approved. To get your account approved, please contact your admins
206 176d5d5d 2023-03-06 jrmu $staff on IRC and by email.
207 176d5d5d 2023-03-06 jrmu
208 176d5d5d 2023-03-06 jrmu EOF
209 176d5d5d 2023-03-06 jrmu }
210 3a155d60 2023-03-05 jrmu my $body = <<"EOF";
211 3a155d60 2023-03-05 jrmu Welcome to IRCNow!
212 84c190b6 2021-12-17 jrmu
213 3a155d60 2023-03-05 jrmu You created an email account:
214 84c190b6 2021-12-17 jrmu
215 f8e21ca0 2023-03-07 jrmu Username: $username\@$mailhostname
216 3a155d60 2023-03-05 jrmu Password: $password
217 f8e21ca0 2023-03-07 jrmu Server: $mailhostname
218 3a155d60 2023-03-05 jrmu IMAP Port: $imapport (STARTTLS)
219 3a155d60 2023-03-05 jrmu SMTP Port: $smtpport (STARTTLS)
220 cae514a3 2023-03-07 jrmu Webpanel: $webmail
221 3a155d60 2023-03-05 jrmu $approvemsg
222 3a155d60 2023-03-05 jrmu *IMPORTANT*: Verify your email address:
223 3a155d60 2023-03-05 jrmu
224 3a155d60 2023-03-05 jrmu Please reply to this email to indicate you have received the email. You must
225 3a155d60 2023-03-05 jrmu reply in order to keep your account.
226 3a155d60 2023-03-05 jrmu
227 3a155d60 2023-03-05 jrmu Connection Instructions: https://wiki.ircnow.org/?n=Email.Email
228 3a155d60 2023-03-05 jrmu
229 3a155d60 2023-03-05 jrmu IRCNow
230 3a155d60 2023-03-05 jrmu EOF
231 4a735b99 2023-03-07 jrmu main::mail($mailfrom, $email, $mailname, "Verify IRCNow Account", $body);
232 3a155d60 2023-03-05 jrmu }
233 3a155d60 2023-03-05 jrmu
234 3a155d60 2023-03-05 jrmu
235 3a155d60 2023-03-05 jrmu sub createmail {
236 3a155d60 2023-03-05 jrmu my ($password, $username) = @_;
237 3a155d60 2023-03-05 jrmu chomp(my $encrypted = `encrypt $password`);
238 f8e21ca0 2023-03-07 jrmu my $line = "${username}\@$mailhostname:${encrypted}::::::userdb_quota_rule=*:storage=1G";
239 3a155d60 2023-03-05 jrmu $line =~ s{\$}{\\\$}g;
240 f8e21ca0 2023-03-07 jrmu my $line2 = "${username}\@$mailhostname vmail";
241 f8e21ca0 2023-03-07 jrmu my $line3 = "${username}\@$mailhostname: ${username}\@$mailhostname";
242 176d5d5d 2023-03-06 jrmu `doas sh -c 'echo $line >> $passwdpath'`;
243 176d5d5d 2023-03-06 jrmu `doas sh -c 'echo $line2 >> $virtualspath'`;
244 176d5d5d 2023-03-06 jrmu `doas sh -c 'echo $line3 >> $senderspath'`;
245 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table passwd`;
246 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table virtuals`;
247 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table users`;
248 3a155d60 2023-03-05 jrmu `doas rcctl reload dovecot`;
249 3a155d60 2023-03-05 jrmu }
250 3a155d60 2023-03-05 jrmu
251 3a155d60 2023-03-05 jrmu sub deletemail {
252 3a155d60 2023-03-05 jrmu my ($username) = @_;
253 8de79cd0 2023-03-06 jrmu my @passwd = main::readarray($passwdpath);
254 8de79cd0 2023-03-06 jrmu my @virtuals = main::readarray($virtualspath);
255 8de79cd0 2023-03-06 jrmu my @senders = main::readarray($senderspath);
256 f8e21ca0 2023-03-07 jrmu @passwd = grep !/^${username}\@${mailhostname}/, @passwd;
257 f8e21ca0 2023-03-07 jrmu @virtuals = grep !/^${username}\@${mailhostname}/, @virtuals;
258 f8e21ca0 2023-03-07 jrmu @senders = grep !/^${username}\@${mailhostname}/, @senders;
259 3a155d60 2023-03-05 jrmu
260 3a155d60 2023-03-05 jrmu # trailing newline necessary
261 77bde4fd 2023-03-06 jrmu `doas touch $passwdpath.bak`;
262 77bde4fd 2023-03-06 jrmu `doas touch $virtualspath.bak`;
263 77bde4fd 2023-03-06 jrmu `doas touch $senderspath.bak`;
264 77bde4fd 2023-03-06 jrmu `doas chmod g+w $passwdpath.bak $virtualspath.bak $senderspath.bak`;
265 a03f914c 2023-03-06 jrmu main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
266 a03f914c 2023-03-06 jrmu copy "${passwdpath}.bak", $passwdpath;
267 9f92fa89 2023-03-06 jrmu main::writefile("$virtualspath.bak", join("\n", @virtuals)."\n");
268 a03f914c 2023-03-06 jrmu copy "${virtualspath}.bak", $virtualspath;
269 9f92fa89 2023-03-06 jrmu main::writefile("$senderspath.bak", join("\n", @senders)."\n");
270 a03f914c 2023-03-06 jrmu copy "${senderspath}.bak", $senderspath;
271 3a155d60 2023-03-05 jrmu
272 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table passwd`;
273 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table virtuals`;
274 1f1dc31d 2023-03-07 jrmu `doas smtpctl update table users`;
275 3a155d60 2023-03-05 jrmu `doas rcctl reload dovecot`;
276 3a155d60 2023-03-05 jrmu }
277 84c190b6 2021-12-17 jrmu 1; # MUST BE LAST STATEMENT IN FILE