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