98 |
static int folder_imap_unsubscribe __P ((folder_t, const char *)); |
static int folder_imap_unsubscribe __P ((folder_t, const char *)); |
99 |
static int folder_imap_get_authority __P ((folder_t, authority_t *)); |
static int folder_imap_get_authority __P ((folder_t, authority_t *)); |
100 |
|
|
|
static int authenticate_imap_login __P ((authority_t)); |
|
|
static int authenticate_imap_sasl_anon __P ((authority_t)); |
|
|
|
|
101 |
/* FETCH */ |
/* FETCH */ |
102 |
static int imap_fetch __P ((f_imap_t)); |
static int imap_fetch __P ((f_imap_t)); |
103 |
static int imap_rfc822 __P ((f_imap_t, char **)); |
static int imap_rfc822 __P ((f_imap_t, char **)); |
124 |
|
|
125 |
static int imap_token __P ((char *, size_t, char **)); |
static int imap_token __P ((char *, size_t, char **)); |
126 |
|
|
127 |
/* Initialize the concrete IMAP mailbox: overload the folder functions */ |
/* Capability */ |
128 |
int |
static int parse_capa __P ((f_imap_t f_imap, char *str)); |
129 |
_folder_imap_init (folder_t folder) |
static int read_capa __P ((f_imap_t f_imap, int force)); |
130 |
{ |
static int check_capa __P ((f_imap_t f_imap, char *capa)); |
|
int status; |
|
|
f_imap_t f_imap; |
|
|
|
|
|
/* Set the authority early: |
|
|
(1) so we can check for errors. |
|
|
(2) allow the client to get the authority for setting the ticket |
|
|
before the open. */ |
|
|
status = folder_imap_get_authority (folder, NULL); |
|
|
if (status != 0) |
|
|
return status; |
|
|
|
|
|
f_imap = folder->data = calloc (1, sizeof (*f_imap)); |
|
|
if (f_imap == NULL) |
|
|
return ENOMEM; |
|
|
|
|
|
f_imap->folder = folder; |
|
|
f_imap->state = IMAP_NO_STATE; |
|
|
|
|
|
folder->_destroy = folder_imap_destroy; |
|
|
|
|
|
folder->_open = folder_imap_open; |
|
|
folder->_close = folder_imap_close; |
|
|
|
|
|
folder->_list = folder_imap_list; |
|
|
folder->_lsub = folder_imap_lsub; |
|
|
folder->_subscribe = folder_imap_subscribe; |
|
|
folder->_unsubscribe = folder_imap_unsubscribe; |
|
|
folder->_delete = folder_imap_delete; |
|
|
folder->_rename = folder_imap_rename; |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Destroy the folder resources. */ |
|
|
static void |
|
|
folder_imap_destroy (folder_t folder) |
|
|
{ |
|
|
if (folder->data) |
|
|
{ |
|
|
f_imap_t f_imap = folder->data; |
|
|
if (f_imap->buffer) |
|
|
free (f_imap->buffer); |
|
|
if (f_imap->capav) |
|
|
argcv_free (f_imap->capac, f_imap->capav); |
|
|
free (f_imap); |
|
|
folder->data = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
static int |
|
|
folder_imap_get_authority (folder_t folder, authority_t *pauth) |
|
|
{ |
|
|
int status = 0; |
|
|
if (folder->authority == NULL) |
|
|
{ |
|
|
/* assert (folder->url); */ |
|
|
if (folder->url == NULL) |
|
|
return EINVAL; |
|
|
|
|
|
if (folder->url->auth == NULL |
|
|
|| strcasecmp (folder->url->auth, "*") == 0) |
|
|
{ |
|
|
status = authority_create (&folder->authority, NULL, folder); |
|
|
authority_set_authenticate (folder->authority, |
|
|
authenticate_imap_login, folder); |
|
|
} |
|
|
else if (strcasecmp (folder->url->auth, "anon") == 0) |
|
|
{ |
|
|
status = authority_create (&folder->authority, NULL, folder); |
|
|
authority_set_authenticate (folder->authority, |
|
|
authenticate_imap_sasl_anon, folder); |
|
|
} |
|
|
else |
|
|
{ |
|
|
/* Not a supported authentication mechanism. */ |
|
|
status = ENOSYS; |
|
|
} |
|
|
} |
|
|
if (pauth) |
|
|
*pauth = folder->authority; |
|
|
return status; |
|
|
} |
|
|
|
|
|
static int |
|
|
parse_capa (f_imap_t f_imap, char *str) |
|
|
{ |
|
|
if (f_imap->capav) |
|
|
argcv_free (f_imap->capac, f_imap->capav); |
|
|
return argcv_get (str, "", NULL, &f_imap->capac, &f_imap->capav); |
|
|
} |
|
|
|
|
|
static int |
|
|
read_capa (f_imap_t f_imap, int force) |
|
|
{ |
|
|
int status = 0; |
|
|
|
|
|
if (force) |
|
|
{ |
|
|
argcv_free (f_imap->capac, f_imap->capav); |
|
|
f_imap->capac = 0; |
|
|
f_imap->capav = NULL; |
|
|
} |
|
|
|
|
|
if (!f_imap->capav) |
|
|
{ |
|
|
status = imap_writeline (f_imap, "g%u CAPABILITY\r\n", |
|
|
f_imap->seq++); |
|
|
status = imap_send (f_imap); |
|
|
status = imap_parse (f_imap); |
|
|
} |
|
|
return status; |
|
|
} |
|
|
|
|
|
static int |
|
|
check_capa (f_imap_t f_imap, char *capa) |
|
|
{ |
|
|
int i; |
|
131 |
|
|
132 |
read_capa (f_imap, 0); |
|
133 |
for (i = 0; i < f_imap->capac; i++) |
/* Authentication methods */ |
|
if (strcasecmp (f_imap->capav[i], capa) == 0) |
|
|
return 0; |
|
|
return 1; |
|
|
} |
|
|
|
|
|
static int |
|
|
tls (folder_t folder) |
|
|
{ |
|
|
#ifdef WITH_TLS |
|
|
int status; |
|
|
f_imap_t f_imap = folder->data; |
|
134 |
|
|
135 |
if (!mu_tls_enable || check_capa (f_imap, "STARTTLS")) |
typedef int (*auth_method_t) __P((authority_t)); |
|
return -1; |
|
|
|
|
|
FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "g%u STARTTLS\n", f_imap->seq); |
|
|
status = imap_writeline (f_imap, "g%u STARTTLS\r\n", |
|
|
f_imap->seq++, f_imap->user, f_imap->passwd); |
|
|
CHECK_ERROR (f_imap, status); |
|
|
status = imap_send (f_imap); |
|
|
CHECK_ERROR (f_imap, status); |
|
|
status = imap_parse (f_imap); |
|
|
if (status == 0) |
|
|
{ |
|
|
stream_t str; |
|
|
status = tls_stream_create_client_from_tcp (&str, folder->stream, 0); |
|
|
CHECK_ERROR (f_imap, status); |
|
|
status = stream_open (str); |
|
|
if (status == 0) |
|
|
folder->stream = str; |
|
|
FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "TLS negotiation %s\n", |
|
|
status == 0 ? "succeeded" : "failed"); |
|
|
read_capa (f_imap, 1); |
|
|
} |
|
|
return status; |
|
|
#else |
|
|
return -1; |
|
|
#endif |
|
|
} |
|
136 |
|
|
137 |
/* Simple User/pass authentication for imap. */ |
/* Simple User/pass authentication for imap. */ |
138 |
static int |
static int |
187 |
{ |
{ |
188 |
CHECK_ERROR_CLOSE (folder, f_imap, MU_ERR_NOPASSWORD); |
CHECK_ERROR_CLOSE (folder, f_imap, MU_ERR_NOPASSWORD); |
189 |
} |
} |
190 |
status = imap_writeline (f_imap, "g%u LOGIN %s %s\r\n", |
status = imap_writeline (f_imap, "g%u LOGIN %s \"%s\"\r\n", |
191 |
f_imap->seq, f_imap->user, f_imap->passwd); |
f_imap->seq, f_imap->user, f_imap->passwd); |
192 |
CHECK_ERROR_CLOSE(folder, f_imap, status); |
CHECK_ERROR_CLOSE(folder, f_imap, status); |
193 |
FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "g%u LOGIN %s *\n", |
FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "g%u LOGIN %s *\n", |
276 |
|
|
277 |
assert (f_imap->state == IMAP_AUTH); |
assert (f_imap->state == IMAP_AUTH); |
278 |
|
|
279 |
|
if (check_capa (f_imap, "AUTH=ANONYMOUS")) |
280 |
|
{ |
281 |
|
FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, |
282 |
|
"ANONYMOUS capability not present disabled\n"); |
283 |
|
return ENOSYS; |
284 |
|
} |
285 |
|
|
286 |
switch (f_imap->auth_state) |
switch (f_imap->auth_state) |
287 |
{ |
{ |
288 |
case IMAP_AUTH_ANON_REQ_WRITE: |
case IMAP_AUTH_ANON_REQ_WRITE: |
339 |
return 0; |
return 0; |
340 |
} |
} |
341 |
|
|
342 |
|
struct auth_tab |
343 |
|
{ |
344 |
|
char *name; |
345 |
|
auth_method_t method; |
346 |
|
}; |
347 |
|
|
348 |
|
/* NOTE: The ordering of methods in this table is from most secure |
349 |
|
to less secure. */ |
350 |
|
|
351 |
|
static struct auth_tab auth_tab[] = { |
352 |
|
{ "login", authenticate_imap_login }, |
353 |
|
{ "anon", authenticate_imap_sasl_anon }, |
354 |
|
{ NULL } |
355 |
|
}; |
356 |
|
|
357 |
|
static auth_method_t |
358 |
|
find_auth_method (const char *name) |
359 |
|
{ |
360 |
|
struct auth_tab *p; |
361 |
|
|
362 |
|
for (p = auth_tab; p->name; p++) |
363 |
|
if (strcasecmp (p->name, name) == 0) |
364 |
|
return p->method; |
365 |
|
|
366 |
|
return NULL; |
367 |
|
} |
368 |
|
|
369 |
|
static int |
370 |
|
authenticate_imap_select (authority_t auth) |
371 |
|
{ |
372 |
|
struct auth_tab *p; |
373 |
|
int status; |
374 |
|
|
375 |
|
for (p = auth_tab; status && p->name; p++) |
376 |
|
status = p->method (auth); |
377 |
|
|
378 |
|
return status; |
379 |
|
} |
380 |
|
|
381 |
|
|
382 |
|
|
383 |
|
|
384 |
|
/* Initialize the concrete IMAP mailbox: overload the folder functions */ |
385 |
|
int |
386 |
|
_folder_imap_init (folder_t folder) |
387 |
|
{ |
388 |
|
int status; |
389 |
|
f_imap_t f_imap; |
390 |
|
|
391 |
|
/* Set the authority early: |
392 |
|
(1) so we can check for errors. |
393 |
|
(2) allow the client to get the authority for setting the ticket |
394 |
|
before the open. */ |
395 |
|
status = folder_imap_get_authority (folder, NULL); |
396 |
|
if (status != 0) |
397 |
|
return status; |
398 |
|
|
399 |
|
f_imap = folder->data = calloc (1, sizeof (*f_imap)); |
400 |
|
if (f_imap == NULL) |
401 |
|
return ENOMEM; |
402 |
|
|
403 |
|
f_imap->folder = folder; |
404 |
|
f_imap->state = IMAP_NO_STATE; |
405 |
|
|
406 |
|
folder->_destroy = folder_imap_destroy; |
407 |
|
|
408 |
|
folder->_open = folder_imap_open; |
409 |
|
folder->_close = folder_imap_close; |
410 |
|
|
411 |
|
folder->_list = folder_imap_list; |
412 |
|
folder->_lsub = folder_imap_lsub; |
413 |
|
folder->_subscribe = folder_imap_subscribe; |
414 |
|
folder->_unsubscribe = folder_imap_unsubscribe; |
415 |
|
folder->_delete = folder_imap_delete; |
416 |
|
folder->_rename = folder_imap_rename; |
417 |
|
|
418 |
|
return 0; |
419 |
|
} |
420 |
|
|
421 |
|
/* Destroy the folder resources. */ |
422 |
|
static void |
423 |
|
folder_imap_destroy (folder_t folder) |
424 |
|
{ |
425 |
|
if (folder->data) |
426 |
|
{ |
427 |
|
f_imap_t f_imap = folder->data; |
428 |
|
if (f_imap->buffer) |
429 |
|
free (f_imap->buffer); |
430 |
|
if (f_imap->capav) |
431 |
|
argcv_free (f_imap->capac, f_imap->capav); |
432 |
|
free (f_imap); |
433 |
|
folder->data = NULL; |
434 |
|
} |
435 |
|
} |
436 |
|
|
437 |
|
static int |
438 |
|
folder_set_auth_method (folder_t folder, auth_method_t method) |
439 |
|
{ |
440 |
|
if (!folder->authority) |
441 |
|
{ |
442 |
|
int status = authority_create (&folder->authority, NULL, folder); |
443 |
|
if (status) |
444 |
|
return status; |
445 |
|
} |
446 |
|
return authority_set_authenticate (folder->authority, method, folder); |
447 |
|
} |
448 |
|
|
449 |
|
static int |
450 |
|
folder_imap_get_authority (folder_t folder, authority_t *pauth) |
451 |
|
{ |
452 |
|
int status = 0; |
453 |
|
if (folder->authority == NULL) |
454 |
|
{ |
455 |
|
/* assert (folder->url); */ |
456 |
|
if (folder->url == NULL) |
457 |
|
return EINVAL; |
458 |
|
|
459 |
|
if (folder->url->auth == NULL |
460 |
|
|| strcasecmp (folder->url->auth, "*") == 0) |
461 |
|
{ |
462 |
|
status = folder_set_auth_method (folder, authenticate_imap_select); |
463 |
|
} |
464 |
|
else |
465 |
|
{ |
466 |
|
char *p, *sp; |
467 |
|
|
468 |
|
for (p = strtok_r (folder->url->auth, ",", &sp); |
469 |
|
status == 0 && p; |
470 |
|
p = strtok_r (NULL, ",", &sp)) |
471 |
|
{ |
472 |
|
auth_method_t method = find_auth_method (p); |
473 |
|
if (method) |
474 |
|
status = folder_set_auth_method (folder, method); |
475 |
|
else |
476 |
|
status = MU_ERR_BAD_AUTH_SCHEME; |
477 |
|
} |
478 |
|
} |
479 |
|
} |
480 |
|
|
481 |
|
if (status) |
482 |
|
return status; |
483 |
|
|
484 |
|
if (pauth) |
485 |
|
*pauth = folder->authority; |
486 |
|
return status; |
487 |
|
} |
488 |
|
|
489 |
|
|
490 |
|
/* Capability handling */ |
491 |
|
static int |
492 |
|
parse_capa (f_imap_t f_imap, char *str) |
493 |
|
{ |
494 |
|
if (f_imap->capav) |
495 |
|
argcv_free (f_imap->capac, f_imap->capav); |
496 |
|
return argcv_get (str, "", NULL, &f_imap->capac, &f_imap->capav); |
497 |
|
} |
498 |
|
|
499 |
|
static int |
500 |
|
read_capa (f_imap_t f_imap, int force) |
501 |
|
{ |
502 |
|
int status = 0; |
503 |
|
|
504 |
|
if (force) |
505 |
|
{ |
506 |
|
argcv_free (f_imap->capac, f_imap->capav); |
507 |
|
f_imap->capac = 0; |
508 |
|
f_imap->capav = NULL; |
509 |
|
} |
510 |
|
|
511 |
|
if (!f_imap->capav) |
512 |
|
{ |
513 |
|
status = imap_writeline (f_imap, "g%u CAPABILITY\r\n", |
514 |
|
f_imap->seq++); |
515 |
|
status = imap_send (f_imap); |
516 |
|
status = imap_parse (f_imap); |
517 |
|
} |
518 |
|
return status; |
519 |
|
} |
520 |
|
|
521 |
|
static int |
522 |
|
check_capa (f_imap_t f_imap, char *capa) |
523 |
|
{ |
524 |
|
int i; |
525 |
|
|
526 |
|
read_capa (f_imap, 0); |
527 |
|
for (i = 0; i < f_imap->capac; i++) |
528 |
|
if (strcasecmp (f_imap->capav[i], capa) == 0) |
529 |
|
return 0; |
530 |
|
return 1; |
531 |
|
} |
532 |
|
|
533 |
|
|
534 |
|
static int |
535 |
|
tls (folder_t folder) |
536 |
|
{ |
537 |
|
#ifdef WITH_TLS |
538 |
|
int status; |
539 |
|
f_imap_t f_imap = folder->data; |
540 |
|
|
541 |
|
if (!mu_tls_enable || check_capa (f_imap, "STARTTLS")) |
542 |
|
return -1; |
543 |
|
|
544 |
|
FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "g%u STARTTLS\n", f_imap->seq); |
545 |
|
status = imap_writeline (f_imap, "g%u STARTTLS\r\n", |
546 |
|
f_imap->seq++, f_imap->user, f_imap->passwd); |
547 |
|
CHECK_ERROR (f_imap, status); |
548 |
|
status = imap_send (f_imap); |
549 |
|
CHECK_ERROR (f_imap, status); |
550 |
|
status = imap_parse (f_imap); |
551 |
|
if (status == 0) |
552 |
|
{ |
553 |
|
stream_t str; |
554 |
|
status = tls_stream_create_client_from_tcp (&str, folder->stream, 0); |
555 |
|
CHECK_ERROR (f_imap, status); |
556 |
|
status = stream_open (str); |
557 |
|
if (status == 0) |
558 |
|
folder->stream = str; |
559 |
|
FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "TLS negotiation %s\n", |
560 |
|
status == 0 ? "succeeded" : "failed"); |
561 |
|
read_capa (f_imap, 1); |
562 |
|
} |
563 |
|
return status; |
564 |
|
#else |
565 |
|
return -1; |
566 |
|
#endif |
567 |
|
} |
568 |
|
|
569 |
/* Create/Open the stream for IMAP. */ |
/* Create/Open the stream for IMAP. */ |
570 |
static int |
static int |
571 |
folder_imap_open (folder_t folder, int flags) |
folder_imap_open (folder_t folder, int flags) |