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

  1. # FILE: posting.pl
  2. # DESCRIPTION: Posting of Messages and uploads of images
  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. ### posting_control
  20. ###
  21. ### Controls posting, "Create New Conversation" functionality, previews,
  22. ### message queue, profanity checking, etc.
  23. ###
  24.  
  25. sub posting_control {
  26.     my ($forceform, $FORMref, $cookie_str) = @_;
  27.     $FORMref = parse_form($ENV{'QUERY_STRING'}, $ENV{'CONTENT_LENGTH'}, $forceform) if ! defined $FORMref;
  28.     dreq("template");
  29.     if ($FORMref->{action} eq "posting_upload_form" && $DCONF->{pro}) {
  30.         dreq("upload-PRO"); upload_pro_control($FORMref);
  31.     }
  32.     post_upload_control($FORMref) if $FORMref->{action} eq "imageupload";
  33.     my $topic = 0; my $page = 0;
  34.     if ($FORMref->{HTTP_REFERER} =~ m|(\d+)/(\d+)|) {
  35.         $topic = $1; $page = $2;
  36.     } elsif ($FORMref->{topic} =~ m|^(\d+)$| && $FORMref->{page} =~ m|^(\d+)$|) {
  37.         $topic = $FORMref->{topic}; $page = $FORMref->{page};
  38.     } else {
  39.         my @k = map { join("", $_, "=>'", form_escape($FORMref->{$_}), "'") } keys %{$FORMref};
  40.         my $k = join("\n", @k);
  41.         error_message("Invalid Referer Error", "<p>Form Referer Not Found.  Found form variables were:</p><pre style=\"font: 10pt courier new\">$k</pre><p>This indicates a problem with the board skin. Please report this to the board administrator.</p>", 0, 1);
  42.     }
  43.     if ($FORMref->{action} eq "form") { ## Create New Conversation
  44.         undef my $subst;
  45.         my $get_page = GetPage($topic, $page);
  46.         if ($get_page->{head}->{param} !~ /Create/) {
  47.             error_message(read_language()->{BPCREATEERROR}, read_language()->{BP_CREATE_NOT_SUPPORTED_HERE}, 0, 1);
  48.         }
  49.         $subst->{head} = $get_page->{head};
  50.         $subst->{levels} = $subst->{head}->{levels};
  51.         $subst->{general}->{create_new_conversation} = 1;
  52.         $subst->{addmessage} = determine_addmessage($topic);
  53.         $subst->{general}->{secure} = -e "$DCONF->{admin_dir}/secure/$topic";
  54.         screen_out("*$get_page->{head}->{topic_number}newconv", $subst);
  55.     }
  56.     if ($FORMref->{action} eq "") { ## Preview or post
  57.         return post_incoming_handler($FORMref, { topic => $topic, page => $page }, $cookie_str);
  58.     }
  59.     error_message("Fell through posting control");
  60. }
  61.  
  62. ###
  63. ### post_upload_control
  64. ###
  65. ### Controls image/attachment uploading features
  66. ###
  67.  
  68. sub post_upload_control {
  69.     my ($FORMref, $dir_override) = @_;
  70.     dreq("fcn-mtp");
  71.     multipart_post_upload_control($FORMref, $dir_override);
  72. }
  73.  
  74. ###
  75. ### post_check_message_size
  76. ###
  77. ### Checks both maximum and minimum message size
  78. ###
  79.  
  80. sub post_check_message_size {
  81.     my ($self, $vpr) = @_;
  82.     return $self if $vpr->{type}->{is_superuser};
  83.     return $self if $self->{preview}->{error};
  84.     my $max = -1;
  85.     $max = $GLOBAL_OPTIONS->{public_msgsize} if ($vpr->{public} || $vpr->{special});
  86.     $max = $GLOBAL_OPTIONS->{registered_msgsize} if $vpr->{user}->{account};
  87.     $max = $GLOBAL_OPTIONS->{registered_mod_msgsize} if $vpr->{moderator}->{account};
  88.     if ($max != 0) {
  89.         if ($max == -1 || length($self->{message}->{text}) > (1000*$max)) {
  90.             my $l = read_language()->{BP_MESSAGE_EXCEEDED_MAXLENGTH};
  91.             my $s = $max == -1 ? 0 : $max;
  92.             my $m = int(length($self->{message}->{text})/100)/10;
  93.             $l =~ s/\%maxsize/$s/g;
  94.             $l =~ s/\%yoursize/$m/g;
  95.             $self->{preview}->{errormsg} = $l;
  96.             $self->{preview}->{error} = 1;
  97.             return $self;
  98.         }
  99.     }
  100.     my $min = 0;
  101.     $min = $GLOBAL_OPTIONS->{public_min_msgsize} if ($vpr->{public} || $vpr->{special});
  102.     $min = $GLOBAL_OPTIONS->{registered_min_msgsize} if $vpr->{user}->{account};
  103.     $min = $GLOBAL_OPTIONS->{registered_mod_min_msgsize} if $vpr->{moderator}->{account};
  104.     return $self if $min == 0;
  105.     my $k = $self->{message}->{text};
  106.     $k =~ s/<.*?>//g;
  107.     my @w = split(/\s+/, $k);
  108.     @w = grep(length($_) >= 2, @w);
  109.     my $w = scalar(@w);
  110.     if ($w < $min) {
  111.         my $l = read_language()->{BP_MESSAGE_TOO_SHORT};
  112.         $l =~ s/\%wordcount/$min/g;
  113.         $l =~ s/\%yourcount/$w/g;
  114.         $self->{preview}->{errormsg} = $l;
  115.         $self->{preview}->{error} = 1;
  116.         return $self;
  117.     }
  118. }
  119.  
  120. ###
  121. ### post_profanity_error
  122. ###
  123. ### Displays error screen if profanity is detected in a post
  124. ###
  125.  
  126. sub post_profanity_error {
  127.     my ($message, $argument, $words) = @_;
  128.     my $subst = $argument->{subst};
  129.     my $privcache = $argument->{privcache};
  130.     my $error_message = read_language()->{PROFANITY_DETECTED_MESSAGE};
  131.     my $l = join("<li>", sort keys(%{ $words })); $l = "<li>$l";
  132.     $error_message =~ s/\%wordlist/$l/g;
  133.     $subst->{preview}->{errormsg} = $error_message;
  134.     post_there_was_an_error($subst, $privcache);
  135. }
  136.  
  137. ###
  138. ### post_incoming_handler
  139. ###
  140. ### Handles an incoming post, processing input and checking everything.  If it
  141. ### looks good, it's passed to "post_message" to be posted.
  142. ###
  143.  
  144. sub post_incoming_handler {
  145.     my ($FORMref, $param, $cookie_str) = @_;
  146.     dreq("webtags", "fcn-usrp", "profane");
  147.     my $subst = {};
  148.     bless $subst;
  149.     $subst->{topic} = $param->{topic};
  150.     my ($am, $privcache) = determine_addmessage($param->{topic}, undef, undef, 1);
  151.     my $get_page = GetPage($param->{topic}, $param->{page});
  152.     $subst->{addmessage} = $am;
  153.     $subst->{head} = $get_page->{head};
  154.     $subst->{levels} = $subst->{head}->{levels};
  155.     $subst->{preview}->{new_conversation} = 1 if $FORMref->{'new_conversation'} == 1;
  156.     $subst->{general}->{create_new_conversation} = $subst->{preview}->{new_conversation};
  157.     $subst->{preview}->{username} = $FORMref->{username};
  158.     $subst->{preview}->{password} = $FORMref->{passwd};
  159.     $subst->{preview}->{email} = $FORMref->{email};
  160.     $subst->{preview}->{html} = 0 + $FORMref->{html} if defined $FORMref->{html_a};
  161.     $subst->{preview}->{active_links} = 0 + $FORMref->{active_links} if defined $FORMref->{active_links_a};
  162.     foreach my $fk (keys(%{ $FORMref })) {
  163.         if ($fk =~ m|^field_|) {
  164.             $subst->{preview}->{$fk} = $FORMref->{$fk};
  165.         }
  166.     }
  167.     my ($vpr, $usr, $mod, $susp) = check_postread_priv( { no_suspend => 1, type => 'p', topic => $param->{topic}, username => $FORMref->{username}, password => $FORMref->{passwd}, cookie => $FORMref->{COOKIE} }, $privcache, 1);
  168.     if ($DCONF->{pro} && defined $vpr->{profile_hash} && $vpr->{profile_hash}->{user} ne "") {
  169.         my $prefstr = join("", grep { length($_) == 1 } keys %{$vpr->{profile_hash}->{enhanced}->{pref}});
  170.         if ($prefstr =~ /[ab]/i) {
  171.             dreq("authwrap-PRO");
  172.             $cookie_str .= create_user_access_cookies($FORMref, {}, [$vpr->{profile_hash}], $prefstr);
  173.         }
  174.     }
  175.     if ($susp && $DCONF->{pro}) {
  176.         dreq("suspend-PRO");
  177.         my $L = suspended_account_preserve_post($FORMref);
  178.         suspended_account($vpr->{profile_hash}, $FORMref->{password}, 0, $L);
  179.     }
  180.     if ($GLOBAL_OPTIONS->{maintenance}) {
  181.         maintenance_mode_error() if ! $vpr->{type}->{is_superuser};
  182.     }
  183.     if (! $GLOBAL_OPTIONS->{posting} && ! $vpr->{type}->{is_superuser}) {
  184.         my $x = read_language()->{BPPOSTINGDISABLEDDESCR};
  185.         $x =~ s|%aopen|<a href="$DCONF->{script_url}/board-contact.$DCONF->{cgi_extension}">|gi;
  186.         $x =~ s|%aclose|</a>|gi;
  187.         error_message(read_language()->{BPPOSTINGDISABLEDTITLE}, $x, 0, 1);
  188.     }
  189.     if ($FORMref->{ADMIN_SCREEN} && ! $vpr->{moderator}->{account}) {
  190.         return error_message(read_language()->{BPADDMSGERROR}, read_language()->{BP_ADD_NOT_SUPPORTED_HERE}, 0, 1);
  191.     }
  192.     if (! $subst->{general}->{create_new_conversation} && $get_page->{head}->{param} !~ /Add/ && ! $FORMref->{ADMIN_SCREEN}) {
  193.         return error_message(read_language()->{BPADDMSGERROR}, read_language()->{BP_ADD_NOT_SUPPORTED_HERE}, 0, 1);
  194.     }
  195.     if ($FORMref->{ADMIN_SCREEN}) {
  196.         $vpr->{profile_hash}->{enhanced}->{pref}->{d} = 0;
  197.         $subst->{general}->{admin_screen} = 1;
  198.     }
  199.     return post_authentication_error($subst, $FORMref, $privcache, $param) if $vpr->{authorized} == 0;
  200.     return post_authentication_error($subst, $FORMref, $privcache, $param) if ($FORMref->{passwd} ne "" && ($vpr->{special} + $vpr->{user}->{account} + $vpr->{moderator}->{account} == 0));
  201.     if (! $vpr->{user}->{account} && ! $vpr->{moderator}->{account} && $GLOBAL_OPTIONS->{public_name_limit} && $GLOBAL_OPTIONS->{public_name_limit_val} > 0) {
  202.         if (length($subst->{preview}->{username}) > $GLOBAL_OPTIONS->{public_name_limit_val}) {
  203.             my $l = read_language()->{BP_NAME_IS_TOO_LONG};
  204.             $l =~ s/\%maxchar/$GLOBAL_OPTIONS->{public_name_limit_val}/;
  205.             $subst->{preview}->{errormsg} = $l;
  206.             $subst->post_new_subject_handler($FORMref, $vpr) if $subst->{preview}->{new_conversation};
  207.             $subst->post_prepare_message($FORMref, $vpr);
  208.             return post_there_was_an_error($subst);
  209.         }
  210.     }
  211.     if ($DCONF->{pro} && $GLOBAL_OPTIONS->{ppmu} > 0 && ($vpr->{public} || $vpr->{special}) && ! $vpr->{user}->{account} && ! $vpr->{moderator}->{account}) {
  212.         dreq("postname-PRO");
  213.         my $un = prepare_userpass($subst->{preview}->{username});
  214.         if (check_restricted_postname($un, $vpr)) {
  215.             $subst->{general}->{restricted_name} = 1;
  216.             return post_authentication_error($subst, $FORMref, $privcache);
  217.         }
  218.     }
  219.     $subst->post_new_subject_handler($FORMref, $vpr, $privcache) if $subst->{preview}->{new_conversation};
  220.     $subst->post_prepare_message($FORMref, $vpr, $privcache);
  221.     $subst->post_prepare_author_name($FORMref, $vpr, $privcache);
  222.     $subst->post_check_message_size($vpr, $privcache);
  223.     if ($DCONF->{pro} && $subst->{message}->{text} =~ m|\+\+\+|) {
  224.         dreq("upload-PRO");
  225.         my ($AA, $text, $source, $sizer, $bad_dim) = scan_upload_attachments_remove_too_big($subst->{message}->{text}, $vpr, $subst->{preview}->{source});
  226.         if ($AA) {
  227.             $subst->{message}->{text} = $text;
  228.             my $sizer2 = int($sizer/100) / 10;
  229.             $subst->{preview}->{source} = $source;
  230.             $subst->{preview}->{error} = 1;
  231.             post_upload_size_error($subst, $FORMref, $privcache, $param, $sizer2);
  232.         } elsif (scalar keys %{ $bad_dim } > 0) {
  233.             $subst->{message}->{text} = $text;
  234.             $subst->{preview}->{source} = $source;
  235.             $subst->{preview}->{error} = 1;
  236.             post_upload_size_error_2($subst, $FORMref, $privcache, $param, $bad_dim->{width}, $bad_dim->{height});
  237.         } else {
  238.             $subst->{preview}->{source} = $source;
  239.         }
  240.         $subst->{message}->{text} = convert_wrapped_attachments($text);
  241.     }
  242.     if (($FORMref->{preview} == 0 || $vpr->{profile_hash}->{enhanced}->{pref}->{d} == 1) && $subst->{preview}->{error} == 0) {
  243.         return post_message_helper($subst, $param, $FORMref, $privcache, $vpr, $get_page);
  244.     } else {
  245.         if ($DCONF->{pro} && $GLOBAL_OPTIONS->{spell_check} && $vpr->{profile_hash}->{enhanced}->{pref}->{i}) {
  246.             dreq("speller-PRO");
  247.             ($subst->{message}->{text}, $subst->{message}->{misspelled_words}) = spell_check_message($subst->{message}->{text});
  248.             $subst->{message}->{misspelled_words} = scalar(keys(%{ $subst->{message}->{misspelled_words} }));
  249.         }
  250.         $subst->{message}->{text} =~ s%<(/?)form(.*?)>%<!--$1 form $2 -->%g;
  251.         $subst->{general}->{secure} = -e "$DCONF->{admin_dir}/secure/$param->{topic}";
  252.         $subst->{general}->{no_message_icons_shown} = 1;
  253.         $subst->{message}->{fields} = [];
  254.         return screen_out("*$subst->{head}->{topic_number}preview", $subst, $cookie_str);
  255.     }
  256. }
  257.  
  258. ###
  259. ### post_message_helper
  260. ###
  261. ### Takes a minimal number of arguments and posts a message using the most
  262. ### common procedure and options.  In particular:
  263. ###  * Double post protection
  264. ###  * Enhanced user profile stuff (++ number of posts, status)
  265. ###  * Checks whether or not to queue
  266. ###  * Performs automatic archiving or pruning
  267. ###  * Performs active reordering
  268. ###  * Updates post counts/last modified/etc. on parent subtopics
  269. ###
  270. ### Note:
  271. ###  $A is prepared text or options ($subst)
  272. ###  $B is parameters from input ($param)
  273. ###  $C is parameters directly from the user ($FORMref)
  274. ###
  275.  
  276. sub post_message_helper {
  277.     my ($A, $B, $C, $privcache, $vpr, $get_page) = @_;
  278.     my $args = $A->{message};
  279.     $args->{emoticon} = $A->{new_conv}->{emoticon};
  280.     $args->{topic_number} = $B->{topic};
  281.     $args->{me_number} = $B->{page};
  282.     $args->{subject} = $A->{new_conv}->{subject};
  283.     $args->{real_author} = $A->{message}->{real_author};
  284.     $args->{is_private} = -e "$DCONF->{message_dir}/$B->{topic}" ? 0 : 1;
  285.     $args->{is_page_mgr} = defined $C->{pagemgr} ? 1 : 0;
  286.     $args->{username} = $C->{username};
  287.     $args->{remote_addr} = $ENV{'REMOTE_ADDR'};
  288.     $args->{remote_host} = $ENV{'REMOTE_HOST'};
  289.     $args->{source} = $A->{message}->{source};
  290.     $args->{entered_uname} = $A->{message}->{field_entered_uname};
  291.     $args->{uname} = $A->{message}->{field_uname};
  292.     $args->{group} = $vpr->{user}->{account} ? "user" : "MODERATOR";
  293.     $args->{group} = "PUBLIC" if $args->{group} eq "MODERATOR" && ! $vpr->{moderator}->{account};
  294.     $args->{personal_fields} = $vpr->{profile_hash}->{enhanced}->{personal} if $DCONF->{pro};
  295.     foreach my $l (keys(%{ $args })) {
  296.         if ($l =~ m|^field_|) {
  297.             $args->{starthash}->{$l} = $args->{$l} if $l ne "ip_address";
  298.         }
  299.     }
  300.     post_double_post_error($args) if post_check_double_post($args, $get_page);
  301.     my $att = post_scan_attachments($A->{message}->{source});
  302.     $args->{author_status} = 3 if ! $DCONF->{pro} && defined $vpr->{user}->{account};
  303.     $args->{author_status} = 9 if ! $DCONF->{pro} && defined $vpr->{moderator}->{account};
  304.     $args->{author_status} = 10 if ! $DCONF->{pro} && defined $vpr->{moderator}->{account} && $vpr->{profile_hash}->{user} eq $DCONF->{superuser};
  305.     my $d = (defined $att ? post_prepare_attachment_tempfile($args, $att) : undef);
  306.     if ($DCONF->{pro}) {
  307.         dreq("queue1-PRO");
  308.         if (queue_must_we_queue($vpr, $att)) {
  309.             $args->{queued} = 1;
  310.             my ($postnum, $pagenum) = queue_enter_message($args, $vpr);
  311.             if ($GLOBAL_OPTIONS->{email} == 1) {
  312.                 dreq("notify");
  313.                 my $pd = queue_fudge_page_data($args);
  314.                 notify_handler($postnum, $pagenum, $args, $pd, { queue => 1 });
  315.             }
  316.             post_upload_form($postnum, $pagenum, $args, $att, $d) if defined $att;
  317.             queue_submitted_message($args);
  318.         }
  319.     }
  320.     if ($DCONF->{pro} && ! $A->{message}->{anonymous}) {
  321.         dreq("fcn-prfl-PRO"); increment_number_of_posts($vpr->{profile_hash}, 1, $args);
  322.     }
  323.     $args->{privcache} = $privcache;
  324.     my @locks = ();
  325.     push @locks, "$DCONF->{message_dir}/$B->{topic}/$B->{page}.$DCONF->{ext}";
  326.     push @locks, "$DCONF->{admin_dir}/msg_index/$B->{topic}-tree.txt";
  327.     lock("post_message_helper", @locks);
  328.     my $PAGECACHE = GetPage($B->{topic}, $B->{page});
  329.     my $TREECACHE = read_tree($B->{topic}, { no_unlock => 1, no_lock => 1 });
  330.     my $presult = post_message($args, $PAGECACHE, $TREECACHE, undef, $privcache);
  331.     post_message_helper_after($args, $presult, $privcache);
  332.     post_upload_form($presult->{postnum}, $presult->{pageinfo}->{head}->{me_number}, $args, $att, $d) if defined $att;
  333.     return seturl(post_get_result_url($args));
  334. }
  335.  
  336. ###
  337. ### post_message_helper_after
  338. ###
  339. ### Performs functions after post_message is called
  340. ###
  341.  
  342. sub post_message_helper_after {
  343.     my ($args, $presult, $privcache) = @_;
  344.     my $PAGECACHE = $presult->{pagecache};
  345.     my $TREECACHE = $presult->{treecache};
  346.     my $TREECAT = $presult->{treecat};
  347.     if ($DCONF->{pro}) {
  348.         dreq("fcn-arch-PRO");
  349.         my $z = $args->{new_conversation} ? 2 : 1;
  350.         my $aa = auto_archive_check($args->{topic_number}, $args->{me_number}, $z, $PAGECACHE, $TREECACHE, $TREECAT);
  351.         if (ref $aa eq "HASH") {
  352.             $PAGECACHE = $aa->{oldfile} if defined $aa->{oldfile};
  353.             $TREECACHE = $aa->{old_topic_tree} if defined $aa->{old_topic_tree};
  354.             $TREECAT = $aa->{old_topic_cat} if defined $aa->{old_topic_cat};
  355.             $TREECACHE = $aa->{tree} if defined $aa->{tree};
  356.             $TREECAT = $aa->{cat} if defined $aa->{cat};
  357.         }
  358.     }
  359.     SetPage($PAGECACHE, { privcache => $privcache, unlock => 1 });
  360.     my $topic_number = $args->{topic_number};
  361.     my $active_reorder_self = (grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'active_subs'})) ? 1 : 0 );
  362.     my $active_reorder_parent = (grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'active_subs_parents'})) ? 1 : 0 );
  363.     if ($args->{new_conversation} == 0 && ($active_reorder_self + $active_reorder_parent > 0)) {
  364.         my @children = ();
  365.         undef my $parent_arrays;
  366.         undef my $is_level;
  367.         foreach my $oo (@{ $PAGECACHE->{head}->{levels} }) {
  368.             $is_level->{ $oo->{level_number} } = 1;
  369.         }
  370.         $is_level->{ $PAGECACHE->{head}->{topic_number} } = 1;
  371.         $is_level->{ 0 } = 1;
  372.         foreach my $refr (@{ $TREECACHE }) {
  373.             next if $refr->{page} == 0;
  374.             push (@{ $parent_arrays->{ $refr->{parent} } }, $refr) if $refr->{level} > 0;
  375.             push (@{ $parent_arrays->{ 0 } }, $refr) if $refr->{level} == 0;
  376.         }
  377.         if ($PAGECACHE->{head}->{topic_number} == $PAGECACHE->{head}->{me_number}) {
  378.             $PAGECACHE->{head}->{parent} = 0;
  379.         }
  380.         if ($active_reorder_self && defined $PAGECACHE->{head}->{parent}) {
  381.             @{$parent_arrays->{ $PAGECACHE->{head}->{parent} } } = sort {$b->{lastmod} <=> $a->{lastmod}} @{$parent_arrays->{ $PAGECACHE->{head}->{parent} } };
  382.             @{$parent_arrays->{ $PAGECACHE->{head}->{parent} } } = sort sort_archive_method @{$parent_arrays->{ $PAGECACHE->{head}->{parent} } } if $GLOBAL_OPTIONS->{sort_archive_method_on};
  383.         }
  384.         if ($active_reorder_parent && defined $PAGECACHE->{head}->{parent}) {
  385.             foreach my $y (keys(%{ $is_level })) {
  386.                 next if $y == $PAGECACHE->{head}->{parent};
  387.                 next if ! defined $parent_arrays->{$y};
  388.                 @{$parent_arrays->{ $y } } = sort {$b->{lastmod} <=> $a->{lastmod}} @{$parent_arrays->{ $y } };
  389.                 @{$parent_arrays->{ $y } } = sort sort_archive_method @{$parent_arrays->{ $y } } if $GLOBAL_OPTIONS->{sort_archive_method_on};
  390.             }
  391.         }
  392.         $TREECACHE = buildtree($parent_arrays, 0);
  393.         write_tree($args->{topic_number}, $TREECACHE, { no_lock => 1 });
  394.         foreach my $page (keys(%{ $is_level })) {
  395.             next if $page == 0;
  396.             next if $page == $PAGECACHE->{head}->{me_number};
  397.             my $rescode = lock("--REGENERATE-SUBTOPICS--", "$DCONF->{message_dir}/$topic_number/$page.$DCONF->{ext}");
  398.             if ($rescode == 1) {
  399.                 my $inp = GetPage($args->{topic_number}, $page);
  400.                 $inp->{sublist} = expand_sublist($inp->{sublist}, $topic_number, $TREECACHE);
  401.                 if ($active_reorder_self && $page == $PAGECACHE->{head}->{parent}) {
  402.                     @{ $inp->{sublist} } = sort {$b->{lastmod} <=> $a->{lastmod} } @{ $inp->{sublist} };
  403.                 }
  404.                 if ($active_reorder_parent && $page != $PAGECACHE->{head}->{parent}) {
  405.                     @{ $inp->{sublist} } = sort {$b->{lastmod} <=> $a->{lastmod} } @{ $inp->{sublist} };
  406.                 }
  407.                 @{ $inp->{sublist} } = sort sort_archive_method @{ $inp->{sublist} } if $GLOBAL_OPTIONS->{sort_archive_method_on};
  408.                 $inp->{general}->{subtopic_raw} = 0;
  409.                 SetPage($inp, { privcache => $privcache, unlock => 1 });
  410.             }
  411.         }
  412.     } else {
  413.         write_tree($args->{topic_number}, $TREECACHE, { no_lock => 1 });
  414.         pages_upward_update($TREECACHE, $TREECAT, [ $args->{me_number} ]);
  415.     }
  416.     $PARAMS->{"lookup:$PAGECACHE->{head}->{topic_number}"} = undef;
  417.     if ($GLOBAL_OPTIONS->{top_page_noregen} != 1) {
  418.         my $ttm = topic_tree_to_main($TREECACHE);
  419.         dreq("topic-pg"); regenerate_topic_page(undef, $ttm);
  420.     }
  421.     if ($GLOBAL_OPTIONS->{email} == 1) {
  422.         dreq("notify");
  423.         notify_handler($presult->{postnum}, $presult->{pageinfo}->{head}->{me_number}, $args, $presult->{pageinfo}, { queue => 0 });
  424.     }
  425. }
  426.  
  427. ###
  428. ### post_new_subject_handler
  429. ###
  430. ### Handles the name of a new subject
  431. ###
  432.  
  433. sub post_new_subject_handler {
  434.     my ($subst, $FORMref, $vpr, $privcache) = @_;
  435.     $subst->{message}->{new_conversation} = 1;
  436.     $subst->{preview}->{subject} = $FORMref->{subject};
  437.     $subst->{preview}->{emoticon} = $FORMref->{emoticon};
  438.     $subst->{new_conv}->{emoticon} = $FORMref->{emoticon};
  439.     my $subject = profanity_filter($FORMref->{subject}, {privcache => $privcache, subst => $subst, alternate_handler => \&post_profanity_error});
  440.     my ($lint_subj, $subtopic_name) = webtags($subject, 3, 1, $vpr->{type}->{is_moderator}, $vpr->{type}->{is_superuser}, $subst->{head}->{topic_number});
  441.     $subtopic_name = "\u$subtopic_name" if $GLOBAL_OPTIONS->{capitalize} == 1;
  442.     if ($lint_subj eq "!Error") {
  443.         $subst->{preview}->{errormsg} = $subtopic_name;
  444.         $subst->{preview}->{error} = 1;
  445.     } elsif ($GLOBAL_OPTIONS->{new_conv_limit_num} > 0 && $GLOBAL_OPTIONS->{new_conv_limit}) {
  446.         my $z = substrnohtml($subtopic_name, $GLOBAL_OPTIONS->{new_conv_limit_num});
  447.         if ($z ne $subtopic_name) {
  448.             my $l = read_language()->{BP_SUBJECT_TOO_LONG};
  449.             $l =~ s/\%maxchar/$GLOBAL_OPTIONS->{new_conv_limit_num}/g;
  450.             $subst->{preview}->{errormsg} = $l;
  451.             $subst->{preview}->{error} = 1;
  452.         }
  453.     }
  454.     if ($subtopic_name !~ m|\S|) {
  455.         $subst->{preview}->{errormsg} = read_language()->{BPCREATEERRORDESC};
  456.         $subst->{preview}->{error} = 1;
  457.     }
  458.     $subst->{new_conv}->{subject} = $subtopic_name;
  459.     return $subst;
  460. }
  461.  
  462. ###
  463. ### post_chop_long_words
  464. ###
  465. ### Implementation of the 'Limit maximum word length in posts' option
  466. ###
  467.  
  468. sub post_chop_long_words {
  469.     my ($message) = @_;
  470.     return $message if $GLOBAL_OPTIONS->{prevent_long_posts} == 0;
  471.     my $thres = 0 + $GLOBAL_OPTIONS->{prevent_long_posts_threshold};
  472.     return $message if $thres <= 0;
  473.     my ($U, $V) = split_u_v($message, '<.*?>');
  474.     foreach my $u (@{$U}) {
  475.         my @v = ();
  476.         while ($u =~ /&#?\w+;/g) { push @v, $& };
  477.         $u =~ s/&#?\w+;/\r/g;
  478.         while ($u =~ /([\S\r]{$thres})([\S\r])/) {
  479.             $u = join("", $`, $1, " ", $2, $');
  480.         }
  481.         while ($u =~ /\r/) {
  482.             $u = join("", $`, shift @v, $');
  483.         }
  484.     }
  485.     return join_u_v($U, $V);
  486. }
  487.  
  488. ###
  489. ### post_prepare_message
  490. ###
  491. ### Handles the incoming message.
  492. ###
  493.  
  494. sub post_prepare_message {
  495.     my ($subst, $FORMref, $vpr, $privcache) = @_;
  496.     $subst->{preview}->{source} = $FORMref->{message};
  497.     my $message = $FORMref->{message};
  498.     $message =~ s/\s+$//;
  499.     $message =~ s/^\s+//;
  500.     if ($GLOBAL_OPTIONS->{html}) {
  501.         dreq("fcn-html"); $message = html_to_webtags($message, $FORMref);
  502.     }
  503.     if ($GLOBAL_OPTIONS->{active_links}) {
  504.         dreq("fcn-html"); $message = activate_links($message, $FORMref);
  505.     }
  506.     $message = profanity_filter($message, {privcache => $privcache, subst => $subst, alternate_handler => \&post_profanity_error, error_report => 1});
  507.     $subst->{message}->{source} = $message;
  508.     my ($lint, $msg) = webtags($message, 0, 1, $vpr->{type}->{is_moderator}, $vpr->{type}->{is_superuser}, $subst->{head}->{topic_number}, 1);
  509.     $msg = post_chop_long_words($msg);
  510.     if ($lint eq "!Error") {
  511.         $subst->{preview}->{errormsg} = $msg;
  512.         post_there_was_an_error($subst, $privcache);
  513.     } elsif ($msg =~ m|<img src="(.*?)"|is && $1 !~ m|^https?://.*$|i) {
  514.         $subst->{preview}->{errormsg} = "Illegal image link: $1 is not permitted";
  515.         post_there_was_an_error($subst, $privcache);
  516.     } else {
  517.         $subst->{message}->{text} = $msg;
  518.         $subst->{preview}->{formatting_tips} = $lint;
  519.         if ($msg !~ m|\S|) {
  520.             $subst->{preview}->{errormsg} = read_language()->{BPADDMSGERRORDESC};
  521.             post_there_was_an_error($subst, $privcache);
  522.         }
  523.         $subst->{preview}->{source} = $FORMref->{message};
  524.     }
  525.     if (defined $vpr->{profile_hash}) {
  526.         if ($DCONF->{pro}) {
  527.             dreq("fcn-prfl-PRO");
  528.             $vpr->{profile_hash} = enhance_result_profile($vpr->{profile_hash})->[0];
  529.         }
  530.         if ($DCONF->{pro} && $vpr->{options_string} !~ m|p| && ($GLOBAL_OPTIONS->{profile_on_post} == 1 || $GLOBAL_OPTIONS->{profile_on_post} eq "")) {
  531.             $subst->{message}->{email} = "$DCONF->{script_url}/board-profile.$DCONF->{cgi_extension}?action=view_profile&profile=$vpr->{profile_hash}->{user}-";
  532.             $subst->{message}->{email} .= $vpr->{type}->{is_moderator} ? "MODERATOR" : "users";
  533.         } elsif ($vpr->{options_string} !~ m|e| && ($GLOBAL_OPTIONS->{email_on_post} == 1 || $GLOBAL_OPTIONS->{email_on_post} eq "")) {
  534.             if ($vpr->{profile_hash}->{enhanced}->{pref}->{c} != 1) {
  535.                 $subst->{message}->{email} = "mailto:$vpr->{profile_hash}->{email}" if ($vpr->{profile_hash}->{email} ne "" && $vpr->{profile_hash}->{email} ne "email");
  536.             }
  537.         }
  538.         if ($GLOBAL_OPTIONS->{fullnames} ne "0" && $vpr->{options_string} !~ m|f| && $vpr->{profile_hash}->{fullname} ne "" && $vpr->{profile_hash}->{fullname} ne "fullname") {
  539.             $subst->{message}->{author} = $vpr->{profile_hash}->{fullname};
  540.         } else {
  541.             $subst->{message}->{author} = ($GLOBAL_OPTIONS->{'capitalize_username'} == 1 ? "\u$vpr->{profile_hash}->{user}" : $vpr->{profile_hash}->{user});
  542.         }
  543.         if ($GLOBAL_OPTIONS->{user_paren} == 1 || $GLOBAL_OPTIONS->{user_paren} eq "") {
  544.             $subst->{message}->{author} .= join($GLOBAL_OPTIONS->{'capitalize_username'} == 1 ? "\u$vpr->{profile_hash}->{user}" : $vpr->{profile_hash}->{user}, " (", ")");
  545.         }
  546.         $subst->{message}->{author_registered} = $vpr->{profile_hash}->{ctime};
  547.         if (defined $vpr->{profile_hash}->{enhanced}->{status}) {
  548.             $subst->{message}->{author_status} = $vpr->{profile_hash}->{enhanced}->{status};
  549.         } else {
  550.             $subst->{message}->{author_status} = 3;
  551.             $subst->{message}->{author_status} = 9 if $vpr->{type}->{is_moderator} == 1;
  552.             $subst->{message}->{author_status} = 10 if $vpr->{type}->{is_superuser} == 1;
  553.         }
  554.         if ($GLOBAL_OPTIONS->{'signature_user'} && ! $vpr->{type}->{is_moderator}) {
  555.             $subst->{message}->{field_signature} = $vpr->{profile_hash}->{enhanced}->{signature};
  556.         } elsif ($GLOBAL_OPTIONS->{'signature_mod'} == 1 && $vpr->{type}->{'is_moderator'} == 1) {
  557.             $subst->{message}->{field_signature} = $vpr->{profile_hash}->{enhanced}->{signature};
  558.         }
  559.         if ($GLOBAL_OPTIONS->{thumbnails_enabled_posts} && $vpr->{profile_hash}->{enhanced}->{picture_num}) {
  560.             $subst->{message}->{thumbnail} = $vpr->{profile_hash}->{enhanced}->{picture_num};
  561.             $subst->{message}->{thumbnail} = "" if $vpr->{profile_hash}->{enhanced}->{picture} eq "";
  562.             $subst->{message}->{thumbnail} = "" if ! -e "$DCONF->{admin_dir}/profiles/$vpr->{profile_hash}->{enhanced}->{picture}";
  563.         }
  564.         $subst->{message}->{real_author} = $vpr->{profile_hash}->{user};
  565.         $subst->{message}->{author_posts} = 1 + $vpr->{profile_hash}->{enhanced}->{posts};
  566.         if ($vpr->{options_string} =~ m|i1|) {
  567.             $subst->{message}->{ip_address} = ($ENV{REMOTE_HOST} ne "" ? $ENV{REMOTE_HOST} : $ENV{REMOTE_ADDR} );
  568.         }
  569.     } else {
  570.         $subst->{message}->{author_status} = 2;
  571.         $subst->{message}->{author} = remove_html(profanity_filter($FORMref->{username}, {privcache => $privcache, subst => $subst, alternate_handler => \&post_profanity_error}));
  572.         $subst->{message}->{author} =~ s/[<>]//g;
  573.         $subst->{message}->{author} =~ s/\((.*?)\)/$1/g;
  574.         if ($GLOBAL_OPTIONS->{public_addparen} == 1) {
  575.             $subst->{message}->{author} .= " (" . read_language()->{UNREGISTERED_NOTICE};
  576.             $subst->{message}->{author} .= ($GLOBAL_OPTIONS->{public_addip} == 1 ? " " : ")");
  577.         }
  578.         $FORMref->{email} = trim($FORMref->{email});
  579.         $subst->{message}->{email} = join("", "mailto:", remove_html($FORMref->{email})) if $FORMref->{email} =~ /^([\w\+\-\.]+)\@([\w\+\-\.]+)$/;
  580.         $subst->{message}->{email} = remove_html($FORMref->{email}) if $FORMref->{email} =~ m|^https?://|;
  581.         $subst->{message}->{email} = "" if ($subst->{message}->{email} =~ m|^https?://|i && ! $GLOBAL_OPTIONS->{'allow_email_url'});
  582.         $subst->{message}->{email} =~ s/[<>"]//g;
  583.         $subst->{message}->{real_author} = escape(read_language()->{UNREGISTERED_NOTICE});
  584.         if ($vpr->{options_string} =~ m|i[12]|) {
  585.             $subst->{message}->{ip_address} = ($ENV{REMOTE_HOST} ne "" ? $ENV{REMOTE_HOST} : $ENV{REMOTE_ADDR} );
  586.         }
  587.     }
  588.     if ($FORMref->{'Anon'} ne "" && $GLOBAL_OPTIONS->{'anonymous'} == 1 && $vpr->{options_string} !~ m|a|) {
  589.         $subst->{message}->{email} = "";
  590.         $subst->{message}->{author} = read_language()->{BPANONYMOUS};
  591.         if ($FORMref->{Anon} ne "1" && $GLOBAL_OPTIONS->{anonymous_name}) {
  592.             $subst->{message}->{author} = remove_html(profanity_filter($FORMref->{Anon}, {privcache => $privcache, subst => $subst, alternate_handler => \&post_profanity_error}));
  593.         }
  594.         $subst->{preview}->{anonymous} = 1;
  595.         $subst->{message}->{anonymous} = 1;
  596.         $subst->{message}->{author_status} = 1;
  597.         $subst->{message}->{field_status} = 1;
  598.         $subst->{message}->{field_registered} = "";
  599.         $subst->{message}->{author_registered} = "";
  600.         $subst->{message}->{author_posts} = "";
  601.         $subst->{message}->{field_posts} = "";
  602.         $subst->{message}->{field_signature} = "";
  603.         $subst->{message}->{thumbnail} = "";
  604.         if ($vpr->{options_string} =~ m|j1|) {
  605.             $subst->{message}->{ip_address} = "";
  606.         } elsif ($vpr->{options_string} =~ m|j2|) {
  607.             $subst->{message}->{ip_address} = ($ENV{REMOTE_HOST} ne "" ? $ENV{REMOTE_HOST} : $ENV{REMOTE_ADDR} );
  608.         }
  609.     }
  610.     $subst->{message}->{'number'} = 0;
  611.     $subst->{message}->{'time'} = time;
  612.     $subst->{message}->{'_iteration'} = 1;
  613.     return $subst;
  614. }
  615.  
  616. ###
  617. ### post_prepare_author_name
  618. ###
  619. ### Prepares the author's name and other information in the "Add a Message" box
  620. ###
  621.  
  622. sub post_prepare_author_name {
  623.     my ($subst, $FORMref, $vpr, $privcache) = @_;
  624.     if ($subst->{message}->{author} !~ m|\S|) {
  625.         $subst->{preview}->{errormsg} = read_language()->{BPAUTHERRORNONAME};
  626.         post_there_was_an_error($subst, $privcache);
  627.     } else {
  628.         $subst->{message}->{author} = char_convert($subst->{message}->{author});
  629.     }
  630.     if ($subst->{message}->{text} !~ m|\S|) {
  631.         $subst->{preview}->{errormsg} = read_language()->{BPADDMSGERRORDESC};
  632.         post_there_was_an_error($subst, $privcache);
  633.     }
  634.     $subst->{preview}->{username} = $FORMref->{username};
  635.     $subst->{preview}->{password} = $FORMref->{passwd};
  636.     $subst->{preview}->{email} = $FORMref->{email};
  637.     foreach my $fk (keys(%{ $FORMref })) {
  638.         if ($fk =~ m|^field_|) {
  639.             my $fieldstuff = profanity_filter($FORMref->{$fk}, {error_report => 1, privcache => $privcache, subst => $subst, alternate_handler => \&post_profanity_error});
  640.             my ($lint, $field) = webtags($fieldstuff, 1, 1, $vpr->{type}->{is_moderator}, $vpr->{type}->{is_superuser}, $subst->{head}->{topic_number});
  641.             if ($lint eq "!Error") {
  642.                 $subst->{preview}->{errormsg} = $field;
  643.                 post_there_was_an_error($subst, $privcache);
  644.             }
  645.             $subst->{message}->{$fk} = $field;
  646.         }
  647.     }
  648.     $subst->{message}->{field_entered_uname} = _prepare_as_entered($FORMref->{username}, $vpr->{profile_hash}->{user});
  649.     $subst->{message}->{entered_uname} = $subst->{message}->{field_entered_uname};
  650.     $subst->{message}->{field_uname} = $vpr->{profile_hash}->{user};
  651.     $subst->{message}->{field_uname} = "\u$subst->{message}->{field_uname}" if $GLOBAL_OPTIONS->{capitalize_username};
  652.     $subst->{message}->{uname} = $subst->{message}->{field_uname};
  653.     $subst->{enhanced_author_data} = $vpr->{profile_hash}->{enhanced};
  654.     $subst->{message}->{groups} = $vpr->{profile_hash}->{groups};
  655.     my %u = map { $_, 1 } grep { /\S/ } split(/\//, $vpr->{profile_hash}->{groups});
  656.     $subst->{message}->{authorgroups} = \%u;
  657.     $subst->{message}->{personal_fields} = $vpr->{profile_hash}->{enhanced}->{personal};
  658.     return $subst;
  659. }
  660.  
  661. sub _prepare_as_entered {
  662.     my ($entered, $inhash) = @_;
  663.     return "" if $inhash eq "";
  664.     my $z = $entered; $z =~ s/([^\w\s])/join("", "&#", ord($1), ";")/ge;
  665.     return $z;
  666. }
  667.  
  668. ###
  669. ### post_message
  670. ###
  671. ### Posts a message onto a message page, creating the new conversation if necessary.
  672. ###
  673.  
  674. sub post_message {
  675.     my ($argument, $PAGECACHE_in, $TREECACHE_in, $TREECAT_in, $privcache) = @_;
  676.     dreq("fcn-tree", "fcn-logs");
  677.     my $newhash = $argument->{starthash};
  678.     my $TREECAT = defined $TREECAT_in ? $TREECAT_in : undef;
  679.     $newhash->{groups} = $argument->{groups};
  680.     $newhash->{authorgroups} = ref $argument->{authorgroups} eq 'HASH' ? $argument->{authorgroups} : {};
  681.     $newhash->{author} = $argument->{author} eq "" ? read_language()->{BPANONYMOUS} : $argument->{author};
  682.     $newhash->{text} = $argument->{text} eq "" ? " " : $argument->{text};
  683.     $newhash->{email} = $argument->{email} eq "" ? "" : $argument->{email};
  684.     $newhash->{number} = $argument->{number} == 0 ? get_postindex() : $argument->{number};
  685.     $newhash->{'time'} = $argument->{'time'} == 0 ? time : $argument->{'time'};
  686.     $newhash->{author_posts} = ($argument->{'author_posts'} == 0 ? 0 : $argument->{'author_posts'});
  687.     $newhash->{author_registered} = ($argument->{'author_registered'} == 0 ? 0 : $argument->{'author_registered'});
  688.     $newhash->{author_status} = ($argument->{author_status} > 0 ? $argument->{author_status} : 1);
  689.     $newhash->{field_signature} = ($argument->{field_signature} eq "" ? "" : $argument->{field_signature});
  690.     $newhash->{field_ip_address} = ($argument->{ip_address} eq "" ? "" : $argument->{ip_address});
  691.     $newhash->{field_uname} = ($argument->{uname} eq "" ? "" : $argument->{uname});
  692.     $newhash->{thumbnail} = ($argument->{thumbnail} eq "" ? "" : $argument->{thumbnail});
  693.     $newhash->{property_approver} = $argument->{approver} if defined $argument->{approver};
  694.     $newhash->{personal_fields} = $argument->{personal_fields};
  695.     $newhash->{remote_addr} = $argument->{remote_addr} if defined $argument->{remote_addr};
  696.     $newhash->{remote_host} = $argument->{remote_host} if defined $argument->{remote_host};
  697.     undef my $pageinfo;
  698.     my $privcache = defined $privcache ? $privcache : $argument->{privcache};
  699.     my $_author = $newhash->{author};
  700.     $_author = $` if $_author =~ /(?:\(|()(.*?)(?:\)|))/;
  701.  
  702.     if ($argument->{new_conversation} == 1) {
  703.         dreq("fcn-pgad");
  704.     }
  705.  
  706.     my @locks = ();
  707.     push @locks, "$DCONF->{message_dir}/$argument->{topic_number}/$argument->{me_number}.$DCONF->{ext}" if ! defined $PAGECACHE_in;
  708.     push @locks, "$DCONF->{admin_dir}/msg_index/$argument->{topic_number}-tree.txt" if ! defined $TREECACHE_in;
  709.     lock("post_message", @locks) if scalar @locks;
  710.  
  711.     my $PAGECACHE = defined $PAGECACHE_in ? $PAGECACHE_in : GetPage($argument->{topic_number}, $argument->{me_number});
  712.     my $TREECACHE = defined $TREECACHE_in ? $TREECACHE_in : read_tree($argument->{topic_number}, { no_unlock => 1, no_lock => 1 });
  713.  
  714.     if ($argument->{new_conversation} == 1) {
  715.         $newhash->{_iteration} = 1;
  716.         my $topic_number = $PAGECACHE->{head}->{topic_number};
  717.         my $new_conv = {};
  718.         $new_conv->{number} = ($argument->{new_conversation_page} == 0 ? undef : $argument->{new_conversation_page});
  719.         $new_conv->{name} = $argument->{subject};
  720.         $new_conv->{position} = 2 if grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'alphabet_subs'}));
  721.         $new_conv->{position} = 1 if grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'reverse_subs'}));
  722.         $new_conv->{position} = 1 if grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'active_subs'}));
  723.         $new_conv->{position} = 0 if $new_conv->{position} == 0;
  724.         $new_conv->{topic} = $argument->{topic_number};
  725.         $new_conv->{host} = $argument->{me_number};
  726.         $new_conv->{new_post} = 1;
  727.         $new_conv->{msg_count} = 1;
  728.         $new_conv->{last_poster} = $_author;
  729.         $new_conv->{originator} = $_author;
  730.         $new_conv->{post_list} = $newhash->{number};
  731.         $new_conv->{type} = "Messages";
  732.         $new_conv->{type} .= "Add" if ($GLOBAL_OPTIONS->{'new_conv_with_add'} == 1 || $GLOBAL_OPTIONS->{'new_conv_with_add'} eq "");
  733.         $new_conv->{type} = "SublistCreate$new_conv->{type}" if $GLOBAL_OPTIONS->{'new_conv_with_button'};
  734.         $new_conv->{first_post} = $newhash;
  735.         $new_conv->{emoticon} = $argument->{emoticon};
  736.         $new_conv->{tree_ref} = $TREECACHE;
  737.         $new_conv->{cat_ref} = $TREECAT;
  738.         $new_conv->{pagehash} = $PAGECACHE;
  739.         $new_conv->{privcache} = $privcache;
  740.         my $r = add_page($new_conv);
  741.         $pageinfo = $r->{newfile};
  742.         $PAGECACHE = $r->{pghash};
  743.         $TREECACHE = $r->{tree};
  744.         $TREECAT = $r->{cat};
  745.         ($TREECACHE, $TREECAT) = tree_upward_update($TREECACHE, undef, $r->{number}, { metoo => 0, posts => '+1', lastmod => $newhash->{'time'}, last_poster => $_author });
  746.     } else {
  747.         my @msg = @{$PAGECACHE->{messages}};
  748.         my $topic_number = $PAGECACHE->{head}->{topic_number};
  749.         if (grep(/^$topic_number$/, split(/,/, $GLOBAL_OPTIONS->{'reverse_msgs'}))) {
  750.             unshift(@msg, $newhash);
  751.             tree_push_post($TREECACHE, $TREECAT, $PAGECACHE->{head}->{me_number}, $newhash->{number}, 1);
  752.         } else {
  753.             push (@msg, $newhash);
  754.             tree_push_post($TREECACHE, $TREECAT, $PAGECACHE->{head}->{me_number}, $newhash->{number}, 0);
  755.         }
  756.         $PAGECACHE->{messages} = \@msg;
  757.         $PAGECACHE->{general}->{messages_raw} = 0;
  758.         ($TREECACHE, $TREECAT) = tree_upward_update($TREECACHE, $TREECAT, $argument->{me_number}, { metoo => 1, posts => '+1', lastmod => $newhash->{'time'}, last_poster => $_author });
  759.         SetPage($PAGECACHE, { unlock => 1, privcache => $privcache }) if ! defined $PAGECACHE_in;
  760.         $pageinfo = $PAGECACHE;
  761.     }
  762.     my $topic_number = $pageinfo->{head}->{topic_number};
  763.     my $me_number = $pageinfo->{head}->{me_number};
  764.     my $postindex = $newhash->{number};
  765.     my $time = $newhash->{'time'};
  766.     my $username = $argument->{real_author};
  767.     my $group = $argument->{group};
  768.     $username = "PUBLIC" if $group eq "PUBLIC";
  769.     my $RA = defined $newhash->{remote_addr} ? $newhash->{remote_addr} : $ENV{REMOTE_ADDR};
  770.     my $RH = defined $newhash->{remote_host} ? $newhash->{remote_host} : $ENV{REMOTE_HOST};
  771.     my $poststr = escape($newhash->{author});
  772.     my $teXT = $newhash->{text}; $teXT =~ s/\+\+\+ (\w+) \+\+\+ (\d+) \+\+\+ (.*?) \+\+\+/\[$3\]/g;
  773.     my $preview_storage = defined $DCONF->{log_preview_length} ? $DCONF->{log_preview_length} : 25;
  774.     my $chars = escape(substr(remove_html($teXT), 0, $preview_storage));
  775.     log_push_entry($topic_number, [ { topic => $topic_number, postindex => $postindex, username => join(":", $username, $group), time => $time, page => $me_number, remote_addr => $RA, remote_host => $RH, firstchars => $chars, poststr => $poststr }]);
  776.     my $stxt = search_stop($newhash->{text});
  777.     search_push_entry($topic_number, [ { topic => $topic_number, postindex => $postindex, page => $me_number, time => $time, text => $stxt } ]);
  778.     return {
  779.         pagecache => $PAGECACHE,
  780.         treecache => $TREECACHE,
  781.         cat => $TREECAT,
  782.         pageinfo => $pageinfo,
  783.         postnum => $newhash->{number},
  784.     };
  785. }
  786.  
  787. ###
  788. ### buildtree
  789. ###
  790. ### Builds a "family tree" recursively
  791. ###
  792.  
  793. sub buildtree {
  794.     my ($arr, $parent) = @_;
  795.     undef my @newtree;
  796.     while (my $x = shift(@{ $arr->{$parent} })) {
  797.         push (@newtree, $x);
  798.         push (@newtree, @{ buildtree($arr, $x->{page}) });
  799.     }
  800.     return \@newtree;
  801. }
  802.  
  803. ###
  804. ### get_postindex
  805. ###
  806. ### Obtains and increases by one the post index
  807. ###
  808.  
  809. sub get_postindex {
  810.     if ($GLOBAL_OPTIONS->{database} && $GLOBAL_OPTIONS->{postindex_fixed} == 0) {
  811.         dreq("adm-dr"); return postindex_database_fix();
  812.     }
  813.     return sql_counter_increment("postindex") if $GLOBAL_OPTIONS->{database};
  814.     my $fref = readfile("$DCONF->{admin_dir}/postindex.txt", "get_postindex", { no_unlock => 1 });
  815.     my $num = $fref->[0]; chomp $num; $num += 1;
  816.     my @fref = ( "$num\n" );
  817.     writefile("$DCONF->{admin_dir}/postindex.txt", \@fref, "get_postindex", { no_lock => 1 });
  818.     return $num;
  819. }
  820.  
  821. ###
  822. ### post_authentication_error
  823. ###
  824. ### Present an authentication error on the preview screen if you are
  825. ### not authorized to post here.
  826. ###
  827.  
  828. sub post_authentication_error {
  829.     my ($subst, $FORMref, $privcache, $param) = @_;
  830.     $subst->{preview}->{errormsg} = read_language()->{BPAUTHERRORINVALID};
  831.     $subst->{preview}->{errormsg} = read_language()->{BP_MATCHES_REGISTERED_NAME} if $subst->{general}->{restricted_name};
  832.     $subst->{preview}->{subject} = $FORMref->{subject};
  833.     $subst->{preview}->{source} = $FORMref->{message};
  834.     post_there_was_an_error($subst, $privcache, $param);
  835. }
  836.  
  837. ###
  838. ### post_there_was_an_error
  839. ###
  840. ### An easy way to get to the preview screen if there was an error
  841. ###
  842.  
  843. sub post_there_was_an_error {
  844.     my ($subst, $privcache, $param) = @_;
  845.     if (ref $PARAMS->{no_exit} eq 'CODE') {
  846.         return &{ $PARAMS->{no_exit} }($subst->{preview}->{errormsg});
  847.     }
  848.     $subst->{addmessage} = determine_addmessage($subst->{topic}, $privcache);
  849.     $subst->{preview}->{error} = 1;
  850.     $subst->{general}->{secure} = -e "$DCONF->{admin_dir}/secure/$subst->{topic}";
  851.     $subst->{general}->{no_message_icons_shown} = 1;
  852.     screen_out("*$subst->{head}->{topic_number}preview", $subst, undef);
  853. }
  854.  
  855. ###
  856. ### post_upload_form
  857. ###
  858. ### Upload image/attachment form
  859. ###
  860.  
  861. sub post_upload_form {
  862.     my ($postnum, $pagenum, $args, $att, $d) = @_;
  863.     my @data = ("postnum=$postnum\n");
  864.     push @data, "pagenum=$pagenum\n";
  865.     push @data, "queued=1\n" if $args->{queued};
  866.     appendfile("$DCONF->{admin_dir}/msg_index/temp/$d.TMP", \@data, "post_upload_form");
  867.     my $subst = {};
  868.     if ($DCONF->{pro}) {
  869.         dreq("upload-PRO");
  870.         $subst->{attachments} = remove_done_attachments($att->{uploads}, $d);
  871.     } else {
  872.         $subst->{attachments} = $att->{uploads};
  873.     }
  874.     $subst->{general}->{target_script} = "board-post";
  875.     $subst->{general}->{action} = "imageupload";
  876.     $subst->{general}->{tempfile} = $d;
  877.     screen_out("upload", $subst);
  878. }
  879.  
  880. ###
  881. ### post_prepare_attachment_tempfile
  882. ###
  883. ### Prepares a temporary file for uploads
  884. ###
  885.  
  886. sub post_prepare_attachment_tempfile {
  887.     my ($argument, $attachment_ref) = @_;
  888.     my $filename = get_number();
  889.     if (! -d "$DCONF->{admin_dir}/msg_index/temp") {
  890.         mkdir("$DCONF->{admin_dir}/msg_index/temp", oct($DCONF->{perms0777})) || error_message("Create Directory Error", "Could not create 'temp' directory under 'msg_index'.  Post with image/attachments failed.");
  891.         chmod(oct($DCONF->{perms0777}), "$DCONF->{admin_dir}/msg_index/temp");
  892.     }
  893.     my @data = ();
  894.     foreach my $u (keys(%{ $argument })) {
  895.         if (ref $argument->{$u} eq "HASH") {
  896.             my $q = $argument->{$u}->{descr}; $q =~ s/(^|[^\\]),/$1\\,/g;
  897.             push @data, "$u=" . escape($q) . "\n" if $u ne "";
  898.         } else {
  899.             my $q = $argument->{$u}; $q =~ s/(^|[^\\]),/$1\\,/g;
  900.             push @data, "$u=" . escape($q) . "\n" if $u ne "";
  901.         }
  902.     }
  903.     foreach my $f (@{ $attachment_ref->{fragments} }) {
  904.         push @data, "=[f]" . escape($f) . "\n";
  905.     }
  906.     foreach my $f (@{ $attachment_ref->{uploads} }) {
  907.         my $line = "=[u]";
  908.         if ($f->{'descr'} =~ m|^(\d+),| && $f->{'done'}) {
  909.             $f->{file} = $1; $f->{'descr'} = $';
  910.         }
  911.         foreach my $k (keys(%{ $f })) {
  912.             my $q = $f->{$k}; $q =~ s/(^|[^\\]),/$1\\,/g;
  913.             $line .= "&$k=>" . escape($q);
  914.         }
  915.         push @data, $line . "\n";
  916.     }
  917.     push @data, "ip=$ENV{REMOTE_ADDR}\n";
  918.     writefile("$DCONF->{admin_dir}/msg_index/temp/$filename.TMP", \@data, { create => 1 });
  919.     return $filename;
  920. }
  921.  
  922. ###
  923. ### post_get_result_url
  924. ###
  925. ### Gets the URL of the page on which a message was posted
  926. ###
  927.  
  928. sub post_get_result_url {
  929.     my ($argument) = @_;
  930.     return "$DCONF->{script_url}/board-admin.$DCONF->{cgi_extension}?username=$argument->{username}&action=pm-page_editor&HTTP_REFERER=$argument->{topic_number}/$argument->{me_number}&menu=4" if $argument->{is_page_mgr};
  931.     return "$DCONF->{authorize_reader}?file=/$argument->{topic_number}/$argument->{me_number}.$DCONF->{ext}&lm=$argument->{time}" if $argument->{is_private} && ! $DCONF->{noqm};
  932.     return "$DCONF->{authorize_reader}?file=/$argument->{topic_number}/$argument->{me_number}.$DCONF->{ext}" if $argument->{is_private};
  933.     return "$DCONF->{message_url}/$argument->{topic_number}/$argument->{me_number}.$DCONF->{ext}?$argument->{time}" if ! $DCONF->{noqm};
  934.     return "$DCONF->{message_url}/$argument->{topic_number}/$argument->{me_number}.$DCONF->{ext}";
  935. }
  936.  
  937. ###
  938. ### post_double_post_error
  939. ###
  940. ### Double post error message
  941. ###
  942.  
  943. sub post_double_post_error {
  944.     my ($argument) = @_;
  945.     undef my $subst;
  946.     $subst->{general}->{clickthrough} = post_get_result_url($argument);
  947.     screen_out("dblpost", $subst);
  948. }
  949.  
  950. ###
  951. ### post_check_double_post
  952. ###
  953. ### Checks to see if this post is a duplicate
  954. ###
  955.  
  956. sub post_check_double_post {
  957.     my ($argument, $get_page) = @_;
  958.     return 0 if ! $GLOBAL_OPTIONS->{'double_post'};
  959.     my $threshold = ( defined $GLOBAL_OPTIONS->{'double_post_threshold'} ? $GLOBAL_OPTIONS->{'double_post_threshold'} : 120 );
  960.     my $timecache = time;
  961.     if ($argument->{new_conversation} == 1) {
  962.         my $topic_tree = read_tree($argument->{topic_number}, { no_lock => 1, no_unlock => 1});
  963.         foreach my $u (@{ $topic_tree }) {
  964.             if ($u->{parent} == $argument->{me_number} && $argument->{subject} eq $u->{name} && $u->{posts} == 1 && $u->{lastmod} > ($timecache - $threshold)) {
  965.                 return 1;
  966.             }
  967.         }
  968.     } else {
  969.         $get_page = GetPage($argument->{topic_number}, $argument->{me_number}) if ! $get_page;
  970.         foreach my $msg (@{ $get_page->{messages} }) {
  971.             return 1 if $msg->{'text'} eq $argument->{text} && $msg->{'time'} > ($timecache - $threshold);
  972.         }
  973.     }
  974.     return 0;
  975. }
  976.  
  977. ###
  978. ### post_scan_attachments
  979. ###
  980. ### Scan a post for attachments and images to upload
  981. ###
  982.  
  983. sub post_scan_attachments {
  984.     my ($message_source) = @_;
  985.     my @img_tags = ("image", read_language()->{IMAGE_TAG_NAME});
  986.     push @img_tags, ("attach", read_language()->{ATTACH_TAG_NAME}, "popgif", "popjpeg", "popattach", "poppng") if $DCONF->{pro};
  987.     @img_tags = grep(/\S/, @img_tags);
  988.     my @result = ();
  989.     my @fragments = ();
  990.     my $s = join("|", @img_tags);
  991.     while ($message_source =~ m%(^|[^\\])\\($s)\{(.*?)\}%i) {
  992.         $message_source = $';
  993.         push @fragments, join("", $`, $1);
  994.         my $u = lc($2);
  995.         my $x = {};
  996.         $x->{type} = "i" if $u eq "image";
  997.         $x->{type} = "i" if $u eq read_language()->{IMAGE_TAG_NAME};
  998.         $x->{type} = "i" if $u eq "popgif";
  999.         $x->{type} = "i" if $u eq "popjpeg";
  1000.         $x->{type} = "i" if $u eq "poppng";
  1001.         $x->{type} = "a" if $u eq "attach";
  1002.         $x->{type} = "a" if $u eq "popattach";
  1003.         $x->{type} = "a" if $u eq read_language()->{ATTACH_TAG_NAME};
  1004.         $x->{descr} = $3;
  1005.         $x->{done} = 1 if $u =~ m|^pop|;
  1006.         push @result, $x;
  1007.     }
  1008.     push @fragments, $message_source;
  1009.     return undef if scalar(@result) == 0;
  1010.     return { fragments => \@fragments, uploads => \@result };
  1011. }
  1012.  
  1013. ###
  1014. ### split_u_v
  1015. ###
  1016. ### Generates arrays for split processing based on pattern
  1017. ###
  1018.  
  1019. sub split_u_v {
  1020.     my ($text, $pat) = @_;
  1021.     $pat = '<.*?>' if $pat eq "";
  1022.     my @u = split(/$pat/, $text);
  1023.     my @v = (); while ($text =~ /($pat)/g) { push @v, $1; }
  1024.     return (\@u, \@v);
  1025. }
  1026.  
  1027. ###
  1028. ### join_u_v
  1029. ###
  1030. ### Re-joins arrays after split processing
  1031. ###
  1032.  
  1033. sub join_u_v {
  1034.     my @u = @{$_[0]};
  1035.     my @v = @{$_[1]};
  1036.     my $text = "";
  1037.     while (scalar(@u) + scalar(@v)) {
  1038.         $text .= shift @u; $text .= shift @v;
  1039.     }
  1040.     return $text;
  1041. }
  1042.  
  1043. 1;
  1044.