# ================================================================== # Gossamer Forum - Advanced web community # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # Revision : $Id: Message.pm,v 1.58.2.1 2003/01/31 21:36:39 jagerman Exp $ # # Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved. # Redistribution in part or in whole strictly prohibited. Please # see LICENSE file for full details. # ================================================================== # package GForum::Message; use strict; use vars qw/$Attachment_Max/; use GForum qw/:user $DB $IN $CFG $USER/; use GForum::Convert; use constants NEW => 0, # READ => 1, # These are for the msg_status field REPLIED => 2; # use GT::AutoLoader; $COMPILE{view} = __LINE__ . <<'END_OF_SUB'; sub view { shift; my ($do, $func) = @_; my $page = $func->{page}; my $message_id = $IN->param('message'); my $message; unless ($message_id and $message = $DB->table('Message', 'User')->select(left_join => { msg_id => $message_id, to_user_id_fk => $USER->{user_id} })->fetchrow_hashref) { return( $page->{no_such_message} => { error => GForum::language('MESSAGE_DOES_NOT_EXIST') } ); } normalize($message); $DB->table('Message')->update({ msg_status => READ }, { msg_id => $message_id }) unless $message->{msg_status}; return( $page->{view} => { %$message } ); } END_OF_SUB $COMPILE{sent_view} = __LINE__ . <<'END_OF_SUB'; sub sent_view { shift; my ($do, $func) = @_; my $page = $func->{page}; my $message_id = $IN->param('sent_message'); my $message; unless ($message_id and $message = ($DB->table('SentMessage', 'User')->select(left_join => { msg_id => $message_id, from_user_id_fk => $USER->{user_id} }) or die $GT::SQL::error)->fetchrow_hashref) { return( $page->{no_such_message} => { error => GForum::language('MESSAGE_DOES_NOT_EXIST') } ); } @$message{keys %$USER} = values %$USER; normalize($message, 1); return( $page->{view} => { %$message } ); } END_OF_SUB $COMPILE{delete} = __LINE__ . <<'END_OF_SUB'; sub delete { my @delete = $IN->param('delete'); my $num_deleted = 0; my $sent_num_deleted = 0; if (@delete) { $num_deleted = $DB->table('Message')->delete({ to_user_id_fk => $USER->{user_id}, msg_id => \@delete }) or die $GT::SQL::error; } my @sent_delete = $IN->param('sent_delete'); if (@sent_delete) { $sent_num_deleted = $DB->table('SentMessage')->delete({ from_user_id_fk => $USER->{user_id}, msg_id => \@sent_delete }) or die $GT::SQL::error; } $GForum::Template::VARS{num_deleted} = 0 + $num_deleted; $GForum::Template::VARS{sent_num_deleted} = 0 + $sent_num_deleted; GForum::do_func('message_list'); } END_OF_SUB # This function is called from the admin templates $COMPILE{delete_old} = __LINE__ . <<'END_OF_SUB'; sub delete_old { my $num_days = shift or return; my $cutoff = time - 24 * 60 * 60 * $num_days; my $deleted = $DB->table('Message')->delete(GT::SQL::Condition->new(msg_time => '<' => $cutoff)) or die $GT::SQL::error; return { messages_deleted => 0 + $deleted } } END_OF_SUB $COMPILE{count_old} = __LINE__ . <<'END_OF_SUB'; sub count_old { my $num_days = shift or return; my $cutoff = time - 24 * 60 * 60 * $num_days; my $count = $DB->table('Message')->count(GT::SQL::Condition->new(msg_time => '<' => $cutoff)); die $GT::SQL::error if not defined $count; return $count; } END_OF_SUB $COMPILE{list} = __LINE__ . <<'END_OF_SUB'; sub list { shift; my ($do, $func) = @_; my $page = $func->{page}; my @messages; my $mu = $DB->table('Message' => 'User'); my $sb = $IN->param('sb'); my $mh = $IN->param('mh'); my $pg = $IN->param('page') || 1; my $so = $IN->param('so'); $sb = 'msg_time' unless $sb and exists $mu->{tables}->{$DB->prefix . "Message"}->{schema}->{cols}->{$sb}; $mh = $USER->{user_default_mh_message} || $CFG->{default_mh_message} || 25 if not $mh or $mh =~ /\D/; $pg = 1 if $pg =~ /\D/; my $start = ($pg - 1) * $mh; $so = ($so and (uc $so eq 'ASC' or uc $so eq 'DESC')) ? uc $so : $sb eq 'msg_time' ? 'DESC' : 'ASC'; $mu->select_options("ORDER BY $sb $so"); $mu->select_options("LIMIT $start, $mh"); my $messages = $mu->select('left_join', { to_user_id_fk => $USER->{user_id} }) or die $GT::SQL::error; my $Message = $DB->table('Message'); $Message->select_options('GROUP BY msg_status'); my %num = $Message->select('msg_status', 'COUNT(*)', { to_user_id_fk => $USER->{user_id} })->fetchall_list; my $new = $num{NEW()} || 0; my $read = $num{READ()} || 0; my $replied = $num{REPLIED()} || 0; my $num_messages = $new + $read + $replied; my $msg_loop = []; while (my $msg = $messages->fetchrow_hashref) { push @$msg_loop, $msg; } normalize($msg_loop); return( $page->{list} => { num_messages => $num_messages, num_new => $new, num_read => $read, num_replied => $replied, messages => $msg_loop, sb => $sb, so => $so, this_page => $pg, mh => $mh } ); } END_OF_SUB # This page returns the same variables as in list()'s template, but with sent_ # prepended. This way this can be called from any page to display the sent # messages. sb, mh, page, etc. should all be prefixed with "sent_". $COMPILE{sent_messages} = __LINE__ . <<'END_OF_SUB'; sub sent_messages { my @messages; my $su = $DB->table('SentMessage' => 'User'); my $sb = $IN->param('sent_sb'); my $mh = $IN->param('mh'); my $pg = $IN->param('sent_page') || 1; my $so = $IN->param('sent_so'); $sb = 'msg_time' unless $sb and exists $su->{tables}->{$DB->prefix . "SentMessage"}->{schema}->{cols}->{$sb}; $mh = $USER->{user_default_mh_message} || $CFG->{default_mh_message} || 25 if not $mh or $mh =~ /\D/; $pg = 1 if $pg =~ /\D/; my $start = ($pg - 1) * $mh; $so = ($so and (uc $so eq 'ASC' or uc $so eq 'DESC')) ? uc $so : $sb eq 'msg_time' ? 'DESC' : 'ASC'; $su->select_options("ORDER BY $sb $so"); $su->select_options("LIMIT $start, $mh"); my $messages = $su->select('left_join', { from_user_id_fk => $USER->{user_id} }) or die $GT::SQL::error; my $num_messages = $DB->table('SentMessage')->count({ from_user_id_fk => $USER->{user_id} }); my $msg_loop = []; while (my $msg = $messages->fetchrow_hashref) { push @$msg_loop, $msg; } normalize($msg_loop, 1); return { sent_num_messages => $num_messages, sent_messages => $msg_loop, sent_sb => $sb, sent_so => $so, this_page => $pg }; } END_OF_SUB $COMPILE{attachment_upload} = __LINE__ . <<'END_OF_SUB'; sub attachment_upload { my $temp_id = $IN->param('temp_id'); if (not $CFG->{allow_message_attachments}) { $GForum::Template::VARS{attachment_error} = GForum::language('ATTACHMENT_NOT_ALLOWED'); return GForum::do_func($IN->param('redo')); } my $file = $IN->param("msg_attachment") or return GForum::do_func($IN->param('redo')); my ($filename, $ext) = $file =~ m{([^\\/]*(?:\.([^.]+))?)$}; require GT::MIMETypes; my $content_type = GT::MIMETypes->guess_type(".$ext"); my $attach = $DB->table('TempAttachment'); my $error; $attach->insert(Message => { tempatt_msg_id => $temp_id, tempatt_filename => $filename, tempatt_content => $content_type, tempatt_fh => $file }) or $error = $attach->{attachment_error}; $GForum::Template::VARS{attachment_error} = $error if $error; GForum::do_func($IN->param('redo')); } END_OF_SUB $COMPILE{attachment_delete} = __LINE__ . <<'END_OF_SUB'; sub attachment_delete { my $att_id = $IN->param('att_id'); my $type = $IN->param('att_type'); # It's a temporary attachment we need to delete. $DB->table('TempAttachment')->delete($att_id); GForum::do_func($IN->param('redo')); } END_OF_SUB $COMPILE{preview} = __LINE__ . <<'END_OF_SUB'; sub preview { my $self = shift; my ($do, $func) = @_; my $page = $func->{page}; my $redo = $IN->param('redo') or die 'GForum::Message->preview called with bad arguments - CGI parameter redo not set'; my $message; my $mt = $DB->table('Message'); for (grep exists $mt->{schema}->{cols}->{$_}, $IN->param) { $message->{$_} = $IN->param($_); } my $user_id = $IN->param('user') or my $username = $IN->param('user_username'); my $user = $user_id ? $DB->table('User')->get($user_id) : $DB->table('User')->select({ user_username => $username })->fetchrow_hashref; require GForum::User; $user->{user_signature} = $USER->{user_signature}; # Otherwise the wrong signature shows up GForum::User::normalize($user); @$message{keys %$user} = values %$user; $message->{msg_time} = time; $message->{msg_ip} = $ENV{REMOTE_ADDR}; if ($message->{msg_body} and $IN->param('advanced_editor')) { $message->{msg_body} = GForum::Convert::advanced_editor_convert($message->{msg_body}); } $message->{msg_body} .= "\n[$CFG->{markup_signature_tag}]" if $IN->param('msg_append_signature'); normalize($message); $GForum::Template::VARS{preview} = \GForum::Template->parse($page->{preview} => { %$message, preview => 1 }); GForum::do_func($redo); } END_OF_SUB $COMPILE{write} = __LINE__ . <<'END_OF_SUB'; sub write { shift; # Discard the package name my ($do, $func) = @_; my $page = $func->{page}; my $user_id = $IN->param('user'); my $username = $IN->param('user_username'); my ($ask_username, $user); if (not $user_id and not $username and not $IN->param('tried_sending')) { $ask_username = 1; } else { if ($username and not $user_id) { $user_id = $DB->table('User')->select(user_id => { user_username => $username })->fetchrow; } unless ($user_id and $user = $DB->table('User')->get($user_id)) { if ($username or $IN->param('tried_sending')) { $GForum::Template::VARS{bad_username} = 1; $GForum::Template::VARS{user_username} = $username; $ask_username = 1; } else { return( $page->{no_such_user} => { error => GForum::language($user_id ? ('USER_DOES_NOT_EXIST') : ('USERNAME_DOES_NOT_EXIST' => $username)) } ) } } } my $style = $IN->param('msg_style'); $style = $USER->{user_default_message_style} || 3 if not defined $style or $style =~ /\D/; if ($CFG->{message_style} < $style or $CFG->{message_style} == 2 and $style == 1) { $style = $CFG->{message_style}; } my $temp_id; my $attach = []; if ($temp_id = $IN->param('temp_id')) { my $sth = $DB->table('TempAttachment')->select({ tempatt_msg_id => $temp_id }); while (my $rec = $sth->fetchrow_hashref) { for (keys %$rec) { if (s/^temp//) { $rec->{$_} = delete $rec->{"temp$_"}; } } push @$attach, $rec; } } else { $temp_id = generate_temp_id(); } my $can_attach = !cant_attach($temp_id); my $msg_subject = $IN->param('msg_subject') || ''; my $msg_body = $IN->param('msg_body') || ''; my $orig_msg_body; my ($is_ie, $ie_version); if ($ENV{HTTP_USER_AGENT} and $ENV{HTTP_USER_AGENT} =~ /MSIE (\d+(?:\.\d+)?)/i and $ENV{HTTP_USER_AGENT} !~ /mac/i) { $is_ie = 1; $ie_version = $1; } if ($msg_body and $IN->param('basic_editor_switch')) { $msg_body = GForum::Convert::advanced_editor_convert($msg_body); } elsif ( ($IN->param('advanced_editor_switch') or not $IN->param('advanced_editor')) and ( ($msg_body and $IN->param('advanced_editor_switch')) or ($IN->param('advanced_editor') and $is_ie and $ie_version >= 5.5) or ($USER->{user_advanced_editor} and not $IN->param('basic_editor') and $is_ie and $ie_version >= 5.5) ) ) { $orig_msg_body = $msg_body; _msg_body_html(\$msg_body, $style >= 2); } my $msg_append_sig = $IN->param('msg_append_signature'); $msg_append_sig = 1 if not $msg_subject and not $msg_body; $ask_username = 1 if not $user or not $user->{user_accept_privmsg}; my %page_data = ( $ask_username ? (ask_username => 1) : (%$user), ($user and !$user->{user_accept_privmsg}) ? (user_no_privmsg => 1) : (), message_style => $CFG->{message_style}, can_attach => $can_attach, msg_style_selected => $style, msg_subject => $msg_subject, msg_body => $msg_body, orig_msg_body => $orig_msg_body, attachments => $attach, num_attachments => scalar @$attach, temp_id => $temp_id, msg_append_signature => $msg_append_sig, advanced_editor => scalar $IN->param('advanced_editor'), basic_editor => scalar $IN->param('basic_editor') ); my $cols = $DB->table('Message')->cols; for my $col (keys %$cols) { next if $cols->{$col}->{protect} or exists $page_data{$col} or not defined(my $val = $IN->param($col)); $page_data{$col} = $val; } return($page->{write} => \%page_data); } END_OF_SUB $COMPILE{reply_write} = __LINE__ . <<'END_OF_SUB'; sub reply_write { shift; my ($do, $func) = splice @_, 0, 2; my $page = $func->{page}; my $parent_id = $IN->param('message') || $IN->param('reply_to'); my $parent; unless ($parent_id and $parent = $DB->table('Message')->get({ msg_id => $parent_id, to_user_id_fk => $USER->{user_id} })) { return( $page->{no_such_message} => { error => GForum::language('MESSAGE_DOES_NOT_EXIST') } ); } my $user; if ($parent->{from_user_id_fk}) { $user = $DB->table('User')->get($parent->{from_user_id_fk}); } else { return( $page->{no_such_user} => { error => GForum::language('USER_DOES_NOT_EXIST') } ); } @$parent{keys %$user} = values %$user; my $style = $IN->param('msg_style'); $style = $USER->{user_default_message_style} || 3 if not defined $style or $style =~ /\D/; if ($CFG->{message_style} < $style or $CFG->{message_style} == 2 and $style == 1) { $style = $CFG->{message_style}; } normalize($parent); for (sort { length($b) <=> length($a) } keys %$parent) { $parent->{"parent_$_"} = delete $parent->{$_}; } my $subject = $IN->param('msg_subject'); my $msg_body = $IN->param('msg_body') || ''; my $orig_msg_body; my $msg_append_sig = $IN->param('msg_append_signature'); $msg_append_sig = 1 if not $subject and not $msg_body; unless ($subject = $IN->param('msg_subject')) { my $re = GForum::language('POST_REGARDING'); ($subject = $parent->{parent_msg_subject}) =~ s/^\s*(?:\Q$re\E)?/$re/; } my ($is_ie, $ie_version); if ($ENV{HTTP_USER_AGENT} and $ENV{HTTP_USER_AGENT} =~ /MSIE (\d+(?:\.\d+)?)/i and $ENV{HTTP_USER_AGENT} !~ /mac/i) { $is_ie = 1; $ie_version = $1; } if ($msg_body and $IN->param('basic_editor_switch')) { $msg_body = GForum::Convert::advanced_editor_convert($msg_body); } elsif ( ($IN->param('advanced_editor_switch') or not $IN->param('advanced_editor')) and ( ($msg_body and $IN->param('advanced_editor_switch')) or ($IN->param('advanced_editor') and $is_ie and $ie_version >= 5.5) or ($USER->{user_advanced_editor} and not $IN->param('basic_editor') and $is_ie and $ie_version >= 5.5) ) ) { $orig_msg_body = $msg_body; _msg_body_html(\$msg_body, $style >= 2); } my $temp_id; my $attach = []; if ($temp_id = $IN->param('temp_id')) { my $sth = $DB->table('TempAttachment')->select({ tempatt_msg_id => $temp_id }); while (my $rec = $sth->fetchrow_hashref) { for (keys %$rec) { if (s/^temp//) { $rec->{$_} = delete $rec->{"temp$_"}; } } push @$attach, $rec; } } else { $temp_id = generate_temp_id(); } my $can_attach = !cant_attach($temp_id); my %page_data = ( %$parent, %$user, user_cols => $USER->{user_cols} || 60, user_rows => $USER->{user_rows} || 10, msg_style_selected => $style, msg_subject => $subject, msg_body => $msg_body, can_attach => $can_attach, attachments => $attach, num_attachments => scalar @$attach, temp_id => $temp_id, msg_append_signature => $msg_append_sig, advanced_editor => scalar $IN->param('advanced_editor'), basic_editor => scalar $IN->param('basic_editor'), message_style => $CFG->{message_style} ); my $cols = $DB->table('Message')->cols; for my $col (keys %$cols) { next if $cols->{$col}->{protect} or exists $page_data{$col} or not defined(my $val = $IN->param($col)); $page_data{$col} = $val; } return($page->{reply_write} => \%page_data); } END_OF_SUB # Converts a scalar reference into its HTML equivelant. Used when switching to # the advanced editor. Converts markup into HTML. Note, however, that this # differs in one way: URL's, which markup converts to: gforum.cgi?url=... do not # forward through gforum.cgi and are kept as the original URL. $COMPILE{_msg_body_html} = __LINE__ . <<'END_OF_SUB'; sub _msg_body_html { my ($message, $html) = @_; GForum::Convert::escape_html($$message) unless $html; GForum::Convert::convert_markup($message); # Convert the URL's: has to become . Also, the .... has to be url unescaped. $$message =~ s//qq||/eg; $$message =~ s/\r?\n/
/g; # That space keeps IE from condensing multiple
's into 1. It is only needed where you have

, and only inside
, but the necessary regex would slow the conversion down quite a bit.
    $$message =~ s/^( +)/' ' x length $1/gem;

    $message;
}
END_OF_SUB

# Takes a single argument - the temp ID.
# Returns an error message if messsages can't be attached, undef if everything is good to go.
$COMPILE{cant_attach} = __LINE__ . <<'END_OF_SUB';
sub cant_attach {
    my @args = @_;
    GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins', 'message_cant_attach', sub { return _plg_cant_attach(@args) });
}

sub _plg_cant_attach {
    $Attachment_Max = undef;
    my $temp_id = shift;
    if (!$CFG->{allow_message_attachments}) {
        return GForum::language('ATTACHMENTS_NOT_ALLOWED');
    }
    if ($CFG->{message_attachments_max_size}) {
        my $total_size = $DB->table('MessageAttachment')->_attachments_size;
        if ($total_size >= $CFG->{message_attachments_max_size}) {
            return GForum::language('ATTACHMENT_LIMIT_EXCEEDED');
        }
        $Attachment_Max = $CFG->{message_attachments_max_size} - $total_size;
        $Attachment_Max = $CFG->{message_attachment_max_size} if $CFG->{message_attachment_max_size} < $Attachment_Max;
    }
    if ($CFG->{message_attachments_max_count}) {
        my $count = $DB->table('MessageAttachment')->count + $DB->table('TempAttachment')->count({ tempatt_type => 'Message' });
        if ($count >= $CFG->{message_attachments_max_count}) {
            return GForum::language('ATTACHMENT_COUNT_EXCEEDED');
        }
    }
    if ($temp_id and $CFG->{message_attachments}) {
        my $count = $DB->table('TempAttachment')->count({ tempatt_msg_id => $temp_id });
        if ($count >= $CFG->{message_attachments}) {
            return GForum::language('ATTACHMENT_COUNT_EXCEEDED');
        }
    }
    return;
}
END_OF_SUB

$COMPILE{generate_temp_id} = __LINE__ . <<'END_OF_SUB';
sub generate_temp_id { require GT::MD5; GT::MD5::md5_hex(time . $$ . rand(16000)) }
END_OF_SUB

$COMPILE{send} = __LINE__ . <<'END_OF_SUB';
sub send {
    shift; # Discard the package name
    uc $ENV{REQUEST_METHOD} eq 'POST' or die "Unable to send with a GET method";
    my ($do, $func) = @_;
    my $page = $func->{page};
    my $user_id = $IN->param('user');
    my $user_username = $IN->param('user_username') if not $user_id;
    my $temp_id = $IN->param('temp_id');

    my $recipient;
    if ($user_id) {
        $recipient = $DB->table('User')->get($user_id)
            or return GForum::do_func($IN->param('reply_to') ? 'message_reply_write' : 'message');
    }
    elsif ($user_username) { # Try to work off the username
        $recipient = $DB->table('User')->select({ user_username => $user_username })->fetchrow_hashref
            or return GForum::do_func($IN->param('reply_to') ? 'message_reply_write' : 'message');
        $user_id = $recipient->{user_id};
    }
    else {
        $IN->param(tried_sending => 1);
        return GForum::do_func($IN->param('reply_to') ? 'message_reply_write' : 'message');
    }

    $recipient->{user_accept_privmsg}
        or return(
            $page->{no_privmsg} => {
                error => GForum::language('USER_NO_PRIVMSG', $recipient->{user_username})
            }
        );

    my $message = $IN->param('msg_body');

    if ($IN->param('advanced_editor')) {
        $message = GForum::Convert::advanced_editor_convert($message);
    }
    $message =~ s/\s+$//; # Remove all trailing whitespace
    $message .= "\n" . $USER->{user_signature} if $IN->param('msg_append_signature');

    my $subject = $IN->param('msg_subject');
    $subject = GForum::language("NO_SUBJECT") unless $subject =~ /[^\s\xa0]/; # \xa0 is Alt-0160 - a non-breaking space

    my $ip = $ENV{REMOTE_ADDR};

    my $style = $IN->param('msg_style');
    $style = $USER->{user_default_message_style} || 3 if not defined $style or $style =~ /\D/;
    if ($CFG->{message_style} < $style or $CFG->{message_style} == 2 and $style == 1) {
        $style = $CFG->{message_style};
    }

    my $insert = {
        to_user_id_fk => $user_id,
        from_user_id_fk => $USER->{user_id},
        msg_subject => $subject,
        msg_body => $message,
        msg_time => time,
        msg_ip => $ip,
        msg_username => $USER->{user_username},
        msg_style => $style
    };
        
    my ($Message, $SentMessage) = ($DB->table('Message'), $DB->table('SentMessage'));
    my $cols = $Message->cols;
    for my $col (keys %$cols) {
        next if $cols->{$col}->{protect} or exists $insert->{$col} or not defined(my $val = $IN->param($col));
        $insert->{$col} = $val;
    }

    my $msg_id = ($DB->table('Message')->insert($insert) or die $GT::SQL::error)->insert_id;

    $insert = {
        msg_id => $msg_id,
        to_user_id_fk => $user_id,
        from_user_id_fk => $USER->{user_id},
        msg_subject => $subject,
        msg_body => $message,
        msg_time => time,
        msg_ip => $ip,
        msg_username => $recipient->{user_username}
    };
    $cols = $SentMessage->cols;
    for my $col (keys %$cols) {
        next if $cols->{$col}->{protect} or exists $insert->{$col} or not defined(my $val = $IN->param($col));
        $insert->{$col} = $val;
    }

    $SentMessage->insert($insert) or die $GT::SQL::error;

    if (my $parent_id = $IN->param('reply_to')) {
        $Message->update({ msg_status => REPLIED }, { msg_id => $parent_id, to_user_id_fk => $USER->{user_id} });
    }

    my @attach_errors;
    my $ma = $DB->table('MessageAttachment');
    # Get all the temporary attachments associated with this ID so that we can
    # transfer the attachments from TempAttachment to MessageAttachment
    my $sth = $DB->table('TempAttachment')->select('tempatt_id', { tempatt_msg_id => $temp_id });
    while (my $tempatt_id = $sth->fetchrow_array) {
        $ma->insert({ tempatt_id => $tempatt_id, msg_id_fk => $msg_id })
            or push @attach_errors, delete $ma->{attachment_error};
    }

    my $fresh_msg = $DB->table('Message', 'User')->select({ msg_id => $msg_id })->fetchrow_hashref or die $GT::SQL::error; # By selecting it again, we make
    # sure anything the subclass or plugins do is displayed.
    $fresh_msg->{msg_body_text} = $fresh_msg->{msg_body};
    normalize($fresh_msg);

    @$fresh_msg{map "to_$_", keys %$recipient} = values %$recipient;

    if ($recipient->{user_message_notify} and $recipient->{user_email}) {
        plain_text(\$fresh_msg->{msg_body_text}, $fresh_msg); # Strip out markup and HTML

        require GT::Mail::Editor;
        my $email = GT::Mail::Editor->new(dir => "$CFG->{admin_root_path}/templates", template => ($recipient->{user_template} || $CFG->{default_template_set}));
        $email->load("message.eml");

        my $headers = $email->headers;
        my %head;
        while (my ($k, $v) = each %$headers) {
            my $val = $v; # Copy it
            $val = GForum::Template->parse("string", $fresh_msg, { string => $val });
            $head{$k} = $val;
        }
        my $body = $email->body;
        $body = GForum::Template->parse("string", $fresh_msg, { string => $body });
        $CFG->{smtp_server} or $CFG->{mail_path} or die 'No mail path of SMTP server set!';
        $head{To} ||= $recipient->{user_email};
        $head{From} ||= $CFG->{admin_email};
        require GT::Mail;
        my $mailer = GT::Mail->new(
            %head,
            msg => $body,
            ($CFG->{smtp_server} ? (smtp => $CFG->{smtp_server}) : (sendmail => $CFG->{mail_path}))
        );

        local $@;
        eval { $mailer->send };
        if ($@ and $GForum::DEBUG || $CFG->{debug_level}) {
            die $mailer->error;
        }
    }

    return(
        $page->{send} => {
            %$fresh_msg,
            (@attach_errors ? (attachment_errors => GForum::language('ATTACHMENT_FAILED', "
\n" . join '', map "$_->[0]: $_->[1]
\n", @attach_errors)) : ()), } ); } END_OF_SUB $COMPILE{download_attachment} = __LINE__ . <<'END_OF_SUB'; sub download_attachment { shift; # Discard the package name my ($do, $func) = @_; my $page = $func->{page}; my $messatt_id; $messatt_id = $IN->param('messatt_id') and my $attachment = $DB->table('MessageAttachment' => 'Message')->select({ messatt_id => $messatt_id, to_user_id_fk => $USER->{user_id} })->fetchrow_hashref or return( $page->{no_such_message_attachment} => { error => GForum::language('ATTACHMENT_DOES_NOT_EXIST') } ); my $file = \do { local *FH; *FH }; my $dir = $attachment->{messatt_id} % 10; my $filename = "$CFG->{message_attachment_directory}/$dir/$messatt_id"; unless (open $file, "<$filename") { $DB->table('MessageAttachment')->delete($messatt_id); return($page->{no_such_message_attachment}, { messatt_if => $messatt_id }); } binmode $file; binmode STDOUT; print $IN->header( -type => $attachment->{messatt_content}, "Content-Disposition" => \("attachment; filename=" . $IN->escape($attachment->{messatt_filename}) . "; size=$attachment->{messatt_size}") ); { local $\; while (read($file, my $chunk, 4096)) { print $chunk; } } return; } END_OF_SUB # Takes two arguments: A scalar reference to a non-normalized msg_body # value, and the normalized Message,User hash it came from. Returns nothing. $COMPILE{plain_text} = __LINE__ . <<'END_OF_SUB'; sub plain_text { my ($str, $msg) = @_; $$str =~ s/
/\n/g if $msg->{msg_style} >= 2; $$str =~ s/<.*?>//g if $msg->{msg_style} >= 2; $$str =~ s/\[(\s*(.*?)\s*)\]/if (exists $CFG->{markup_tags}->{lc $2} or lc $2 eq lc $CFG->{signature_markup_tag}) { "" } elsif (substr($1, 0, 1) eq ".") { "[" . substr($1, 1) . "]" } else { "[$1]" }/eg if $msg->{msg_style} % 2; convert_signature($str, \$msg->{msg_signature}); return; } END_OF_SUB sub normalize { my ($msg, $sent) = @_; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins', "message_normalize", \&_plg_normalize, $msg, $sent); } sub _plg_normalize { my $msgs = ref $_[0] eq 'ARRAY' ? shift : [shift]; my $sent = shift; my $blank; my $literal = $IN->param('literal'); for my $msg (@$msgs) { $msg->{msg_date} = GForum::date($msg->{msg_time}); if ($msg->{msg_style} < 2 or $literal) { escape_html($msg->{msg_body}); } if ($msg->{msg_style} % 2 and not $literal) { convert_markup(\$msg->{msg_body}); } if ($msg->{user_id}) { require GForum::User; GForum::User::normalize($msg); } else { # The user has been deleted require GForum::User; $blank ||= GForum::User::blank_user({ user_username => 'username', user_title => \GForum::language('USER_DELETED'), user_signature => '' }); @$msg{keys %$blank} = values %$blank; $msg->{user_username} = $msg->{msg_username}; $msg->{user_signature} = $msg->{msg_signature_deleted}; } my $signature = $msg->{user_signature}; unless ($CFG->{signature_allow_html} and not $literal) { escape_html($signature); } if ($CFG->{signature_allow_markup} == 2 and not $literal) { convert_markup(\$signature); } elsif ($CFG->{signature_allow_markup} and not $literal) { local $GForum::Convert::No_Image = 1; convert_markup(\$signature); } if (not $CFG->{signature_allow_html} and not $CFG->{signature_allow_markup} or $literal) { $signature =~ s/ / /g; } convert_signature(\$msg->{msg_body}, \$signature); $msg->{msg_body} =~ s/\r?\n/
/g; # That space keeps IE from condensing multiple
's into 1. It is only needed where you have

, but that regex would slow the converter down quite a bit. $msg->{msg_body} =~ s/^( +)/' ' x length $1/gem; my $body = $msg->{msg_body}; $msg->{msg_body} = \$body; } attachments($msgs) unless $sent; # Sent messages don't have attachments set. $msgs } # Takes a array ref of hash refs and sets $hash->{msg_attachments} to an array # ref of hash refs. The hash refs are the attachments of the message. If # "msg_has_attachments" is not set, nothing is done. There is no returned value. sub attachments { my $msgs = ref $_[0] eq 'ARRAY' ? shift : [shift]; my $sth = $DB->table('MessageAttachment')->select({ msg_id_fk => [map { $_->{msg_has_attachments} ? $_->{msg_id} : () } @$msgs] }); my %att; while (my $att = $sth->fetchrow_hashref) { $att->{messatt_filename_escaped} = escape_string($IN->escape($att->{messatt_filename})); push @{$att{$att->{msg_id_fk}}}, $att; } for my $msg (@$msgs) { $msg->{msg_has_attachments} and exists $att{$msg->{msg_id}} or $msg->{msg_num_attachments} = 0, next; my @attachments = @{$att{$msg->{msg_id}}}; $msg->{msg_attachments} = \@attachments; $msg->{msg_num_attachments} = @attachments; } return; } 1;