summaryrefslogtreecommitdiff
path: root/src/sms_send_email.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sms_send_email.c')
-rw-r--r--src/sms_send_email.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/src/sms_send_email.c b/src/sms_send_email.c
new file mode 100644
index 0000000..90f9296
--- /dev/null
+++ b/src/sms_send_email.c
@@ -0,0 +1,488 @@
+/*
+ * Send SMS through email
+ *
+ * Copyright (C) 2010 by Multi-Tech Systems
+ *
+ * Author: James Maki <jmaki@multitech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "global.h"
+
+#if HAVE_LIBESMTP
+#include <openssl/ssl.h>
+#include <auth-client.h>
+#include <libesmtp.h>
+#endif
+
+#include "utils.h"
+#include "cmd_options.h"
+#include "sms_utils.h"
+#include "atcmd.h"
+#include "phonebook.h"
+
+struct send_options {
+ char *file;
+ char *subject;
+};
+
+#if HAVE_LIBESMTP
+static FILE *open_message_file(struct send_options *options)
+{
+ FILE *fp;
+ int tmp;
+
+ if (options->file) {
+ fp = fopen(options->file, "r");
+ if (!fp) {
+ log_error("failed to open %s for reading", options->file);
+ return NULL;
+ }
+ } else if (Global.core.interactive && Global.core.edit_file && Global.core.editor) {
+ tmp = systemf("%s %s", Global.core.editor, Global.core.edit_file);
+ if (tmp < 0 || !WIFEXITED(tmp) || WEXITSTATUS(tmp)) {
+ log_error("edit failed");
+ return NULL;
+ }
+
+ char *path = shell_path_expand(Global.core.edit_file);
+ if (!path) {
+ return NULL;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ log_error("failed to open %s for reading", path);
+ free(path);
+ return NULL;
+ }
+ free(path);
+ } else {
+ fp = stdin;
+ }
+
+ return fp;
+}
+
+char *fgets_noecho(char *buf, size_t len, FILE *stream)
+{
+ int err;
+ struct termios params;
+ char *ret = NULL;
+ tcflag_t c_lflag;
+
+ err = tcgetattr(fileno(stream), &params);
+ if (err < 0) {
+ log_error("tcgetattr failed: %m");
+ return NULL;
+ }
+
+ c_lflag = params.c_lflag;
+
+ params.c_lflag &= ~ECHO;
+ params.c_lflag |= ECHONL;
+
+ err = tcsetattr(fileno(stream), TCSAFLUSH, &params);
+ if (err < 0) {
+ log_error("tcsetattr failed: %m");
+ return NULL;
+ }
+
+ ret = fgets(buf, len, stream);
+
+ params.c_lflag = c_lflag;
+
+ err = tcsetattr(fileno(stream), TCSAFLUSH, &params);
+ if (err < 0) {
+ log_error("tcsetattr failed: %m");
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct auth_callback_data {
+ char user[128];
+ char passwd[128];
+ char realm[128];
+};
+
+int auth_callback(auth_client_request_t request, char **result, int fields, void *arg)
+{
+ struct auth_callback_data *data = (struct auth_callback_data *) arg;
+ char *cp;
+ int i;
+
+ for (i = 0; i < fields; i++) {
+ if (request[i].flags & AUTH_PASS) {
+ if (!Global.smtp.passwd) {
+ printf("Password: ");
+ fflush(stdout);
+
+ cp = fgets_noecho(data->passwd,
+ sizeof(data->passwd), stdin);
+ if (!cp) {
+ *data->passwd = '\0';
+ }
+
+ cp = strrchr(data->passwd, '\n');
+ if (cp) {
+ *cp = '\0';
+ }
+
+ result[i] = data->passwd;
+ } else {
+ result[i] = Global.smtp.passwd;
+ }
+ } else if (request[i].flags & AUTH_USER) {
+ if (!Global.smtp.user) {
+ printf("Username: ");
+ fflush(stdout);
+
+ cp = fgets(data->user, sizeof(data->user), stdin);
+ if (!cp) {
+ *data->user = '\0';
+ }
+
+ cp = strrchr(data->user, '\n');
+ if (cp) {
+ *cp = '\0';
+ }
+
+ result[i] = data->user;
+ } else {
+ result[i] = Global.smtp.user;
+ }
+ } else if (request[i].flags & AUTH_REALM) {
+ printf("Realm: ");
+ fflush(stdout);
+
+ cp = fgets(data->realm, sizeof(data->realm), stdin);
+ if (!cp) {
+ *data->realm = '\0';
+ }
+
+ cp = strrchr(data->realm, '\n');
+ if (cp) {
+ *cp = '\0';
+ }
+
+ result[i] = data->realm;
+ } else {
+ result[i] = NULL;
+ }
+ }
+
+ return 1;
+}
+
+int tls_callback(char *buf, int len, int flag, void *arg)
+{
+ char *cp;
+
+ printf("Certificate Password: ");
+ fflush(stdout);
+
+ cp = fgets_noecho(buf, len, stdin);
+ if (!cp) {
+ *buf = '\0';
+ }
+ cp = strrchr(buf, '\n');
+ if (cp) {
+ *cp = '\0';
+ }
+
+ return strlen(buf);
+}
+
+void event_callback(smtp_session_t session, int event, void *args, ...)
+{
+ va_list ap;
+ long long_arg;
+ int *ok;
+
+ va_start(ap, args);
+
+ switch (event) {
+ case SMTP_EV_CONNECT:
+ case SMTP_EV_MAILSTATUS:
+ case SMTP_EV_RCPTSTATUS:
+ case SMTP_EV_MESSAGEDATA:
+ case SMTP_EV_MESSAGESENT:
+ case SMTP_EV_DISCONNECT:
+ break;
+ case SMTP_EV_WEAK_CIPHER:
+ long_arg = va_arg(ap, long);
+ log_debug("SMTP_EV_WEAK_CIPHER: bits: %ld", long_arg);
+ ok = va_arg(ap, int *);
+ *ok = 1;
+ break;
+
+ case SMTP_EV_STARTTLS_OK:
+ log_debug("SMTP_EV_STARTTLS_OK");
+ break;
+
+ case SMTP_EV_INVALID_PEER_CERTIFICATE:
+ long_arg = va_arg(ap, long);
+ log_debug("SMTP_EV_INVALID_PEER_CERTIFICATE: error: %ld", long_arg);
+ ok = va_arg(ap, int *);
+ *ok = 1;
+ break;
+
+ case SMTP_EV_NO_PEER_CERTIFICATE:
+ log_debug("SMTP_EV_NO_PEER_CERTIFICATE");
+ ok = va_arg(ap, int *);
+ *ok = 1;
+ break;
+
+ case SMTP_EV_WRONG_PEER_CERTIFICATE:
+ log_debug("SMTP_EV_WRONG_PEER_CERTIFICATE");
+ ok = va_arg(ap, int *);
+ *ok = 1;
+ break;
+
+ case SMTP_EV_NO_CLIENT_CERTIFICATE:
+ log_debug("SMTP_EV_NO_CLIENT_CERTIFICATE");
+ ok = va_arg(ap, int *);
+ *ok = 1;
+ break;
+
+ default:
+ log_debug("unknown event %d", event);
+ }
+
+ va_end(ap);
+}
+
+static void print_recipient_status(smtp_recipient_t recipient,
+ const char *mailbox, void *arg)
+{
+ const smtp_status_t *status;
+
+ status = smtp_recipient_status(recipient);
+
+ printf(" - addr: %s\n", mailbox);
+ printf(" code: %d\n", status->code);
+ printf(" message: %s\n", status->text);
+}
+
+static int do_send_email(struct send_options *options, int argc, char **argv)
+{
+ char buf[1024];
+ struct sigaction sa;
+ struct auth_callback_data auth_arg;
+ FILE *fp = NULL;
+ int ret = false;
+ int tmp;
+ int i;
+
+ smtp_session_t session = NULL;
+ smtp_message_t message;
+ auth_context_t authctx = NULL;
+ const smtp_status_t *status;
+
+ auth_client_init();
+
+ if (argc < 1) {
+ log_error("at least one recipient addr is required");
+ goto done;
+ }
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ if (!Global.smtp.server) {
+ log_error("smtp config missing");
+ goto done;
+ }
+
+ session = smtp_create_session();
+ message = smtp_add_message(session);
+
+ if (Global.smtp.encryption && !strcmp(Global.smtp.encryption, "tls")) {
+ smtp_starttls_enable(session, Starttls_REQUIRED);
+ }
+
+ if (strchr(Global.smtp.server, ':') || !Global.smtp.port) {
+ snprintf(buf, sizeof(buf), "%s", Global.smtp.server);
+ } else {
+ snprintf(buf, sizeof(buf), "%s:%d",
+ Global.smtp.server, Global.smtp.port);
+ }
+ smtp_set_server(session, buf);
+
+ authctx = auth_create_context();
+ if (!authctx) {
+ log_error("auth_create_context failed");
+ goto done;
+ }
+ auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
+ auth_set_interact_cb(authctx, auth_callback, &auth_arg);
+ smtp_starttls_set_password_cb(tls_callback, NULL);
+ smtp_set_eventcb(session, event_callback, NULL);
+ smtp_auth_set_context(session, authctx);
+
+ if (Global.user.email) {
+ smtp_set_reverse_path(message, Global.user.email);
+ smtp_set_header(message, "From", Global.user.name,
+ Global.user.email);
+ }
+ if (options->subject) {
+ smtp_set_header(message, "Subject", options->subject);
+ }
+
+ fp = open_message_file(options);
+ if (!fp) {
+ log_error("opening sms message file failed");
+ goto done;
+ }
+
+ tmp = smtp_set_message_fp(message, fp);
+
+ int nr_rcpts = 0;
+ for (i = 0; i < argc; i++) {
+ if (strchr(argv[i], '@')) {
+ smtp_add_recipient(message, argv[i]);
+ nr_rcpts++;
+ } else if (Global.send_email.domain) {
+ snprintf(buf, sizeof(buf), "%s@%s",
+ argv[i], Global.send_email.domain);
+ smtp_add_recipient(message, buf);
+ nr_rcpts++;
+ } else {
+ log_notice("skipping recipient %s", argv[i]);
+ }
+ }
+
+ if (!nr_rcpts) {
+ log_error("email contains no recipients");
+ goto done;
+ }
+
+ tmp = smtp_start_session(session);
+ if (!tmp) {
+ smtp_strerror(smtp_errno(), buf, sizeof(buf));
+ log_error("smtp_start_session: %s", buf);
+ goto done;
+ }
+
+ status = smtp_message_transfer_status(message);
+ printf("transfer-status:\n");
+ printf(" - code: %d\n", status->code);
+ printf(" message: %s\n", status->text ?: "");
+ printf("recipient-status:\n");
+
+ smtp_enumerate_recipients(message, print_recipient_status, NULL);
+
+ if (status->code == 250 && Global.core.interactive && Global.core.edit_file) {
+ systemf("rm -f %s", Global.core.edit_file);
+ }
+
+ ret = true;
+
+done:
+ if (authctx) {
+ auth_destroy_context(authctx);
+ }
+ if (session) {
+ smtp_destroy_session(session);
+ }
+ if (fp) {
+ fclose(fp);
+ }
+
+ auth_client_exit();
+
+ return ret;
+}
+#else
+static int do_send_email(struct send_options *options, int argc, char **argv)
+{
+ return false;
+}
+#endif
+
+static char *short_options = __CMD_OPT_FILE;
+static struct option long_options[] = {
+ {"file", 1, NULL, CMD_OPT_FILE},
+ {"subject", 1, NULL, CMD_OPT_SUBJECT},
+ {NULL, 0, NULL, 0},
+};
+
+void sms_send_email_help(FILE *out) {
+ fprintf(out, "usage: send-email [ OPTIONS ... ] <number>\n");
+ fprintf(out, "where OPTIONS := { \n");
+ fprintf(out, " -f, --file <input-file> |\n");
+ fprintf(out, " --subject <email-subject>\n");
+ fprintf(out, " }\n");
+ fprintf(out, "\n");
+}
+
+int sms_send_email(int argc, char **argv)
+{
+ int i;
+ int option_index;
+ int ret;
+
+ struct send_options options;
+ options.file = NULL;
+ options.subject = NULL;
+
+ while ((i = getopt_long(argc, argv, short_options, long_options, &option_index)) >= 0) {
+ switch (i) {
+ case 0:
+ break;
+
+ case CMD_OPT_FILE:
+ options.file = optarg;
+ break;
+
+ case CMD_OPT_SUBJECT:
+ options.subject = optarg;
+ break;
+
+ default:
+ sms_send_email_help(stderr);
+ return false;
+ }
+ }
+
+ if (optind >= argc) {
+ sms_send_email_help(stderr);
+ return false;
+ }
+ argc -= optind;
+ argv += optind;
+
+ ret = do_send_email(&options, argc, argv);
+
+ return ret;
+}