posta/imap.c
2023-04-12 01:24:28 +02:00

218 lines
3.9 KiB
C

#include "imap.h"
#include "utils.h"
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <openssl/err.h>
imap_t *imap_connect(const char *addr, const char *port)
{
int sock;
char *res;
FILE *sockfile;
imap_t *state;
struct addrinfo* address = NULL;
getaddrinfo(addr, port, 0, &address);
struct sockaddr_in *p = (struct sockaddr_in *)address->ai_addr;
if ((sock = create_connection(p)) == -1)
return NULL;
if ((sockfile = fdopen(sock, "r")) == NULL)
return NULL;
state = (imap_t *) malloc(sizeof(imap_t));
state->sock = sock;
state->sockfile = sockfile;
if (strcmp(port, "993") == 0) {
state->tls = 1;
imap_starttls(state);
} else {
state->tls = 0;
}
res = imap_response(state);
if (res[2] != 'O' || res[3] != 'K')
return NULL;
free(res);
return state;
}
void imap_close(imap_t *state)
{
close_connection(state->sock);
fclose(state->sockfile);
free(state);
}
char *imap_response(imap_t *state)
{
size_t len;
char *res;
char c;
int i;
len = 15;
res = (char *) calloc(len, sizeof(char));
i = 0;
if (!state->tls) {
c = fgetc(state->sockfile);
} else {
SSL_read(state->ssl, &c, 1);
}
while (c != '\n') {
res[i] = c;
i++;
if (i > len) {
len = i+5;
res = (char *) reallocarray(res, len, sizeof(char));
}
if (!state->tls) {
c = fgetc(state->sockfile);
} else {
SSL_read(state->ssl, &c, 1);
}
}
if (i < len)
res = (char *) reallocarray(res, i+1, sizeof(char));
res[i] = '\0';
return res;
}
int imap_send(imap_t *state, char *cmd)
{
if (!state->tls) {
return send(state->sock, (const void *)cmd, strlen(cmd), 0);
} else {
return SSL_write(state->ssl, (const void *)cmd, strlen(cmd)) > 0;
}
}
int imap_capability(imap_t *state)
{
state->capabilities = 0;
char *cmd, *res, *token;
cmd = "A0 CAPABILITY\n";
imap_send(state, cmd);
res = imap_response(state);
token = get_token(res);
if (!imap_match_token(token, "*"))
return -1;
token = get_token(NULL);
if (!imap_match_token(token, TOK_CAPABILITY))
return -1;
while ((token = get_token(NULL)) != NULL) {
if (imap_match_token(token, TOK_IMAP4))
state->capabilities |= IMAP4;
else if (imap_match_token(token, TOK_STARTTLS))
state->capabilities |= STARTTLS;
}
free(res);
res = imap_response(state);
free(res);
return 0;
}
int imap_match_token(char *t1, const char *t2)
{
return strcmp(t1, t2) == 0;
}
int imap_starttls(imap_t* state)
{
if (!state->tls) {
char *cmd, *res, *token;
cmd = "A1 STARTTLS\n";
imap_send(state, cmd);
res = imap_response(state);
token = get_token(res);
if (!imap_match_token(token, "A1"))
return 1;
token = get_token(NULL);
if (!imap_match_token(token, "OK"))
return 2;
}
state->ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
state->ssl = SSL_new(state->ssl_ctx);
SSL_set_fd(state->ssl, state->sock);
if (SSL_connect(state->ssl) == 0) {
return -1;
}
state->tls = 1;
return 0;
}
int imap_login(imap_t* state, char *username, char *password)
{
char *cmd, *res, *token;
size_t datalen = strlen(username) + strlen(password) + 12;
cmd = calloc(datalen, sizeof(char));
snprintf(cmd, datalen, "A2 LOGIN %s %s\n", username, password);
imap_send(state, cmd);
res = imap_response(state);
token = get_token(res);
if (!imap_match_token(token, "*"))
return -1;
token = get_token(NULL);
if (!imap_match_token(token, TOK_CAPABILITY))
return -1;
while ((token = get_token(NULL)) != NULL) {
if (imap_match_token(token, TOK_IMAP4))
state->capabilities |= IMAP4;
else if (imap_match_token(token, TOK_STARTTLS))
state->capabilities |= STARTTLS;
}
free(res);
res = imap_response(state);
token = get_token(res);
if (!imap_match_token(token, "A2"))
return 1;
token = get_token(NULL);
if (!imap_match_token(token, TOK_OK))
return 1;
return 0;
}