From 14fb44b17123b27e562379f51b75ee889982688d Mon Sep 17 00:00:00 2001 From: James Maki Date: Fri, 23 Apr 2010 11:58:20 -0500 Subject: initial commit --- src/sms_send_email.c | 488 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 src/sms_send_email.c (limited to 'src/sms_send_email.c') 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" + +#if HAVE_LIBESMTP +#include +#include +#include +#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), ¶ms); + 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, ¶ms); + 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, ¶ms); + 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 ... ] \n"); + fprintf(out, "where OPTIONS := { \n"); + fprintf(out, " -f, --file |\n"); + fprintf(out, " --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; +} -- cgit v1.2.3