BBS-Universal

 view release on metacpan or  search on metacpan

lib/BBS/Universal.pm  view on Meta::CPAN

        'TOGGLE SHOW EMAIL' => sub {
            my $self = shift;
            $self->users_toggle_permission('show_email');
            return ($self->load_menu('files/main/account'));
        },
        'TOGGLE PREFER NICKNAME' => sub {
            my $self = shift;
            $self->users_toggle_permission('prefer_nickname');
            return ($self->load_menu('files/main/account'));
        },
        'TOGGLE PLAY FORTUNES' => sub {
            my $self = shift;
            $self->users_toggle_permission('play_fortunes');
            return ($self->load_menu('files/main/account'));
        },
        'BBS LIST ADD' => sub {
            my $self = shift;
            $self->bbs_list_add();
            return ($self->load_menu('files/main/bbs_listing'));
        },
        'BBS LISTING' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/bbs_listing'));
        },
        'LIST USERS' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/list_users'));
        },
        'ACCOUNT MANAGER' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/account'));
        },
        'BACK' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/menu'));
        },
        'DISCONNECT' => sub {
            my $self = shift;
            $self->output("\nDisconnect, are you sure (y|N)?  ");
            unless ($self->decision()) {
                return ($self->load_menu('files/main/menu'));
            }
            $self->output("\n");
        },
        'FILE CATEGORY' => sub {
            my $self = shift;
            $self->choose_file_category();
            return ($self->load_menu('files/main/files_menu'));
        },
        'FILES' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/files_menu'));
        },
        'LIST FILES SUMMARY' => sub {
            my $self = shift;
            $self->files_list_summary(FALSE);
            return ($self->load_menu('files/main/files_menu'));
        },
        'UPLOAD FILE' => sub {
            my $self = shift;
            $self->files_upload_choices();
            return ($self->load_menu('files/main/files_menu'));
        },
        'LIST FILES DETAILED' => sub {
            my $self = shift;
            $self->files_list_detailed(FALSE);
            return ($self->load_menu('files/main/files_menu'));
        },
        'SEARCH FILES SUMMARY' => sub {
            my $self = shift;
            $self->files_list_summary(TRUE);
            return ($self->load_menu('files/main/files_menu'));
        },
        'SEARCH FILES DETAILED' => sub {
            my $self = shift;
            $self->files_list_detailed(TRUE);
            return ($self->load_menu('files/main/files_menu'));
        },
        'NEWS' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/news'));
        },
        'NEWS SUMMARY' => sub {
            my $self = shift;
            $self->news_summary();
            return ($self->load_menu('files/main/news'));
        },
        'NEWS DISPLAY' => sub {
            my $self = shift;
            $self->news_display();
            return ($self->load_menu('files/main/news'));
        },
        'FORUMS' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/forums'));
        },
        'ABOUT' => sub {
            my $self = shift;
            return ($self->load_menu('files/main/about'));
        },
	};
	$self->{'debug'}->DEBUG(['End Commands initialize']);
}

 

# package BBS::Universal::DB;

sub db_initialize {
    my $self = shift;
    $self->{'debug'}->DEBUG(['Start DB Initialize']);
    $self->{'debug'}->DEBUG(['End DB Initialize']);
    return ($self);
} ## end sub db_initialize

sub db_connect {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start DB Connect']);
    my @dbhosts = split(/\s*,\s*/, $self->{'CONF'}->{'STATIC'}->{'DATABASE HOSTNAME'});
    my $errors  = '';

lib/BBS/Universal.pm  view on Meta::CPAN


sub db_disconnect {
    my $self = shift;
    $self->{'debug'}->DEBUG(['Start DB Disconnect']);
    $self->{'dbh'}->disconnect() if (defined($self->{'dbh'}));
    $self->{'debug'}->DEBUG(['End DB Disconnect']);
    return (TRUE);
} ## end sub db_disconnect

 

# package BBS::Universal::FileTransfer;

sub filetransfer_initialize {
    my ($self) = @_;
    $self->{'debug'}->DEBUG(['Start FileTransfer Initialize']);
    $self->{'debug'}->DEBUG(['End FileTransfer Initialize']);
    return ($self);
} ## end sub filetransfer_initialize

sub files_type {
    my ($self, $file) = @_;

    $self->{'debug'}->DEBUG(['Start File Type']);
    my @tmp = split(/\./, $file);
    my $ext = uc(pop(@tmp));
    my $sth = $self->{'dbh'}->prepare('SELECT type FROM file_types WHERE extension=?');
    $sth->execute($ext);
    my $name;
    if ($sth->rows > 0) {
        $name = $sth->fetchrow_array();
    }
    $sth->finish();
    $self->{'debug'}->DEBUG(['End File Type']);
    return ($ext, $name);
} ## end sub files_type

sub files_load_file {
    my ($self, $file) = @_;

    $self->{'debug'}->DEBUG(['Start Files Load File']);
    my $filename = sprintf('%s.%s', $file, $self->{'USER'}->{'text_mode'});
    $self->{'CACHE'}->set(sprintf('SERVER %02d %s', $self->{'thread_number'}, 'CURRENT MENU FILE'), $filename);
    open(my $FILE, '<', $filename);
    my @text = <$FILE>;
    close($FILE);
    chomp(@text);
    $self->{'debug'}->DEBUG(['End Files Load File']);
    return (join("\n", @text));
} ## end sub files_load_file

sub files_list_summary {
    my ($self, $search) = @_;

    $self->{'debug'}->DEBUG(['Start Files List Summary']);
    my $sth;
    my $filter;
    if ($search) {
        $self->prompt('Search for (blank for all)');
        $filter = $self->get_line({ 'type' => STRING, 'max' => 255, 'default' => '' });
        $sth    = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE (filename LIKE ? OR title LIKE ?) AND category_id=? ORDER BY uploaded DESC');
        $sth->execute('%' . $filter . '%', '%' . $filter . '%', $self->{'USER'}->{'file_category'});
    } else {
        $sth = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE category_id=? ORDER BY uploaded DESC');
        $sth->execute($self->{'USER'}->{'file_category'});
    }
    my @files;
    my $max_filename = 10;
    my $max_title    = 20;
    if ($sth->rows > 0) {
        while (my $row = $sth->fetchrow_hashref()) {
            push(@files, $row);
            $max_filename = max(length($row->{'filename'}), $max_filename);
            $max_title    = max(length($row->{'title'}),    $max_title);
        }
        my $table = Text::SimpleTable->new($max_filename, $max_title);
        $table->row('FILENAME', 'TITLE');
        $table->hr();
        foreach my $record (@files) {
            $table->row($record->{'filename'}, $record->{'title'});
        }
        my $mode = $self->{'USER'}->{'text_mode'};
        if ($mode eq 'ANSI') {
            my $text = $table->boxes2('MAGENTA')->draw();
            while ($text =~ / (FILENAME|TITLE) /s) {
                my $ch  = $1;
                my $new = '[% BRIGHT YELLOW %]' . $ch . '[% RESET %]';
                $text =~ s/ $ch / $new /gs;
            }
            $self->output("\n$text");
        } elsif ($mode eq 'ATASCII') {
            $self->output("\n" . $self->color_border($table->boxes->draw(), 'MAGENTA'));
        } elsif ($mode eq 'PETSCII') {
            my $text = $table->boxes->draw();
            while ($text =~ / (FILENAME|TITLE) /s) {
                my $ch  = $1;
                my $new = '[% YELLOW %]' . $ch . '[% RESET %]';
                $text =~ s/ $ch / $new /gs;
            }
            $self->output("\n" . $self->color_border($text, 'PURPLE'));
        } else {
            $self->output("\n" . $table->draw());
        }
    } elsif ($search) {
        $self->output("\nSorry '$filter' not found");
    } else {
        $self->output("\nSorry, this file category is empty\n");
    }
    $self->output("\nPress a key to continue ...");
    $self->get_key(ECHO, BLOCKING);
    $self->{'debug'}->DEBUG(['End Files List Summary']);
    return (TRUE);
} ## end sub files_list_summary

sub files_choices {
    my ($self, $record) = @_;

    while ($self->is_connected()) {
        my $view    = FALSE;
        my $mapping = {
            'TEXT' => '',
            'Z'    => { 'command' => 'BACK',        'color' => 'WHITE', 'access_level' => 'USER',         'text' => 'Return to File Menu' },
            'N'    => { 'command' => 'NEXT',        'color' => 'BLUE',  'access_level' => 'USER',         'text' => 'Next file' },
            'D'    => { 'command' => 'DOWNLOAD',    'color' => 'CYAN',  'access_level' => 'VETERAN',      'text' => 'Download file' },
            'R'    => { 'command' => 'REMOVE FILE', 'color' => 'RED',   'access_level' => 'JUNIOR SYSOP', 'text' => 'Remove file' },
        };
        if ($record->{'extension'} =~ /^(TXT|ASC|ATA|PET|VT|ANS|MD|INF|CDF|PL|PM|PY|C|CPP|H|SH|CSS|HTM|HTML|SHTML|JS|JAVA|XML|BAT)$/ && $self->check_access_level('VETERAN')) {
            $view = TRUE;
            $mapping->{'V'} = { 'command' => 'VIEW FILE', 'color' => 'CYAN', 'access_level' => 'VETERAN', 'text' => 'View file' };
        } ## end if ($record->{'extension'...})
        $self->show_choices($mapping);
        $self->prompt('Choose');
        my $key;
        do {
            $key = uc($self->get_key());
        } until ($key =~ /D|N|Z/ || ($key eq 'V' && $view) || ($key eq 'R' && $self->check_access_level('JUNION SYSOP')));
        $self->output($mapping->{$key}->{'command'} . "\n");
        if ($mapping->{$key}->{'command'} eq 'DOWNLOAD') {
            my $file = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $record->{'filename'};
            $mapping = {
                'B' => { 'command' => 'BACK',   'color' => 'WHITE',       'access_level' => 'USER',    'text' => 'Return to File Menu' },
                'Y' => { 'command' => 'YMODEM', 'color' => 'YELLOW',      'access_level' => 'VETERAN', 'text' => 'Download with the Ymodem protocol' },
                'X' => { 'command' => 'XMODEM', 'color' => 'BRIGHT BLUE', 'access_level' => 'VETERAN', 'text' => 'Download with the Xmodem protocol' },
                'Z' => { 'command' => 'ZMODEM', 'color' => 'GREEN',       'access_level' => 'VETERAN', 'text' => 'Download with the Zmodem protocol' },
            };
            $self->show_choices($mapping);
            $self->prompt('Choose');
            do {
                $key = uc($self->get_key());
            } until ($key =~ /B|X|Y|Z/);
            $self->output($mapping->{$key}->{'command'});
            if ($mapping->{$key}->{'command'} eq 'XMODEM') {
                system('sz', '--xmodem', '--quiet', '--binary', $file);
            } elsif ($mapping->{$key}->{'command'} eq 'YMODEM') {
                system('sz', '--ymodem', '--quiet', '--binary', $file);
            } elsif ($mapping->{$key}->{'command'} eq 'ZMODEM') {
                system('sz', '--zmodem', '--quiet', '--binary', '--resume', $file);
            } else {
                return (FALSE);
            }
            return (TRUE);
        } elsif ($mapping->{$key}->{'command'} eq 'VIEW FILE' && $self->check_access_level($mapping->{$key}->{'access_level'})) {
            my $file = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $record->{'filename'};
            open(my $VIEW, '<', $file);
            binmode($VIEW, ":encoding(UTF-8)");
            my $data;
            read($VIEW, $data, $record->{'file_size'}, 0);
            close($VIEW);
            $self->output('[% CLS %]' . $data . '[% RESET %]');
        } elsif ($mapping->{$key}->{'command'} eq 'REMOVE FILE' && $self->check_access_level($mapping->{$key}->{'access_level'})) {
            return (TRUE);
        } elsif ($mapping->{$key}->{'command'} eq 'NEXT') {
            return (TRUE);
        } elsif ($mapping->{$key}->{'command'} eq 'BACK') {
            return (FALSE);
        }
    } ## end while ($self->is_connected...)
} ## end sub files_choices

sub files_upload_choices {
    my ($self) = @_;
    my $ckey;

    $self->prompt('File Name? ');
    my $file = $self->get_line({ 'type' => FILENAME, 'max' => 255, 'default' => '' });
    my $ext  = uc($file =~ /\.(.*?)$/);

    $self->prompt('Title (Fiendly name)? ');
    my $title = $self->get_line({ 'type' => STRING, 'max' => 255, 'default' => '' });

    $self->prompt('Description? ');
    my $description = $self->get_line({ 'type' => STRING, 'max' => 255, 'default' => '' });

    my $file_category = $self->{'USER'}->{'file_category'};

    my $mapping = {
        'B' => { 'command' => 'BACK',   'color' => 'WHITE',       'access_level' => 'USER',    'text' => 'Return to File Menu' },
        'Y' => { 'command' => 'YMODEM', 'color' => 'YELLOW',      'access_level' => 'VETERAN', 'text' => 'Upload with the Ymodem protocol' },
        'X' => { 'command' => 'XMODEM', 'color' => 'BRIGHT BLUE', 'access_level' => 'VETERAN', 'text' => 'Upload with the Xmodem protocol' },
        'Z' => { 'command' => 'ZMODEM', 'color' => 'GREEN',       'access_level' => 'VETERAN', 'text' => 'Upload with the Zmodem protocol' },
    };
    $self->show_choices($mapping);
    $self->prompt('Choose');
    do {
        $ckey = uc($self->get_key());
    } until ($ckey =~ /B|X|Y|Z/);
    $self->output($mapping->{$ckey}->{'command'});
    if ($mapping->{$ckey}->{'command'} eq 'XMODEM') {
        if ($self->files_receive_file($file, XMODEM)) {
            my $filename = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file;
            my $size     = (-s $filename);
            my $sth      = $self->{'dbh'}->prepare('INSERT INTO files (category,filename,title,file_type,description,file_size) VALUES (?,?,?,(SELECT id FROM file_types WHERE extension=?),?,?');
            $sth->execute($file_category, $file, $title, $ext, $description, $size);
            $sth->finish();
        } ## end if ($self->files_receive_file...)
    } elsif ($mapping->{$ckey}->{'command'} eq 'YMODEM') {
        if ($self->files_receive_file($file, YMODEM)) {
            my $filename = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file;
            my $size     = (-s $filename);
            my $sth      = $self->{'dbh'}->prepare('INSERT INTO files (category,filename,title,file_type,description,file_size) VALUES (?,?,?,(SELECT id FROM file_types WHERE extension=?),?,?');
            $sth->execute($file_category, $file, $title, $ext, $description, $size);
            $sth->finish();
        } ## end if ($self->files_receive_file...)
    } elsif ($mapping->{$ckey}->{'command'} eq 'ZMODEM') {
        if ($self->files_receive_file($file, ZMODEM)) {
            my $filename = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file;
            my $size     = (-s $filename);
            my $sth      = $self->{'dbh'}->prepare('INSERT INTO files (category,filename,title,file_type,description,file_size) VALUES (?,?,?,(SELECT id FROM file_types WHERE extension=?),?,?');
            $sth->execute($file_category, $file, $title, $ext, $description, $size);
            $sth->finish();
        } ## end if ($self->files_receive_file...)
    } else {
        return (FALSE);
    }
    if ($? == -1) {
        $self->{'debug'}->ERROR(["Could not execute rz:  $!"]);
    } elsif ($? & 127) {
        $self->{'debug'}->ERROR(["File Transfer Aborted:  $!"]);
    } else {
        $self->{'debug'}->DEBUG(['File Transfer Successful']);
    }
    return (TRUE);
} ## end sub files_upload_choices

sub files_list_detailed {
    my ($self, $search) = @_;

    $self->{'debug'}->DEBUG(['Start Files List Detailed']);
    my $sth;
    my $filter;
    my $columns = $self->{'USER'}->{'max_columns'};
    if ($search) {
        $self->prompt('Search for');
        $filter = $self->get_line({ 'type' => STRING, 'max' => 255, 'default' => '' });
        $sth    = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE (filename LIKE ? OR title LIKE ?) AND category_id=? ORDER BY uploaded DESC');
        $sth->execute('%' . $filter . '%', '%' . $filter . '%', $self->{'USER'}->{'file_category'});
    } else {
        $sth = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE category_id=? ORDER BY uploaded DESC');
        $sth->execute($self->{'USER'}->{'file_category'});
    }
    my @files;
    if ($sth->rows > 0) {
        $self->{'debug'}->DEBUGMAX(\@files);
        my $table;
        my $mode = $self->{'USER'}->{'text_mode'};
        while (my $row = $sth->fetchrow_hashref()) {
            push(@files, $row);
        }
        $sth->finish();
        foreach my $record (@files) {
            if ($mode eq 'ANSI') {
                $self->output("\n" . '[% HORIZONTAL RULE GREEN %]' . "\n");
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]       TITLE [% RESET %] ' . $record->{'title'} . "\n");
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]    FILENAME [% RESET %] ' . $record->{'filename'} . "\n");
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]   FILE SIZE [% RESET %] ' . format_number($record->{'file_size'}) . "\n");
                if ($record->{'prefer_nickname'}) {
                    $self->output('[% B_BLUE %][% BRIGHT WHITE %]    UPLOADER [% RESET %] ' . $record->{'nickname'} . "\n");
                } else {
                    $self->output('[% B_BLUE %][% BRIGHT WHITE %]    UPLOADER [% RESET %] ' . $record->{'fullname'} . "\n");
                }
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]   FILE TYPE [% RESET %] ' . $record->{'type'} . "\n");
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]    UPLOADED [% RESET %] ' . $record->{'uploaded'} . "\n");
                $self->output('[% B_BLUE %][% BRIGHT WHITE %]      THUMBS [% RESET %] [% THUMBS UP SIGN %] ' . (0 + $record->{'thumbs_up'}) . '   [% THUMBS DOWN SIGN %] ' . (0 + $record->{'tumbs_down'}) . "\n");
                $self->output('[% HORIZONTAL RULE GREEN %]' . "\n");
            } else {
                $self->output("\n      TITLE: " . $record->{'title'} . "\n");
                $self->output('   FILENAME: ' . $record->{'filename'} . "\n");
                $self->output('  FILE SIZE: ' . format_number($record->{'file_size'}) . "\n");
                if ($record->{'prefer_nickname'}) {
                    $self->output('   UPLOADER: ' . $record->{'nickname'} . "\n");
                } else {
                    $self->output('   UPLOADER: ' . $record->{'fullname'} . "\n");
                }
                $self->output('  FILE TYPE: ' . $record->{'type'} . "\n");
                $self->output('   UPLOADED: ' . $record->{'uploaded'} . "\n");
                $self->output('  THUMBS UP: ' . (0 + $record->{'thumbs_up'}) . "\n");
                $self->output('THUMBS DOWN: ' . (0 + $record->{'thumbs_down'}) . "\n");
            } ## end else [ if ($mode eq 'ANSI') ]
            last unless ($self->files_choices($record));
        } ## end foreach my $record (@files)
    } elsif ($search) {
        $self->output("\nSorry '$filter' not found");
    } else {
        $self->output("\nSorry, this file category is empty\n");
    }
    $self->output("\nPress a key to continue ...");
    $self->get_key(ECHO, BLOCKING);
    $self->{'debug'}->DEBUG(['End Files List Detailed']);
    return (TRUE);
} ## end sub files_list_detailed

sub files_save_file {
    my ($self) = @_;
    $self->{'debug'}->DEBUG(['Start Save File']);
    $self->{'debug'}->DEBUG(['End Save File']);
    return (TRUE);
} ## end sub files_save_file

sub files_receive_file {
    my ($self, $file, $protocol) = @_;

    my $success = TRUE;
    $self->{'debug'}->DEBUG(['Start Receive File']);
    unless ($self->{'local_mode'}) {
        if ($protocol == YMODEM) {
            $self->{'debug'}->DEBUG(["Send file $file with Ymodem"]);
            $success = $self->files_receive_file_ymodem($self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file);
        } elsif ($protocol == ZMODEM) {
            $self->{'debug'}->DEBUG(["Send file $file with Zmodem"]);
            $success = $self->files_receive_file_zmodem($self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file);
        } else {    # Xmodem
            $success = $self->files_receive_file_xmodem($self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'} . '/' . $file);
            $self->{'debug'}->DEBUG(["Send file $file with Xmodem"]);
        }
    } else {
        $self->output("Upload not allowed in local mode\n");
    }
    $self->{'debug'}->DEBUG(['End Receive File']);
    return ($success);
} ## end sub files_receive_file

sub files_receive_file_xmodem {
    my ($self, $file) = @_;

    $self->{'debug'}->DEBUG(['Start files_receive_file_xmodem']);
    my $sock = $self->{'cl_socket'};
    unless ($sock) {
        $self->{'debug'}->ERROR(["No client socket for XMODEM receive"]);
        return 0;
    }

    $self->output("\nStart sending your file via Xmodem\n");
    my $path = $file;
    my $FH;

lib/BBS/Universal.pm  view on Meta::CPAN


        # if exec fails
        POSIX::_exit(1);
    } ## end if ($pid == 0)

    # parent: wait for child, return success based on exit status
    waitpid($pid, 0);
    my $status = $?;    # full status
    if ($status == -1) {
        $self->{'debug'}->ERROR(["Failed to waitpid for $cmd: $!"]);
        return 0;
    }
    my $exitcode = ($status >> 8) & 0xFF;
    if ($exitcode != 0) {
        $self->{'debug'}->DEBUG(["$cmd exited with code $exitcode"]);
    }
    return $exitcode == 0 ? 1 : 0;
} ## end sub _run_on_socket

sub files_send_zmodem {
    my ($self, $file) = @_;
    $self->{'debug'}->DEBUG(['Start files_send_zmodem (using lrzsz)']);

    # Require lrzsz (sz) to be installed on the system.
    # sz will write to the socket (which we've dup'd to STDOUT in child).
    my $sock = $self->{'cl_socket'};
    unless ($sock) {
        $self->{'debug'}->ERROR(["No client socket for ZMODEM send"]);
        return 0;
    }

    $self->output("\nStart Zmodem file download\n");

    # full path to file on server
    my $path = $file;
    unless (-e $path) {
        $self->{'debug'}->ERROR(["File not found for ZMODEM send: $path"]);
        return 0;
    }

    # Use sz --zmodem --binary --quiet --resume <file>
    # note: --resume is helpful if client requests resume. Adjust flags per your lrzsz version.
    my @args = ('--zmodem', '--binary', '--quiet', '--resume', $path);

    my $ok = $self->_run_on_socket('sz', \@args);
    $self->output("\nFile download complete\n");
    $self->{'debug'}->DEBUG(['End files_send_zmodem (using lrzsz)']);
    return $ok;
} ## end sub files_send_zmodem

sub files_receive_file_zmodem {
    my ($self, $file) = @_;
    $self->{'debug'}->DEBUG(['Start files_receive_file_zmodem (using lrzsz)']);

    my $sock = $self->{'cl_socket'};
    unless ($sock) {
        $self->{'debug'}->ERROR(["No client socket for ZMODEM receive"]);
        return 0;
    }

    $self->output("\nStart Zmodem file upload\n");

    # When rz receives files it writes them into the current working directory.
    # Use the destination directory from config (same place other uploads are stored).
    my $dest_dir = $self->{'CONF'}->{'BBS ROOT'} . '/' . $self->{'CONF'}->{'FILES PATH'} . '/' . $self->{'USER'}->{'file_category_path'};

    # ensure directory exists
    unless (-d $dest_dir) {
        File::Path::mkpath($dest_dir);
        if ($@) {
            $self->{'debug'}->ERROR(["Failed to create dest dir $dest_dir: $@"]);
            return 0;
        }
    } ## end unless (-d $dest_dir)

    # We will chdir in the child before exec so the received file lands in $dest_dir.
    # Use rz --binary --quiet. Depending on lrzsz version you may want --overwrite or --keep
    my @args = ('--binary', '--overwrite', '--quiet');

    my $ok = $self->_run_on_socket('rz', \@args, $dest_dir);

    $self->output("\nFile upload complete\n");
    $self->{'debug'}->DEBUG(['End files_receive_file_zmodem (using lrzsz)']);
    return $ok;
} ## end sub files_receive_file_zmodem

 

# package BBS::Universal::Messages;

sub messages_initialize {
    my $self = shift;
    $self->{'debug'}->DEBUG(['Start Messages Initialize']);
    $self->{'debug'}->DEBUG(['End Messages Initialize']);
    return ($self);
} ## end sub messages_initialize

sub messages_forum_categories {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start Messages Forum Categories']);
    my $command = '';
    my $id;
    my $sth      = $self->{'dbh'}->prepare('SELECT * FROM message_categories ORDER BY description');
    my $category = $self->{'USER'}->{'forum_category'};
    $sth->execute();    # $self->{'USER'}->{'forum_category'});
    my $mapping = {
        'TEXT' => '',
        'Z'    => { 'command' => 'BACK', 'color' => 'WHITE', 'access_level' => 'USER', 'text' => 'Return to Forum Menu' },
    };
    my @menu_choices = @{ $self->{'MENU CHOICES'} };

    while (my $result = $sth->fetchrow_hashref()) {
        if ($self->check_access_level($result->{'access_level'})) {
            $mapping->{ shift(@menu_choices) } = {
                'command'      => $result->{'name'},
                'id'           => $result->{'id'},
                'color'        => ($category == $result->{'id'}) ? 'GREEN' : 'WHITE',
                'access_level' => $result->{'access_level'},
                'text'         => $result->{'description'},
            };
        } ## end if ($self->check_access_level...)
    } ## end while (my $result = $sth->...)
    $sth->finish();
    $self->show_choices($mapping);
    $self->prompt('Choose Forum Category');
    my $key;
    do {
        $key = uc($self->get_key(SILENT, BLOCKING));
    } until (exists($mapping->{$key}) || $key eq chr(3) || !$self->is_connected());
    if ($key eq chr(3)) {
        $command = 'DISCONNECT';
    } else {
        $id      = $mapping->{$key}->{'id'};
        $command = $mapping->{$key}->{'command'};
    }
    return ($command) if ($key eq 'Z');
    if ($self->is_connected() && $command ne 'DISCONNECT') {
        $self->output($command);
        $sth = $self->{'dbh'}->prepare('UPDATE users SET forum_category=? WHERE id=?');
        $sth->execute($id, $self->{'USER'}->{'id'});
        $sth->finish();

lib/BBS/Universal.pm  view on Meta::CPAN

        while ($text =~ /\[\%\s+HORIZONTAL RULE (.*?)\s+\%\]/) {
            my $rule = "[% $1 %]" . '[% TOP HORIZONTAL BAR %]' x $self->{'USER'}->{'max_columns'} . '[% RESET %]';
            $text =~ s/\[\%\s+HORIZONTAL RULE (.*?)\s+\%\]/$rule/gs;
        }
        foreach my $string (keys %{ $self->{'petscii_meta'} }) {    # Decode macros
            if ($string =~ /CLEAR|CLS/i && ($self->{'sysop'} || $self->{'local_mode'})) {
                my $ch = locate(($self->{'CACHE'}->get('START_ROW') + $self->{'CACHE'}->get('ROW_ADJUST')), 1) . cldown;
                $text =~ s/\[\%\s+$string\s+\%\]/$ch/gi;
            } else {
                $text =~ s/\[\%\s+$string\s+\%\]/$self->{'petscii_meta'}->{$string}->{'out'}/gi;
            }
        } ## end foreach my $string (keys %{...})
    } ## end if (length($text) > 1)
    my $s_len = length($text);
    my $nl    = $self->{'petscii_meta'}->{'NEWLINE'}->{'out'};
    foreach my $count (0 .. $s_len) {
        my $char = substr($text, $count, 1);
        if ($char eq "\n") {
            if ($text !~ /$nl/ && !$self->{'local_mode'}) {    # translate only if the file doesn't have ASCII newlines
                $char = $nl;
            }
            $lines--;
            if ($lines <= 0) {
                $lines = $mlines;
                last unless ($self->scroll($nl));
            }
        } ## end if ($char eq "\n")
        $self->send_char($char);
    } ## end foreach my $count (0 .. $s_len)
    $self->{'debug'}->DEBUG(['End PETSCII Output']);
    return (TRUE);
} ## end sub petscii_output

 

# package BBS::Universal::SysOp;

sub sysop_initialize {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start SysOp Initialize']);

    # Screen size and derived sections for layout
    my ($wsize, $hsize, $wpixels, $hpixels) = GetTerminalSize();
    $self->{'wsize'} = $wsize;
    $self->{'hsize'} = $hsize;
    $self->{'debug'}->DEBUG(["Screen Size is $wsize x $hsize"]);

    my $sections     = _sections_for_width($wsize);
    my $versions     = $self->sysop_versions_format($sections, FALSE);
    my $bbs_versions = $self->sysop_versions_format($sections, TRUE);

    # Visual config
    $self->{'sysop_menu_colors'} = [91, 93, 92, 95, 94, 96];
    $self->{'sysop_menu_files'}  = ['', '', '', '', ''];

    # Default user capability flags
    $self->{'flags_default'} = {
        'prefer_nickname' => 'ON',
        'view_files'      => 'ON',
        'upload_files'    => 'OFF',
        'download_files'  => 'ON',
        'remove_files'    => 'OFF',
        'read_message'    => 'ON',
        'post_message'    => 'ON',
        'remove_message'  => 'OFF',
        'sysop'           => 'OFF',
        'show_email'      => 'OFF',
    };

    # Tokens (static + dynamic)
    my $static  = _build_static_tokens($self, $versions, $bbs_versions);
    my $dynamic = _build_dynamic_tokens($self);
    $self->{'sysop_tokens'} = { %{$static}, %{$dynamic} };

    # Field orderings
    $self->{'SYSOP ORDER DETAILED'} = [
        qw(
            id
            fullname
            username
            given
            family
            nickname
            email
            birthday
            location
            access_level
            date_format
            baud_rate
            text_mode
            max_columns
            max_rows
            timeout
            retro_systems
            accomplishments
            prefer_nickname
            view_files
            upload_files
            download_files
            remove_files
            play_fortunes
            read_message
            post_message
            remove_message
            sysop
            banned
            login_time
            logout_time
        )
    ];

    $self->{'SYSOP ORDER ABBREVIATED'} = [
        qw(
            id
            fullname
            username
            given
            family
            nickname
            text_mode
        )
    ];

    # Field type definitions
    $self->{'SYSOP FIELD TYPES'} = {
        'id'              => { 'type' => NUMERIC, 'max' => 2,   'min' => 2 },
        'username'        => { 'type' => HOST,    'max' => 32,  'min' => 16 },
        'fullname'        => { 'type' => STRING,  'max' => 20,  'min' => 15 },
        'given'           => { 'type' => STRING,  'max' => 120, 'min' => 32 },
        'family'          => { 'type' => STRING,  'max' => 120, 'min' => 32 },
        'nickname'        => { 'type' => STRING,  'max' => 120, 'min' => 32 },
        'email'           => { 'type' => STRING,  'max' => 120, 'min' => 32 },
        'birthday'        => { 'type' => STRING,  'max' => 10,  'min' => 10 },
        'location'        => { 'type' => STRING,  'max' => 120, 'min' => 40 },
        'date_format'     => { 'type' => RADIO,   'max' => 14,  'min' => 14, 'choices' => ['MONTH/DAY/YEAR', 'DAY/MONTH/YEAR', 'YEAR/MONTH/DAY'], 'default' => 'DAY/MONTH/YEAR', },
        'access_level'    => { 'type' => RADIO,   'max' => 12,  'min' => 12, 'choices' => ['USER', 'VETERAN', 'JUNIOR SYSOP', 'SYSOP'], 'default' => 'USER', },
        'baud_rate'       => { 'type' => RADIO,   'max' => 5,   'min' => 5,  'choices' => ['FULL', '115200', '57600', '38400', '19200', '9600', '4800', '2400', '1200', '600', '300'], 'default' => 'FULL', },
        'login_time'      => { 'type' => STRING,  'max' => 10,  'min' => 10 },
        'logout_time'     => { 'type' => STRING,  'max' => 10,  'min' => 10 },
        'text_mode'       => { 'type' => RADIO,   'max' => 7,   'min' => 9, 'choices' => ['ANSI', 'ASCII', 'ATASCII', 'PETSCII'], 'default' => 'ASCII', },
        'max_rows'        => { 'type' => NUMERIC, 'max' => 3,   'min' => 3, 'default' => 25 },
        'max_columns'     => { 'type' => NUMERIC, 'max' => 3,   'min' => 3, 'default' => 80 },
        'timeout'         => { 'type' => NUMERIC, 'max' => 5,   'min' => 5, 'default' => 10 },
        'retro_systems'   => { 'type' => STRING,  'max' => 120, 'min' => 40 },
        'accomplishments' => { 'type' => STRING,  'max' => 120, 'min' => 40 },
        'prefer_nickname' => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'view_files'      => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'ON' },
        'banned'          => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'upload_files'    => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'download_files'  => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'remove_files'    => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'read_message'    => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'ON' },
        'post_message'    => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'remove_message'  => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'play_fortunes'   => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'ON' },
        'sysop'           => { 'type' => BOOLEAN, 'max' => 5,   'min' => 5, 'choices' => ['ON', 'OFF'], 'default' => 'OFF' },
        'password'        => { 'type' => STRING,  'max' => 64,  'min' => 32 },
    };

    $self->{'debug'}->DEBUG(['End SysOp Initialize']);
    return $self;
} ## end sub sysop_initialize

# Helper: map terminal width to section count
sub _sections_for_width {
    my ($wsize) = @_;
    return 1 if $wsize <= 80;
    return 2 if $wsize <= 120;
    return 3 if $wsize <= 160;
    return 4 if $wsize <= 200;
    return 5 if $wsize <= 240;
    return 6;
} ## end sub _sections_for_width

# Helper: build static token fields from $self
sub _build_static_tokens {
    my ($self, $versions, $bbs_versions) = @_;
    return {
        'HOSTNAME'     => $self->sysop_hostname,
        'IP ADDRESS'   => $self->sysop_ip_address(),
        'CPU BITS'     => $self->{'CPU'}->{'CPU BITS'},
        'CPU CORES'    => $self->{'CPU'}->{'CPU CORES'},
        'CPU SPEED'    => $self->{'CPU'}->{'CPU SPEED'},
        'CPU IDENTITY' => $self->{'CPU'}->{'CPU IDENTITY'},
        'CPU THREADS'  => $self->{'CPU'}->{'CPU THREADS'},
        'HARDWARE'     => $self->{'CPU'}->{'HARDWARE'},
        'VERSIONS'     => $versions,
        'BBS VERSIONS' => $bbs_versions,
        'BBS NAME'     => colored(['green'], $self->{'CONF'}->{'BBS NAME'}),
    };
} ## end sub _build_static_tokens

# Helper: build dynamic token closures (uniform style)
sub _build_dynamic_tokens {
    my ($self) = @_;
    return {
        'THREADS COUNT'   => sub { my $self = shift; return $self->{'CACHE'}->get('THREADS_RUNNING'); },
        'USERS COUNT'     => sub { my $self = shift; return $self->db_count_users(); },
        'UPTIME'          => sub { my $self = shift; my $uptime = `uptime -p`; chomp($uptime); return $uptime; },
        'DISK FREE SPACE' => sub { my $self = shift; return $self->sysop_disk_free(); },
        'MEMORY'          => sub { my $self = shift; return $self->sysop_memory(); },
        'ONLINE'          => sub { my $self = shift; return $self->sysop_online_count(); },
        'CPU LOAD'        => sub { my $self = shift; return $self->cpu_info->{'CPU LOAD'}; },
        'ENVIRONMENT'     => sub { my $self = shift; return $self->sysop_showenv(); },
        'FILE CATEGORY'   => sub {
			my $self = shift;
			my $sth  = $self->{'dbh'}->prepare('SELECT description FROM file_categories WHERE id=?');
			$sth->execute($self->{'USER'}->{'file_category'});
			my ($result) = $sth->fetchrow_array();

lib/BBS/Universal.pm  view on Meta::CPAN

        foreach my $name (@order) {
            push(@hw, $self->{'SYSOP FIELD TYPES'}->{$name}->{'min'});
        }
        $table = Text::SimpleTable->new(@hw);
        if ($list_mode =~ /ABBREVIATED/) {
            $table->row(@order);
        } else {
            my @title = ();
            foreach my $heading (@order) {
                push(@title, $self->sysop_vertical_heading($heading));
            }
            $table->row(@title);
        } ## end else [ if ($list_mode =~ /ABBREVIATED/)]
        $table->hr();
        while (my $row = $sth->fetchrow_hashref()) {
            my @vals = ();
            foreach my $name (@order) {
                push(@vals, $row->{$name} . '');
                $self->{'debug'}->DEBUGMAX([$name, $row->{$name}]);
            }
            $table->row(@vals);
        } ## end while (my $row = $sth->fetchrow_hashref...)
        $sth->finish();
        my $string = $table->thick('CYAN')->draw();
        $self->sysop_pager("$string\n");
    } ## end else [ if ($list_mode =~ /VERTICAL/)]
    print 'Press a key to continue ... ';
    $self->{'debug'}->DEBUG(['End SysOp List Users']);
    return ($self->sysop_keypress());
} ## end sub sysop_list_users

sub sysop_delete_files { # Placeholder
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start SysOp Delete Files']);
    $self->{'debug'}->DEBUG(['End SysOp Delete Files']);
    return (TRUE);
} ## end sub sysop_delete_files

sub sysop_list_files {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start SysOp List Files']);
    my ($wsize, $hsize, $wpixels, $hpixels) = GetTerminalSize();
    my $sth = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE category_id=?');
    $sth->execute($self->{'USER'}->{'file_category'});
    my $sizes = {};
    while (my $row = $sth->fetchrow_hashref()) {
        foreach my $name (keys %{$row}) {
            if ($name eq 'file_size') {
                my $size = format_number($row->{$name});
                $sizes->{$name} = max(length($size), $sizes->{$name});
            } else {
                $sizes->{$name} = max(length($row->{$name}), $sizes->{$name});
            }
        } ## end foreach my $name (keys %{$row...})
    } ## end while (my $row = $sth->fetchrow_hashref...)
    $sth->finish();
    my $table;
    if ($wsize > 150) {
        $table = Text::SimpleTable->new(max(5, $sizes->{'title'}), max(8, $sizes->{'filename'}), max(4, $sizes->{'type'}), max(11, $sizes->{'description'}), max(8, $sizes->{'username'}), max(4, $sizes->{'file_size'}), max(8, $sizes->{'uploaded'}), ma...
        $table->row('TITLE', 'FILENAME', 'TYPE', 'DESCRIPTION', 'UPLOADER', 'SIZE', 'UPLOADED', 'THUMBS UP', 'THUMBS DOWN');
    } else {
        $table = Text::SimpleTable->new(max(5, $sizes->{'filename'}), max(8, $sizes->{'title'}), max(4, $sizes->{'extension'}), max(11, $sizes->{'description'}), max(8, $sizes->{'username'}), max(4, $sizes->{'file_size'}), max(9, $sizes->{'thumbs_up'...
        $table->row('TITLE', 'FILENAME', 'TYPE', 'DESCRIPTION', 'UPLOADER', 'SIZE', 'THUMBS UP', 'THUMBS DOWN');
    }
    $table->hr();
    $sth = $self->{'dbh'}->prepare('SELECT * FROM files_view WHERE category_id=?');
    $sth->execute($self->{'USER'}->{'file_category'});
    my $category;

    while (my $row = $sth->fetchrow_hashref()) {
        if ($wsize > 150) {
            $table->row($row->{'title'}, $row->{'filename'}, $row->{'type'}, $row->{'description'}, $row->{'username'}, format_number($row->{'file_size'}), $row->{'uploaded'}, sprintf('%-06u', $row->{'thumbs_up'}), sprintf('%-06u', $row->{'thumbs_dow...
        } else {
            $table->row($row->{'title'}, $row->{'filename'}, $row->{'extension'}, $row->{'description'}, $row->{'username'}, format_number($row->{'file_size'}), sprintf('%-06u', $row->{'thumbs_up'}), sprintf('%-06u', $row->{'thumbs_down'}));
        }
        $category = $row->{'category'};
    } ## end while (my $row = $sth->fetchrow_hashref...)
    $sth->finish();
    $self->sysop_output("\n" . '[% B_ORANGE %][% BLACK %] Current Category [% RESET %] [% BRIGHT YELLOW %][% BLACK RIGHT-POINTING TRIANGLE %][% RESET %] [% BRIGHT WHITE %][% FILE CATEGORY %][% RESET %]');
    my $tbl = $table->twin('YELLOW')->draw();
    while ($tbl =~ / (TITLE|FILENAME|TYPE|DESCRIPTION|UPLOADER|SIZE|UPLOADED|THUMBS UP|THUMBS DOWN) /) {
        my $ch  = $1;
        my $new = '[% BRIGHT GREEN %]' . $ch . '[% RESET %]';
        $tbl =~ s/ $ch / $new /gs;
    }
    $self->sysop_output("\n$tbl\nPress a Key To Continue ...");
    $self->sysop_keypress();
    print " BACK\n";
    $self->{'debug'}->DEBUG(['End SysOp List Files']);
    return (TRUE);
} ## end sub sysop_list_files

sub sysop_color_border {
    my ($self, $tbl, $color, $type) = @_;

    $self->{'debug'}->DEBUG(['Start SysOp Color Border']);
    $color = '[% ' . $color . ' %]';
    my $new;
    if ($tbl =~ /(─)/) {
        my $ch = $1;
        $new = $ch;
        if ($type eq 'DOUBLE') {
            $new =~ s/─/\[\% BOX DRAWINGS DOUBLE HORIZONTAL \%\]/gs;
        } elsif ($type eq 'HEAVY') {
            $new =~ s/─/\[\% BOX DRAWINGS HEAVY HORIZONTAL \%\]/gs;
        }
        $new = $color . $new . '[% RESET %]';
        $tbl =~ s/$ch/$new/gs;
    } ## end if ($tbl =~ /(─)/)
    if ($tbl =~ /(│)/) {
        my $ch = $1;
        $new = $ch;
        if ($type eq 'DOUBLE') {
            $new =~ s/│/\[\% BOX DRAWINGS DOUBLE VERTICAL \%\]/gs;
        } elsif ($type eq 'HEAVY') {
            $new =~ s/│/\[\% BOX DRAWINGS HEAVY VERTICAL \%\]/gs;
        }
        $new = '[% RESET %]' . $color . $new . '[% RESET %]';
        $tbl =~ s/$ch/$new/gs;
    } ## end if ($tbl =~ /(│)/)
    if ($tbl =~ /(┌)/) {
        my $ch = $1;
        $new = $ch;
        if ($type eq 'ROUNDED') {
            $new =~ s/┌/\[\% BOX DRAWINGS LIGHT ARC DOWN AND RIGHT \%\]/gs;
        } elsif ($type eq 'DOUBLE') {
            $new =~ s/┌/\[\% BOX DRAWINGS DOUBLE DOWN AND RIGHT \%\]/gs;
        } elsif ($type eq 'HEAVY') {
            $new =~ s/┌/\[\% BOX DRAWINGS HEAVY DOWN AND RIGHT \%\]/gs;
        }
        $new = $color . $new . '[% RESET %]';
        $tbl =~ s/$ch/$new/gs;

lib/BBS/Universal.pm  view on Meta::CPAN

    if (defined($user_row)) {
        my ($wsize, $hsize, $wpixels, $hpixels) = GetTerminalSize();
        do {
            my $valsize = 1;
            foreach my $fld (keys %{$user_row}) {
                $valsize = max($valsize, length($user_row->{$fld}));
            }
            $valsize = min($valsize, $wsize - 29);
            my $table = Text::SimpleTable->new(6, 16, $valsize);
            $table->row('CHOICE', 'FIELD', 'VALUE');
            $table->hr();
            my $count = 0;
            my %choice;
            foreach my $field (@{ $self->{'SYSOP ORDER DETAILED'} }) {
                if ($field =~ /_time|fullname|_category|id/) {
                    $table->row(' ', uc($field), $user_row->{$field} . '');
                } else {
                    if ($user_row->{$field} =~ /^(0|1)$/) {
                        $table->row($choices[$count], uc($field), $self->sysop_true_false($user_row->{$field}, 'YN'));
                    } elsif ($field eq 'access_level') {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . ' - USER, VETERAN, JUNIOR SYSOP, SYSOP');
                    } elsif ($field eq 'date_format') {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . ' - YEAR/MONTH/DAY, MONTH/DAY/YEAR, DAY/MONTH/YEAR');
                    } elsif ($field eq 'baud_rate') {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . ' - 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, FULL');
                    } elsif ($field eq 'text_mode') {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . ' - ASCII, ANSI, ATASCII, PETSCII');
                    } elsif ($field eq 'timeout') {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . ' - Minutes');
                    } else {
                        $table->row($choices[$count], uc($field), $user_row->{$field} . '');
                    }
                    $count++ if ($key_exit eq $choices[$count]);
                    $choice{ $choices[$count] } = $field;
                    $count++;
                } ## end else [ if ($field =~ /_time|fullname|_category|id/)]
            } ## end foreach my $field (@{ $self...})
            my $tbl = $table->round('BRIGHT CYAN')->draw();
            while ($tbl =~ / (CHOICE|FIELD|VALUE|No|Yes|USER. VETERAN. JUNIOR SYSOP. SYSOP|YEAR.MONTH.DAY, MONTH.DAY.YEAR, DAY.MONTH.YEAR|300. 600. 1200. 2400. 4800. 9600. 19200. FULL|ASCII. ANSI. ATASCII. PETSCII|Minutes) /) {
                my $ch = $1;
                my $new;
                if ($ch =~ /Yes/) {
                    $new = '[% GREEN %]' . $ch . '[% RESET %]';
                } elsif ($ch =~ /No/) {
                    $new = '[% RED %]' . $ch . '[% RESET %]';
                } elsif ($ch =~ /CHOICE|FIELD|VALUE/) {
                    $new = '[% BRIGHT YELLOW %]' . $ch . '[% RESET %]';
                } else {
                    $new = '[% RGB 50,50,150 %]' . $ch . '[% RESET %]';
                }
                $tbl =~ s/$ch/$new/g;
            } ## end while ($tbl =~ / (CHOICE|FIELD|VALUE|No|Yes|USER. VETERAN. JUNIOR SYSOP. SYSOP|YEAR.MONTH.DAY, MONTH.DAY.YEAR, DAY.MONTH.YEAR|300. 600. 1200. 2400. 4800. 9600. 19200. FULL|ASCII. ANSI. ATASCII. PETSCII|Minutes) /)
            $self->sysop_output('[% CLS %]' . $tbl . "\n");
            $self->sysop_show_choices($mapping);
            $self->sysop_prompt('Choose');
            do {
                $key = uc($self->sysop_keypress());
            } until ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' =~ /$key/i);
            if ($key !~ /$key_exit/i) {
                print 'Edit > (', $choice{$key}, ' = ', $user_row->{ $choice{$key} }, ') > ';
                if ($choice{$key} =~ /^(play_fortunes|prefer_nickname|view_files|upload_files|download_files|remove_files|read_message|post_message|remove_message|sysop)$/) {
                    $user_row->{ $choice{$key} } = ($user_row->{ $choice{$key} } == 1) ? 0 : 1;
                    my $sth = $self->{'dbh'}->prepare('UPDATE permissions SET ' . $choice{$key} . '= !' . $choice{$key} . '  WHERE id=?');
                    $sth->execute($user_row->{'id'});
                    $sth->finish();
                } elsif($choice{$key} =~ /text_mode/) {
                    my $new = $self->sysop_get_line($self->{'SYSOP FIELD TYPES'}->{ $choice{$key} }, $user_row->{ $choice{$key} });
                    $user_row->{ $choice{$key} } = $new;
                    my $sth = $self->{'dbh'}->prepare('UPDATE users SET ' . $choice{$key} . '=? WHERE id=?');
                    $sth->execute($new, $self->{'text_modes'}->{$user_row->{'id'}});
                    $sth->finish();
                } else {
                    my $new = $self->sysop_get_line($self->{'SYSOP FIELD TYPES'}->{ $choice{$key} }, $user_row->{ $choice{$key} });
                    $user_row->{ $choice{$key} } = $new;
                    my $sth = $self->{'dbh'}->prepare('UPDATE users SET ' . $choice{$key} . '=? WHERE id=?');
                    $sth->execute($new, $user_row->{'id'});
                    $sth->finish();
                } ## end else [ if ($choice{$key} =~ /^(prefer_nickname|view_files|upload_files|download_files|remove_files|read_message|post_message|remove_message|sysop)$/)]
            } else {
                print "BACK\n";
            }
        } until ($key =~ /$key_exit/i);
    } elsif ($search ne '') {
        print "User not found!\n\n";
    }
    $self->{'debug'}->DEBUG(['End SysOp User Edit']);
    return (TRUE);
} ## end sub sysop_user_edit

sub sysop_new_user_edit {
    my $self = shift;
    my $row  = shift;
    my $file = shift;

    $self->{'debug'}->DEBUG(['Start SysOp User Edit']);
    my $mapping = $self->sysop_load_menu($row, $file);
    print locate($row, 1), cldown, $mapping->{'TEXT'};
    delete($mapping->{'TEXT'});
    my ($key_exit) = (keys %{$mapping});
    my @choices = qw( 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y );
    my $key;
    my @responses;
    my $sth = $self->{'dbh'}->prepare('SELECT * FROM users_view WHERE access_level=?');
    $sth->execute('USER');
    my $user_row;

    while ($user_row = $sth->fetchrow_hashref()) {
        push(@responses, $user_row);
    }
    $sth->finish();

    $self->{'debug'}->DEBUGMAX(\@responses);
    my ($wsize, $hsize, $wpixels, $hpixels) = GetTerminalSize();
    while ($user_row = pop(@responses)) {
        do {
            my $valsize = 1;
            foreach my $fld (keys %{$user_row}) {
                $valsize = max($valsize, length($user_row->{$fld}));
            }
            $valsize = min($valsize, $wsize - 29);
            my $table = Text::SimpleTable->new(6, 16, $valsize);
            $table->row('CHOICE', 'FIELD', 'VALUE');
            $table->hr();
            my $count = 0;
            my %choice;
            foreach my $field (@{ $self->{'SYSOP ORDER DETAILED'} }) {
                if ($field =~ /_time|fullname|_category|id/) {
                    $table->row(' ', $field, $user_row->{$field} . '');
                } else {
                    if ($user_row->{$field} =~ /^(0|1)$/) {
                        $table->row($choices[$count], $field, $self->sysop_true_false($user_row->{$field}, 'YN'));
                    } elsif ($field eq 'access_level') {
                        $table->row($choices[$count], $field, $user_row->{$field} . ' - USER, VETERAN, JUNIOR SYSOP, SYSOP');
                    } elsif ($field eq 'date_format') {
                        $table->row($choices[$count], $field, $user_row->{$field} . ' - YEAR/MONTH/DAY, MONTH/DAY/YEAR, DAY/MONTH/YEAR');
                    } elsif ($field eq 'baud_rate') {
                        $table->row($choices[$count], $field, $user_row->{$field} . ' - 300, 600, 1200, 2400, 4800, 9600, 19200, FULL');
                    } elsif ($field eq 'text_mode') {
                        $table->row($choices[$count], $field, $user_row->{$field} . ' - ASCII, ANSI, ATASCII, PETSCII');
                    } elsif ($field eq 'timeout') {
                        $table->row($choices[$count], $field, $user_row->{$field} . ' - Minutes');
                    } else {
                        $table->row($choices[$count], $field, $user_row->{$field} . '');
                    }
                    $count++ if ($key_exit eq $choices[$count]);
                    $choice{ $choices[$count] } = $field;
                    $count++;
                } ## end else [ if ($field =~ /_time|fullname|_category|id/)]
            } ## end foreach my $field (@{ $self...})
            my $tbl = $table->round('BRIGHT CYAN')->draw();
            while ($tbl =~ / (CHOICE|FIELD|VALUE|No|Yes|USER. VETERAN. JUNIOR SYSOP. SYSOP|YEAR.MONTH.DAY, MONTH.DAY.YEAR, DAY.MONTH.YEAR|300. 600. 1200. 2400. 4800. 9600. 19200. FULL|ASCII. ANSI. ATASCII. PETSCII|Minutes) /) {
                my $ch = $1;
                my $new;
                if ($ch =~ /Yes/) {
                    $new = '[% GREEN %]' . $ch . '[% RESET %]';
                } elsif ($ch =~ /No/) {
                    $new = '[% RED %]' . $ch . '[% RESET %]';
                } elsif ($ch =~ /CHOICE|FIELD|VALUE/) {
                    $new = '[% BRIGHT YELLOW %]' . $ch . '[% RESET %]';
                } else {
                    $new = '[% RGB 50,50,150 %]' . $ch . '[% RESET %]';
                }
                $tbl =~ s/$ch/$new/g;
            } ## end while ($tbl =~ / (CHOICE|FIELD|VALUE|No|Yes|USER. VETERAN. JUNIOR SYSOP. SYSOP|YEAR.MONTH.DAY, MONTH.DAY.YEAR, DAY.MONTH.YEAR|300. 600. 1200. 2400. 4800. 9600. 19200. FULL|ASCII. ANSI. ATASCII. PETSCII|Minutes) /)
            $self->sysop_output('[% CLS %]' . $tbl . "\n");
            $self->sysop_show_choices($mapping);
            $self->sysop_prompt('Choose');
            do {
                $key = uc($self->sysop_keypress());
            } until ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' =~ /$key/i);
            if ($key !~ /$key_exit/i) {
                print 'Edit > (', $choice{$key}, ' = ', $user_row->{ $choice{$key} }, ') > ';
                my $new = $self->sysop_get_line(ECHO, 1 + $self->{'SYSOP FIELD TYPES'}->{ $choice{$key} }->{'max'}, $user_row->{ $choice{$key} });
                unless ($new eq '') {
                    $new =~ s/^(Yes|On)$/1/i;
                    $new =~ s/^(No|Off)$/0/i;
                }
                $user_row->{ $choice{$key} } = $new;
                if ($key =~ /prefer_nickname|view_files|upload_files|download_files|remove_files|read_message|post_message|remove_message|sysop/) {
                    my $sth = $self->{'dbh'}->prepare('UPDATE permissions SET ' . choice { $key } . '=? WHERE id=?');
                    $sth->execute($new, $user_row->{'id'});
                    $sth->finish();
                } else {
                    my $sth = $self->{'dbh'}->prepare('UPDATE users SET ' . $choice{$key} . '=? WHERE id=?');
                    $sth->execute($new, $user_row->{'id'});
                    $sth->finish();
                }
            } else {
                print "BACK\n";
            }
        } until ($key =~ /$key_exit/i);
    } ## end while ($user_row = pop(@responses...))
    $self->{'debug'}->DEBUG(['End SysOp User Edit']);
    return (TRUE);
} ## end sub sysop_new_user_edit

sub sysop_user_add {
    my $self = shift;
    my $row  = shift;
    my $file = shift;

    $self->{'debug'}->DEBUG(['Start SysOp User Add']);
    my $flags_default = $self->{'flags_default'};
    my $mapping       = $self->sysop_load_menu($row, $file);
    print locate($row, 1), cldown, $mapping->{'TEXT'};
    my $table = Text::SimpleTable->new(15, 150);
    my $user_template;
    my @tmp = grep(!/id|banned|fullname|_time|max_|_category/, @{ $self->{'SYSOP ORDER DETAILED'} });
    push(@tmp, 'password');

    foreach my $name (@tmp) {
        my $size = max(3, $self->{'SYSOP FIELD TYPES'}->{$name}->{'max'});
        if ($name eq 'timeout') {
            $table->row($name, '_' x $size . ' - Minutes');
        } elsif ($name eq 'baud_rate') {
            $table->row($name, '_' x $size . ' - 300 or 600 or 1200 or 2400 or 4800 or 9600 or 19200 or FULL');
        } elsif ($name =~ /username|given|family|password/) {
            if ($name eq 'given') {
                $table->row("$name (first)", '_' x $size . ' - Cannot be empty');
            } elsif ($name eq 'family') {
                $table->row("$name (last)", '_' x $size . ' - Cannot be empty');
            } else {
                $table->row($name, '_' x $size . ' - Cannot be empty');
            }
        } elsif ($name eq 'date_format') {
            $table->row($name, '_' x $size . ' - YEAR/MONTH/DAY or MONTH/DAY/YEAR or DAY/MONTH/YEAR');
        } elsif ($name eq 'access_level') {
            $table->row($name, '_' x $size . ' - USER or VETERAN or JUNIOR SYSOP or SYSOP');
        } elsif ($name eq 'text_mode') {
            $table->row($name, '_' x $size . ' - ANSI or ASCII or ATASCII or PETSCII');
        } elsif ($name eq 'birthday') {
            $table->row($name, '_' x $size . ' - YEAR-MM-DD');
        } elsif ($name =~ /(prefer_nickname|_files|_message|sysop|fortunes)/) {
            $table->row($name, '_' x $size . ' - Yes/No or True/False or On/Off or 1/0');
        } elsif ($name =~ /location|retro_systems|accomplishments/) {
            $table->row($name, '_' x ($self->{'SYSOP FIELD TYPES'}->{$name}->{'max'}));
        } else {
            $table->row($name, '_' x $size);
        }

lib/BBS/Universal.pm  view on Meta::CPAN

            'color'        => 'WHITE',
            'access_level' => 'USER',
            'text'         => $result->{'text_mode'},
        };
        $count++;
    } ## end while (my $result = $sth->...)
    $sth->finish();

    $self->show_choices($mapping);
    my $mode = $self->{'USER'}->{'text_mode'};
    if ($mode eq 'ANSI') {
        $self->prompt('([% BRIGHT YELLOW %]' . $self->{'USER'}->{'username'} . '[% RESET %]) ' . 'Choose');
    } elsif ($mode eq 'ATASCII') {
        $self->prompt('(' . $self->{'USER'}->{'username'} . ') ' . 'Choose');
    } elsif ($mode eq 'PETSCII') {
        $self->prompt('([% YELLOW %]' . $self->{'USER'}->{'username'} . '[% RESET %]) ' . 'Choose');
    } else {
        $self->prompt('(' . $self->{'USER'}->{'username'} . ') ' . 'Choose');
    }
    my $key;
    do {
        $key = uc($self->get_key(SILENT, FALSE));
    } until (exists($mapping->{$key}) || $key eq chr(3) || !$self->is_connected());
    $self->output($mapping->{$key}->{'command'} . "\n");
    unless ($key eq 'Z' || $key eq chr(3)) {
        my $command = $mapping->{$key}->{'command'};
        my $sth     = $self->{'dbh'}->prepare('UPDATE users SET text_mode=(SELECT id FROM text_modes WHERE text_mode=?) WHERE id=?');
        $sth->execute($command, $self->{'USER'}->{'id'});
        $sth->finish;
        $self->{'USER'}->{'text_mode'} = $command;
        $self->{'debug'}->DEBUG(["  Text Mode:  $command"]);
    } ## end unless ($key eq 'Z' || $key...)
    $self->{'debug'}->DEBUG(['Start Users Update Text Mode']);
    return (TRUE);
} ## end sub users_update_text_mode

sub users_load {
    my $self     = shift;
    my $username = shift;
    my $password = shift;

    $self->{'debug'}->DEBUG(['Start Users Load']);
    my $sth;
    if ($self->{'sysop'}) {
        $sth = $self->{'dbh'}->prepare('SELECT * FROM users_view WHERE username=?');
        $sth->execute($username);
    } else {
        $sth = $self->{'dbh'}->prepare('SELECT * FROM users_view WHERE username=? AND password=SHA2(?,512)');
        $sth->execute($username, $password);
    }
    my $results  = $sth->fetchrow_hashref();
    my $response = FALSE;
    if (defined($results)) {
        $self->{'USER'} = $results;
        delete($self->{'USER'}->{'password'});
        foreach my $field (    # For numeric values
            qw(
            show_email
            prefer_nickname
            view_files
            upload_files
            download_files
            remove_files
            read_message
            post_message
            remove_message
            play_fortunes
            banned
            sysop
            )
        ) {
            $self->{'USER'}->{$field} = 0 + $self->{'USER'}->{$field};
        } ## end foreach my $field (  qw( show_email...))
        $response = TRUE;
    } ## end if (defined($results))
    $self->{'debug'}->DEBUG(['End Users Load']);
    return ($response);
} ## end sub users_load

sub users_get_date {
    my $self     = shift;
    my $old_date = shift;

    $self->{'debug'}->DEBUG(['Start User Get Date']);
    my $response;
    if ($old_date =~ / /) {
        my $time;
        ($old_date, $time) = split(/ /, $old_date);
        my ($year, $month, $day) = split(/-/, $old_date);
        my $date = $self->{'USER'}->{'date_format'};
        $date =~ s/YEAR/$year/;
        $date =~ s/MONTH/$month/;
        $date =~ s/DAY/$day/;
        $response = "$date $time";
    } else {
        my ($year, $month, $day) = split(/-/, $old_date);
        my $date = $self->{'USER'}->{'date_format'};
        $date =~ s/YEAR/$year/;
        $date =~ s/MONTH/$month/;
        $date =~ s/DAY/$day/;
        $response = $date;
    } ## end else [ if ($old_date =~ / /) ]
    $self->{'debug'}->DEBUG(['End User Get Date']);
    return ($response);
} ## end sub users_get_date

sub users_list {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start Users List']);
    my $sth = $self->{'dbh'}->prepare(
        q{
              SELECT username,
                     fullname,
                     nickname,
                     accomplishments,
                     retro_systems,
                     birthday,
                     prefer_nickname,
                     location
                FROM users_view

lib/BBS/Universal.pm  view on Meta::CPAN

        }
    } ## end while (my $results = $sth...)
    $sth->finish;
    my $text;
    my $mode = $self->{'USER'}->{'text_mode'};
    if ($mode eq 'ANSI') {
        $text = $table->boxes2('GREEN')->draw();
        foreach my $orig ('USERNAME', 'NICKNAME', 'FULLNAME', 'LOCATION', 'RETRO SYSTEMS', 'BDAY', 'ACCOMPLISHMENTS') {
            my $ch = '[% BRIGHT YELLOW %]' . $orig . '[% RESET %]';
            $text =~ s/$orig/$ch/gs;
        }
    } elsif ($mode eq 'ATASCII') {
        $text = $self->color_border($table->boxes->draw(), '');
    } elsif ($mode eq 'PETSCII') {
        $text = $table->boxes->draw();
        foreach my $orig ('USERNAME', 'NICKNAME', 'FULLNAME', 'LOCATION', 'RETRO SYSTEMS', 'BDAY', 'ACCOMPLISHMENTS') {
            my $ch = '[% YELLOW %]' . $orig . '[% RESET %]';
            $text =~ s/$orig/$ch/gs;
        }
        $text = $self->color_border($text, 'GREEN');
    } else {
        $text = $table->draw();
    }
    $self->{'debug'}->DEBUG(['End Users List']);
    return ($text);
} ## end sub users_list

sub users_add {
    my $self          = shift;
    my $user_template = shift;

    $self->{'debug'}->DEBUG(['Start Users Add']);
    $self->{'debug'}->DEBUGMAX([$user_template]);
    $self->{'dbh'}->begin_work;
    my $sth = $self->{'dbh'}->prepare(
        q{
            INSERT INTO users (
                username,
                given,
                family,
                nickname,
                email,
                accomplishments,
                retro_systems,
                birthday,
                location,
                baud_rate,
                text_mode,
                password)
              VALUES (?,?,?,?,?,?,?,DATE(?),?,?,(SELECT text_modes.id FROM text_modes WHERE text_modes.text_mode=?),SHA2(?,512))
        }
    );
    $sth->execute($user_template->{'username'}, $user_template->{'given'}, $user_template->{'family'}, $user_template->{'nickname'}, $user_template->{'email'}, $user_template->{'accomplishments'}, $user_template->{'retro_systems'}, $user_template->{'...
    $sth->finish;
    $sth = $self->{'dbh'}->prepare(
        q{
            INSERT INTO permissions (
                id,
                prefer_nickname,
                view_files,
                upload_files,
                download_files,
                remove_files,
                read_message,
                show_email,
                post_message,
                remove_message,
                sysop,
                play_fortunes,
                timeout)
              VALUES (LAST_INSERT_ID(),?,?,?,?,?,?,?,?,?,?,?,?,?);
        }
    );
    $sth->execute($user_template->{'prefer_nickname'}, $user_template->{'view_files'}, $user_template->{'upload_files'}, $user_template->{'download_files'}, $user_template->{'remove_files'}, $user_template->{'read_message'}, $user_template->{'show_em...
    my $response;

    if ($self->{'dbh'}->err) {
        $self->{'dbh'}->rollback;
        $sth->finish();
        $response = FALSE;
    } else {
        $self->{'dbh'}->commit;
        $sth->finish();
        $response = TRUE;
    }
    $self->{'debug'}->DEBUG(['End Users Add']);
    return ($response);
} ## end sub users_add

sub users_delete {
    my $self = shift;
    my $id   = shift;

    $self->{'debug'}->DEBUG(['Start Users Delete']);
    if ($id == 1) {
        $self->{'debug'}->ERROR(['  Attempt to delete SysOp user']);
        return (FALSE);
    }
    $self->{'debug'}->WARNING(["  Delete user $id"]);
    $self->{'dbh'}->begin_work();
    my $sth = $self->{'dbh'}->prepare('DELETE FROM permissions WHERE id=?');
    $sth->execute($id);
    if ($self->{'dbh'}->err) {
        $self->{'debug'}->ERROR([$self->{'dbh'}->errstr]);
        $self->{'dbh'}->rollback();
        $sth->finish();
        $self->{'debug'}->DEBUG(['   End Users Delete']);
        return (FALSE);
    } else {
        $sth->finish();
        $sth = $self->{'dbh'}->prepare('DELETE FROM users WHERE id=?');
        $sth->execute($id);
        if ($self->{'dbh'}->err) {
            $self->{'debug'}->ERROR([$self->{'dbh'}->errstr]);
            $self->{'dbh'}->rollback();
            $sth->finish();
            $self->{'debug'}->DEBUG(['   End Users Delete']);
            return (FALSE);
        } else {
            $self->{'dbh'}->commit();
            $sth->finish();
            $self->{'debug'}->DEBUG(['   End Users Delete']);
            return (TRUE);
        } ## end else [ if ($self->{'dbh'}->err)]
    } ## end else [ if ($self->{'dbh'}->err)]
    $self->{'debug'}->DEBUG(['End Users Delete']);
} ## end sub users_delete

sub users_file_category {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start Users File Category']);
    my $sth = $self->{'dbh'}->prepare('SELECT description FROM file_categories WHERE id=?');
    $sth->execute($self->{'USER'}->{'file_category'});

lib/BBS/Universal.pm  view on Meta::CPAN

    $sth->execute($self->{'USER'}->{'rss_category'});
    my ($category) = ($sth->fetchrow_array());
    $sth->finish();
    $self->{'debug'}->DEBUG(['End Users RSS Category']);
    return ($category);
} ## end sub users_rss_category

sub users_find {
    my $self = shift;
    $self->{'debug'}->DEBUG(['Start Users Find']);
    $self->{'debug'}->DEBUG(['End Users Find']);
    return (TRUE);
} ## end sub users_find

sub users_count {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start Users Count']);
    my $sth = $self->{'dbh'}->prepare('SELECT COUNT(*) FROM users');
    $sth->execute();
    my ($count) = ($sth->fetchrow_array());
    $sth->finish();
    $self->{'debug'}->DEBUG(['End Users Count']);
    return ($count);
} ## end sub users_count

sub users_info {
    my $self = shift;

    $self->{'debug'}->DEBUG(['Start Users Info']);
    my $table;
    my $text  = '';
    my $width = 1;

    foreach my $field (keys %{ $self->{'USER'} }) {
        $width = max($width, length($self->{'USER'}->{$field}));
    }

    my $columns = $self->{'USER'}->{'max_columns'};
    $self->{'debug'}->DEBUG(["  $columns Columns"]);
    if ($columns <= 40) {
        $table = sprintf('%-15s=%-25s', 'FIELD', 'VALUE') . "\n";
        $table .= '-' x $self->{'USER'}->{'max_columns'} . "\n";
        $table .= sprintf('%-15s=%-25s', 'ACCOUNT NUMBER',  $self->{'USER'}->{'id'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'USERNAME',        $self->{'USER'}->{'username'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'FULL NAME',       $self->{'USER'}->{'fullname'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'NICKNAME',        $self->{'USER'}->{'nickname'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'EMAIL',           $self->{'USER'}->{'email'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'DATE FORMAT',     $self->{'USER'}->{'date_format'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'SCREEN',          $self->{'USER'}->{'max_columns'} . 'x' . $self->{'USER'}->{'max_rows'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'BIRTHDAY',        $self->users_get_date($self->{'USER'}->{'birthday'})) . "\n";
        $table .= sprintf('%-15s=%-25s', 'LOCATION',        $self->{'USER'}->{'location'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'BAUD RATE',       $self->{'USER'}->{'baud_rate'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'LAST LOGIN',      $self->{'USER'}->{'login_time'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'LAST LOGOUT',     $self->{'USER'}->{'logout_time'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'TEXT MODE',       $self->{'USER'}->{'text_mode'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'IDLE TIMEOUT',    $self->{'USER'}->{'timeout'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'SHOW EMAIL',      $self->yes_no($self->{'USER'}->{'show_email'},      FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'PREFER NICKNAME', $self->yes_no($self->{'USER'}->{'prefer_nickname'}, FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'VIEW FILES',      $self->yes_no($self->{'USER'}->{'view_files'},      FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'UPLOAD FILES',    $self->yes_no($self->{'USER'}->{'upload_files'},    FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'DOWNLOAD FILES',  $self->yes_no($self->{'USER'}->{'download_files'},  FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'REMOVE FILES',    $self->yes_no($self->{'USER'}->{'remove_files'},    FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'READ MESSAGES',   $self->yes_no($self->{'USER'}->{'read_message'},    FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'POST MESSAGES',   $self->yes_no($self->{'USER'}->{'post_message'},    FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'REMOVE MESSAGES', $self->yes_no($self->{'USER'}->{'remove_message'},  FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'PLAY FORTUNES',   $self->yes_no($self->{'USER'}->{'play_fortunes'},   FALSE)) . "\n";
        $table .= sprintf('%-15s=%-25s', 'ACCESS LEVEL',    $self->{'USER'}->{'access_level'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'RETRO SYSTEMS',   $self->{'USER'}->{'retro_systems'}) . "\n";
        $table .= sprintf('%-15s=%-25s', 'ACCOMPLISHMENTS', $self->{'USER'}->{'accomplishments'}) . "\n";
    } elsif ((($width + 22) * 2) <= $columns) {
        $table = Text::SimpleTable->new(15, $width, 15, $width);
        $table->row('FIELD', 'VALUE', 'FIELD', 'VALUE');
        $table->hr();
        $table->row('ACCOUNT NUMBER',  $self->{'USER'}->{'id'},                                    'USERNAME',        $self->{'USER'}->{'username'});
        $table->row('FULLNAME',        $self->{'USER'}->{'fullname'},                              'NICKNAME',        $self->{'USER'}->{'nickname'});
        $table->row('EMAIL',           $self->{'USER'}->{'email'},                                 'SCREEN',          $self->{'USER'}->{'max_columns'} . 'x' . $self->{'USER'}->{'max_rows'});
        $table->row('BIRTHDAY',        $self->users_get_date($self->{'USER'}->{'birthday'}),       'LOCATION',        $self->{'USER'}->{'location'});
        $table->row('BAUD RATE',       $self->{'USER'}->{'baud_rate'},                             'LAST LOGIN',      $self->users_get_date($self->{'USER'}->{'login_time'}));
        $table->row('DATE FORMAT',     $self->{'USER'}->{'date_format'},                           'LAST LOGOUT',     $self->users_get_date($self->{'USER'}->{'logout_time'}));
        $table->row('IDLE TIMEOUT',    $self->{'USER'}->{'timeout'},                               'TEXT MODE',       $self->{'USER'}->{'text_mode'});
        $table->row('PREFER NICKNAME', $self->yes_no($self->{'USER'}->{'prefer_nickname'}, FALSE), 'VIEW FILES',      $self->yes_no($self->{'USER'}->{'view_files'}, FALSE));
        $table->row('UPLOAD FILES',    $self->yes_no($self->{'USER'}->{'upload_files'}, FALSE),    'DOWNLOAD FILES',  $self->yes_no($self->{'USER'}->{'download_files'}, FALSE));
        $table->row('REMOVE FILES',    $self->yes_no($self->{'USER'}->{'remove_files'}, FALSE),    'READ MESSAGES',   $self->yes_no($self->{'USER'}->{'read_message'}, FALSE));
        $table->row('POST MESSAGES',   $self->yes_no($self->{'USER'}->{'post_message'}, FALSE),    'REMOVE MESSAGES', $self->yes_no($self->{'USER'}->{'remove_message'}, FALSE));
        $table->row('SHOW EMAIL',      $self->yes_no($self->{'USER'}->{'show_email'}, FALSE),      'ACCESS LEVEL',    $self->{'USER'}->{'access_level'});
		$table->row('PLAY FORTUNES',   $self->yes_no($self->{'USER'}->{'play_fortunes'}, FALSE),   'ACCOMPLISHMENTS', $self->{'USER'}->{'accomplishments'});
		$table->row('RETRO SYSTEMS',   $self->{'USER'}->{'retro_systems'},'','');
    } else {
        $width = min($width + 7, $self->{'USER'}->{'max_columns'} - 7);
        $table = Text::SimpleTable->new(15, $width);
        $table->row('FIELD', 'VALUE');
        $table->hr();
        $table->row('ACCOUNT NUMBER',  $self->{'USER'}->{'id'});
        $table->row('USERNAME',        $self->{'USER'}->{'username'});
        $table->row('FULLNAME',        $self->{'USER'}->{'fullname'});
        $table->row('NICKNAME',        $self->{'USER'}->{'nickname'});
        $table->row('EMAIL',           $self->{'USER'}->{'email'});
        $table->row('DATE FORMAT',     $self->{'USER'}->{'date_format'});
        $table->row('SCREEN',          $self->{'USER'}->{'max_columns'} . 'x' . $self->{'USER'}->{'max_rows'});
        $table->row('BIRTHDAY',        $self->users_get_date($self->{'USER'}->{'birthday'}));
        $table->row('LOCATION',        $self->{'USER'}->{'location'});
        $table->row('BAUD RATE',       $self->{'USER'}->{'baud_rate'});
        $table->row('LAST LOGIN',      $self->{'USER'}->{'login_time'});
        $table->row('LAST LOGOUT',     $self->{'USER'}->{'logout_time'});
        $table->row('TEXT MODE',       $self->{'USER'}->{'text_mode'});
        $table->row('IDLE TIMEOUT',    $self->{'USER'}->{'timeout'});
        $table->row('SHOW EMAIL',      $self->yes_no($self->{'USER'}->{'show_email'},      FALSE));
        $table->row('PREFER NICKNAME', $self->yes_no($self->{'USER'}->{'prefer_nickname'}, FALSE));
        $table->row('VIEW FILES',      $self->yes_no($self->{'USER'}->{'view_files'},      FALSE));
        $table->row('UPLOAD FILES',    $self->yes_no($self->{'USER'}->{'upload_files'},    FALSE));
        $table->row('DOWNLOAD FILES',  $self->yes_no($self->{'USER'}->{'download_files'},  FALSE));
        $table->row('REMOVE FILES',    $self->yes_no($self->{'USER'}->{'remove_files'},    FALSE));
        $table->row('READ MESSAGES',   $self->yes_no($self->{'USER'}->{'read_message'},    FALSE));
        $table->row('POST MESSAGES',   $self->yes_no($self->{'USER'}->{'post_message'},    FALSE));
        $table->row('REMOVE MESSAGES', $self->yes_no($self->{'USER'}->{'remove_message'},  FALSE));
        $table->row('PLAY FORTUNES',   $self->yes_no($self->{'USER'}->{'play_fortunes'},   FALSE));
        $table->row('ACCESS LEVEL',    $self->{'USER'}->{'access_level'});
        $table->row('RETRO SYSTEMS',   $self->{'USER'}->{'retro_systems'});
        $table->row('ACCOMPLISHMENTS', $self->{'USER'}->{'accomplishments'});
    } ## end else [ if ($columns <= 40) ]

    my $mode = $self->{'USER'}->{'text_mode'};
    if ($mode eq 'ATASCII') {
        $text = $self->color_border($table->boxes->draw(), 'WHITE');
    } elsif ($mode eq 'ANSI') {
        $text = $table->boxes2('RGB 0,90,190')->draw();
        my $no    = colored(['red'],           'NO');
        my $yes   = colored(['green'],         'YES');
        my $field = colored(['bright_yellow'], 'FIELD');
        my $va    = colored(['bright_yellow'], 'VALUE');
        $text =~ s/ FIELD / $field /gs;
        $text =~ s/ VALUE / $va /gs;
        $text =~ s/ NO / $no /gs;
        $text =~ s/ YES / $yes /gs;

        foreach $field ('PLAY FORTUNES', 'ACCESS LEVEL', 'SUFFIX', 'ACCOUNT NUMBER', 'USERNAME', 'FULLNAME', 'SCREEN', 'BIRTHDAY', 'LOCATION', 'BAUD RATE', 'LAST LOGIN', 'LAST LOGOUT', 'TEXT MODE', 'IDLE TIMEOUT', 'RETRO SYSTEMS', 'ACCOMPLISHMENTS', ...
            my $ch = colored(['yellow'], $field);
            $text =~ s/$field/$ch/gs;
        }
    } elsif ($mode eq 'PETSCII') {
        $text = $table->boxes->draw();
        my $no    = '[% RED %]NO[% RESET %]';
        my $yes   = '[% GREEN %]YES[% RESET %]';
        my $field = '[% YELLOW %]FIELD[% RESET %]';
        my $va    = '[% YELLOW %]VALUE[% RESET %]';
        $text =~ s/ FIELD / $field /gs;
        $text =~ s/ VALUE / $va /gs;
        $text =~ s/ NO / $no /gs;
        $text =~ s/ YES / $yes /gs;

        foreach $field ('PLAY FORTUNES', 'ACCESS LEVEL', 'SUFFIX', 'ACCOUNT NUMBER', 'USERNAME', 'FULLNAME', 'SCREEN', 'BIRTHDAY', 'LOCATION', 'BAUD RATE', 'LAST LOGIN', 'LAST LOGOUT', 'TEXT MODE', 'IDLE TIMEOUT', 'RETRO SYSTEMS', 'ACCOMPLISHMENTS', ...
            my $ch = '[% BROWN %]' . $field . '[% RESET %]';
            $text =~ s/$field/$ch/gs;
        }
        $text = $self->color_border($text, 'BLUE');
    } else {
        $text = $table->draw();
    }
    $self->{'debug'}->DEBUG(['End Users Info']);
    return ($text);
} ## end sub users_info

 

# MANUAL IMPORT HERE #

1;



( run in 0.759 second using v1.01-cache-2.11-cpan-b16cb0d3907 )