xref: /haiku/src/kits/mail/MailAttachment.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
3  */
4 
5 
6 /*! Classes which handle mail attachments */
7 
8 
9 #include <MailAttachment.h>
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 
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>
21 
22 #include <AutoDeleter.h>
23 
24 #include <mail_encoding.h>
25 #include <NodeMessage.h>
26 
27 
28 /*! No attributes or awareness of the file system at large
29 */
30 BSimpleMailAttachment::BSimpleMailAttachment()
31 	:
32 	fStatus(B_NO_INIT),
33 	_data(NULL),
34 	_raw_data(NULL),
35 	_we_own_data(false)
36 {
37 	Initialize(base64);
38 }
39 
40 
41 BSimpleMailAttachment::BSimpleMailAttachment(BPositionIO *data,
42 	mail_encoding encoding)
43 	:
44 	_data(data),
45 	_raw_data(NULL),
46 	_we_own_data(false)
47 {
48 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
49 
50 	Initialize(encoding);
51 }
52 
53 
54 BSimpleMailAttachment::BSimpleMailAttachment(const void *data, size_t length,
55 	mail_encoding encoding)
56 	:
57 	_data(new BMemoryIO(data,length)),
58 	_raw_data(NULL),
59 	_we_own_data(true)
60 {
61 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
62 
63 	Initialize(encoding);
64 }
65 
66 
67 BSimpleMailAttachment::BSimpleMailAttachment(BFile *file, bool deleteWhenDone)
68 	:
69 	_data(NULL),
70 	_raw_data(NULL),
71 	_we_own_data(false)
72 {
73 	Initialize(base64);
74 	SetTo(file, deleteWhenDone);
75 }
76 
77 
78 BSimpleMailAttachment::BSimpleMailAttachment(entry_ref *ref)
79 	:
80 	_data(NULL),
81 	_raw_data(NULL),
82 	_we_own_data(false)
83 {
84 	Initialize(base64);
85 	SetTo(ref);
86 }
87 
88 
89 BSimpleMailAttachment::~BSimpleMailAttachment()
90 {
91 	if (_we_own_data)
92 		delete _data;
93 }
94 
95 
96 void
97 BSimpleMailAttachment::Initialize(mail_encoding encoding)
98 {
99 	SetEncoding(encoding);
100 	SetHeaderField("Content-Disposition","BMailAttachment");
101 }
102 
103 
104 status_t
105 BSimpleMailAttachment::SetTo(BFile *file, bool deleteFileWhenDone)
106 {
107 	char type[B_MIME_TYPE_LENGTH] = "application/octet-stream";
108 
109 	BNodeInfo nodeInfo(file);
110 	if (nodeInfo.InitCheck() == B_OK)
111 		nodeInfo.GetType(type);
112 
113 	SetHeaderField("Content-Type", type);
114 	// TODO: No way to get file name (see SetTo(entry_ref *))
115 	//SetFileName(ref->name);
116 
117 	if (deleteFileWhenDone)
118 		SetDecodedDataAndDeleteWhenDone(file);
119 	else
120 		SetDecodedData(file);
121 
122 	return fStatus = B_OK;
123 }
124 
125 
126 status_t
127 BSimpleMailAttachment::SetTo(entry_ref *ref)
128 {
129 	BFile *file = new BFile(ref, B_READ_ONLY);
130 	if ((fStatus = file->InitCheck()) < B_OK) {
131 		delete file;
132 		return fStatus;
133 	}
134 
135 	if (SetTo(file, true) != B_OK)
136 		return fStatus;
137 
138 	SetFileName(ref->name);
139 	return fStatus = B_OK;
140 }
141 
142 
143 status_t
144 BSimpleMailAttachment::InitCheck()
145 {
146 	return fStatus;
147 }
148 
149 
150 status_t
151 BSimpleMailAttachment::FileName(char *text)
152 {
153 	BMessage contentType;
154 	HeaderField("Content-Type", &contentType);
155 
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");
163 	}
164 	if (!fileName)
165 		fileName = contentType.FindString("filename");
166 	if (!fileName) {
167 		contentType.MakeEmpty();
168 		HeaderField("Content-Location", &contentType);
169 		fileName = contentType.FindString("unlabeled");
170 	}
171 	if (!fileName)
172 		return B_NAME_NOT_FOUND;
173 
174 	strncpy(text, fileName, B_FILE_NAME_LENGTH);
175 	return B_OK;
176 }
177 
178 
179 void
180 BSimpleMailAttachment::SetFileName(const char *name)
181 {
182 	BMessage contentType;
183 	HeaderField("Content-Type", &contentType);
184 
185 	if (contentType.ReplaceString("name", name) != B_OK)
186 		contentType.AddString("name", name);
187 
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);
193 
194 	SetHeaderField ("Content-Type", &contentType);
195 }
196 
197 
198 status_t
199 BSimpleMailAttachment::GetDecodedData(BPositionIO *data)
200 {
201 	ParseNow();
202 
203 	if (!_data)
204 		return B_IO_ERROR;
205 	if (data == NULL)
206 		return B_BAD_VALUE;
207 
208 	char buffer[256];
209 	ssize_t length;
210 	_data->Seek(0,SEEK_SET);
211 
212 	while ((length = _data->Read(buffer, sizeof(buffer))) > 0)
213 		data->Write(buffer, length);
214 
215 	return B_OK;
216 }
217 
218 
219 BPositionIO *
220 BSimpleMailAttachment::GetDecodedData()
221 {
222 	ParseNow();
223 	return _data;
224 }
225 
226 
227 status_t
228 BSimpleMailAttachment::SetDecodedDataAndDeleteWhenDone(BPositionIO *data)
229 {
230 	_raw_data = NULL;
231 
232 	if (_we_own_data)
233 		delete _data;
234 
235 	_data = data;
236 	_we_own_data = true;
237 
238 	return B_OK;
239 }
240 
241 
242 status_t
243 BSimpleMailAttachment::SetDecodedData(BPositionIO *data)
244 {
245 	_raw_data = NULL;
246 
247 	if (_we_own_data)
248 		delete _data;
249 
250 	_data = data;
251 	_we_own_data = false;
252 
253 	return B_OK;
254 }
255 
256 
257 status_t
258 BSimpleMailAttachment::SetDecodedData(const void *data, size_t length)
259 {
260 	_raw_data = NULL;
261 
262 	if (_we_own_data)
263 		delete _data;
264 
265 	_data = new BMemoryIO(data,length);
266 	_we_own_data = true;
267 
268 	return B_OK;
269 }
270 
271 
272 void
273 BSimpleMailAttachment::SetEncoding(mail_encoding encoding)
274 {
275 	_encoding = encoding;
276 
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;
298 	}
299 
300 	SetHeaderField("Content-Transfer-Encoding", cte);
301 }
302 
303 
304 mail_encoding
305 BSimpleMailAttachment::Encoding()
306 {
307 	return _encoding;
308 }
309 
310 
311 status_t
312 BSimpleMailAttachment::SetToRFC822(BPositionIO *data, size_t length,
313 	bool parseNow)
314 {
315 	//---------Massive memory squandering!---ALERT!----------
316 	if (_we_own_data)
317 		delete _data;
318 
319 	off_t position = data->Position();
320 	BMailComponent::SetToRFC822(data, length, parseNow);
321 
322 	// this actually happens...
323 	if (data->Position() - position > (off_t)length)
324 		return B_ERROR;
325 
326 	length -= (data->Position() - position);
327 
328 	_raw_data = data;
329 	_raw_length = length;
330 	_raw_offset = data->Position();
331 
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;
345 
346 	if (parseNow)
347 		ParseNow();
348 
349 	return B_OK;
350 }
351 
352 
353 void
354 BSimpleMailAttachment::ParseNow()
355 {
356 	if (_raw_data == NULL || _raw_length == 0)
357 		return;
358 
359 	_raw_data->Seek(_raw_offset, SEEK_SET);
360 
361 	char *src = (char *)malloc(_raw_length);
362 	if (src == NULL)
363 		return;
364 
365 	size_t size = _raw_length;
366 
367 	size = _raw_data->Read(src, _raw_length);
368 
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
373 
374 	size = decode(_encoding,(char *)(buffer->Buffer()),src,size,0);
375 	free(src);
376 
377 	buffer->SetSize(size);
378 
379 	_data = buffer;
380 	_we_own_data = true;
381 
382 	_raw_data = NULL;
383 
384 	return;
385 }
386 
387 
388 status_t
389 BSimpleMailAttachment::RenderToRFC822(BPositionIO *renderTo)
390 {
391 	ParseNow();
392 	BMailComponent::RenderToRFC822(renderTo);
393 	//---------Massive memory squandering!---ALERT!----------
394 
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;
400 
401 	MemoryDeleter sourceDeleter(src);
402 
403 	_data->Seek(0, SEEK_SET);
404 
405 	ssize_t read = _data->Read(src, size);
406 	if (read < B_OK)
407 		return read;
408 
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;
418 
419 	MemoryDeleter destinationDeleter(dest);
420 
421 	destSize = encode (_encoding, dest, src, read, false /* headerMode */);
422 	if (destSize < B_OK)
423 		return destSize;
424 
425 	if (destSize > 0)
426 		read = renderTo->Write(dest, destSize);
427 
428 	return read > 0 ? B_OK : read;
429 }
430 
431 
432 //	#pragma mark -
433 
434 
435 /*!	Supports and sends attributes.
436 */
437 BAttributedMailAttachment::BAttributedMailAttachment()
438 	:
439 	fContainer(NULL),
440 	fStatus(B_NO_INIT),
441 	_data(NULL),
442 	_attributes_attach(NULL)
443 {
444 }
445 
446 
447 BAttributedMailAttachment::BAttributedMailAttachment(BFile *file,
448 	bool deleteWhenDone)
449 	:
450 	fContainer(NULL),
451 	_data(NULL),
452 	_attributes_attach(NULL)
453 {
454 	SetTo(file, deleteWhenDone);
455 }
456 
457 
458 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
459 	:
460 	fContainer(NULL),
461 	_data(NULL),
462 	_attributes_attach(NULL)
463 {
464 	SetTo(ref);
465 }
466 
467 
468 BAttributedMailAttachment::~BAttributedMailAttachment()
469 {
470 	// Our SimpleAttachments are deleted by fContainer
471 	delete fContainer;
472 }
473 
474 
475 status_t
476 BAttributedMailAttachment::Initialize()
477 {
478 	// _data & _attributes_attach will be deleted by the container
479 	delete fContainer;
480 
481 	fContainer = new BMIMEMultipartMailContainer("++++++BFile++++++");
482 
483 	_data = new BSimpleMailAttachment();
484 	fContainer->AddComponent(_data);
485 
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);
491 
492 	fContainer->SetHeaderField("Content-Type", "multipart/x-bfile");
493 	fContainer->SetHeaderField("Content-Disposition", "BMailAttachment");
494 
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");
498 
499 	return B_OK;
500 }
501 
502 
503 status_t
504 BAttributedMailAttachment::SetTo(BFile *file, bool deleteFileWhenDone)
505 {
506 	if (file == NULL)
507 		return fStatus = B_BAD_VALUE;
508 
509 	if ((fStatus = Initialize()) < B_OK)
510 		return fStatus;
511 
512 	_attributes << *file;
513 
514 	if ((fStatus = _data->SetTo(file, deleteFileWhenDone)) < B_OK)
515 		return fStatus;
516 
517 	// Set boundary
518 
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());
525 
526 	return fStatus = B_OK;
527 }
528 
529 
530 status_t
531 BAttributedMailAttachment::SetTo(entry_ref *ref)
532 {
533 	if (ref == NULL)
534 		return fStatus = B_BAD_VALUE;
535 
536 	if ((fStatus = Initialize()) < B_OK)
537 		return fStatus;
538 
539 	BNode node(ref);
540 	if ((fStatus = node.InitCheck()) < B_OK)
541 		return fStatus;
542 
543 	_attributes << node;
544 
545 	if ((fStatus = _data->SetTo(ref)) < B_OK)
546 		return fStatus;
547 
548 	// Set boundary
549 
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] = '_';
559 	}
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());
565 
566 	return fStatus = B_OK;
567 }
568 
569 
570 status_t
571 BAttributedMailAttachment::InitCheck()
572 {
573 	return fStatus;
574 }
575 
576 
577 void
578 BAttributedMailAttachment::SaveToDisk(BEntry *entry)
579 {
580 	BString path = "/tmp/";
581 	char name[B_FILE_NAME_LENGTH] = "";
582 	_data->FileName(name);
583 	path << name;
584 
585 	BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE);
586 	(BNode&)file << _attributes;
587 	_data->GetDecodedData(&file);
588 	file.Sync();
589 
590 	entry->SetTo(path.String());
591 }
592 
593 
594 void
595 BAttributedMailAttachment::SetEncoding(mail_encoding encoding)
596 {
597 	_data->SetEncoding(encoding);
598 	if (_attributes_attach != NULL)
599 		_attributes_attach->SetEncoding(encoding);
600 }
601 
602 
603 mail_encoding
604 BAttributedMailAttachment::Encoding()
605 {
606 	return _data->Encoding();
607 }
608 
609 
610 status_t
611 BAttributedMailAttachment::FileName(char *name)
612 {
613 	return _data->FileName(name);
614 }
615 
616 
617 void
618 BAttributedMailAttachment::SetFileName(const char *name)
619 {
620 	_data->SetFileName(name);
621 }
622 
623 
624 status_t
625 BAttributedMailAttachment::GetDecodedData(BPositionIO *data)
626 {
627 	BNode *node = dynamic_cast<BNode *>(data);
628 	if (node != NULL)
629 		*node << _attributes;
630 
631 	_data->GetDecodedData(data);
632 	return B_OK;
633 }
634 
635 
636 status_t
637 BAttributedMailAttachment::SetDecodedData(BPositionIO *data)
638 {
639 	BNode *node = dynamic_cast<BNode *>(data);
640 	if (node != NULL)
641 		_attributes << *node;
642 
643 	_data->SetDecodedData(data);
644 	return B_OK;
645 }
646 
647 
648 status_t
649 BAttributedMailAttachment::SetToRFC822(BPositionIO *data, size_t length,
650 	bool parseNow)
651 {
652 	status_t err = Initialize();
653 	if (err < B_OK)
654 		return err;
655 
656 	err = fContainer->SetToRFC822(data, length, parseNow);
657 	if (err < B_OK)
658 		return err;
659 
660 	BMimeType type;
661 	fContainer->MIMEType(&type);
662 	if (strcmp(type.Type(), "multipart/x-bfile") != 0)
663 		return B_BAD_TYPE;
664 
665 	// get data and attributes
666 	if ((_data = dynamic_cast<BSimpleMailAttachment *>(
667 			fContainer->GetComponent(0))) == NULL)
668 		return B_BAD_VALUE;
669 
670 	if (parseNow) {
671 		// Force it to make a copy of the data. Needed for forwarding
672 		// messages hack.
673 		_data->GetDecodedData();
674 	}
675 
676 	if ((_attributes_attach = dynamic_cast<BSimpleMailAttachment *>(
677 				fContainer->GetComponent(1))) == NULL
678 		|| _attributes_attach->GetDecodedData() == NULL)
679 		return B_OK;
680 
681 	// Convert the attribute binary attachment into a convenient easy to use
682 	// BMessage.
683 
684 	int32 len
685 		= ((BMallocIO *)(_attributes_attach->GetDecodedData()))->BufferLength();
686 	char *start = (char *)malloc(len);
687 	if (start == NULL)
688 		return B_NO_MEMORY;
689 
690 	MemoryDeleter deleter(start);
691 
692 	if (_attributes_attach->GetDecodedData()->ReadAt(0, start, len) < len)
693 		return B_IO_ERROR;
694 
695 	int32 index = 0;
696 	while (index < len) {
697 		char *name = &start[index];
698 		index += strlen(name) + 1;
699 
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);
704 
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);
709 
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;
713 	}
714 
715 	return B_OK;
716 }
717 
718 
719 status_t
720 BAttributedMailAttachment::RenderToRFC822(BPositionIO *renderTo)
721 {
722 	BMallocIO *io = new BMallocIO;
723 
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);
735 
736 		type_code swappedType = B_HOST_TO_BENDIAN_INT32(type);
737 		io->Write(&swappedType, sizeof(type_code));
738 
739 		int64 length, swapped;
740 		length = dataLen;
741 		swapped = B_HOST_TO_BENDIAN_INT64(length);
742 		io->Write(&swapped,sizeof(int64));
743 
744 		void *buffer = malloc(dataLen);
745 		if (buffer == NULL) {
746 			delete io;
747 			return B_NO_MEMORY;
748 		}
749 		memcpy(buffer, data, dataLen);
750 		swap_data(type, buffer, dataLen, B_SWAP_HOST_TO_BENDIAN);
751 		io->Write(buffer, dataLen);
752 		free(buffer);
753 	}
754 	if (_attributes_attach == NULL)
755 		_attributes_attach = new BSimpleMailAttachment;
756 
757 	_attributes_attach->SetDecodedDataAndDeleteWhenDone(io);
758 
759 	return fContainer->RenderToRFC822(renderTo);
760 }
761 
762 
763 status_t
764 BAttributedMailAttachment::MIMEType(BMimeType *mime)
765 {
766 	return _data->MIMEType(mime);
767 }
768 
769 
770 // #pragma mark - The reserved function stubs
771 
772 
773 void BMailAttachment::_ReservedAttachment1() {}
774 void BMailAttachment::_ReservedAttachment2() {}
775 void BMailAttachment::_ReservedAttachment3() {}
776 void BMailAttachment::_ReservedAttachment4() {}
777 
778 void BSimpleMailAttachment::_ReservedSimple1() {}
779 void BSimpleMailAttachment::_ReservedSimple2() {}
780 void BSimpleMailAttachment::_ReservedSimple3() {}
781 
782 void BAttributedMailAttachment::_ReservedAttributed1() {}
783 void BAttributedMailAttachment::_ReservedAttributed2() {}
784 void BAttributedMailAttachment::_ReservedAttributed3() {}
785