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 <malloc.h> 16 17 class _EXPORT BSimpleMailAttachment; 18 class _EXPORT BAttributedMailAttachment; 19 class _EXPORT BMailAttachment; 20 21 #include <MailAttachment.h> 22 #include <mail_encoding.h> 23 #include <NodeMessage.h> 24 25 //--------------BSimpleMailAttachment-No attributes or awareness of the file system at large----- 26 27 BSimpleMailAttachment::BSimpleMailAttachment() 28 : BMailAttachment(), 29 fStatus(B_NO_INIT), 30 _data(NULL), 31 _raw_data(NULL), 32 _we_own_data(false) 33 { 34 Initialize(base64); 35 } 36 37 BSimpleMailAttachment::BSimpleMailAttachment(BPositionIO *data, mail_encoding encoding) 38 : BMailAttachment(), 39 _data(data), 40 _raw_data(NULL), 41 _we_own_data(false) 42 { 43 fStatus = data == NULL ? B_BAD_VALUE : B_OK; 44 45 Initialize(encoding); 46 } 47 48 BSimpleMailAttachment::BSimpleMailAttachment(const void *data, size_t length, mail_encoding encoding) 49 : BMailAttachment(), 50 _data(new BMemoryIO(data,length)), 51 _raw_data(NULL), 52 _we_own_data(true) 53 { 54 fStatus = data == NULL ? B_BAD_VALUE : B_OK; 55 56 Initialize(encoding); 57 } 58 59 BSimpleMailAttachment::BSimpleMailAttachment(BFile *file, bool delete_when_done) 60 : BMailAttachment(), 61 _data(NULL), 62 _raw_data(NULL), 63 _we_own_data(false) 64 { 65 Initialize(base64); 66 SetTo(file,delete_when_done); 67 } 68 69 BSimpleMailAttachment::BSimpleMailAttachment(entry_ref *ref) 70 : BMailAttachment(), 71 _data(NULL), 72 _raw_data(NULL), 73 _we_own_data(false) 74 { 75 Initialize(base64); 76 SetTo(ref); 77 } 78 79 BSimpleMailAttachment::~BSimpleMailAttachment() 80 { 81 if (_we_own_data) 82 delete _data; 83 } 84 85 void BSimpleMailAttachment::Initialize(mail_encoding encoding) 86 { 87 SetEncoding(encoding); 88 SetHeaderField("Content-Disposition","BMailAttachment"); 89 } 90 91 status_t BSimpleMailAttachment::SetTo(BFile *file, bool delete_file_when_done) 92 { 93 char type[B_MIME_TYPE_LENGTH] = "application/octet-stream"; 94 95 BNodeInfo nodeInfo(file); 96 if (nodeInfo.InitCheck() == B_OK) 97 nodeInfo.GetType(type); 98 99 SetHeaderField("Content-Type",type); 100 //---No way to get file name (see SetTo(entry_ref *)) 101 //SetFileName(ref->name); 102 103 if (delete_file_when_done) 104 SetDecodedDataAndDeleteWhenDone(file); 105 else 106 SetDecodedData(file); 107 108 return fStatus = B_OK; 109 } 110 111 status_t BSimpleMailAttachment::SetTo(entry_ref *ref) 112 { 113 BFile *file = new BFile(ref,B_READ_ONLY); 114 115 if ((fStatus = file->InitCheck()) < B_OK) 116 { 117 delete file; 118 return fStatus; 119 } 120 if (SetTo(file,true) < B_OK) 121 // fStatus is set by SetTo() 122 return fStatus; 123 124 SetFileName(ref->name); 125 return fStatus = B_OK; 126 } 127 128 status_t BSimpleMailAttachment::InitCheck() 129 { 130 return fStatus; 131 } 132 133 status_t BSimpleMailAttachment::FileName(char *text) { 134 BMessage content_type; 135 HeaderField("Content-Type",&content_type); 136 137 const char *fileName = content_type.FindString("name"); 138 if (!fileName) 139 fileName = content_type.FindString("filename"); 140 if (!fileName) 141 { 142 content_type.MakeEmpty(); 143 HeaderField("Content-Disposition",&content_type); 144 fileName = content_type.FindString("name"); 145 } 146 if (!fileName) 147 fileName = content_type.FindString("filename"); 148 if (!fileName) 149 { 150 content_type.MakeEmpty(); 151 HeaderField("Content-Location",&content_type); 152 fileName = content_type.FindString("unlabeled"); 153 } 154 if (!fileName) 155 return B_NAME_NOT_FOUND; 156 157 strncpy(text,fileName,B_FILE_NAME_LENGTH); 158 return B_OK; 159 } 160 161 162 void BSimpleMailAttachment::SetFileName(const char *name) { 163 BMessage content_type; 164 165 HeaderField("Content-Type",&content_type); 166 167 if (content_type.ReplaceString("name",name) != B_OK) 168 content_type.AddString("name",name); 169 170 // Request that the file name header be encoded in UTF-8 if it has weird 171 // characters. If it is just a plain name, the header will appear normal. 172 if (content_type.ReplaceInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION) != B_OK) 173 content_type.AddInt32(kHeaderCharsetString, B_MAIL_UTF8_CONVERSION); 174 175 SetHeaderField ("Content-Type", &content_type); 176 } 177 178 179 status_t 180 BSimpleMailAttachment::GetDecodedData(BPositionIO *data) 181 { 182 ParseNow(); 183 184 if (!_data) 185 return B_IO_ERROR; 186 if (data == NULL) 187 return B_BAD_VALUE; 188 189 char buffer[256]; 190 ssize_t length; 191 _data->Seek(0,SEEK_SET); 192 193 while ((length = _data->Read(buffer,sizeof(buffer))) > 0) 194 data->Write(buffer,length); 195 196 return B_OK; 197 } 198 199 200 BPositionIO * 201 BSimpleMailAttachment::GetDecodedData() 202 { 203 ParseNow(); 204 205 return _data; 206 } 207 208 status_t BSimpleMailAttachment::SetDecodedDataAndDeleteWhenDone(BPositionIO *data) { 209 _raw_data = NULL; 210 211 if (_we_own_data) 212 delete _data; 213 214 _data = data; 215 _we_own_data = true; 216 217 return B_OK; 218 } 219 220 status_t BSimpleMailAttachment::SetDecodedData(BPositionIO *data) { 221 _raw_data = NULL; 222 223 if (_we_own_data) 224 delete _data; 225 226 _data = data; 227 _we_own_data = false; 228 229 return B_OK; 230 } 231 232 status_t BSimpleMailAttachment::SetDecodedData(const void *data, size_t length) { 233 _raw_data = NULL; 234 235 if (_we_own_data) 236 delete _data; 237 238 _data = new BMemoryIO(data,length); 239 _we_own_data = true; 240 241 return B_OK; 242 } 243 244 void BSimpleMailAttachment::SetEncoding(mail_encoding encoding) { 245 _encoding = encoding; 246 247 char *cte = NULL; //--Content Transfer Encoding 248 switch (_encoding) { 249 case base64: 250 cte = "base64"; 251 break; 252 case seven_bit: 253 case no_encoding: 254 cte = "7bit"; 255 break; 256 case eight_bit: 257 cte = "8bit"; 258 break; 259 case uuencode: 260 cte = "uuencode"; 261 break; 262 case quoted_printable: 263 cte = "quoted-printable"; 264 break; 265 default: 266 cte = "bug-not-implemented"; 267 break; 268 } 269 270 SetHeaderField("Content-Transfer-Encoding",cte); 271 } 272 273 mail_encoding BSimpleMailAttachment::Encoding() { 274 return _encoding; 275 } 276 277 status_t BSimpleMailAttachment::SetToRFC822(BPositionIO *data, size_t length, bool parse_now) { 278 //---------Massive memory squandering!---ALERT!---------- 279 if (_we_own_data) 280 delete _data; 281 282 off_t position = data->Position(); 283 BMailComponent::SetToRFC822(data,length,parse_now); 284 285 // this actually happens... 286 if (data->Position() - position > length) 287 return B_ERROR; 288 289 length -= (data->Position() - position); 290 291 _raw_data = data; 292 _raw_length = length; 293 _raw_offset = data->Position(); 294 295 BString encoding = HeaderField("Content-Transfer-Encoding"); 296 if (encoding.IFindFirst("base64") >= 0) 297 _encoding = base64; 298 else if (encoding.IFindFirst("quoted-printable") >= 0) 299 _encoding = quoted_printable; 300 else if (encoding.IFindFirst("uuencode") >= 0) 301 _encoding = uuencode; 302 else if (encoding.IFindFirst("7bit") >= 0) 303 _encoding = seven_bit; 304 else if (encoding.IFindFirst("8bit") >= 0) 305 _encoding = eight_bit; 306 else 307 _encoding = no_encoding; 308 309 if (parse_now) 310 ParseNow(); 311 312 return B_OK; 313 } 314 315 void BSimpleMailAttachment::ParseNow() { 316 if (_raw_data == NULL || _raw_length == 0) 317 return; 318 319 _raw_data->Seek(_raw_offset,SEEK_SET); 320 321 char *src = (char *)malloc(_raw_length); 322 size_t size = _raw_length; 323 324 size = _raw_data->Read(src,_raw_length); 325 326 BMallocIO *buffer = new BMallocIO; 327 buffer->SetSize(size); //-------8bit is *always* more efficient than an encoding, so the buffer will *never* be larger than before 328 329 size = decode(_encoding,(char *)(buffer->Buffer()),src,size,0); 330 free(src); 331 332 buffer->SetSize(size); 333 334 _data = buffer; 335 _we_own_data = true; 336 337 _raw_data = NULL; 338 339 return; 340 } 341 342 status_t BSimpleMailAttachment::RenderToRFC822(BPositionIO *render_to) { 343 ParseNow(); 344 BMailComponent::RenderToRFC822(render_to); 345 //---------Massive memory squandering!---ALERT!---------- 346 // now with error checks, dumb :-) -- axeld. 347 348 _data->Seek(0,SEEK_END); 349 off_t size = _data->Position(); 350 char *src = (char *)malloc(size); 351 if (src == NULL) 352 return B_NO_MEMORY; 353 354 _data->Seek(0,SEEK_SET); 355 356 ssize_t read = _data->Read(src,size); 357 if (read < B_OK) 358 return read; // Return an error code and leak memory. 359 360 // The encoded text will never be more than twice as large with any 361 // conceivable encoding. But just in case, there's a function call which 362 // will tell us how much space is needed. 363 ssize_t destSize = max_encoded_length(_encoding,read); 364 if (destSize < B_OK) // Invalid encodings like uuencode rejected here. 365 return destSize; 366 char *dest = (char *)malloc(destSize); 367 if (dest == NULL) 368 return B_NO_MEMORY; 369 370 destSize = encode (_encoding, dest, src, read, false /* headerMode */); 371 if (destSize < B_OK) 372 return destSize; 373 if (destSize > 0) 374 read = render_to->Write(dest,destSize); 375 free (src); 376 free (dest); 377 return (read > 0) ? B_OK : read; 378 } 379 380 381 //-------BAttributedMailAttachment--Awareness of bfs, sends attributes-- 382 // #pragma mark - 383 384 385 BAttributedMailAttachment::BAttributedMailAttachment() 386 : BMailAttachment(), 387 fContainer(NULL), 388 fStatus(B_NO_INIT), 389 _data(NULL), 390 _attributes_attach(NULL) 391 { 392 } 393 394 BAttributedMailAttachment::BAttributedMailAttachment(BFile *file, bool delete_when_done) 395 : BMailAttachment(), 396 fContainer(NULL), 397 _data(NULL), 398 _attributes_attach(NULL) 399 { 400 SetTo(file,delete_when_done); 401 } 402 403 BAttributedMailAttachment::BAttributedMailAttachment(entry_ref *ref) 404 : BMailAttachment(), 405 fContainer(NULL), 406 _data(NULL), 407 _attributes_attach(NULL) 408 { 409 SetTo(ref); 410 } 411 412 BAttributedMailAttachment::~BAttributedMailAttachment() { 413 // Our SimpleAttachments are deleted by fContainer 414 delete fContainer; 415 } 416 417 418 status_t BAttributedMailAttachment::Initialize() 419 { 420 // _data & _attributes_attach will be deleted by the container 421 if (fContainer != NULL) 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 return B_NO_MEMORY; 643 memcpy(allocd,data,dataLen); 644 swap_data(type, allocd, dataLen, B_SWAP_HOST_TO_BENDIAN); 645 io->Write(allocd,dataLen); 646 free(allocd); 647 } 648 if (_attributes_attach == NULL) 649 _attributes_attach = new BSimpleMailAttachment; 650 651 _attributes_attach->SetDecodedDataAndDeleteWhenDone(io); 652 653 return fContainer->RenderToRFC822(render_to); 654 } 655 656 status_t BAttributedMailAttachment::MIMEType(BMimeType *mime) { 657 return _data->MIMEType(mime); 658 } 659 660 661 // The reserved function stubs 662 // #pragma mark - 663 664 void BMailAttachment::_ReservedAttachment1() {} 665 void BMailAttachment::_ReservedAttachment2() {} 666 void BMailAttachment::_ReservedAttachment3() {} 667 void BMailAttachment::_ReservedAttachment4() {} 668 669 void BSimpleMailAttachment::_ReservedSimple1() {} 670 void BSimpleMailAttachment::_ReservedSimple2() {} 671 void BSimpleMailAttachment::_ReservedSimple3() {} 672 673 void BAttributedMailAttachment::_ReservedAttributed1() {} 674 void BAttributedMailAttachment::_ReservedAttributed2() {} 675 void BAttributedMailAttachment::_ReservedAttributed3() {} 676