271 lines
7.2 KiB
Perl
271 lines
7.2 KiB
Perl
|
|
#!/usr/bin/perl
|
||
|
|
#
|
||
|
|
# DW::EmailPost::Comment
|
||
|
|
#
|
||
|
|
# Reply to a comment via email
|
||
|
|
#
|
||
|
|
# Authors:
|
||
|
|
# Afuna <coder.dw@afunamatata.com>
|
||
|
|
#
|
||
|
|
# Copyright (c) 2013 by Dreamwidth Studios, LLC.
|
||
|
|
#
|
||
|
|
# This program is free software; you may redistribute it and/or modify it under
|
||
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
||
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
||
|
|
|
||
|
|
package DW::EmailPost::Comment;
|
||
|
|
|
||
|
|
use base qw(DW::EmailPost::Base);
|
||
|
|
use strict;
|
||
|
|
|
||
|
|
use Digest::SHA;
|
||
|
|
|
||
|
|
use LJ::Utils qw(rand_chars);
|
||
|
|
use LJ::Protocol;
|
||
|
|
use DW::CleanEmail;
|
||
|
|
use LJ::Comment;
|
||
|
|
|
||
|
|
=head1 NAME
|
||
|
|
|
||
|
|
DW::EmailPost::Comment - Handle comment replies via email
|
||
|
|
|
||
|
|
=HEAD1 SYNOPSIS
|
||
|
|
|
||
|
|
# when generating the email:
|
||
|
|
DW::EmailPost::Comment->replyto_address( $poster_u, $journal_u, $ditemid, $dtalkid );
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub _find_destination {
|
||
|
|
my ( $class, @to_addresses ) = @_;
|
||
|
|
|
||
|
|
foreach my $dest (@to_addresses) {
|
||
|
|
next unless $dest =~ /^
|
||
|
|
(\S+?) # any prefix
|
||
|
|
\@\Q$LJ::EMAIL_REPLY_DOMAIN\E
|
||
|
|
$
|
||
|
|
/ix;
|
||
|
|
return $1;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
# Checks the validity of the replyto (left side of the email address).
|
||
|
|
# String is in the format of: userid-journalid-ditemid-dtalkid-auth
|
||
|
|
# Return 1 if success
|
||
|
|
sub _parse_destination {
|
||
|
|
my ( $self, $replyto_token ) = @_;
|
||
|
|
|
||
|
|
my ( $remote_userid, $journalid, $ditemid, $dtalkid, $message_auth ) = split "-",
|
||
|
|
$replyto_token;
|
||
|
|
|
||
|
|
my $u = LJ::load_userid($remote_userid);
|
||
|
|
return unless $u;
|
||
|
|
|
||
|
|
my $ju = LJ::load_userid($journalid);
|
||
|
|
return unless $ju;
|
||
|
|
|
||
|
|
$self->{u} = $u;
|
||
|
|
$self->{ju} = $ju;
|
||
|
|
$self->{journal} = $ju->user;
|
||
|
|
|
||
|
|
# shouldn't happen but just in case
|
||
|
|
return unless $u->emailpost_auth;
|
||
|
|
|
||
|
|
my $class = ref $self;
|
||
|
|
my $real_auth = $class->_hash( $u, $ju, $ditemid, $dtalkid );
|
||
|
|
return $self->err("Invalid secret address. Unable to post as $u->{user}.")
|
||
|
|
unless $message_auth eq $real_auth;
|
||
|
|
|
||
|
|
$self->{ditemid} = $ditemid;
|
||
|
|
$self->{parent} = $dtalkid;
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub _process {
|
||
|
|
my $self = $_[0];
|
||
|
|
|
||
|
|
$self->_check_sender or return $self->send_error;
|
||
|
|
|
||
|
|
$self->_set_props( $self->{u}, %{ $self->{post_headers} || {} } );
|
||
|
|
|
||
|
|
# remove reply cruft
|
||
|
|
$self->{body} = DW::CleanEmail->nonquoted_text( $self->{body} );
|
||
|
|
$self->{subject} =
|
||
|
|
DW::EmailPost::Comment->determine_subject( $self->{subject}, $self->{ju}, $self->{ditemid},
|
||
|
|
$self->{parent} );
|
||
|
|
|
||
|
|
$self->cleanup_body_final;
|
||
|
|
|
||
|
|
# build the comment
|
||
|
|
my $req = {
|
||
|
|
ver => 1,
|
||
|
|
|
||
|
|
username => $self->{u}->username,
|
||
|
|
journal => $self->{ju}->username,
|
||
|
|
ditemid => $self->{ditemid},
|
||
|
|
parent => $self->{parent},
|
||
|
|
|
||
|
|
body => $self->{body},
|
||
|
|
subject => $self->{subject},
|
||
|
|
prop_picture_keyword => $self->{props}->{picture_keyword},
|
||
|
|
prop_editor => $self->{props}->{editor},
|
||
|
|
|
||
|
|
useragent => "emailpost",
|
||
|
|
};
|
||
|
|
|
||
|
|
# post!
|
||
|
|
my $post_error;
|
||
|
|
LJ::Protocol::do_request( "addcomment", $req, \$post_error, { noauth => 1, nocheckcap => 1 } );
|
||
|
|
return $self->send_error( LJ::Protocol::error_message($post_error) ) if $post_error;
|
||
|
|
|
||
|
|
return ( 1, "Comment success" );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
# check that this either from their raw address, or one of their allowed senders
|
||
|
|
# we use raw address so that they don't need to do additional setup to reply as mobile post
|
||
|
|
sub _check_sender {
|
||
|
|
my $self = $_[0];
|
||
|
|
|
||
|
|
my $addrlist = LJ::Emailpost::Web::get_allowed_senders( $self->{u}, 1 );
|
||
|
|
|
||
|
|
# well this shouldn't happen because we include their raw email in the allowed senders
|
||
|
|
unless ( ref $addrlist && keys %$addrlist ) {
|
||
|
|
return $self->err( "No allowed senders have been saved for your account.",
|
||
|
|
{ nomail => 1 } );
|
||
|
|
}
|
||
|
|
|
||
|
|
my $from = $self->{from};
|
||
|
|
return $self->err("Unauthorized sender address: $from")
|
||
|
|
unless grep { lc $from eq lc $_ } keys %$addrlist;
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub _set_props {
|
||
|
|
my ( $self, $u, %post_headers ) = @_;
|
||
|
|
|
||
|
|
my $props = {};
|
||
|
|
$props->{picture_keyword} = $post_headers{userpic}
|
||
|
|
|| $post_headers{icon};
|
||
|
|
$props->{editor} = $self->_choose_editor( $post_headers{format} );
|
||
|
|
|
||
|
|
$self->{props} = $props;
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub dblog_opts {
|
||
|
|
return ( t => "reply" );
|
||
|
|
}
|
||
|
|
|
||
|
|
=head1 Class Methods
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
# Generates the hash to be used in the reply to address
|
||
|
|
sub _hash {
|
||
|
|
my ( $class, $u, $ju, $ditemid, $dtalkid ) = @_;
|
||
|
|
return Digest::SHA::sha256_hex( $ju->id . $ditemid . $dtalkid . $u->emailpost_auth );
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< $class->replyto_address( $u, $journal, $ditemid, $dtalkid ) >>
|
||
|
|
|
||
|
|
Get the reply-to address for this user + journal + entry + comment combination
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub replyto_address {
|
||
|
|
my ( $class, $u, $journalu, $ditemid, $dtalkid ) = @_;
|
||
|
|
return join( "-",
|
||
|
|
$u->userid, $journalu->userid, $ditemid, $dtalkid,
|
||
|
|
$class->_hash( $u, $journalu, $ditemid, $dtalkid ) )
|
||
|
|
. "\@$LJ::EMAIL_REPLY_DOMAIN";
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< $class->replyto_address_header( $u, $journal, $ditemid, $dtalkid ) >>
|
||
|
|
|
||
|
|
Returns the reply-to address with a pretty name, suitable for use in the reply-to-address header
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub replyto_address_header {
|
||
|
|
my ( $class, $u, $journal, $ditemid, $dtalkid ) = @_;
|
||
|
|
|
||
|
|
my $reply_as = LJ::Lang::get_default_text(
|
||
|
|
"emailpost.reply.address",
|
||
|
|
{
|
||
|
|
user => $u->display_username,
|
||
|
|
}
|
||
|
|
);
|
||
|
|
my $email = $class->replyto_address( $u, $journal, $ditemid, $dtalkid );
|
||
|
|
return qq{"$reply_as" <$email>};
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< $class->determine_subject( $email_subject, $ju, $ditemid, $parent ) >>
|
||
|
|
|
||
|
|
Decide what the subject should be (either from the email or the parent comments)
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub determine_subject {
|
||
|
|
my ( $class, $email_subject, $ju, $ditemid, $parent ) = @_;
|
||
|
|
|
||
|
|
my $generated_subject_id = ' [ ' . $ju->display_name . ' - ' . $ditemid . ' ]';
|
||
|
|
|
||
|
|
# use subject from email first
|
||
|
|
my $subject = $email_subject;
|
||
|
|
|
||
|
|
# does the email subject look like we generated it?
|
||
|
|
# if so, then we assume we want to use the parent comment's subject (if any)
|
||
|
|
# otherwise we assume they've set their own comment subject (no need to clean then)
|
||
|
|
if ( $subject =~ /\Q$generated_subject_id\E$/ ) {
|
||
|
|
|
||
|
|
# we always have a parent comment, because that's the only way we can get an auth hash
|
||
|
|
# if that changes, we'll have to add checking here
|
||
|
|
my $parent_obj = LJ::Comment->new( $ju, dtalkid => $parent );
|
||
|
|
$subject = DW::CleanEmail->reply_subject( $parent_obj->subject_text );
|
||
|
|
}
|
||
|
|
|
||
|
|
return $subject;
|
||
|
|
}
|
||
|
|
|
||
|
|
=head1 User Methods
|
||
|
|
|
||
|
|
=head2 C<< $u->generate_emailpost_auth() >>
|
||
|
|
|
||
|
|
Generates an auth to be used when replying to a comment via email
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub generate_auth {
|
||
|
|
my ($u) = $_[0];
|
||
|
|
|
||
|
|
my $auth = LJ::rand_chars(32);
|
||
|
|
$u->set_prop( emailpost_auth => $auth );
|
||
|
|
|
||
|
|
# just log that the emailpost_auth has changed
|
||
|
|
# automatically records remote / uniq / ip if available
|
||
|
|
$u->log_event('emailpost_auth');
|
||
|
|
|
||
|
|
return $auth;
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< $u->emailpost_auth >>
|
||
|
|
|
||
|
|
Gets the auth to be used when replying to a comment via email.
|
||
|
|
|
||
|
|
If you don't have one yet, generate one automatically.
|
||
|
|
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub emailpost_auth {
|
||
|
|
my ($u) = $_[0];
|
||
|
|
|
||
|
|
return $u->prop("emailpost_auth") || $u->generate_emailpost_auth;
|
||
|
|
}
|
||
|
|
|
||
|
|
*LJ::User::generate_emailpost_auth = \&generate_auth;
|
||
|
|
*LJ::User::emailpost_auth = \&emailpost_auth;
|