diff --git a/src/agents/collector/collector.c b/src/agents/collector/collector.c index 0468711..5777f7a 100644 --- a/src/agents/collector/collector.c +++ b/src/agents/collector/collector.c @@ -41,6 +41,64 @@ CollectorGlobals Collector = {0}; +static size_t DiscardCurlData(char *ptr, size_t size, size_t count, void *data) +{ + (void) ptr; (void) data; + return size * count; +} + +static int ProbeExternalAccount(sqlite3 *database, + const CollectorExternalAccount *account, + long now) +{ + CURL *curl = NULL; + char secret[512]; + char url[768]; + char error[CURL_ERROR_SIZE] = {0}; + const char *scheme; + CURLcode code; + int secretResult; + + if (account->inbound_protocol[0] == '\0' || account->inbound_host[0] == '\0' || + account->inbound_secret_id[0] == '\0') { + CollectorAccountsMarkResult(database, account->id, now, 0, + "external account is missing transport credentials"); + return -1; + } + secretResult = CollectorAccountsSecretGet(database, account->inbound_secret_id, + secret, sizeof(secret)); + if (secretResult != 1) { + CollectorAccountsMarkResult(database, account->id, now, 0, + "external account secret not found or too long"); + return -1; + } + scheme = strcmp(account->inbound_protocol, "imap") == 0 ? "imap" : "pop3"; + if (snprintf(url, sizeof(url), "%s%s://%s:%d/%s", scheme, + strcmp(account->inbound_tls, "implicit") == 0 ? "s" : "", + account->inbound_host, account->inbound_port, + account->destination_folder[0] != '\0' ? account->destination_folder : "INBOX") >= (int) sizeof(url)) { + CollectorAccountsMarkResult(database, account->id, now, 0, "external account URL too long"); + return -1; + } + curl = curl_easy_init(); + if (curl == NULL) return -1; + 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_CONNECTTIMEOUT, 30L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DiscardCurlData); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); + code = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (code != CURLE_OK) { + CollectorAccountsMarkResult(database, account->id, now, 0, + error[0] != '\0' ? error : curl_easy_strerror(code)); + return -1; + } + return CollectorAccountsMarkResult(database, account->id, now, 1, NULL); +} + static void CollectorLoop(void *ignored) { @@ -61,6 +119,10 @@ CollectorLoop(void *ignored) Log(LOG_ERROR, "Unable to query due external mail accounts"); } else if (due > 0) { Log(LOG_DEBUG, "%d external mail account(s) are due for collection", due); + CollectorExternalAccount account; + if (CollectorAccountsNextDue(accounts, (long) now, &account) == 1) { + ProbeExternalAccount(accounts, &account, (long) now); + } } } if (lastCalendarCollection == 0 || now - lastCalendarCollection >= 6 * 60 * 60) { diff --git a/src/agents/collector/external_accounts.c b/src/agents/collector/external_accounts.c index 47ba0de..ef64399 100644 --- a/src/agents/collector/external_accounts.c +++ b/src/agents/collector/external_accounts.c @@ -82,7 +82,7 @@ CollectorAccountsNextDue(sqlite3 *database, long now, sqlite3_stmt *statement = NULL; const char query[] = "SELECT id, owner, email_address, inbound_protocol, inbound_host, " - "inbound_port, inbound_tls, inbound_username, destination_folder, " + "inbound_port, inbound_tls, inbound_username, inbound_secret_id, destination_folder, " "delete_policy, COALESCE(delete_after_days, 0) " "FROM external_accounts WHERE enabled = 1 AND inbound_protocol != 'none' " "AND (last_checked_at IS NULL OR last_checked_at + poll_interval <= ?) " @@ -116,9 +116,10 @@ CollectorAccountsNextDue(sqlite3 *database, long now, account->inbound_port = sqlite3_column_int(statement, 5); COPY_ACCOUNT_FIELD(6, inbound_tls); COPY_ACCOUNT_FIELD(7, inbound_username); - COPY_ACCOUNT_FIELD(8, destination_folder); - COPY_ACCOUNT_FIELD(9, delete_policy); - account->delete_after_days = sqlite3_column_int(statement, 10); + COPY_ACCOUNT_FIELD(8, inbound_secret_id); + COPY_ACCOUNT_FIELD(9, destination_folder); + COPY_ACCOUNT_FIELD(10, delete_policy); + account->delete_after_days = sqlite3_column_int(statement, 11); #undef COPY_ACCOUNT_FIELD sqlite3_finalize(statement); return 1; diff --git a/src/agents/collector/external_accounts.h b/src/agents/collector/external_accounts.h index 1f2e6d9..2b4cccd 100644 --- a/src/agents/collector/external_accounts.h +++ b/src/agents/collector/external_accounts.h @@ -14,6 +14,7 @@ typedef struct { int inbound_port; char inbound_tls[16]; char inbound_username[256]; + char inbound_secret_id[128]; char destination_folder[256]; char delete_policy[16]; int delete_after_days;