+
+sub get_frame_file {
+ (my $frame, my $frame_data, my $settings) = @_;
+ my $file;
+ my $pattern;
+
+ if ($frame_data->{'frame'} ne '') {
+ $file = $frame_data->{'frame'};
+ }
+ else {
+ $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
+ $file = sprintf(
+ $pattern,
+ int($frame), $frame_data->{'ext'}
+ );
+ }
+ return validate_filename($file);
+}
+
+sub get_page_file {
+ (my $frame, my $frame_data, my $settings) = @_;
+ my $file;
+ my $pattern;
+
+ if ($frame == 0) {
+ return 'index.htm';
+ }
+ if ($frame_data->{'page'} ne '') {
+ $file = $frame_data->{'page'};
+ }
+ else {
+ $pattern = validate_filename($settings->{'frame'}, '%d.%ext');
+ $file = sprintf(
+ $pattern,
+ int($frame), 'htm'
+ );
+ }
+ return validate_filename($file);
+}
+
+sub validate_filename {
+ (my $filename, my $fallback) = @_;
+ if ($fallback eq '') {
+ $fallback = '';
+ }
+
+ # TODO: more checks
+
+ if ($filename =~ /^\./) {
+ return $fallback;
+ }
+ if (index($filename, PATH_SEPARATOR()) >= 0) {
+ return $fallback;
+ }
+ return $filename;
+}
+
+sub validate_frame_data {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'ongtime'} ne '') {
+ $data{'ongtime'} = int($data{'ongtime'});
+ }
+ if ($data{'timer'} ne '') {
+ $data{'timer'} = int($data{'timer'});
+ }
+ if ($data{'width'} ne '') {
+ $data{'width'} = int($data{'width'});
+ }
+ if ($data{'height'} ne '') {
+ $data{'height'} = int($data{'height'});
+ }
+ if ($data{'page'} ne '') {
+ $data{'page'} = validate_filename($data{'page'});
+ }
+ if ($data{'frame'} ne '') {
+ $data{'frame'} = validate_filename($data{'frame'});
+ }
+
+ return %data;
+}
+
+sub validate_settings {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'ongtime'} ne '') {
+ $data{'ongtime'} = int($data{'ongtime'});
+ }
+ if ($data{'dynamicongtime'} ne '') {
+ $data{'dynamicongtime'} = int($data{'dynamicongtime'});
+ }
+ if ($data{'firstongtime'} ne '') {
+ $data{'firstongtime'} = int($data{'firstongtime'});
+ }
+ if ($data{'last'} ne '') {
+ $data{'last'} = int($data{'last'});
+ }
+ $data{'frame'} = validate_filename($data{'frame'}, '%d.%s');
+
+ return %data;
+}
+
+sub validate_state {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'state'} ne '') {
+ $data{'state'} = int($data{'state'});
+ }
+ if ($data{'last'} ne '') {
+ $data{'last'} = int($data{'last'});
+ }
+ if ($data{'nextong'} ne '') {
+ $data{'nextong'} = int($data{'nextong'});
+ }
+
+ return %data;
+}
+
+sub validate_words_list {
+ (my $data_in, my $not_list) = @_;
+ my %data = %$data_in;
+
+ if ($data{'ongtime'} ne '') {
+ $data{'ongtime'} = int($data{'ongtime'});
+ }
+
+ if ($not_list) {
+ my $id_list = '';
+ foreach my $ID (split(/\r?\n/, $data{'content'})) {
+ $ID = validate_filename($ID);
+ if ($ID ne '') {
+ $id_list .= $ID."\n";
+ }
+ }
+ $data{'content'} = $id_list;
+ }
+ else {
+ my @id_list;
+ foreach my $ID (@{$data{'content'}}) {
+
+ $ID = validate_filename($ID);
+ if ($ID ne '') {
+ push @id_list, $ID;
+ }
+ }
+ $data{'content'} = [@id_list];
+ }
+
+ return %data;
+}
+
+sub validate_words {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'posttime'} ne '') {
+ $data{'posttime'} = int($data{'posttime'});
+ }
+ if ($data{'edittime'} ne '') {
+ $data{'edittime'} = int($data{'edittime'});
+ }
+
+ return %data;
+}
+
+sub validate_story {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'id'} ne '') {
+ $data{'id'} = int($data{'id'});
+ }
+ if ($data{'pass'} ne '') {
+ $data{'pass'} = int($data{'pass'});
+ }
+ if ($data{'state'} ne '') {
+ $data{'state'} = int($data{'state'});
+ }
+
+ return %data;
+}
+
+sub validate_goto {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ foreach my $key (keys %data) {
+ if ($key =~ /^ongtime-([0-9]+)$/) {
+ my $new_key = 'ongtime-'.int($1);
+ $data{$new_key} = int($data{$key});
+ if ($new_key != $key) {
+ delete $data{$key};
+ }
+ }
+ }
+
+ return %data;
+}
+
+sub validate_attachment {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'frame'} ne '') {
+ $data{'frame'} = int($data{'frame'});
+ }
+ $data{'filename'} = validate_filename($data{'filename'});
+
+ return %data;
+}
+
+sub validate_coincidence {
+ (my $data_in) = @_;
+ my %data = %$data_in;
+
+ if ($data{'server'} ne '') {
+ $data{'server'} = int($data{'server'});
+ }
+
+ return %data;
+}
+
+sub read_frame_data {
+ (my $f, my $default) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # frame ID
+ $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
+ }
+ elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
+ $file = DATA_NOACCESS_PATH();
+ }
+ elsif ($f =~ /^d(efault)?$/) { # default
+ $file = DATA_DEFAULT_PATH();
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_DEFAULT_PATH();
+ }
+
+ %data = read_data_file($file);
+ if (ref ($default)) {
+ %data = merge_settings($default, \%data);
+ }
+ elsif ($default ne '') {
+ my %default_data = read_data_file(DATA_DEFAULT_PATH());
+ %data = merge_settings(\%default_data, \%data);
+ }
+
+ return validate_frame_data(\%data);
+}
+
+sub write_frame_data {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # frame ID
+ $file = join_path(PATH_SEPARATOR(), DATA_PATH(), int($&));
+ }
+ elsif ($f =~ /^(c(frt)?)|(noaccess)$/) { # CFRT (no access)
+ return 0; # forbidden
+ }
+ elsif ($f =~ /^d(efault)?$/) { # default
+ return 0; # forbidden
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ return 0; # forbidden
+ }
+
+ my %_data = validate_frame_data($data);
+
+ return write_data_file($file, \%_data);
+}
+
+sub read_default {
+ return read_frame_data('default');
+}
+
+sub read_noaccess {
+ (my $default) = @_;
+ return read_frame_data('noaccess', $default);
+}
+
+sub read_settings {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_SETTINGS_PATH();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_settings(\%data);
+}
+
+sub read_state {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_STATE_PATH();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_state(\%data);
+}
+
+sub write_state {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = PERL_DATA_STATE_PATH();
+ }
+
+ my %_data = validate_state($data);
+
+ return write_data_file($file, \%_data);
+}
+
+sub read_words_list {
+ (my $f, my $header_only, my $not_list) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # frame ID
+ $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else { # which frame ???
+ return ('posts' => 0);
+ }
+
+ %data = read_data_file(
+ $file,
+ '', # encoding
+ 0, # no header
+ $header_only,
+ not $not_list # as list
+ );
+
+ return validate_words_list(\%data, $not_list);
+}
+
+sub write_words_list {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # frame ID
+ $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), int($&));
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else { # which frame ???
+ return 0;
+ }
+
+ my %_data = validate_words_list($data);
+
+ return write_data_file(
+ $file, # file
+ \%_data,
+ '', # encoding
+ 0, # no header
+ 0, # header only
+ 1 # as list
+ );
+}
+
+sub read_words {
+ (my $f, my $default) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+\.[0-9\.]+$/) { # post ID
+ $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else { # which post ???
+ return ();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_words(\%data);
+}
+
+sub write_words {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9\.]+$/) { # post ID
+ $file = join_path(PATH_SEPARATOR(), DATA_WORDS_PATH(), $&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else { # which post ???
+ return 0;
+ }
+
+ my %_data = validate_words($data);
+
+ return write_data_file($file, \%_data);
+}
+
+sub read_story {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # story ID
+ $file = DATA_STORY_PATH().int($&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_STORY_PATH();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_story(\%data);
+}
+
+sub write_story {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # story ID
+ $file = DATA_STORY_PATH().int($&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_STORY_PATH();
+ }
+
+ my %_data = validate_story($data);
+
+ return write_data_file($file, \%_data);
+}
+
+sub read_goto {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_LIST_PATH();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_goto(\%data);
+}
+
+sub write_goto {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_LIST_PATH();
+ }
+
+ my %_data = validate_goto($data);
+
+ return write_data_file($file, \%_data);
+}
+
+sub read_attachment {
+ (my $f, my $default) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # attachment ID
+ $file = DATA_ATTACH_PATH().int($&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ return ();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_attachment(\%data);
+}
+
+sub read_coincidence {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_COIN_PATH();
+ }
+
+ %data = read_data_file($file);
+
+ return validate_coincidence(\%data);
+}
+
+sub read_chat {
+ (my $f) = @_;
+ my $file;
+ my %data;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # chat ID
+ $file = DATA_CHAT_PATH().int($&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_CHAT_PATH();
+ }
+
+ return read_data_file($file);
+
+ # no validation
+}
+
+sub write_chat {
+ (my $f, my $data) = @_;
+ my $file;
+
+ if (ref ($f)) { # already open file
+ $file = $f;
+ }
+ elsif ($f =~ /^[0-9]+$/) { # chat ID
+ $file = DATA_CHAT_PATH().int($&);
+ }
+ elsif ($f ne '') { # path
+ $file = $f;
+ }
+ else {
+ $file = DATA_CHAT_PATH();
+ }
+
+ # no validation
+
+ return write_data_file($file, $data);
+}
+
+