diff options
Diffstat (limited to 'src/pdu.c')
-rw-r--r-- | src/pdu.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/pdu.c b/src/pdu.c new file mode 100644 index 0000000..2ceb7ae --- /dev/null +++ b/src/pdu.c @@ -0,0 +1,306 @@ +/* + * PDU common + * + * Copyright (C) 2010 by James Maki + * + * Author: James Maki <jamescmaki@gmail.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 + * + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <ctype.h> +#include <time.h> +#include <unistd.h> + +#include "config.h" +#include "log.h" +#include "pdu.h" +#include "sms_utils.h" + +int hex_nibble_scan(const char *buf, size_t len) +{ + static const char digits[] = "0123456789ABCDEF"; + + int i; + int result = 0; + + STRLEN_CHECK(buf, len, -1); + + for (i = 0; i < len; i++) { + const char *where; + + where = strchr(digits, toupper(buf[i])); + if (!where) { + return -1; + } + + result = (result << 4) + (where - digits); + } + + return result; +} + +int hex_byte_decode(const char *buf) +{ + return hex_nibble_scan(buf, HEX_BYTE_LEN); +} + +int hex_byte_encode(char *buf, size_t len, int byte) +{ + if (len < HEX_BYTE_LEN) { + log_error("buffer is not large enough"); + } + + return snprintf(buf, len, "%02X", byte & 0xFF); +} + +int nibble_swap(char *buf, size_t len) +{ + int i; + char c; + + if (len & 1) { + BUG("odd buffer size found"); + } + + for (i = 0; i < len; i += HEX_BYTE_LEN) { + c = buf[i]; + buf[i] = buf[i + 1]; + buf[i + 1] = c; + } + + return i; +} + +int strunpad(char *str, unsigned char c) +{ + char *cp = strchr(str, c); + + if (cp) { + *cp = '\0'; + } + + return strlen(str); +} + +int strpad(char *str, size_t len, unsigned char c) +{ + int i; + int start = strlen(str); + + for (i = start; i < len; i++) { + str[i] = c; + } + + str[i] = '\0'; + + return i - start; +} + +int pdu_format_timestamp(struct pdu_info *pdu, char *str, size_t len, const char *fmt) +{ + if (len <= 0) { + return 0; + } + + switch (pdu->type.msg_type) { + case PDU_MTI_DELIVER: + case PDU_MTI_REPORT: + return strftime(str, len, fmt, &pdu->timestamp); + default: + *str = '\0'; + return 0; + } +} + +int pdu_format_vp(struct pdu_info *pdu, char *str, size_t len) +{ + int val; + + if (len <= 0) { + return 0; + } + + *str = '\0'; + + val = pdu->validity_period; + + switch (pdu->type.msg_type) { + case PDU_MTI_SUBMIT: + if (val >= 0 && val <= 143) { + val = (val + 1) * 5; + } else if (val >= 144 && val <= 167) { + val = ((val - 143) * 30) + (12 * 60); + } else if (val >= 168 && val <= 196) { + val = (val - 166) * (24 * 60); + } else { + val = (val - 192) * (7 * 24 * 60); + } + + return snprintf(str, len, "%d", val); + default: + return 0; + } +} + +int pdu_addr_check(const char *addr_str) +{ + if (addr_str[0] == '+') { + addr_str++; + } + + if (!*addr_str) { + log_error("empty addr"); + return -1; + } + + int len = strlen(addr_str); + int bad = strcspn(addr_str, "1234567890"); + + if (bad != len) { + log_error("bad character in addr at offset %d", bad); + return -1; + } + + return 0; +} + +#define GWRITTEN_SEP(c) ((c) == '-' || (c) == '.') +#define NUM_ADDR "1234567890-." +#define CMD_ADDR "1234567890*#+-." + +int pdu_addr_type_infer(const char *str) +{ + if (*str == '+' && strlen(str + 1) == strspn(str + 1, NUM_ADDR)) { + log_debug("SMS_ADDR_GLOBAL"); + return SMS_ADDR_GLOBAL; + } else if (strlen(str) == strspn(str, NUM_ADDR)) { + log_debug("SMS_ADDR_LOCAL"); + return SMS_ADDR_LOCAL; + } else if (strlen(str) == strspn(str, CMD_ADDR)) { + log_debug("SMS_ADDR_CMD"); + return SMS_ADDR_CMD; + } else { + log_debug("SMS_ADDR_TEXT"); + return SMS_ADDR_TEXT; + } +} + +static int pdu_addr_clean_copy(struct pdu_addr *addr, const char *src) +{ + char *dest = addr->addr; + int count = 0; + int c; + + while((c = *src++)) { + if(isdigit(c)) { + if(count >= PDU_ADDR_SIZE - 1) { + log_error("addr exceeds max length"); + return -1; + } + dest[count++] = c; + } else if(GWRITTEN_SEP(c)) { + continue; + } else if(c == '+' && !count) { + continue; + } else { + log_error("addr contains invalid char %c at offset %d", c, count); + return -1; + } + } + + dest[count] = '\0'; + + if(!count) { + log_error("empty addr"); + return -1; + } + + return count; +} + +int pdu_addr_fill(struct pdu_addr *addr, const char *addr_str, int type) +{ + int tmp; + + memset(addr, 0 , sizeof(*addr)); + + addr->type = pdu_addr_type_infer(addr_str); + + tmp = pdu_addr_clean_copy(addr, addr_str); + if (tmp < 0) { + return tmp; + } + + log_debug("addr: %s", addr->addr); + + if (type) { + addr->type = type; + } + + log_debug("addr-type: 0x%02X", addr->type); + + return tmp; +} + +int pdu_user_data_read(int fd, struct pdu_info *pdu) +{ + int err; + char c; + int total = 0; + int len; + + if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { + len = PDU_UD_7BIT_MAX; + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_EIGHT) { + len = PDU_UD_8BIT_MAX; + } else { + return -1; + } + + while (1) { + err = read(fd, &c, 1); + if (err < 0) { + log_error("read failed: %m"); + return err; + } else if (err > 0) { + if (total < len) { + pdu->user_data[total++] = c; + } else { + log_debug("read max message length %d", total); + goto done; + } + } else { + log_debug("read eof at %d", total); + goto done; + } + } + +done: + + pdu->user_data[total] = '\0'; + pdu->user_data_len = total; + + debug_buffer("message is: ", pdu->user_data, total); + + return total; +} |