1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2005 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "mailstream_cfstream.h"
33 
34 #if HAVE_CFNETWORK
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <TargetConditionals.h>
37 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
38 #include <CFNetwork/CFNetwork.h>
39 #include <Security/Security.h>
40 #else
41 #include <CoreServices/CoreServices.h>
42 #endif
43 #endif
44 
45 #ifndef WIN32
46 #include <pthread.h>
47 #endif
48 
49 #if LIBETPAN_IOS_DISABLE_SSL && HAVE_CFNETWORK
50 #define CFSTREAM_ENABLED_DEFAULT 1
51 #else
52 #define CFSTREAM_ENABLED_DEFAULT 0
53 #endif
54 
55 LIBETPAN_EXPORT
56 int mailstream_cfstream_enabled = CFSTREAM_ENABLED_DEFAULT;
57 
58 LIBETPAN_EXPORT
59 int mailstream_cfstream_voip_enabled = 0;
60 
61 enum {
62   STATE_NONE,
63   STATE_WAIT_OPEN,
64   STATE_OPEN_READ_DONE,
65   STATE_OPEN_WRITE_DONE,
66   STATE_OPEN_READ_WRITE_DONE,
67   STATE_OPEN_WRITE_READ_DONE,
68   STATE_WAIT_READ,
69   STATE_READ_DONE,
70   STATE_WAIT_WRITE,
71   STATE_WRITE_DONE,
72   STATE_WAIT_IDLE,
73   STATE_IDLE_DONE,
74   STATE_WAIT_SSL,
75   STATE_SSL_READ_DONE,
76   STATE_SSL_WRITE_DONE,
77   STATE_SSL_READ_WRITE_DONE,
78   STATE_SSL_WRITE_READ_DONE
79 };
80 
81 #if HAVE_CFNETWORK
82 struct mailstream_cfstream_data {
83   int state;
84   CFStreamClientContext streamContext;
85 
86   CFReadStreamRef readStream;
87   void * readBuffer;
88   size_t readBufferSize;
89   ssize_t readResult;
90   int readOpenResult;
91   int readSSLResult;
92 
93   CFWriteStreamRef writeStream;
94   const void * writeBuffer;
95   size_t writeBufferSize;
96   ssize_t writeResult;
97   int writeOpenResult;
98   int writeSSLResult;
99 
100   Boolean cancelled;
101   CFRunLoopSourceRef cancelSource;
102   CFRunLoopSourceContext cancelContext;
103 
104   Boolean idleInterrupted;
105   CFRunLoopSourceRef idleInterruptedSource;
106   CFRunLoopSourceContext idleInterruptedContext;
107   int idleMaxDelay;
108 
109   CFRunLoopRef runloop;
110   pthread_mutex_t runloop_lock;
111 
112   int ssl_enabled;
113   int ssl_level;
114   int ssl_is_server;
115   char * ssl_peer_name;
116   int ssl_certificate_verification_mask;
117 };
118 #endif
119 
120 /* data */
121 
122 #if HAVE_CFNETWORK
123 static int low_open(mailstream_low * s);
124 static void cfstream_data_close(struct mailstream_cfstream_data * socket_data);
125 #endif
126 
127 /* mailstream_low, socket */
128 
129 static int mailstream_low_cfstream_close(mailstream_low * s);
130 static ssize_t mailstream_low_cfstream_read(mailstream_low * s,
131                                             void * buf, size_t count);
132 static ssize_t mailstream_low_cfstream_write(mailstream_low * s,
133                                              const void * buf, size_t count);
134 static void mailstream_low_cfstream_free(mailstream_low * s);
135 static int mailstream_low_cfstream_get_fd(mailstream_low * s);
136 static void mailstream_low_cfstream_cancel(mailstream_low * s);
137 static carray * mailstream_low_cfstream_get_certificate_chain(mailstream_low * s);
138 
139 static int mailstream_low_cfstream_setup_idle(mailstream_low * s);
140 static int mailstream_low_cfstream_unsetup_idle(mailstream_low * s);
141 static int mailstream_low_cfstream_interrupt_idle(mailstream_low * s);
142 
143 static mailstream_low_driver local_mailstream_cfstream_driver = {
144   /* mailstream_read */ mailstream_low_cfstream_read,
145   /* mailstream_write */ mailstream_low_cfstream_write,
146   /* mailstream_close */ mailstream_low_cfstream_close,
147   /* mailstream_get_fd */ mailstream_low_cfstream_get_fd,
148   /* mailstream_free */ mailstream_low_cfstream_free,
149   /* mailstream_cancel */ mailstream_low_cfstream_cancel,
150   /* mailstream_get_cancel */ NULL,
151   /* mailstream_get_certificate_chain */ mailstream_low_cfstream_get_certificate_chain,
152   /* mailstream_setup_idle */ mailstream_low_cfstream_setup_idle,
153   /* mailstream_unsetup_idle */ mailstream_low_cfstream_unsetup_idle,
154   /* mailstream_interrupt_idle */ mailstream_low_cfstream_interrupt_idle,
155 };
156 
157 mailstream_low_driver * mailstream_cfstream_driver =
158 &local_mailstream_cfstream_driver;
159 
160 #if HAVE_CFNETWORK
cfstream_data_new(CFReadStreamRef readStream,CFWriteStreamRef writeStream)161 static struct mailstream_cfstream_data * cfstream_data_new(CFReadStreamRef readStream, CFWriteStreamRef writeStream)
162 {
163   struct mailstream_cfstream_data * cfstream_data;
164 
165   cfstream_data = (struct mailstream_cfstream_data * ) malloc(sizeof(* cfstream_data));
166   memset(cfstream_data, 0, sizeof(* cfstream_data));
167   cfstream_data->readStream = (CFReadStreamRef) CFRetain(readStream);
168   cfstream_data->writeStream = (CFWriteStreamRef) CFRetain(writeStream);
169   cfstream_data->ssl_level = MAILSTREAM_CFSTREAM_SSL_LEVEL_NEGOCIATED_SSL;
170   pthread_mutex_init(&cfstream_data->runloop_lock, NULL);
171 
172   return cfstream_data;
173 }
174 
cfstream_data_free(struct mailstream_cfstream_data * cfstream_data)175 static void cfstream_data_free(struct mailstream_cfstream_data * cfstream_data)
176 {
177   cfstream_data_close(cfstream_data);
178   pthread_mutex_destroy(&cfstream_data->runloop_lock);
179   free(cfstream_data->ssl_peer_name);
180   free(cfstream_data);
181 }
182 
cfstream_data_close(struct mailstream_cfstream_data * cfstream_data)183 static void cfstream_data_close(struct mailstream_cfstream_data * cfstream_data)
184 {
185   if (cfstream_data->writeStream != NULL) {
186     CFWriteStreamSetClient(cfstream_data->writeStream, kCFStreamEventNone, NULL, NULL);
187     CFWriteStreamClose(cfstream_data->writeStream);
188     CFRelease(cfstream_data->writeStream);
189     cfstream_data->writeStream = NULL;
190   }
191   if (cfstream_data->readStream != NULL) {
192     CFReadStreamSetClient(cfstream_data->readStream, kCFStreamEventNone, NULL, NULL);
193     CFReadStreamClose(cfstream_data->readStream);
194     CFRelease(cfstream_data->readStream);
195     cfstream_data->readStream = NULL;
196   }
197 }
198 #endif
199 
mailstream_cfstream_open(const char * hostname,int16_t port)200 mailstream * mailstream_cfstream_open(const char * hostname, int16_t port)
201 {
202 	return mailstream_cfstream_open_voip_timeout(hostname, port, 0, 0);
203 }
204 
mailstream_cfstream_open_timeout(const char * hostname,int16_t port,time_t timeout)205 mailstream * mailstream_cfstream_open_timeout(const char * hostname, int16_t port, time_t timeout)
206 {
207 	return mailstream_cfstream_open_voip_timeout(hostname, port, 0, timeout);
208 }
209 
mailstream_cfstream_open_voip(const char * hostname,int16_t port,int voip_enabled)210 mailstream * mailstream_cfstream_open_voip(const char * hostname, int16_t port, int voip_enabled)
211 {
212 	return mailstream_cfstream_open_voip_timeout(hostname, port, voip_enabled, 0);
213 }
214 
mailstream_cfstream_open_voip_timeout(const char * hostname,int16_t port,int voip_enabled,time_t timeout)215 mailstream * mailstream_cfstream_open_voip_timeout(const char * hostname, int16_t port, int voip_enabled,
216   time_t timeout)
217 {
218 #if HAVE_CFNETWORK
219   mailstream_low * low;
220   mailstream * s;
221 
222   low = mailstream_low_cfstream_open_voip_timeout(hostname, port, voip_enabled, timeout);
223   if (low == NULL) {
224     return NULL;
225   }
226   s = mailstream_new(low, 8192);
227   return s;
228 #else
229   return NULL;
230 #endif
231 }
232 
233 #if HAVE_CFNETWORK
cancelPerform(void * info)234 static void cancelPerform(void *info)
235 {
236   struct mailstream_cfstream_data * cfstream_data;
237   mailstream_low * s;
238 
239   //fprintf(stderr, "cancelled\n");
240 
241   s = info;
242   cfstream_data = (struct mailstream_cfstream_data *) s->data;
243   cfstream_data->cancelled = true;
244 }
245 
readDataFromStream(mailstream_low * s)246 static void readDataFromStream(mailstream_low * s)
247 {
248   struct mailstream_cfstream_data * cfstream_data;
249 
250   cfstream_data = (struct mailstream_cfstream_data *) s->data;
251 
252   cfstream_data->readResult = CFReadStreamRead(cfstream_data->readStream,
253                                                cfstream_data->readBuffer,
254                                                cfstream_data->readBufferSize);
255   //fprintf(stderr, "data read %i\n", (int) cfstream_data->readResult);
256 }
257 
writeDataToStream(mailstream_low * s)258 static void writeDataToStream(mailstream_low * s)
259 {
260   struct mailstream_cfstream_data * cfstream_data;
261 
262   cfstream_data = (struct mailstream_cfstream_data *) s->data;
263 
264   cfstream_data->writeResult = CFWriteStreamWrite(cfstream_data->writeStream,
265                                                   cfstream_data->writeBuffer,
266                                                   cfstream_data->writeBufferSize);
267   //fprintf(stderr, "data written %i\n", (int) cfstream_data->writeResult);
268 }
269 
readStreamCallback(CFReadStreamRef stream,CFStreamEventType eventType,void * clientCallBackInfo)270 static void readStreamCallback(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
271 {
272   mailstream_low * s;
273   struct mailstream_cfstream_data * cfstream_data;
274 
275   s = (mailstream_low *) clientCallBackInfo;
276   cfstream_data = (struct mailstream_cfstream_data *) s->data;
277 
278   switch (eventType) {
279     case kCFStreamEventNone:
280       break;
281     case kCFStreamEventOpenCompleted:
282       cfstream_data->readResult = 0;
283       cfstream_data->readOpenResult = 0;
284       switch (cfstream_data->state) {
285         case STATE_WAIT_OPEN:
286           cfstream_data->state = STATE_OPEN_READ_DONE;
287           break;
288         case STATE_OPEN_WRITE_DONE:
289           cfstream_data->state = STATE_OPEN_WRITE_READ_DONE;
290           break;
291       }
292       break;
293     case kCFStreamEventHasBytesAvailable:
294       cfstream_data->readSSLResult = 0;
295       switch (cfstream_data->state) {
296         case STATE_WAIT_READ:
297           //fprintf(stderr, "has data\n");
298           readDataFromStream(s);
299           cfstream_data->state = STATE_READ_DONE;
300           break;
301         case STATE_WAIT_IDLE:
302           cfstream_data->state = STATE_IDLE_DONE;
303           break;
304         case STATE_WAIT_SSL:
305           cfstream_data->state = STATE_SSL_READ_DONE;
306           break;
307         case STATE_SSL_WRITE_DONE:
308           cfstream_data->state = STATE_SSL_WRITE_READ_DONE;
309           break;
310       }
311       break;
312     case kCFStreamEventCanAcceptBytes:
313       break;
314     case kCFStreamEventErrorOccurred:
315       cfstream_data->readResult = -1;
316       cfstream_data->readOpenResult = -1;
317       cfstream_data->readSSLResult = -1;
318       switch (cfstream_data->state) {
319         case STATE_WAIT_OPEN:
320           cfstream_data->state = STATE_OPEN_READ_DONE;
321           break;
322         case STATE_OPEN_WRITE_DONE:
323           cfstream_data->state = STATE_OPEN_WRITE_READ_DONE;
324           break;
325         case STATE_WAIT_READ:
326           //fprintf(stderr, "error read\n");
327           cfstream_data->state = STATE_READ_DONE;
328           break;
329         case STATE_WAIT_IDLE:
330           cfstream_data->state = STATE_IDLE_DONE;
331           break;
332         case STATE_WAIT_SSL:
333           cfstream_data->state = STATE_SSL_READ_DONE;
334           break;
335         case STATE_SSL_WRITE_DONE:
336           cfstream_data->state = STATE_SSL_WRITE_READ_DONE;
337           break;
338       }
339       break;
340     case kCFStreamEventEndEncountered:
341       cfstream_data->readResult = 0;
342       cfstream_data->readOpenResult = 0;
343       cfstream_data->readSSLResult = 0;
344       switch (cfstream_data->state) {
345         case STATE_WAIT_OPEN:
346           cfstream_data->state = STATE_OPEN_READ_DONE;
347           break;
348         case STATE_OPEN_WRITE_DONE:
349           cfstream_data->state = STATE_OPEN_WRITE_READ_DONE;
350           break;
351         case STATE_WAIT_READ:
352           //fprintf(stderr, "end read\n");
353           cfstream_data->state = STATE_READ_DONE;
354           break;
355         case STATE_WAIT_IDLE:
356           cfstream_data->state = STATE_IDLE_DONE;
357           break;
358       }
359       break;
360   }
361 }
362 
writeStreamCallback(CFWriteStreamRef stream,CFStreamEventType eventType,void * clientCallBackInfo)363 static void writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
364 {
365   mailstream_low * s;
366   struct mailstream_cfstream_data * cfstream_data;
367 
368   s = (mailstream_low *) clientCallBackInfo;
369   cfstream_data = (struct mailstream_cfstream_data *) s->data;
370 
371   switch (eventType) {
372     case kCFStreamEventNone:
373       break;
374     case kCFStreamEventOpenCompleted:
375       cfstream_data->writeResult = 0;
376       cfstream_data->writeOpenResult = 0;
377       switch (cfstream_data->state) {
378         case STATE_WAIT_OPEN:
379           cfstream_data->state = STATE_OPEN_WRITE_DONE;
380           break;
381         case STATE_OPEN_READ_DONE:
382           cfstream_data->state = STATE_OPEN_READ_WRITE_DONE;
383           break;
384       }
385       break;
386     case kCFStreamEventHasBytesAvailable:
387       break;
388     case kCFStreamEventCanAcceptBytes:
389       //fprintf(stderr, "can accept\n");
390       cfstream_data->writeSSLResult = 0;
391       switch (cfstream_data->state) {
392         case STATE_WAIT_WRITE:
393           writeDataToStream(s);
394           cfstream_data->state = STATE_WRITE_DONE;
395           break;
396         case STATE_WAIT_SSL:
397           cfstream_data->state = STATE_SSL_WRITE_DONE;
398           break;
399         case STATE_SSL_READ_DONE:
400           cfstream_data->state = STATE_SSL_READ_WRITE_DONE;
401           break;
402       }
403       break;
404     case kCFStreamEventErrorOccurred:
405       cfstream_data->writeResult = -1;
406       cfstream_data->writeOpenResult = -1;
407       cfstream_data->writeSSLResult = -1;
408       switch (cfstream_data->state) {
409         case STATE_WAIT_OPEN:
410           cfstream_data->state = STATE_OPEN_WRITE_DONE;
411           break;
412         case STATE_OPEN_READ_DONE:
413           cfstream_data->state = STATE_OPEN_READ_WRITE_DONE;
414           break;
415         case STATE_WAIT_WRITE:
416           cfstream_data->state = STATE_OPEN_WRITE_DONE;
417           break;
418         case STATE_WAIT_SSL:
419           cfstream_data->state = STATE_SSL_WRITE_DONE;
420           break;
421         case STATE_SSL_READ_DONE:
422           cfstream_data->state = STATE_SSL_READ_WRITE_DONE;
423           break;
424       }
425       break;
426     case kCFStreamEventEndEncountered:
427       cfstream_data->writeResult = -1;
428       cfstream_data->writeOpenResult = -1;
429       cfstream_data->writeSSLResult = -1;
430       switch (cfstream_data->state) {
431         case STATE_WAIT_OPEN:
432           cfstream_data->state = STATE_OPEN_WRITE_DONE;
433           break;
434         case STATE_OPEN_READ_DONE:
435           cfstream_data->state = STATE_OPEN_READ_WRITE_DONE;
436           break;
437         case STATE_WAIT_WRITE:
438           cfstream_data->state = STATE_OPEN_WRITE_DONE;
439           break;
440       }
441       break;
442   }
443 }
444 #endif
445 
mailstream_low_cfstream_open(const char * hostname,int16_t port)446 mailstream_low * mailstream_low_cfstream_open(const char * hostname, int16_t port)
447 {
448     return mailstream_low_cfstream_open_voip_timeout(hostname, port, mailstream_cfstream_voip_enabled, 0);
449 }
450 
mailstream_low_cfstream_open_timeout(const char * hostname,int16_t port,time_t timeout)451 mailstream_low * mailstream_low_cfstream_open_timeout(const char * hostname, int16_t port,
452   time_t timeout)
453 {
454 	return mailstream_low_cfstream_open_voip_timeout(hostname, port,
455 	  mailstream_cfstream_voip_enabled, timeout);
456 }
457 
mailstream_low_cfstream_open_voip(const char * hostname,int16_t port,int voip_enabled)458 mailstream_low * mailstream_low_cfstream_open_voip(const char * hostname, int16_t port, int voip_enabled)
459 {
460 	return mailstream_low_cfstream_open_voip_timeout(hostname, port, voip_enabled, 0);
461 }
462 
463 #if HAVE_CFNETWORK
numberIntValue(CFNumberRef nb)464 static int numberIntValue(CFNumberRef nb)
465 {
466   if (nb == NULL) {
467     return 0;
468   }
469 
470   int result;
471   CFNumberGetValue(nb, kCFNumberIntType, &result);
472   return result;
473 }
474 #endif
475 
mailstream_low_cfstream_open_voip_timeout(const char * hostname,int16_t port,int voip_enabled,time_t timeout)476 mailstream_low * mailstream_low_cfstream_open_voip_timeout(const char * hostname, int16_t port,
477   int voip_enabled, time_t timeout)
478 {
479 #if HAVE_CFNETWORK
480   mailstream_low * s;
481   struct mailstream_cfstream_data * cfstream_data;
482   CFReadStreamRef readStream;
483   CFWriteStreamRef writeStream;
484   CFStringRef hostString;
485   CFOptionFlags readFlags;
486   CFOptionFlags writeFlags;
487   int r;
488 
489   hostString = CFStringCreateWithCString(NULL, hostname, kCFStringEncodingUTF8);
490   CFStreamCreatePairWithSocketToHost(NULL, hostString, port, &readStream, &writeStream);
491   CFRelease(hostString);
492 
493 #if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
494   if (voip_enabled) {
495     CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
496     CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
497   }
498 #endif
499 
500 #if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
501   CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
502   CFNumberRef nbEnabled = CFDictionaryGetValue(proxySettings, kCFNetworkProxiesSOCKSEnable);
503   if (numberIntValue(nbEnabled)) {
504     CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, proxySettings);
505     CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, proxySettings);
506   }
507   CFRelease(proxySettings);
508 #endif
509 
510   cfstream_data = cfstream_data_new(readStream, writeStream);
511   s = mailstream_low_new(cfstream_data, mailstream_cfstream_driver);
512 	mailstream_low_set_timeout(s, timeout);
513 
514   //fprintf(stderr, "open %s %i -> %p\n", hostname, port, s);
515 
516   /* setup streams */
517   cfstream_data->streamContext.info = s;
518 
519   readFlags = kCFStreamEventOpenCompleted |
520   kCFStreamEventHasBytesAvailable |
521   kCFStreamEventErrorOccurred |
522   kCFStreamEventEndEncountered;
523 
524   writeFlags = kCFStreamEventOpenCompleted |
525   kCFStreamEventCanAcceptBytes |
526   kCFStreamEventErrorOccurred |
527   kCFStreamEventEndEncountered;
528 
529   CFReadStreamSetClient(cfstream_data->readStream, readFlags, readStreamCallback, &cfstream_data->streamContext);
530   CFWriteStreamSetClient(cfstream_data->writeStream, writeFlags, writeStreamCallback, &cfstream_data->streamContext);
531 
532   CFRelease(readStream);
533   CFRelease(writeStream);
534   readStream = NULL;
535   writeStream = NULL;
536 
537   /* setup cancel */
538   cfstream_data->cancelContext.info = s;
539   cfstream_data->cancelContext.perform = cancelPerform;
540   cfstream_data->cancelSource = CFRunLoopSourceCreate(NULL, 0, &cfstream_data->cancelContext);
541 
542   r = low_open(s);
543   if (r < 0) {
544     mailstream_low_cfstream_close(s);
545     return NULL;
546   }
547 
548   return s;
549 #else
550   return NULL;
551 #endif
552 }
553 
554 
mailstream_low_cfstream_close(mailstream_low * s)555 static int mailstream_low_cfstream_close(mailstream_low * s)
556 {
557 #if HAVE_CFNETWORK
558   struct mailstream_cfstream_data * cfstream_data;
559 
560   cfstream_data = (struct mailstream_cfstream_data *) s->data;
561 
562   if (cfstream_data->cancelSource != NULL) {
563     CFRelease(cfstream_data->cancelSource);
564     cfstream_data->cancelSource = NULL;
565   }
566 
567   cfstream_data_close(cfstream_data);
568 
569   return 0;
570 #else
571   return 0;
572 #endif
573 }
574 
mailstream_low_cfstream_free(mailstream_low * s)575 static void mailstream_low_cfstream_free(mailstream_low * s)
576 {
577 #if HAVE_CFNETWORK
578   struct mailstream_cfstream_data * cfstream_data;
579 
580   cfstream_data = (struct mailstream_cfstream_data *) s->data;
581   cfstream_data_free(cfstream_data);
582   s->data = NULL;
583 
584   free(s);
585 #endif
586 }
587 
mailstream_low_cfstream_get_fd(mailstream_low * s)588 static int mailstream_low_cfstream_get_fd(mailstream_low * s)
589 {
590 #if HAVE_CFNETWORK
591   struct mailstream_cfstream_data * cfstream_data = NULL;
592   CFDataRef native_handle_data = NULL;
593   CFSocketNativeHandle native_handle_value = -1;
594   CFIndex native_data_len  = 0;
595   CFIndex native_value_len = 0;
596 
597   if (!s)
598     return -1;
599 
600   cfstream_data = (struct mailstream_cfstream_data *) s->data;
601 
602   if (!cfstream_data->readStream)
603     return -1;
604 
605   native_handle_data = (CFDataRef)CFReadStreamCopyProperty(cfstream_data->readStream, kCFStreamPropertySocketNativeHandle);
606   if (!native_handle_data)
607     return -1;
608 
609   native_data_len  = CFDataGetLength(native_handle_data);
610   native_value_len = (CFIndex)sizeof(native_handle_value);
611 
612   if (native_data_len != native_value_len) {
613     CFRelease(native_handle_data);
614     return -1;
615   }
616 
617   CFDataGetBytes(native_handle_data, CFRangeMake(0, MIN(native_data_len, native_value_len)), (UInt8 *)&native_handle_value);
618   CFRelease(native_handle_data);
619 
620   return native_handle_value;
621 #else
622   return -1;
623 #endif
624 }
625 
626 #if HAVE_CFNETWORK
setup_runloop(mailstream_low * s)627 static void setup_runloop(mailstream_low * s)
628 {
629   struct mailstream_cfstream_data * cfstream_data;
630 
631   cfstream_data = (struct mailstream_cfstream_data *) s->data;
632 
633   pthread_mutex_lock(&cfstream_data->runloop_lock);
634 
635   cfstream_data->runloop = (CFRunLoopRef) CFRetain(CFRunLoopGetCurrent());
636   if (cfstream_data->cancelSource != NULL) {
637     CFRunLoopAddSource(cfstream_data->runloop, cfstream_data->cancelSource, kCFRunLoopDefaultMode);
638     //fprintf(stderr, "add cancel source %p\n", cfstream_data->cancelSource);
639   }
640   if (cfstream_data->idleInterruptedSource != NULL) {
641     CFRunLoopAddSource(cfstream_data->runloop, cfstream_data->idleInterruptedSource, kCFRunLoopDefaultMode);
642     //fprintf(stderr, "add idle source %p\n", cfstream_data->idleInterruptedSource);
643   }
644 
645   pthread_mutex_unlock(&cfstream_data->runloop_lock);
646 }
647 
unsetup_runloop(mailstream_low * s)648 static void unsetup_runloop(mailstream_low * s)
649 {
650   struct mailstream_cfstream_data * cfstream_data;
651 
652   cfstream_data = (struct mailstream_cfstream_data *) s->data;
653 
654   pthread_mutex_lock(&cfstream_data->runloop_lock);
655 
656   if (cfstream_data->idleInterruptedSource != NULL) {
657     CFRunLoopRemoveSource(cfstream_data->runloop, cfstream_data->idleInterruptedSource, kCFRunLoopDefaultMode);
658   }
659   if (cfstream_data->cancelSource != NULL) {
660     CFRunLoopRemoveSource(cfstream_data->runloop, cfstream_data->cancelSource, kCFRunLoopDefaultMode);
661   }
662   if (cfstream_data->runloop != NULL) {
663     CFRelease(cfstream_data->runloop);
664     cfstream_data->runloop = NULL;
665   }
666 
667 
668   pthread_mutex_unlock(&cfstream_data->runloop_lock);
669 }
670 
671 enum {
672   WAIT_RUNLOOP_EXIT_NO_ERROR,
673   WAIT_RUNLOOP_EXIT_INTERRUPTED,
674   WAIT_RUNLOOP_EXIT_CANCELLED,
675   WAIT_RUNLOOP_EXIT_TIMEOUT,
676 };
677 
wait_runloop(mailstream_low * s,int wait_state)678 static int wait_runloop(mailstream_low * s, int wait_state)
679 {
680   struct mailstream_cfstream_data * cfstream_data;
681   int read_scheduled;
682   int write_scheduled;
683   int error;
684 
685   setup_runloop(s);
686 
687   cfstream_data = (struct mailstream_cfstream_data *) s->data;
688   cfstream_data->state = wait_state;
689 
690   read_scheduled = 0;
691   write_scheduled = 0;
692   error = WAIT_RUNLOOP_EXIT_NO_ERROR;
693 
694   switch (wait_state) {
695     case STATE_WAIT_OPEN:
696       //fprintf(stderr, "wait open\n");
697       CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
698       CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
699       read_scheduled = 1;
700       write_scheduled = 1;
701       break;
702     case STATE_WAIT_READ:
703       //fprintf(stderr, "wait read\n");
704       CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
705       read_scheduled = 1;
706       break;
707     case STATE_WAIT_WRITE:
708       //fprintf(stderr, "wait write\n");
709       CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
710       write_scheduled = 1;
711       break;
712     case STATE_WAIT_IDLE:
713       //fprintf(stderr, "wait idle\n");
714       CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
715       read_scheduled = 1;
716       break;
717     case STATE_WAIT_SSL:
718       //fprintf(stderr, "wait ssl\n");
719       CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
720       CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
721       read_scheduled = 1;
722       write_scheduled = 1;
723       break;
724   }
725 
726   if (read_scheduled) {
727     if (CFReadStreamHasBytesAvailable(cfstream_data->readStream)) {
728       readStreamCallback(cfstream_data->readStream, kCFStreamEventHasBytesAvailable, s);
729     }
730   }
731   if (write_scheduled) {
732     if (CFWriteStreamCanAcceptBytes(cfstream_data->writeStream)) {
733       writeStreamCallback(cfstream_data->writeStream, kCFStreamEventCanAcceptBytes, s);
734     }
735   }
736 
737   while (1) {
738     struct timeval timeout;
739     CFTimeInterval delay;
740     int r;
741     int done;
742 
743     if (cfstream_data->cancelled) {
744       error = WAIT_RUNLOOP_EXIT_CANCELLED;
745       break;
746     }
747     if (cfstream_data->state == STATE_WAIT_IDLE) {
748       if (cfstream_data->idleInterrupted) {
749         error = WAIT_RUNLOOP_EXIT_INTERRUPTED;
750         break;
751       }
752     }
753 
754     done = 0;
755     switch (cfstream_data->state) {
756       case STATE_OPEN_READ_DONE:
757         CFReadStreamUnscheduleFromRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
758         read_scheduled = 0;
759         break;
760       case STATE_OPEN_WRITE_DONE:
761         CFWriteStreamUnscheduleFromRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
762         write_scheduled = 0;
763         break;
764       case STATE_OPEN_READ_WRITE_DONE:
765         done = 1;
766         break;
767       case STATE_OPEN_WRITE_READ_DONE:
768         done = 1;
769         break;
770       case STATE_READ_DONE:
771         done = 1;
772         break;
773       case STATE_WRITE_DONE:
774         done = 1;
775         break;
776       case STATE_IDLE_DONE:
777         done = 1;
778         break;
779       case STATE_SSL_READ_DONE:
780         done = 1;
781         break;
782       case STATE_SSL_WRITE_DONE:
783         done = 1;
784         break;
785       case STATE_SSL_READ_WRITE_DONE:
786         done = 1;
787         break;
788       case STATE_SSL_WRITE_READ_DONE:
789         done = 1;
790         break;
791     }
792 
793     if (done) {
794       break;
795     }
796 
797     if (wait_state == STATE_WAIT_IDLE) {
798       timeout.tv_sec = cfstream_data->idleMaxDelay;
799       timeout.tv_usec = 0;
800     }
801     else {
802 	    if (s->timeout == 0) {
803 				timeout = mailstream_network_delay;
804 			}
805 			else {
806 	      timeout.tv_sec = s->timeout;
807 	      timeout.tv_usec = 0;
808 			}
809     }
810     delay = (CFTimeInterval) timeout.tv_sec + (CFTimeInterval) timeout.tv_usec / (CFTimeInterval) 1e6;
811 
812     r = CFRunLoopRunInMode(kCFRunLoopDefaultMode, delay, true);
813     if (r == kCFRunLoopRunTimedOut) {
814       error = WAIT_RUNLOOP_EXIT_TIMEOUT;
815       break;
816     }
817   }
818 
819   if (read_scheduled) {
820     CFReadStreamUnscheduleFromRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
821   }
822   if (write_scheduled) {
823     CFWriteStreamUnscheduleFromRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode);
824   }
825 
826   unsetup_runloop(s);
827 
828   if (error != WAIT_RUNLOOP_EXIT_NO_ERROR)
829     return error;
830 
831   return WAIT_RUNLOOP_EXIT_NO_ERROR;
832 }
833 #endif
834 
mailstream_low_cfstream_read(mailstream_low * s,void * buf,size_t count)835 static ssize_t mailstream_low_cfstream_read(mailstream_low * s,
836                                             void * buf, size_t count)
837 {
838 #if HAVE_CFNETWORK
839   struct mailstream_cfstream_data * cfstream_data;
840   int r;
841 
842   cfstream_data = (struct mailstream_cfstream_data *) s->data;
843   cfstream_data->readBuffer = buf;
844   cfstream_data->readBufferSize = count;
845 
846   if (cfstream_data->cancelled) {
847     return -1;
848   }
849 
850   if (CFReadStreamGetStatus(cfstream_data->readStream) == kCFStreamStatusError) {
851     return -1;
852   }
853 
854   if (CFReadStreamHasBytesAvailable(cfstream_data->readStream)) {
855     readDataFromStream(s);
856     return cfstream_data->readResult;
857   }
858 
859   r = wait_runloop(s, STATE_WAIT_READ);
860   if (r != WAIT_RUNLOOP_EXIT_NO_ERROR) {
861     return -1;
862   }
863 
864   return cfstream_data->readResult;
865 #else
866   return -1;
867 #endif
868 }
869 
mailstream_low_cfstream_write(mailstream_low * s,const void * buf,size_t count)870 static ssize_t mailstream_low_cfstream_write(mailstream_low * s,
871                                              const void * buf, size_t count)
872 {
873 #if HAVE_CFNETWORK
874   struct mailstream_cfstream_data * cfstream_data;
875   int r;
876 
877   cfstream_data = (struct mailstream_cfstream_data *) s->data;
878   cfstream_data->writeBuffer = buf;
879   cfstream_data->writeBufferSize = count;
880 
881   if (cfstream_data->cancelled)
882     return -1;
883 
884   if (CFWriteStreamGetStatus(cfstream_data->writeStream) == kCFStreamStatusError) {
885     return -1;
886   }
887 
888   if (CFWriteStreamCanAcceptBytes(cfstream_data->writeStream)) {
889     writeDataToStream(s);
890     return cfstream_data->writeResult;
891   }
892 
893   r = wait_runloop(s, STATE_WAIT_WRITE);
894   if (r != WAIT_RUNLOOP_EXIT_NO_ERROR) {
895     return -1;
896   }
897 
898   return cfstream_data->writeResult;
899 #else
900   return -1;
901 #endif
902 }
903 
904 #if HAVE_CFNETWORK
low_open(mailstream_low * s)905 static int low_open(mailstream_low * s)
906 {
907   struct mailstream_cfstream_data * cfstream_data;
908   int r;
909 
910   cfstream_data = (struct mailstream_cfstream_data *) s->data;
911 
912   CFReadStreamOpen(cfstream_data->readStream);
913   CFWriteStreamOpen(cfstream_data->writeStream);
914 
915   r = wait_runloop(s, STATE_WAIT_OPEN);
916   if (r != WAIT_RUNLOOP_EXIT_NO_ERROR) {
917     return -1;
918   }
919 
920   if (cfstream_data->writeOpenResult < 0)
921     return -1;
922   if (cfstream_data->readOpenResult < 0)
923     return -1;
924 
925   return 0;
926 }
927 #endif
928 
mailstream_low_cfstream_cancel(mailstream_low * s)929 static void mailstream_low_cfstream_cancel(mailstream_low * s)
930 {
931 #if HAVE_CFNETWORK
932   struct mailstream_cfstream_data * cfstream_data;
933 
934   cfstream_data = (struct mailstream_cfstream_data *) s->data;
935 
936   pthread_mutex_lock(&cfstream_data->runloop_lock);
937 
938   if (cfstream_data->cancelSource != NULL) {
939     CFRunLoopSourceSignal(cfstream_data->cancelSource);
940   }
941   if (cfstream_data->runloop != NULL) {
942     CFRunLoopWakeUp(cfstream_data->runloop);
943   }
944 
945   pthread_mutex_unlock(&cfstream_data->runloop_lock);
946 #endif
947 }
948 
mailstream_cfstream_set_ssl_enabled(mailstream * s,int ssl_enabled)949 int mailstream_cfstream_set_ssl_enabled(mailstream * s, int ssl_enabled)
950 {
951 #if HAVE_CFNETWORK
952   struct mailstream_cfstream_data * cfstream_data;
953   int r;
954   CFIndex count;
955 
956   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
957   cfstream_data->ssl_enabled = ssl_enabled;
958   if (ssl_enabled) {
959     CFMutableDictionaryRef settings;
960 
961     settings = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
962     switch (cfstream_data->ssl_level) {
963       case MAILSTREAM_CFSTREAM_SSL_LEVEL_NONE:
964         CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNone);
965         break;
966       case MAILSTREAM_CFSTREAM_SSL_LEVEL_SSLv2:
967         CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelSSLv2);
968         break;
969       case MAILSTREAM_CFSTREAM_SSL_LEVEL_SSLv3:
970         CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelSSLv3);
971         break;
972       case MAILSTREAM_CFSTREAM_SSL_LEVEL_TLSv1:
973         CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelTLSv1);
974         break;
975       case MAILSTREAM_CFSTREAM_SSL_LEVEL_NEGOCIATED_SSL:
976         CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
977         break;
978     }
979 
980     if ((cfstream_data->ssl_certificate_verification_mask & MAILSTREAM_CFSTREAM_SSL_ALLOWS_EXPIRED_CERTIFICATES) != 0) {
981       CFDictionarySetValue(settings, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
982     }
983     if ((cfstream_data->ssl_certificate_verification_mask & MAILSTREAM_CFSTREAM_SSL_ALLOWS_EXPIRED_ROOTS) != 0) {
984       CFDictionarySetValue(settings, kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
985     }
986     if ((cfstream_data->ssl_certificate_verification_mask & MAILSTREAM_CFSTREAM_SSL_ALLOWS_ANY_ROOT) != 0) {
987       CFDictionarySetValue(settings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
988     }
989     if ((cfstream_data->ssl_certificate_verification_mask & MAILSTREAM_CFSTREAM_SSL_DISABLE_VALIDATES_CERTIFICATE_CHAIN) != 0) {
990       CFDictionarySetValue(settings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
991     }
992 
993     CFReadStreamSetProperty(cfstream_data->readStream, kCFStreamPropertySSLSettings, settings);
994     CFWriteStreamSetProperty(cfstream_data->writeStream, kCFStreamPropertySSLSettings, settings);
995     CFRelease(settings);
996   }
997   else {
998     CFMutableDictionaryRef settings;
999 
1000     settings = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1001     CFDictionarySetValue(settings, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelNone);
1002 		CFReadStreamSetProperty(cfstream_data->readStream, kCFStreamPropertySSLSettings, settings);
1003 		CFWriteStreamSetProperty(cfstream_data->writeStream, kCFStreamPropertySSLSettings, settings);
1004     CFRelease(settings);
1005   }
1006 
1007   // We need to investigate more about how to establish a STARTTLS connection.
1008   // For now, wait until we get the certificate chain.
1009 
1010   CFArrayRef certs;
1011   SecTrustRef secTrust;
1012   while (1) {
1013     r = wait_runloop(s->low, STATE_WAIT_SSL);
1014     if (r != WAIT_RUNLOOP_EXIT_NO_ERROR) {
1015       return -1;
1016     }
1017     if (cfstream_data->writeSSLResult < 0)
1018       return -1;
1019     if (cfstream_data->readSSLResult < 0)
1020       return -1;
1021 
1022     secTrust = (SecTrustRef)CFReadStreamCopyProperty(cfstream_data->readStream, kCFStreamPropertySSLPeerTrust);
1023     if (secTrust) {
1024         // SecTrustEvaluate() needs to be called before SecTrustGetCertificateCount() in Mac OS X <= 10.8
1025         SecTrustEvaluate(secTrust, NULL);
1026         count = SecTrustGetCertificateCount(secTrust);
1027         CFRelease(secTrust);
1028     }
1029     else {
1030         certs = CFReadStreamCopyProperty(cfstream_data->readStream, kCFStreamPropertySSLPeerCertificates);
1031         if (certs) {
1032             count = CFArrayGetCount(certs);
1033             CFRelease(certs);
1034         }
1035         else {
1036             // No trust and no certs, wait more.
1037             continue;
1038         }
1039     }
1040 
1041     if (count == 0) {
1042       // No certificates, wait more.
1043       continue;
1044     }
1045 
1046     break;
1047   }
1048 
1049   return 0;
1050 #else
1051   return -1;
1052 #endif
1053 }
1054 
mailstream_cfstream_is_ssl_enabled(mailstream * s)1055 int mailstream_cfstream_is_ssl_enabled(mailstream * s)
1056 {
1057 #if HAVE_CFNETWORK
1058   struct mailstream_cfstream_data * cfstream_data;
1059   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
1060   return cfstream_data->ssl_enabled;
1061 #else
1062   return 0;
1063 #endif
1064 }
1065 
mailstream_cfstream_set_ssl_verification_mask(mailstream * s,int verification_mask)1066 void mailstream_cfstream_set_ssl_verification_mask(mailstream * s, int verification_mask)
1067 {
1068 #if HAVE_CFNETWORK
1069   struct mailstream_cfstream_data * cfstream_data;
1070   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
1071   cfstream_data->ssl_certificate_verification_mask = verification_mask;
1072 #endif
1073 }
1074 
mailstream_cfstream_set_ssl_peer_name(mailstream * s,const char * peer_name)1075 void mailstream_cfstream_set_ssl_peer_name(mailstream * s, const char * peer_name)
1076 {
1077 #if HAVE_CFNETWORK
1078   struct mailstream_cfstream_data * cfstream_data;
1079   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
1080 
1081   if (cfstream_data->ssl_peer_name != peer_name) {
1082     free(cfstream_data->ssl_peer_name);
1083     cfstream_data->ssl_peer_name = NULL;
1084     if (peer_name != NULL) {
1085       cfstream_data->ssl_peer_name = strdup(peer_name);
1086     }
1087   }
1088 #endif
1089 }
1090 
mailstream_cfstream_set_ssl_is_server(mailstream * s,int is_server)1091 void mailstream_cfstream_set_ssl_is_server(mailstream * s, int is_server)
1092 {
1093 #if HAVE_CFNETWORK
1094   struct mailstream_cfstream_data * cfstream_data;
1095   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
1096   cfstream_data->ssl_is_server = is_server;
1097 #endif
1098 }
1099 
mailstream_cfstream_set_ssl_level(mailstream * s,int ssl_level)1100 void mailstream_cfstream_set_ssl_level(mailstream * s, int ssl_level)
1101 {
1102 #if HAVE_CFNETWORK
1103   struct mailstream_cfstream_data * cfstream_data;
1104   cfstream_data = (struct mailstream_cfstream_data *) s->low->data;
1105   cfstream_data->ssl_level = ssl_level;
1106 #endif
1107 }
1108 
mailstream_cfstream_wait_idle(mailstream * s,int max_idle_delay)1109 int mailstream_cfstream_wait_idle(mailstream * s, int max_idle_delay)
1110 {
1111   return mailstream_low_cfstream_wait_idle(s->low, max_idle_delay);
1112 }
1113 
mailstream_low_cfstream_wait_idle(mailstream_low * low,int max_idle_delay)1114 int mailstream_low_cfstream_wait_idle(mailstream_low * low, int max_idle_delay)
1115 {
1116 #if HAVE_CFNETWORK
1117   struct mailstream_cfstream_data * cfstream_data;
1118   int r;
1119 
1120   cfstream_data = (struct mailstream_cfstream_data *) low->data;
1121   cfstream_data->idleMaxDelay = max_idle_delay;
1122 
1123   r = wait_runloop(low, STATE_WAIT_IDLE);
1124   switch (r) {
1125     case WAIT_RUNLOOP_EXIT_TIMEOUT:
1126       return MAILSTREAM_IDLE_TIMEOUT;
1127     case WAIT_RUNLOOP_EXIT_INTERRUPTED:
1128       return MAILSTREAM_IDLE_INTERRUPTED;
1129     case WAIT_RUNLOOP_EXIT_CANCELLED:
1130       return MAILSTREAM_IDLE_CANCELLED;
1131   }
1132   return MAILSTREAM_IDLE_HASDATA;
1133 #else
1134   return MAILSTREAM_IDLE_ERROR;
1135 #endif
1136 }
1137 
1138 #if HAVE_CFNETWORK
idleInterruptedPerform(void * info)1139 static void idleInterruptedPerform(void *info)
1140 {
1141   struct mailstream_cfstream_data * cfstream_data;
1142   mailstream_low * s;
1143 
1144   s = info;
1145 
1146   cfstream_data = (struct mailstream_cfstream_data *) s->data;
1147   cfstream_data->idleInterrupted = true;
1148 }
1149 #endif
1150 
mailstream_low_cfstream_setup_idle(mailstream_low * s)1151 static int mailstream_low_cfstream_setup_idle(mailstream_low * s)
1152 {
1153 #if HAVE_CFNETWORK
1154   struct mailstream_cfstream_data * cfstream_data;
1155 
1156   cfstream_data = (struct mailstream_cfstream_data *) s->data;
1157   cfstream_data->idleInterrupted = false;
1158   cfstream_data->idleInterruptedContext.info = s;
1159   cfstream_data->idleInterruptedContext.perform = idleInterruptedPerform;
1160   cfstream_data->idleInterruptedSource = CFRunLoopSourceCreate(NULL, 0, &cfstream_data->idleInterruptedContext);
1161   return 0;
1162 #else
1163   return -1;
1164 #endif
1165 }
1166 
mailstream_low_cfstream_unsetup_idle(mailstream_low * s)1167 static int mailstream_low_cfstream_unsetup_idle(mailstream_low * s)
1168 {
1169 #if HAVE_CFNETWORK
1170   struct mailstream_cfstream_data * cfstream_data;
1171 
1172   cfstream_data = (struct mailstream_cfstream_data *) s->data;
1173   if (cfstream_data->idleInterruptedSource != NULL) {
1174     CFRelease(cfstream_data->idleInterruptedSource);
1175     cfstream_data->idleInterruptedSource = NULL;
1176   }
1177   return 0;
1178 #else
1179   return -1;
1180 #endif
1181 }
1182 
mailstream_low_cfstream_interrupt_idle(mailstream_low * s)1183 static int mailstream_low_cfstream_interrupt_idle(mailstream_low * s)
1184 {
1185 #if HAVE_CFNETWORK
1186   struct mailstream_cfstream_data * cfstream_data;
1187 
1188   cfstream_data = (struct mailstream_cfstream_data *) s->data;
1189 
1190   pthread_mutex_lock(&cfstream_data->runloop_lock);
1191 
1192   if (cfstream_data->idleInterruptedSource != NULL) {
1193     CFRunLoopSourceSignal(cfstream_data->idleInterruptedSource);
1194   }
1195   if (cfstream_data->runloop != NULL) {
1196     CFRunLoopWakeUp(cfstream_data->runloop);
1197   }
1198 
1199   pthread_mutex_unlock(&cfstream_data->runloop_lock);
1200   return 0;
1201 #else
1202   return -1;
1203 #endif
1204 }
1205 
mailstream_low_cfstream_get_certificate_chain(mailstream_low * s)1206 static carray * mailstream_low_cfstream_get_certificate_chain(mailstream_low * s)
1207 {
1208 #if HAVE_CFNETWORK
1209   struct mailstream_cfstream_data * cfstream_data;
1210   unsigned int i;
1211   carray * result;
1212   CFArrayRef certs;
1213   CFIndex count;
1214 
1215   cfstream_data = (struct mailstream_cfstream_data *) s->data;
1216 
1217   SecTrustRef secTrust = (SecTrustRef)CFReadStreamCopyProperty(cfstream_data->readStream, kCFStreamPropertySSLPeerTrust);
1218   if (secTrust) {
1219       // SecTrustEvaluate() needs to be called before SecTrustGetCertificateCount() in Mac OS X <= 10.8
1220       SecTrustEvaluate(secTrust, NULL);
1221       count = SecTrustGetCertificateCount(secTrust);
1222       result = carray_new(4);
1223       for(i = 0 ; i < count ; i ++) {
1224           SecCertificateRef cert = (SecCertificateRef) SecTrustGetCertificateAtIndex(secTrust, i);
1225           CFDataRef data = SecCertificateCopyData(cert);
1226           if (data == NULL) {
1227             carray_free(result);
1228             CFRelease(secTrust);
1229             return NULL;
1230           }
1231           CFIndex length = CFDataGetLength(data);
1232           const UInt8 * bytes = CFDataGetBytePtr(data);
1233           MMAPString * str = mmap_string_sized_new(length);
1234           mmap_string_append_len(str, (char*) bytes, length);
1235           carray_add(result, str, NULL);
1236           CFRelease(data);
1237       }
1238       CFRelease(secTrust);
1239   }
1240   else {
1241       certs = CFReadStreamCopyProperty(cfstream_data->readStream, kCFStreamPropertySSLPeerCertificates);
1242       if (certs) {
1243           count = CFArrayGetCount(certs);
1244           result = carray_new(4);
1245           for(i = 0 ; i < count ; i ++) {
1246               SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(certs, i);
1247               CFDataRef data = SecCertificateCopyData(cert);
1248               if (data == NULL) {
1249                 carray_free(result);
1250                 CFRelease(certs);
1251                 return NULL;
1252               }
1253               CFIndex length = CFDataGetLength(data);
1254               const UInt8 * bytes = CFDataGetBytePtr(data);
1255               MMAPString * str = mmap_string_sized_new(length);
1256               mmap_string_append_len(str, (char*) bytes, length);
1257               carray_add(result, str, NULL);
1258               CFRelease(data);
1259           }
1260           CFRelease(certs);
1261       }
1262       else {
1263           return NULL;
1264       }
1265   }
1266 
1267   return result;
1268 #else
1269   return NULL;
1270 #endif
1271 }
1272