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

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. //
  2. // CWIMAPStore+Protected.m
  3. // Pantomime
  4. //
  5. // Created by Andreas Buff on 05.09.17.
  6. // Copyright © 2017 pEp Security S.A. All rights reserved.
  7. //
  8. #import "CWIMAPStore+Protected.h"
  9. #import "CWIMAPFolder.h"
  10. #import "Pantomime/NSString+Extensions.h"
  11. #import "CWThreadSafeArray.h"
  12. #import <pEpIOSToolboxForExtensions/PEPLogger.h>
  13. @implementation CWIMAPStore (Protected)
  14. //
  15. //
  16. //
  17. - (CWIMAPQueueObject * _Nullable)currentQueueObject
  18. {
  19. @synchronized (self) {
  20. return _currentQueueObject;
  21. }
  22. }
  23. //
  24. //
  25. //
  26. - (void)setCurrentQueueObject:(CWIMAPQueueObject * _Nullable)currentQueueObject
  27. {
  28. @synchronized (self) {
  29. if (_currentQueueObject != currentQueueObject) {
  30. _currentQueueObject = currentQueueObject;
  31. }
  32. }
  33. }
  34. //
  35. //
  36. //
  37. - (NSData *) nextTag
  38. {
  39. _tag++;
  40. return [self lastTag];
  41. }
  42. //
  43. //
  44. //
  45. - (NSData *) lastTag
  46. {
  47. char str[5];
  48. sprintf(str, "%04x", _tag);
  49. return [NSData dataWithBytes: str length: 4];
  50. }
  51. //
  52. //
  53. //
  54. - (void) subscribeToFolderWithName: (NSString *) theName
  55. {
  56. [self sendCommand: IMAP_SUBSCRIBE
  57. info: [NSDictionary dictionaryWithObject: theName forKey: @"Name"]
  58. arguments: @"SUBSCRIBE \"%@\"", [theName modifiedUTF7String]];
  59. }
  60. //
  61. //
  62. //
  63. - (void) unsubscribeToFolderWithName: (NSString *) theName
  64. {
  65. [self sendCommand: IMAP_UNSUBSCRIBE
  66. info: [NSDictionary dictionaryWithObject: theName forKey: @"Name"]
  67. arguments: @"UNSUBSCRIBE \"%@\"", [theName modifiedUTF7String]];
  68. }
  69. //
  70. //
  71. //
  72. - (NSDictionary *) folderStatus: (NSArray *) theArray
  73. {
  74. int i;
  75. [_folderStatus removeAllObjects];
  76. // C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
  77. // S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
  78. // S: A042 OK STATUS completed
  79. //
  80. // We send: MESSAGES UNSEEN
  81. for (i = 0; i < [theArray count]; i++)
  82. {
  83. // RFC3501 says we SHOULD NOT call STATUS on the selected mailbox - so we won't do it.
  84. if (_selectedFolder && [[_selectedFolder name] isEqualToString: [theArray objectAtIndex: i]])
  85. {
  86. continue;
  87. }
  88. [self sendCommand: IMAP_STATUS
  89. info: [NSDictionary dictionaryWithObject: [theArray objectAtIndex: i] forKey: @"Name"]
  90. arguments: @"STATUS \"%@\" (MESSAGES UNSEEN)", [[theArray objectAtIndex: i] modifiedUTF7String]];
  91. }
  92. return _folderStatus;
  93. }
  94. //
  95. //
  96. //
  97. - (void) sendCommand: (IMAPCommand) theCommand info: (NSDictionary *) theInfo arguments: (NSString *) theFormat, ...
  98. {
  99. va_list args;
  100. va_start(args, theFormat);
  101. NSString *aString = [[NSString alloc] initWithFormat: theFormat arguments: args];
  102. [self sendCommandInternal:theCommand info:theInfo string:aString];
  103. }
  104. //
  105. //
  106. //
  107. - (void) sendCommandInternal: (IMAPCommand) theCommand info: (NSDictionary * _Nullable) theInfo
  108. string:(NSString * _Nonnull)theString
  109. {
  110. // If two calls to this method happen concurrently, one might empty the queue the other is
  111. // currenly using (causes |SENDING null|).
  112. @synchronized(self) {
  113. if (theCommand == IMAP_EMPTY_QUEUE)
  114. {
  115. if ([_queue count])
  116. {
  117. // We dequeue the first inserted command from the queue.
  118. self.currentQueueObject = [_queue lastObject];
  119. }
  120. else
  121. {
  122. // The queue is empty, we have nothing more to do...
  123. LogInfo(@"sendCommand currentQueueObject = nil");
  124. self.currentQueueObject = nil;
  125. return;
  126. }
  127. }
  128. else
  129. {
  130. //
  131. // We must check in the queue if we aren't trying to add a command that is already there.
  132. // This could happend if -rawSource is called in IMAPMessage multiple times before
  133. // PantomimeMessageFetchCompleted is sent.
  134. //
  135. // We skip this verification for the IMAP_APPEND command as a messages with the same size
  136. // could be quickly appended to the folder and we do NOT want to skip the second one.
  137. //
  138. for (CWIMAPQueueObject *aQueueObject in _queue) {
  139. if (aQueueObject.command == theCommand && theCommand != IMAP_APPEND &&
  140. [aQueueObject.arguments isEqualToString: theString])
  141. {
  142. //LogInfo(@"A COMMAND ALREADY EXIST!!!!");
  143. return;
  144. }
  145. }
  146. CWIMAPQueueObject *aQueueObject = [[CWIMAPQueueObject alloc]
  147. initWithCommand: theCommand arguments: theString
  148. tag: [self nextTag] info: theInfo];
  149. [_queue insertObject: aQueueObject atIndex: 0];
  150. RELEASE(aQueueObject);
  151. LogInfo(@"%p queue size = %lul", self, (unsigned long) [_queue count]);
  152. // If we had queued commands, we return since we'll eventually
  153. // dequeue them one by one. Otherwise, we run it immediately.
  154. if ([_queue count] > 1)
  155. {
  156. //LogInfo(@"%p QUEUED |%@|", self, theString);
  157. return;
  158. }
  159. self.currentQueueObject = aQueueObject;
  160. }
  161. BOOL isPrivate = NO;
  162. if (self.currentQueueObject.command == IMAP_LOGIN) {
  163. isPrivate = YES;
  164. }
  165. if (isPrivate) {
  166. LogInfo(@"%p Sending private data |*******|", self);
  167. } else {
  168. LogInfo(@"%p Sending |%@|", self, self.currentQueueObject.arguments);
  169. }
  170. _lastCommand = self.currentQueueObject.command;
  171. [self bulkWriteData:@[self.currentQueueObject.tag,
  172. [NSData dataWithBytes: " " length: 1],
  173. [self.currentQueueObject.arguments dataUsingEncoding: _defaultStringEncoding],
  174. _crlf]];
  175. PERFORM_SELECTOR_2(_delegate, @selector(commandSent:), @"PantomimeCommandSent", [NSNumber numberWithInt: _lastCommand], @"Command");
  176. }
  177. }
  178. - (CWIMAPFolder *)folderForNameInternal:(NSString *)name
  179. mode:(PantomimeFolderMode)mode
  180. updateExistsCount:(BOOL)updateExistsCount
  181. {
  182. CWIMAPFolder *folder = [_openFolders objectForKey:name];
  183. LogInfo(@"select folder %@", name);
  184. if (folder) {
  185. if ([_selectedFolder.name isEqualToString:name]) {
  186. // We have the folder already and it is already selected.
  187. if (!updateExistsCount) {
  188. // Return it in case exists count is not of interest ...
  189. return folder;
  190. }
  191. // ... otherwize update exists count by calling SELECT/EXAMINE.
  192. }
  193. } else {
  194. folder = [self folderWithName:name];
  195. [_openFolders setObject:folder forKey:name];
  196. }
  197. [folder setStore:self];
  198. folder.mode = mode;
  199. //LogInfo(@"_connection_state.opening_mailbox = %d", _connection_state.opening_mailbox);
  200. // If we are already opening a mailbox, we must interrupt the process
  201. // and open the preferred one instead.
  202. if (_connection_state.opening_mailbox) {
  203. // Safety measure - in case close (so -removeFolderFromOpenFolders)
  204. // on the selected folder wasn't called.
  205. if (_selectedFolder) {
  206. [_openFolders removeObjectForKey:[_selectedFolder name]];
  207. }
  208. [super cancelRequest];
  209. [self reconnect];
  210. _selectedFolder = folder;
  211. return _selectedFolder;
  212. }
  213. _connection_state.opening_mailbox = YES;
  214. if (mode == PantomimeReadOnlyMode) {
  215. [self sendCommand:IMAP_EXAMINE info:nil arguments:@"EXAMINE \"%@\"", [name modifiedUTF7String]];
  216. } else {
  217. [self sendCommand:IMAP_SELECT info:nil arguments:@"SELECT \"%@\"", [name modifiedUTF7String]];
  218. }
  219. // This folder becomes the selected one. This will have to be improved in the future.
  220. _selectedFolder = folder;
  221. return _selectedFolder;
  222. }
  223. //
  224. //
  225. //
  226. - (void)signalFolderSyncError
  227. {
  228. PERFORM_SELECTOR_2(_delegate, @selector(folderSyncFailed:),
  229. PantomimeFolderSyncFailed, _selectedFolder, @"Folder");
  230. }
  231. //
  232. //
  233. //
  234. - (void)signalFolderFetchCompleted
  235. {
  236. //LogInfo(@"DONE PREFETCHING FOLDER");
  237. NSMutableDictionary *info = [NSMutableDictionary new];
  238. if (_selectedFolder) {
  239. info[@"Folder"] = _selectedFolder;
  240. }
  241. PERFORM_SELECTOR_3(_delegate,
  242. @selector(folderFetchCompleted:),
  243. PantomimeFolderFetchCompleted,
  244. info);
  245. }
  246. @end
  247. @implementation CWIMAPQueueObject
  248. //
  249. //
  250. //
  251. - (id) initWithCommand: (IMAPCommand) theCommand
  252. arguments: (NSString *) theArguments
  253. tag: (NSData *) theTag
  254. info: (NSDictionary *) theInfo
  255. {
  256. self = [super init];
  257. //LogInfo(@"CWIMAPQueueObject.init %@\n", self);
  258. _command = theCommand;
  259. _literal = 0;
  260. ASSIGN(_arguments, theArguments);
  261. ASSIGN(_tag, theTag);
  262. if (theInfo)
  263. {
  264. _info = [[NSMutableDictionary alloc] initWithDictionary: theInfo];
  265. }
  266. else
  267. {
  268. _info = [[NSMutableDictionary alloc] init];
  269. }
  270. return self;
  271. }
  272. //
  273. //
  274. //
  275. - (NSString *) description
  276. {
  277. if (self.command == IMAP_LOGIN || self.command == IMAP_AUTHENTICATE_LOGIN) {
  278. // Arguments might hold passwords.
  279. return [NSString stringWithFormat: @"%d (IMAP_LOGIN or IMAP_AUTHENTICATE_LOGIN)", self.command];
  280. }
  281. return [NSString stringWithFormat: @"%d %@", self.command, self.arguments];
  282. }
  283. @end