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