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 &copy; 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>