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*/
30BSimpleMailAttachment::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
41BSimpleMailAttachment::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
54BSimpleMailAttachment::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
67BSimpleMailAttachment::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
78BSimpleMailAttachment::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
89BSimpleMailAttachment::~BSimpleMailAttachment()
90{
91	if (_we_own_data)
92		delete _data;
93}
94
95
96void
97BSimpleMailAttachment::Initialize(mail_encoding encoding)
98{
99	SetEncoding(encoding);
100	SetHeaderField("Content-Disposition","BMailAttachment");
101}
102
103
104status_t
105BSimpleMailAttachment::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
126status_t
127BSimpleMailAttachment::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
143status_t
144BSimpleMailAttachment::InitCheck()
145{
146	return fStatus;
147}
148
149
150status_t
151BSimpleMailAttachment::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
179void
180BSimpleMailAttachment::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
198status_t
199BSimpleMailAttachment::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
219BPositionIO *
220BSimpleMailAttachment::GetDecodedData()
221{
222	ParseNow();
223	return _data;
224}
225
226
227status_t
228BSimpleMailAttachment::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
242status_t
243BSimpleMailAttachment::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
257status_t
258BSimpleMailAttachment::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
272void
273BSimpleMailAttachment::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
304mail_encoding
305BSimpleMailAttachment::Encoding()
306{
307	return _encoding;
308}
309
310
311status_t
312BSimpleMailAttachment::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
353void
354BSimpleMailAttachment::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
388status_t
389BSimpleMailAttachment::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*/
437BAttributedMailAttachment::BAttributedMailAttachment()
438	:
439	fContainer(NULL),
440	fStatus(B_NO_INIT),
441	_data(NULL),
442	_attributes_attach(NULL)
443{
444}
445
446
447BAttributedMailAttachment::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
458BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
459	:
460	fContainer(NULL),
461	_data(NULL),
462	_attributes_attach(NULL)
463{
464	SetTo(ref);
465}
466
467
468BAttributedMailAttachment::~BAttributedMailAttachment()
469{
470	// Our SimpleAttachments are deleted by fContainer
471	delete fContainer;
472}
473
474
475status_t
476BAttributedMailAttachment::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
503status_t
504BAttributedMailAttachment::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
530status_t
531BAttributedMailAttachment::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
570status_t
571BAttributedMailAttachment::InitCheck()
572{
573	return fStatus;
574}
575
576
577void
578BAttributedMailAttachment::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
594void
595BAttributedMailAttachment::SetEncoding(mail_encoding encoding)
596{
597	_data->SetEncoding(encoding);
598	if (_attributes_attach != NULL)
599		_attributes_attach->SetEncoding(encoding);
600}
601
602
603mail_encoding
604BAttributedMailAttachment::Encoding()
605{
606	return _data->Encoding();
607}
608
609
610status_t
611BAttributedMailAttachment::FileName(char *name)
612{
613	return _data->FileName(name);
614}
615
616
617void
618BAttributedMailAttachment::SetFileName(const char *name)
619{
620	_data->SetFileName(name);
621}
622
623
624status_t
625BAttributedMailAttachment::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
636status_t
637BAttributedMailAttachment::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
648status_t
649BAttributedMailAttachment::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
719status_t
720BAttributedMailAttachment::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
763status_t
764BAttributedMailAttachment::MIMEType(BMimeType *mime)
765{
766	return _data->MIMEType(mime);
767}
768
769
770// #pragma mark - The reserved function stubs
771
772
773void BMailAttachment::_ReservedAttachment1() {}
774void BMailAttachment::_ReservedAttachment2() {}
775void BMailAttachment::_ReservedAttachment3() {}
776void BMailAttachment::_ReservedAttachment4() {}
777
778void BSimpleMailAttachment::_ReservedSimple1() {}
779void BSimpleMailAttachment::_ReservedSimple2() {}
780void BSimpleMailAttachment::_ReservedSimple3() {}
781
782void BAttributedMailAttachment::_ReservedAttributed1() {}
783void BAttributedMailAttachment::_ReservedAttributed2() {}
784void BAttributedMailAttachment::_ReservedAttributed3() {}
785