/* * PDU common * * 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 * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "pdu.h" #include "sms_utils.h" char strGSMTable[GSM_TABLE_SIZE] = { '@','£','$','¥','è','é','ù','ì','ò','Ç','\n','Ø','ø','\r','Å','å', 'Δ','_','Φ','Γ','Λ','Ω','Π','Ψ','Σ','Θ','Ξ','\x1B','Æ','æ','ß','É', ' ','!','\"','#','¤','%','&','\'','(',')','*','+',',','-','.','/', '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?', '¡','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z','Ä','Ö','Ñ','Ü','\xA7', '¿','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', 'p','q','r','s','t','u','v','w','x','y','z','ä','ö','ñ','ü','à'}; char strExtendedTable[GSM_TABLE_SIZE] = { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ','^',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ','`','{','}',' ',' ',' ',' ',' ','\\', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','[','~',']',' ', '|',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ','€',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 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; }