You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

342 lines
9.5 KiB

//
// CWIMAPStore+Protected.m
// Pantomime
//
// Created by Andreas Buff on 05.09.17.
// Copyright © 2017 pEp Security S.A. All rights reserved.
//
#import "CWIMAPStore+Protected.h"
#import "CWIMAPFolder.h"
#import "Pantomime/NSString+Extensions.h"
#import "CWThreadSafeArray.h"
#import <pEpIOSToolboxForExtensions/PEPLogger.h>
@implementation CWIMAPStore (Protected)
//
//
//
- (CWIMAPQueueObject * _Nullable)currentQueueObject
{
@synchronized (self) {
return _currentQueueObject;
}
}
//
//
//
- (void)setCurrentQueueObject:(CWIMAPQueueObject * _Nullable)currentQueueObject
{
@synchronized (self) {
if (_currentQueueObject != currentQueueObject) {
_currentQueueObject = currentQueueObject;
}
}
}
//
//
//
- (NSData *) nextTag
{
_tag++;
return [self lastTag];
}
//
//
//
- (NSData *) lastTag
{
char str[5];
sprintf(str, "%04x", _tag);
return [NSData dataWithBytes: str length: 4];
}
//
//
//
- (void) subscribeToFolderWithName: (NSString *) theName
{
[self sendCommand: IMAP_SUBSCRIBE
info: [NSDictionary dictionaryWithObject: theName forKey: @"Name"]
arguments: @"SUBSCRIBE \"%@\"", [theName modifiedUTF7String]];
}
//
//
//
- (void) unsubscribeToFolderWithName: (NSString *) theName
{
[self sendCommand: IMAP_UNSUBSCRIBE
info: [NSDictionary dictionaryWithObject: theName forKey: @"Name"]
arguments: @"UNSUBSCRIBE \"%@\"", [theName modifiedUTF7String]];
}
//
//
//
- (NSDictionary *) folderStatus: (NSArray *) theArray
{
int i;
[_folderStatus removeAllObjects];
// C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
// S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
// S: A042 OK STATUS completed
//
// We send: MESSAGES UNSEEN
for (i = 0; i < [theArray count]; i++)
{
// RFC3501 says we SHOULD NOT call STATUS on the selected mailbox - so we won't do it.
if (_selectedFolder && [[_selectedFolder name] isEqualToString: [theArray objectAtIndex: i]])
{
continue;
}
[self sendCommand: IMAP_STATUS
info: [NSDictionary dictionaryWithObject: [theArray objectAtIndex: i] forKey: @"Name"]
arguments: @"STATUS \"%@\" (MESSAGES UNSEEN)", [[theArray objectAtIndex: i] modifiedUTF7String]];
}
return _folderStatus;
}
//
//
//
- (void) sendCommand: (IMAPCommand) theCommand info: (NSDictionary *) theInfo arguments: (NSString *) theFormat, ...
{
va_list args;
va_start(args, theFormat);
NSString *aString = [[NSString alloc] initWithFormat: theFormat arguments: args];
[self sendCommandInternal:theCommand info:theInfo string:aString];
}
//
//
//
- (void) sendCommandInternal: (IMAPCommand) theCommand info: (NSDictionary * _Nullable) theInfo
string:(NSString * _Nonnull)theString
{
// If two calls to this method happen concurrently, one might empty the queue the other is
// currenly using (causes |SENDING null|).
@synchronized(self) {
if (theCommand == IMAP_EMPTY_QUEUE)
{
if ([_queue count])
{
// We dequeue the first inserted command from the queue.
self.currentQueueObject = [_queue lastObject];
}
else
{
// The queue is empty, we have nothing more to do...
LogInfo(@"sendCommand currentQueueObject = nil");
self.currentQueueObject = nil;
return;
}
}
else
{
//
// We must check in the queue if we aren't trying to add a command that is already there.
// This could happend if -rawSource is called in IMAPMessage multiple times before
// PantomimeMessageFetchCompleted is sent.
//
// We skip this verification for the IMAP_APPEND command as a messages with the same size
// could be quickly appended to the folder and we do NOT want to skip the second one.
//
for (CWIMAPQueueObject *aQueueObject in _queue) {
if (aQueueObject.command == theCommand && theCommand != IMAP_APPEND &&
[aQueueObject.arguments isEqualToString: theString])
{
//LogInfo(@"A COMMAND ALREADY EXIST!!!!");
return;
}
}
CWIMAPQueueObject *aQueueObject = [[CWIMAPQueueObject alloc]
initWithCommand: theCommand arguments: theString
tag: [self nextTag] info: theInfo];
[_queue insertObject: aQueueObject atIndex: 0];
RELEASE(aQueueObject);
LogInfo(@"%p queue size = %lul", self, (unsigned long) [_queue count]);
// If we had queued commands, we return since we'll eventually
// dequeue them one by one. Otherwise, we run it immediately.
if ([_queue count] > 1)
{
//LogInfo(@"%p QUEUED |%@|", self, theString);
return;
}
self.currentQueueObject = aQueueObject;
}
BOOL isPrivate = NO;
if (self.currentQueueObject.command == IMAP_LOGIN) {
isPrivate = YES;
}
if (isPrivate) {
LogInfo(@"%p Sending private data |*******|", self);
} else {
LogInfo(@"%p Sending |%@|", self, self.currentQueueObject.arguments);
}
_lastCommand = self.currentQueueObject.command;
[self bulkWriteData:@[self.currentQueueObject.tag,
[NSData dataWithBytes: " " length: 1],
[self.currentQueueObject.arguments dataUsingEncoding: _defaultCStringEncoding],
_crlf]];
PERFORM_SELECTOR_2(_delegate, @selector(commandSent:), @"PantomimeCommandSent", [NSNumber numberWithInt: _lastCommand], @"Command");
}
}
- (CWIMAPFolder *)folderForNameInternal:(NSString *)name
mode:(PantomimeFolderMode)mode
updateExistsCount:(BOOL)updateExistsCount
{
CWIMAPFolder *folder = [_openFolders objectForKey:name];
LogInfo(@"select folder %@", name);
if (folder) {
if ([_selectedFolder.name isEqualToString:name]) {
// We have the folder already and it is already selected.
if (!updateExistsCount) {
// Return it in case exists count is not of interest ...
return folder;
}
// ... otherwize update exists count by calling SELECT/EXAMINE.
}
} else {
folder = [self folderWithName:name];
[_openFolders setObject:folder forKey:name];
}
[folder setStore:self];
folder.mode = mode;
//LogInfo(@"_connection_state.opening_mailbox = %d", _connection_state.opening_mailbox);
// If we are already opening a mailbox, we must interrupt the process
// and open the preferred one instead.
if (_connection_state.opening_mailbox) {
// Safety measure - in case close (so -removeFolderFromOpenFolders)
// on the selected folder wasn't called.
if (_selectedFolder) {
[_openFolders removeObjectForKey:[_selectedFolder name]];
}
[super cancelRequest];
[self reconnect];
_selectedFolder = folder;
return _selectedFolder;
}
_connection_state.opening_mailbox = YES;
if (mode == PantomimeReadOnlyMode) {
[self sendCommand:IMAP_EXAMINE info:nil arguments:@"EXAMINE \"%@\"", [name modifiedUTF7String]];
} else {
[self sendCommand:IMAP_SELECT info:nil arguments:@"SELECT \"%@\"", [name modifiedUTF7String]];
}
// This folder becomes the selected one. This will have to be improved in the future.
_selectedFolder = folder;
return _selectedFolder;
}
//
//
//
- (void)signalFolderSyncError
{
PERFORM_SELECTOR_2(_delegate, @selector(folderSyncFailed:),
PantomimeFolderSyncFailed, _selectedFolder, @"Folder");
}
//
//
//
- (void)signalFolderFetchCompleted
{
//LogInfo(@"DONE PREFETCHING FOLDER");
NSMutableDictionary *info = [NSMutableDictionary new];
if (_selectedFolder) {
info[@"Folder"] = _selectedFolder;
}
PERFORM_SELECTOR_3(_delegate,
@selector(folderFetchCompleted:),
PantomimeFolderFetchCompleted,
info);
}
@end
@implementation CWIMAPQueueObject
//
//
//
- (id) initWithCommand: (IMAPCommand) theCommand
arguments: (NSString *) theArguments
tag: (NSData *) theTag
info: (NSDictionary *) theInfo
{
self = [super init];
//LogInfo(@"CWIMAPQueueObject.init %@\n", self);
_command = theCommand;
_literal = 0;
ASSIGN(_arguments, theArguments);
ASSIGN(_tag, theTag);
if (theInfo)
{
_info = [[NSMutableDictionary alloc] initWithDictionary: theInfo];
}
else
{
_info = [[NSMutableDictionary alloc] init];
}
return self;
}
//
//
//
- (NSString *) description
{
if (self.command == IMAP_LOGIN || self.command == IMAP_AUTHENTICATE_LOGIN) {
// Arguments might hold passwords.
return [NSString stringWithFormat: @"%d (IMAP_LOGIN or IMAP_AUTHENTICATE_LOGIN)", self.command];
}
return [NSString stringWithFormat: @"%d %@", self.command, self.arguments];
}
@end