BPicture: Fix archive constructor.
[haiku.git] / src / kits / mail / MailAttachment.cpp
blob259ea97a11cc92efaef8a51a7c9477d4bd339f0b
1 /*
2 * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
3 */
6 /*! Classes which handle mail attachments */
9 #include <MailAttachment.h>
11 #include <stdlib.h>
12 #include <stdio.h>
14 #include <ByteOrder.h>
15 #include <DataIO.h>
16 #include <Entry.h>
17 #include <File.h>
18 #include <Mime.h>
19 #include <NodeInfo.h>
20 #include <String.h>
22 #include <AutoDeleter.h>
24 #include <mail_encoding.h>
25 #include <NodeMessage.h>
28 /*! No attributes or awareness of the file system at large
30 BSimpleMailAttachment::BSimpleMailAttachment()
32 fStatus(B_NO_INIT),
33 _data(NULL),
34 _raw_data(NULL),
35 _we_own_data(false)
37 Initialize(base64);
41 BSimpleMailAttachment::BSimpleMailAttachment(BPositionIO *data,
42 mail_encoding encoding)
44 _data(data),
45 _raw_data(NULL),
46 _we_own_data(false)
48 fStatus = data == NULL ? B_BAD_VALUE : B_OK;
50 Initialize(encoding);
54 BSimpleMailAttachment::BSimpleMailAttachment(const void *data, size_t length,
55 mail_encoding encoding)
57 _data(new BMemoryIO(data,length)),
58 _raw_data(NULL),
59 _we_own_data(true)
61 fStatus = data == NULL ? B_BAD_VALUE : B_OK;
63 Initialize(encoding);
67 BSimpleMailAttachment::BSimpleMailAttachment(BFile *file, bool deleteWhenDone)
69 _data(NULL),
70 _raw_data(NULL),
71 _we_own_data(false)
73 Initialize(base64);
74 SetTo(file, deleteWhenDone);
78 BSimpleMailAttachment::BSimpleMailAttachment(entry_ref *ref)
80 _data(NULL),
81 _raw_data(NULL),
82 _we_own_data(false)
84 Initialize(base64);
85 SetTo(ref);
89 BSimpleMailAttachment::~BSimpleMailAttachment()
91 if (_we_own_data)
92 delete _data;
96 void
97 BSimpleMailAttachment::Initialize(mail_encoding encoding)
99 SetEncoding(encoding);
100 SetHeaderField("Content-Disposition","BMailAttachment");
104 status_t
105 BSimpleMailAttachment::SetTo(BFile *file, bool deleteFileWhenDone)
107 char type[B_MIME_TYPE_LENGTH] = "application/octet-stream";
109 BNodeInfo nodeInfo(file);
110 if (nodeInfo.InitCheck() == B_OK)
111 nodeInfo.GetType(type);
113 SetHeaderField("Content-Type", type);
114 // TODO: No way to get file name (see SetTo(entry_ref *))
115 //SetFileName(ref->name);
117 if (deleteFileWhenDone)
118 SetDecodedDataAndDeleteWhenDone(file);
119 else
120 SetDecodedData(file);
122 return fStatus = B_OK;
126 status_t
127 BSimpleMailAttachment::SetTo(entry_ref *ref)
129 BFile *file = new BFile(ref, B_READ_ONLY);
130 if ((fStatus = file->InitCheck()) < B_OK) {
131 delete file;
132 return fStatus;
135 if (SetTo(file, true) != B_OK)
136 return fStatus;
138 SetFileName(ref->name);
139 return fStatus = B_OK;
143 status_t
144 BSimpleMailAttachment::InitCheck()
146 return fStatus;
150 status_t
151 BSimpleMailAttachment::FileName(char *text)
153 BMessage contentType;
154 HeaderField("Content-Type", &contentType);
156 const char *fileName = contentType.FindString("name");
157 if (!fileName)
158 fileName = contentType.FindString("filename");
159 if (!fileName) {
160 contentType.MakeEmpty();
161 HeaderField("Content-Disposition", &contentType);
162 fileName = contentType.FindString("name");
164 if (!fileName)
165 fileName = contentType.FindString("filename");
166 if (!fileName) {
167 contentType.MakeEmpty();
168 HeaderField("Content-Location", &contentType);
169 fileName = contentType.FindString("unlabeled");
171 if (!fileName)
172 return B_NAME_NOT_FOUND;
174 strncpy(text, fileName, B_FILE_NAME_LENGTH);
175 return B_OK;
179 void
180 BSimpleMailAttachment::SetFileName(const char *name)
182 BMessage contentType;
183 HeaderField("Content-Type", &contentType);
185 if (contentType.ReplaceString("name", name) != B_OK)
186 contentType.AddString("name", name);
188 // Request that the file name header be encoded in UTF-8 if it has weird
189 // characters. If it is just a plain name, the header will appear normal.
190 if (contentType.ReplaceInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION)
191 != B_OK)
192 contentType.AddInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION);
194 SetHeaderField ("Content-Type", &contentType);
198 status_t
199 BSimpleMailAttachment::GetDecodedData(BPositionIO *data)
201 ParseNow();
203 if (!_data)
204 return B_IO_ERROR;
205 if (data == NULL)
206 return B_BAD_VALUE;
208 char buffer[256];
209 ssize_t length;
210 _data->Seek(0,SEEK_SET);
212 while ((length = _data->Read(buffer, sizeof(buffer))) > 0)
213 data->Write(buffer, length);
215 return B_OK;
219 BPositionIO *
220 BSimpleMailAttachment::GetDecodedData()
222 ParseNow();
223 return _data;
227 status_t
228 BSimpleMailAttachment::SetDecodedDataAndDeleteWhenDone(BPositionIO *data)
230 _raw_data = NULL;
232 if (_we_own_data)
233 delete _data;
235 _data = data;
236 _we_own_data = true;
238 return B_OK;
242 status_t
243 BSimpleMailAttachment::SetDecodedData(BPositionIO *data)
245 _raw_data = NULL;
247 if (_we_own_data)
248 delete _data;
250 _data = data;
251 _we_own_data = false;
253 return B_OK;
257 status_t
258 BSimpleMailAttachment::SetDecodedData(const void *data, size_t length)
260 _raw_data = NULL;
262 if (_we_own_data)
263 delete _data;
265 _data = new BMemoryIO(data,length);
266 _we_own_data = true;
268 return B_OK;
272 void
273 BSimpleMailAttachment::SetEncoding(mail_encoding encoding)
275 _encoding = encoding;
277 const char *cte = NULL; //--Content Transfer Encoding
278 switch (_encoding) {
279 case base64:
280 cte = "base64";
281 break;
282 case seven_bit:
283 case no_encoding:
284 cte = "7bit";
285 break;
286 case eight_bit:
287 cte = "8bit";
288 break;
289 case uuencode:
290 cte = "uuencode";
291 break;
292 case quoted_printable:
293 cte = "quoted-printable";
294 break;
295 default:
296 cte = "bug-not-implemented";
297 break;
300 SetHeaderField("Content-Transfer-Encoding", cte);
304 mail_encoding
305 BSimpleMailAttachment::Encoding()
307 return _encoding;
311 status_t
312 BSimpleMailAttachment::SetToRFC822(BPositionIO *data, size_t length,
313 bool parseNow)
315 //---------Massive memory squandering!---ALERT!----------
316 if (_we_own_data)
317 delete _data;
319 off_t position = data->Position();
320 BMailComponent::SetToRFC822(data, length, parseNow);
322 // this actually happens...
323 if (data->Position() - position > (off_t)length)
324 return B_ERROR;
326 length -= (data->Position() - position);
328 _raw_data = data;
329 _raw_length = length;
330 _raw_offset = data->Position();
332 BString encoding = HeaderField("Content-Transfer-Encoding");
333 if (encoding.IFindFirst("base64") >= 0)
334 _encoding = base64;
335 else if (encoding.IFindFirst("quoted-printable") >= 0)
336 _encoding = quoted_printable;
337 else if (encoding.IFindFirst("uuencode") >= 0)
338 _encoding = uuencode;
339 else if (encoding.IFindFirst("7bit") >= 0)
340 _encoding = seven_bit;
341 else if (encoding.IFindFirst("8bit") >= 0)
342 _encoding = eight_bit;
343 else
344 _encoding = no_encoding;
346 if (parseNow)
347 ParseNow();
349 return B_OK;
353 void
354 BSimpleMailAttachment::ParseNow()
356 if (_raw_data == NULL || _raw_length == 0)
357 return;
359 _raw_data->Seek(_raw_offset, SEEK_SET);
361 char *src = (char *)malloc(_raw_length);
362 if (src == NULL)
363 return;
365 size_t size = _raw_length;
367 size = _raw_data->Read(src, _raw_length);
369 BMallocIO *buffer = new BMallocIO;
370 buffer->SetSize(size);
371 // 8bit is *always* more efficient than an encoding, so the buffer
372 // will *never* be larger than before
374 size = decode(_encoding,(char *)(buffer->Buffer()),src,size,0);
375 free(src);
377 buffer->SetSize(size);
379 _data = buffer;
380 _we_own_data = true;
382 _raw_data = NULL;
384 return;
388 status_t
389 BSimpleMailAttachment::RenderToRFC822(BPositionIO *renderTo)
391 ParseNow();
392 BMailComponent::RenderToRFC822(renderTo);
393 //---------Massive memory squandering!---ALERT!----------
395 _data->Seek(0, SEEK_END);
396 off_t size = _data->Position();
397 char *src = (char *)malloc(size);
398 if (src == NULL)
399 return B_NO_MEMORY;
401 MemoryDeleter sourceDeleter(src);
403 _data->Seek(0, SEEK_SET);
405 ssize_t read = _data->Read(src, size);
406 if (read < B_OK)
407 return read;
409 // The encoded text will never be more than twice as large with any
410 // conceivable encoding. But just in case, there's a function call which
411 // will tell us how much space is needed.
412 ssize_t destSize = max_encoded_length(_encoding, read);
413 if (destSize < B_OK) // Invalid encodings like uuencode rejected here.
414 return destSize;
415 char *dest = (char *)malloc(destSize);
416 if (dest == NULL)
417 return B_NO_MEMORY;
419 MemoryDeleter destinationDeleter(dest);
421 destSize = encode (_encoding, dest, src, read, false /* headerMode */);
422 if (destSize < B_OK)
423 return destSize;
425 if (destSize > 0)
426 read = renderTo->Write(dest, destSize);
428 return read > 0 ? B_OK : read;
432 // #pragma mark -
435 /*! Supports and sends attributes.
437 BAttributedMailAttachment::BAttributedMailAttachment()
439 fContainer(NULL),
440 fStatus(B_NO_INIT),
441 _data(NULL),
442 _attributes_attach(NULL)
447 BAttributedMailAttachment::BAttributedMailAttachment(BFile *file,
448 bool deleteWhenDone)
450 fContainer(NULL),
451 _data(NULL),
452 _attributes_attach(NULL)
454 SetTo(file, deleteWhenDone);
458 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
460 fContainer(NULL),
461 _data(NULL),
462 _attributes_attach(NULL)
464 SetTo(ref);
468 BAttributedMailAttachment::~BAttributedMailAttachment()
470 // Our SimpleAttachments are deleted by fContainer
471 delete fContainer;
475 status_t
476 BAttributedMailAttachment::Initialize()
478 // _data & _attributes_attach will be deleted by the container
479 delete fContainer;
481 fContainer = new BMIMEMultipartMailContainer("++++++BFile++++++");
483 _data = new BSimpleMailAttachment();
484 fContainer->AddComponent(_data);
486 _attributes_attach = new BSimpleMailAttachment();
487 _attributes.MakeEmpty();
488 _attributes_attach->SetHeaderField("Content-Type",
489 "application/x-be_attribute; name=\"BeOS Attributes\"");
490 fContainer->AddComponent(_attributes_attach);
492 fContainer->SetHeaderField("Content-Type", "multipart/x-bfile");
493 fContainer->SetHeaderField("Content-Disposition", "BMailAttachment");
495 // also set the header fields of this component, in case someone asks
496 SetHeaderField("Content-Type", "multipart/x-bfile");
497 SetHeaderField("Content-Disposition", "BMailAttachment");
499 return B_OK;
503 status_t
504 BAttributedMailAttachment::SetTo(BFile *file, bool deleteFileWhenDone)
506 if (file == NULL)
507 return fStatus = B_BAD_VALUE;
509 if ((fStatus = Initialize()) < B_OK)
510 return fStatus;
512 _attributes << *file;
514 if ((fStatus = _data->SetTo(file, deleteFileWhenDone)) < B_OK)
515 return fStatus;
517 // Set boundary
519 // Also, we have the make up the boundary out of whole cloth
520 // This is likely to give a completely random string
521 BString boundary;
522 boundary << "BFile--" << ((long)file ^ time(NULL)) << "-"
523 << ~((long)file ^ (long)&fStatus ^ (long)&_attributes) << "--";
524 fContainer->SetBoundary(boundary.String());
526 return fStatus = B_OK;
530 status_t
531 BAttributedMailAttachment::SetTo(entry_ref *ref)
533 if (ref == NULL)
534 return fStatus = B_BAD_VALUE;
536 if ((fStatus = Initialize()) < B_OK)
537 return fStatus;
539 BNode node(ref);
540 if ((fStatus = node.InitCheck()) < B_OK)
541 return fStatus;
543 _attributes << node;
545 if ((fStatus = _data->SetTo(ref)) < B_OK)
546 return fStatus;
548 // Set boundary
550 // This is likely to give a completely random string
551 BString boundary;
552 char buffer[512];
553 strcpy(buffer, ref->name);
554 for (int32 i = strlen(buffer); i-- > 0;) {
555 if (buffer[i] & 0x80)
556 buffer[i] = 'x';
557 else if (buffer[i] == ' ' || buffer[i] == ':')
558 buffer[i] = '_';
560 buffer[32] = '\0';
561 boundary << "BFile-" << buffer << "--" << ((long)_data ^ time(NULL))
562 << "-" << ~((long)_data ^ (long)&buffer ^ (long)&_attributes)
563 << "--";
564 fContainer->SetBoundary(boundary.String());
566 return fStatus = B_OK;
570 status_t
571 BAttributedMailAttachment::InitCheck()
573 return fStatus;
577 void
578 BAttributedMailAttachment::SaveToDisk(BEntry *entry)
580 BString path = "/tmp/";
581 char name[B_FILE_NAME_LENGTH] = "";
582 _data->FileName(name);
583 path << name;
585 BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE);
586 (BNode&)file << _attributes;
587 _data->GetDecodedData(&file);
588 file.Sync();
590 entry->SetTo(path.String());
594 void
595 BAttributedMailAttachment::SetEncoding(mail_encoding encoding)
597 _data->SetEncoding(encoding);
598 if (_attributes_attach != NULL)
599 _attributes_attach->SetEncoding(encoding);
603 mail_encoding
604 BAttributedMailAttachment::Encoding()
606 return _data->Encoding();
610 status_t
611 BAttributedMailAttachment::FileName(char *name)
613 return _data->FileName(name);
617 void
618 BAttributedMailAttachment::SetFileName(const char *name)
620 _data->SetFileName(name);
624 status_t
625 BAttributedMailAttachment::GetDecodedData(BPositionIO *data)
627 BNode *node = dynamic_cast<BNode *>(data);
628 if (node != NULL)
629 *node << _attributes;
631 _data->GetDecodedData(data);
632 return B_OK;
636 status_t
637 BAttributedMailAttachment::SetDecodedData(BPositionIO *data)
639 BNode *node = dynamic_cast<BNode *>(data);
640 if (node != NULL)
641 _attributes << *node;
643 _data->SetDecodedData(data);
644 return B_OK;
648 status_t
649 BAttributedMailAttachment::SetToRFC822(BPositionIO *data, size_t length,
650 bool parseNow)
652 status_t err = Initialize();
653 if (err < B_OK)
654 return err;
656 err = fContainer->SetToRFC822(data, length, parseNow);
657 if (err < B_OK)
658 return err;
660 BMimeType type;
661 fContainer->MIMEType(&type);
662 if (strcmp(type.Type(), "multipart/x-bfile") != 0)
663 return B_BAD_TYPE;
665 // get data and attributes
666 if ((_data = dynamic_cast<BSimpleMailAttachment *>(
667 fContainer->GetComponent(0))) == NULL)
668 return B_BAD_VALUE;
670 if (parseNow) {
671 // Force it to make a copy of the data. Needed for forwarding
672 // messages hack.
673 _data->GetDecodedData();
676 if ((_attributes_attach = dynamic_cast<BSimpleMailAttachment *>(
677 fContainer->GetComponent(1))) == NULL
678 || _attributes_attach->GetDecodedData() == NULL)
679 return B_OK;
681 // Convert the attribute binary attachment into a convenient easy to use
682 // BMessage.
684 int32 len
685 = ((BMallocIO *)(_attributes_attach->GetDecodedData()))->BufferLength();
686 char *start = (char *)malloc(len);
687 if (start == NULL)
688 return B_NO_MEMORY;
690 MemoryDeleter deleter(start);
692 if (_attributes_attach->GetDecodedData()->ReadAt(0, start, len) < len)
693 return B_IO_ERROR;
695 int32 index = 0;
696 while (index < len) {
697 char *name = &start[index];
698 index += strlen(name) + 1;
700 type_code code;
701 memcpy(&code, &start[index], sizeof(type_code));
702 code = B_BENDIAN_TO_HOST_INT32(code);
703 index += sizeof(type_code);
705 int64 buf_length;
706 memcpy(&buf_length, &start[index], sizeof(buf_length));
707 buf_length = B_BENDIAN_TO_HOST_INT64(buf_length);
708 index += sizeof(buf_length);
710 swap_data(code, &start[index], buf_length, B_SWAP_BENDIAN_TO_HOST);
711 _attributes.AddData(name, code, &start[index], buf_length);
712 index += buf_length;
715 return B_OK;
719 status_t
720 BAttributedMailAttachment::RenderToRFC822(BPositionIO *renderTo)
722 BMallocIO *io = new BMallocIO;
724 #if defined(HAIKU_TARGET_PLATFORM_DANO)
725 const
726 #endif
727 char *name;
728 type_code type;
729 for (int32 i = 0; _attributes.GetInfo(B_ANY_TYPE, i, &name, &type) == B_OK;
730 i++) {
731 const void *data;
732 ssize_t dataLen;
733 _attributes.FindData(name, type, &data, &dataLen);
734 io->Write(name, strlen(name) + 1);
736 type_code swappedType = B_HOST_TO_BENDIAN_INT32(type);
737 io->Write(&swappedType, sizeof(type_code));
739 int64 length, swapped;
740 length = dataLen;
741 swapped = B_HOST_TO_BENDIAN_INT64(length);
742 io->Write(&swapped,sizeof(int64));
744 void *buffer = malloc(dataLen);
745 if (buffer == NULL) {
746 delete io;
747 return B_NO_MEMORY;
749 memcpy(buffer, data, dataLen);
750 swap_data(type, buffer, dataLen, B_SWAP_HOST_TO_BENDIAN);
751 io->Write(buffer, dataLen);
752 free(buffer);
754 if (_attributes_attach == NULL)
755 _attributes_attach = new BSimpleMailAttachment;
757 _attributes_attach->SetDecodedDataAndDeleteWhenDone(io);
759 return fContainer->RenderToRFC822(renderTo);
763 status_t
764 BAttributedMailAttachment::MIMEType(BMimeType *mime)
766 return _data->MIMEType(mime);
770 // #pragma mark - The reserved function stubs
773 void BMailAttachment::_ReservedAttachment1() {}
774 void BMailAttachment::_ReservedAttachment2() {}
775 void BMailAttachment::_ReservedAttachment3() {}
776 void BMailAttachment::_ReservedAttachment4() {}
778 void BSimpleMailAttachment::_ReservedSimple1() {}
779 void BSimpleMailAttachment::_ReservedSimple2() {}
780 void BSimpleMailAttachment::_ReservedSimple3() {}
782 void BAttributedMailAttachment::_ReservedAttributed1() {}
783 void BAttributedMailAttachment::_ReservedAttributed2() {}
784 void BAttributedMailAttachment::_ReservedAttributed3() {}