/* * 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; }