📄 smsc_at.c
字号:
type = octstr_get_char(data, 1) & 3; switch (type) { case AT_DELIVER_SM: msg = at2_pdu_decode_deliver_sm(data, privdata); break; case AT_STATUS_REPORT_SM: msg = at2_pdu_decode_report_sm(data, privdata); break; /* Add other message types here: */ } return msg;}static Msg *at2_pdu_decode_deliver_sm(Octstr *data, PrivAT2data *privdata){ int len, pos, i, ntype; int udhi, dcs, udhlen, pid; Octstr *origin = NULL; Octstr *udh = NULL; Octstr *text = NULL, *tmpstr; Octstr *pdu = NULL; Msg *message = NULL; struct universaltime mtime; /* time structure */ long stime; /* time in seconds */ int timezone; /* timezone in 15 minutes jumps from GMT */ /* * Note: some parts of the PDU are not decoded because they are * not needed for the Msg type. */ /* convert the pdu to binary format for ease of processing */ pdu = at2_convertpdu(data); /* UDH Indicator */ udhi = (octstr_get_char(pdu, 0) & 64) >> 6; /* originating address */ len = octstr_get_char(pdu, 1); if (len > 20) /* maximum valid number of semi-octets in Address-Value field */ goto msg_error; ntype = octstr_get_char(pdu, 2); pos = 3; if ((ntype & 0xD0) == 0xD0) { /* Alphanumeric sender */ origin = octstr_create(""); tmpstr = octstr_copy(pdu, 3, len); at2_decode7bituncompressed(tmpstr, (((len - 1) * 4 - 3) / 7) + 1, origin, 0); octstr_destroy(tmpstr); debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric sender <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(origin)); pos += (len + 1) / 2; } else { origin = octstr_create(""); if ((ntype & 0x90) == 0x90) { /* International number */ octstr_append_char(origin, '+'); } for (i = 0; i < len; i += 2, pos++) { octstr_append_char(origin, (octstr_get_char(pdu, pos) & 15) + 48); if (i + 1 < len) octstr_append_char(origin, (octstr_get_char(pdu, pos) >> 4) + 48); } debug("bb.smsc.at2", 0, "AT2[%s]: Numeric sender %s <%s>", octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""), octstr_get_cstr(origin)); } if (pos > octstr_len(pdu)) goto msg_error; /* PID */ pid = octstr_get_char(pdu, pos); pos++; /* DCS */ dcs = octstr_get_char(pdu, pos); pos++; /* get the timestamp */ mtime.year = swap_nibbles(octstr_get_char(pdu, pos)); pos++; mtime.year += (mtime.year < 70 ? 2000 : 1900); mtime.month = swap_nibbles(octstr_get_char(pdu, pos)); mtime.month--; pos++; mtime.day = swap_nibbles(octstr_get_char(pdu, pos)); pos++; mtime.hour = swap_nibbles(octstr_get_char(pdu, pos)); pos++; mtime.minute = swap_nibbles(octstr_get_char(pdu, pos)); pos++; mtime.second = swap_nibbles(octstr_get_char(pdu, pos)); pos++; /* * time zone: * * time zone is "swapped nibble", with the MSB as the sign (1 is negative). */ timezone = swap_nibbles(octstr_get_char(pdu, pos)); pos++; timezone = ((timezone >> 7) ? -1 : 1) * (timezone & 127); /* * Ok, that was the time zone as read from the PDU. Now how to interpert it? * All the handsets I tested send the timestamp of their local time and the * timezone as GMT+0. I assume that the timestamp is the handset's local time, * so we need to apply the timezone in reverse to get GM time: */ /* * time in PDU is handset's local time and timezone is handset's time zone * difference from GMT */ mtime.hour -= timezone / 4; mtime.minute -= 15 * (timezone % 4); stime = date_convert_universal(&mtime); /* get data length * XXX: Is it allowed to have length = 0 ??? (alex) */ len = octstr_get_char(pdu, pos); pos++; debug("bb.smsc.at2", 0, "AT2[%s]: User data length read as (%d)", octstr_get_cstr(privdata->name), len); /* if there is a UDH */ udhlen = 0; if (udhi && len > 0) { udhlen = octstr_get_char(pdu, pos); pos++; if (udhlen + 1 > len) goto msg_error; udh = octstr_copy(pdu, pos, udhlen); pos += udhlen; len -= udhlen + 1; } else if (len <= 0) /* len < 0 is impossible, but sure is sure */ udhi = 0; debug("bb.smsc.at2", 0, "AT2[%s]: Udh decoding done len=%d udhi=%d udhlen=%d udh='%s'", octstr_get_cstr(privdata->name), len, udhi, udhlen, (udh ? octstr_get_cstr(udh) : "")); if (pos > octstr_len(pdu) || len < 0) goto msg_error; /* build the message */ message = msg_create(sms); if (!dcs_to_fields(&message, dcs)) { /* XXX Should reject this message? */ debug("bb.smsc.at2", 0, "AT2[%s]: Invalid DCS", octstr_get_cstr(privdata->name)); dcs_to_fields(&message, 0); } message->sms.pid = pid; /* deal with the user data -- 7 or 8 bit encoded */ tmpstr = octstr_copy(pdu, pos, len); if (message->sms.coding == DC_8BIT || message->sms.coding == DC_UCS2) { text = octstr_duplicate(tmpstr); } else { int offset = 0; text = octstr_create(""); if (udhi && message->sms.coding == DC_7BIT) { int nbits; nbits = (udhlen + 1) * 8; /* fill bits for UDH to septet boundary */ offset = (((nbits / 7) + 1) * 7 - nbits) % 7; } at2_decode7bituncompressed(tmpstr, len, text, offset); } message->sms.sender = origin; if (octstr_len(privdata->my_number)) { message->sms.receiver = octstr_duplicate(privdata->my_number); } else { /* Put a dummy address in the receiver for now (SMSC requires one) */ message->sms.receiver = octstr_create_from_data("1234", 4); } if (udhi) { message->sms.udhdata = udh; } message->sms.msgdata = text; message->sms.time = stime; /* cleanup */ octstr_destroy(pdu); octstr_destroy(tmpstr); return message; msg_error: error(1, "AT2[%s]: Invalid DELIVER-SMS pdu!", octstr_get_cstr(privdata->name)); O_DESTROY(udh); O_DESTROY(origin); O_DESTROY(text); O_DESTROY(pdu); return NULL;}static Msg *at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata){ Msg *dlrmsg = NULL; Octstr *pdu, *msg_id, *tmpstr = NULL, *receiver = NULL; int type, tp_mr, len, ntype, pos; /* * parse the PDU. */ /* convert the pdu to binary format for ease of processing */ pdu = at2_convertpdu(data); /* Message reference */ tp_mr = octstr_get_char(pdu, 1); msg_id = octstr_format("%d", tp_mr); debug("bb.smsc.at2", 0, "AT2[%s]: got STATUS-REPORT for message <%d>:", octstr_get_cstr(privdata->name), tp_mr); /* reciver address */ len = octstr_get_char(pdu, 2); ntype = octstr_get_char(pdu, 3); pos = 4; if ((ntype & 0xD0) == 0xD0) { /* Alphanumeric sender */ receiver = octstr_create(""); tmpstr = octstr_copy(pdu, pos, (len + 1) / 2); at2_decode7bituncompressed(tmpstr, (((len - 1) * 4 - 3) / 7) + 1, receiver, 0); octstr_destroy(tmpstr); debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric receiver <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(receiver)); pos += (len + 1) / 2; } else { int i; receiver = octstr_create(""); if ((ntype & 0x90) == 0x90) { /* International number */ octstr_append_char(receiver, '+'); } for (i = 0; i < len; i += 2, pos++) { octstr_append_char(receiver, (octstr_get_char(pdu, pos) & 15) + 48); if (i + 1 < len) octstr_append_char(receiver, (octstr_get_char(pdu, pos) >> 4) + 48); } debug("bb.smsc.at2", 0, "AT2[%s]: Numeric receiver %s <%s>", octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""), octstr_get_cstr(receiver)); } pos += 14; /* skip time stamps for now */ if ((type = octstr_get_char(pdu, pos)) == -1 ) { error(1, "AT2[%s]: STATUS-REPORT pdu too short to have TP-Status field !", octstr_get_cstr(privdata->name)); goto error; } /* Check DLR type: * 3GPP TS 23.040 defines this a bit mapped field with lots of options * most of which are not really intersting to us, as we are only interested * in one of three conditions : failed, held in SC for delivery later, or delivered successfuly * and here's how I suggest to test it (read the 3GPP reference for further detailes) - * we'll test the 6th and 5th bits (7th bit when set making all other values 'reseved' so I want to test it). */ type = type & 0xE0; /* filter out everything but the 7th, 6th and 5th bits */ switch (type) { case 0x00: /* 0 0 : success class */ type = DLR_SUCCESS; tmpstr = octstr_create("Success"); break; case 0x20: /* 0 1 : buffered class (temporary error) */ type = DLR_BUFFERED; tmpstr = octstr_create("Buffered"); break; case 0x40: case 0x60: default: /* 1 0 : failed class */ /* 1 1 : failed class (actually, temporary error but timed out) */ /* and any other value (can't think of any) is considered failure */ type = DLR_FAIL; tmpstr = octstr_create("Failed"); break; } /* Actually, the above implementation is not correct, as the reference * says that implementations should consider any "reserved" values to be * "failure", but most reserved values fall into one of the three * categories. It will catch "reserved" values where the first 3 MSBits * are not set as "Success" which may not be correct. */ if ((dlrmsg = dlr_find(privdata->conn->id, msg_id, receiver, type)) == NULL) { debug("bb.smsc.at2", 1, "AT2[%s]: Received delivery notification but can't find that ID in the DLR storage", octstr_get_cstr(privdata->name)); goto error; } /* Beware DLR URL is now in msg->sms.dlr_url given by dlr_find() */ dlrmsg->sms.msgdata = octstr_duplicate(tmpstr); error: O_DESTROY(tmpstr); O_DESTROY(pdu); O_DESTROY(receiver); O_DESTROY(msg_id); return dlrmsg;}static Octstr *at2_convertpdu(Octstr *pdutext){ Octstr *pdu; int i; int len = octstr_len(pdutext); pdu = octstr_create(""); for (i = 0; i < len; i += 2) { octstr_append_char(pdu, at2_hexchar(octstr_get_char(pdutext, i)) * 16 + at2_hexchar(octstr_get_char(pdutext, i + 1))); } return pdu;}static int at2_rmask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };static int at2_lmask[8] = { 0, 128, 192, 224, 240, 248, 252, 254 };static void at2_decode7bituncompressed(Octstr *input, int len, Octstr *decoded, int offset){ unsigned char septet, octet, prevoctet; int i; int r = 1; int c = 7; int pos = 0; /* Shift the buffer offset bits to the left */ if (offset > 0) { unsigned char *ip; for (i = 0, ip = octstr_get_cstr(input); i < octstr_len(input); i++) { if (i == octstr_len(input) - 1) *ip = *ip >> offset; else *ip = (*ip >> offset) | (*(ip + 1) << (8 - offset)); ip++; } } octet = octstr_get_char(input, pos); prevoctet = 0; for (i = 0; i < len; i++) { septet = ((octet & at2_rmask[c]) << (r - 1)) + prevoctet; octstr_append_char(decoded, septet); prevoctet = (octet & at2_lmask[r]) >> c; /* When r=7 we have a full character in prevoctet */ if ((r == 7) && (i < len - 1)) { i++; octstr_append_char(decoded, prevoctet); prevoctet = 0; } r = (r > 6) ? 1 : r + 1; c = (c < 2) ? 7 : c - 1; pos++; octet = octstr_get_char(input, pos); } charset_gsm_to_latin1(decoded);}static void at2_send_messages(PrivAT2data *privdata){ Msg *msg; do { if (privdata->modem->enable_mms && gw_prioqueue_len(privdata->outgoing_queue) > 1) at2_send_modem_command(privdata, "AT+CMMS=2", 0, 0); if ((msg = gw_prioqueue_remove(privdata->outgoing_queue))) at2_send_one_message(privdata, msg); } while (msg);}static void at2_send_one_message(PrivAT2data *p
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -