9 use File::Copy qw(copy);
11 use Digest::SHA qw(sha256_hex);
13 my %conf = %main::conf;
14 my $chans = $conf{chans};
15 my $staff = $conf{staff};
16 my $mailhostname = $conf{mailhostname};
17 my $mailfrom = $conf{mailfrom};
18 my $mailname = $conf{mailname};
19 my $imapport = $conf{imapport};
20 my $smtpport = $conf{smtpport};
21 my $teamchans = $conf{teamchans};
22 my @teamchans = split /[,\s]+/m, $teamchans;
23 my $webmail = $conf{webmail};
24 my $approval = $conf{approval};
25 my $expires = $conf{expires};
26 my $passwdpath = "/etc/mail/passwd";
27 my $virtualspath = "/etc/mail/virtuals";
28 my $senderspath = "/etc/mail/users";
31 main::cbind("msg", "-", "mail", \&mmail);
34 #dependencies for encrypt
35 unveil("/usr/bin/encrypt", "rx") or die "Unable to unveil $!";
36 #dependencies for mail
37 unveil("/usr/sbin/sendmail", "rx") or die "Unable to unveil $!";
38 unveil($passwdpath, "rwc") or die "Unable to unveil $!";
39 unveil($virtualspath, "rwc") or die "Unable to unveil $!";
40 unveil($senderspath, "rwc") or die "Unable to unveil $!";
41 unveil("$passwdpath.bak", "rwc") or die "Unable to unveil $!";
42 unveil("$virtualspath.bak", "rwc") or die "Unable to unveil $!";
43 unveil("$senderspath.bak", "rwc") or die "Unable to unveil $!";
44 unveil("/usr/lib/libutil.so.13.1", "r") or die "Unable to unveil $!";
45 unveil("/bin/sh", "rx") or die "Unable to unveil $!";
49 my ($bot, $nick, $host, $hand, @args) = @_;
52 ($chan, $text) = ($args[0], $args[1]);
53 } else { $text = $args[0]; }
54 my $hostmask = "$nick!$host";
55 if (defined($chan) && $chans =~ /$chan/) {
56 main::putserv($bot, "PRIVMSG $chan :$nick: Please check private message");
59 main::putserv($bot, "PRIVMSG $nick :Type !help for new instructions");
60 foreach my $chan (@teamchans) {
61 main::putservlocalnet($bot, "PRIVMSG $chan :$staff: Help *$nick* on network ".$bot->{name}." with mail");
64 } elsif (main::isstaff($bot, $nick) && $text =~ /^delete\s+([[:ascii:]]+)/) {
66 if (SQLite::deleterows("mail", "username", $username)) {
67 deletemail($username);
68 foreach my $chan (@teamchans) {
69 main::putserv($bot, "PRIVMSG $chan :$username email deleted");
73 } elsif (main::isstaff($bot, $nick) && $text =~ /^approve\s+([[:ascii:]]+)/) {
75 my @passwd = main::readarray($passwdpath);
76 foreach my $line (@passwd) {
77 $line =~ s/^#(${username}\@${mailhostname}.*)/$1/;
79 # trailing newline necessary
80 `doas touch $passwdpath.bak`;
81 `doas chmod g+w $passwdpath.bak`;
82 main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
83 copy "${passwdpath}.bak", $passwdpath;
85 foreach my $chan (@teamchans) {
86 main::putserv($bot, "PRIVMSG $chan :$username mail approved");
90 ### Check duplicate hostmasks ###
91 my @rows = SQLite::selectrows("irc", "hostmask", $hostmask);
92 foreach my $row (@rows) {
93 my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
94 if (defined($password)) {
95 main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
100 if ($text =~ /^captcha\s+([[:alnum:]]+)/) {
102 # TODO avoid using host mask because cloaking can cause problems
103 my $ircid = SQLite::id("irc", "nick", $nick, $expires);
104 my $captcha = SQLite::get("mail", "ircid", $ircid, "captcha");
105 if ($text ne $captcha) {
106 main::putserv($bot, "PRIVMSG $nick :Wrong captcha. To get a new captcha, type !mail <username> <email>");
109 my $pass = Hash::newpass();
110 chomp(my $encrypted = `encrypt $pass`);
111 my $username = SQLite::get("mail", "ircid", $ircid, "username");
112 my $email = SQLite::get("mail", "ircid", $ircid, "email");
113 my $hashirc = SQLite::get("irc", "id", $ircid, "hashid");
114 SQLite::set("mail", "ircid", $ircid, "password", $encrypted);
116 createmail($pass, $username);
117 main::putserv($bot, "PRIVMSG $nick :Check your email!");
119 mailmail($username, $pass, $email);
121 my @passwd = main::readarray($passwdpath);
122 foreach my $line (@passwd) {
123 $line =~ s/^(${username}\@${mailhostname}.*)/#$1/;
125 # trailing newline necessary
126 `doas touch $passwdpath.bak`;
127 `doas chmod g+w $passwdpath.bak`;
128 main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
129 copy "${passwdpath}.bak", $passwdpath;
131 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 foreach my $chan (@teamchans) {
133 main::putservlocalnet($bot, "PRIVMSG $chan :$staff: $nick\'s account $username must be manually unblocked before it can be used.");
136 foreach my $chan (@teamchans) {
137 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");
139 #www($newnick, $reply, $password, "bouncer");
141 } elsif ($text =~ /^([[:alnum:]]+)\s+([[:ascii:]]+)/) {
142 my ($username, $email) = ($1, $2);
143 my @userrows = SQLite::selectrows("mail", "username", $username);
144 foreach my $row (@userrows) {
145 my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
146 if (defined($password)) {
147 main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
151 my @emailrows = SQLite::selectrows("mail", "email", $email);
152 foreach my $row (@userrows) {
153 my $password = SQLite::get("mail", "ircid", $row->{id}, "password");
154 if (defined($password)) {
155 main::putserv($bot, "PRIVMSG $nick :Sorry, only one account per person. Please contact staff if you need help.");
160 # my @users = treeget($znctree, "User", "Node");
161 foreach my $user (@users) {
162 if ($user eq $username) {
163 main::putserv($bot, "PRIVMSG $nick :Sorry, username taken. Please contact staff if you need help.");
168 #my $captcha = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..4;
169 my $captcha = int(rand(999));
170 my $ircid = int(rand(9223372036854775807));
171 my $hashid = sha256_hex("$ircid");
172 SQLite::set("irc", "id", $ircid, "localtime", time());
173 SQLite::set("irc", "id", $ircid, "hashid", sha256_hex($ircid));
174 SQLite::set("irc", "id", $ircid, "date", main::date());
175 SQLite::set("irc", "id", $ircid, "hostmask", $hostmask);
176 SQLite::set("irc", "id", $ircid, "nick", $nick);
177 SQLite::set("mail", "ircid", $ircid, "username", $username);
178 SQLite::set("mail", "ircid", $ircid, "email", $email);
179 SQLite::set("mail", "ircid", $ircid, "captcha", $captcha);
180 SQLite::set("mail", "ircid", $ircid, "hashid", $hashid);
181 main::whois($bot, $nick);
182 main::ctcp($bot, $nick);
183 main::putserv($bot, "PRIVMSG $nick :".`figlet $captcha`);
184 #main::putserv($bot, "PRIVMSG $nick :https://$hostname/$hashid/captcha.png");
185 #main::putserv($bot, "PRIVMSG $nick :https://$hostname/register.php?hashirc=$hashid");
186 main::putserv($bot, "PRIVMSG $nick :Type !mail captcha <text>");
187 foreach my $chan (@teamchans) {
188 main::putservlocalnet($bot, "PRIVMSG $chan :$nick\'s on $bot->{name} mail captcha is $captcha");
191 main::putserv($bot, "PRIVMSG $nick :Invalid username or email. Type !mail <username> <email> to try again.");
192 foreach my $chan (@teamchans) {
193 main::putservlocalnet($bot, "PRIVMSG $chan :$staff: Help *$nick* on network ".$bot->{name}." with mail");
199 my( $username, $password, $email )=@_;
201 if ($approval eq "true") {
202 $approvemsg = <<"EOF";
204 *IMPORTANT*: Your account has been created but it has not yet been
205 approved. To get your account approved, please contact your admins
206 $staff on IRC and by email.
213 You created an email account:
215 Username: $username\@$mailhostname
217 Server: $mailhostname
218 IMAP Port: $imapport (STARTTLS)
219 SMTP Port: $smtpport (STARTTLS)
222 *IMPORTANT*: Verify your email address:
224 Please reply to this email to indicate you have received the email. You must
225 reply in order to keep your account.
227 Connection Instructions: https://wiki.ircnow.org/?n=Email.Email
231 main::mail($mailfrom, $email, $mailname, "Verify IRCNow Account", $body);
236 my ($password, $username) = @_;
237 chomp(my $encrypted = `encrypt $password`);
238 my $line = "${username}\@$mailhostname:${encrypted}::::::userdb_quota_rule=*:storage=1G";
239 $line =~ s{\$}{\\\$}g;
240 my $line2 = "${username}\@$mailhostname vmail";
241 my $line3 = "${username}\@$mailhostname: ${username}\@$mailhostname";
242 `doas sh -c 'echo $line >> $passwdpath'`;
243 `doas sh -c 'echo $line2 >> $virtualspath'`;
244 `doas sh -c 'echo $line3 >> $senderspath'`;
245 `doas smtpctl update table passwd`;
246 `doas smtpctl update table virtuals`;
247 `doas smtpctl update table users`;
248 `doas rcctl reload dovecot`;
253 my @passwd = main::readarray($passwdpath);
254 my @virtuals = main::readarray($virtualspath);
255 my @senders = main::readarray($senderspath);
256 @passwd = grep !/^${username}\@${mailhostname}/, @passwd;
257 @virtuals = grep !/^${username}\@${mailhostname}/, @virtuals;
258 @senders = grep !/^${username}\@${mailhostname}/, @senders;
260 # trailing newline necessary
261 `doas touch $passwdpath.bak`;
262 `doas touch $virtualspath.bak`;
263 `doas touch $senderspath.bak`;
264 `doas chmod g+w $passwdpath.bak $virtualspath.bak $senderspath.bak`;
265 main::writefile("$passwdpath.bak", join("\n", @passwd)."\n");
266 copy "${passwdpath}.bak", $passwdpath;
267 main::writefile("$virtualspath.bak", join("\n", @virtuals)."\n");
268 copy "${virtualspath}.bak", $virtualspath;
269 main::writefile("$senderspath.bak", join("\n", @senders)."\n");
270 copy "${senderspath}.bak", $senderspath;
272 `doas smtpctl update table passwd`;
273 `doas smtpctl update table virtuals`;
274 `doas smtpctl update table users`;
275 `doas rcctl reload dovecot`;
277 1; # MUST BE LAST STATEMENT IN FILE