home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / FAQ / discus_admin_1357211388 / source / adm-dr.pl < prev    next >
Text File  |  2009-11-06  |  49KB  |  1,349 lines

  1. # FILE: adm-dr.pl
  2. # DESCRIPTION: Data Recovery Interfaces and functions
  3. #-------------------------------------------------------------------------------
  4. # DISCUS COPYRIGHT NOTICE
  5. #
  6. # Discus is copyright (c) 2002 by DiscusWare, LLC, all rights reserved.
  7. # The use of Discus is governed by the Discus License Agreement which is
  8. # available from the Discus WWW site at:
  9. #    http://www.discusware.com/discus/license
  10. #
  11. # Pursuant to the Discus License Agreement, this copyright notice may not be
  12. # removed or altered in any way.
  13. #-------------------------------------------------------------------------------
  14.  
  15. use strict;
  16. use vars qw($GLOBAL_OPTIONS $DCONF $PARAMS);
  17.  
  18. ###
  19. ### DR_admin
  20. ###
  21. ### Controls the Data Recovery utility
  22. ###
  23.  
  24. sub DR_admin {
  25.     my ($FORMref) = @_;
  26.     my $result = check_password($FORMref->{username}, undef, { type_required => 'moderator' }, $FORMref->{'COOKIE'});
  27.     bad_login( { bad_username => 1 } ) if scalar(@{ $result }) == 0;
  28.     bad_login( { superuser_required => 1 } ) if $result->[0]->{user} ne $DCONF->{superuser};
  29.     if ($FORMref->{action} eq "dr_tpc") {
  30.         topic_recovery($FORMref, $result);
  31.         $FORMref->{action} = "data_recovery";
  32.     }
  33.     if ($FORMref->{'action'} eq "dr_reindex") {
  34.         if ($DCONF->{pro} && $DCONF->{tree_as_sql} && $GLOBAL_OPTIONS->{database}) {
  35.             dreq("sql-trem-PRO");
  36.             return sql_reindex_main($FORMref, $result);
  37.         }
  38.         dreq("fcn-indx");
  39.         INDEXING_main({ menuaction => $FORMref->{menu}, position => "start", topic => $FORMref->{topic}, username => $result->[0]->{user} });
  40.     }
  41.     if ($FORMref->{'action'} eq "dr_indexing") {
  42.         dreq("fcn-indx");
  43.         INDEXING_main({ menuaction => $FORMref->{menu}, username => $result->[0]->{user}, position => "continue", tempfile => $FORMref->{tempfile}, counter => $FORMref->{counter} });
  44.     }
  45.     if ($FORMref->{'action'} eq "dr_sorting") {
  46.         dreq("fcn-indx");
  47.         INDEXING_main({ menuaction => $FORMref->{menu}, username => $result->[0]->{user}, position => "sort", counter => $FORMref->{counter} });
  48.         if ($FORMref->{menu} == 1) {
  49.             $FORMref->{spot} = 1;
  50.             page_recovery_master($FORMref, $result);
  51.         }
  52.         $FORMref->{action} = "data_recovery";
  53.         $FORMref->{menu} = 2;
  54.     }
  55.     if ($FORMref->{'action'} eq "dr_sindxfin") {
  56.         dreq("fcn-indx");
  57.         INDEXING_main({ username => $result->[0]->{user}, position => "search", counter => $FORMref->{counter} });
  58.         $FORMref->{action} = "data_recovery";
  59.         $FORMref->{menu} = 3;
  60.     }
  61.     if ($FORMref->{'action'} eq "data_recovery") {
  62.         my $subst = {};
  63.         $subst->{general}->{username} = $result->[0]->{user};
  64.         $subst->{general}->{menu} = defined $FORMref->{menu} ? $FORMref->{menu} : -1;
  65.         $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$result->[0]->{user}&action=data_recovery";
  66.         $subst->{topics} = board_topics(undef, undef, undef, 1, 1);
  67.         if ($FORMref->{menu} == 1) {
  68.             opendir(DIR, "$DCONF->{admin_dir}/backups");
  69.             my @d = grep { /^\d+MP\.TMP$/ } map { $_ } readdir(DIR);
  70.             closedir(DIR);
  71.             my @x = ();
  72.             foreach my $d (@d) {
  73.                 $d =~ /^(\d+)MP\.TMP$/;
  74.                 my $i = {};
  75.                 $i->{file} = $1;
  76.                 my $l = readfile("$DCONF->{admin_dir}/backups/$d", "DR_admin", { no_lock => 1, no_unlock => 1, zero_ok => 1 });
  77.                 $i->{unmoved_count} = ref $l eq 'ARRAY' ? scalar @{$l} : 0;
  78.                 $i->{date_started} = (stat "$DCONF->{admin_dir}/backups/$d")[9];
  79.                 push @x, $i;
  80.             }
  81.             $subst->{movedpages} = \@x;
  82.         }
  83.         if ($FORMref->{menu} == 2 && $FORMref->{topic} != 0) {
  84.             $subst->{general}->{flag_reindex} = 0 + $FORMref->{topic};
  85.         }
  86.         if ($FORMref->{menu} == 5) {
  87.             for (my $i = 2; $i <= 10; $i++) {
  88.                 my $x = join("", "status", $i);
  89.                 $subst->{status}->{$x} = $GLOBAL_OPTIONS->{$x};
  90.             }
  91.         }
  92.         screen_out("dr_main", $subst);
  93.     }
  94.     if ($FORMref->{action} eq "dr_pgr") {
  95.         page_recovery_master($FORMref, $result);
  96.     }
  97.     if ($FORMref->{action} eq "dr_pgs") {
  98.         specific_page_recovery_master($FORMref, $result);
  99.     }
  100.     if ($FORMref->{action} eq "dr_log") {
  101.         log_recovery_master($FORMref, $result);
  102.     }
  103.     if ($FORMref->{action} eq "dr_thumb") {
  104.         dreq("dr_thumb-PRO");
  105.         thumbnail_recovery_master($FORMref, $result);
  106.     }
  107.     if ($FORMref->{action} eq "dr_profiles") {
  108.         dreq("dr_thumb-PRO");
  109.         profile_recovery_master($FORMref, $result);
  110.     }
  111.     if ($FORMref->{action} eq "dr_posts") {
  112.         dreq("dr_posts-PRO");
  113.         post_counter_master($FORMref, $result);
  114.     }
  115.     if ($FORMref->{action} eq "dr_enh") {
  116.         dreq("dr_posts-PRO");
  117.         enhanced_profile_cleaner($FORMref, $result);
  118.     }
  119.     if ($FORMref->{action} eq "dr_mvp") {
  120.         page_move_recovery_master($FORMref, $result);
  121.     }
  122.     if ($FORMref->{'action'} eq "dr_perm_reset") {
  123.         my $subst = {};
  124.         $subst->{topics} = postread_privilege_reset();
  125.         $subst->{general}->{username} = $result->[0]->{user};
  126.         $subst->{general}->{menu} = $FORMref->{menu};
  127.         $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$result->[0]->{user}&action=data_recovery";
  128.         screen_out("dr_main", $subst);
  129.     }
  130. }
  131.  
  132. ###
  133. ### topic_recovery
  134. ###
  135. ### Recovers the list of board topics and other information
  136. ###
  137.  
  138. sub topic_recovery {
  139.     my ($FORMref, $result) = @_;
  140.     dreq("topic-pg");
  141.     my $Q = undef;
  142.     my @topics = ();
  143.     my %t = undef;
  144.     my @added = ();
  145.     if ($FORMref->{topic_rec_type} == 2) {
  146.         $Q = read_topic_page();
  147.         @topics = @{ $Q->{topics} };
  148.         %t = map { $_->{number}, 1 } @topics;
  149.     }
  150.     my @d = @{ directory_list($DCONF->{message_dir}, '/\d+$', 0) };
  151.     push @d, @{ directory_list($DCONF->{secdir}, '/\d+$', 0) } if $DCONF->{pro};
  152.     foreach my $d (@d) {
  153.         if ($d =~ m|/(\d+)$|) {
  154.             next if ! -e "$d/$1.$DCONF->{ext}";
  155.             next if $t{$1};
  156.             push @topics, { number => $1, type => 1 };
  157.             push @added, $1;
  158.         }
  159.     }
  160.     $Q->{topics} = \@topics;
  161.     write_topic_page($Q);
  162.     if (scalar(@added)) {
  163.         dreq("fcn-indx");
  164.         INDEXING_main({ menuaction => 2, position => "start", topic => join(",", @added), username => $result->[0]->{user} });
  165.     }
  166. }
  167.  
  168. ###
  169. ### log_recovery_master
  170. ###
  171. ### Operates the log recovery system
  172. ###
  173.  
  174. sub log_recovery_master {
  175.     my ($FORMref, $result) = @_;
  176.     my $subst = {};
  177.     $subst->{general}->{username} = $result->[0]->{user};
  178.     $FORMref->{tempfile} =~ s/[^\w\-]//g;
  179.     $FORMref->{action} = "dr_log";
  180.     $subst->{general}->{tempfile} = $FORMref->{tempfile};
  181.     log_recovery_0($FORMref, $subst) if $FORMref->{spot} == 0;
  182.     log_recovery_1($FORMref, $subst) if $FORMref->{spot} == 1;
  183.     log_recovery_2($FORMref, $subst) if $FORMref->{spot} == 2;
  184.     log_recovery_3($FORMref, $subst) if $FORMref->{spot} == 3;
  185.     log_recovery_4($FORMref, $subst) if $FORMref->{spot} == 4;
  186.     $subst->{general}->{menu} = 5;
  187.     $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$result->[0]->{user}&action=data_recovery";
  188.     $subst->{topics} = board_topics();
  189.     screen_out("dr_main", $subst);
  190. }
  191.  
  192. ###
  193. ### log_recovery_0
  194. ###
  195. ### Build file lists
  196. ###
  197.  
  198. sub log_recovery_0 {
  199.     my ($FORMref, $subst) = @_;
  200.     dreq("fcn-regn");
  201.     my $X = incremental({ operation => 'build', suffix => 'logr' });
  202.     $subst->{general}->{tempfile} = $X->{tempfile};
  203.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$X->{tempfile}&spot=1&done=0&total=$X->{count}";
  204.     $subst->{'gauge'}->{'operation'} = 16;
  205.     $subst->{'gauge'}->{'description'} = 1601;
  206.     $subst->{'gauge'}->{'percent'} = 0;
  207.     screen_out("gauge", $subst);
  208. }
  209.  
  210. ###
  211. ### log_recovery_1
  212. ###
  213. ### Build all posts into a summary file
  214. ###
  215.  
  216. sub log_recovery_1 {
  217.     my ($FORMref, $subst) = @_;
  218.     dreq("fcn-regn");
  219.     my $i = incremental({ tempfile => $FORMref->{tempfile}, operation => "read" });
  220.     my $R = regeneration_resource_init();
  221.     my $out = join("/", $DCONF->{admin_dir}, "backups", "$FORMref->{tempfile}OUT");
  222.     my $old_topic = 0;
  223.     my $preview_storage = defined $DCONF->{log_preview_length} ? $DCONF->{log_preview_length} : 25;
  224.     lock("*");
  225.     while (my $f = shift @{ $i->{data} }) {
  226.         chomp $f;
  227.         my ($topic, $page) = ($f =~ m|^(\d+)/(\d+)|);
  228.         if ($old_topic == 0) {
  229.             open (OUT, ">> $out-$topic.TMP");
  230.             $old_topic = $topic;
  231.         } elsif ($topic != $old_topic) {
  232.             unshift @{ $i->{data} }, "$f\n";
  233.             last;
  234.         }
  235.         my $pinfo = GetPage($topic, $page, { no_error => 1 });
  236.         next if $pinfo->{head}->{me_number} != $page;
  237.         next if ref $pinfo->{messages} ne 'ARRAY';
  238.         next if scalar @{ $pinfo->{messages} } == 0;
  239.         foreach my $message (@{ $pinfo->{messages} }) {
  240.             my $firstfew = escape(substr(remove_html($message->{text}), 0, $preview_storage));
  241.             my $postby = escape($message->{author});
  242.             my $uid = $message->{email} =~ m|profile=(.*?)-(\w+)| ? join(":", $1, $2) : "";
  243.             print OUT join("\t", $topic, $page, $message->{number}, $message->{'time'}, $uid, $postby, "$firstfew\n");
  244.         }
  245.         $R = regeneration_resource_estimate($R, 1, $pinfo->{general}->{'length'});
  246.         $FORMref->{done}++;
  247.         last if regeneration_reset_trigger($R);
  248.     }
  249.     unlock("*");
  250.     close (OUT);
  251.     my $iw = incremental({ operation => 3, data => $i->{data}, tempfile => $FORMref->{tempfile} });
  252.     if ($iw->{'continue'}) {
  253.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=1&done=$FORMref->{done}&total=$FORMref->{total}";
  254.         $subst->{'gauge'}->{'operation'} = 16;
  255.         $subst->{'gauge'}->{'description'} = 1602;
  256.         $subst->{'gauge'}->{'percent'} = $FORMref->{total} > 0 ? (int(100*$FORMref->{done}/$FORMref->{total})) : 0;
  257.         screen_out("gauge", $subst);
  258.     }
  259.     unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}.TMP";
  260.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=2";
  261.     $subst->{'gauge'}->{'operation'} = 16;
  262.     $subst->{'gauge'}->{'description'} = 1603;
  263.     $subst->{'gauge'}->{'percent'} = 0;
  264.     screen_out("gauge", $subst);
  265. }
  266.  
  267. ###
  268. ### log_recovery_2
  269. ###
  270. ### Compare existing posts to logs
  271. ###
  272.  
  273. sub log_recovery_2 {
  274.     my ($FORMref, $subst) = @_;
  275.     opendir(DIR, "$DCONF->{admin_dir}/backups");
  276.     my @u = grep { /^$FORMref->{tempfile}OUT-(\d+)\.TMP$/ } readdir(DIR);
  277.     closedir(DIR);
  278.     $FORMref->{total} = scalar(@u) if ! $FORMref->{total};
  279.     if ($u[0] ne "") {
  280.         if ($DCONF->{pro} && $GLOBAL_OPTIONS->{database}) {
  281.             dreq("sql-logr-PRO");
  282.             sql_log_recovery_2($FORMref, $subst, $u[0]);
  283.         } else {
  284.             dreq("fcn-logs");
  285.             my $inlog = {};
  286.             my $repair = {};
  287.             my $postchars = {};
  288.             my $topic_save = 0;
  289.             open (FILEU, "< $DCONF->{admin_dir}/backups/$u[0]");
  290.             while (my $z = <FILEU>) {
  291.                 chomp $z;
  292.                 my ($topic, $page, $postnum, $posttime, $uid, $author, $chars) = split(/\t/, $z);
  293.                 if ($postnum > 0) {
  294.                     $topic_save = $topic;
  295.                     if (! scalar keys %{$inlog}) {
  296.                         $inlog->{fileread} = 1;
  297.                         open (LOG, "< $DCONF->{admin_dir}/msg_index/$topic-log.txt");
  298.                         while (my $j = <LOG>) {
  299.                             my ($post, $who, $when, $where, $ip, $host, $chars, $author) = split(/;/, $j, 8);
  300.                             $inlog->{post}->{$post} = 1;
  301.                             $postchars->{$post} = $chars;
  302.                         }
  303.                         close (LOG);
  304.                     }
  305.                     if ($inlog->{post}->{$postnum} && ! $inlog->{done}->{$postnum}) {
  306.                         $inlog->{done}->{$postnum} = 1;
  307.                         if ($postchars->{$postnum} ne $chars) {
  308.                             $repair->{$postnum}->{type} = "fix";
  309.                             $repair->{$postnum}->{postindex} = $postnum;
  310.                             $repair->{$postnum}->{topic} = $topic;
  311.                             $repair->{$postnum}->{page} = $page;
  312.                             $repair->{$postnum}->{firstchars} = $chars;
  313.                         }
  314.                     } elsif ($inlog->{post}->{$postnum} && $inlog->{done}->{$postnum}) {
  315.                         dreq("posting");
  316.                         my $m = get_postindex();
  317.                         $repair->{$postnum}->{type} = "change";
  318.                         $repair->{$postnum}->{postindex} = $m;
  319.                         $repair->{$postnum}->{topic} = $topic;
  320.                         $repair->{$postnum}->{page} = $page;
  321.                         $repair->{$postnum}->{firstchars} = $chars;
  322.                     } else {
  323.                         $repair->{$postnum}->{type} = "add";
  324.                         $repair->{$postnum}->{where} = join("/", $topic, $page);
  325.                         $repair->{$postnum}->{'time'} = $posttime;
  326.                         $repair->{$postnum}->{'username'} = $uid;
  327.                         $repair->{$postnum}->{'remote_addr'} = "";
  328.                         $repair->{$postnum}->{'remote_host'} = "";
  329.                         $repair->{$postnum}->{'firstchars'} = $chars;
  330.                         $repair->{$postnum}->{'poststr'} = $author;
  331.                         $repair->{$postnum}->{'postindex'} = $postnum;
  332.                     }
  333.                     $inlog->{done}->{$postnum} = 1;
  334.                 }
  335.             }
  336.             close (FILEU);
  337.             foreach my $x (keys %{$inlog->{post}}) {
  338.                 $repair->{$x}->{type} = "delete" if ! $inlog->{done}->{$x};
  339.             }
  340.             unlink "$DCONF->{admin_dir}/msg_index/$topic_save-log.out";
  341.             if (scalar keys %{$repair}) {
  342.                 my $dp = {};
  343.                 my $seen = {};
  344.                 my $size = 0;
  345.                 open (LOG_IN, "< $DCONF->{admin_dir}/msg_index/$topic_save-log.txt");
  346.                 open (LOG_OUT, "> $DCONF->{admin_dir}/msg_index/$topic_save-log.out");
  347.                 while (<LOG_IN>) {
  348.                     my ($x) = split(/;/, $_, 2);
  349.                     if (ref $repair->{$x} ne 'HASH' || $repair->{$x} eq "") {
  350.                         $size += length($_);
  351.                         print LOG_OUT $_;
  352.                         next;
  353.                     }
  354.                     if ($repair->{$x}->{type} eq "fix") {
  355.                         my $hh = log_line_to_hash($_);
  356.                         $hh->{postindex} = $repair->{$x}->{postindex};
  357.                         $hh->{firstchars} = $repair->{$x}->{firstchars} if defined $repair->{$x}->{firstchars};
  358.                         my $line = log_hash_to_line($hh);
  359.                         print LOG_OUT $line;
  360.                         $size += length($line);
  361.                     }
  362.                     if ($repair->{$x}->{type} eq "change") {
  363.                         if (! $seen->{$x}) {
  364.                             $seen->{$x} = 1;
  365.                             print LOG_OUT $_;
  366.                             $size += length($_);
  367.                             next;
  368.                         }
  369.                         my $hh = log_line_to_hash($_);
  370.                         $hh->{postindex} = $repair->{$x}->{postindex};
  371.                         $hh->{firstchars} = $repair->{$x}->{firstchars} if defined $repair->{$x}->{firstchars};
  372.                         my $line = log_hash_to_line($hh);
  373.                         print LOG_OUT $line;
  374.                         $size += length($line);
  375.                         next if $dp->{$repair->{$x}->{page}};
  376.                         $dp->{$repair->{$x}->{page}} = 1;
  377.                         my $p = GetPage($repair->{$x}->{topic}, $repair->{$x}->{page});
  378.                         foreach my $m (@{ $p->{messages} }) {
  379.                             if (defined $repair->{$m->{number}}->{postindex}) {
  380.                                 $m->{number} = $repair->{$m->{number}}->{postindex};
  381.                             }
  382.                         }
  383.                         SetPage($p);
  384.                     }
  385.                 }
  386.                 foreach my $x (keys %{$repair}) {
  387.                     next if $repair->{$x}->{type} ne "add";
  388.                     my $line = log_hash_to_line($repair->{$x});
  389.                     print LOG_OUT $line;
  390.                     $size += length($line);
  391.                 }
  392.                 close (LOG_IN);
  393.                 close (LOG_OUT);
  394.                 rename_file("$DCONF->{admin_dir}/msg_index/$topic_save-log.out", $size, "$DCONF->{admin_dir}/msg_index/$topic_save-log.txt");
  395.             }
  396.         }
  397.         unlink "$DCONF->{admin_dir}/backups/$u[0]";
  398.         $FORMref->{done}++;
  399.     }
  400.     if ($u[0] ne "" && $u[1] ne "") {
  401.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=2&done=$FORMref->{done}&total=$FORMref->{total}";
  402.         $subst->{'gauge'}->{'operation'} = 16;
  403.         $subst->{'gauge'}->{'description'} = 1603;
  404.         $subst->{'gauge'}->{'percent'} = $FORMref->{total} > 0 ? (int(100*$FORMref->{done}/$FORMref->{total})) : 0;
  405.         screen_out("gauge", $subst);
  406.     }
  407.     if ($DCONF->{pro} && $GLOBAL_OPTIONS->{database}) {
  408.         return 0;
  409.     }
  410.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=3";
  411.     $subst->{'gauge'}->{'operation'} = 16;
  412.     $subst->{'gauge'}->{'description'} = 1604;
  413.     $subst->{'gauge'}->{'percent'} = 0;
  414.     screen_out("gauge", $subst);
  415. }
  416.  
  417. ###
  418. ### log_recovery_3
  419. ###
  420. ### Mini-log maintenance/recovery
  421. ###
  422.  
  423. sub log_recovery_3 {
  424.     my ($FORMref, $subst, $kick) = @_;
  425.     my $M = board_topics();
  426.     return if ref $M ne 'ARRAY';
  427.     my @t = grep { $_->{type} == 1 } @{$M};
  428.     return if scalar @t == 0;
  429.     my $tmp = join("", $$, time, "MINI");
  430.     $tmp = $FORMref->{tempfile} if defined $FORMref->{tempfile};
  431.     $tmp =~ s/\W//g;
  432.     open (TF, "> $DCONF->{admin_dir}/backups/$tmp.TMP");
  433.     print TF map { join("", $_->{number}, "\n") } @t;
  434.     close (TF);
  435.     my $ttl = scalar(@t);
  436.     return $ttl if $kick;
  437.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$tmp&spot=4&total=$ttl";
  438.     $subst->{'gauge'}->{'operation'} = 16;
  439.     $subst->{'gauge'}->{'description'} = 1605;
  440.     $subst->{'gauge'}->{'percent'} = 0;
  441.     screen_out("gauge", $subst);
  442. }
  443.  
  444. ###
  445. ### log_recovery_4
  446. ###
  447. ### Mini-log maintenance/recovery action
  448. ###
  449.  
  450. sub log_recovery_4 {
  451.     my ($FORMref, $subst, $kick, $from_upgrade) = @_;
  452.     my $z = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}.TMP", "log_recovery_4", { zero_ok => 1, no_lock => 1, no_unlock => 1 });
  453.     if (ref $z eq 'ARRAY' && scalar @{$z}) {
  454.         my $i = shift @{$z};
  455.         dreq("fcn-logs");
  456.         chomp $i;
  457.         create_minilog($i);
  458.         $FORMref->{done}++;
  459.     }
  460.     if (ref $z ne 'ARRAY' || scalar @{$z} == 0) {
  461.         unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}.TMP";
  462.         return (1, 1) if $from_upgrade;
  463.         return undef;
  464.     }
  465.     writefile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}.TMP", $z, "log_recovery_4", { zero_ok => 1, no_lock => 1, no_unlock => 1 });
  466.     return ($FORMref->{done}, 0) if $from_upgrade;
  467.     return $FORMref->{done} if $kick;
  468.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&total=$FORMref->{total}&done=$FORMref->{done}&spot=4";
  469.     $subst->{'gauge'}->{'operation'} = 16;
  470.     $subst->{'gauge'}->{'description'} = 1605;
  471.     $subst->{'gauge'}->{'percent'} = $FORMref->{total} > 0 ? (int(100*$FORMref->{done}/$FORMref->{total})) : 0;
  472.     screen_out("gauge", $subst);
  473. }
  474.  
  475. ###
  476. ### specific_page_recovery_master
  477. ###
  478. ### Operates the specific page recovery system
  479. ###
  480.  
  481. sub specific_page_recovery_master {
  482.     my ($FORMref, $result) = @_;
  483.     my $subst = {};
  484.     $subst->{general}->{username} = $result->[0]->{user};
  485.     $FORMref->{tempfile} =~ s/\D//g;
  486.     $FORMref->{action} = "dr_pgs";
  487.     specific_page_recovery_0($FORMref, $subst) if $FORMref->{spot} == 0;
  488.     specific_page_recovery_1($FORMref, $subst) if $FORMref->{spot} == 1;
  489.     specific_page_recovery_2($FORMref, $subst) if $FORMref->{spot} == 2;
  490.     error_message("Undefined Error", "Contact DiscusWare support, code 'specific_page_recovery_master'", 0, 1);
  491. }
  492.  
  493.  
  494. ###
  495. ### page_recovery_master
  496. ###
  497. ### Operates the page recovery system
  498. ###
  499.  
  500. sub page_recovery_master {
  501.     my ($FORMref, $result) = @_;
  502.     my $subst = {};
  503.     $subst->{general}->{username} = $result->[0]->{user};
  504.     $FORMref->{tempfile} =~ s/\D//g;
  505.     $FORMref->{action} = "dr_pgr";
  506.     page_recovery_0($FORMref, $subst) if $FORMref->{spot} == 0;
  507.     page_recovery_1($FORMref, $subst) if $FORMref->{spot} == 1;
  508.     page_recovery_2($FORMref, $subst) if $FORMref->{spot} == 2;
  509.     page_recovery_3($FORMref, $subst) if $FORMref->{spot} == 3;
  510.     page_recovery_4($FORMref, $subst) if $FORMref->{spot} == 4;
  511.     page_recovery_5($FORMref, $subst) if $FORMref->{spot} == 5;
  512.     page_recovery_6($FORMref, $subst) if $FORMref->{spot} == 6;
  513.     page_recovery_7($FORMref, $subst) if $FORMref->{spot} == 7;
  514.     $subst->{general}->{menu} = 0;
  515.     $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$result->[0]->{user}&action=data_recovery";
  516.     $subst->{topics} = board_topics();
  517.     screen_out("dr_main", $subst);
  518. }
  519.  
  520. ###
  521. ### page_move_recovery_master
  522. ###
  523. ### Continues a page move recovery operation
  524. ###
  525.  
  526. sub page_move_recovery_master {
  527.     my ($FORMref, $result) = @_;
  528.     my $subst = {};
  529.     $subst->{general}->{username} = $result->[0]->{user};
  530.     $FORMref->{tempfile} =~ s/\D//g;
  531.     if (-e "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}MP.TMP") {
  532.         my $chgfile = "";
  533.         my $L = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}MP.TMP", "page_move_recovery_master", { no_lock => 1, no_unlock => 1, zero_ok => 1 });
  534.         my $P = substr($FORMref->{tempfile}, 0, 12);
  535.         opendir(DIR, "$DCONF->{admin_dir}/backups");
  536.         my @d = grep { /^CHG$P\d+\.TMP$/ } map { $_ } readdir(DIR);
  537.         if (scalar @d != 1) {
  538.             error_message("Data Recovery Error", "Could not find change file (1)", 0, 1);
  539.         }
  540.         $chgfile = $` if $d[0] =~ /\W/;
  541.         if ($chgfile eq "") {
  542.             error_message("Data Recovery Error", "Could not find change file (2)", 0, 1);
  543.         }
  544.         dreq("fcn-regn");
  545.         regenerate_board({ esc => 0, operation => 6, description => 601, tempfile => "$FORMref->{tempfile}MP", done => 0, total => scalar(@{$L}), action => "regen7", username => $result->[0]->{user}, changefile => $chgfile });
  546.     }
  547.     $subst->{general}->{menu} = 0;
  548.     $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$result->[0]->{user}&action=data_recovery";
  549.     $subst->{topics} = board_topics();
  550.     screen_out("dr_main", $subst);
  551. }
  552.  
  553.  
  554. ###
  555. ### page_recovery_0
  556. ###
  557. ### Handles first input to initiate process
  558. ###
  559.  
  560. sub page_recovery_0 {
  561.     my ($FORMref, $subst) = @_;
  562.     if ($FORMref->{maint}) {
  563.         dreq("adm-opts");
  564.         $GLOBAL_OPTIONS = options_save({ maintenance => 1 });
  565.     }
  566.     if ($FORMref->{reindex}) {
  567.         dreq("fcn-indx");
  568.         my $bt = board_topics();
  569.         my %h = map {$_->{number}, 1} grep($_->{type}==1,@{$bt});
  570.         INDEXING_main({ topic => \%h, menuaction => 1, position => "start", username => $subst->{general}->{username} });
  571.     }
  572.     page_recovery_1($FORMref, $subst);
  573. }
  574.  
  575. ###
  576. ### specific_page_recovery_0
  577. ###
  578. ### Initialize temporary files
  579. ###
  580.  
  581. sub specific_page_recovery_0 {
  582.     my ($FORMref, $subst) = @_;
  583.     my $tcache = time;
  584.     my $topic = $FORMref->{topic}; $topic =~ s/\D//g;
  585.     my $path = get_message_path($topic);
  586.     error_message("Invalid Topic", "Selected topic number is not valid", 0, 1) if ! -e $path;
  587.     my $tfname = join("", $$, substr($tcache, length($tcache)-5, 5), substr($ENV{REMOTE_ADDR}, length($ENV{REMOTE_ADDR})-8, 8));
  588.     $tfname =~ s/\D//g;
  589.     dreq("fcn-regn");
  590.     my $filelist = incremental({ tempfile => $tfname, operation => 1, topic => $topic });
  591.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&topic=$topic&page=$FORMref->{page}&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$filelist->{tempfile}&spot=1&done=0&total=$filelist->{count}";
  592.     $subst->{'gauge'}->{'operation'} = 9;
  593.     $subst->{'gauge'}->{'description'} = 950;
  594.     $subst->{'gauge'}->{'percent'} = 0;
  595.     screen_out("gauge", $subst);
  596. }
  597.  
  598. ###
  599. ### specific_page_recovery_1
  600. ###
  601. ### Analyze files to see if they are children of the selected page or not
  602. ###
  603.  
  604. sub specific_page_recovery_1 {
  605.     my ($FORMref, $subst) = @_;
  606.     my $done = $FORMref->{done};
  607.     dreq("fcn-regn");
  608.     my $tempfile = incremental({ operation => "read", tempfile => $FORMref->{tempfile} });
  609.     my $R = regeneration_resource_init();
  610.     open (TFOUT, ">> $DCONF->{admin_dir}/backups/$FORMref->{tempfile}-RES.TMP");
  611.     while (my $f = shift @{ $tempfile->{data} }) {
  612.         chomp $f;
  613.         my ($topic, $page) = ($f =~ m|^(\d+)/(\d+)|);
  614.         $done += 1;
  615.         next if $page == $FORMref->{page};
  616.         next if $topic == $page;
  617.         my $K = GetPage($topic, $page);
  618.         if ($K->{head}->{parent} == $FORMref->{page}) {
  619.             print TFOUT "$page\n";
  620.         }
  621.         $R = regeneration_resource_estimate($R, 1, $K->{general}->{length});
  622.         last if regeneration_reset_trigger($R);
  623.     }
  624.     close (TFOUT);
  625.     if (ref $tempfile->{data} eq 'ARRAY' && scalar @{ $tempfile->{data} } > 0) {
  626.         incremental({ operation => 3, data => $tempfile->{data}, tempfile => $FORMref->{tempfile} });
  627.         $FORMref->{done} = $done;
  628.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&topic=$FORMref->{topic}&page=$FORMref->{page}&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=1&done=$done&total=$FORMref->{total}";
  629.         $subst->{'gauge'}->{'operation'} = 9;
  630.         $subst->{'gauge'}->{'description'} = 951;
  631.         $subst->{'gauge'}->{'percent'} = $FORMref->{total} == 0 ? 0 : int(100 * $FORMref->{done} / $FORMref->{total});
  632.         screen_out("gauge", $subst);
  633.     } else {
  634.         unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}.TMP";
  635.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&topic=$FORMref->{topic}&page=$FORMref->{page}&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=2";
  636.         $subst->{'gauge'}->{'operation'} = 9;
  637.         $subst->{'gauge'}->{'description'} = 952;
  638.         $subst->{'gauge'}->{'percent'} = 0;
  639.         screen_out("gauge", $subst);
  640.     }
  641. }
  642.  
  643. ###
  644. ### specific_page_recovery_2
  645. ###
  646. ### Analyze a certain page and put the links back onto that page if needed
  647. ###
  648.  
  649. sub specific_page_recovery_2 {
  650.     my ($FORMref, $subst) = @_;
  651.     my $K = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-RES.TMP", "specific_page_recovery_2", { zero_ok => 1, no_lock => 1, no_unlock => 1 });
  652.     unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-RES.TMP";
  653.     my $L = GetPage($FORMref->{topic}, $FORMref->{page}, { zero_ok => 1, lock => 1, no_error => 1 });
  654.     my %z1 = map { $_->{number}, 1 } grep { $_->{islink} == 0 } @{$L->{sublist}};
  655.     my %z2 = map { chomp; $_, 1 } grep { /^\d+/ } @{$K};
  656.     my %z3 = map { $_, 1 } grep { $z1{$_} != 1 } keys %z2;
  657.     my %z4 = map { $_, 1 } grep { $z2{$_} != 1 } keys %z1;
  658.     if (! scalar keys %z3 && ! scalar keys %z4) {
  659.         my ($topic, $page) = (0 + $FORMref->{topic}, 0 + $FORMref->{page});
  660.         $subst->{general}->{change} = 0;
  661.         my $q = get_message_path($topic);
  662.         my $filename = join("/", $q, "$page.$DCONF->{ext}");
  663.         unlock("$filename");
  664.     } else {
  665.         if (! $L->{head}->{topic_number}) {
  666.             $L->{head}->{topic_number} = 0 + $FORMref->{topic};
  667.             $L->{head}->{me_number} = 0 + $FORMref->{page};
  668.             $L->{sublist} = [];
  669.             $L->{head}->{param} = "Sublist";
  670.             $L->{head}->{me_name} = "Recovered Page";
  671.         }
  672.         my @x = @{expand_sublist($L->{sublist}, $L->{head}->{topic_number})};
  673.         @x = grep { $z4{$_->{number}} == 0 } @x;
  674.         foreach my $q (keys %z3) {
  675.             push @x, { type => 0, number => $q, name => 'Recovered', icon => 'tree_k' };
  676.         }
  677.         $L->{sublist} = \@x;
  678.         $L->{general}->{subtopic_raw} = 0;
  679.         SetPage($L, { unlock => 1 });
  680.         $subst->{general}->{count} = 0 + scalar @{$K};
  681.         $subst->{general}->{change} = 1;
  682.         $subst->{general}->{removed} = scalar keys %z4;
  683.         $subst->{general}->{added} = scalar keys %z3;
  684.         $subst->{general}->{topic} = $FORMref->{topic};
  685.     }
  686.     $subst->{general}->{menu} = 100;
  687.     $subst->{general}->{username} = $FORMref->{username};
  688.     $subst->{general}->{url} = "$PARAMS->{cgiurl}?username=$FORMref->{username}&action=data_recovery";
  689.     screen_out("dr_main", $subst);
  690. }
  691.  
  692. ###
  693. ### page_recovery_1
  694. ###
  695. ### Initialize temporary files
  696. ###
  697.  
  698. sub page_recovery_1 {
  699.     my ($FORMref, $subst) = @_;
  700.     my $tcache = time;
  701.     my $tfname = join("", $$, substr($tcache, length($tcache)-5, 5), substr($ENV{REMOTE_ADDR}, length($ENV{REMOTE_ADDR})-8, 8));
  702.     $tfname =~ s/\D//g;
  703.     my $budir = join("/", $DCONF->{admin_dir}, "backups");
  704.     dreq("fcn-regn");
  705.     my $bt = board_topics();
  706.     my %h = map {$_->{number}, 1} grep($_->{type}==1,@{$bt});
  707.     foreach my $T (keys(%h)) {
  708.         touch_createfile("$budir/$tfname-$T.TMP") || error_message("Page Recovery Error", "Could not create temporary file!");
  709.     }
  710.     my $filelist = incremental( { tempfile => $tfname, topic => \%h, operation => "build"} );
  711.     writefile("$budir/$tfname-C2.TMP", $filelist->{data}, "page_recovery_1", { no_lock => 1, no_unlock => 1, create => 1 });
  712.     writefile("$budir/$tfname-FP.TMP", $filelist->{data}, "page_recovery_1", { no_lock => 1, no_unlock => 1, create => 1 });
  713.     writefile("$budir/$tfname-FL.TMP", $filelist->{data}, "page_recovery_1", { no_lock => 1, no_unlock => 1, create => 1 });
  714.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$filelist->{tempfile}&spot=2&done=0&total=$filelist->{count}";
  715.     $subst->{'gauge'}->{'operation'} = 9;
  716.     $subst->{'gauge'}->{'description'} = 901;
  717.     $subst->{'gauge'}->{'percent'} = 0;
  718.     screen_out("gauge", $subst);
  719. }
  720.  
  721. ###
  722. ### page_recovery_2
  723. ###
  724. ### Check to see if pages link to themselves, parents, etc.
  725. ###
  726.  
  727. sub page_recovery_2 {
  728.     my ($FORMref, $subst) = @_;
  729.     dreq("fcn-regn");
  730.     my $tf = join("-", $FORMref->{tempfile}, "C2");
  731.     if (-e "$DCONF->{admin_dir}/backups/$tf.TMP") {
  732.         my $tempfile = incremental({ operation => "read", tempfile => $tf });
  733.         my $R = regeneration_resource_init();
  734.         lock("*");
  735.         my @d = ();
  736.         my $done = $FORMref->{done};
  737.         while (my $f = shift @{ $tempfile->{data} }) {
  738.             chomp $f;
  739.             my ($topic, $page) = ($f =~ m|^(\d+)/(\d+)|);
  740.             my $K = GetPage($topic, $page);
  741.             push @d, join("", join("\t", $topic, $page, join(",", map {$_->{number}} grep($_->{islink} == 0, @{$K->{sublist}}))), "\n");
  742.             $R = regeneration_resource_estimate($R, 1, 3 * $K->{general}->{length});
  743.             $done++;
  744.             last if regeneration_reset_trigger($R);
  745.         }
  746.         unlock("*");
  747.         appendfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-TD.TMP", \@d);
  748.         my $iw = incremental({ operation => 3, data => $tempfile->{data}, tempfile => $tf });
  749.         my $total = $FORMref->{total};
  750.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=2&done=$done&total=$total";
  751.         $subst->{'gauge'}->{'operation'} = 9;
  752.         $subst->{'gauge'}->{'description'} = 901;
  753.         $subst->{'gauge'}->{'percent'} = $total > 0 ? int(100 * $done / $total) : 0;
  754.         screen_out("gauge", $subst);
  755.     }
  756.     my $bt = board_topics();
  757.     my %h = map {$_->{number}, 1} grep($_->{type}==1,@{$bt});
  758.     my $Q = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-TD.TMP", "page_recovery_2", { no_lock => 1, no_unlock => 1, create => 1 });
  759.     unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-TD.TMP";
  760.     my $S = {};
  761.     foreach my $line (@{$Q}) {
  762.         my ($topic, $page, $subs) = split(/\s+/, $line);
  763.         foreach my $s (split(/,/, $subs)) {
  764.             $S->{$topic}->{$page}->{$s}++;
  765.         }
  766.     }
  767.     my $seen = {}; my $err = {};
  768.     foreach my $t (keys %h) {
  769.         my ($sn, $er) = _page_recovery_2_dig($t, $t, -1, $S, $seen, $err);
  770.         hash_merge($seen, $sn);
  771.         hash_merge($err, $er);
  772.     }
  773.     if (scalar(keys(%{$err}))) {
  774.         my @k = ();
  775.         foreach my $t (keys(%{$err})) {
  776.             foreach my $p (keys(%{$err->{$t}})) {
  777.                 my @q = ();
  778.                 foreach my $k (keys(%{$err->{$t}->{$p}})) {
  779.                     push @q, "$k" if $err->{$t}->{$p}->{$k} == 1;
  780.                 }
  781.                 push @k, join("", join("\t", $t, $p, join(",", @q), "\n"));
  782.             }
  783.         }
  784.         writefile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-BL.TMP", \@k, "page_recovery_2", { create => 1, no_lock => 1, no_unlock => 1 });
  785.         my $sck = scalar(@k);
  786.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=3&done=0&total=$sck";
  787.         $subst->{'gauge'}->{'operation'} = 9;
  788.         $subst->{'gauge'}->{'description'} = 902;
  789.         $subst->{'gauge'}->{'percent'} = 0;
  790.         screen_out("gauge", $subst);
  791.     }
  792.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=4";
  793.     $subst->{'gauge'}->{'operation'} = 9;
  794.     $subst->{'gauge'}->{'description'} = 903;
  795.     $subst->{'gauge'}->{'percent'} = 0;
  796.     screen_out("gauge", $subst);
  797. }
  798.  
  799. sub _page_recovery_2_dig {
  800.     my ($topic, $page, $par, $S, $seen, $err) = @_;
  801.     $seen->{$topic}->{$page} = $par;
  802.     foreach my $Z (keys %{$S->{$topic}->{$page}}) {
  803.         if ($S->{$topic}->{$page}->{$Z} > 1) {
  804.             $err->{$topic}->{$page}->{$Z} = 2;
  805.         } elsif ($seen->{$topic}->{$Z} != 0) {
  806.             $err->{$topic}->{$page}->{$Z} = 1;
  807.         } else {
  808.             ($seen, $err) = _page_recovery_2_dig($topic, $Z, $page, $S, $seen, $err);
  809.         }
  810.     }
  811.     return ($seen, $err);
  812. }
  813.  
  814. ###
  815. ### page_recovery_3
  816. ###
  817. ### Kills bad links (to page itself or to parents or multiple links) within pages
  818. ###
  819.  
  820. sub page_recovery_3 {
  821.     my ($FORMref, $subst) = @_;
  822.     my $bldf = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-BL.TMP", "page_recovery_3", { no_lock => 1, no_unlock => 1 });
  823.     my $k = shift @{$bldf};
  824.     my ($topic, $page, $links) = split(/\s+/, $k);
  825.     my $GF = GetPage($topic, $page, { lock => 1 });
  826.     my @u = split(/\n/, $GF->{sublist_raw});
  827.     my %l = map { $_, 1 } split(/,/, $links);
  828.     my @n = ();
  829.     my $flag = 0;
  830.     foreach my $q (@u) {
  831.         if ($q =~ /^<!--Top: (\d+)-->(.*)/) {
  832.             my $num = $1;
  833.             $flag = 0;
  834.             if ($l{$num}) {
  835.                 my $t = $2;
  836.                 $flag = 1 if $t =~ /^\s*$/;
  837.             } else {
  838.                 push @n, $q;
  839.                 $l{$num} = 1;
  840.             }
  841.         } elsif ($q =~ m|<!--/Top-->|) {
  842.             push @n, $q if ! $flag;
  843.             $flag = 0;
  844.         } elsif ($q =~ m|<!--URL: (\d+)-->|) {
  845.             push @n, $q;
  846.             $flag = 0;
  847.         } elsif ($q =~ /\S/) {
  848.             push @n, $q;
  849.         }
  850.     }
  851.     $GF->{sublist_raw} = join("\n", @n); $GF->{sublist_raw} .= "\n";
  852.     SetPage($GF, { unlock => 1 });
  853.     if (scalar(@{$bldf}) == 0) {
  854.         unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-BL.TMP";
  855.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=4";
  856.         $subst->{'gauge'}->{'operation'} = 9;
  857.         $subst->{'gauge'}->{'description'} = 902;
  858.         $subst->{'gauge'}->{'percent'} = 100;
  859.         screen_out("gauge", $subst);
  860.     }
  861.     writefile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-BL.TMP", $bldf, "page_recovery_3", { no_lock => 1, no_unlock => 1 });
  862.     $FORMref->{done}++;
  863.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=3&total=$FORMref->{total}&done=$FORMref->{done}";
  864.     $subst->{'gauge'}->{'operation'} = 9;
  865.     $subst->{'gauge'}->{'description'} = 902;
  866.     $subst->{'gauge'}->{'percent'} = $FORMref->{total} == 0 ? 0 : int(100 * $FORMref->{done} / $FORMref->{total});
  867.     screen_out("gauge", $subst);
  868. }
  869.  
  870. ###
  871. ### page_recovery_4
  872. ###
  873. ### Check for duplicates in file list
  874. ###
  875.  
  876. sub page_recovery_4 {
  877.     my ($FORMref, $subst) = @_;
  878.     dreq("fcn-regn");
  879.     my $tempfile = incremental({ operation => "read", tempfile => $FORMref->{tempfile} });
  880.     my $budir = join("/", $DCONF->{admin_dir}, "backups");
  881.     my $dupe = {};
  882.     foreach my $entry (@{$tempfile->{data}}) {
  883.         if ($entry =~ /^(\d+)\/(\d+)\./) {
  884.             my ($topic, $page) = ($1, $2);
  885.             push @{ $dupe->{$page} }, $topic;
  886.         }
  887.     }
  888.     my @Z = grep { scalar(@{ $dupe->{$_} }) > 1 } keys(%{$dupe});
  889.     if (scalar(@Z) == 0) {
  890.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=6";
  891.         $subst->{'gauge'}->{'operation'} = 9;
  892.         $subst->{'gauge'}->{'description'} = 903;
  893.         $subst->{'gauge'}->{'percent'} = 100;
  894.         screen_out("gauge", $subst);
  895.     }
  896.     touch_createfile("$budir/$FORMref->{tempfile}-DUPE.TMP") || error_message("Page Recovery Error", "Could not create temporary file!");
  897.     my @d = ();
  898.     foreach my $z (@Z) {
  899.         push @d, join("", join("\t", $z, join(",", @{$dupe->{$z}})), "\n");
  900.     }
  901.     appendfile("$budir/$FORMref->{tempfile}-DUPE.TMP", \@d, "page_recovery_2", { no_lock => 1, no_unlock => 1 });
  902.     my $td = scalar(@d);
  903.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=5&total=$td";
  904.     $subst->{'gauge'}->{'operation'} = 9;
  905.     $subst->{'gauge'}->{'description'} = 904;
  906.     $subst->{'gauge'}->{'percent'} = 0;
  907.     screen_out("gauge", $subst);
  908. }
  909.  
  910. ###
  911. ### page_recovery_5
  912. ###
  913. ### Fix all duplicates in file list by removing unwanted files
  914. ###
  915.  
  916. sub page_recovery_5 {
  917.     my ($FORMref, $subst) = @_;
  918.     my $dupefile = readfile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-DUPE.TMP", "page_recovery_3", { no_lock => 1, no_unlock => 1 });
  919.     my $U = shift @{$dupefile} or return page_recovery_6($FORMref, $subst);
  920.     my ($pagenum, $topics, $pass) = split(/\s+/, $U);
  921.     my @T = split(/,/, $topics);
  922.     my $pgref = {};
  923.     foreach my $topic (@T) {
  924.         $pgref->{$topic} = GetPage($topic, $pagenum, { no_error => 1 });
  925.     }
  926.     my $R = _page_recovery_5($pgref, \@T, $pagenum, $pass);
  927.     if (ref $R eq 'HASH') {
  928.         my $topic = (keys(%{$R}))[0];
  929.         foreach my $TN (@T) {
  930.             next if $TN == $topic;
  931.             my $tdir = get_message_path($TN);
  932.             my $Z = $R->{$topic};
  933.             unlink "$tdir/$pagenum.$DCONF->{ext}";
  934.             my $pnt = GetPage($TN, $Z->{head}->{parent}, { no_error => 1 });
  935.             if ($pnt->{head}->{topic_number} == $TN) {
  936.                 my @u = split(/\n/, $pnt->{sublist_raw});
  937.                 my @n = ();
  938.                 my $flag = 0;
  939.                 foreach my $q (@u) {
  940.                     if ($q =~ /^<!--Top: (\d+)-->(.*)/) {
  941.                         my ($o, $t) = ($1, $2);
  942.                         if ($o == $pagenum) {
  943.                             $flag = 1 if $t =~ /^\s*$/;
  944.                             next;
  945.                         }
  946.                         push @n, $q;
  947.                     } elsif ($q =~ m%<!--/Top-->%) {
  948.                         push @n, $q if $flag == 0;
  949.                         $flag = 0;
  950.                     } elsif ($q =~ /\S/ && $flag != 0) {
  951.                         push @n, $q;
  952.                     }
  953.                 }
  954.                 $pnt->{sublist_raw} = join("\n", @n); $pnt->{sublist_raw} .= "\n";
  955.                 SetPage($pnt, { });
  956.             }
  957.         }
  958.         if (scalar(@{$dupefile}) == 0) {
  959.             unlink "$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-DUPE.TMP";
  960.             return page_recovery_6($FORMref, $subst);
  961.         } else {
  962.             foreach my $q (@{$dupefile}) {
  963.                 my ($pagenums, $topicsnum, $pass) = split(/\s+/, $q);
  964.                 $q = join("", join("\t", $pagenums, $topicsnum), "\n");
  965.                 log_error("adm-dr.pl", "page_recovery_5", "Note: duplicated file $pagenums in topics $topicsnum");
  966.             }
  967.             writefile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-DUPE.TMP", $dupefile, "page_recovery_5");
  968.             $FORMref->{done}++;
  969.             $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=5&total=$FORMref->{total}&done=$FORMref->{done}";
  970.             $subst->{'gauge'}->{'operation'} = 9;
  971.             $subst->{'gauge'}->{'description'} = 904;
  972.             $subst->{'gauge'}->{'percent'} = $FORMref->{total} == 0 ? 0 : int(100 * $FORMref->{done} / $FORMref->{total});
  973.             screen_out("gauge", $subst);
  974.         }
  975.     } else {
  976.         push @{$dupefile}, join("", join("\t", $pagenum, $topics, 1 + $pass), "\n");
  977.         writefile("$DCONF->{admin_dir}/backups/$FORMref->{tempfile}-DUPE.TMP", $dupefile, "page_recovery_5");
  978.         $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=5&total=$FORMref->{total}&done=$FORMref->{done}";
  979.         $subst->{'gauge'}->{'operation'} = 9;
  980.         $subst->{'gauge'}->{'description'} = 904 + $pass;
  981.         $subst->{'gauge'}->{'percent'} = $FORMref->{total} == 0 ? 0 : int(100 * $FORMref->{done} / $FORMref->{total});
  982.         screen_out("gauge", $subst);
  983.     }
  984. }
  985.  
  986. sub _page_recovery_5 {
  987.     my ($pgref, $topicref, $pagenum, $pass) = @_;
  988.     my %pg = %{$pgref}; my @T = @{$topicref};
  989.  
  990.     # Zap corrupted files - those who do not know who they are
  991.     foreach my $topic (@T) {
  992.         if ($pg{$topic}->{head}->{me_number} != $pagenum) {
  993.             log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic deleted due to corruption");
  994.             delete $pg{$topic};
  995.         }
  996.     }
  997.     return \%pg if scalar(keys(%pg)) <= 1;
  998.  
  999.     # Zap topics that are in the wrong topic directory
  1000.     foreach my $topic (@T) {
  1001.         if (grep($_ == $pagenum, @T) && $pagenum != $topic) {
  1002.             log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic deleted due to wrong location");
  1003.             delete $pg{$topic};
  1004.         }
  1005.     }
  1006.     return \%pg if scalar(keys(%pg)) <= 1;
  1007.  
  1008.     # Zap pages in the wrong topic directory, if one is in the right directory
  1009.     my %rightdir = {};
  1010.     foreach my $topic (@T) {
  1011.         $rightdir{$topic} = 1 if $pg{$topic}->{head}->{topic_number} == $topic;
  1012.     }
  1013.     if (scalar(keys %rightdir) == 1) {
  1014.         foreach my $topic (@T) {
  1015.             if (! $rightdir{$topic}) {
  1016.                 log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic deleted due to wrong location when one found in right location");
  1017.                 delete $pg{$topic};
  1018.             }
  1019.         }
  1020.         return \%pg;
  1021.     }
  1022.  
  1023.     # Re-calculate the parent page from the headers & read parent pages
  1024.     my %parents = {};
  1025.     foreach my $topic (@T) {
  1026.         my $self = $pg{$topic};
  1027.         my @l = @{$self->{head}->{levels}};
  1028.         my $parent = $self->{head}->{topic_number};
  1029.         $parent = $l[scalar(@l)-1]->{level_number} if scalar(@l) > 1;
  1030.         $self->{head}->{parent} = $parent;
  1031.         $parents{$topic} = GetPage($topic, $parent, { no_error => 1 });
  1032.         $pg{$topic} = $self;
  1033.     }
  1034.  
  1035.     # See how many of the parent pages exist -- if only 1 we're done
  1036.     if (scalar(grep ( $parents{$_}->{head}->{me_number} > 0, keys %parents)) == 1) {
  1037.         foreach my $topic (@T) {
  1038.             if ($parents{$topic}->{head}->{me_number} <= 0) {
  1039.                 log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic deleted due to only one parent remaining");
  1040.                 delete $pg{$topic};
  1041.             }
  1042.         }
  1043.         return \%pg;
  1044.     }
  1045.  
  1046.     # SECOND PASS -- see if parents claim this page
  1047.     if ($pass >= 1) {
  1048.         my $claims = {};
  1049.         foreach my $topic (@T) {
  1050.             $claims->{$topic} = 1 if grep ( $_->{number} == $pagenum , @{$parents{$topic}->{sublist}});
  1051.         }
  1052.         if (scalar(keys %{$claims}) == 1) {
  1053.             foreach my $topic (@T) {
  1054.                 if (! $claims->{$topic}) {
  1055.                     log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic deleted due to no claims");
  1056.                     delete $pg{$topic};
  1057.                 }
  1058.             }
  1059.             return \%pg;
  1060.         }
  1061.     }
  1062.  
  1063.     # THIRD PASS -- see if the content is different among the pages
  1064.     if ($pass >= 2) {
  1065.         my %content = {};
  1066.         foreach my $topic (@T) {
  1067.             my @q = map {$_->{number}} @{$pg{$topic}->{sublist}};
  1068.             push @q, map {$_->{number}} @{$pg{$topic}->{messages}};
  1069.             $content{$topic} = join("\t", @q);
  1070.         }
  1071.         my $same = 0;
  1072.         foreach my $t1 (@T) {
  1073.             foreach my $t2 (@T) {
  1074.                 $same++ if $content{$t1} == $content{$t2};
  1075.             }
  1076.         }
  1077.         if ($same < scalar(@T)*scalar(@T) && $pass >= 2) {
  1078.             my @O = sort {scalar(@{$pg{$a}->{sublist}}) <=> scalar(@{$pg{$b}->{sublist}}) } @T;
  1079.             my @R = @T;
  1080.             my $nt = pop @R;
  1081.             foreach my $topic (@R) {
  1082.                 my $d = get_message_path($topic);
  1083.                 my $G = get_number();
  1084.                 my $P = GetPage($topic, $pagenum, { no_error => 1 });
  1085.                 $P->{head}->{me_number} = $G;
  1086.                 my @l = @{$P->{head}->{levels}};
  1087.                 my $parent = $P->{head}->{topic_number};
  1088.                 $parent = $l[scalar(@l)-2]->{level_number} if scalar(@l) > 2;
  1089.                 $P->{head}->{parent} = $parent;
  1090.                 $l[$#l]->{level_number} = $G if (scalar(@l) > 0);
  1091.                 $P->{head}->{levels} = \@l;
  1092.                 SetPage($P, { });
  1093.                 unlink "$d/$pagenum.$DCONF->{ext}";
  1094.                 unlock("$d/$pagenum.$DCONF->{ext}");
  1095.                 my $PAR = GetPage($topic, $parent, { no_error => 1 });
  1096.                 if ($PAR->{head}->{me_number} == $parent) {
  1097.                     $PAR->{general}->{subtopic_raw} = 1;
  1098.                     $PAR->{sublist_raw} =~ s/\/$topic\/$pagenum\.$DCONF->{ext}/\/$topic\/$G\.$DCONF->{ext}/g;
  1099.                     $PAR->{sublist_raw} =~ s/<!--Top: $pagenum-->/<!--Top: $G-->/g;
  1100.                     log_error("adm-dr.pl", "_page_recovery_5", "Note: $pagenum in $topic renumbered; is now $G [a]");
  1101.                     SetPage($PAR, { });
  1102.                 } else {
  1103.                     unlock("$d/$parent.$DCONF->{ext}");
  1104.                 }
  1105. #                foreach my $pg (@{ $P->{sublist} }) {
  1106. #                    _page_recovery_5_renum($topic, $pg->{number}, scalar(@l), $G, 1, {$parent => 1, $G => 1, $pagenum => 1});
  1107. #                }
  1108.                 delete $pg{$topic};
  1109.             }
  1110.             return \%pg;
  1111.         }
  1112.     }
  1113.  
  1114.     # FOURTH PASS -- see if these appear in their respective tree files
  1115.  
  1116.     if ($pass >= 3) {
  1117.         my %ait = {};
  1118. O1:        foreach my $topic (@T) {
  1119.             my $l = read_tree($topic);
  1120. O2:            foreach my $q (@{$l}) {
  1121.                 if ($q->{page} == $pagenum) {
  1122.                     $ait{$topic} = 1;
  1123.                     last O2;
  1124.                 }
  1125.             }
  1126.         }
  1127.         if (scalar(keys %ait) == 1) {
  1128.             foreach my $topic (@T) {
  1129.                 delete $pg{$topic} if ! $ait{$topic};
  1130.             }
  1131.             return \%pg;
  1132.         }
  1133.     }
  1134.  
  1135.     # FIFTH PASS -- duplicated pages -- essentially random selection
  1136.  
  1137.     if ($pass >= 4) {
  1138.         delete $pg{(keys %pg)[0]} while scalar(keys(%pg)) > 1;
  1139.         return \%pg;
  1140.     }
  1141.     return undef;
  1142. }
  1143.  
  1144. sub _page_recovery_5_renum {
  1145.     my ($topic, $page, $level, $new_num, $flag, $done) = @_;
  1146.     my $GP = GetPage($topic, $page, { no_error => 1 });
  1147.     if ($GP->{head}->{me_number} == $page) {
  1148.         $new_num = get_number();
  1149.         $GP->{head}->{levels}->[$level]->{level_number} = $new_num;
  1150.         $GP->{head}->{parent} = $new_num if $flag;
  1151.         SetPage($GP);
  1152.         $done->{$page} = 1;
  1153.         log_error("adm-dr.pl", "_page_recovery_5_renum", "Note: $page in $topic renumbered; is now $new_num [b]");
  1154.         foreach my $p (@{$GP->{sublist}}) {
  1155.             next if $done->{$p->{number}};
  1156.             _page_recovery_5_renum($topic, $p->{number}, $level, $new_num, 0, $done);
  1157.         }
  1158.     } else {
  1159.         unlock("/$topic/$page.$DCONF->{ext}");
  1160.     }
  1161. }
  1162.  
  1163. ###
  1164. ### page_recovery_6
  1165. ###
  1166. ### Fix parent numbers on any pages where they are broken
  1167. ###
  1168.  
  1169. sub page_recovery_6 {
  1170.     my ($FORMref, $subst) = @_;
  1171.     dreq("fcn-regn");
  1172.     my $tf = join("-", $FORMref->{tempfile}, "FP");
  1173.     if (-e "$DCONF->{admin_dir}/backups/$tf.TMP") {
  1174.         my $tempfile = incremental({ operation => "read", tempfile => $tf });
  1175.         $FORMref->{total} = scalar(@{$tempfile->{data}}) if $FORMref->{total} == 0;
  1176.         my $R = regeneration_resource_init();
  1177.         my $done = $FORMref->{done};
  1178.         lock("*");
  1179.         while (my $f = shift @{ $tempfile->{data} }) {
  1180.             chomp $f;
  1181.             my ($topic, $page) = ($f =~ m|^(\d+)/(\d+)|);
  1182.             my $K = GetPage($topic, $page, { no_error => 1 });
  1183.             next if ref $K->{head}->{levels} ne 'ARRAY';
  1184.             my @l = @{$K->{head}->{levels}};
  1185.             $done++;
  1186.             my $parent = $K->{head}->{topic_number};
  1187.             $parent = $l[scalar(@l)-2]->{level_number} if scalar(@l) > 2;
  1188.             $parent = 0 if $K->{head}->{topic_number} == $K->{head}->{me_number};
  1189.             if ($parent != $K->{head}->{parent}) {
  1190.                 $K->{head}->{parent} = $parent;
  1191.                 SetPage($K);
  1192.             }
  1193.             $R = regeneration_resource_estimate($R, 1, 3 * $K->{general}->{length});
  1194.             last if regeneration_reset_trigger($R);
  1195.         }
  1196.         unlock("*");
  1197.         my $iw = incremental({ operation => 3, data => $tempfile->{data}, tempfile => $tf });
  1198.         if ($iw->{'continue'}) {
  1199.             my $total = $FORMref->{total};
  1200.             $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=6&done=$done&total=$total";
  1201.             $subst->{'gauge'}->{'operation'} = 9;
  1202.             $subst->{'gauge'}->{'description'} = 908;
  1203.             $subst->{'gauge'}->{'percent'} = $total > 0 ? int(100 * $done / $total) : 0;
  1204.             screen_out("gauge", $subst);
  1205.         }
  1206.     }
  1207.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=7";
  1208.     $subst->{'gauge'}->{'operation'} = 9;
  1209.     $subst->{'gauge'}->{'description'} = 908;
  1210.     $subst->{'gauge'}->{'percent'} = 100;
  1211.     screen_out("gauge", $subst);
  1212. }
  1213.  
  1214. ###
  1215. ### page_recovery_7
  1216. ###
  1217. ### Delete links from pages that point to non-existent destinations
  1218. ###
  1219.  
  1220. sub page_recovery_7 {
  1221.     my ($FORMref, $subst) = @_;
  1222.     dreq("fcn-regn");
  1223.     my $tf = join("-", $FORMref->{tempfile}, "FL");
  1224.     if (-e "$DCONF->{admin_dir}/backups/$tf.TMP") {
  1225.         my $tempfile = incremental({ operation => "read", tempfile => $tf });
  1226.         $FORMref->{total} = scalar(@{$tempfile->{data}}) if $FORMref->{total} == 0;
  1227.         my $R = regeneration_resource_init();
  1228.         my $done = $FORMref->{done};
  1229.         lock("*");
  1230.         while (my $f = shift @{ $tempfile->{data} }) {
  1231.             chomp $f;
  1232.             my ($topic, $page) = ($f =~ m|^(\d+)/(\d+)|);
  1233.             my $tdir = get_message_path($topic);
  1234.             my $K = GetPage($topic, $page, { no_error => 1 });
  1235.             next if ref $K->{head}->{levels} ne 'ARRAY';
  1236.             $done++;
  1237.             my @sln = ();
  1238.             my %nu = undef;
  1239.             foreach my $sl (@{ $K->{sublist} }) {
  1240.                 if ($sl->{number} > 0 && ($sl->{islink} || -e "$tdir/$sl->{number}.$DCONF->{ext}")) {
  1241.                     push @sln, $sl;
  1242.                 } else {
  1243.                     log_error("adm-dr.pl", "page_recovery_7", "Deleted bad subtopic $sl->{number} from $topic/$page");
  1244.                 }
  1245.             }
  1246.             $K->{sublist} = \@sln;
  1247.             $K->{general}->{subtopic_raw} = 0;
  1248.             SetPage($K, { unlock => 1 });
  1249.             $R = regeneration_resource_estimate($R, 1, 15 * $K->{general}->{length});
  1250.             last if regeneration_reset_trigger($R);
  1251.         }
  1252.         unlock("*");
  1253.         my $iw = incremental({ operation => 3, data => $tempfile->{data}, tempfile => $tf });
  1254.         if ($iw->{'continue'}) {
  1255.             my $total = $FORMref->{total};
  1256.             $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=7&done=$done&total=$total";
  1257.             $subst->{'gauge'}->{'operation'} = 9;
  1258.             $subst->{'gauge'}->{'description'} = 909;
  1259.             $subst->{'gauge'}->{'percent'} = $total > 0 ? int(100 * $done / $total) : 0;
  1260.             screen_out("gauge", $subst);
  1261.         }
  1262.     }
  1263.     $subst->{'gauge'}->{'refresh_url'} = "$PARAMS->{cgiurl}?&action=$FORMref->{action}&username=$subst->{general}->{username}&tempfile=$FORMref->{tempfile}&spot=8";
  1264.     $subst->{'gauge'}->{'operation'} = 9;
  1265.     $subst->{'gauge'}->{'description'} = 909;
  1266.     $subst->{'gauge'}->{'percent'} = 100;
  1267.     screen_out("gauge", $subst);
  1268. }
  1269.  
  1270. ###
  1271. ### postread_privilege_reset
  1272. ###
  1273. ### Resets posting and reading privileges if file topicprv.txt has been reset
  1274. ###
  1275.  
  1276. sub postread_privilege_reset {
  1277.     my $topics = board_topics();
  1278.     dreq("fcn-usrp");
  1279.     my @u = ();
  1280.     foreach my $topic (@{ $topics }) {
  1281.         my $tn = $topic->{number};
  1282.         my $ph = {};
  1283.         $ph->{action} = "add_topic";
  1284.         $ph->{type} = "p";
  1285.         $ph->{topic} = $tn;
  1286.         $ph->{ip} = "*";
  1287.         push @u, $ph;
  1288.         if ($DCONF->{pro}) {
  1289.             my $rh = {};
  1290.             $rh->{action} = "set_equal";
  1291.             $rh->{type} = "r";
  1292.             $rh->{topic} = $tn;
  1293.             if (-e "$DCONF->{message_dir}/$tn") {
  1294.                 $rh->{ip} = "*";
  1295.             } else {
  1296.                 $rh->{user} = "*";
  1297.                 $rh->{user} = "*";
  1298.             }
  1299.             push @u, $rh;
  1300.         } else {
  1301.             $ph->{type} = "r";
  1302.             push @u, $ph;
  1303.         }
  1304.     }
  1305.     write_topic_privilege_file(\@u);
  1306.     return $topics;
  1307. }
  1308.  
  1309. ###
  1310. ### postindex_database_fix
  1311. ###
  1312. ### Fixes improper use of postindex.txt and database
  1313. ###
  1314.  
  1315. sub postindex_database_fix {
  1316.     my @max = ();
  1317.     dreq("dbint");
  1318.     my $sth = database_dbh()->prepare("SELECT max(postnum) FROM $PARAMS->{db_prefix}log;");
  1319.     $sth->execute();
  1320.     push @max, ($sth->fetchrow_array())[0];
  1321.     $sth->finish();
  1322.     my $fref = readfile("$DCONF->{admin_dir}/postindex.txt", "get_postindex");
  1323.     push @max, trim($fref->[0]);
  1324.     my $sth2 = database_dbh()->prepare("SELECT value FROM $PARAMS->{db_prefix}counters WHERE ID = 2;");
  1325.     $sth2->execute();
  1326.     push @max, ($sth2->fetchrow_array())[0];
  1327.     $sth2->finish();
  1328.     my $sth3 = database_dbh()->prepare("SELECT max(postnum) FROM $PARAMS->{db_prefix}search;");
  1329.     $sth3->execute();
  1330.     push @max, ($sth3->fetchrow_array())[0];
  1331.     $sth3->finish();
  1332.     my $maximum = 1 + _max(@max);
  1333.     my $sth3 = database_dbh()->prepare("UPDATE $PARAMS->{db_prefix}counters SET value = 1+$maximum WHERE ID = 2;");
  1334.     $sth3->execute();
  1335.     dreq("adm-opts");
  1336.     options_save({postindex_fixed => 1 });
  1337.     return 1+$maximum;
  1338. }
  1339.  
  1340. sub _max {
  1341.     my $max = undef;
  1342.     foreach my $q (@_) {
  1343.         $max = $q if (! defined $max || $q > $max);
  1344.     }
  1345.     return $max;
  1346. }
  1347.  
  1348. 1;
  1349.