# FILE: src-board-subs-9
# Message posting and authentication
#-------------------------------------------------------------------------------
# DISCUS VERSION 3.10 COPYRIGHT NOTICE
#
# Discus 3.10 is copyright (c) 2000 by DiscusWare, LLC, all rights reserved.
# The use of Discus is governed by the Discus License Agreement which is
# available from the Discus WWW site at:
#    http://www.discusware.com/discus/license
#
# Pursuant to the Discus License Agreement, this copyright notice may not be
# removed or altered in any way.
#-------------------------------------------------------------------------------

#---SEPARATOR---#
#REQ:search_stop
#REQ:regenerate_message
#REQ:format_messages
#REQ:change_board_colors
#REQ:regenerate_subtopic_list
#REQ:auto_prune
#REQ:update_time
#REQ:get_number

sub post_message {
	my ($topic_number,$me_number,$message_hold,$newmessage,$FULLNAME,$EMAIL,$PROFILE_LINK,$username,$group_hold,$timepostin,$rain,$rhin,$new_subject_name) = @_;
	&flush_language ($topic_number, 1);
	my ($reversed_flag, $line, @c, %updates, @rm, $line, $msg, $trep, $nmhold, $new_subject_number);
	if ($new_subject_name ne "") {
		$new_subject_number = &get_number("check", $topic_number);
	} else {
		$new_subject_number = 0;
	}
	$poststr = &remove_html($FULLNAME, 1);
	if ($poststr =~ m|(.*)\((.*?)\)|) {
		my $o = $1; my $paren = $2;
		if ($o =~ /\S/) {
			$poststr = $o; $poststr =~ s/\s+$//;
		} else {
			$poststr = $paren;
		}
	}
	&lock("post_message", "$admin_dir/postindex.txt");
	open (FILE, "$admin_dir/postindex.txt");
	@postindex = <FILE>;
	close (FILE);
	$post = $postindex[0]; chomp $post;
	$post += 1;
	$postindex = $post;
	@post = ("$postindex\n");
	$pwline = \@post;
	&safe_write("$admin_dir/postindex.txt", $pwline);
	&unlock("post_message", "$admin_dir/postindex.txt");
	$datetime = &get_date_time("long", $timepostin);
	$datetime = $datestring_in if $datestring_in ne "";
	$timepostin = time if $timepostin == 0;
	$glo = $GLOBAL_OPTIONS{'alphabet_subs'}; @glo = split(/,/, $glo);
	$alph = 0; $alph = 1 if grep(/^$topic_number$/, @glo);
	$glo = $GLOBAL_OPTIONS{'reverse_subs'}; @glo = split(/,/, $glo);
	$rev = 0; $rev = 1 if grep(/^$topic_number$/, @glo);
	@rm = split(/,/, $GLOBAL_OPTIONS{'reverse_msgs'});
	if (grep(/^$topic_number$/, @rm)) {
		$reversed_flag = 1;
	} else {
		$reversed_flag = 0;
	}
	@ac = split(/,/, $GLOBAL_OPTIONS{'active_subs'});
	if (grep(/^$topic_number$/, @ac)) {
		$active_reorder = 1;
	} else {
		$active_reorder = 0;
	}
	@ac = split(/,/, $GLOBAL_OPTIONS{'active_subs_parents'});
	if (grep(/^$topic_number$/, @ac)) {
		$active_reorder = 1;
	} else {
		$active_reorder = 2 if $active_reorder == 1;
	}
	$pi = $postindex; $pi = (-1) * $postindex if $reversed_flag == 1;
	my ($me_number_hold, $pn_number);
	$me_number_hold = $me_number;
	($header_hold, $color_hold) = &update_time($topic_number, $me_number, $timepostin, $timepostin, $pi, $active_reorder, $new_subject_number, $new_subject_name, $poststr, $postindex);
	$pn_number = $me_number;
	$me_number = $new_subject_number if $new_subject_number;
	&lock("post_message", "$message_dir/$topic_number/$me_number.$ext");
	my ($head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src);
	if (!$new_subject_number) {
		($head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src) = &get_page($topic_number, $me_number);
	} else {
		$head = ""; $color = ""; $lm = ""; $ann = "", $ann_src = ""; $sublist = ""; $about = ""; $about_src = "";
		$message = ""; $message_src = ""; $description_src = "";
		@hh = split(/\n/, $header_hold);
		@hh = grep(!/^<!--Parent:/, @hh);
		push (@hh, "<!--Parent: $pn_number-->");
		@hh = grep(!/^<!--Me:/, @hh);
		push (@hh, "<!--Me: $me_number/$new_subject_name-->");
		$maxlev = 0;
		foreach $line (@hh) {
			if ($line =~ m|<!--Level (\d+):|) {
				$maxlev = $1 if $1 > $maxlev;
			}
		}
		$maxlev += 1;
		push (@hh, "<!--Level $maxlev: $me_number/$new_subject_name-->");
		@hh = grep(!/<!--Param:/, @hh);
		$type = "Messages";
		$type .= "Add" if ($GLOBAL_OPTIONS{'new_conv_with_add'} == 1 || $GLOBAL_OPTIONS{'new_conv_with_add'} eq "");
		$type = "SublistCreate$type" if $GLOBAL_OPTIONS{'new_conv_with_button'};
		push (@hh, "<!--Param: $type-->");
		$head = join("\n", @hh);
		$color = $color_hold;
	}
	$message .= "\n";
	@msg = split(/\n/, $message); $ct = scalar(grep(/<!-Post: (\d+)-!>/, @msg));
	if ($reversed_flag) {
		$message2 = $message; $message = "";
	}
	$mmsg = "\n";
	$mmsg .= "<!-Post: $postindex-!><!-Time: $timepostin-!>\n";
	$P = 1 + $ct;
	$msg = &format_messages($FULLNAME, $postindex, $newmessage, 1+$ct, $EMAIL, $timepostin, "$username:$group_hold", "$topic_number/$me_number", $P);
	$mmsg .=  "$msg\n";
	$mmsg .= "<!-/Post: $postindex-!>\n";
	$message .= $mmsg;
	if ($reversed_flag) {
		$message .= $message2;
	}
	($message) = &regenerate_message($topic_number, $me_number, $message);
	$ctr = 0;
	$message_hold = " $message_hold";
	while ($message_hold =~ /([^\\])\\image\{([^\}]*)\}/) {
		$ch = $1; $tag = $2;
		++$ctr;
		$tag =~ s/\\//g; $tag =~ s/\{//g; $tag =~ s/\}//g;
		$message_hold = $` . "$ch\\image_notuploaded{$ctr,$tag}" . $';
	}
	if ($L{IMAGE_TAG_NAME} ne "image" && $L{IMAGE_TAG_NAME} ne "") {
		$trep = $L{IMAGE_TAG_NAME};
		while ($message_hold =~ /([^\\])\\$trep\{([^\}]*)\}/) {
			$ch = $1; $tag = $2;
			$tag =~ s/\\//g; $tag =~ s/\{//g; $tag =~ s/\}//g;
			++$ctr;
			$message_hold = $` . "$ch\\image_notuploaded{$ctr,$tag}" . $';
		}
	}
	if ($pro) {
		while ($message_hold =~ /([^\\])\\attach\{([^\}]*)\}/) {
			$ch = $1; $tag = $2;
			++$ctr;
			$tag =~ s/\\//g; $tag =~ s/\{//g; $tag =~ s/\}//g;
			$message_hold = $` . "$ch\\attachment_notuploaded{$ctr,$tag}" . $';
		}
		if ($L{ATTACH_TAG_NAME} ne "attach" && $L{ATTACH_TAG_NAME} ne "") {
			$trep = $L{ATTACH_TAG_NAME};
			while ($message_hold =~ /([^\\])\\$trep\{([^\}]*)\}/) {
				$ch = $1; $tag = $2;
				$tag =~ s/\\//g; $tag =~ s/\{//g; $tag =~ s/\}//g;
				++$ctr;
				$message_hold = $` . "$ch\\attachment_notuploaded{$ctr,$tag}" . $';
			}
		}
	}
	my ($chars, $message_hold_2);
	$message_hold =~ s/^\s+//; $message_hold =~ s/\s+$//;
	$message_hold_2 = $message_hold; $message_hold_2 .= " ";
	$poststr = &escape($poststr);
	$nmtemp = $newmessage; $nmtemp = &remove_html($newmessage, 1); $nmtemp .= " "; $nmtemp =~ m|^([\s\S]{0,40})\s|; $chars = $1; $chars =~ s/^\s+//; $chars =~ s/\s+$//; $chars = &escape($chars);
	$message_hold = &escape($message_hold);
	&set_page($topic_number, $me_number, $head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, "", $description_src);
	&unlock("post_message", "$message_dir/$topic_number/$me_number.$ext");
	my ($PM_ME_NUMBER) = ($me_number);
	&lock("post_message", "$admin_dir/msg_index/$topic_number-log.txt");
	$rain = $ENV{'REMOTE_ADDR'} if !$rain;
	$rhin = $ENV{'REMOTE_HOST'} if !$rhin;
	open (LOG, ">>$admin_dir/msg_index/$topic_number-log.txt");
	print LOG "$postindex;$username:$group_hold;$timepostin;$topic_number/$me_number;$rain;$rhin;$chars;$poststr;$EMAIL\n";
	close (LOG);
	&unlock("post_message", "$admin_dir/msg_index/$topic_number-log.txt");
	($output, $junk) = &search_stop($newmessage);
	&lock("post_message", "$admin_dir/msg_index/$topic_number-search.txt");
	open(FILE, ">>$admin_dir/msg_index/$topic_number-search.txt");
	print FILE "$postindex $topic_number $me_number $output\n";
	close (FILE);
	&unlock("post_message", "$admin_dir/msg_index/$topic_number-search.txt");
	if ($GLOBAL_OPTIONS{'nosource'} == 0) {
		open (SRC, ">>$admin_dir/msg_index/$topic_number/$me_number-src.txt");
		print SRC "$postindex $message_hold\n";
		close (SRC);
	}
	if ($pro) {
		&auto_prune($topic_number, $PM_ME_NUMBER);
	}
	&change_board_colors("*");
	return ($postindex, &unescape($message_hold), $PM_ME_NUMBER);
}

#---SEPARATOR---#

sub get_number {
	my ($num, @num, $pwline, $nolock);
	my ($lock, $arg2) = @_;
	$nolock = 0; $nolock = 1 if $lock == 12345;
	&lock("get_number", "$admin_dir/data.txt") if !$nolock;
	if (open(NUMBER,"$admin_dir/data.txt")) {
		$num = <NUMBER>;
		close(NUMBER);
		$num = 0 if $num == 0;
		$num += 1;
		@num = ($num);
		$pwline = \@num;
		&safe_write("$admin_dir/data.txt", $pwline);
		&unlock("get_number", "$admin_dir/data.txt") if !$nolock;
	} else {
		&unlock("get_number", "$admin_dir/data.txt") if !$nolock;
		&error_message("File Error", "Cannot open data index file ($admin_dir/data.txt)") if $DIAGNOSTICS == 0;
		return "X $!" if $DIAGNOSTICS == 1;
	}
	if ($lock eq "check") {
		if (-e "$message_dir/$arg2/$num.$ext" || -e "$secdir/$arg2/$num.$ext") {
			&log_error("src-board-subs-9", "get_number", "Warning! New child page ($arg2/$num) already exists.");
			if ($GLOBAL_OPTIONS{'diskquota_disable'} == 1 || $GLOBAL_OPTIONS{'diskquota_disable'} eq "") {
				open (BACKUPS, ">$admin_dir/backups/QUOTA.txt");
				print BACKUPS time, "\n";
				print BACKUPS &escape("Create new destination file $arg2/$num.$ext; file already exists!  The data.txt file is probably corrupt."), "\n";
				close (BACKUPS);
				&ex('mail_administrator_quota', 1);
			}
			&error_message("Create new file error", "Could not create new file, as file already exists in this topic.  The board administrator should check the data.txt file for problems.", 0, 1);
		}
	}
	return $num;
}

#---SEPARATOR---#
#REQ:save_profile_information
#REQ:read_profile

sub verify_user_password {
	my ($username, $guess, $nocookies, $cookieset, $record, $silent, $force_save) = @_;
	my ($is_su, $is_moderator, $is_user);
	my ($user, $pass, $full, $email, $editing, $en, $lc, $group, $em, $fu);
	my ($Xuser, $Xpass, $Xfull, $Xemail, $Xediting, $Xen, $Xlc, $Xgroup, $Xem, $Xfu);
	my ($line, @passwd_line, @result_out, $ok);
	$timer = time;
	$is_su = 0; $is_moderator = 0; $is_user = 0;
	$username =~ tr/A-Z/a-z/; $guess =~ tr/A-Z/a-z/;
	$username =~ s/^\s+//; $username =~ s/\s+$//;
	$guess =~ s/^\s+//; $guess =~ s/\s+$//;
	undef @result_out; undef @modify_lines;
	open(PASSWD,"$admin_dir/passwd.txt");
	@passwd_line = <PASSWD>;
	close(PASSWD);
	foreach $line (@passwd_line) {
		next if $line !~ m|\S|;
		$ok = "";
		chomp $line;
		($user, $pass, $full, $email, $editing) = split(/:/, $line);
		($Xuser, $Xpass, $Xem, $Xfu, $Xediting, $Xen, $Xlc) = split(/:/, $line);
		chomp $Xlc;
		if ($user eq $username) {
			$test_pass = crypt($guess, $pass);
			$ok = "";
			$ok = "Yes" if $test_pass eq $pass;
			$ok = "No" if $guess eq "";
			$ok = "No" if $pass eq "";
			$ok = "Yes" if ($COOKIE{'pass' . $COOKIE_ID} eq crypt($pass, "cookie") && !$nocookies);
			$ok = "Yes" if ($COOKIE{'pass' . $COOKIE_ID} eq crypt($pass, "cookie") && $guess eq "adminlogin");
			$ok = "Yes" if ($COOKIE{'cpwd' . $COOKIE_ID} eq crypt($pass, "cookie") && !$nocookies);
			if ($ok eq "Yes") {
				if ($Xlc =~ m|/|) {
					$Xlc = $`; $Xlcsave = $Xlc;
				}
				$is_su = 1 if $user eq $superuser;
				$is_mod = 1;
				if ($record == 1 && $silent ne "on") {
					$Xlc = time; $Xlcsave = $Xlc;
				}
				if ($editing > 2) {
					$editing -= 2;
					&save_profile_information("", "", "", $editing, "", "$Xlc/$timer", "", "USERS:$line");
					$line = join(":", $Xuser, $Xpass, $Xem, $Xfu, $editing, $Xen, "$Xlc/$timer");
					$line .= "\n";
				} else {
					push (@modify_lines, "PASSWD:$line");
					$line = join(":", $Xuser, $Xpass, $Xem, $Xfu, $Xediting, $Xen, "$Xlc/$timer");
					$line .= "\n";
				}
				push (@result_out, "PASSWD:$line");
			}

			if ($pro == 1 && $ok eq "Yes") {
				&read_profile("$user-MODERATOR", 1) if ($pro == 1 && $ok eq "Yes");
			}
			if ($COOKIE{'user' . $COOKIE_ID} ne $Xuser && $ok eq "Yes" && !$cookieset) {
				&print_cookie_string($Xuser);
			}
			if ($COOKIE{'cpwd' . $COOKIE_ID} ne crypt($pass, "cookie") && $ok eq "Yes" && !$cookieset) {
				&print_cookie_string("", length($guess), $pass) if $guess ne "";
				&print_cookie_string("", length($COOKIE{'rpwd' . $COOKIE_ID}), $pass) if $guess eq "";
			}
		}
	}
	open (PASSWD, "$admin_dir/users.txt");
	@users = <PASSWD>;
	close (PASSWD);
	foreach $line (@users) {
		next if $line !~ m|\S|;
		chomp $line;
		$ok = "";
		($user, $pass, $em, $fu, $editing, $en, $lc, $group) = split(/:/, $line);
		($Xuser, $Xpass, $Xem, $Xfu, $Xediting, $Xen, $Xlc, $Xgroup) = split(/:/, $line);
		if ($user eq $username) {
			$test_pass = crypt($guess, $pass);
			$ok = "Yes" if $test_pass eq $pass;
			$ok = "No" if $guess eq "";
			$ok = "No" if $pass eq "";
			$ok = "Yes" if ($COOKIE{'cpwd' . $COOKIE_ID} eq crypt($pass, "cookie") && !$nocookies);
			if ($ok eq "Yes") {
				$is_user = 1;
				if ($Xlc =~ m|/|) {
					$Xlc = $`; $Xlcsave = $Xlc;
				}
				if ($record == 1 && $silent ne "on") {
					$Xlc = time; $Xlcsave = $Xlc;
				}
				if ($editing > 2) {
					$editing -= 2;
					&save_profile_information("", "", "", $editing, "", "$Xlc/$timer", "", "USERS:$line");
					$line = join(":", $Xuser, $Xpass, $Xem, $Xfu, $editing, $Xen, "$Xlc/$timer", $Xgroup);
					$line .= "\n";
				} else {
					push (@modify_lines, "USERS:$line");
					$line = join(":", $Xuser, $Xpass, $Xem, $Xfu, $Xediting, $Xen, "$Xlc/$timer", $Xgroup);
					$line .= "\n";
				}
				push (@result_out, "USERS:$line");
			}
			if ($pro && $ok eq "Yes") {
				&read_profile("$user-$group", 1);
			}
			if ($COOKIE{'user' . $COOKIE_ID} ne $Xuser && $ok eq "Yes" && !$cookieset) {
				&print_cookie_string($Xuser);
			}
			if ($COOKIE{'cpwd' . $COOKIE_ID} ne crypt($pass, "cookie") && $ok eq "Yes" && !$cookieset) {
				&print_cookie_string("", length($guess), $pass) if $guess ne "";
				&print_cookie_string("", length($COOKIE{'rpwd' . $COOKIE_ID}), $pass) if $guess eq "";
			}
		}
	}
	$result = join("\n", @result_out);
	&error_message($L{FILELOCKERROR}, $L{NOTICE_MAINTENANCEMODE}, 0, 1, 1) if ($GLOBAL_OPTIONS{'maintenance'} && !$is_su);

	# If frequent problems with "File Locking Error" on users.txt, comment out this line:
	$force_save = 1;
	# -----------------------------------------------------------------------------------

	&save_profile_information("", "", "", "", "", "$Xlcsave/$timer", "", @modify_lines) if $force_save;
	return ($result, $is_su, $is_mod, $is_user);
}

#---SEPARATOR---#
#REQ:regenerate_subtopic_list
#REQ:change_board_colors
#REQ:format_subtopics

sub update_time {
	my ($topic_number, $me_number, $time, $datestring, $add_post, $fancy_reordering, $new_subject_number, $new_subject_name, $poster_name, $postindex) = @_;
	my (%updates, $line, @c, $key, $flag, @TREE_STRUCTURE_FILE);
	my ($me_number_hold);
	$me_number_hold = $me_number;
	undef @reverse_parentage;
	undef %parent;
	undef @TREE_STRUCTURE_FILE;
	$flag = 0;
	$time = time if $time == 0;
	&lock("update_time", "$admin_dir/tree.txt");
	open (TREE, "$admin_dir/tree.txt");
	@TREE_STRUCTURE_FILE = <TREE>;
	close (TREE);
	foreach $line (@TREE_STRUCTURE_FILE) {
		@c = split(/\t/, $line);
		if ($c[1] == $topic_number) {
			$c[9] = $time;
			$c[7] += 1 if $add_post;
			$line = join("\t", @c);
			last;
		}
	}
	open (TREE, ">$admin_dir/tree.txt");
	print TREE @TREE_STRUCTURE_FILE;
	close (TREE);
	&unlock("update_time", "$admin_dir/tree.txt");
	&lock("update_time", "$admin_dir/msg_index/$topic_number-tree.txt");
	open (TREE, "$admin_dir/msg_index/$topic_number-tree.txt");
	@TREE_STRUCTURE_FILE = <TREE>;
	close (TREE);
	if ($new_subject_number) {
		$glo = $GLOBAL_OPTIONS{'alphabet_subs'}; @glo = split(/,/, $glo);
		$alph = 0; $alph = 1 if grep(/^$topic_number$/, @glo);
		$glo = $GLOBAL_OPTIONS{'reverse_subs'}; @glo = split(/,/, $glo);
		$rev = 0; $rev = 1 if grep(/^$topic_number$/, @glo);
		$ct = 0; $ct_2 = 0; undef @my_p; $flag = -1; $hold = 0; undef %found;
		$flag = -1;
		foreach $line (@TREE_STRUCTURE_FILE) {
			$ct++;
			@c = split(/\t/, $line);
			if ($c[2] == $me_number) {
				$flag = $c[0];
				$hold = $ct;
				$chold = $line;
			} elsif ($flag != -1) {
				if ($c[0] <= $flag) {
					$flag = -1;
					last;
				} else {
					push(@my_p, $line);
					if ($c[0] == (1+$flag)) {
						$found{$c[2]} = $line;
						$my_pos = $c[2];
					} else {
						$found{$my_pos} .= $line;
					}
				}
			}
		}
		@c = split(/\t/, $chold);
		$type = "MessagesAdd";
		$type = "SublistCreateMessagesAdd" if $GLOBAL_OPTIONS{'new_conv_with_button'};
		$newline = join("\t", ($c[0]+1), $c[1], $new_subject_number, $me_number, &escape($new_subject_name), $type, $c[6], 0, time, time, $poster_name, "");
		$newline .= "\n";
		if ($alph == 0 && $rev == 0) {
			push (@my_p, $newline);
		} elsif ($alph == 0 && $rev == 1) {
			splice(@my_p, 0, 0, $newline);
		} elsif ($alph == 1) {
			$found{"newpost"} = $newline;
			@my_p_new = sort alphabetical_tree values(%found);
			@my_p_new = reverse(@my_p_new) if $rev;
			$x = join("", @my_p_new);
			@my_p = split(/\n/, $x);
			foreach $line (@my_p) {
				$line .= "\n";
			}
		}
		splice(@TREE_STRUCTURE_FILE, $hold, scalar(@my_p)-1, @my_p);
	}
	$me_number = $new_subject_number if $new_subject_number;
	foreach $line (reverse(@TREE_STRUCTURE_FILE)) {
		@c = split(/\t/, $line);
		if ($c[2] == $me_number) {
			$updates{$c[2]} = -100 if $updates{$c[2]} == 0;
			$updates{$c[3]} = $c[2] if $updates{$c[3]} == 0;
			push (@reverse_parentage, $c[2]);
			$parent{$c[2]} = $c[3];
		} elsif ($updates{$c[2]} != 0) {
			$updates{$c[3]} = $c[2];
			push (@reverse_parentage, $c[2]);
			$parent{$c[2]} = $c[3];
		}
	}
	undef @tsf_updatetime;
	undef %st_lists;
	foreach $line (@TREE_STRUCTURE_FILE) {
		@c = split(/\t/, $line);
		if ($updates{$c[2]} != 0) {
			if ($time != 0) {
				$c[8] = $time;
				$c[9] = $time;
			} else {
				$c[8] = time;
				$c[9] = time;
			}
			$c[7] += 1 if $add_post;
			$st_lists{$c[3]} .= ":$c[2]";
			if ($c[2] == $me_number && $add_post) {
				chomp $c[11];
				@x = split(/,/, $c[11]);
				if ($add_post < 0) {
					unshift (@x, -$add_post);
				} else {
					push (@x, $add_post);
				}
				$c[11] = join(",", @x);
				$c[11] .= "\n";
			}
			$line = join("\t", @c);
			$line .= "\n" if $line !~ m|\n$|;
		}
	}
	if ($fancy_reordering != 0) {
		$rev = 1; $alph = 0;
		foreach $x (@reverse_parentage) {
			@st_list = split(/:/, $st_lists{$x});
			@st_list = grep(/\S/, @st_list);
			next if $x == $me_number;
			undef %seen;
			undef %T_Items;
			undef @ascending;
			foreach $line (@TREE_STRUCTURE_FILE) {
				@c = split(/\t/, $line);
				if ($c[3] == $x) {
					$seen{$c[2]} = $c[2];
					$T_Items{$c[2]} .= $line;
					$line = "";
					push (@ascending, $c[2]);
				} elsif ($seen{$c[3]} != 0) {
					$seen{$c[2]} = $seen{$c[3]};
					$T_Items{$seen{$c[2]}} .= $line;
					$line = "";
				}
			}
			@TREE_STRUCTURE_FILE = grep(/\S/, @TREE_STRUCTURE_FILE);
			$promote = $updates{$x}; $promote = $me_number if $promote <= 0;
			$promote = $me_number if $promote == -100;
			@ascending = grep(!/^$promote$/, @ascending);
			unshift(@ascending, $promote);
			undef @NEW_TREE;
			foreach $line (@TREE_STRUCTURE_FILE) {
				@c = split(/\t/, $line);
				if ($c[2] == $x) {
					push (@NEW_TREE, $line);
					foreach $key (@ascending) {
						push (@NEW_TREE, $T_Items{$key});
					}
				} else {
					push (@NEW_TREE, $line);
				}
			}
			@TREE_STRUCTURE_FILE = @NEW_TREE;
			undef @NEW_TREE;
			last if $fancy_reordering == 2;
		}
	}
	$pwline = \@TREE_STRUCTURE_FILE;
	&safe_write("$admin_dir/msg_index/$topic_number-tree.txt", $pwline);
	undef @TREE_STRUCTURE_FILE;
	$me_number = $me_number_hold;
	&unlock("update_time", "$admin_dir/msg_index/$topic_number-tree.txt");
	if ($new_subject_number) {
		&lock("update_time", "$message_dir/$topic_number/$me_number.$ext");
		my ($head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src) = &get_page($topic_number,$me_number);
		$ts = &get_date_time('shorter'); $dt = time;
		$ts = $datetime_in if $datetime_in ne "";
		$url = "$message_url/$topic_number/$new_subject_number.$ext";
		$url .= "?$dt" if !$noqm;
		($sl1) = &format_subtopics($url, $new_subject_name, $ts, 1, $poster_name, 1, $topic_number);
		if ($rev == 0 && $alph == 0) {
			$sublist .= "\n<!-Top: $new_subject_number-!>$sl1\n";
		} else {
			$sublist = "<!-Top: $new_subject_number-!>$sl1\n$sublist";
		}
		@sublist = split(/\n/, $sublist);
		@sublist = grep(/\S/, @sublist);
		@sublist = sort alphabetical_sort @sublist if $alph;
		@sublist = reverse(@sublist) if ($alph && $rev);
		$sublist = join("\n", @sublist);
		$sublist = &regenerate_subtopic_list($topic_number, $me_number, $sublist);
		$lm = time;
		$head_parent_hold = $head; $color_parent_hold = $color;
		&set_page($topic_number, $me_number, $head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src);
		$WRITTEN = 1;
		&unlock("update_time", "$message_dir/$topic_number/$me_number.$ext");
	}
	my ($head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src);
	$me_number = $new_subject_number if $new_subject_number;
	foreach $key (keys(%updates)) {
		next if $key == 0;
		next if ($key == $me_number_hold && $WRITTEN);
		next if $key == $new_subject_number;
		&lock("update_time", "$message_dir/$topic_number/$key.$ext");
		($head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src) = &get_page($topic_number, $key);
		if ($fancy_reordering) {
			if ($fancy_reordering != 2 || $updates{$key} == $me_number) {
				undef @newsublist;
				@sublist = split(/\n/, $sublist);
				undef %printed;
				@ascending = ($updates{$key});
				foreach $line (@sublist) {
					if ($line =~ /<!-Top: (\d+)-!>/ || $line =~ /<!-URL: (\d+)-!>/) {
						$newarray{$1} = $line;
						$printed{$1} = 0;
						push (@ascending, $1) if $1 != $updates{$key};
					}
				}
				foreach $a_key (@ascending) {
					push (@newsublist, $newarray{$a_key});
					$printed{$a_key} = 1;
				}
				foreach $a_key (keys(%printed)) {
					push (@newsublist, $newarray{$a_key}) if $printed{$a_key} == 0;
				}
				$sublist = join("\n", @newsublist);
			}
		}
		$sublist = &regenerate_subtopic_list($topic_number, $key, $sublist);
		&set_page($topic_number, $key, $head, $color, $lm, $ann, $ann_src, $sublist, $about, $about_src, $message, $message_src, $description_src);
		&unlock("update_time", "$message_dir/$topic_number/$key.$ext");
	}
	&change_board_colors("*");
	$me_number = $me_number_hold;
	return ($head_parent_hold, $color_parent_hold);
}

sub alphabetical_sort {
	$a =~ m|<A[^>]+>(.*?)</A>|i; $subnamea = $1;
	$b =~ m|<A[^>]+>(.*?)</A>|i; $subnameb = $1;
	$subnamea =~ tr/A-Z/a-z/;
	$subnameb =~ tr/A-Z/a-z/;
	return 1 if $subnamea gt $subnameb;
	return -1 if $subnamea lt $subnameb;
	return 0;
}

sub alphabetical_tree {
	$a =~ m|^(\d+)\t(\d+)\t(\d+)\t(\d+)\t(\S+)|; $ac = &unescape($5);
	$b =~ m|^(\d+)\t(\d+)\t(\d+)\t(\d+)\t(\S+)|; $bc = &unescape($5);
	$ac =~ tr/A-Z/a-z/;
	$bc =~ tr/A-Z/a-z/;
	return 1 if $ac gt $bc;
	return -1 if $ac lt $bc;
	return 0;
}

#---SEPARATOR---#
#REQ:verify_user_password

sub verify_postread_privileges {
	my ($topic, $username, $password, $special_password, $filename, $setcookie) = @_;
	my (@security, $topic_number, $ip, $usrgrp, $modgrp, @ip, $a, @ipf, $ipc, $ipf, $c, $RA, $RH);
	my ($poster_type, $auth, $line_return); $poster_type = 0; $auth = 0;
	open (SECURITY, "$admin_dir/$filename.txt");
	@security = <SECURITY>;
	close (SECURITY);
	($security) = grep(/^$topic:/, @security);
	$security =~ s/\s+$//;
	if ($security eq "$topic::::" || $security eq "") {
		return 3;
	}
	$valid_usr = 0; $valid_mod = 0; $valid_spec = 0; $valid_public = 0;
	$setc = (1 - $setcookie);
	($topic_number, $ip, $usrgrp, $modgrp, $password_spec) = split(/:/, $security);
	$authorized = 0;
	$banned = 0; $im_public = 0;
	if ($ip ne "") {
		@ip = split(/,/, $ip);
		$order = "";
		foreach $ip (@ip) {
			$a = 0; $ip =~ s/\s//g;
			if ($ip =~ m|^!|) {
				$ip = $';
				$ban = 1;
			} else {
				$ban = 0; $im_public = 1;
			}
			$RA = $ENV{'REMOTE_ADDR'}; $RH = $ENV{'REMOTE_HOST'};
			if ($ip =~ m|~|) {
				@ipf = split(/~/, $ip);
				if ($ip =~ m|~$|) {
					push (@ipf, "");
				}
				$ipc = scalar(@ipf);
				$a = 1; $c = 0;
				foreach $ipf (@ipf) {
					$c += 1;
					if ($c == 1) {
						if ($RA !~ m|^$ipf|i && $RH !~ m|^$ipf|i) {
							$a = 0;
						}
					} elsif ($c == $ipc) {
						if ($RA !~ m|$ipf$|i && $RH !~ m|$ipf$|i) {
							$a = 0;
						}
					} else {
						if ($RA !~ m|$ipf|i && $RH !~ m|$ipf|i) {
							$a = 0;
						}
					}
					$RA = $' if $RA =~ m|$ipf|;
					$RH = $' if $RH =~ m|$ipf|;
				}
				$authorized = 1 if ($a == 1 && $ban == 0);
				$banned = 1 if ($a == 1 && $ban != 0);
			} else {
				if ($RA eq $ip || $RH eq $ip) {
					$authorized = 1 if $ban == 0;
					$banned = 1 if $ban != 0;
				}
			}
		}
		if ($authorized == 1 && $banned == 0) {
			$usrgrp = "~"; $modgrp = "~";
		}
	}
	if ($usrgrp ne "" || $modgrp ne "" || $password ne "") {
		$usrgrp = "~" if ($im_public && $usrgrp eq "");
		$modgrp = "~" if ($im_public && $modgrp eq "");
		@u = split(/,/, $usrgrp); @p = split(/,/, $modgrp);
		($result, $is_su, $is_mod, $is_user) = &verify_user_password($username, $password, 0, $setc);
		@result = split(/\n/, $result);
		$authorized = 0 if ($password ne "" && scalar(@result) == 0);
		foreach $line (@result) {
			chomp $line;
			($filen, $user, $pass, $email, $full, $foo1, $foo2, $foo3, $group) = split(/:/, $line);
			foreach $u (@u) {
				if ($filen eq "USERS" && $group eq $u) {
					$auth = 1;
					$line_return = $line if $line_return eq "";
					$valid_usr = 1;
				} elsif ($filen eq "USERS" && $u eq "~") {
					$auth = 1;
					$line_return = $line if $line_return eq "";
					$valid_usr = 1;
				} elsif ($authorized == 1) {
					$auth = 1;
					$line_return = $line if $line_return eq "";
					$valid_usr = 1;
				}
			}
			foreach $u (@p) {
				if ($filen eq "PASSWD") {
					$v = &verify_owner($u, $user);
					if ($v || $u eq "~" || $authorized == 1) {
						$auth = 1;
						$line_return = $line if $line_return eq "";
						$valid_mod = 1;
					}
				}
			}
		}
	}
	if ($password_spec ne "") {
		$special_password =~ tr/A-Z/a-z/;
		if ($special_password ne "") {
			$c = crypt($special_password, $password_spec);
			if ($c eq $password_spec) {
				$result = &verify_user_password($username, $password);
				@result = split(/\n/, $result);
				$auth = 1; $valid_spec = 1;
			}
		}
	}
	$poster_type = 0; $poster_binary = "";
	if ($valid_usr) {
		$poster_binary = "1"; $poster_type += 1;
	} else {
		$poster_binary = "0";
	}
	if ($valid_mod) {
		$poster_binary .= "1"; $poster_type += 2;
	} else {
		$poster_binary .= "0";
	}
	if ($valid_spec) {
		$poster_binary .= "1"; $poster_type += 4;
	} else {
		$poster_binary .= "0";
	}
	if ($authorized) {
		$poster_binary .= "1"; $poster_type += 8;
	} else {
		$poster_binary .= "0";
	}
	if ($banned == 1) {
		return 2 if ($authorized == 0 && $auth == 0);
		return 2 if ($order eq "D" && $auth == 0);
		return 2 if ($order eq "" && $auth == 0);
	}
	$auth = 1 if $authorized;
	return ($auth, $line_return, $poster_type, $is_su, $is_mod, $is_user, $poster_binary);
}

sub verify_owner {
	my ($owner, $username) = @_;
	$username =~ tr/A-Z/a-z/;
	my (@group_data, $group, $usernames, @username, $groupname);
	open (GROUP, "$admin_dir/groups.txt") || &error_message("File Error", "Cannot open group file (groups.txt)!");
	@group_data = <GROUP>;
	close (GROUP);
	foreach $group (@group_data) {
		chop ($group) if $group =~ /\n$/;
		($groupname, $usernames) = split(/:/, $group);
		if ($groupname eq $owner) {
			@username = split(/,/, $usernames);
			if (grep (/^$username$/, @username)) {
				return 1;
			} else {
				return 0;
			}
		}
	}
	return 0;
}

# END - FILE IS CORRECTLY UPLOADED #
