1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
55 This module should never be instantiated directly by client code. it's an internal
56 module which should only be instantiated through exported APIs in Ticket, Queue and other
66 package RT::Attachment;
67 use base 'RT::Record';
69 sub Table {'Attachments'}
80 use MIME::QuotedPrint;
82 use RT::Util 'mime_recommended_filename';
85 sub _OverlayAccessible {
87 TransactionId => { 'read'=>1, 'public'=>1, 'write' => 0 },
88 MessageId => { 'read'=>1, 'write' => 0 },
89 Parent => { 'read'=>1, 'write' => 0 },
90 ContentType => { 'read'=>1, 'write' => 0 },
91 Subject => { 'read'=>1, 'write' => 0 },
92 Content => { 'read'=>1, 'write' => 0 },
93 ContentEncoding => { 'read'=>1, 'write' => 0 },
94 Headers => { 'read'=>1, 'write' => 0 },
95 Filename => { 'read'=>1, 'write' => 0 },
96 Creator => { 'read'=>1, 'auto'=>1, },
97 Created => { 'read'=>1, 'auto'=>1, },
103 Create a new attachment. Takes a paramhash:
105 'Attachment' Should be a single MIME body with optional subparts
106 'Parent' is an optional id of the parent attachment
107 'TransactionId' is the mandatory id of the transaction this attachment is associated with.;
113 my %args = ( id => 0,
119 # For ease of reference
120 my $Attachment = $args{'Attachment'};
122 # if we didn't specify a ticket, we need to bail
123 unless ( $args{'TransactionId'} ) {
124 $RT::Logger->crit( "RT::Attachment->Create couldn't, as you didn't specify a transaction" );
128 # If we possibly can, collapse it to a singlepart
129 $Attachment->make_singlepart;
131 my $head = $Attachment->head;
134 my $Subject = Encode::decode( 'UTF-8', $head->get( 'subject' ) );
135 $Subject = '' unless defined $Subject;
139 my $MessageId = Encode::decode( "UTF-8", $head->get( 'Message-ID' ) );
140 defined($MessageId) or $MessageId = '';
142 $MessageId =~ s/^<(.*?)>$/$1/o;
145 my $Filename = mime_recommended_filename($Attachment);
148 $Filename =~ s!.*/!! if $Filename;
151 unless ( $head->get('Content-Length') ) {
153 $length = length $Attachment->bodyhandle->as_string
154 if defined $Attachment->bodyhandle;
155 $head->replace( 'Content-Length' => Encode::encode( "UTF-8", $length ) );
157 $head = $head->as_string;
159 # MIME::Head doesn't support perl strings well and can return
160 # octets which later will be double encoded in low-level code
161 $head = Encode::decode( 'UTF-8', $head );
163 # If a message has no bodyhandle, that means that it has subparts (or appears to)
164 # and we should act accordingly.
165 unless ( defined $Attachment->bodyhandle ) {
166 my ($id) = $self->SUPER::Create(
167 TransactionId => $args{'TransactionId'},
168 Parent => $args{'Parent'},
169 ContentType => $Attachment->mime_type,
171 MessageId => $MessageId,
176 $RT::Logger->crit("Attachment insert failed - ". $RT::Handle->dbh->errstr);
177 my $txn = RT::Transaction->new($self->CurrentUser);
178 $txn->Load($args{'TransactionId'});
180 $txn->Object->_NewTransaction( Type => 'AttachmentError', ActivateScrips => 0, Data => $Filename );
185 foreach my $part ( $Attachment->parts ) {
186 my $SubAttachment = RT::Attachment->new( $self->CurrentUser );
187 my ($id) = $SubAttachment->Create(
188 TransactionId => $args{'TransactionId'},
193 $RT::Logger->crit("Attachment insert failed: ". $RT::Handle->dbh->errstr);
200 #If it's not multipart
203 my ( $encoding, $type, $note_args );
204 ( $encoding, $content, $type, $Filename, $note_args ) =
205 $self->_EncodeLOB( $Attachment->bodyhandle->as_string, $Attachment->mime_type, $Filename, );
207 my $id = $self->SUPER::Create(
208 TransactionId => $args{'TransactionId'},
209 ContentType => $type,
210 ContentEncoding => $encoding,
211 Parent => $args{'Parent'},
215 Filename => $Filename,
216 MessageId => $MessageId,
221 $self->TransactionObj->Object->_NewTransaction( %$note_args );
225 $RT::Logger->crit("Attachment insert failed: ". $RT::Handle->dbh->errstr);
226 my $txn = RT::Transaction->new($self->CurrentUser);
227 $txn->Load($args{'TransactionId'});
229 $txn->Object->_NewTransaction( Type => 'AttachmentError', ActivateScrips => 0, Data => $Filename );
236 =head2 TransactionObj
238 Returns the transaction object asscoiated with this attachment.
245 unless ( $self->{_TransactionObj} ) {
246 $self->{_TransactionObj} = RT::Transaction->new( $self->CurrentUser );
247 $self->{_TransactionObj}->Load( $self->TransactionId );
250 unless ($self->{_TransactionObj}->Id) {
251 $RT::Logger->crit( "Attachment ". $self->id
252 ." can't find transaction ". $self->TransactionId
253 ." which it is ostensibly part of. That's bad");
255 return $self->{_TransactionObj};
260 Returns a parent's L<RT::Attachment> object if this attachment
261 has a parent, otherwise returns undef.
267 return undef unless $self->Parent;
269 my $parent = RT::Attachment->new( $self->CurrentUser );
270 $parent->LoadById( $self->Parent );
276 Takes a MIME type as a string or regex. Returns an L<RT::Attachment> object
277 for the nearest containing part with a matching L</ContentType>. Strings must
278 match exactly and all matches are done case insensitively. Strings ending in a
279 C</> must only match the first part of the MIME type. For example:
281 # Find the nearest multipart/* container
282 my $container = $attachment->Closest("multipart/");
284 Returns undef if no such object is found.
291 my $part = $self->ParentObj or return undef;
293 $type = qr/^\Q$type\E$/
294 unless ref $type eq "REGEX";
296 while (lc($part->ContentType) !~ $type) {
297 $part = $part->ParentObj or last;
300 return ($part and $part->id) ? $part : undef;
305 Returns an L<RT::Attachments> object which is preloaded with
306 all attachments objects with this attachment's Id as their
314 my $kids = RT::Attachments->new( $self->CurrentUser );
315 $kids->ChildrenOf( $self->Id );
321 Returns an L<RT::Attachments> object containing all the attachments sharing
322 the same immediate parent as the current object, excluding the current
325 If the current attachment is a top-level part (i.e. Parent == 0) then a
326 guaranteed empty L<RT::Attachments> object is returned.
332 my $siblings = RT::Attachments->new( $self->CurrentUser );
334 $siblings->ChildrenOf( $self->Parent );
335 $siblings->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
338 $siblings->Limit( SUBCLAUSE => 'empty', FIELD => 'id', VALUE => 0 );
345 Returns the attachment's content. if it's base64 encoded, decode it
352 return $self->_DecodeLOB(
353 $self->GetHeader('Content-Type'), # Includes charset, unlike ->ContentType
354 $self->ContentEncoding,
355 $self->_Value('Content', decode_utf8 => 0),
359 =head2 OriginalContent
361 Returns the attachment's content as octets before RT's mangling.
362 Generally this just means restoring text content back to its
365 If the attachment has a C<message/*> Content-Type, its children attachments
366 are reconstructed and returned as a string.
370 sub OriginalContent {
373 # message/* content types represent raw messages. Since we break them
374 # apart when they come in, we'll reconstruct their child attachments when
375 # you ask for the OriginalContent of the message/ part.
376 if ($self->IsMessageContentType) {
377 # There shouldn't be more than one "subpart" to a message/* attachment
378 my $child = $self->Children->First;
379 return $self->Content unless $child and $child->id;
380 return $child->ContentAsMIME(Children => 1)->as_string;
383 return $self->Content unless RT::I18N::IsTextualContentType($self->ContentType);
385 my $content = $self->_DecodeLOB(
386 "application/octet-stream", # Force _DecodeLOB to not decode to characters
387 $self->ContentEncoding,
388 $self->_Value('Content', decode_utf8 => 0),
391 my $entity = MIME::Entity->new();
392 $entity->head->add("Content-Type", $self->GetHeader("Content-Type"));
393 $entity->bodyhandle( MIME::Body::Scalar->new( $content ) );
394 my $from = RT::I18N::_FindOrGuessCharset($entity);
395 $from = 'utf-8' if not $from or not Encode::find_encoding($from);
397 my $to = RT::I18N::_CanonicalizeCharset(
398 $self->OriginalEncoding || 'utf-8'
402 eval { Encode::from_to($content, $from => $to) };
404 $RT::Logger->error("Could not convert attachment from $from to $to: ".$@);
409 =head2 OriginalEncoding
411 Returns the attachment's original encoding.
415 sub OriginalEncoding {
417 return $self->GetHeader('X-RT-Original-Encoding');
422 Returns length of L</Content> in bytes.
429 return undef unless $self->TransactionObj->CurrentUserCanSee;
431 my $len = $self->GetHeader('Content-Length');
432 unless ( defined $len ) {
434 no warnings 'uninitialized';
435 $len = length($self->Content) || 0;
436 $self->SetHeader('Content-Length' => $len);
441 =head2 FriendlyContentLength
443 Returns L</ContentLength> in bytes, kilobytes, or megabytes as most
444 appropriate. The size is suffixed with C<MiB>, C<KiB>, or C<B> and the returned
447 Returns the empty string if the L</ContentLength> is 0 or undefined.
451 sub FriendlyContentLength {
453 my $size = $self->ContentLength;
454 return '' unless $size;
457 if ( $size > 1024*1024 ) {
458 $res = $self->loc( "[_1]MiB", int( $size / 1024 / 102.4 ) / 10 );
460 elsif ( $size > 1024 ) {
461 $res = $self->loc( "[_1]KiB", int( $size / 102.4 ) / 10 );
464 $res = $self->loc( "[_1]B", $size );
469 =head2 ContentAsMIME [Children => 1]
471 Returns MIME entity built from this attachment.
473 If the optional parameter C<Children> is set to a true value, the children are
474 recursively added to the entity.
478 sub _EncodeHeaderToMIME {
479 my ( $self, $header_name, $header_val ) = @_;
480 if ($header_name =~ /^Content-/i) {
481 my $params = MIME::Field::ParamVal->parse_params($header_val);
482 $header_val = delete $params->{'_'};
483 foreach my $key ( sort keys %$params ) {
484 my $value = $params->{$key};
485 if ( $value =~ /[^\x00-\x7f]/ ) { # check for non-ASCII
486 $value = q{UTF-8''} . URI->new(
487 Encode::encode('UTF-8', $value)
489 $value =~ s/(["\\])/\\$1/g;
490 $header_val .= qq{; ${key}*="$value"};
493 $header_val .= qq{; $key="$value"};
497 elsif ( $header_name =~ /^(?:Resent-)?(?:To|From|B?Cc|Sender|Reply-To)$/i ) {
498 my @addresses = RT::EmailParser->ParseEmailAddress( $header_val );
499 foreach my $address ( @addresses ) {
500 foreach my $field (qw(phrase comment)) {
501 my $v = $address->$field() or next;
502 $v = RT::Interface::Email::EncodeToMIME( String => $v );
503 $address->$field($v);
506 $header_val = join ', ', map $_->format, @addresses;
509 $header_val = RT::Interface::Email::EncodeToMIME(
510 String => $header_val
523 my $entity = MIME::Entity->new();
524 foreach my $header ($self->SplitHeaders) {
525 my ($h_key, $h_val) = split /:/, $header, 2;
527 $h_key, $self->_EncodeHeaderToMIME($h_key, $h_val)
531 if ($entity->is_multipart) {
532 if ($opts{'Children'} and not $self->IsMessageContentType) {
533 my $children = $self->Children;
534 while (my $child = $children->Next) {
535 $entity->add_part( $child->ContentAsMIME(%opts) );
539 # since we want to return original content, let's use original encoding
540 $entity->head->mime_attr(
541 "Content-Type.charset" => $self->OriginalEncoding )
542 if $self->OriginalEncoding;
545 MIME::Body::Scalar->new( $self->OriginalContent )
552 =head2 IsMessageContentType
554 Returns a boolean indicating if the Content-Type of this attachment is a
559 sub IsMessageContentType {
561 return $self->ContentType =~ m{^\s*message/}i ? 1 : 0;
566 Returns a hashref of all addresses related to this attachment.
567 The keys of the hash are C<From>, C<To>, C<Cc>, C<Bcc>, C<RT-Send-Cc>
568 and C<RT-Send-Bcc>. The values are references to lists of
569 L<Email::Address> objects.
573 our @ADDRESS_HEADERS = qw(From To Cc Bcc RT-Send-Cc RT-Send-Bcc);
579 my $current_user_address = lc($self->CurrentUser->EmailAddress || '');
580 foreach my $hdr (@ADDRESS_HEADERS) {
582 my $line = $self->GetHeader($hdr);
584 foreach my $AddrObj ( Email::Address->parse( $line )) {
585 my $address = $AddrObj->address;
586 $address = lc RT::User->CanonicalizeEmailAddress($address);
587 next if $current_user_address eq $address;
588 next if RT::EmailParser->IsRTAddress($address);
589 push @Addresses, $AddrObj ;
591 $data{$hdr} = \@Addresses;
598 Returns a multi-line string of the To, From, Cc, Date and Subject headers.
605 my @hdrs = $self->_SplitHeaders;
606 while (my $str = shift @hdrs) {
607 next unless $str =~ /^(To|From|RT-Send-Cc|Cc|Bcc|Date|Subject):/i;
608 $hdrs .= $str . "\n";
609 $hdrs .= shift( @hdrs ) . "\n" while ($hdrs[0] =~ /^[ \t]+/);
616 Returns this object's headers as a string. This method specifically
617 removes the RT-Send-Bcc: header, so as to never reveal to whom RT sent a Bcc.
618 We need to record the RT-Send-Cc and RT-Send-Bcc values so that we can actually send
619 out mail. The mailing rules are separated from the ticket update code by
620 an abstraction barrier that makes it impossible to pass this data directly.
625 return join("\n", $_[0]->SplitHeaders);
628 =head2 EncodedHeaders
630 Takes encoding as argument and returns the attachment's headers as octets in encoded
633 This is not protection using quoted printable or base64 encoding.
639 my $encoding = shift || 'utf8';
640 return Encode::encode( $encoding, $self->Headers );
643 =head2 GetHeader $TAG
645 Returns the value of the header Tag as a string. This bypasses the weeding out
646 done in Headers() above.
653 foreach my $line ($self->_SplitHeaders) {
654 next unless $line =~ /^\Q$tag\E:\s+(.*)$/si;
656 #if we find the header, return its value
660 # we found no header. return an empty string
664 =head2 DelHeader $TAG
666 Delete a field from the attachment's headers.
675 foreach my $line ($self->_SplitHeaders) {
676 next if $line =~ /^\Q$tag\E:\s+/i;
677 $newheader .= "$line\n";
679 return $self->__Set( Field => 'Headers', Value => $newheader);
682 =head2 AddHeader $TAG, $VALUE, ...
684 Add one or many fields to the attachment's headers.
691 my $newheader = $self->__Value( 'Headers' );
692 while ( my ($tag, $value) = splice @_, 0, 2 ) {
693 $value = $self->_CanonicalizeHeaderValue($value);
694 $newheader .= "$tag: $value\n";
696 return $self->__Set( Field => 'Headers', Value => $newheader);
699 =head2 SetHeader ( 'Tag', 'Value' )
701 Replace or add a Header to the attachment's headers.
708 my $value = $self->_CanonicalizeHeaderValue(shift);
712 foreach my $line ( $self->_SplitHeaders ) {
713 if ( $line =~ /^\Q$tag\E:\s+/i ) {
714 # replace first instance, skip all the rest
716 $newheader .= "$tag: $value\n";
720 $newheader .= "$line\n";
724 $newheader .= "$tag: $value\n" unless $replaced;
725 $self->__Set( Field => 'Headers', Value => $newheader);
728 sub _CanonicalizeHeaderValue {
732 $value = '' unless defined $value;
734 $value =~ s/\r*\n/\n /g;
741 Returns an array of this attachment object's headers, with one header
742 per array entry. Multiple lines are folded.
744 B<Never> returns C<RT-Send-Bcc> field.
750 return (grep !/^RT-Send-Bcc/i, $self->_SplitHeaders(@_) );
755 Returns an array of this attachment object's headers, with one header
756 per array entry. multiple lines are folded.
763 my $headers = (shift || $self->_Value('Headers'));
765 # XXX TODO: splitting on \n\w is _wrong_ as it treats \n[ as a valid
766 # continuation, which it isn't. The correct split pattern, per RFC 2822,
767 # is /\n(?=[^ \t]|\z)/. That is, only "\n " or "\n\t" is a valid
768 # continuation. Older values of X-RT-GnuPG-Status contain invalid
769 # continuations and rely on this bogus split pattern, however, so it is
770 # left as-is for now.
771 for (split(/\n(?=\w|\z)/,$headers)) {
782 my $txn = $self->TransactionObj;
783 return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
784 return (0, $self->loc('Permission Denied'))
785 unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
786 return (0, $self->loc('Cryptography is disabled'))
787 unless RT->Config->Get('Crypt')->{'Enable'};
788 return (0, $self->loc('Attachments encryption is disabled'))
789 unless RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'};
791 my $type = $self->ContentType;
792 if ( $type =~ /^x-application-rt\/[^-]+-encrypted/i ) {
793 return (1, $self->loc('Already encrypted'));
794 } elsif ( $type =~ /^multipart\//i ) {
795 return (1, $self->loc('No need to encrypt'));
798 my $queue = $txn->TicketObj->QueueObj;
800 foreach my $address ( grep $_,
801 $queue->CorrespondAddress,
802 $queue->CommentAddress,
803 RT->Config->Get('CorrespondAddress'),
804 RT->Config->Get('CommentAddress'),
806 my %res = RT::Crypt->GetKeysInfo( Key => $address, Type => 'private' );
807 next if $res{'exit_code'} || !$res{'info'};
808 %res = RT::Crypt->GetKeysForEncryption( $address );
809 next if $res{'exit_code'} || !$res{'info'};
810 $encrypt_for = $address;
812 unless ( $encrypt_for ) {
813 return (0, $self->loc('No key suitable for encryption'));
816 my $content = $self->Content;
817 my %res = RT::Crypt->SignEncryptContent(
818 Content => \$content,
821 Recipients => [ $encrypt_for ],
823 if ( $res{'exit_code'} ) {
824 return (0, $self->loc('Encryption error; contact the administrator'));
827 my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
829 return ($status, $self->loc("Couldn't replace content with encrypted data: [_1]", $msg));
832 $type = qq{x-application-rt\/$res{'Protocol'}-encrypted; original-type="$type"};
833 $self->__Set( Field => 'ContentType', Value => $type );
834 $self->SetHeader( 'Content-Type' => $type );
836 return (1, $self->loc('Successfuly encrypted data'));
842 my $txn = $self->TransactionObj;
843 return (0, $self->loc('Permission Denied')) unless $txn->CurrentUserCanSee;
844 return (0, $self->loc('Permission Denied'))
845 unless $txn->TicketObj->CurrentUserHasRight('ModifyTicket');
846 return (0, $self->loc('Cryptography is disabled'))
847 unless RT->Config->Get('Crypt')->{'Enable'};
849 my $type = $self->ContentType;
851 if ( $type =~ /^x-application-rt\/([^-]+)-encrypted/i ) {
853 $protocol =~ s/gpg/gnupg/; # backwards compatibility
854 ($type) = ($type =~ /original-type="(.*)"/i);
855 $type ||= 'application/octet-stream';
857 return (1, $self->loc('Is not encrypted'));
860 my $queue = $txn->TicketObj->QueueObj;
862 $queue->CorrespondAddress,
863 $queue->CommentAddress,
864 RT->Config->Get('CorrespondAddress'),
865 RT->Config->Get('CommentAddress')
868 my $content = $self->Content;
869 my %res = RT::Crypt->DecryptContent(
870 Protocol => $protocol,
871 Content => \$content,
872 Recipients => \@addresses,
874 if ( $res{'exit_code'} ) {
875 return (0, $self->loc('Decryption error; contact the administrator'));
878 my ($status, $msg) = $self->__Set( Field => 'Content', Value => $content );
880 return ($status, $self->loc("Couldn't replace content with decrypted data: [_1]", $msg));
882 $self->__Set( Field => 'ContentType', Value => $type );
883 $self->SetHeader( 'Content-Type' => $type );
885 return (1, $self->loc('Successfuly decrypted data'));
890 Takes the name of a table column.
891 Returns its value as a string, if the user passes an ACL check
899 #if the field is public, return it.
900 if ( $self->_Accessible( $field, 'public' ) ) {
901 return ( $self->__Value( $field, @_ ) );
904 return undef unless $self->TransactionObj->CurrentUserCanSee;
905 return $self->__Value( $field, @_ );
908 # Attachments don't change; by adding this cache config directive,
909 # we don't lose pathalogically on long tickets.
912 'cache_for_sec' => 180,
921 Returns the current value of id.
922 (In the database, id is stored as int(11).)
930 Returns the current value of TransactionId.
931 (In the database, TransactionId is stored as int(11).)
935 =head2 SetTransactionId VALUE
938 Set TransactionId to VALUE.
939 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
940 (In the database, TransactionId will be stored as a int(11).)
948 Returns the current value of Parent.
949 (In the database, Parent is stored as int(11).)
953 =head2 SetParent VALUE
957 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
958 (In the database, Parent will be stored as a int(11).)
966 Returns the current value of MessageId.
967 (In the database, MessageId is stored as varchar(160).)
971 =head2 SetMessageId VALUE
974 Set MessageId to VALUE.
975 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
976 (In the database, MessageId will be stored as a varchar(160).)
984 Returns the current value of Subject.
985 (In the database, Subject is stored as varchar(255).)
989 =head2 SetSubject VALUE
992 Set Subject to VALUE.
993 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
994 (In the database, Subject will be stored as a varchar(255).)
1002 Returns the current value of Filename.
1003 (In the database, Filename is stored as varchar(255).)
1007 =head2 SetFilename VALUE
1010 Set Filename to VALUE.
1011 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1012 (In the database, Filename will be stored as a varchar(255).)
1020 Returns the current value of ContentType.
1021 (In the database, ContentType is stored as varchar(80).)
1025 =head2 SetContentType VALUE
1028 Set ContentType to VALUE.
1029 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1030 (In the database, ContentType will be stored as a varchar(80).)
1036 =head2 ContentEncoding
1038 Returns the current value of ContentEncoding.
1039 (In the database, ContentEncoding is stored as varchar(80).)
1043 =head2 SetContentEncoding VALUE
1046 Set ContentEncoding to VALUE.
1047 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1048 (In the database, ContentEncoding will be stored as a varchar(80).)
1056 Returns the current value of Content.
1057 (In the database, Content is stored as longblob.)
1061 =head2 SetContent VALUE
1064 Set Content to VALUE.
1065 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1066 (In the database, Content will be stored as a longblob.)
1074 Returns the current value of Headers.
1075 (In the database, Headers is stored as longtext.)
1079 =head2 SetHeaders VALUE
1082 Set Headers to VALUE.
1083 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
1084 (In the database, Headers will be stored as a longtext.)
1092 Returns the current value of Creator.
1093 (In the database, Creator is stored as int(11).)
1101 Returns the current value of Created.
1102 (In the database, Created is stored as datetime.)
1109 sub _CoreAccessible {
1113 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
1115 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
1117 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1119 {read => 1, write => 1, sql_type => 12, length => 160, is_blob => 0, is_numeric => 0, type => 'varchar(160)', default => ''},
1121 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
1123 {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
1125 {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
1127 {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
1129 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longblob', default => ''},
1131 {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
1133 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
1135 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
1140 sub FindDependencies {
1142 my ($walker, $deps) = @_;
1144 $self->SUPER::FindDependencies($walker, $deps);
1145 $deps->Add( out => $self->TransactionObj );
1152 Dependencies => undef,
1155 my $deps = $args{'Dependencies'};
1158 # Nested attachments
1159 my $objs = RT::Attachments->new( $self->CurrentUser );
1170 push( @$list, $objs );
1172 $deps->_PushDependencies(
1173 BaseObject => $self,
1174 Flags => RT::Shredder::Constants::DEPENDS_ON,
1175 TargetObjects => $list,
1176 Shredder => $args{'Shredder'}
1178 return $self->SUPER::__DependsOn( %args );
1181 RT::Base->_ImportOverlays();