xref: /haiku/src/kits/mail/MailAttachment.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
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 	if (fContainer != NULL)
423 		delete fContainer;
424 
425 	fContainer = new BMIMEMultipartMailContainer("++++++BFile++++++");
426 
427 	_data = new BSimpleMailAttachment();
428 	fContainer->AddComponent(_data);
429 
430 	_attributes_attach = new BSimpleMailAttachment();
431 	_attributes.MakeEmpty();
432 	_attributes_attach->SetHeaderField("Content-Type","application/x-be_attribute; name=\"BeOS Attributes\"");
433 	fContainer->AddComponent(_attributes_attach);
434 
435 	fContainer->SetHeaderField("Content-Type","multipart/x-bfile");
436 	fContainer->SetHeaderField("Content-Disposition","BMailAttachment");
437 
438 	// also set the header fields of this component, in case someone asks
439 	SetHeaderField("Content-Type","multipart/x-bfile");
440 	SetHeaderField("Content-Disposition","BMailAttachment");
441 
442 	return B_OK;
443 }
444 
445 
446 status_t BAttributedMailAttachment::SetTo(BFile *file, bool delete_file_when_done)
447 {
448 	if (file == NULL)
449 		return fStatus = B_BAD_VALUE;
450 
451 	if ((fStatus = Initialize()) < B_OK)
452 		return fStatus;
453 
454 	_attributes << *file;
455 
456 	if ((fStatus = _data->SetTo(file,delete_file_when_done)) < B_OK)
457 		return fStatus;
458 
459 	// Set boundary
460 
461 	//---Also, we have the make up the boundary out of whole cloth
462 	//------This is likely to give a completely random string---
463 	BString boundary;
464 	boundary << "BFile--" << (int32(file) ^ time(NULL)) << "-" << ~((int32)file ^ (int32)&fStatus ^ (int32)&_attributes) << "--";
465 	fContainer->SetBoundary(boundary.String());
466 
467 	return fStatus = B_OK;
468 }
469 
470 status_t BAttributedMailAttachment::SetTo(entry_ref *ref)
471 {
472 	if (ref == NULL)
473 		return fStatus = B_BAD_VALUE;
474 
475 	if ((fStatus = Initialize()) < B_OK)
476 		return fStatus;
477 
478 	BNode node(ref);
479 	if ((fStatus = node.InitCheck()) < B_OK)
480 		return fStatus;
481 
482 	_attributes << node;
483 
484 	if ((fStatus = _data->SetTo(ref)) < B_OK)
485 		return fStatus;
486 
487 	// Set boundary
488 
489 	//------This is likely to give a completely random string---
490 	BString boundary;
491 	char buffer[512];
492 	strcpy(buffer, ref->name);
493 	for (int32 i = strlen(buffer);i-- > 0;)
494 	{
495 		if (buffer[i] & 0x80)
496 			buffer[i] = 'x';
497 		else if (buffer[i] == ' ' || buffer[i] == ':')
498 			buffer[i] = '_';
499 	}
500 	buffer[32] = '\0';
501 	boundary << "BFile-" << buffer << "--" << ((int32)_data ^ time(NULL)) << "-" << ~((int32)_data ^ (int32)&buffer ^ (int32)&_attributes) << "--";
502 	fContainer->SetBoundary(boundary.String());
503 
504 	return fStatus = B_OK;
505 }
506 
507 status_t BAttributedMailAttachment::InitCheck()
508 {
509 	return fStatus;
510 }
511 
512 void BAttributedMailAttachment::SaveToDisk(BEntry *entry) {
513 	BString path = "/tmp/";
514 	char name[255] = "";
515 	_data->FileName(name);
516 	path << name;
517 
518 	BFile file(path.String(),B_READ_WRITE | B_CREATE_FILE);
519 	(BNode&)file << _attributes;
520 	_data->GetDecodedData(&file);
521 	file.Sync();
522 
523 	entry->SetTo(path.String());
524 }
525 
526 void BAttributedMailAttachment::SetEncoding(mail_encoding encoding) {
527 	_data->SetEncoding(encoding);
528 	if (_attributes_attach != NULL)
529 		_attributes_attach->SetEncoding(encoding);
530 }
531 
532 mail_encoding BAttributedMailAttachment::Encoding() {
533 	return _data->Encoding();
534 }
535 
536 status_t BAttributedMailAttachment::FileName(char *name) {
537 	return _data->FileName(name);
538 }
539 
540 void BAttributedMailAttachment::SetFileName(const char *name) {
541 	_data->SetFileName(name);
542 }
543 
544 status_t BAttributedMailAttachment::GetDecodedData(BPositionIO *data) {
545 	BNode *node = dynamic_cast<BNode *>(data);
546 	if (node != NULL)
547 		*node << _attributes;
548 
549 	_data->GetDecodedData(data);
550 	return B_OK;
551 }
552 
553 status_t BAttributedMailAttachment::SetDecodedData(BPositionIO *data) {
554 	BNode *node = dynamic_cast<BNode *>(data);
555 	if (node != NULL)
556 		_attributes << *node;
557 
558 	_data->SetDecodedData(data);
559 	return B_OK;
560 }
561 
562 status_t BAttributedMailAttachment::SetToRFC822(BPositionIO *data, size_t length, bool parse_now)
563 {
564 	status_t err = Initialize();
565 	if (err < B_OK)
566 		return err;
567 
568 	err = fContainer->SetToRFC822(data,length,parse_now);
569 	if (err < B_OK)
570 		return err;
571 
572 	BMimeType type;
573 	fContainer->MIMEType(&type);
574 	if (strcmp(type.Type(),"multipart/x-bfile") != 0)
575 		return B_BAD_TYPE;
576 
577 	// get data and attributes
578 	if ((_data = dynamic_cast<BSimpleMailAttachment *>(fContainer->GetComponent(0))) == NULL)
579 		return B_BAD_VALUE;
580 	if (parse_now)
581 		_data->GetDecodedData(); // Force it to make a copy of the data.  Needed for forwarding messages hack.
582 
583 	if ((_attributes_attach = dynamic_cast<BSimpleMailAttachment *>(fContainer->GetComponent(1))) == NULL
584 		|| _attributes_attach->GetDecodedData() == NULL)
585 			return B_OK;
586 
587 	// Convert the attribute binary attachment into a convenient easy to use BMessage.
588 
589 	int32 len = ((BMallocIO *)(_attributes_attach->GetDecodedData()))->BufferLength();
590 	char *start = (char *)malloc(len);
591 	if (start == NULL)
592 		return B_NO_MEMORY;
593 
594 	if (_attributes_attach->GetDecodedData()->ReadAt(0,start,len) < len)
595 		return B_IO_ERROR;
596 
597 	int32 index = 0;
598 	while (index < len) {
599 		char *name = &start[index];
600 		index += strlen(name) + 1;
601 
602 		type_code code;
603 		memcpy(&code, &start[index], sizeof(type_code));
604 		code = B_BENDIAN_TO_HOST_INT32(code);
605 		index += sizeof(type_code);
606 
607 		int64 buf_length;
608 		memcpy(&buf_length, &start[index], sizeof(buf_length));
609 		buf_length = B_BENDIAN_TO_HOST_INT64(buf_length);
610 		index += sizeof(buf_length);
611 
612 		swap_data(code, &start[index], buf_length, B_SWAP_BENDIAN_TO_HOST);
613 		_attributes.AddData(name, code, &start[index], buf_length);
614 		index += buf_length;
615 	}
616 	free(start);
617 
618 	return B_OK;
619 }
620 
621 status_t BAttributedMailAttachment::RenderToRFC822(BPositionIO *render_to) {
622 	BMallocIO *io = new BMallocIO;
623 #if defined(HAIKU_TARGET_PLATFORM_DANO)
624 	const
625 #endif
626 	char *name;
627 	type_code type, swap_typed;
628 	for (int32 i = 0; _attributes.GetInfo(B_ANY_TYPE,i,&name,&type) == B_OK; i++) {
629 		const void *data;
630 		ssize_t dataLen;
631 		_attributes.FindData(name,type,&data,&dataLen);
632 		io->Write(name,strlen(name) + 1);
633 		swap_typed = B_HOST_TO_BENDIAN_INT32(type);
634 		io->Write(&swap_typed,sizeof(type_code));
635 
636 		int64 length, swapped;
637 		length = dataLen;
638 		swapped = B_HOST_TO_BENDIAN_INT64(length);
639 		io->Write(&swapped,sizeof(int64));
640 
641 		void *allocd = malloc(dataLen);
642 		if (allocd == NULL)
643 			return B_NO_MEMORY;
644 		memcpy(allocd,data,dataLen);
645 		swap_data(type, allocd, dataLen, B_SWAP_HOST_TO_BENDIAN);
646 		io->Write(allocd,dataLen);
647 		free(allocd);
648 	}
649 	if (_attributes_attach == NULL)
650 		_attributes_attach = new BSimpleMailAttachment;
651 
652 	_attributes_attach->SetDecodedDataAndDeleteWhenDone(io);
653 
654 	return fContainer->RenderToRFC822(render_to);
655 }
656 
657 status_t BAttributedMailAttachment::MIMEType(BMimeType *mime) {
658 	return _data->MIMEType(mime);
659 }
660 
661 
662 //	The reserved function stubs
663 //	#pragma mark -
664 
665 void BMailAttachment::_ReservedAttachment1() {}
666 void BMailAttachment::_ReservedAttachment2() {}
667 void BMailAttachment::_ReservedAttachment3() {}
668 void BMailAttachment::_ReservedAttachment4() {}
669 
670 void BSimpleMailAttachment::_ReservedSimple1() {}
671 void BSimpleMailAttachment::_ReservedSimple2() {}
672 void BSimpleMailAttachment::_ReservedSimple3() {}
673 
674 void BAttributedMailAttachment::_ReservedAttributed1() {}
675 void BAttributedMailAttachment::_ReservedAttributed2() {}
676 void BAttributedMailAttachment::_ReservedAttributed3() {}
677