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 > 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--" << (int32(file) ^ time(NULL)) << "-" 523 << ~((int32)file ^ (int32)&fStatus ^ (int32)&_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 << "--" << ((int32)_data ^ time(NULL)) 562 << "-" << ~((int32)_data ^ (int32)&buffer ^ (int32)&_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[255] = ""; 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