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/pdu_encode.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 src/pdu_encode.c (limited to 'src/pdu_encode.c') diff --git a/src/pdu_encode.c b/src/pdu_encode.c new file mode 100644 index 0000000..704bc5b --- /dev/null +++ b/src/pdu_encode.c @@ -0,0 +1,307 @@ +/* + * PDU Encode + * + * Copyright (C) 2010 by James Maki + * + * 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 "global.h" +#include "utils.h" +#include "pdu_encode.h" + +#define ENCODE_FAIL(cond, name, ret) \ +do { \ + if (cond) { \ + log_error("encode failed at %s", name); \ + return ret; \ + } \ +} while (0) + +int pdu_encode_timestamp(char *pdu_str, size_t len, struct tm *tm) +{ + int tmp; + int off_upper; + int off_lower; + int off; + int negative; + + if (len < PDU_TIMESTAMP_SIZE) { + log_error("buffer is not large enough to hold a timestamp"); + return -1; + } + + tmp = strftime(pdu_str, PDU_TIMESTAMP_SIZE, "%y%m%d%H%M%S", tm); + ENCODE_FAIL(tmp != GMT_OFFSET_IDX, "timestamp", -1); + + off = tm->tm_gmtoff; + if (off < 0) { + off = -off; + negative = 1; + } + off = off / 60 / 15; + + off_upper = off / 10; + off_lower = off % 10; + + if (negative) { + off_upper |= BIT(3); + } + + snprintf(pdu_str + GMT_OFFSET_IDX, 2, "%X", off_upper & 0xF); + snprintf(pdu_str + GMT_OFFSET_IDX + 1, 2, "%X", off_lower & 0xF); + + nibble_swap(pdu_str, PDU_TIMESTAMP_LEN); + strpad(pdu_str, PDU_TIMESTAMP_LEN, 'F'); + + log_debug("%s", pdu_str); + + return PDU_TIMESTAMP_LEN; +} + +int pdu_encode_addr(char *pdu_str, size_t len, struct pdu_addr *addr, int smsc) +{ + char *begin = pdu_str; + int addr_len; + int tmp; + + addr_len = strlen(addr->addr); + + if (smsc) { + if (addr_len) { + if (addr_len & 1) { + addr_len++; + } + addr_len = addr_len / HEX_BYTE_LEN + 1; + } + } + + log_debug("addr-len [transformed]: 0x%02X", addr_len); + addr->len = addr_len; + + tmp = hex_byte_encode(pdu_str, len, addr_len); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "addr-len", -1); + pdu_str += tmp; + len -= tmp; + + if (smsc && !addr_len) { + log_debug("smsc addr is empty"); + goto done; + } + + tmp = hex_byte_encode(pdu_str, len, addr->type); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "addr-type", -1); + pdu_str += tmp; + len -= tmp; + + addr_len = strlen(addr->addr); + if (addr_len & 1) { + addr_len++; + } + + if (len < addr_len + 1) { + log_error("buffer is not large enough to hold the addr"); + return -1; + } + + strcpy(pdu_str, addr->addr); + strpad(pdu_str, addr_len, 'F'); + nibble_swap(pdu_str, addr_len); + len -= addr_len; + pdu_str += addr_len; + +done: + + return pdu_str - begin; +} + +#define septet_idx(n) ((n) * 8 / 7) + +#define encode_octet(buf, n) \ +shiftr(bit_maskl(cycledown(n, 7) + 1, 7) & buf[septet_idx(n)], cycleup(n, 7)) | \ +shiftl(bit_maskr(cycleup(n, 7) + 1) & buf[septet_idx(n) + 1], cycledown(n, 7) + 1) + +int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu) +{ + char *begin = pdu_str; + int tmp; + uint8_t octets[PDU_OCTETS_MAX]; + int nr_octets; + int i; + + if (pdu->data_coding.general.unused) { + log_error("data coding group 0x%02X not implemented", + pdu->data_coding.data_coding & 0xF0); + return -1; + } + + if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { + log_debug("data coding alphabet is default"); + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_EIGHT) { + log_debug("data coding alphabet is eight"); + } else { + log_debug("data coding alphabet 0x%02X not implemented", + pdu->data_coding.general.alphabet); + return -1; + } + + if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { + if (pdu->user_data_len > PDU_UD_7BIT_MAX) { + log_error("string exceeds 7-bit data max length"); + return -1; + } + + nr_octets = octet_align(pdu->user_data_len); + for (i = 0; i < nr_octets; i++) { + octets[i] = encode_octet(pdu->user_data, i); + } + } else { + if (pdu->user_data_len > PDU_UD_8BIT_MAX) { + log_error("string exceeds 8-bit data max length"); + return -1; + } + + nr_octets = pdu->user_data_len; + for (i = 0; i < pdu->user_data_len; i++) { + octets[i] = pdu->user_data[i]; + } + } + + if (len < nr_octets * 2 + HEX_BYTE_LEN + 1) { + log_error("buffer is not large enough to hold user-data"); + return -1; + } + + tmp = hex_byte_encode(pdu_str, len, pdu->user_data_len); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "user-data-len", -1); + pdu_str += tmp; + len -= tmp; + + for (i = 0; i < nr_octets; i++) { + hex_byte_encode(pdu_str, len, octets[i]); + pdu_str += HEX_BYTE_LEN; + } + len -= nr_octets * 2; + pdu_str[i] = '\0'; + + log_debug("user-data-len: 0x%02X", pdu->user_data_len); + log_debug("nr_octets: 0x%02X", nr_octets); + + return pdu_str - begin; +} + +int pdu_encode(char *pdu_str, size_t len, struct pdu_info *pdu) +{ + char *begin = pdu_str; + char *msg_begin = pdu_str; + int tmp; + + tmp = pdu_encode_addr(pdu_str, len, &pdu->smsc_addr, 1); + ENCODE_FAIL(tmp < 0, "smsc-addr", -1); + len -= tmp; + pdu_str += tmp; + + msg_begin = pdu_str; + + tmp = hex_byte_encode(pdu_str, len, pdu->type.type); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "type", -1); + len -= tmp; + pdu_str += tmp; + + if (pdu->type.msg_type == PDU_MTI_SUBMIT || + pdu->type.msg_type == PDU_MTI_REPORT) { + tmp = hex_byte_encode(pdu_str, len, pdu->msg_reference); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "msg-reference", -1); + len -= tmp; + pdu_str += tmp; + } + + tmp = pdu_encode_addr(pdu_str, len, &pdu->addr, 0); + ENCODE_FAIL(tmp < 0, "addr", -1); + len -= tmp; + pdu_str += tmp; + + switch (pdu->type.msg_type) { + case PDU_MTI_REPORT: + tmp = pdu_encode_timestamp(pdu_str, len, &pdu->timestamp); + ENCODE_FAIL(tmp < 0, "report-timestamp", -1); + len -= tmp; + pdu_str += tmp; + + break; + case PDU_MTI_DELIVER: + case PDU_MTI_SUBMIT: + tmp = hex_byte_encode(pdu_str, len, pdu->protocol_id); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "protocol-id", -1); + len -= tmp; + pdu_str += tmp; + + tmp = hex_byte_encode(pdu_str, len, pdu->data_coding.data_coding); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "data-coding-scheme", -1); + len -= tmp; + pdu_str += tmp; + + if (pdu->type.msg_type == PDU_MTI_SUBMIT) { + switch (pdu->type.validity_period_format) { + case PDU_VPF_RELATIVE: + tmp = hex_byte_encode(pdu_str, len, pdu->validity_period); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "validity-period", -1); + len -= tmp; + pdu_str += tmp; + + break; + + case PDU_VPF_ENHANCED: + log_warning("PDU_VPF_ENHANCED? Falling through to absolute"); + case PDU_VPF_ABSOUTE: + tmp = pdu_encode_timestamp(pdu_str, len, &pdu->timestamp); + ENCODE_FAIL(tmp < 0, "validity-period-timestamp", -1); + len -= tmp; + pdu_str += tmp; + + break; + + case PDU_VPF_NOT_PRESENT: + default: + break; + } + } else if (pdu->type.msg_type == PDU_MTI_DELIVER) { + tmp = pdu_encode_timestamp(pdu_str, len, &pdu->timestamp); + ENCODE_FAIL(tmp < 0, "delivery-timestamp", -1); + len -= tmp; + pdu_str += tmp; + } + + tmp = pdu_encode_user_data(pdu_str, len, pdu); + ENCODE_FAIL(tmp < 0, "user-data", -1); + len -= tmp; + pdu_str += tmp; + } + + pdu->msg_len = (pdu_str - msg_begin) / 2; + log_debug("msg-len: %d", pdu->msg_len); + + return pdu_str - begin; +} + -- cgit v1.2.3