+Returns an array of this attachment object's headers, with one header
+per array entry. Multiple lines are folded.
+
+B<Never> returns C<RT-Send-Bcc> field.
+
+=cut
+
+sub SplitHeaders {
+ my $self = shift;
+ return (grep !/^RT-Send-Bcc/i, $self->_SplitHeaders(@_) );
+}
+
+=head2 _SplitHeaders
+
+Returns an array of this attachment object's headers, with one header
+per array entry. multiple lines are folded.
+
+
+=cut
+
+sub _SplitHeaders {
+ my $self = shift;
+ my $headers = (shift || $self->_Value('Headers'));
+ my @headers;
+ # XXX TODO: splitting on \n\w is _wrong_ as it treats \n[ as a valid
+ # continuation, which it isn't. The correct split pattern, per RFC 2822,
+ # is /\n(?=[^ \t]|\z)/. That is, only "\n " or "\n\t" is a valid
+ # continuation. Older values of X-RT-GnuPG-Status contain invalid
+ # continuations and rely on this bogus split pattern, however, so it is
+ # left as-is for now.
+ for (split(/\n(?=\w|\z)/,$headers)) {
+ push @headers, $_;
+
+ }
+ return(@headers);
+}
+
+
+sub Encrypt {
+ my $self = shift;
+
+ my $txn = $self->TransactionObj;
+ return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
+ return (0, $self->loc('Permission Denied'))
+ unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
+ return (0, $self->loc('Cryptography is disabled'))
+ unless RT->Config->Get('Crypt')->{'Enable'};
+ return (0, $self->loc('Attachments encryption is disabled'))
+ unless RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'};
+
+ my $type = $self->ContentType;
+ if ( $type =~ /^x-application-rt\/[^-]+-encrypted/i ) {
+ return (1, $self->loc('Already encrypted'));
+ } elsif ( $type =~ /^multipart\//i ) {
+ return (1, $self->loc('No need to encrypt'));
+ }
+
+ my $queue = $txn->TicketObj->QueueObj;
+ my $encrypt_for;
+ foreach my $address ( grep $_,
+ $queue->CorrespondAddress,
+ $queue->CommentAddress,
+ RT->Config->Get('CorrespondAddress'),
+ RT->Config->Get('CommentAddress'),
+ ) {
+ my %res = RT::Crypt->GetKeysInfo( Key => $address, Type => 'private' );
+ next if $res{'exit_code'} || !$res{'info'};
+ %res = RT::Crypt->GetKeysForEncryption( $address );
+ next if $res{'exit_code'} || !$res{'info'};
+ $encrypt_for = $address;
+ }
+ unless ( $encrypt_for ) {
+ return (0, $self->loc('No key suitable for encryption'));
+ }
+
+ my $content = $self->Content;
+ my %res = RT::Crypt->SignEncryptContent(
+ Content => \$content,
+ Sign => 0,
+ Encrypt => 1,
+ Recipients => [ $encrypt_for ],
+ );
+ if ( $res{'exit_code'} ) {
+ return (0, $self->loc('Encryption error; contact the administrator'));
+ }
+
+ my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
+ unless ( $status ) {
+ return ($status, $self->loc("Couldn't replace content with encrypted data: [_1]", $msg));
+ }
+
+ $type = qq{x-application-rt\/$res{'Protocol'}-encrypted; original-type="$type"};
+ $self->__Set( Field => 'ContentType', Value => $type );
+ $self->SetHeader( 'Content-Type' => $type );
+
+ return (1, $self->loc('Successfuly encrypted data'));
+}
+
+sub Decrypt {
+ my $self = shift;
+
+ my $txn = $self->TransactionObj;
+ return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
+ return (0, $self->loc('Permission Denied'))
+ unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
+ return (0, $self->loc('Cryptography is disabled'))
+ unless RT->Config->Get('Crypt')->{'Enable'};
+
+ my $type = $self->ContentType;
+ my $protocol;
+ if ( $type =~ /^x-application-rt\/([^-]+)-encrypted/i ) {
+ $protocol = $1;
+ $protocol =~ s/gpg/gnupg/; # backwards compatibility
+ ($type) = ($type =~ /original-type="(.*)"/i);
+ $type ||= 'application/octet-stream';
+ } else {
+ return (1, $self->loc('Is not encrypted'));
+ }
+
+ my $queue = $txn->TicketObj->QueueObj;
+ my @addresses =
+ $queue->CorrespondAddress,
+ $queue->CommentAddress,
+ RT->Config->Get('CorrespondAddress'),
+ RT->Config->Get('CommentAddress')
+ ;
+
+ my $content = $self->Content;
+ my %res = RT::Crypt->DecryptContent(
+ Protocol => $protocol,
+ Content => \$content,
+ Recipients => \@addresses,
+ );
+ if ( $res{'exit_code'} ) {
+ return (0, $self->loc('Decryption error; contact the administrator'));
+ }
+
+ my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
+ unless ( $status ) {
+ return ($status, $self->loc("Couldn't replace content with decrypted data: [_1]", $msg));
+ }
+ $self->__Set( Field => 'ContentType', Value => $type );
+ $self->SetHeader( 'Content-Type' => $type );
+
+ return (1, $self->loc('Successfuly decrypted data'));
+}
+
+=head2 _Value
+
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
+
+=cut
+
+sub _Value {
+ my $self = shift;
+ my $field = shift;
+
+ #if the field is public, return it.
+ if ( $self->_Accessible( $field, 'public' ) ) {
+ return ( $self->__Value( $field, @_ ) );
+ }
+
+ return undef unless $self->TransactionObj->CurrentUserCanSee;
+ return $self->__Value( $field, @_ );
+}
+
+# Attachments don't change; by adding this cache config directive,
+# we don't lose pathalogically on long tickets.
+sub _CacheConfig {
+ {
+ 'cache_for_sec' => 180,
+ }
+}
+
+
+
+
+=head2 id
+
+Returns the current value of id.