diff options
Diffstat (limited to 'rt/lib/RT/Attachment.pm')
| -rwxr-xr-x | rt/lib/RT/Attachment.pm | 601 | 
1 files changed, 326 insertions, 275 deletions
| diff --git a/rt/lib/RT/Attachment.pm b/rt/lib/RT/Attachment.pm index 2ed520162..916ac355e 100755 --- a/rt/lib/RT/Attachment.pm +++ b/rt/lib/RT/Attachment.pm @@ -1,372 +1,423 @@ -# BEGIN LICENSE BLOCK -#  -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -#  -# (Except where explictly superceded by other copyright notices) -#  -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -#  -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU -# General Public License for more details. -#  -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -#  -#  -# END LICENSE BLOCK -# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>) -# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.   -#  -# !! DO NOT EDIT THIS FILE !! -# - -use strict; - +# $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Attachment.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $ +# Copyright 2000 Jesse Vincent <jesse@fsck.com> +# Released under the terms of the GNU Public License  =head1 NAME -RT::Attachment - +  RT::Attachment -- an RT attachment object  =head1 SYNOPSIS -=head1 DESCRIPTION +  use RT::Attachment; -=head1 METHODS -=cut - -package RT::Attachment; -use RT::Record;  - - -use vars qw( @ISA ); -@ISA= qw( RT::Record ); - -sub _Init { -  my $self = shift;  - -  $self->Table('Attachments'); -  $self->SUPER::_Init(@_); -} +=head1 DESCRIPTION +This module should never be instantiated directly by client code. it's an internal  +module which should only be instantiated through exported APIs in Ticket, Queue and other  +similar objects. +=head1 METHODS -=item Create PARAMHASH +=begin testing -Create takes a hash of values and creates a row in the database: +ok (require RT::TestHarness); +ok (require RT::Attachment); -  int(11) 'TransactionId'. -  int(11) 'Parent'. -  varchar(160) 'MessageId'. -  varchar(255) 'Subject'. -  varchar(255) 'Filename'. -  varchar(80) 'ContentType'. -  varchar(80) 'ContentEncoding'. -  longtext 'Content'. -  longtext 'Headers'. +=end testing  =cut - - - -sub Create { -    my $self = shift; -    my %args = (  -                TransactionId => '0', -                Parent => '0', -                MessageId => '', -                Subject => '', -                Filename => '', -                ContentType => '', -                ContentEncoding => '', -                Content => '', -                Headers => '', - -		  @_); -    $self->SUPER::Create( -                         TransactionId => $args{'TransactionId'}, -                         Parent => $args{'Parent'}, -                         MessageId => $args{'MessageId'}, -                         Subject => $args{'Subject'}, -                         Filename => $args{'Filename'}, -                         ContentType => $args{'ContentType'}, -                         ContentEncoding => $args{'ContentEncoding'}, -                         Content => $args{'Content'}, -                         Headers => $args{'Headers'}, -); - +package RT::Attachment; +use RT::Record; +use MIME::Base64; +use vars qw|@ISA|; +@ISA= qw(RT::Record); + +# {{{ sub _Init +sub _Init  { +    my $self = shift;  +    $self->{'table'} = "Attachments"; +    return($self->SUPER::_Init(@_));  } +# }}} +# {{{ sub _ClassAccessible  +sub _ClassAccessible { +    { +    TransactionId   => { 'read'=>1, 'public'=>1, }, +    MessageId       => { 'read'=>1, }, +    Parent          => { 'read'=>1, }, +    ContentType     => { 'read'=>1, }, +    Subject         => { 'read'=>1, }, +    Content         => { 'read'=>1, }, +    ContentEncoding => { 'read'=>1, }, +    Headers         => { 'read'=>1, }, +    Filename        => { 'read'=>1, }, +    Creator         => { 'read'=>1, 'auto'=>1, }, +    Created         => { 'read'=>1, 'auto'=>1, }, +  }; +} +# }}} +# {{{ sub TransactionObj  -=item id - -Returns the current value of id.  -(In the database, id is stored as int(11).) - - -=cut - - -=item TransactionId - -Returns the current value of TransactionId.  -(In the database, TransactionId is stored as int(11).) - - - -=item SetTransactionId VALUE - - -Set TransactionId to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, TransactionId will be stored as a int(11).) +=head2 TransactionObj +Returns the transaction object asscoiated with this attachment.  =cut +sub TransactionObj { +    require RT::Transaction; +    my $self=shift; +    unless (exists $self->{_TransactionObj}) { +	$self->{_TransactionObj}=RT::Transaction->new($self->CurrentUser); +	$self->{_TransactionObj}->Load($self->TransactionId); +    } +    return $self->{_TransactionObj}; +} -=item Parent - -Returns the current value of Parent.  -(In the database, Parent is stored as int(11).) - - - -=item SetParent VALUE +# }}} +# {{{ sub Create  -Set Parent to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, Parent will be stored as a int(11).) +=head2 Create +Create a new attachment. Takes a paramhash: +     +    'Attachment' Should be a single MIME body with optional subparts +    'Parent' is an optional Parent RT::Attachment object +    'TransactionId' is the mandatory id of the Transaction this attachment is associated with.;  =cut +sub Create  { +    my $self = shift; +    my ($id); +    my %args = ( id => 0, +		 TransactionId => 0, +		 Parent => 0, +		 Attachment => undef, +		 @_ +	       ); +     +     +    #For ease of reference +    my $Attachment = $args{'Attachment'}; +     +    #if we didn't specify a ticket, we need to bail +    if ( $args{'TransactionId'} == 0) { +	$RT::Logger->crit("RT::Attachment->Create couldn't, as you didn't specify a transaction\n"); +	return (0); +	 +    } +     +    #If we possibly can, collapse it to a singlepart +    $Attachment->make_singlepart; +     +    #Get the subject +    my $Subject = $Attachment->head->get('subject',0); +    defined($Subject) or $Subject = ''; +    chomp($Subject); +   +    #Get the filename +    my $Filename = $Attachment->head->recommended_filename; +     +    if ($Attachment->parts) { +	$id = $self->SUPER::Create(TransactionId => $args{'TransactionId'}, +				   Parent => 0, +				   ContentType  => $Attachment->mime_type, +				   Headers => $Attachment->head->as_string, +				   Subject => $Subject, +				    +				  ); + 	foreach my $part ($Attachment->parts) {	 +	    my $SubAttachment = new RT::Attachment($self->CurrentUser); +	    $SubAttachment->Create(TransactionId => $args{'TransactionId'}, +				   Parent => $id, +				   Attachment => $part, +				   ContentType  => $Attachment->mime_type, +				   Headers => $Attachment->head->as_string(), +				    +				  ); +	} +	return ($id); +    } +   +   +    #If it's not multipart +    else { +	 +	my $ContentEncoding = 'none';  +	 +	my $Body = $Attachment->bodyhandle->as_string; +	 +	#get the max attachment length from RT +	my $MaxSize = $RT::MaxAttachmentSize; +	 +	#if the current attachment contains nulls and the  +	#database doesn't support embedded nulls +	 +	if ( (! $RT::Handle->BinarySafeBLOBs) && +	     ( $Body =~ /\x00/ ) ) { +	    # set a flag telling us to mimencode the attachment +	    $ContentEncoding = 'base64'; +	     +	    #cut the max attchment size by 25% (for mime-encoding overhead. +	    $RT::Logger->debug("Max size is $MaxSize\n"); +	    $MaxSize = $MaxSize * 3/4;	 +	} +	 +	#if the attachment is larger than the maximum size +	if (($MaxSize) and ($MaxSize < length($Body))) { +	    # if we're supposed to truncate large attachments +	    if ($RT::TruncateLongAttachments) { +		# truncate the attachment to that length. +		$Body = substr ($Body, 0, $MaxSize); + +	    } +	     +	    # elsif we're supposed to drop large attachments on the floor, +	    elsif ($RT::DropLongAttachments) { +		# drop the attachment on the floor +		$RT::Logger->info("$self: Dropped an attachment of size ". length($Body). +				  "\n". "It started: ". substr($Body, 0, 60) . "\n"); +		return(undef); +	    } +	} +	# if we need to mimencode the attachment +	if ($ContentEncoding eq 'base64') { +	    # base64 encode the attachment +	    $Body = MIME::Base64::encode_base64($Body); +	     +	} +	 +	my $id = $self->SUPER::Create(TransactionId => $args{'TransactionId'}, +				      ContentType  => $Attachment->mime_type, +				      ContentEncoding => $ContentEncoding, +				      Parent => $args{'Parent'}, +				      Content => $Body, +				      Headers => $Attachment->head->as_string, +				      Subject => $Subject, +				      Filename => $Filename, +				     ); +	return ($id); +    } +} -=item MessageId - -Returns the current value of MessageId.  -(In the database, MessageId is stored as varchar(160).) - - +# }}} -=item SetMessageId VALUE +# {{{ sub Content -Set MessageId to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, MessageId will be stored as a varchar(160).) +=head2 Content +Returns the attachment's content. if it's base64 encoded, decode it  +before returning it.  =cut - -=item Subject - -Returns the current value of Subject.  -(In the database, Subject is stored as varchar(255).) - +sub Content { +  my $self = shift; +  if ( $self->ContentEncoding eq 'none' || ! $self->ContentEncoding ) { +      return $self->_Value('Content'); +  } elsif ( $self->ContentEncoding eq 'base64' ) { +      return MIME::Base64::decode_base64($self->_Value('Content')); +  } else { +      return( "Unknown ContentEncoding ". $self->ContentEncoding); +  } +} -=item SetSubject VALUE +# }}} +# {{{ sub Children -Set Subject to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, Subject will be stored as a varchar(255).) +=head2 Children +  Returns an RT::Attachments object which is preloaded with all Attachments objects with this Attachment\'s Id as their 'Parent'  =cut +sub Children { +    my $self = shift; +     +    my $kids = new RT::Attachments($self->CurrentUser); +    $kids->ChildrenOf($self->Id); +    return($kids); +} -=item Filename - -Returns the current value of Filename.  -(In the database, Filename is stored as varchar(255).) - - - -=item SetFilename VALUE - - -Set Filename to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, Filename will be stored as a varchar(255).) - - -=cut +# }}} +# {{{ UTILITIES -=item ContentType +# {{{ sub Quote  -Returns the current value of ContentType.  -(In the database, ContentType is stored as varchar(80).) +sub Quote { +    my $self=shift; +    my %args=(Reply=>undef, # Prefilled reply (i.e. from the KB/FAQ system) +	      @_); -=item SetContentType VALUE +    my ($quoted_content, $body, $headers); +    my $max=0; +    # TODO: Handle Multipart/Mixed (eventually fix the link in the +    # ShowHistory web template?) +    if ($self->ContentType =~ m{^(text/plain|message)}i) { +	$body=$self->Content; -Set ContentType to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, ContentType will be stored as a varchar(80).) +	# Do we need any preformatting (wrapping, that is) of the message? +	# Remove quoted signature. +	$body =~ s/\n-- \n(.*)$//s; -=cut +	# What's the longest line like? +	foreach (split (/\n/,$body)) { +	    $max=length if ( length > $max); +	} +	if ($max>76) { +	    require Text::Wrapper; +	    my $wrapper=new Text::Wrapper +		( +		 columns => 70,  +		 body_start => ($max > 70*3 ? '   ' : ''), +		 par_start => '' +		 ); +	    $body=$wrapper->wrap($body); +	} -=item ContentEncoding +	$body =~ s/^/> /gm; -Returns the current value of ContentEncoding.  -(In the database, ContentEncoding is stored as varchar(80).) +	$body = '[' . $self->TransactionObj->CreatorObj->Name() . ' - ' . $self->TransactionObj->CreatedAsString() +	            . "]:\n\n" +   	        . $body . "\n\n"; +    } else { +	$body = "[Non-text message not quoted]\n\n"; +    } +     +    $max=60 if $max<60; +    $max=70 if $max>78; +    $max+=2; +    return (\$body, $max); +} +# }}} -=item SetContentEncoding VALUE +# {{{ sub NiceHeaders - pulls out only the most relevant headers +=head2 NiceHeaders -Set ContentEncoding to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, ContentEncoding will be stored as a varchar(80).) +Returns the To, From, Cc, Date and Subject headers. +It is a known issue that this breaks if any of these headers are not +properly unfolded.  =cut +sub NiceHeaders { +    my $self=shift; +    my $hdrs=""; +    for (split(/\n/,$self->Headers)) { +	    $hdrs.="$_\n" if /^(To|From|RT-Send-Cc|Cc|Date|Subject): /i +    } +    return $hdrs; +} +# }}} -=item Content - -Returns the current value of Content.  -(In the database, Content is stored as longtext.) - - - -=item SetContent VALUE - +# {{{ sub Headers -Set Content to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, Content will be stored as a longtext.) +=head2 Headers +Returns this object's headers as a string.  This method specifically +removes the RT-Send-Bcc: header, so as to never reveal to whom RT sent a Bcc. +We need to record the RT-Send-Cc and RT-Send-Bcc values so that we can actually send +out mail. (The mailing rules are seperated from the ticket update code by +an abstraction barrier that makes it impossible to pass this data directly  =cut +sub Headers { +    my $self = shift; +    my $hdrs=""; +    for (split(/\n/,$self->SUPER::Headers)) { +	    $hdrs.="$_\n" unless /^(RT-Send-Bcc): /i +    } +    return $hdrs; +} -=item Headers - -Returns the current value of Headers.  -(In the database, Headers is stored as longtext.) - - - -=item SetHeaders VALUE - - -Set Headers to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, Headers will be stored as a longtext.) - - -=cut +# }}} -=item Creator +# {{{ sub GetHeader -Returns the current value of Creator.  -(In the database, Creator is stored as int(11).) +=head2 GetHeader ( 'Tag') +Returns the value of the header Tag as a string. This bypasses the weeding out +done in Headers() above.  =cut +sub GetHeader { +    my $self = shift; +    my $tag = shift; +    foreach my $line (split(/\n/,$self->SUPER::Headers)) { +        $RT::Logger->debug( "Does $line match $tag\n"); +        if ($line =~ /^$tag:\s+(.*)$/i) { #if we find the header, return its value +            return ($1); +        } +    } +     +    # we found no header. return an empty string +    return undef; +} +# }}} -=item Created +# {{{ sub _Value  -Returns the current value of Created.  -(In the database, Created is stored as datetime.) +=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')) { +	#$RT::Logger->debug("Skipping ACL check for $field\n"); +	return($self->__Value($field)); +	 +    } +     +    #If it's a comment, we need to be extra special careful +    elsif ( (($self->TransactionObj->CurrentUserHasRight('ShowTicketComments')) and +	     ($self->TransactionObj->Type eq 'Comment') )  or +	    ($self->TransactionObj->CurrentUserHasRight('ShowTicket'))) { +	 +	return($self->__Value($field)); +    } +    #if they ain't got rights to see, don't let em +    else { +	    return(undef); +	} +    	 +     +} -sub _ClassAccessible { -    { -      -        id => -		{read => 1, type => 'int(11)', default => ''}, -        TransactionId =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, -        Parent =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, -        MessageId =>  -		{read => 1, write => 1, type => 'varchar(160)', default => ''}, -        Subject =>  -		{read => 1, write => 1, type => 'varchar(255)', default => ''}, -        Filename =>  -		{read => 1, write => 1, type => 'varchar(255)', default => ''}, -        ContentType =>  -		{read => 1, write => 1, type => 'varchar(80)', default => ''}, -        ContentEncoding =>  -		{read => 1, write => 1, type => 'varchar(80)', default => ''}, -        Content =>  -		{read => 1, write => 1, type => 'longtext', default => ''}, -        Headers =>  -		{read => 1, write => 1, type => 'longtext', default => ''}, -        Creator =>  -		{read => 1, auto => 1, type => 'int(11)', default => '0'}, -        Created =>  -		{read => 1, auto => 1, type => 'datetime', default => ''}, - - } -}; - - -        eval "require RT::Attachment_Overlay"; -        if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Overlay.pm}) { -            die $@; -        }; - -        eval "require RT::Attachment_Vendor"; -        if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Vendor.pm}) { -            die $@; -        }; - -        eval "require RT::Attachment_Local"; -        if ($@ && $@ !~ qr{^Can't locate RT/Attachment_Local.pm}) { -            die $@; -        }; - - - - -=head1 SEE ALSO - -This class allows "overlay" methods to be placed -into the following files _Overlay is for a System overlay by the original author, -_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.   - -These overlay files can contain new subs or subs to replace existing subs in this module. - -If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line  - -   no warnings qw(redefine); - -so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. - -RT::Attachment_Overlay, RT::Attachment_Vendor, RT::Attachment_Local - -=cut +# }}} +# }}}  1; | 
