[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 18/23] email alerts working


MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

From: arf20 <aruizfernandez05@xxxxxxxxx>

---
 alert.c             | 89 +++++++++++++++++++++++++++++++++++++++++----
 config.c            | 21 +++++++++--
 config.h            |  3 ++
 events.log          |  5 ---
 monitor.cfg.example | 11 ++++--
 5 files changed, 111 insertions(+), 18 deletions(-)

diff --git a/alert.c b/alert.c
index a1ba674..1499670 100644
--- a/alert.c
+++ b/alert.c
@@ -25,7 +25,6 @@ size_t alerts_size = 0, alerts_capacity = INIT_VEC_CAPACITY;
 
 static const char *type_str[] = { "api", "email" };
 static const char *status_str[] = { "down", "up" };
-static const char *from = NULL;
 
 
 static size_t
@@ -73,15 +72,93 @@ send_api(const target_t *target, const char *endpoint, const char *content_type,
 
     printf("%ld\n", http_code);
 
-    return http_code == 200 ? STATUS_UP : STATUS_DOWN;
+    return http_code;
 }
 
+typedef struct {
+    size_t bytes_read;
+    char *body;
+} email_send_status_t;
+
+static size_t
+read_cb(char *ptr, size_t size, size_t nmemb, void *userp)
+{
+    email_send_status_t *send_status = (email_send_status_t *)userp;
+    const char *data;
+    size_t len;
+ 
+    if ((size * nmemb) == 0) {
+        return 0;
+    }
+ 
+    data = &send_status->body[send_status->bytes_read];
+ 
+    len = strlen(data);
+    if(size * nmemb < len)
+        len = size * nmemb;
+    memcpy(ptr, data, len);
+    send_status->bytes_read += len;
+ 
+    return len;
+}
 
 static int
-send_email(const target_t *target, const char *address, const char *subject_tmpl,
-    const char *body_tmpl)
+send_email(const target_t *target, const char *address,
+    const char *subject_tmpl, const char *body_tmpl)
 {
+    static char buff[4096], buff2[1024], buff3[1024], timestr[256];
+
+    CURL *curl = curl_easy_init();
+    if (!curl) {
+        fprintf(stderr, "Error allocating cURL handle\n");
+        return -1;
+    }
+
+    curl_easy_setopt(curl, CURLOPT_URL, alert_config.mail_server);
+    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, alert_config.from);
+
+    curl_easy_setopt(curl, CURLOPT_USERNAME, alert_config.user);
+    curl_easy_setopt(curl, CURLOPT_PASSWORD, alert_config.password);
+
     
+    time_t now = time(NULL);
+    struct tm *tm_now = gmtime(&now);
+    strftime(timestr, 256, "%a, %d %b %Y %T %z", tm_now);
+
+    snprintf(buff2, 1024, subject_tmpl, target->name,
+        status_str[target->status]);
+    snprintf(buff3, 1024, body_tmpl, target->name, status_str[target->status]);
+    snprintf(buff, 4096, "Date: %s\r\nTo: %s\r\nFrom: %s\r\n"
+        "Subject: %s\r\n\r\n%s\r\n", timestr, address, alert_config.from,
+        buff2, buff3);
+
+    email_send_status_t send_status = {
+        .bytes_read = 0,
+        .body = buff
+    };
+    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb);
+    curl_easy_setopt(curl, CURLOPT_READDATA, &send_status);
+    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
+    struct curl_slist *recipients = NULL;
+    recipients = curl_slist_append(recipients, address);
+    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
+
+    CURLcode curl_code = curl_easy_perform(curl);
+    if (curl_code != CURLE_OK) {
+        printf("curl_easy_perform() failed: %s\n",
+            curl_easy_strerror(curl_code));
+        return STATUS_DOWN;
+    }
+
+    long resp_code;
+    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp_code);
+
+    curl_easy_cleanup(curl);
+
+    printf("%ld\n", resp_code);
+
+    return resp_code;
 }
 
 
@@ -90,8 +167,6 @@ alert_init()
 {
     alerts = malloc(INIT_VEC_CAPACITY * sizeof(alert_t));
 
-    from = alert_config.from;
-    
     printf("alerts:\n");
 
     char line[256];
@@ -157,7 +232,7 @@ alert_trigger(const target_t *target)
     strftime(timestr, 256, "%F %T", tm_now);
 
     for (int i = 0; i < alerts_size; i++) {
-        printf("[%s] [monitor] alerted %.16s about %s\n",
+        printf("[%s] [monitor] alerted %.16s about %s: ",
             timestr, alerts[i].target, target->name);
         send_funcs[alerts[i].type](target, alerts[i].target, alerts[i].extra,
             alerts[i].body_tmpl);
diff --git a/config.c b/config.c
index 39dc1b9..1d485d7 100644
--- a/config.c
+++ b/config.c
@@ -8,7 +8,7 @@
 unsigned short port = DEFAULT_PORT;
 char *log_path = DEFAULT_LOG_PATH;
 monitor_config_t monitor_config = { .interval = DEFAULT_INTERVAL };
-alert_config_t   alert_config;
+alert_config_t   alert_config = { 0 };
 
 int
 config_load(const char *conf_path)
@@ -64,11 +64,23 @@ config_load(const char *conf_path)
             value[strlen(value) - 1] = '\0';
             log_path = strdup(value);
             printf("\tlog path: %s\n", log_path);
-        } else if (strcmp(line, "from") == 0) {
+        } else if (strcmp(line, "mailserver") == 0) {
+            value[strlen(value) - 1] = '\0';
+            alert_config.mail_server = strdup(value);
+            printf("\tmailserver: %s\n", alert_config.mail_server);
+        } else if (strcmp(line, "mailfrom") == 0) {
             value[strlen(value) - 1] = '\0';
             alert_config.from = strdup(value);
             printf("\tfrom: %s\n", log_path);
-        } else if (strcmp(line, "target") == 0) {
+        } else if (strcmp(line, "mailuser") == 0) {
+            value[strlen(value) - 1] = '\0';
+            alert_config.user = strdup(value);
+        } else if (strcmp(line, "mailpassword") == 0) {
+            value[strlen(value) - 1] = '\0';
+            alert_config.password = strdup(value);
+        }
+
+        else if (strcmp(line, "target") == 0) {
             target_pos += snprintf(target_pos,
                 cfgsize - (target_pos - monitor_config.target_config),
                 "%s", value);
@@ -85,6 +97,9 @@ config_load(const char *conf_path)
 
     fclose(cfgf);
 
+    if (!alert_config.from || !alert_config.mail_server)
+        fprintf(stderr, "[config] W: no mail\n");
+
     return 0;
 }
 
diff --git a/config.h b/config.h
index dbcfb97..0c0b502 100644
--- a/config.h
+++ b/config.h
@@ -19,7 +19,10 @@ typedef struct {
 } monitor_config_t;
 
 typedef struct {
+    char *mail_server;
     char *from;
+    char *user;
+    char *password;
     char *alert_config;
 } alert_config_t;
 
diff --git a/events.log b/events.log
index c481d10..8399053 100644
--- a/events.log
+++ b/events.log
@@ -78,8 +78,3 @@ dns,2025-10-27T15:43:17+0000,up
 https,2025-10-27T15:43:17+0000,up
 http,2025-11-04T18:39:12Z,down
 http,2025-11-04T18:40:12Z,up
-test,2025-11-10T12:34:07+0000,down
-test,2025-11-10T12:42:29+0000,up
-test,2025-11-10T12:42:54+0000,down
-test,2025-11-10T12:45:09+0000,up
-test,2025-11-10T12:46:50+0000,down
diff --git a/monitor.cfg.example b/monitor.cfg.example
index 226a88e..821f108 100644
--- a/monitor.cfg.example
+++ b/monitor.cfg.example
@@ -15,13 +15,18 @@ target=reach,ipv4,2.59.235.35
 target=dns,dns,arf20.com
 target=web,http,http://arf20.com
 target=web,https,https://arf20.com
+target=web,test,http://localhost:8989
 
 # email From
-from=status@xxxxxxxxx
+mailfrom=ARFNET Status Monitor <status@xxxxxxxxx>
+mailserver=smtps://mail.example.com:465
+mailuser=username
+mailpassword=password
 
 # what to alert
 # alert=api,<url>,<content-type>,<body template>
-alert=api,https://arf20.com/%s,application/json,{"content":"%s is %s"}
+alert=api,https://discord.com/api/webhooks/example,application/json,{"content":"%s is %s"}
+alert=api,https://api.telegram.org/botexample/sendMessage,application/json,{"chat_id": "exampleroom", "text": "%s is %s", "disable_notification": false}
 # alert=email,<address>,<subject template>,<body template>
-alert=email,arf20@xxxxxxxxx,%s is %s,%s is %s
+alert=email,it@xxxxxxxxxxx,%s is %s,%s is %s
 
-- 
2.47.3


References:
[arfnet2-status PATCH 00/23] First releasearf20 <arf20@xxxxxxxxx>