xref: /haiku/src/kits/mail/MailAttachment.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
1 /* BMailAttachment - classes which handle mail attachments
2 **
3 ** Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
4 */
5 
6 
7 #include <DataIO.h>
8 #include <String.h>
9 #include <File.h>
10 #include <Entry.h>
11 #include <Mime.h>
12 #include <ByteOrder.h>
13 #include <NodeInfo.h>
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 
18 class _EXPORT BSimpleMailAttachment;
19 class _EXPORT BAttributedMailAttachment;
20 class _EXPORT BMailAttachment;
21 
22 #include <MailAttachment.h>
23 #include <mail_encoding.h>
24 #include <NodeMessage.h>
25 
26 //--------------BSimpleMailAttachment-No attributes or awareness of the file system at large-----
27 
28 BSimpleMailAttachment::BSimpleMailAttachment()
29 	: BMailAttachment(),
30 	fStatus(B_NO_INIT),
31 	_data(NULL),
32 	_raw_data(NULL),
33 	_we_own_data(false)
34 {
35 	Initialize(base64);
36 }
37 
38 BSimpleMailAttachment::BSimpleMailAttachment(BPositionIO *data, mail_encoding encoding)
39 	: BMailAttachment(),
40 	_data(data),
41 	_raw_data(NULL),
42 	_we_own_data(false)
43 {
44 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
45 
46 	Initialize(encoding);
47 }
48 
49 BSimpleMailAttachment::BSimpleMailAttachment(const void *data, size_t length, mail_encoding encoding)
50 	: BMailAttachment(),
51 	_data(new BMemoryIO(data,length)),
52 	_raw_data(NULL),
53 	_we_own_data(true)
54 {
55 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
56 
57 	Initialize(encoding);
58 }
59 
60 BSimpleMailAttachment::BSimpleMailAttachment(BFile *file, bool delete_when_done)
61 	: BMailAttachment(),
62 	_data(NULL),
63 	_raw_data(NULL),
64 	_we_own_data(false)
65 {
66 	Initialize(base64);
67 	SetTo(file,delete_when_done);
68 }
69 
70 BSimpleMailAttachment::BSimpleMailAttachment(entry_ref *ref)
71 	: BMailAttachment(),
72 	_data(NULL),
73 	_raw_data(NULL),
74 	_we_own_data(false)
75 {
76 	Initialize(base64);
77 	SetTo(ref);
78 }
79 
80 BSimpleMailAttachment::~BSimpleMailAttachment()
81 {
82 	if (_we_own_data)
83 		delete _data;
84 }
85 
86 void BSimpleMailAttachment::Initialize(mail_encoding encoding)
87 {
88 	SetEncoding(encoding);
89 	SetHeaderField("Content-Disposition","BMailAttachment");
90 }
91 
92 status_t BSimpleMailAttachment::SetTo(BFile *file, bool delete_file_when_done)
93 {
94 	char type[B_MIME_TYPE_LENGTH] = "application/octet-stream";
95 
96 	BNodeInfo nodeInfo(file);
97 	if (nodeInfo.InitCheck() == B_OK)
98 		nodeInfo.GetType(type);
99 
100 	SetHeaderField("Content-Type",type);
101 	//---No way to get file name (see SetTo(entry_ref *))
102 	//SetFileName(ref->name);
103 
104 	if (delete_file_when_done)
105 		SetDecodedDataAndDeleteWhenDone(file);
106 	else
107 		SetDecodedData(file);
108 
109 	return fStatus = B_OK;
110 }
111 
112 status_t BSimpleMailAttachment::SetTo(entry_ref *ref)
113 {
114 	BFile *file = new BFile(ref,B_READ_ONLY);
115 
116 	if ((fStatus = file->InitCheck()) < B_OK)
117 	{
118 		delete file;
119 		return fStatus;
120 	}
121 	if (SetTo(file,true) < B_OK)
122 		// fStatus is set by SetTo()
123 		return fStatus;
124 
125 	SetFileName(ref->name);
126 	return fStatus = B_OK;
127 }
128 
129 status_t BSimpleMailAttachment::InitCheck()
130 {
131 	return fStatus;
132 }
133 
134 status_t BSimpleMailAttachment::FileName(char *text) {
135 	BMessage content_type;
136 	HeaderField("Content-Type",&content_type);
137 
138 	const char *fileName = content_type.FindString("name");
139 	if (!fileName)
140 		fileName = content_type.FindString("filename");
141 	if (!fileName)
142 	{
143 		content_type.MakeEmpty();
144 		HeaderField("Content-Disposition",&content_type);
145 		fileName = content_type.FindString("name");
146 	}
147 	if (!fileName)
148 		fileName = content_type.FindString("filename");
149 	if (!fileName)
150 	{
151 		content_type.MakeEmpty();
152 		HeaderField("Content-Location",&content_type);
153 		fileName = content_type.FindString("unlabeled");
154 	}
155 	if (!fileName)
156 		return B_NAME_NOT_FOUND;
157 
158 	strncpy(text,fileName,B_FILE_NAME_LENGTH);
159 	return B_OK;
160 }
161 
162 
163 void BSimpleMailAttachment::SetFileName(const char *name) {
164 	BMessage content_type;
165 
166 	HeaderField("Content-Type",&content_type);
167 
168 	if (content_type.ReplaceString("name",name) != B_OK)
169 		content_type.AddString("name",name);
170 
171 	// Request that the file name header be encoded in UTF-8 if it has weird
172 	// characters.  If it is just a plain name, the header will appear normal.
173 	if (content_type.ReplaceInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION) != B_OK)
174 		content_type.AddInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION);
175 
176 	SetHeaderField ("Content-Type", &content_type);
177 }
178 
179 
180 status_t
181 BSimpleMailAttachment::GetDecodedData(BPositionIO *data)
182 {
183 	ParseNow();
184 
185 	if (!_data)
186 		return B_IO_ERROR;
187 	if (data == NULL)
188 		return B_BAD_VALUE;
189 
190 	char buffer[256];
191 	ssize_t length;
192 	_data->Seek(0,SEEK_SET);
193 
194 	while ((length = _data->Read(buffer,sizeof(buffer))) > 0)
195 		data->Write(buffer,length);
196 
197 	return B_OK;
198 }
199 
200 
201 BPositionIO *
202 BSimpleMailAttachment::GetDecodedData()
203 {
204 	ParseNow();
205 
206 	return _data;
207 }
208 
209 status_t BSimpleMailAttachment::SetDecodedDataAndDeleteWhenDone(BPositionIO *data) {
210 	_raw_data = NULL;
211 
212 	if (_we_own_data)
213 		delete _data;
214 
215 	_data = data;
216 	_we_own_data = true;
217 
218 	return B_OK;
219 }
220 
221 status_t BSimpleMailAttachment::SetDecodedData(BPositionIO *data) {
222 	_raw_data = NULL;
223 
224 	if (_we_own_data)
225 		delete _data;
226 
227 	_data = data;
228 	_we_own_data = false;
229 
230 	return B_OK;
231 }
232 
233 status_t BSimpleMailAttachment::SetDecodedData(const void *data, size_t length) {
234 	_raw_data = NULL;
235 
236 	if (_we_own_data)
237 		delete _data;
238 
239 	_data = new BMemoryIO(data,length);
240 	_we_own_data = true;
241 
242 	return B_OK;
243 }
244 
245 void BSimpleMailAttachment::SetEncoding(mail_encoding encoding) {
246 	_encoding = encoding;
247 
248 	char *cte = NULL; //--Content Transfer Encoding
249 	switch (_encoding) {
250 		case base64:
251 			cte = "base64";
252 			break;
253 		case seven_bit:
254 		case no_encoding:
255 			cte = "7bit";
256 			break;
257 		case eight_bit:
258 			cte = "8bit";
259 			break;
260 		case uuencode:
261 			cte = "uuencode";
262 			break;
263 		case quoted_printable:
264 			cte = "quoted-printable";
265 			break;
266 		default:
267 			cte = "bug-not-implemented";
268 			break;
269 	}
270 
271 	SetHeaderField("Content-Transfer-Encoding",cte);
272 }
273 
274 mail_encoding BSimpleMailAttachment::Encoding() {
275 	return _encoding;
276 }
277 
278 status_t BSimpleMailAttachment::SetToRFC822(BPositionIO *data, size_t length, bool parse_now) {
279 	//---------Massive memory squandering!---ALERT!----------
280 	if (_we_own_data)
281 		delete _data;
282 
283 	off_t position = data->Position();
284 	BMailComponent::SetToRFC822(data,length,parse_now);
285 
286 	// this actually happens...
287 	if (data->Position() - position > length)
288 		return B_ERROR;
289 
290 	length -= (data->Position() - position);
291 
292 	_raw_data = data;
293 	_raw_length = length;
294 	_raw_offset = data->Position();
295 
296 	BString encoding = HeaderField("Content-Transfer-Encoding");
297 	if (encoding.IFindFirst("base64") >= 0)
298 		_encoding = base64;
299 	else if (encoding.IFindFirst("quoted-printable") >= 0)
300 		_encoding = quoted_printable;
301 	else if (encoding.IFindFirst("uuencode") >= 0)
302 		_encoding = uuencode;
303 	else if (encoding.IFindFirst("7bit") >= 0)
304 		_encoding = seven_bit;
305 	else if (encoding.IFindFirst("8bit") >= 0)
306 		_encoding = eight_bit;
307 	else
308 		_encoding = no_encoding;
309 
310 	if (parse_now)
311 		ParseNow();
312 
313 	return B_OK;
314 }
315 
316 void BSimpleMailAttachment::ParseNow() {
317 	if (_raw_data == NULL || _raw_length == 0)
318 		return;
319 
320 	_raw_data->Seek(_raw_offset,SEEK_SET);
321 
322 	char *src = (char *)malloc(_raw_length);
323 	size_t size = _raw_length;
324 
325 	size = _raw_data->Read(src,_raw_length);
326 
327 	BMallocIO *buffer = new BMallocIO;
328 	buffer->SetSize(size); //-------8bit is *always* more efficient than an encoding, so the buffer will *never* be larger than before
329 
330 	size = decode(_encoding,(char *)(buffer->Buffer()),src,size,0);
331 	free(src);
332 
333 	buffer->SetSize(size);
334 
335 	_data = buffer;
336 	_we_own_data = true;
337 
338 	_raw_data = NULL;
339 
340 	return;
341 }
342 
343 status_t BSimpleMailAttachment::RenderToRFC822(BPositionIO *render_to) {
344 	ParseNow();
345 	BMailComponent::RenderToRFC822(render_to);
346 	//---------Massive memory squandering!---ALERT!----------
347 	//	now with error checks, dumb :-) -- axeld.
348 
349 	_data->Seek(0,SEEK_END);
350 	off_t size = _data->Position();
351 	char *src = (char *)malloc(size);
352 	if (src == NULL)
353 		return B_NO_MEMORY;
354 
355 	_data->Seek(0,SEEK_SET);
356 
357 	ssize_t read = _data->Read(src,size);
358 	if (read < B_OK)
359 		return read; // Return an error code and leak memory.
360 
361 	// The encoded text will never be more than twice as large with any
362 	// conceivable encoding.  But just in case, there's a function call which
363 	// will tell us how much space is needed.
364 	ssize_t destSize = max_encoded_length(_encoding,read);
365 	if (destSize < B_OK) // Invalid encodings like uuencode rejected here.
366 		return destSize;
367 	char *dest = (char *)malloc(destSize);
368 	if (dest == NULL)
369 		return B_NO_MEMORY;
370 
371 	destSize = encode (_encoding, dest, src, read, false /* headerMode */);
372 	if (destSize < B_OK)
373 		return destSize;
374 	if (destSize > 0)
375 		read = render_to->Write(dest,destSize);
376 	free (src);
377 	free (dest);
378 	return (read > 0) ? B_OK : read;
379 }
380 
381 
382 //-------BAttributedMailAttachment--Awareness of bfs, sends attributes--
383 //	#pragma mark -
384 
385 
386 BAttributedMailAttachment::BAttributedMailAttachment()
387 	: BMailAttachment(),
388 	fContainer(NULL),
389 	fStatus(B_NO_INIT),
390 	_data(NULL),
391 	_attributes_attach(NULL)
392 {
393 }
394 
395 BAttributedMailAttachment::BAttributedMailAttachment(BFile *file, bool delete_when_done)
396 	: BMailAttachment(),
397 	fContainer(NULL),
398 	_data(NULL),
399 	_attributes_attach(NULL)
400 {
401 	SetTo(file,delete_when_done);
402 }
403 
404 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
405 	: BMailAttachment(),
406 	fContainer(NULL),
407 	_data(NULL),
408 	_attributes_attach(NULL)
409 {
410 	SetTo(ref);
411 }
412 
413 BAttributedMailAttachment::~BAttributedMailAttachment() {
414 	// Our SimpleAttachments are deleted by fContainer
415 	delete fContainer;
416 }
417 
418 
419 status_t BAttributedMailAttachment::Initialize()
420 {
421 	// _data & _attributes_attach will be deleted by the container
422 	delete fContainer;
423 
424 	fContainer = new BMIMEMultipartMailContainer("++++++BFile++++++");
425 
426 	_data = new BSimpleMailAttachment();
427 	fContainer->AddComponent(_data);
428 
429 	_attributes_attach = new BSimpleMailAttachment();
430 	_attributes.MakeEmpty();
431 	_attributes_attach->SetHeaderField("Content-Type","application/x-be_attribute; name=\"BeOS Attributes\"");
432 	fContainer->AddComponent(_attributes_attach);
433 
434 	fContainer->SetHeaderField("Content-Type","multipart/x-bfile");
435 	fContainer->SetHeaderField("Content-Disposition","BMailAttachment");
436 
437 	// also set the header fields of this component, in case someone asks
438 	SetHeaderField("Content-Type","multipart/x-bfile");
439 	SetHeaderField("Content-Disposition","BMailAttachment");
440 
441 	return B_OK;
442 }
443 
444 
445 status_t BAttributedMailAttachment::SetTo(BFile *file, bool delete_file_when_done)
446 {
447 	if (file == NULL)
448 		return fStatus = B_BAD_VALUE;
449 
450 	if ((fStatus = Initialize()) < B_OK)
451 		return fStatus;
452 
453 	_attributes << *file;
454 
455 	if ((fStatus = _data->SetTo(file,delete_file_when_done)) < B_OK)
456 		return fStatus;
457 
458 	// Set boundary
459 
460 	//---Also, we have the make up the boundary out of whole cloth
461 	//------This is likely to give a completely random string---
462 	BString boundary;
463 	boundary << "BFile--" << (int32(file) ^ time(NULL)) << "-" << ~((int32)file ^ (int32)&fStatus ^ (int32)&_attributes) << "--";
464 	fContainer->SetBoundary(boundary.String());
465 
466 	return fStatus = B_OK;
467 }
468 
469 status_t BAttributedMailAttachment::SetTo(entry_ref *ref)
470 {
471 	if (ref == NULL)
472 		return fStatus = B_BAD_VALUE;
473 
474 	if ((fStatus = Initialize()) < B_OK)
475 		return fStatus;
476 
477 	BNode node(ref);
478 	if ((fStatus = node.InitCheck()) < B_OK)
479 		return fStatus;
480 
481 	_attributes << node;
482 
483 	if ((fStatus = _data->SetTo(ref)) < B_OK)
484 		return fStatus;
485 
486 	// Set boundary
487 
488 	//------This is likely to give a completely random string---
489 	BString boundary;
490 	char buffer[512];
491 	strcpy(buffer, ref->name);
492 	for (int32 i = strlen(buffer);i-- > 0;)
493 	{
494 		if (buffer[i] & 0x80)
495 			buffer[i] = 'x';
496 		else if (buffer[i] == ' ' || buffer[i] == ':')
497 			buffer[i] = '_';
498 	}
499 	buffer[32] = '\0';
500 	boundary << "BFile-" << buffer << "--" << ((int32)_data ^ time(NULL)) << "-" << ~((int32)_data ^ (int32)&buffer ^ (int32)&_attributes) << "--";
501 	fContainer->SetBoundary(boundary.String());
502 
503 	return fStatus = B_OK;
504 }
505 
506 status_t BAttributedMailAttachment::InitCheck()
507 {
508 	return fStatus;
509 }
510 
511 void BAttributedMailAttachment::SaveToDisk(BEntry *entry) {
512 	BString path = "/tmp/";
513 	char name[255] = "";
514 	_data->FileName(name);
515 	path << name;
516 
517 	BFile file(path.String(),B_READ_WRITE | B_CREATE_FILE);
518 	(BNode&)file << _attributes;
519 	_data->GetDecodedData(&file);
520 	file.Sync();
521 
522 	entry->SetTo(path.String());
523 }
524 
525 void BAttributedMailAttachment::SetEncoding(mail_encoding encoding) {
526 	_data->SetEncoding(encoding);
527 	if (_attributes_attach != NULL)
528 		_attributes_attach->SetEncoding(encoding);
529 }
530 
531 mail_encoding BAttributedMailAttachment::Encoding() {
532 	return _data->Encoding();
533 }
534 
535 status_t BAttributedMailAttachment::FileName(char *name) {
536 	return _data->FileName(name);
537 }
538 
539 void BAttributedMailAttachment::SetFileName(const char *name) {
540 	_data->SetFileName(name);
541 }
542 
543 status_t BAttributedMailAttachment::GetDecodedData(BPositionIO *data) {
544 	BNode *node = dynamic_cast<BNode *>(data);
545 	if (node != NULL)
546 		*node << _attributes;
547 
548 	_data->GetDecodedData(data);
549 	return B_OK;
550 }
551 
552 status_t BAttributedMailAttachment::SetDecodedData(BPositionIO *data) {
553 	BNode *node = dynamic_cast<BNode *>(data);
554 	if (node != NULL)
555 		_attributes << *node;
556 
557 	_data->SetDecodedData(data);
558 	return B_OK;
559 }
560 
561 status_t BAttributedMailAttachment::SetToRFC822(BPositionIO *data, size_t length, bool parse_now)
562 {
563 	status_t err = Initialize();
564 	if (err < B_OK)
565 		return err;
566 
567 	err = fContainer->SetToRFC822(data,length,parse_now);
568 	if (err < B_OK)
569 		return err;
570 
571 	BMimeType type;
572 	fContainer->MIMEType(&type);
573 	if (strcmp(type.Type(),"multipart/x-bfile") != 0)
574 		return B_BAD_TYPE;
575 
576 	// get data and attributes
577 	if ((_data = dynamic_cast<BSimpleMailAttachment *>(fContainer->GetComponent(0))) == NULL)
578 		return B_BAD_VALUE;
579 	if (parse_now)
580 		_data->GetDecodedData(); // Force it to make a copy of the data.  Needed for forwarding messages hack.
581 
582 	if ((_attributes_attach = dynamic_cast<BSimpleMailAttachment *>(fContainer->GetComponent(1))) == NULL
583 		|| _attributes_attach->GetDecodedData() == NULL)
584 			return B_OK;
585 
586 	// Convert the attribute binary attachment into a convenient easy to use BMessage.
587 
588 	int32 len = ((BMallocIO *)(_attributes_attach->GetDecodedData()))->BufferLength();
589 	char *start = (char *)malloc(len);
590 	if (start == NULL)
591 		return B_NO_MEMORY;
592 
593 	if (_attributes_attach->GetDecodedData()->ReadAt(0,start,len) < len)
594 		return B_IO_ERROR;
595 
596 	int32 index = 0;
597 	while (index < len) {
598 		char *name = &start[index];
599 		index += strlen(name) + 1;
600 
601 		type_code code;
602 		memcpy(&code, &start[index], sizeof(type_code));
603 		code = B_BENDIAN_TO_HOST_INT32(code);
604 		index += sizeof(type_code);
605 
606 		int64 buf_length;
607 		memcpy(&buf_length, &start[index], sizeof(buf_length));
608 		buf_length = B_BENDIAN_TO_HOST_INT64(buf_length);
609 		index += sizeof(buf_length);
610 
611 		swap_data(code, &start[index], buf_length, B_SWAP_BENDIAN_TO_HOST);
612 		_attributes.AddData(name, code, &start[index], buf_length);
613 		index += buf_length;
614 	}
615 	free(start);
616 
617 	return B_OK;
618 }
619 
620 status_t BAttributedMailAttachment::RenderToRFC822(BPositionIO *render_to) {
621 	BMallocIO *io = new BMallocIO;
622 #if defined(HAIKU_TARGET_PLATFORM_DANO)
623 	const
624 #endif
625 	char *name;
626 	type_code type, swap_typed;
627 	for (int32 i = 0; _attributes.GetInfo(B_ANY_TYPE,i,&name,&type) == B_OK; i++) {
628 		const void *data;
629 		ssize_t dataLen;
630 		_attributes.FindData(name,type,&data,&dataLen);
631 		io->Write(name,strlen(name) + 1);
632 		swap_typed = B_HOST_TO_BENDIAN_INT32(type);
633 		io->Write(&swap_typed,sizeof(type_code));
634 
635 		int64 length, swapped;
636 		length = dataLen;
637 		swapped = B_HOST_TO_BENDIAN_INT64(length);
638 		io->Write(&swapped,sizeof(int64));
639 
640 		void *allocd = malloc(dataLen);
641 		if (allocd == NULL) {
642 			delete io;
643 			return B_NO_MEMORY;
644 		}
645 		memcpy(allocd,data,dataLen);
646 		swap_data(type, allocd, dataLen, B_SWAP_HOST_TO_BENDIAN);
647 		io->Write(allocd,dataLen);
648 		free(allocd);
649 	}
650 	if (_attributes_attach == NULL)
651 		_attributes_attach = new BSimpleMailAttachment;
652 
653 	_attributes_attach->SetDecodedDataAndDeleteWhenDone(io);
654 
655 	return fContainer->RenderToRFC822(render_to);
656 }
657 
658 status_t BAttributedMailAttachment::MIMEType(BMimeType *mime) {
659 	return _data->MIMEType(mime);
660 }
661 
662 
663 //	The reserved function stubs
664 //	#pragma mark -
665 
666 void BMailAttachment::_ReservedAttachment1() {}
667 void BMailAttachment::_ReservedAttachment2() {}
668 void BMailAttachment::_ReservedAttachment3() {}
669 void BMailAttachment::_ReservedAttachment4() {}
670 
671 void BSimpleMailAttachment::_ReservedSimple1() {}
672 void BSimpleMailAttachment::_ReservedSimple2() {}
673 void BSimpleMailAttachment::_ReservedSimple3() {}
674 
675 void BAttributedMailAttachment::_ReservedAttributed1() {}
676 void BAttributedMailAttachment::_ReservedAttributed2() {}
677 void BAttributedMailAttachment::_ReservedAttributed3() {}
678