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 */
BSimpleMailAttachment()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
BSimpleMailAttachment(BPositionIO * data,mail_encoding encoding)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
BSimpleMailAttachment(const void * data,size_t length,mail_encoding encoding)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
BSimpleMailAttachment(BFile * file,bool deleteWhenDone)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
BSimpleMailAttachment(entry_ref * ref)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
~BSimpleMailAttachment()89 BSimpleMailAttachment::~BSimpleMailAttachment()
90 {
91 if (_we_own_data)
92 delete _data;
93 }
94
95
96 void
Initialize(mail_encoding encoding)97 BSimpleMailAttachment::Initialize(mail_encoding encoding)
98 {
99 SetEncoding(encoding);
100 SetHeaderField("Content-Disposition","BMailAttachment");
101 }
102
103
104 status_t
SetTo(BFile * file,bool deleteFileWhenDone)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
SetTo(entry_ref * ref)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
InitCheck()144 BSimpleMailAttachment::InitCheck()
145 {
146 return fStatus;
147 }
148
149
150 status_t
FileName(char * text)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
SetFileName(const char * name)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
GetDecodedData(BPositionIO * data)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 *
GetDecodedData()220 BSimpleMailAttachment::GetDecodedData()
221 {
222 ParseNow();
223 return _data;
224 }
225
226
227 status_t
SetDecodedDataAndDeleteWhenDone(BPositionIO * data)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
SetDecodedData(BPositionIO * data)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
SetDecodedData(const void * data,size_t length)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
SetEncoding(mail_encoding encoding)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
Encoding()305 BSimpleMailAttachment::Encoding()
306 {
307 return _encoding;
308 }
309
310
311 status_t
SetToRFC822(BPositionIO * data,size_t length,bool parseNow)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
ParseNow()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
RenderToRFC822(BPositionIO * renderTo)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 */
BAttributedMailAttachment()437 BAttributedMailAttachment::BAttributedMailAttachment()
438 :
439 fContainer(NULL),
440 fStatus(B_NO_INIT),
441 _data(NULL),
442 _attributes_attach(NULL)
443 {
444 }
445
446
BAttributedMailAttachment(BFile * file,bool deleteWhenDone)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
BAttributedMailAttachment(entry_ref * ref)458 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref)
459 :
460 fContainer(NULL),
461 _data(NULL),
462 _attributes_attach(NULL)
463 {
464 SetTo(ref);
465 }
466
467
~BAttributedMailAttachment()468 BAttributedMailAttachment::~BAttributedMailAttachment()
469 {
470 // Our SimpleAttachments are deleted by fContainer
471 delete fContainer;
472 }
473
474
475 status_t
Initialize()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
SetTo(BFile * file,bool deleteFileWhenDone)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
SetTo(entry_ref * ref)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
InitCheck()571 BAttributedMailAttachment::InitCheck()
572 {
573 return fStatus;
574 }
575
576
577 void
SaveToDisk(BEntry * entry)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
SetEncoding(mail_encoding encoding)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
Encoding()604 BAttributedMailAttachment::Encoding()
605 {
606 return _data->Encoding();
607 }
608
609
610 status_t
FileName(char * name)611 BAttributedMailAttachment::FileName(char *name)
612 {
613 return _data->FileName(name);
614 }
615
616
617 void
SetFileName(const char * name)618 BAttributedMailAttachment::SetFileName(const char *name)
619 {
620 _data->SetFileName(name);
621 }
622
623
624 status_t
GetDecodedData(BPositionIO * data)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
SetDecodedData(BPositionIO * data)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
SetToRFC822(BPositionIO * data,size_t length,bool parseNow)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
RenderToRFC822(BPositionIO * renderTo)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
MIMEType(BMimeType * mime)764 BAttributedMailAttachment::MIMEType(BMimeType *mime)
765 {
766 return _data->MIMEType(mime);
767 }
768
769
770 // #pragma mark - The reserved function stubs
771
772
_ReservedAttachment1()773 void BMailAttachment::_ReservedAttachment1() {}
_ReservedAttachment2()774 void BMailAttachment::_ReservedAttachment2() {}
_ReservedAttachment3()775 void BMailAttachment::_ReservedAttachment3() {}
_ReservedAttachment4()776 void BMailAttachment::_ReservedAttachment4() {}
777
_ReservedSimple1()778 void BSimpleMailAttachment::_ReservedSimple1() {}
_ReservedSimple2()779 void BSimpleMailAttachment::_ReservedSimple2() {}
_ReservedSimple3()780 void BSimpleMailAttachment::_ReservedSimple3() {}
781
_ReservedAttributed1()782 void BAttributedMailAttachment::_ReservedAttributed1() {}
_ReservedAttributed2()783 void BAttributedMailAttachment::_ReservedAttributed2() {}
_ReservedAttributed3()784 void BAttributedMailAttachment::_ReservedAttributed3() {}
785