Annotation of portolis/portolis.cgi, revision 1.2
1.1 snw 1: #!/usr/pkg/bin/perl
2:
3: #
4: # Portolis Mail Management Portal
5: # Copyright (C) 2025 Coherent Logic Development LLC
6: #
1.2 ! snw 7: # $Id: portolis.cgi,v 1.1.1.1 2025/02/15 23:09:22 snw Exp $
1.1 snw 8: # Author: Serena Willis <snw@coherent-logic.com>
9: #
10: # Licensed AGPL-3.0
11: #
1.2 ! snw 12: # $Log: portolis.cgi,v $
! 13: # Revision 1.1.1.1 2025/02/15 23:09:22 snw
! 14: # Initial commit
! 15: #
1.1 snw 16: #
17:
18: use CGI qw(:standard);
19: use CGI::Session;
20:
21: $portolis_version = '0.0.4';
22: $session = CGI::Session->new();
23:
24: sub init
25: {
26: $cookie = cookie(CGISESSID => $session->id );
27: print header(-cookie=>$cookie);
28: }
29:
30: sub find_alias {
31: my ($target, $alias) = @_;
32:
33: open(FH, '<', '/etc/postfix/virtual_aliases');
34:
35: while(<FH>) {
36: my @entry = split(' ', $_);
37: my $falias = $entry[0];
38: my $ftarget = $entry[1];
39:
40: if(($ftarget eq $target) && ($falias eq $alias)) {
41: close FH;
42: return 1;
43: }
44: }
45:
46: close FH;
47: return 0;
48: }
49:
50: sub add_alias {
51: my ($target, $alias) = @_;
52:
53: }
54:
55: sub vmail_auth {
56: my ($auser, $apass) = @_;
57:
58: my $line = "";
59: my @pwent = ();
60:
61: open(FH, "<", "/usr/pkg/etc/dovecot/users");
62:
63: while(<FH>){
64: $line = $_;
65: @pwent = split(':', $line);
66: if($pwent[0] eq $auser) {
67:
68: my $result = `/usr/pkg/bin/doveadm pw -t \'$pwent[1]\' -p \'$apass\'`;
69: my @res = split(' ', $result);
70:
71: if($res[1] eq "(verified)") {
72: close FH;
73: return 1;
74: }
75: }
76: }
77:
78: close FH;
79: return 0;
80: }
81:
82: sub render_header {
83: my ($title) = @_;
84:
85: my $navbar = '';
86: my $html = <<"END_HDR";
87: <HTML>
88: <HEAD>
89: <TITLE>$title</TITLE>
90: </HEAD>
1.2 ! snw 91: <BODY BGCOLOR=PaleGoldenrod BACKGROUND=/images/linen2d.jpg>
1.1 snw 92: END_HDR
93:
94: print $html;
95:
96: if($session->param("~logged-in")) {
97: my $email = $session->param("~email");
98: $navbar = <<"END_NAVL";
99: <A HREF=/cgi-bin/portolis.cgi?exec=home>$email</A> | <A HREF=https://webmail.coherent-logic.com>WebMail</A> | <A HREF=/cgi-bin/portolis.cgi?exec=logout>Log out</A>
100: <HR>
101: END_NAVL
102: }
103: else {
104: $navbar = <<"END_NAVO";
105: <A HREF=https://webmail.coherent-logic.com>WebMail</A> | <A HREF=/cgi-bin/portolis.cgi?exec=login>Log In</A>
106: <HR>
107: END_NAVO
108: }
109:
110: print $navbar;
111:
112: }
113:
114: sub render_footer {
115:
116: my $html = <<'END_FTR';
117: <HR>
1.2 ! snw 118: <EM>$Id: portolis.cgi,v 1.1.1.1 2025/02/15 23:09:22 snw Exp $<BR>
1.1 snw 119: Copyright © 2025 Coherent Logic Development LLC</EM>
120: </BODY>
121: </HTML>
122: END_FTR
123:
124: print $html;
125: }
126:
127: sub list_aliases {
128: my $email = $session->param("~email");
129: open(FH, '<', '/etc/postfix/virtual_aliases');
130:
131: print "<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>";
132: print "<TR><TH>Alias</TH><TH>Actions</TH></TR>";
133: while(<FH>) {
134: my $ln = $_;
135: my @line = split(' ', $ln);
136:
137: if($line[1] eq $email) {
138: print "<TR><TD>$line[0]</TD><TD><A HREF=/cgi-bin/portolis.cgi?exec=delalias&id=$line[0]>Delete</A></TD></TR>";
139: }
140: }
141: print "</TABLE>";
142: print "<A HREF=/cgi-bin/portolis.cgi?exec=newalias>New Alias</A>";
143: close FH;
144: }
145:
146: sub exec_home {
147: render_header "Home";
148: my $email = $session->param("~email");
149: my $html = <<"END_HOME";
150: <CENTER>
151: <H1>Account Overview</H1>
152: <P>Your e-mail address is <B>$email</B></P>
153: END_HOME
154:
155: print $html;
156:
157: list_aliases();
158:
159: print "</CENTER>";
160: }
161:
162: sub exec_newalias {
163: my $html = '';
164:
165: if($session->param("~logged-in") == 0) {
166: render_header "Access Denied";
167: $html = <<"END_BADNAF";
168: <H1>Access Denied</H1>
169: <P>You are not logged in.</P>
170: END_BADNAF
171:
172: print $html;
173: return;
174: }
175:
176: my $email = $session->param("~email");
177: render_header "New Alias";
178:
179: if(request_method() eq 'GET') {
180: $html = <<"END_NAF";
181: <CENTER>
182: <H1>Create New Alias</H1>
183: <FORM METHOD=POST ACTION=/cgi-bin/portolis.cgi?exec=newalias>
184: <TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>
185: <TR>
186: <TD><B>Alias:</B></TD>
187: <TD><INPUT TYPE=TEXT NAME=alias></TD>
188: </TR>
189: <TR>
190: <TD><B>Target:</B></TD>
191: <TD>$email</TD>
192: </TR>
193: <TR>
194: <TD COLSPAN=2 ALIGN=RIGHT>
195: <INPUT TYPE=SUBMIT NAME=SUBMIT VALUE=Submit>
196: </TD>
197: </TR>
198: </TABLE>
199: </FORM>
200: </CENTER>
201: END_NAF
202: }
203: else {
204: my $proposed = param("alias");
205: my $result = find_alias($email, $proposed);
206:
207: if($result == 0) {
208: open(FH, '>>', '/etc/postfix/virtual_aliases');
209: flock(FH, 2);
210: print FH "$proposed\t$email\n";
211: flock(FH, 8);
212: close(FH);
213: chdir '/etc/postfix';
214: system '/usr/sbin/postmap virtual_aliases';
215: $html = <<"END_AAOK";
216: <CENTER>
217: <H1>Alias Added</H1>
218: <A HREF=/cgi-bin/portolis.cgi?exec=home>Home</A>
219: </CENTER>
220: END_AAOK
221: }
222: else {
223: $html = <<"END_AADUP";
224: <CENTER>
225: <H1>Duplicate Alias</H1>
226: <P>The alias <B>$proposed</B> already exists</P>
227: <A HREF=/cgi-bin/portolis.cgi?exec=newalias>Try Again</a>
228: </CENTER>
229: END_AADUP
230: }
231: }
232:
233: print $html;
234:
235: }
236:
237: sub exec_delalias {
238: my $alias = url_param('id');
239: my $html = '';
240:
241: if($session->param("~logged-in") == 0) {
242: render_header "Access Denied";
243: $html = <<"END_BADDAF";
244: <H1>Access Denied</H1>
245: <P>You are not logged in.</P>
246: END_BADDAF
247:
248: print $html;
249: return;
250: }
251:
252: my $email = $session->param("~email");
253: render_header "Delete Alias";
254:
255: if(request_method() eq 'GET') {
256: if(find_alias($email, $alias) == 1) {
257: my @lines = ();
258:
259: open(FH, '<', '/etc/postfix/virtual_aliases');
260: flock(FH, 2);
261: while(<FH>) {
262: my $line = $_;
263: my @tmp = split(' ', $line);
264: if($tmp[0] ne $alias) {
265: push @lines, $line;
266: }
267: if(($tmp[0] eq $alias) && ($tmp[1] ne $email)) {
268: push @lines, $line;
269: }
270: }
271: flock(FH, 8);
272: close(FH);
273: open(FH, '>', '/etc/postfix/virtual_aliases');
274: flock(FH, 2);
275: seek(FH, 0, 0);
276: truncate(FH, 0);
277: print FH @lines;
278: flock(FH, 8);
279: close FH;
280: chdir '/etc/postfix';
281: system '/usr/sbin/postmap virtual_aliases';
282: $html = <<"END_DAGOOD";
283: <CENTER>
284: <H1>Alias Deleted</H1>
285: <P><A HREF=/cgi-bin/portolis.cgi?exec=home>Home</A>
286: </CENTER>
287: END_DAGOOD
288: }
289: else {
290: $html = <<"END_DANOF";
291: <CENTER>
292: <H1>Invalid Alias</H1>
293: <P><A HREF=/cgi-bin/portolis.cgi?exec=home>Home</A>
294: </CENTER>
295: END_DANOF
296: }
297: }
298:
299: print $html;
300:
301: }
302:
303: sub exec_login {
304:
305: render_header "Login";
306:
307: my $html = '';
308:
309: if(request_method() eq 'GET') {
310: $html = <<"END_LOGIN";
311: <CENTER>
312: <H1>Log In</H1>
313: <FORM METHOD=POST ACTION=/cgi-bin/portolis.cgi?exec=login>
314: <TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>
315: <tr>
316: <td><b>E-Mail Address:</b></td>
317: <td><input type="text" name="email"></td>
318: </tr>
319: <tr>
320: <td><b>Password:</b></td>
321: <td><input type="password" name="password"></td>
322: </tr>
323: <tr>
324: <td colspan=2 align=right><input type=submit name=submit value=Submit></td>
325: </tr>
326: </TABLE>
327: </FORM>
328: </CENTER>
329: END_LOGIN
330: }
331: elsif(request_method() eq 'POST') {
332: my $email = param("email");
333: my $password = param("password");
334:
335: if(vmail_auth($email, $password) == 1) {
336: $session->param("~logged-in", 1);
337: $session->param("~email", $email);
338: $html = <<'END_LOGINDONE';
339: <script>location.replace('/cgi-bin/portolis.cgi?exec=home');</script>
340:
341: END_LOGINDONE
342: }
343: else {
344: $html = <<'END_ACD';
345: <H1>Access Denied</H1>
346: <P>Invalid e-mail address or password</P>
347: END_ACD
348: }
349: }
350:
351: print $html;
352:
353:
354: }
355:
356: sub exec_logout {
357: $session->clear(['~logged-in', 'email']);
358: my $html = <<'END_LO';
359: <script>location.replace('/cgi-bin/portolis.cgi?exec=login');</script>
360: END_LO
361: print $html;
362: }
363:
364: sub exec_unknown {
365:
366: render_header "Error";
367:
368: my $html = <<'END_UNK';
369: <CENTER>
370: <H1>Invalid Request</H1>
371: <P>The action you're attempting is invalid or has not yet been implemented. Please try something else.</P>
372: </CENTER>
373: END_UNK
374:
375: print $html;
376:
377: }
378:
379: sub main {
380: init;
381:
382: my $exec = url_param('exec');
383:
384: if($exec ne '') {
385: if($exec eq 'login') {
386: $funcref = \&exec_login;
387: }
388: elsif($exec eq 'logout') {
389: $funcref = \&exec_logout;
390: }
391: elsif($exec eq 'home') {
392: $funcref = \&exec_home;
393: }
394: elsif($exec eq 'newalias') {
395: $funcref = \&exec_newalias;
396: }
397: elsif($exec eq 'delalias') {
398: $funcref = \&exec_delalias;
399: }
400: else {
401: $funcref = \&exec_unknown;
402: }
403: }
404: else {
405: $funcref = \&exec_login;
406: }
407:
408: &$funcref;
409:
410: render_footer;
411: }
412:
413: main();
414:
415:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>