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