1 # $Header: /home/cvs/cvsroot/freeside/rt/lib/RT/Attachment.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
2 # Copyright 2000 Jesse Vincent <jesse@fsck.com>
3 # Released under the terms of the GNU Public License
7 RT::Attachment -- an RT attachment object
16 This module should never be instantiated directly by client code. it's an internal
17 module which should only be instantiated through exported APIs in Ticket, Queue and other
26 ok (require RT::TestHarness);
27 ok (require RT::Attachment);
33 package RT::Attachment;
42 $self->{'table'} = "Attachments";
43 return($self->SUPER::_Init(@_));
47 # {{{ sub _ClassAccessible
48 sub _ClassAccessible {
50 TransactionId => { 'read'=>1, 'public'=>1, },
51 MessageId => { 'read'=>1, },
52 Parent => { 'read'=>1, },
53 ContentType => { 'read'=>1, },
54 Subject => { 'read'=>1, },
55 Content => { 'read'=>1, },
56 ContentEncoding => { 'read'=>1, },
57 Headers => { 'read'=>1, },
58 Filename => { 'read'=>1, },
59 Creator => { 'read'=>1, 'auto'=>1, },
60 Created => { 'read'=>1, 'auto'=>1, },
65 # {{{ sub TransactionObj
69 Returns the transaction object asscoiated with this attachment.
74 require RT::Transaction;
76 unless (exists $self->{_TransactionObj}) {
77 $self->{_TransactionObj}=RT::Transaction->new($self->CurrentUser);
78 $self->{_TransactionObj}->Load($self->TransactionId);
80 return $self->{_TransactionObj};
89 Create a new attachment. Takes a paramhash:
91 'Attachment' Should be a single MIME body with optional subparts
92 'Parent' is an optional Parent RT::Attachment object
93 'TransactionId' is the mandatory id of the Transaction this attachment is associated with.;
100 my %args = ( id => 0,
108 #For ease of reference
109 my $Attachment = $args{'Attachment'};
111 #if we didn't specify a ticket, we need to bail
112 if ( $args{'TransactionId'} == 0) {
113 $RT::Logger->crit("RT::Attachment->Create couldn't, as you didn't specify a transaction\n");
118 #If we possibly can, collapse it to a singlepart
119 $Attachment->make_singlepart;
122 my $Subject = $Attachment->head->get('subject',0);
123 defined($Subject) or $Subject = '';
127 my $Filename = $Attachment->head->recommended_filename;
129 if ($Attachment->parts) {
130 $id = $self->SUPER::Create(TransactionId => $args{'TransactionId'},
132 ContentType => $Attachment->mime_type,
133 Headers => $Attachment->head->as_string,
137 foreach my $part ($Attachment->parts) {
138 my $SubAttachment = new RT::Attachment($self->CurrentUser);
139 $SubAttachment->Create(TransactionId => $args{'TransactionId'},
142 ContentType => $Attachment->mime_type,
143 Headers => $Attachment->head->as_string(),
151 #If it's not multipart
154 my $ContentEncoding = 'none';
156 my $Body = $Attachment->bodyhandle->as_string;
158 #get the max attachment length from RT
159 my $MaxSize = $RT::MaxAttachmentSize;
161 #if the current attachment contains nulls and the
162 #database doesn't support embedded nulls
164 if ( (! $RT::Handle->BinarySafeBLOBs) &&
165 ( $Body =~ /\x00/ ) ) {
166 # set a flag telling us to mimencode the attachment
167 $ContentEncoding = 'base64';
169 #cut the max attchment size by 25% (for mime-encoding overhead.
170 $RT::Logger->debug("Max size is $MaxSize\n");
171 $MaxSize = $MaxSize * 3/4;
174 #if the attachment is larger than the maximum size
175 if (($MaxSize) and ($MaxSize < length($Body))) {
176 # if we're supposed to truncate large attachments
177 if ($RT::TruncateLongAttachments) {
178 # truncate the attachment to that length.
179 $Body = substr ($Body, 0, $MaxSize);
183 # elsif we're supposed to drop large attachments on the floor,
184 elsif ($RT::DropLongAttachments) {
185 # drop the attachment on the floor
186 $RT::Logger->info("$self: Dropped an attachment of size ". length($Body).
187 "\n". "It started: ". substr($Body, 0, 60) . "\n");
191 # if we need to mimencode the attachment
192 if ($ContentEncoding eq 'base64') {
193 # base64 encode the attachment
194 $Body = MIME::Base64::encode_base64($Body);
198 my $id = $self->SUPER::Create(TransactionId => $args{'TransactionId'},
199 ContentType => $Attachment->mime_type,
200 ContentEncoding => $ContentEncoding,
201 Parent => $args{'Parent'},
203 Headers => $Attachment->head->as_string,
205 Filename => $Filename,
218 Returns the attachment's content. if it's base64 encoded, decode it
225 if ( $self->ContentEncoding eq 'none' || ! $self->ContentEncoding ) {
226 return $self->_Value('Content');
227 } elsif ( $self->ContentEncoding eq 'base64' ) {
228 return MIME::Base64::decode_base64($self->_Value('Content'));
230 return( "Unknown ContentEncoding ". $self->ContentEncoding);
241 Returns an RT::Attachments object which is preloaded with all Attachments objects with this Attachment\'s Id as their 'Parent'
248 my $kids = new RT::Attachments($self->CurrentUser);
249 $kids->ChildrenOf($self->Id);
263 my %args=(Reply=>undef, # Prefilled reply (i.e. from the KB/FAQ system)
266 my ($quoted_content, $body, $headers);
269 # TODO: Handle Multipart/Mixed (eventually fix the link in the
270 # ShowHistory web template?)
271 if ($self->ContentType =~ m{^(text/plain|message)}i) {
272 $body=$self->Content;
274 # Do we need any preformatting (wrapping, that is) of the message?
276 # Remove quoted signature.
277 $body =~ s/\n-- \n(.*)$//s;
279 # What's the longest line like?
280 foreach (split (/\n/,$body)) {
281 $max=length if ( length > $max);
285 require Text::Wrapper;
286 my $wrapper=new Text::Wrapper
289 body_start => ($max > 70*3 ? ' ' : ''),
292 $body=$wrapper->wrap($body);
297 $body = '[' . $self->TransactionObj->CreatorObj->Name() . ' - ' . $self->TransactionObj->CreatedAsString()
302 $body = "[Non-text message not quoted]\n\n";
309 return (\$body, $max);
313 # {{{ sub NiceHeaders - pulls out only the most relevant headers
317 Returns the To, From, Cc, Date and Subject headers.
319 It is a known issue that this breaks if any of these headers are not
327 for (split(/\n/,$self->Headers)) {
328 $hdrs.="$_\n" if /^(To|From|RT-Send-Cc|Cc|Date|Subject): /i
338 Returns this object's headers as a string. This method specifically
339 removes the RT-Send-Bcc: header, so as to never reveal to whom RT sent a Bcc.
340 We need to record the RT-Send-Cc and RT-Send-Bcc values so that we can actually send
341 out mail. (The mailing rules are seperated from the ticket update code by
342 an abstraction barrier that makes it impossible to pass this data directly
349 for (split(/\n/,$self->SUPER::Headers)) {
350 $hdrs.="$_\n" unless /^(RT-Send-Bcc): /i
360 =head2 GetHeader ( 'Tag')
362 Returns the value of the header Tag as a string. This bypasses the weeding out
363 done in Headers() above.
370 foreach my $line (split(/\n/,$self->SUPER::Headers)) {
371 $RT::Logger->debug( "Does $line match $tag\n");
372 if ($line =~ /^$tag:\s+(.*)$/i) { #if we find the header, return its value
377 # we found no header. return an empty string
386 Takes the name of a table column.
387 Returns its value as a string, if the user passes an ACL check
397 #if the field is public, return it.
398 if ($self->_Accessible($field, 'public')) {
399 #$RT::Logger->debug("Skipping ACL check for $field\n");
400 return($self->__Value($field));
404 #If it's a comment, we need to be extra special careful
405 elsif ( (($self->TransactionObj->CurrentUserHasRight('ShowTicketComments')) and
406 ($self->TransactionObj->Type eq 'Comment') ) or
407 ($self->TransactionObj->CurrentUserHasRight('ShowTicket'))) {
409 return($self->__Value($field));
411 #if they ain't got rights to see, don't let em