Fetch remote messages through libcurl
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
#define COLLECTOR_REMOTE_RESPONSE_MAX (1024U * 1024U)
|
||||
#define COLLECTOR_REMOTE_IDS_MAX 10000U
|
||||
#define COLLECTOR_REMOTE_MESSAGE_MAX (100U * 1024U * 1024U)
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
@@ -19,6 +20,12 @@ typedef struct {
|
||||
int overflow;
|
||||
} ResponseBuffer;
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
size_t length;
|
||||
int overflow;
|
||||
} MessageWriter;
|
||||
|
||||
static void
|
||||
SetError(char *error, size_t error_size, const char *message)
|
||||
{
|
||||
@@ -49,24 +56,44 @@ CollectResponse(char *data, size_t size, size_t count, void *context)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int
|
||||
AddId(CollectorRemoteIds *ids, const char *start, size_t length)
|
||||
static char *
|
||||
CopyToken(const char *start, size_t length)
|
||||
{
|
||||
char **larger;
|
||||
char *copy;
|
||||
if (length == 0) return 0;
|
||||
if (ids->count >= COLLECTOR_REMOTE_IDS_MAX) return -1;
|
||||
if (length == 0) return NULL;
|
||||
copy = malloc(length + 1);
|
||||
if (copy == NULL) return -1;
|
||||
if (copy == NULL) return NULL;
|
||||
memcpy(copy, start, length);
|
||||
copy[length] = '\0';
|
||||
return copy;
|
||||
}
|
||||
|
||||
static int
|
||||
AddId(CollectorRemoteIds *ids, const char *uid, size_t uid_length,
|
||||
const char *locator, size_t locator_length)
|
||||
{
|
||||
CollectorRemoteId *larger;
|
||||
char *uid_copy;
|
||||
char *locator_copy;
|
||||
if (uid_length == 0 || locator_length == 0) return 0;
|
||||
if (ids->count >= COLLECTOR_REMOTE_IDS_MAX) return -1;
|
||||
uid_copy = CopyToken(uid, uid_length);
|
||||
locator_copy = CopyToken(locator, locator_length);
|
||||
if (uid_copy == NULL || locator_copy == NULL) {
|
||||
free(uid_copy);
|
||||
free(locator_copy);
|
||||
return -1;
|
||||
}
|
||||
larger = realloc(ids->items, sizeof(*ids->items) * (ids->count + 1));
|
||||
if (larger == NULL) {
|
||||
free(copy);
|
||||
free(uid_copy);
|
||||
free(locator_copy);
|
||||
return -1;
|
||||
}
|
||||
ids->items = larger;
|
||||
ids->items[ids->count++] = copy;
|
||||
ids->items[ids->count].uid = uid_copy;
|
||||
ids->items[ids->count].locator = locator_copy;
|
||||
ids->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -77,13 +104,18 @@ ParsePop3Ids(char *response, CollectorRemoteIds *ids)
|
||||
char *save = NULL;
|
||||
for (line = strtok_r(response, "\r\n", &save); line != NULL;
|
||||
line = strtok_r(NULL, "\r\n", &save)) {
|
||||
char *number_end;
|
||||
char *uid;
|
||||
size_t number_length;
|
||||
while (isspace((unsigned char) *line)) line++;
|
||||
if (!isdigit((unsigned char) *line)) continue;
|
||||
uid = line;
|
||||
while (*uid != '\0' && !isspace((unsigned char) *uid)) uid++;
|
||||
number_end = line;
|
||||
while (*number_end != '\0' && !isspace((unsigned char) *number_end)) number_end++;
|
||||
number_length = (size_t) (number_end - line);
|
||||
uid = number_end;
|
||||
while (isspace((unsigned char) *uid)) uid++;
|
||||
if (*uid != '\0' && AddId(ids, uid, strcspn(uid, " \t")) != 0) return -1;
|
||||
if (*uid != '\0' && AddId(ids, uid, strcspn(uid, " \t"),
|
||||
line, number_length) != 0) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -101,7 +133,8 @@ ParseImapIds(char *response, CollectorRemoteIds *ids)
|
||||
if (!isdigit((unsigned char) *token)) break;
|
||||
end = token;
|
||||
while (isdigit((unsigned char) *end)) end++;
|
||||
if (AddId(ids, token, (size_t) (end - token)) != 0) return -1;
|
||||
if (AddId(ids, token, (size_t) (end - token), token,
|
||||
(size_t) (end - token)) != 0) return -1;
|
||||
token = end;
|
||||
}
|
||||
return 0;
|
||||
@@ -112,12 +145,107 @@ CollectorRemoteIdsFree(CollectorRemoteIds *ids)
|
||||
{
|
||||
size_t index;
|
||||
if (ids == NULL) return;
|
||||
for (index = 0; index < ids->count; index++) free(ids->items[index]);
|
||||
for (index = 0; index < ids->count; index++) {
|
||||
free(ids->items[index].uid);
|
||||
free(ids->items[index].locator);
|
||||
}
|
||||
free(ids->items);
|
||||
ids->items = NULL;
|
||||
ids->count = 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
WriteMessage(char *data, size_t size, size_t count, void *context)
|
||||
{
|
||||
MessageWriter *writer = context;
|
||||
size_t bytes;
|
||||
if (size != 0 && count > SIZE_MAX / size) return 0;
|
||||
bytes = size * count;
|
||||
if (bytes > COLLECTOR_REMOTE_MESSAGE_MAX - writer->length) {
|
||||
writer->overflow = 1;
|
||||
return 0;
|
||||
}
|
||||
if (bytes != 0 && fwrite(data, 1, bytes, writer->file) != bytes) return 0;
|
||||
writer->length += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int
|
||||
CollectorRemoteFetch(const CollectorExternalAccount *account,
|
||||
const char *secret, const CollectorRemoteId *id,
|
||||
FILE *message, size_t *message_size,
|
||||
char *error, size_t error_size)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode code;
|
||||
MessageWriter writer = {0};
|
||||
char curl_error[CURL_ERROR_SIZE] = {0};
|
||||
char url[896];
|
||||
char *folder = NULL;
|
||||
char *locator = NULL;
|
||||
const char *scheme;
|
||||
int result = -1;
|
||||
|
||||
if (account == NULL || secret == NULL || id == NULL || id->locator == NULL ||
|
||||
message == NULL || message_size == NULL ||
|
||||
strpbrk(account->inbound_host, "/?#@\r\n") != NULL) {
|
||||
SetError(error, error_size, "invalid external message fetch parameters");
|
||||
return -1;
|
||||
}
|
||||
*message_size = 0;
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL) return -1;
|
||||
locator = curl_easy_escape(curl, id->locator, 0);
|
||||
if (locator == NULL) goto done;
|
||||
if (strcmp(account->inbound_protocol, "imap") == 0) {
|
||||
scheme = strcmp(account->inbound_tls, "implicit") == 0 ? "imaps" : "imap";
|
||||
folder = curl_easy_escape(curl,
|
||||
account->destination_folder[0] != '\0' ? account->destination_folder : "INBOX", 0);
|
||||
if (folder == NULL || snprintf(url, sizeof(url), "%s://%s:%d/%s;UID=%s",
|
||||
scheme, account->inbound_host, account->inbound_port, folder, locator) >= (int) sizeof(url)) goto done;
|
||||
} else if (strcmp(account->inbound_protocol, "pop3") == 0) {
|
||||
scheme = strcmp(account->inbound_tls, "implicit") == 0 ? "pop3s" : "pop3";
|
||||
if (snprintf(url, sizeof(url), "%s://%s:%d/%s", scheme,
|
||||
account->inbound_host, account->inbound_port, locator) >= (int) sizeof(url)) goto done;
|
||||
} else {
|
||||
SetError(error, error_size, "unsupported external account protocol");
|
||||
goto done;
|
||||
}
|
||||
writer.file = message;
|
||||
rewind(message);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_USERNAME, account->inbound_username);
|
||||
curl_easy_setopt(curl, CURLOPT_PASSWORD, secret);
|
||||
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMessage);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writer);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error);
|
||||
code = curl_easy_perform(curl);
|
||||
if (code != CURLE_OK) {
|
||||
SetError(error, error_size, writer.overflow ? "external message exceeds 100 MiB" :
|
||||
(curl_error[0] != '\0' ? curl_error : curl_easy_strerror(code)));
|
||||
goto done;
|
||||
}
|
||||
if (fflush(message) != 0 || writer.length == 0) {
|
||||
SetError(error, error_size, "external server returned an empty message");
|
||||
goto done;
|
||||
}
|
||||
*message_size = writer.length;
|
||||
SetError(error, error_size, "");
|
||||
result = 0;
|
||||
|
||||
done:
|
||||
if (folder != NULL) curl_free(folder);
|
||||
if (locator != NULL) curl_free(locator);
|
||||
curl_easy_cleanup(curl);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
CollectorRemoteList(const CollectorExternalAccount *account,
|
||||
const char *secret, CollectorRemoteIds *ids,
|
||||
|
||||
@@ -2,17 +2,27 @@
|
||||
#define BONGO_COLLECTOR_EXTERNAL_TRANSPORT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "external_accounts.h"
|
||||
|
||||
typedef struct {
|
||||
char **items;
|
||||
char *uid;
|
||||
char *locator;
|
||||
} CollectorRemoteId;
|
||||
|
||||
typedef struct {
|
||||
CollectorRemoteId *items;
|
||||
size_t count;
|
||||
} CollectorRemoteIds;
|
||||
|
||||
int CollectorRemoteList(const CollectorExternalAccount *account,
|
||||
const char *secret, CollectorRemoteIds *ids,
|
||||
char *error, size_t error_size);
|
||||
int CollectorRemoteFetch(const CollectorExternalAccount *account,
|
||||
const char *secret, const CollectorRemoteId *id,
|
||||
FILE *message, size_t *message_size,
|
||||
char *error, size_t error_size);
|
||||
void CollectorRemoteIdsFree(CollectorRemoteIds *ids);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user