diff options
Diffstat (limited to 'src/sms_list.c')
-rw-r--r-- | src/sms_list.c | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/src/sms_list.c b/src/sms_list.c new file mode 100644 index 0000000..0f9a9e6 --- /dev/null +++ b/src/sms_list.c @@ -0,0 +1,502 @@ +/* + * SMS List Messages + * + * 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 "global.h" +#include "cmd_options.h" +#include "sms_utils.h" +#include "list.h" +#include "sms_list.h" +#include "pdu_decode.h" +#include "utils.h" +#include "atcmd.h" + +#define CMGL_HEADER_START "+CMGL: " +#define CMGR_HEADER_START "+CMGR: " + +static int process_header(struct msg_list_info *list_info, char *buf, size_t len) +{ + char *save = buf; + char *token; + + struct sms_msg *msg; + int index; + int status; + char *name; + int msg_len; + + token = atcmd_response_brk(&save); + if (!token) { + log_debug("response tokenizing failed at start"); + return -1; + } + + switch (list_info->cmd_type) { + case CMD_TYPE_CMGL: + token = atcmd_value_tok(&save); + if (!token) { + log_debug("response tokenizing failed at index"); + return -2; + } + index = atoi(token); + + break; + case CMD_TYPE_CMGR: + index = list_info->index; + break; + default: + return false; + } + + token = atcmd_value_tok(&save); + if (!token) { + log_debug("response tokenizing failed at msg-status"); + return -3; + } + status = atoi(token); + + token = atcmd_value_tok(&save); + if (!token) { + log_debug("response tokenizing failed at name"); + return -4; + } + name = token; + + token = atcmd_value_tok(&save); + if (!token) { + log_debug("response tokenizing failed at msg-len"); + return -5; + } + msg_len = atoi(token); + + switch (status) { + case SMS_MSG_REC_UNREAD: + list_info->nr_unread++; + break; + case SMS_MSG_REC_READ: + list_info->nr_read++; + break; + case SMS_MSG_STO_UNSENT: + list_info->nr_unsent++; + break; + case SMS_MSG_STO_SENT: + list_info->nr_sent++; + break; + default: + log_warning("msg-status unknown"); + list_info->nr_unknown++; + } + + msg = sms_msg_alloc(); + if (!msg) { + log_error("out of memory"); + return -6; + } + + memset(msg, 0, sizeof(*msg)); + + msg->index = index; + msg->status = status; + snprintf(msg->name, sizeof(msg->name), "%s", name); + msg->len = msg_len; + + list_add_tail(&msg->list, &list_info->msg_list); + + list_info->nr_msgs++; + + return 0; +} + +static int print_list_yaml(struct msg_list_info *list_info) +{ + struct sms_msg *msg; + char buf[64]; + int indent; + + indent = 0; + + indentf(indent, "---\n"); + indentf(indent, "\n"); + + indentf(indent, "messages:\n"); + + list_for_each_entry(msg, &list_info->msg_list, list) { + indent = YAML_INDENT; + + indentf(indent, "- index: %d\n", msg->index); + indent += YAML_INDENT; + + indentf(indent, "message-status: %d\n", msg->status); + indentf(indent, "name: %s\n", msg->name); + if (Global.core.verbose) { + indentf(indent, "length: %d\n", msg->len); + } + + indentf(indent, "pdu:\n"); + indent += YAML_INDENT; + + if (Global.core.verbose) { + indentf(indent, "message-length: %d\n", msg->pdu.msg_len); + } + + if (Global.core.verbose) { + indentf(indent, "smsc-addr-length: 0x%02X\n", msg->pdu.smsc_addr.len); + } + indentf(indent, "smsc-addr-type: 0x%02X\n", msg->pdu.smsc_addr.type); + indentf(indent, "smsc-addr: %s\n", msg->pdu.smsc_addr.addr); + + indentf(indent, "type: 0x%02X\n", msg->pdu.type.type); + indentf(indent, "message-reference: 0x%02X\n", msg->pdu.msg_reference); + indentf(indent, "protocol-id: 0x%02X\n", msg->pdu.protocol_id); + + if (Global.core.verbose) { + indentf(indent, "addr-length: 0x%02X\n", msg->pdu.addr.len); + } + indentf(indent, "addr-type: 0x%02X\n", msg->pdu.addr.type); + indentf(indent, "addr: %s\n", msg->pdu.addr.addr); + + pdu_format_vp(&msg->pdu, buf, sizeof(buf)); + indentf(indent, "validity-period: %s\n", buf); + pdu_format_timestamp(&msg->pdu, buf, sizeof(buf), "%Y-%m-%d %T %z"); + indentf(indent, "timestamp: %s\n", buf); + + if (Global.core.verbose) { + indentf(indent, "user-data-length: 0x%02X\n", msg->pdu.user_data_len); + } + indentf(indent, "user-data: \"%.*J\"\n", msg->pdu.user_data_len, msg->pdu.user_data); + indentf(indent, "\n"); + } + + indent = 0; + + if (Global.core.verbose) { + indentf(indent, "\n"); + + indentf(indent, "statistics:\n"); + indent += YAML_INDENT; + + indentf(indent, "unread: %d\n", list_info->nr_unread); + indentf(indent, "read: %d\n", list_info->nr_read); + indentf(indent, "unsent: %d\n", list_info->nr_unsent); + indentf(indent, "sent: %d\n", list_info->nr_sent); + indentf(indent, "unknown: %d\n", list_info->nr_unknown); + indentf(indent, "total: %d\n", list_info->nr_msgs); + indentf(indent, "\n"); + } + + indent = 0; + + indentf(indent, "...\n"); + + return 1; +} + +enum { + LIST_STATE_BLANK, + LIST_STATE_OPEN, + LIST_STATE_PDU, + LIST_STATE_CLOSE, +}; + +static int list_info_callback(char *buf, size_t len, void *data) +{ + int err; + struct msg_list_info *list_info = (struct msg_list_info *) data; + struct sms_msg *msg; + + list_info->nr_lines++; + + switch (list_info->state) { + case LIST_STATE_BLANK: + log_debug("state: LIST_STATE_BLANK"); + + if (*buf) { + list_info->state = LIST_STATE_CLOSE; + log_debug("AT response error: %s", buf); + return -1; + } + + list_info->state = LIST_STATE_OPEN; + + return 0; + + case LIST_STATE_OPEN: + log_debug("state: LIST_STATE_OPEN"); + + if (!strncmp(buf, CMGL_HEADER_START, strlen(CMGL_HEADER_START)) || + !strncmp(buf, CMGR_HEADER_START, strlen(CMGR_HEADER_START))) { + list_info->state = LIST_STATE_PDU; + + return process_header(list_info, buf, len); + } else if (!strcmp(buf, "OK")) { + list_info->state = LIST_STATE_CLOSE; + return 1; + } else if (*buf) { + list_info->state = LIST_STATE_CLOSE; + + log_debug("AT response error: %s", buf); + + return -1; + } else { + return 0; + } + + case LIST_STATE_PDU: + log_debug("state: LIST_STATE_PDU"); + + if (list_empty(&list_info->msg_list)) { + log_debug("empty list"); + return -1; + } + + msg = list_entry(list_info->msg_list.prev, typeof(*msg), list); + if (msg->len > 0) { + err = pdu_decode(buf, &msg->pdu); + if (err < 0) { + log_warning("pdu decode failed: %d", err); + } + } + + list_info->state = LIST_STATE_OPEN; + + return 0; + + default: + return -1; + } + + return -1; +} + +void sms_list_free(struct msg_list_info *list_info) +{ + struct sms_msg *msg; + struct sms_msg *msg_save; + + list_for_each_entry_safe(msg, msg_save, &list_info->msg_list, list) { + list_del(&msg->list); + sms_msg_free(msg); + } +} + +int sms_list_get(int fd, struct msg_list_info *list_info) +{ + int tmp; + + INIT_LIST_HEAD(&list_info->msg_list); + + list_info->state = LIST_STATE_BLANK; + list_info->nr_lines = 0; + list_info->nr_unread = 0; + list_info->nr_read = 0; + list_info->nr_unsent = 0; + list_info->nr_sent = 0; + list_info->nr_unknown = 0; + list_info->nr_msgs = 0; + + tmp = atcmd_plus_cmgf_write(fd, SMS_PDU_MODE); + if (tmp < 0) { + log_error("setting pdu mode failed"); + return -1; + } + + switch (list_info->cmd_type) { + case CMD_TYPE_CMGL: + atcmd_writeline(fd, "AT+CMGL=%d", list_info->status); + break; + + case CMD_TYPE_CMGR: + atcmd_writeline(fd, "AT+CMGR=%d", list_info->index); + break; + + default: + return -1; + } + + tmp = atcmd_response_foreach_line(fd, list_info_callback, list_info); + if (tmp < 0) { + log_error("listing failed"); + sms_list_free(list_info); + + return tmp; + } + + return 0; +} + +static int do_list(int fd, int cmd_type, int argc, char **argv) +{ + int tmp; + char *arg; + struct msg_list_info list_info; + + memset(&list_info, 0, sizeof(list_info)); + + if (argc < 1) { + log_error("msg-status or index expected"); + return false; + } + arg = *argv; + argc--; argv++; + + list_info.cmd_type = cmd_type; + + switch (list_info.cmd_type) { + case CMD_TYPE_CMGL: + if (!strcmp(arg, "unread")) { + list_info.status = SMS_MSG_REC_UNREAD; + } else if (!strcmp(arg, "read")) { + list_info.status = SMS_MSG_REC_READ; + } else if (!strcmp(arg, "unsent")) { + list_info.status = SMS_MSG_STO_UNSENT; + } else if (!strcmp(arg, "sent")) { + list_info.status = SMS_MSG_STO_SENT; + } else if (!strcmp(arg, "all")) { + list_info.status = SMS_MSG_ALL; + } else { + log_error("invalid msg-status %s", arg); + return false; + } + + break; + + case CMD_TYPE_CMGR: + list_info.index = atoi(arg); + break; + + default: + return false; + } + + tmp = sms_list_get(fd, &list_info); + if (tmp < 0) { + log_error("failed to get msg list"); + return false; + } + + print_list_yaml(&list_info); + + sms_list_free(&list_info); + + return true; +} + +static char *short_options = ""; +static struct option long_options[] = { + {NULL, 0, NULL, 0}, +}; + +void sms_list_help(FILE *out) { + fprintf(out, "usage: list STATUS\n"); + fprintf(out, "where STATUS := { \n"); + fprintf(out, " unread\n"); + fprintf(out, " read\n"); + fprintf(out, " unsent\n"); + fprintf(out, " sent\n"); + fprintf(out, " all\n"); + fprintf(out, " }\n"); + fprintf(out, "\n"); +} + +void sms_read_help(FILE *out) { + fprintf(out, "usage: read <index>\n"); + fprintf(out, "\n"); +} + +static void help_select(FILE *out, int cmd_type) +{ + switch (cmd_type) { + case CMD_TYPE_CMGL: + sms_list_help(out); + break; + case CMD_TYPE_CMGR: + sms_read_help(out); + break; + default: + fprintf(out, "usage: ?\n"); + } +} + +int sms_list(int argc, char **argv) +{ + int i; + int option_index; + int ret; + int fd; + + if (argc < 1) { + log_debug("should have received at least one argument"); + return false; + } + + char *object = *argv; + int cmd_type; + + if (!tokcmp(object, "list")) { + cmd_type = CMD_TYPE_CMGL; + } else if (!tokcmp(object, "read")) { + cmd_type = CMD_TYPE_CMGR; + } else { + log_debug("object should be one of { list | read }"); + return false; + } + + while ((i = getopt_long(argc, argv, short_options, long_options, &option_index)) >= 0) { + switch (i) { + case 0: + break; + + default: + help_select(stderr, cmd_type); + return false; + } + } + + if (optind >= argc) { + help_select(stderr, cmd_type); + return false; + } + argc -= optind; + argv += optind; + + fd = tty_open(Global.core.device, Global.core.baud_rate); + if (fd < 0) { + fprintf(stderr, "failed to open tty device %s\n", Global.core.device); + return false; + } + atcmd_init(fd, Global.core.read_timeout); + + ret = do_list(fd, cmd_type, argc, argv); + + tty_close(fd); + + return ret; +} |