File:  [Coherent Logic Development] / ChivanetModcon / modcon
Revision 1.8: download - view: text, annotated - select for diffs
Mon Mar 10 15:34:45 2025 UTC (4 months, 3 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Undo the change

    1: #!/usr/bin/env perl
    2: 
    3: #
    4: # ChivaNet Moderator Console
    5: #  Copyright (C) 2025 Coherent Logic Development LLC
    6: #
    7: # $Id: modcon,v 1.8 2025/03/10 15:34:45 snw Exp $
    8: #
    9: # Author: Serena Willis <snw@coherent-logic.com>
   10: #
   11: # Licensed AGPL-3.0
   12: #
   13: # $Log: modcon,v $
   14: # Revision 1.8  2025/03/10 15:34:45  snw
   15: # Undo the change
   16: #
   17: # Revision 1.7  2025/03/10 15:31:54  snw
   18: # Test
   19: #
   20: # Revision 1.6  2025/02/04 18:55:12  snw
   21: # Updates
   22: #
   23: # Revision 1.5  2025/02/01 03:17:46  snw
   24: # Fix session list
   25: #
   26: # Revision 1.4  2025/01/31 19:41:00  snw
   27: # Move to a UNIX UI paradigm
   28: #
   29: # Revision 1.3  2025/01/31 15:39:06  snw
   30: # Minor fixes
   31: #
   32: # Revision 1.2  2025/01/31 13:38:51  snw
   33: # Initial basic functions working
   34: #
   35: # Revision 1.1.1.1  2025/01/30 19:16:06  snw
   36: # Initial commit
   37: #
   38: #
   39: 
   40: use REST::Client;
   41: use JSON;
   42: use Data::Dumper;
   43: use Term::ReadKey;
   44: use Getopt::Long;
   45: use Text::Glob qw(match_glob);
   46: 
   47: my $sso_account = '';
   48: my $apikey = '';
   49: my $online = 0;
   50: my $modcon_version = '0.0.1';
   51: my $cnclient = '';
   52: my $wchar = '';
   53: my $hchar = '';
   54: my $wpixels = '';
   55: my $hpixels = '';
   56: my $account = '';
   57: my $mode = '---';
   58: my $user = '---';
   59: $site = '';
   60: 
   61: sub list_directories {
   62:     return ('sso', 'ras');
   63: }
   64: 
   65: sub list_sso_users {
   66:     $cnclient->GET("/chivanet/users");       
   67:     my $json = $cnclient->responseContent();
   68:     my $hashref = decode_json($json);
   69: 
   70:     if($hashref->{ok} == 1) {
   71:         my $arrayref = $hashref->{users};
   72:         my @users = @{$arrayref};
   73: 
   74:         return @users;
   75:     }
   76:     else {
   77:         print "RPC error\n";
   78:         return ();
   79:     }
   80: }
   81: 
   82: 
   83: sub select_sso_user {
   84:     my($id) = @_;
   85: 
   86:     $cnclient->GET("/chivanet/validate_user?id=$id");
   87:     my $json = $cnclient->responseContent();
   88:     my $hashref = decode_json($json);
   89:     
   90:     if($hashref->{exists} == 1) {
   91: 
   92:         $cnclient->GET("/chivanet/user?id=$id");
   93:         my $json = $cnclient->responseContent();
   94:         $account = decode_json($json);
   95: 
   96:         return $id;
   97:     }
   98:     else {
   99:         return "---";
  100:     }            
  101: }
  102: 
  103: sub select_ras_user {
  104:     my($id) = @_;
  105: 
  106:     $cnclient->GET("/chivanet/validate_sn?id=$id");
  107:     my $json = $cnclient->responseContent();
  108:     my $hashref = decode_json($json);
  109: 
  110:     if($hashref->{exists} == 1) {
  111:         return $id;
  112:     }
  113:     else {
  114:         return "---";
  115:     }
  116: }
  117: 
  118: sub trace_ras_sn {
  119:     my($user) = @_;
  120:     
  121:     $cnclient->GET("/chivanet/trace_sn?id=$user");
  122:     my $json = $cnclient->responseContent();
  123:     my $hashref = decode_json($json);
  124:     my $record = $hashref->{account};
  125:     my $result = $record->{id};
  126:     
  127:     print "RAS screen name $user belongs to SSO account $result; switching to SSO mode\n";
  128:     return $result;   
  129: }
  130: 
  131: sub list_ras_sessions {
  132:     $cnclient->GET("/chivanet/ras_sessions");
  133:     my $json = $cnclient->responseContent();
  134:     my $hashref = decode_json($json);
  135:     my $sessions = $hashref->{sessions};
  136:     my $arrayref = $sessions->{sessions};
  137:     my @result = ();
  138:     
  139:     foreach my $session (@{$arrayref}) {
  140:         push(@result, $session->{id});
  141:     }
  142:     
  143:     return @result;
  144: }
  145: 
  146: sub list_ras_screennames {
  147:     my($id) = @_;
  148: 
  149:     my @result = ();
  150:     $cnclient->GET("/chivanet/user_ras_screen_names?id=$id");
  151:     my $json = $cnclient->responseContent();
  152:     my $hashref = decode_json($json);
  153:     my $arrayref = $hashref->{screen_names};
  154: 
  155:     foreach my $item (@{$arrayref}) {
  156:         push(@result, $item->{screen_name});
  157:     }
  158: 
  159:     
  160:     return @result;   
  161: }
  162: 
  163: sub list_ras_users {
  164: 
  165:     my @result = ();
  166:     
  167:     $cnclient->GET("/chivanet/all_ras_screen_names");
  168:     my $json = $cnclient->responseContent();
  169:     my $hashref = decode_json($json);
  170: 
  171:     if($hashref->{ok} == 0) {
  172:         print "RPC error\n";
  173:         return @result;
  174:     }
  175:     
  176:     my $arrayref = $hashref->{screen_names};
  177: 
  178:     foreach my $entryref (@{$arrayref}) {
  179:         push(@result, $entryref->{id});
  180:     }
  181: 
  182:     return @result;    
  183: }
  184: 
  185: sub print_sso_user {
  186:     my $act = $account->{account};
  187: 
  188:     print "\n";
  189:     print "Username          :  $act->{id}\n";
  190:     print "Real Name         :  $act->{last_name}, $act->{first_name}\n";
  191:     print "Display Name      :  $act->{display_name}\n";
  192:     print "Pronouns          :  $act->{pronouns}\n";
  193:     print "Profile Image     :  https://chivanet.org$act->{profile_photo}\n";        
  194:     print "E-Mail Address    :  $act->{email}\n";
  195:     print "Permission Level  :  $act->{perm_level}\n";
  196:     print "Created           :  $act->{create_ts}\n";
  197:     print "Banned            :  $act->{mod_banned}\n\n";
  198: }
  199: 
  200: sub print_ras_user {
  201:     my($id) = @_;
  202: 
  203:     $cnclient->GET("/chivanet/sn_status?id=$id");
  204:     my $json = $cnclient->responseContent();
  205:     my $hashref = decode_json($json);
  206:     my $status = $hashref->{status};
  207:     my $online = "offline";
  208:     
  209:     if($status->{online} == 1) {        
  210:         $online = "online";        
  211:     }
  212:     
  213:     print "\n";
  214: 
  215: 
  216:     if($online eq "online") {
  217:         my $sess = $status->{session};
  218:         my $service = 'AIM';
  219:         
  220:         if($sess->{is_icq} == 1) {
  221:             $service = 'ICQ';
  222:         }
  223: 
  224:         if($sess->{idle_seconds} > 0) {
  225:             $online = "idle";
  226:         }
  227:         
  228:         print "Screen Name       :  $id [$online]\n";
  229:         print "Service           :  $service\n";
  230:         if($online eq "online") {
  231:             print "Time Online (secs):  $sess->{online_seconds}\n";
  232:         }
  233:         else {
  234:             print "Time Idle (secs)  :  $sess->{idle_seconds}\n";
  235:         }
  236:         print "Away Message      :  $sess->{away_message}\n\n";
  237:     }
  238:     else {
  239:         print "Screen Name       :  $id [$online]\n\n";
  240:     }    
  241:     
  242: }
  243: 
  244: sub send_im {
  245:     my($user, $msgbody) = @_;
  246: 
  247:     print "sending message \"$msgbody\" to $user...";   
  248:     
  249:     my $msg = "<font color=red><strong>[ChivaNet Support]:</strong></font> $msgbody";
  250:     
  251:     $cnclient->GET("/chivanet/send_im?from=ChivaNet&to=$user&message=$msg");
  252:     my $json = $cnclient->responseContent();
  253:     my $hashref = decode_json($json);
  254:     
  255:     if($hashref->{status} == 1) {
  256:         print "[OK]\n";
  257:     }
  258:     else {
  259:         print "[FAIL]\n";
  260:     }
  261: }
  262: 
  263: sub ls {
  264:     my ($description, $match, @entries) = @_;
  265: 
  266:     if($match eq "") {
  267:         $match = '*';
  268:     }
  269: 
  270:     print "Directory of [$description] matching \'$match\':\n\n";
  271:         
  272:     my @sorted = sort(@entries);
  273:     my $maxlen = 0;
  274:     
  275:     foreach my $entry (@sorted) {
  276:         my $len = length($entry);
  277:         if ($len > $maxlen) {
  278:             $maxlen = $len;
  279:         }
  280:     }
  281: 
  282:     $maxlen = $maxlen + 2;
  283: 
  284:     my $ct = $#sorted + 1;
  285:     foreach my $entry (@sorted) {
  286:         if(match_glob($match, $entry)) {
  287:             if($col + $maxlen >= $wchar) {
  288:                 print "$entry\n";
  289:                 $col = 0;
  290:             }
  291:             else {
  292:                 printf("%-$maxlen\s", $entry);
  293:                 $col = $col + $maxlen;
  294:             }
  295:         }
  296:         else {
  297:             $ct = $ct - 1;
  298:         }
  299:     }
  300:     
  301:     print "\n\n$ct matching items in directory\n";
  302: }
  303: 
  304: sub prompt {
  305:     
  306:     my $rawcmd = '';
  307:     my $pmode = $mode;    
  308:     my @path = ();
  309:     
  310:     while (1) {
  311:         $pmode = lc $mode;
  312:         my $prompt = '';
  313:         if($pmode ne "---") {
  314:             if($user eq '---') {
  315:                 $prompt = "$sso_account\@$site:[/$pmode]\$ ";
  316:             }
  317:             else {
  318:                 $prompt = "$sso_account\@$site:[/$pmode/$user]\$ ";
  319:             }
  320:         }
  321:         else {
  322:             $prompt = "$sso_account\@$site:[/]\$ ";
  323:         }
  324:         print $prompt;
  325:         my $line = <STDIN>;
  326:         chomp($line);
  327:         $rawcmd = $line;
  328:         
  329:         my @cmd = split(' ', $rawcmd);
  330:         
  331:         if ($cmd[0] eq "exit" || $cmd[0] eq "logout" || $cmd[0] eq "quit" || $cmd[0] eq "bye" || $cmd[0] eq "lo") {
  332:             return;
  333:         }
  334:         elsif ($cmd[0] eq "pwd") {
  335:             my $pstr = join('/', @path);
  336:             print "/$pstr\n";        
  337:         }        
  338:         elsif ($cmd[0] eq "cd") {
  339:             my $abspath = false;
  340: 
  341:             if($cmd[1] eq "..") {
  342:                 if(@path) {
  343:                     if($#path == 1) {
  344:                         $user = "---";
  345:                         @path = ($pmode);
  346:                     }
  347:                     elsif($#path == 0) {
  348:                         $mode = "---";
  349:                         @path = ();
  350:                     }
  351:                 }
  352:                 else {
  353:                     print "already in root directory\n";
  354:                 }
  355:             }
  356:             else {
  357:                 my @oldpath = @path;
  358:                 @path = split('/', $cmd[1]);
  359:             
  360:                 if(substr($cmd[1], 0, 1) eq '/') {
  361:                     $abspath = true;
  362:                     shift @path;                
  363:                 }
  364:                 
  365:                 if($abspath eq true || !@oldpath) {
  366:                     if($#path == 0) {
  367:                         # mode, no user
  368:                         if($path[0] eq "sso") {
  369:                             $mode = "SSO";
  370:                             $user = "---";
  371:                             @path = ('sso');
  372:                         }
  373:                         elsif($path[0] eq "ras") {
  374:                             $mode = "RAS";
  375:                             $user = "---";
  376:                             @path = ('ras');
  377:                         }
  378:                         else {
  379:                             print "$path[0]:  no such directory exists\n";
  380:                             @path = @oldpath;
  381:                         }
  382:                     }
  383:                     elsif($#path == 1) {
  384:                         # mode and user
  385:                         if($path[0] eq "sso") {
  386:                             $user = select_sso_user($path[1]);                          
  387:                             if($user eq "---") {
  388:                                 print "$path[1]:  no such file exists in $pmode\n";
  389:                                 @path = @oldpath;
  390:                             }
  391:                             else {
  392:                                 $mode = "SSO";
  393:                                 @path = ('sso', $user);
  394:                             }
  395:                         }
  396:                         elsif($path[0] eq "ras") {
  397:                             $user = select_ras_user($path[1]);
  398:                             if($user eq "---") {
  399:                                 print "$path[1]:  no such file exists in $pmode\n";
  400:                                 @path = @oldpath;
  401:                             }
  402:                             else {
  403:                                 $mode = "RAS";
  404:                                 @path = ('ras', $user);
  405:                             }
  406:                         }
  407:                         else {
  408:                             print "$path[0]:  no such directory exists\n";
  409:                             @path = @oldpath;
  410:                         }
  411:                     }
  412:                     else {
  413:                         if($cmd[1] eq '/') {
  414:                             $mode = '---';
  415:                             $user = '---';
  416:                             @path = ();
  417:                         }
  418:                         else {
  419:                             print "cd:  invalid path specification\n";
  420:                             @path = @oldpath;
  421:                         }
  422:                     }
  423:                 }
  424:                 else {
  425:                     if($#oldpath == 1) {
  426:                         print "invalid path specification\n";
  427:                         @path = @oldpath;
  428:                     }
  429:                     else {
  430:                         if($#path == 0) {
  431:                             if($pmode eq "sso") {
  432:                                 $user = select_sso_user($path[0]);                          
  433:                                 if($user eq "---") {
  434:                                     print "$path[0]:  no such file exists in $pmode\n";
  435:                                     @path = @oldpath;
  436:                                 }
  437:                                 else {
  438:                                     $mode = "SSO";
  439:                                     @path = ('sso', $user);
  440:                                 }
  441:                             }
  442:                             elsif($pmode eq "ras") {
  443:                                 $user = select_ras_user($path[0]);                          
  444:                                 if($user eq "---") {
  445:                                     print "$path[0]:  no such file exists in $pmode\n";
  446:                                     @path = @oldpath;
  447:                                 }
  448:                                 else {
  449:                                     $mode = "RAS";
  450:                                     @path = ('ras', $user);
  451:                                 }
  452:                             }
  453:                             else {
  454:                                 print "$path[0]:  no such directory exists\n";
  455:                                 @path = @oldpath;                            
  456:                             }
  457:                         }
  458:                         else {
  459:                             print "invalid path specification\n";
  460:                             @path = @oldpath;
  461:                         }
  462:                     }
  463:                 }                                         
  464:             } # if ..                                  
  465:         }
  466:         elsif ($cmd[0] eq "im") {
  467:             if($mode eq "RAS") {
  468:                 if($user ne "---") {
  469:                     my @msga = @cmd[1..$#cmd];
  470:                     my $msgbody = join(' ', @msga);                    
  471:                     send_im($user, $msgbody);
  472:                 }
  473:                 else {
  474:                     print "im:  no user selected\n";
  475:                 }
  476:             }
  477:             else {
  478:                 print "im:  invalid command outside of ras directory\n";
  479:             }
  480:         }    
  481:         elsif ($cmd[0] eq "trace") {
  482:             if($mode eq "RAS") {
  483:                 if($user ne "---") {                    
  484:                     my $id = trace_ras_sn($user);
  485:                     $mode = "SSO";
  486:                     $user = select_sso_user($id);
  487:                     print_sso_user;
  488:                 }
  489:                 else {
  490:                     print "trace:  no user select\n";
  491:                 }
  492:             }
  493:             else {
  494:                 print "trace:  invalid command outside of ras directory\n";
  495:             }
  496:         }
  497:         elsif ($cmd[0] eq "field") {
  498:             if($mode eq "SSO") {
  499:                 if($user ne "---") {
  500:                     my $act = $account->{account};
  501:                     print "$user\-\>$cmd[1]:  $act->{$cmd[1]}\n";
  502:                 }
  503:                 else {
  504:                     print "field:  no user selected\n";
  505:                 }
  506:             }
  507:             else {
  508:                 print "field:  command invalid outside of ras directory\n";
  509:             }
  510:         }
  511:         elsif ($cmd[0] eq "sessions") {
  512:             if($mode ne "RAS") {
  513:                 print "sessions:  must be in ras mode\n";
  514:             }
  515:             else {
  516:                 if($cmd[1]) {
  517:                     ls "active RAS sessions", $cmd[1], list_ras_sessions();
  518:                 }
  519:                 else {
  520:                     ls "active RAS sessions", '*', list_ras_sessions();
  521:                 }
  522:             }
  523:         }
  524:         elsif ($cmd[0] eq "lssn") {
  525:             if($mode ne "SSO" || $user eq "---") {
  526:                 print "lssn:  must be in sso mode with a user selected\n";
  527:             }
  528:             else {
  529:                 my @sns = list_ras_screennames($user);
  530:                 if($cmd[1]) {
  531:                     ls "RAS screen names for $user", $match, @sns;
  532:                 }
  533:                 else {
  534:                     ls "RAS screen names for $user", '*', @sns;
  535:                 }
  536:             }                    
  537:         }
  538:         elsif ($cmd[0] eq "ls") {
  539:             my @entries = ();
  540:             
  541:             if($mode eq "---") {
  542:                 @entries = list_directories();
  543:             }
  544:             else {
  545:                 if($user ne "---") {
  546:                     if($mode eq "SSO") {
  547:                         print_sso_user;
  548:                     }
  549:                     elsif($mode eq "RAS") {
  550:                         print_ras_user($user);
  551:                     }                                
  552:                 }
  553:                 else {
  554:                     if($mode eq "SSO") {
  555:                         @entries = list_sso_users();
  556:                     }
  557:                     else {
  558:                         @entries = list_ras_users();
  559:                     }
  560:                 }
  561:             }
  562: 
  563:             if(@entries) {
  564:                 my $col = 0;
  565:                 my $pstr = join('/', @path);
  566:                 my $pfin = "/$pstr";
  567:                 if($cmd[1]) {
  568:                     ls $pfin, $cmd[1], @entries;
  569:                 }
  570:                 else {
  571:                     ls $pfin, '*', @entries;
  572:                 }
  573:             }
  574: 
  575:         }
  576:         else {
  577:             print "$cmd[0]: command not found\n";
  578:         }
  579:     }
  580: 
  581: }
  582: 
  583: sub main {  
  584:     ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();    
  585: 
  586:     GetOptions("site=s" => \$site) or die "error in command line arguments";
  587:     
  588:     if($site eq "") {
  589:         print "modcon: must supply -site command-line argument\n";
  590:         return;
  591:     }
  592:     
  593:     $cnclient = REST::Client->new({
  594:         host => "https://$site/rest/api",
  595:         timeout => 10});
  596:     
  597:     print "ChivaNet MODCON $modcon_version\n";
  598:     print " Copyright (C) 2025 Coherent Logic Development LLC\n\n";
  599: 
  600:     print "username: ";
  601:     $sso_account = <STDIN>;
  602:     chomp($sso_account);
  603: 
  604:     print "password: ";
  605:     ReadMode('noecho');
  606:     my $password = <STDIN>;
  607:     chomp($password);
  608:     ReadMode('normal');
  609: 
  610:     
  611:     my $params = $cnclient->buildQuery([username => $sso_account, password => $password]);
  612:     my $result = $cnclient->POST("/chivanet/modcon_auth", substr($params, 1), {'Content-type' => 'application/x-www-form-urlencoded'});
  613:     my $http_response = $result->{_res};
  614:     my $json = $http_response->{_content};
  615:     my $apiresult = decode_json($json);
  616:     
  617:     if($apiresult->{ok} == 1) {
  618:         $cnclient->addHeader('Authorization', "Apikey $apiresult->{token}");
  619: 
  620:         select_sso_user $sso_account;
  621:         my $act = $account->{account};
  622:         
  623:         print "\n\nWelcome to MODCON, $act->{display_name}!\n\n";
  624:         
  625:         prompt();
  626:     }
  627:     else {
  628:         print "\nerror: $apiresult->{error}\n";
  629:         return;
  630:     }
  631: 
  632:     print "Goodbye.\n"
  633: }
  634: 
  635: main();

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>