1 /*
2 * Copyright 2012-2016, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "IMAPFolder.h"
8
9 #include <set>
10
11 #include <ByteOrder.h>
12 #include <Debug.h>
13 #include <Directory.h>
14 #include <File.h>
15 #include <fs_attr.h>
16 #include <Messenger.h>
17 #include <Node.h>
18 #include <Path.h>
19
20 #include <NodeMessage.h>
21
22 #include "IMAPProtocol.h"
23
24
25 static const char* kMailboxNameAttribute = "IMAP:mailbox";
26 static const char* kUIDValidityAttribute = "IMAP:uidvalidity";
27 static const char* kLastUIDAttribute = "IMAP:lastuid";
28 static const char* kStateAttribute = "IMAP:state";
29 static const char* kFlagsAttribute = "IMAP:flags";
30 static const char* kUIDAttribute = "MAIL:unique_id";
31
32
33 class TemporaryFile : public BFile {
34 public:
TemporaryFile(BFile & file)35 TemporaryFile(BFile& file)
36 :
37 fFile(file),
38 fDeleteFile(false)
39 {
40 }
41
~TemporaryFile()42 ~TemporaryFile()
43 {
44 if (fDeleteFile) {
45 fFile.Unset();
46 BEntry(fPath.Path()).Remove();
47 }
48 }
49
Init(const BPath & path,entry_ref & ref)50 status_t Init(const BPath& path, entry_ref& ref)
51 {
52 int32 tries = 53;
53 while (true) {
54 BString name("temp-mail-");
55 name << system_time();
56
57 fPath = path;
58 fPath.Append(name.String());
59
60 status_t status = fFile.SetTo(fPath.Path(),
61 B_CREATE_FILE | B_FAIL_IF_EXISTS | B_READ_WRITE);
62 if (status == B_FILE_EXISTS && tries-- > 0)
63 continue;
64 if (status != B_OK)
65 return status;
66
67 fDeleteFile = true;
68 return get_ref_for_path(fPath.Path(), &ref);
69 }
70 }
71
KeepFile()72 void KeepFile()
73 {
74 fDeleteFile = false;
75 }
76
77 private:
78 BFile& fFile;
79 BPath fPath;
80 bool fDeleteFile;
81 };
82
83
84 // #pragma mark -
85
86
IMAPFolder(IMAPProtocol & protocol,const BString & mailboxName,const entry_ref & ref)87 IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
88 const entry_ref& ref)
89 :
90 BHandler(mailboxName.String()),
91 fProtocol(protocol),
92 fRef(ref),
93 fMailboxName(mailboxName),
94 fUIDValidity(UINT32_MAX),
95 fLastUID(0),
96 fListener(NULL),
97 fFolderStateInitialized(false),
98 fQuitFolderState(false)
99 {
100 mutex_init(&fLock, "imap folder lock");
101 mutex_init(&fFolderStateLock, "imap folder state lock");
102 }
103
104
~IMAPFolder()105 IMAPFolder::~IMAPFolder()
106 {
107 MutexLocker locker(fLock);
108 if (!fFolderStateInitialized && fListener != NULL) {
109 fQuitFolderState = true;
110 locker.Unlock();
111 wait_for_thread(fReadFolderStateThread, NULL);
112 }
113 }
114
115
116 status_t
Init()117 IMAPFolder::Init()
118 {
119 // Initialize from folder attributes
120 BNode node(&fRef);
121 status_t status = node.InitCheck();
122 if (status != B_OK)
123 return status;
124
125 node_ref nodeRef;
126 status = node.GetNodeRef(&nodeRef);
127 if (status != B_OK)
128 return status;
129
130 fNodeID = nodeRef.node;
131
132 BString originalMailboxName;
133 if (node.ReadAttrString(kMailboxNameAttribute, &originalMailboxName) == B_OK
134 && originalMailboxName != fMailboxName) {
135 // TODO: mailbox name has changed
136 }
137
138 fUIDValidity = _ReadUInt32(node, kUIDValidityAttribute);
139 fLastUID = _ReadUInt32(node, kLastUIDAttribute);
140 printf("IMAP: %s, last UID %" B_PRIu32 "\n", fMailboxName.String(),
141 fLastUID);
142
143 attr_info info;
144 status = node.GetAttrInfo(kStateAttribute, &info);
145 if (status == B_OK) {
146 struct entry {
147 uint32 uid;
148 uint32 flags;
149 } _PACKED;
150 struct entry* entries = (struct entry*)malloc(info.size);
151 if (entries == NULL)
152 return B_NO_MEMORY;
153
154 ssize_t bytesRead = node.ReadAttr(kStateAttribute, B_RAW_TYPE, 0,
155 entries, info.size);
156 if (bytesRead != info.size)
157 return B_BAD_DATA;
158
159 for (size_t i = 0; i < info.size / sizeof(entry); i++) {
160 uint32 uid = B_BENDIAN_TO_HOST_INT32(entries[i].uid);
161 uint32 flags = B_BENDIAN_TO_HOST_INT32(entries[i].flags);
162
163 fFlagsMap.insert(std::make_pair(uid, flags));
164 }
165 }
166
167 return B_OK;
168 }
169
170
171 void
SetListener(FolderListener * listener)172 IMAPFolder::SetListener(FolderListener* listener)
173 {
174 ASSERT(fListener == NULL);
175
176 fListener = listener;
177
178 // Initialize current state from actual folder
179 // TODO: this should be done in another thread
180 _InitializeFolderState();
181 }
182
183
184 void
SetUIDValidity(uint32 uidValidity)185 IMAPFolder::SetUIDValidity(uint32 uidValidity)
186 {
187 if (fUIDValidity == uidValidity)
188 return;
189
190 // TODO: delete all mails that have the same UID validity value we had
191 fUIDValidity = uidValidity;
192
193 BNode node(&fRef);
194 _WriteUInt32(node, kUIDValidityAttribute, uidValidity);
195 }
196
197
198 status_t
GetMessageEntryRef(uint32 uid,entry_ref & ref)199 IMAPFolder::GetMessageEntryRef(uint32 uid, entry_ref& ref)
200 {
201 MutexLocker locker(fLock);
202 return _GetMessageEntryRef(uid, ref);
203 }
204
205
206 status_t
GetMessageUID(const entry_ref & ref,uint32 & uid) const207 IMAPFolder::GetMessageUID(const entry_ref& ref, uint32& uid) const
208 {
209 BNode node(&ref);
210 status_t status = node.InitCheck();
211 if (status != B_OK)
212 return status;
213
214 uid = _ReadUniqueID(node);
215 if (uid == 0)
216 return B_ENTRY_NOT_FOUND;
217
218 return B_OK;
219 }
220
221
222 uint32
MessageFlags(uint32 uid)223 IMAPFolder::MessageFlags(uint32 uid)
224 {
225 MutexLocker locker(fLock);
226 UIDToFlagsMap::const_iterator found = fFlagsMap.find(uid);
227 if (found == fFlagsMap.end())
228 return 0;
229
230 return found->second;
231 }
232
233
234 /*! Synchronizes the message flags/state from the server with the local
235 one.
236 */
237 void
SyncMessageFlags(uint32 uid,uint32 mailboxFlags)238 IMAPFolder::SyncMessageFlags(uint32 uid, uint32 mailboxFlags)
239 {
240 if (uid > LastUID())
241 return;
242
243 entry_ref ref;
244 BNode node;
245
246 while (true) {
247 status_t status = GetMessageEntryRef(uid, ref);
248 if (status == B_ENTRY_NOT_FOUND) {
249 // The message does not exist anymore locally, delete it on the
250 // server
251 // TODO: copy it to the trash directory first!
252 if (fProtocol.Settings()->DeleteRemoteWhenLocal())
253 fProtocol.UpdateMessageFlags(*this, uid, IMAP::kDeleted);
254 return;
255 }
256 if (status == B_OK)
257 status = node.SetTo(&ref);
258 if (status == B_TIMED_OUT) {
259 // We don't know the message state yet
260 fPendingFlagsMap.insert(std::make_pair(uid, mailboxFlags));
261 }
262 if (status != B_OK)
263 return;
264
265 break;
266 }
267 fSynchronizedUIDsSet.insert(uid);
268
269 uint32 previousFlags = MessageFlags(uid);
270 uint32 currentFlags = previousFlags;
271 if (_MailToIMAPFlags(node, currentFlags) != B_OK)
272 return;
273
274 // Compare flags to previous/current flags, and update either the
275 // message on the server, or the message locally (or even both)
276
277 uint32 nextFlags = mailboxFlags;
278 _TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
279 IMAP::kSeen, nextFlags);
280 _TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
281 IMAP::kAnswered, nextFlags);
282
283 if (nextFlags != previousFlags)
284 _WriteFlags(node, nextFlags);
285 if (currentFlags != nextFlags) {
286 // Update mail message attributes
287 BMessage attributes;
288 _IMAPToMailFlags(nextFlags, attributes);
289 node << attributes;
290
291 fFlagsMap[uid] = nextFlags;
292 }
293 if (mailboxFlags != nextFlags) {
294 // Update server flags
295 fProtocol.UpdateMessageFlags(*this, uid, nextFlags);
296 }
297 }
298
299
300 void
MessageEntriesFetched()301 IMAPFolder::MessageEntriesFetched()
302 {
303 _WaitForFolderState();
304
305 // Synchronize all pending flags first
306 UIDToFlagsMap::const_iterator pendingIterator = fPendingFlagsMap.begin();
307 for (; pendingIterator != fPendingFlagsMap.end(); pendingIterator++)
308 SyncMessageFlags(pendingIterator->first, pendingIterator->second);
309
310 fPendingFlagsMap.clear();
311
312 // Delete all local messages that are no longer found on the server
313
314 MutexLocker locker(fLock);
315 UIDSet deleteUIDs;
316 UIDToRefMap::const_iterator iterator = fRefMap.begin();
317 for (; iterator != fRefMap.end(); iterator++) {
318 uint32 uid = iterator->first;
319 if (fSynchronizedUIDsSet.find(uid) == fSynchronizedUIDsSet.end())
320 deleteUIDs.insert(uid);
321 }
322
323 fSynchronizedUIDsSet.clear();
324 locker.Unlock();
325
326 UIDSet::const_iterator deleteIterator = deleteUIDs.begin();
327 for (; deleteIterator != deleteUIDs.end(); deleteIterator++)
328 _DeleteLocalMessage(*deleteIterator);
329 }
330
331
332 /*! Stores the given \a stream into a temporary file using the provided
333 BFile object. A new file will be created, and the \a ref object will
334 point to it. The file will remain open when this method exits without
335 an error.
336
337 \a length will reflect how many bytes are left to read in case there
338 was an error.
339 */
340 status_t
StoreMessage(uint32 fetchFlags,BDataIO & stream,size_t & length,entry_ref & ref,BFile & file)341 IMAPFolder::StoreMessage(uint32 fetchFlags, BDataIO& stream,
342 size_t& length, entry_ref& ref, BFile& file)
343 {
344 BPath path;
345 status_t status = path.SetTo(&fRef);
346 if (status != B_OK)
347 return status;
348
349 TemporaryFile temporaryFile(file);
350 status = temporaryFile.Init(path, ref);
351 if (status != B_OK)
352 return status;
353
354 status = _WriteStream(file, stream, length);
355 if (status == B_OK)
356 temporaryFile.KeepFile();
357
358 return status;
359 }
360
361
362 /*! Writes UID, and flags to the message, and notifies the protocol that a
363 message has been fetched. This method also closes the \a file passed in.
364 */
365 void
MessageStored(entry_ref & ref,BFile & file,uint32 fetchFlags,uint32 uid,uint32 flags)366 IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
367 uint32 uid, uint32 flags)
368 {
369 _WriteUniqueIDValidity(file);
370 _WriteUniqueID(file, uid);
371 if ((fetchFlags & IMAP::kFetchFlags) != 0)
372 _WriteFlags(file, flags);
373
374 BMessage attributes;
375 _IMAPToMailFlags(flags, attributes);
376
377 fProtocol.MessageStored(*this, ref, file, fetchFlags, attributes);
378 file.Unset();
379
380 fRefMap.insert(std::make_pair(uid, ref));
381
382 if (uid > fLastUID) {
383 // Update last known UID
384 fLastUID = uid;
385
386 BNode directory(&fRef);
387 status_t status = _WriteUInt32(directory, kLastUIDAttribute, uid);
388 if (status != B_OK) {
389 fprintf(stderr, "IMAP: Could not write last UID for mailbox "
390 "%s: %s\n", fMailboxName.String(), strerror(status));
391 }
392 }
393 }
394
395
396 /*! Pushes the refs for the pending bodies to the pending bodies list.
397 This allows to prevent retrieving bodies more than once.
398 */
399 void
RegisterPendingBodies(IMAP::MessageUIDList & uids,const BMessenger * replyTo)400 IMAPFolder::RegisterPendingBodies(IMAP::MessageUIDList& uids,
401 const BMessenger* replyTo)
402 {
403 MutexLocker locker(fLock);
404
405 MessengerList messengers;
406 if (replyTo != NULL)
407 messengers.push_back(*replyTo);
408
409 IMAP::MessageUIDList::const_iterator iterator = uids.begin();
410 for (; iterator != uids.end(); iterator++) {
411 if (replyTo != NULL) {
412 fPendingBodies[*iterator].push_back(*replyTo);
413 } else {
414 // Note: GCC 13 or later warns about the unused result of the statement below.
415 // This code should be reviewed as part of #18478.
416 #if __GNUC__ >= 13
417 # pragma GCC diagnostic push
418 # pragma GCC diagnostic warning "-Wunused-result"
419 #endif
420 fPendingBodies[*iterator].begin();
421 #if __GNUC__ >= 13
422 # pragma GCC diagnostic pop
423 #endif
424 }
425 }
426 }
427
428
429 /*! Appends the given \a stream as body to the message file for the
430 specified unique ID. The file will remain open when this method exits
431 without an error.
432
433 \a length will reflect how many bytes are left to read in case there
434 were an error.
435 */
436 status_t
StoreBody(uint32 uid,BDataIO & stream,size_t & length,entry_ref & ref,BFile & file)437 IMAPFolder::StoreBody(uint32 uid, BDataIO& stream, size_t& length,
438 entry_ref& ref, BFile& file)
439 {
440 status_t status = GetMessageEntryRef(uid, ref);
441 if (status != B_OK)
442 return status;
443
444 status = file.SetTo(&ref, B_OPEN_AT_END | B_WRITE_ONLY);
445 if (status != B_OK)
446 return status;
447
448 BPath path(&ref);
449 printf("IMAP: write body to %s\n", path.Path());
450
451 return _WriteStream(file, stream, length);
452 }
453
454
455 /*! Notifies the protocol that a body has been fetched.
456 This method also closes the \a file passed in.
457 */
458 void
BodyStored(entry_ref & ref,BFile & file,uint32 uid)459 IMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid)
460 {
461 BMessage attributes;
462 fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody, attributes);
463 file.Unset();
464
465 _NotifyStoredBody(ref, uid, B_OK);
466 }
467
468
469 void
StoringBodyFailed(const entry_ref & ref,uint32 uid,status_t error)470 IMAPFolder::StoringBodyFailed(const entry_ref& ref, uint32 uid, status_t error)
471 {
472 _NotifyStoredBody(ref, uid, error);
473 }
474
475
476 void
DeleteMessage(uint32 uid)477 IMAPFolder::DeleteMessage(uint32 uid)
478 {
479 // TODO: move message to trash (server side)
480
481 _DeleteLocalMessage(uid);
482 }
483
484
485 void
MessageReceived(BMessage * message)486 IMAPFolder::MessageReceived(BMessage* message)
487 {
488 switch (message->what) {
489 default:
490 BHandler::MessageReceived(message);
491 break;
492 }
493 }
494
495
496 void
_WaitForFolderState()497 IMAPFolder::_WaitForFolderState()
498 {
499 while (true) {
500 MutexLocker locker(fFolderStateLock);
501 if (fFolderStateInitialized)
502 return;
503 }
504 }
505
506
507 void
_InitializeFolderState()508 IMAPFolder::_InitializeFolderState()
509 {
510 mutex_lock(&fFolderStateLock);
511
512 fReadFolderStateThread = spawn_thread(&IMAPFolder::_ReadFolderState,
513 "IMAP folder state", B_NORMAL_PRIORITY, this);
514 if (fReadFolderStateThread >= 0)
515 resume_thread(fReadFolderStateThread);
516 else
517 mutex_unlock(&fFolderStateLock);
518 }
519
520
521 void
_ReadFolderState()522 IMAPFolder::_ReadFolderState()
523 {
524 BDirectory directory(&fRef);
525 BEntry entry;
526 while (directory.GetNextEntry(&entry) == B_OK) {
527 entry_ref ref;
528 BNode node;
529 if (!entry.IsFile() || entry.GetRef(&ref) != B_OK
530 || node.SetTo(&entry) != B_OK)
531 continue;
532
533 uint32 uidValidity = _ReadUniqueIDValidity(node);
534 if (uidValidity != fUIDValidity) {
535 // TODO: add file to mailbox
536 continue;
537 }
538 uint32 uid = _ReadUniqueID(node);
539 uint32 flags = _ReadFlags(node);
540
541 MutexLocker locker(fLock);
542 if (fQuitFolderState)
543 return;
544
545 fRefMap.insert(std::make_pair(uid, ref));
546 fFlagsMap.insert(std::make_pair(uid, flags));
547
548 // // TODO: make sure a listener exists at this point!
549 // std::set<uint32>::iterator found = lastUIDs.find(uid);
550 // if (found != lastUIDs.end()) {
551 // // The message is still around
552 // lastUIDs.erase(found);
553 //
554 // uint32 flagsFound = MessageFlags(uid);
555 // if (flagsFound != flags) {
556 // // Its flags have changed locally, and need to be updated
557 // fListener->MessageFlagsChanged(_Token(uid), ref,
558 // flagsFound, flags);
559 // }
560 // } else {
561 // // This is a new message
562 // // TODO: the token must be the originating token!
563 // uid = fListener->MessageAdded(_Token(uid), ref);
564 // _WriteUniqueID(node, uid);
565 // }
566 //
567 }
568
569 fFolderStateInitialized = true;
570 mutex_unlock(&fFolderStateLock);
571 }
572
573
574 /*static*/ status_t
_ReadFolderState(void * self)575 IMAPFolder::_ReadFolderState(void* self)
576 {
577 ((IMAPFolder*)self)->_ReadFolderState();
578 return B_OK;
579 }
580
581
582 const MessageToken
_Token(uint32 uid) const583 IMAPFolder::_Token(uint32 uid) const
584 {
585 MessageToken token;
586 token.mailboxName = fMailboxName;
587 token.uidValidity = fUIDValidity;
588 token.uid = uid;
589
590 return token;
591 }
592
593
594 void
_NotifyStoredBody(const entry_ref & ref,uint32 uid,status_t status)595 IMAPFolder::_NotifyStoredBody(const entry_ref& ref, uint32 uid, status_t status)
596 {
597 MutexLocker locker(fLock);
598 MessengerMap::iterator found = fPendingBodies.find(uid);
599 if (found != fPendingBodies.end()) {
600 MessengerList messengers = found->second;
601 fPendingBodies.erase(found);
602 locker.Unlock();
603
604 MessengerList::iterator iterator = messengers.begin();
605 for (; iterator != messengers.end(); iterator++)
606 BInboundMailProtocol::ReplyBodyFetched(*iterator, ref, status);
607 }
608 }
609
610
611 status_t
_GetMessageEntryRef(uint32 uid,entry_ref & ref) const612 IMAPFolder::_GetMessageEntryRef(uint32 uid, entry_ref& ref) const
613 {
614 UIDToRefMap::const_iterator found = fRefMap.find(uid);
615 if (found == fRefMap.end())
616 return !fFolderStateInitialized ? B_TIMED_OUT : B_ENTRY_NOT_FOUND;
617
618 ref = found->second;
619 return B_OK;
620 }
621
622
623 status_t
_DeleteLocalMessage(uint32 uid)624 IMAPFolder::_DeleteLocalMessage(uint32 uid)
625 {
626 entry_ref ref;
627 status_t status = GetMessageEntryRef(uid, ref);
628 if (status != B_OK)
629 return status;
630
631 fRefMap.erase(uid);
632 fFlagsMap.erase(uid);
633
634 BEntry entry(&ref);
635 return entry.Remove();
636 }
637
638
639 void
_IMAPToMailFlags(uint32 flags,BMessage & attributes)640 IMAPFolder::_IMAPToMailFlags(uint32 flags, BMessage& attributes)
641 {
642 // TODO: add some utility function for this in libmail.so
643 if ((flags & IMAP::kAnswered) != 0)
644 attributes.AddString(B_MAIL_ATTR_STATUS, "Answered");
645 else if ((flags & IMAP::kFlagged) != 0)
646 attributes.AddString(B_MAIL_ATTR_STATUS, "Starred");
647 else if ((flags & IMAP::kSeen) != 0)
648 attributes.AddString(B_MAIL_ATTR_STATUS, "Read");
649 }
650
651
652 status_t
_MailToIMAPFlags(BNode & node,uint32 & flags)653 IMAPFolder::_MailToIMAPFlags(BNode& node, uint32& flags)
654 {
655 BString mailStatus;
656 status_t status = node.ReadAttrString(B_MAIL_ATTR_STATUS, &mailStatus);
657 if (status != B_OK)
658 return status;
659
660 flags &= ~(IMAP::kAnswered | IMAP::kSeen);
661
662 // TODO: add some utility function for this in libmail.so
663 if (mailStatus == "Answered")
664 flags |= IMAP::kAnswered | IMAP::kSeen;
665 else if (mailStatus == "Read")
666 flags |= IMAP::kSeen;
667 else if (mailStatus == "Starred")
668 flags |= IMAP::kFlagged | IMAP::kSeen;
669
670 return B_OK;
671 }
672
673
674 void
_TestMessageFlags(uint32 previousFlags,uint32 mailboxFlags,uint32 currentFlags,uint32 testFlag,uint32 & nextFlags)675 IMAPFolder::_TestMessageFlags(uint32 previousFlags, uint32 mailboxFlags,
676 uint32 currentFlags, uint32 testFlag, uint32& nextFlags)
677 {
678 if ((previousFlags & testFlag) != (mailboxFlags & testFlag)) {
679 if ((previousFlags & testFlag) == (currentFlags & testFlag)) {
680 // The flags on the mailbox changed, update local flags
681 nextFlags &= ~testFlag;
682 nextFlags |= mailboxFlags & testFlag;
683 } else {
684 // Both flags changed. Since we don't have the means to do
685 // conflict resolution, we use a best effort mechanism
686 nextFlags |= testFlag;
687 }
688 return;
689 }
690
691 // Previous message flags, and mailbox flags are identical, see
692 // if the user changed the flag locally
693 if ((currentFlags & testFlag) != (previousFlags & testFlag)) {
694 // Flag changed, update mailbox
695 nextFlags &= ~testFlag;
696 nextFlags |= currentFlags & testFlag;
697 }
698 }
699
700
701 uint32
_ReadUniqueID(BNode & node) const702 IMAPFolder::_ReadUniqueID(BNode& node) const
703 {
704 // For compatibility we must assume that the UID is stored as a string
705 BString string;
706 if (node.ReadAttrString(kUIDAttribute, &string) != B_OK)
707 return 0;
708
709 return strtoul(string.String(), NULL, 0);
710 }
711
712
713 status_t
_WriteUniqueID(BNode & node,uint32 uid) const714 IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid) const
715 {
716 // For compatibility we must assume that the UID is stored as a string
717 BString string;
718 string << uid;
719
720 return node.WriteAttrString(kUIDAttribute, &string);
721 }
722
723
724 uint32
_ReadUniqueIDValidity(BNode & node) const725 IMAPFolder::_ReadUniqueIDValidity(BNode& node) const
726 {
727
728 return _ReadUInt32(node, kUIDValidityAttribute);
729 }
730
731
732 status_t
_WriteUniqueIDValidity(BNode & node) const733 IMAPFolder::_WriteUniqueIDValidity(BNode& node) const
734 {
735 return _WriteUInt32(node, kUIDValidityAttribute, fUIDValidity);
736 }
737
738
739 uint32
_ReadFlags(BNode & node) const740 IMAPFolder::_ReadFlags(BNode& node) const
741 {
742 return _ReadUInt32(node, kFlagsAttribute);
743 }
744
745
746 status_t
_WriteFlags(BNode & node,uint32 flags) const747 IMAPFolder::_WriteFlags(BNode& node, uint32 flags) const
748 {
749 return _WriteUInt32(node, kFlagsAttribute, flags);
750 }
751
752
753 uint32
_ReadUInt32(BNode & node,const char * attribute) const754 IMAPFolder::_ReadUInt32(BNode& node, const char* attribute) const
755 {
756 uint32 value;
757 ssize_t bytesRead = node.ReadAttr(attribute, B_UINT32_TYPE, 0,
758 &value, sizeof(uint32));
759 if (bytesRead == (ssize_t)sizeof(uint32))
760 return value;
761
762 return 0;
763 }
764
765
766 status_t
_WriteUInt32(BNode & node,const char * attribute,uint32 value) const767 IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value) const
768 {
769 ssize_t bytesWritten = node.WriteAttr(attribute, B_UINT32_TYPE, 0,
770 &value, sizeof(uint32));
771 if (bytesWritten == (ssize_t)sizeof(uint32))
772 return B_OK;
773
774 return bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
775 }
776
777
778 status_t
_WriteStream(BFile & file,BDataIO & stream,size_t & length) const779 IMAPFolder::_WriteStream(BFile& file, BDataIO& stream, size_t& length) const
780 {
781 char buffer[65535];
782 while (length > 0) {
783 ssize_t bytesRead = stream.Read(buffer,
784 std::min(sizeof(buffer), length));
785 if (bytesRead < 0)
786 return bytesRead;
787 if (bytesRead <= 0)
788 break;
789
790 length -= bytesRead;
791
792 ssize_t bytesWritten = file.Write(buffer, bytesRead);
793 if (bytesWritten < 0)
794 return bytesWritten;
795 if (bytesWritten != bytesRead)
796 return B_IO_ERROR;
797 }
798
799 return B_OK;
800 }
801