xref: /haiku/src/kits/mail/MailAttachment.cpp (revision a4f6a81235ca2522c01f532de13cad9b729d4029)
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 <malloc.h>
16 
17 class _EXPORT BSimpleMailAttachment;
18 class _EXPORT BAttributedMailAttachment;
19 class _EXPORT BMailAttachment;
20 
21 #include <MailAttachment.h>
22 #include <mail_encoding.h>
23 #include <NodeMessage.h>
24 
25 //--------------BSimpleMailAttachment-No attributes or awareness of the file system at large-----
26 
27 BSimpleMailAttachment::BSimpleMailAttachment()
28 	: BMailAttachment(),
29 	fStatus(B_NO_INIT),
30 	_data(NULL),
31 	_raw_data(NULL),
32 	_we_own_data(false)
33 {
34 	Initialize(base64);
35 }
36 
37 BSimpleMailAttachment::BSimpleMailAttachment(BPositionIO *data, mail_encoding encoding)
38 	: BMailAttachment(),
39 	_data(data),
40 	_raw_data(NULL),
41 	_we_own_data(false)
42 {
43 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
44 
45 	Initialize(encoding);
46 }
47 
48 BSimpleMailAttachment::BSimpleMailAttachment(const void *data, size_t length, mail_encoding encoding)
49 	: BMailAttachment(),
50 	_data(new BMemoryIO(data,length)),
51 	_raw_data(NULL),
52 	_we_own_data(true)
53 {
54 	fStatus = data == NULL ? B_BAD_VALUE : B_OK;
55 
56 	Initialize(encoding);
57 }
58 
59 BSimpleMailAttachment::BSimpleMailAttachment(BFile *file, bool delete_when_done)
60 	: BMailAttachment(),
61 	_data(NULL),
62 	_raw_data(NULL),
63 	_we_own_data(false)
64 {
65 	Initialize(base64);
66 	SetTo(file,delete_when_done);
67 }
68 
69 BSimpleMailAttachment::BSimpleMailAttachment(entry_ref *ref)
70 	: BMailAttachment(),
71 	_data(NULL),
72 	_raw_data(NULL),
73 	_we_own_data(false)
74 {
75 	Initialize(base64);
76 	SetTo(ref);
77 }
78 
79 BSimpleMailAttachment::~BSimpleMailAttachment()
80 {
81 	if (_we_own_data)
82 		delete _data;
83 }
84 
85 void BSimpleMailAttachment::Initialize(mail_encoding encoding)
86 {
87 	SetEncoding(encoding);
88 	SetHeaderField("Content-Disposition","BMailAttachment");
89 }
90 
91 status_t BSimpleMailAttachment::SetTo(BFile *file, bool delete_file_when_done)
92 {
93 	char type[B_MIME_TYPE_LENGTH] = "application/octet-stream";
94 
95 	BNodeInfo nodeInfo(file);
96 	if (nodeInfo.InitCheck() == B_OK)
97 		nodeInfo.GetType(type);
98 
99 	SetHeaderField("Content-Type",type);
100 	//---No way to get file name (see SetTo(entry_ref *))
101 	//SetFileName(ref->name);
102 
103 	if (delete_file_when_done)
104 		SetDecodedDataAndDeleteWhenDone(file);
105 	else
106 		SetDecodedData(file);
107 
108 	return fStatus = B_OK;
109 }
110 
111 status_t BSimpleMailAttachment::SetTo(entry_ref *ref)
112 {
113 	BFile *file = new BFile(ref,B_READ_ONLY);
114 
115 	if ((fStatus = file->InitCheck()) < B_OK)
116 	{
117 		delete file;
118 		return fStatus;
119 	}
120 	if (SetTo(file,true) < B_OK)
121 		// fStatus is set by SetTo()
122 		return fStatus;
123 
124 	SetFileName(ref->name);
125 	return fStatus = B_OK;
126 }
127 
128 status_t BSimpleMailAttachment::InitCheck()
129 {
130 	return fStatus;
131 }
132 
133 status_t BSimpleMailAttachment::FileName(char *text) {
134 	BMessage content_type;
135 	HeaderField("Content-Type",&content_type);
136 
137 	const char *fileName = content_type.FindString("name");
138 	if (!fileName)
139 		fileName = content_type.FindString("filename");
140 	if (!fileName)
141 	{
142 		content_type.MakeEmpty();
143 		HeaderField("Content-Disposition",&content_type);
144 		fileName = content_type.FindString("name");
145 	}
146 	if (!fileName)
147 		fileName = content_type.FindString("filename");
148 	if (!fileName)
149 	{
150 		content_type.MakeEmpty();
151 		HeaderField("Content-Location",&content_type);
152 		fileName = content_type.FindString("unlabeled");
153 	}
154 	if (!fileName)
155 		return B_NAME_NOT_FOUND;
156 
157 	strncpy(text,fileName,B_FILE_NAME_LENGTH);
158 	return B_OK;
159 }
160 
161 
162 void BSimpleMailAttachment::SetFileName(const char *name) {
163 	BMessage content_type;
164 
165 	HeaderField("Content-Type",&content_type);
166 
167 	if (content_type.ReplaceString("name",name) != B_OK)
168 		content_type.AddString("name",name);
169 
170 	// Request that the file name header be encoded in UTF-8 if it has weird
171 	// characters.  If it is just a plain name, the header will appear normal.
172 	if (content_type.ReplaceInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION) != B_OK)
173 		content_type.AddInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION);
174 
175 	SetHeaderField ("Content-Type", &content_type);
176 }
177 
178 
179 status_t
180 BSimpleMailAttachment::GetDecodedData(BPositionIO *data)
181 {
182 	ParseNow();
183 
184 	if (!_data)
185 		return B_IO_ERROR;
186 	if (data == NULL)
187 		return B_BAD_VALUE;
188 
189 	char buffer[256];
190 	ssize_t length;
191 	_data->Seek(0,SEEK_SET);
192 
193 	while ((length = _data->Read(buffer,sizeof(buffer))) > 0)
194 		data->Write(buffer,length);
195 
196 	return B_OK;
197 }
198 
199 
200 BPositionIO *
201 BSimpleMailAttachment::GetDecodedData()
202 {
203 	ParseNow();
204 
205 	return _data;
206 }
207 
208 status_t BSimpleMailAttachment::SetDecodedDataAndDeleteWhenDone(BPositionIO *data) {
209 	_raw_data = NULL;
210 
211 	if (_we_own_data)
212 		delete _data;
213 
214 	_data = data;
215 	_we_own_data = true;
216 
217 	return B_OK;
218 }
219 
220 status_t BSimpleMailAttachment::SetDecodedData(BPositionIO *data) {
221 	_raw_data = NULL;
222 
223 	if (_we_own_data)
224 		delete _data;
225 
226 	_data = data;
227 	_we_own_data = false;
228 
229 	return B_OK;
230 }
231 
232 status_t BSimpleMailAttachment::SetDecodedData(const void *data, size_t length) {
233 	_raw_data = NULL;
234 
235 	if (_we_own_data)
236 		delete _data;
237 
238 	_data = new BMemoryIO(data,length);
239 	_we_own_data = true;
240 
241 	return B_OK;
242 }
243 
244 void BSimpleMailAttachment::SetEncoding(mail_encoding encoding) {
245 	_encoding = encoding;
246 
247 	char *cte = NULL; //--Content Transfer Encoding
248 	switch (_encoding) {
249 		case base64:
250 			cte = "base64";
251 			break;
252 		case seven_bit:
253 		case no_encoding:
254 			cte = "7bit";
255 			break;
256 		case eight_bit:
257 			cte = "8bit";
258 			break;
259 		case uuencode:
260 			cte = "uuencode";
261 			break;
262 		case quoted_printable:
263 			cte = "quoted-printable";
264 			break;
265 		default:
266 			cte = "bug-not-implemented";
267 			break;
268 	}
269 
270 	SetHeaderField("Content-Transfer-Encoding",cte);
271 }
272 
273 mail_encoding BSimpleMailAttachment::Encoding() {
274 	return _encoding;
275 }
276 
277 status_t BSimpleMailAttachment::SetToRFC822(BPositionIO *data, size_t length, bool parse_now) {
278 	//---------Massive memory squandering!---ALERT!----------
279 	if (_we_own_data)
280 		delete _data;
281 
282 	off_t position = data->Position();
283 	BMailComponent::SetToRFC822(data,length,parse_now);
284 
285 	// this actually happens...
286 	if (data->Position() - position > length)
287 		return B_ERROR;
288 
289 	length -= (data->Position() - position);
290 
291 	_raw_data = data;
292 	_raw_length = length;
293 	_raw_offset = data->Position();
294 
295 	BString encoding = HeaderField("Content-Transfer-Encoding");
296 	if (encoding.IFindFirst("base64") >= 0)
297 		_encoding = base64;
298 	else if (encoding.IFindFirst("quoted-printable") >= 0)
299 		_encoding = quoted_printable;
300 	else if (encoding.IFindFirst("uuencode") >= 0)
301 		_encoding = uuencode;
302 	else if (encoding.IFindFirst("7bit") >= 0)
303 		_encoding = seven_bit;
304 	else if (encoding.IFindFirst("8bit") >= 0)
305 		_encoding = eight_bit;
306 	else
307 		_encoding = no_encoding;
308 
309 	if (parse_now)
310 		ParseNow();
311 
312 	return B_OK;
313 }
314 
315 void BSimpleMailAttachment::ParseNow() {
316 	if (_raw_data == NULL || _raw_length == 0)
317 		return;
318 
319 	_raw_data->Seek(_raw_offset,SEEK_SET);
320 
321 	char *src = (char *)malloc(_raw_length);
322 	size_t size = _raw_length;
323 
324 	size = _raw_data->Read(src,_raw_length);
325 
326 	BMallocIO *buffer = new BMallocIO;
327 	buffer->SetSize(size); //-------8bit is *always* more efficient than an encoding, so the buffer will *never* be larger than before
328 
329 	size = decode(_encoding,(char *)(buffer->Buffer()),src,size,0);
330 	free(src);
331 
332 	buffer->SetSize(size);
333 
334 	_data = buffer;
335 	_we_own_data = true;
336 
337 	_raw_data = NULL;
338 
339 	return;
340 }
341 
342 status_t BSimpleMailAttachment::RenderToRFC822(BPositionIO *render_to) {
343 	ParseNow();
344 	BMailComponent::RenderToRFC822(render_to);
345 	//---------Massive memory squandering!---ALERT!----------
346 	//	now with error checks, dumb :-) -- axeld.
347 
348 	_data->Seek(0,SEEK_END);
349 	off_t size = _data->Position();
350 	char *src = (char *)malloc(size);
351 	if (src == NULL)
352 		return B_NO_MEMORY;
353 
354 	_data->Seek(0,SEEK_SET);
355 
356 	ssize_t read = _data->Read(src,size);
357 	if (read < B_OK)
358 		return read; // Return an error code and leak memory.
359 
360 	// The encoded text will never be more than twice as large with any
361 	// conceivable encoding.  But just in case, there's a function call which
362 	// will tell us how much space is needed.
363 	ssize_t destSize = max_encoded_length(_encoding,read);
364 	if (destSize < B_OK) // Invalid encodings like uuencode rejected here.
365 		return destSize;
366 	char *dest = (char *)malloc(destSize);
367 	if (dest == NULL)
368 		return B_NO_MEMORY;
369 
370 	destSize = encode (_encoding, dest, src, read, false /* headerMode */);
371 	if (destSize < B_OK)
372 		return destSize;
373 	if (destSize > 0)
374 		read = render_to->Write(dest,destSize);
375 	free (src);
376 	free (dest);
377 	return (read > 0) ? B_OK : read;
378 }
379 
380 
381 //-------BAttributedMailAttachment--Awareness of bfs, sends attributes--
382 //	#pragma mark -
383 
384 
385 BAttributedMailAttachment::BAttributedMailAttachment()
386 	: BMailAttachment(),
387 	fContainer(NULL),
388 	fStatus(B_NO_INIT),
389 	_data(NULL),
390 	_attributes_attach(NULL)
391 {
392 }
393 
394 BAttributedMailAttachment::BAttributedMailAttachment(BFile *file, bool delete_when_done)
395 	: BMailAttachment(),
396 	fContainer(NULL),
397 	_data(NULL),
398 	_attributes_attach(NULL)
399 {
400 	SetTo(file,delete_when_done);
401 }
402 
403 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
404 	: BMailAttachment(),
405 	fContainer(NULL),
406 	_data(NULL),
407 	_attributes_attach(NULL)
408 {
409 	SetTo(ref);
410 }
411 
412 BAttributedMailAttachment::~BAttributedMailAttachment() {
413 	// Our SimpleAttachments are deleted by fContainer
414 	delete fContainer;
415 }
416 
417 
418 status_t BAttributedMailAttachment::Initialize()
419 {
420 	// _data & _attributes_attach will be deleted by the container
421 	if (fContainer != NULL)
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 			return B_NO_MEMORY;
643 		memcpy(allocd,data,dataLen);
644 		swap_data(type, allocd, dataLen, B_SWAP_HOST_TO_BENDIAN);
645 		io->Write(allocd,dataLen);
646 		free(allocd);
647 	}
648 	if (_attributes_attach == NULL)
649 		_attributes_attach = new BSimpleMailAttachment;
650 
651 	_attributes_attach->SetDecodedDataAndDeleteWhenDone(io);
652 
653 	return fContainer->RenderToRFC822(render_to);
654 }
655 
656 status_t BAttributedMailAttachment::MIMEType(BMimeType *mime) {
657 	return _data->MIMEType(mime);
658 }
659 
660 
661 //	The reserved function stubs
662 //	#pragma mark -
663 
664 void BMailAttachment::_ReservedAttachment1() {}
665 void BMailAttachment::_ReservedAttachment2() {}
666 void BMailAttachment::_ReservedAttachment3() {}
667 void BMailAttachment::_ReservedAttachment4() {}
668 
669 void BSimpleMailAttachment::_ReservedSimple1() {}
670 void BSimpleMailAttachment::_ReservedSimple2() {}
671 void BSimpleMailAttachment::_ReservedSimple3() {}
672 
673 void BAttributedMailAttachment::_ReservedAttributed1() {}
674 void BAttributedMailAttachment::_ReservedAttributed2() {}
675 void BAttributedMailAttachment::_ReservedAttributed3() {}
676