summaryrefslogtreecommitdiff
path: root/src/pdu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdu.c')
-rw-r--r--src/pdu.c306
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;
+}