📄 smsc_cimd2.c
字号:
unsigned char minpacket[sizeof("sOO:SSSte")]; packet = gw_malloc(sizeof(*packet)); packet->operation = operation; packet->seq = seq; sprintf(minpacket, STX_str "%02d:%03d" TAB_str ETX_str, operation, seq); packet->data = octstr_create(minpacket); return packet;}/* Add a parameter to the end of packet */static void packet_add_parm(struct packet *packet, int parmtype, int parmno, Octstr *value, SMSCConn *conn){ unsigned char parmh[sizeof("tPPP:")]; long position; long len; int copied = 0; len = octstr_len(value); gw_assert(packet != NULL); gw_assert(parm_type(parmno) == parmtype); if (len > parm_maxlen(parmno)) { warning(0, "CIMD2[%s]: %s parameter too long, truncating from " "%ld to %ld characters", octstr_get_cstr(conn->id), parm_name(parmno), len, (long) parm_maxlen(parmno)); value = octstr_copy(value, 0, parm_maxlen(parmno)); copied = 1; } /* There's a TAB and ETX at the end; insert it before those. * The new parameter will come with a new starting TAB. */ position = octstr_len(packet->data) - 2; sprintf(parmh, TAB_str "%03d:", parmno); octstr_insert_data(packet->data, position, parmh, strlen(parmh)); octstr_insert(packet->data, value, position + strlen(parmh)); if (copied) octstr_destroy(value);}/* Add a String parameter to the packet */static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn){ packet_add_parm(packet, P_STRING, parmno, value, conn);}/* Add an Address parameter to the packet */static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn){ gw_assert(octstr_check_range(value, 0, octstr_len(value), isphonedigit)); packet_add_parm(packet, P_ADDRESS, parmno, value, conn);}/* Add an SMS parameter to the packet. The caller is expected to have done * the translation to the GSM character set already. */static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn){ packet_add_parm(packet, P_SMS, parmno, value, conn);}/* There is no function for adding a Time parameter to the packet, because * the format makes Time parameters useless for us. If you find that you * need to use them, then also add code for querying the SMS center timestamp * and using that for synchronization. And beware of DST changes. *//* Add a Hexadecimal parameter to the packet */static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn){ value = octstr_duplicate(value); octstr_binary_to_hex(value, 1); /* 1 for uppercase hex, i.e. A .. F */ packet_add_parm(packet, P_HEX, parmno, value, conn); octstr_destroy(value);}/* Add an Integer parameter to the packet */static void packet_add_int_parm(struct packet *packet, int parmno, long value, SMSCConn *conn){ unsigned char buf[128]; Octstr *valuestr; gw_assert(parm_in_range(parmno, value)); sprintf(buf, "%ld", value); valuestr = octstr_create(buf); packet_add_parm(packet, P_INT, parmno, valuestr, conn); octstr_destroy(valuestr);}static void packet_set_checksum(struct packet *packet){ Octstr *data; int checksum; long pos, len; unsigned char buf[16]; gw_assert(packet != NULL); data = packet->data; if (octstr_get_char(data, octstr_len(data) - 2) != TAB) { /* Packet already has checksum; kill it. */ octstr_delete(data, octstr_len(data) - 3, 2); } gw_assert(octstr_get_char(data, octstr_len(data) - 2) == TAB); /* Sum all the way up to the last TAB */ checksum = 0; for (pos = 0, len = octstr_len(data); pos < len - 1; pos++) { checksum += octstr_get_char(data, pos); checksum &= 0xff; } sprintf(buf, "%02X", checksum); octstr_insert_data(data, len - 1, buf, 2);}static void packet_set_sequence(struct packet *packet, int seq){ unsigned char buf[16]; gw_assert(packet != NULL); gw_assert(seq >= 0); gw_assert(seq < 256); sprintf(buf, "%03d", seq); /* Start at 4 to skip the <STX> ZZ: part of the header. */ octstr_set_char(packet->data, 4, buf[0]); octstr_set_char(packet->data, 5, buf[1]); octstr_set_char(packet->data, 6, buf[2]); packet->seq = seq;}static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix, SMSCConn *conn){ struct packet *packet; PrivData *pdata = conn->data; Octstr *text; int spaceleft; long truncated; int dcs = 0; int setvalidity = 0; gw_assert(msg != NULL); gw_assert(msg->type == sms); gw_assert(msg->sms.receiver != NULL); dcs = fields_to_dcs(msg, (msg->sms.alt_dcs != -1 ? msg->sms.alt_dcs : conn->alt_dcs)); if (msg->sms.sender == NULL) msg->sms.sender = octstr_create(""); if (!parm_valid_address(msg->sms.receiver)) { warning(0, "CIMD2[%s]: non-digits in destination phone number '%s', discarded", octstr_get_cstr(conn->id), octstr_get_cstr(msg->sms.receiver)); return NULL; } packet = packet_create(SUBMIT_MESSAGE, BOGUS_SEQUENCE); packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver, conn); /* CIMD2 interprets the originating address as a sub-address to * our connection number (so if the connection is "400" and we * fill in "600" as the sender number, the user sees "400600"). * Since we have no way to ask what this number is, it has to * be configured. */ /* Quick and dirty check to see if we are using alphanumeric sender */ if (parm_valid_address(msg->sms.sender)) { /* We are not, so send in the usual way */ /* Speed up the default case */ if (octstr_len(sender_prefix) == 0) { packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender, conn); } else if (octstr_compare(sender_prefix, octstr_imm("never")) != 0) { if (octstr_ncompare(sender_prefix, msg->sms.sender, octstr_len(sender_prefix)) == 0) { Octstr *sender; sender = octstr_copy(msg->sms.sender, octstr_len(sender_prefix), octstr_len(msg->sms.sender)); packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender, conn); octstr_destroy(sender); } else { warning(0, "CIMD2[%s]: Sending message with originating address <%s>, " "which does not start with the sender-prefix.", octstr_get_cstr(conn->id), octstr_get_cstr(msg->sms.sender)); } } } else { /* The test above to check if sender was all digits failed, so assume we want alphanumeric sender */ packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender, conn); } /* Add the validity period if necessary. This sets the relative validity * period as this is the description of the "validity" parameter of the * sendsms interface. * * Convert from minutes to GSM 03.40 specification (section 9.2.3.12). * 0-143 = 0 to 12 hours in 5 minute increments. * 144-167 = 12hrs30min to 24hrs in 30 minute increments. * 168-196 = 2days to 30days in 1 day increments. * 197-255 = 5weeks to 63weeks in 1 week increments. * * This code was copied from smsc_at2.c. */ if (msg->sms.validity >= 0) { if (msg->sms.validity > 635040) setvalidity = 255; if (msg->sms.validity >= 50400 && msg->sms.validity <= 635040) setvalidity = (msg->sms.validity - 1) / 7 / 24 / 60 + 192 + 1; if (msg->sms.validity > 43200 && msg->sms.validity < 50400) setvalidity = 197; if (msg->sms.validity >= 2880 && msg->sms.validity <= 43200) setvalidity = (msg->sms.validity - 1) / 24 / 60 + 166 + 1; if (msg->sms.validity > 1440 && msg->sms.validity < 2880) setvalidity = 168; if (msg->sms.validity >= 750 && msg->sms.validity <= 1440) setvalidity = (msg->sms.validity - 720 - 1) / 30 + 143 + 1; if (msg->sms.validity > 720 && msg->sms.validity < 750) setvalidity = 144; if (msg->sms.validity >= 5 && msg->sms.validity <= 720) setvalidity = (msg->sms.validity - 1) / 5 - 1 + 1; if (msg->sms.validity < 5) setvalidity = 0; packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity, conn); } /* Explicitly ask not to get status reports. * If we do not do this, the server's default might be to * send status reports in some cases, and we don't do anything * with those reports anyway. */ /* ask for the delivery reports if needed*/ if (!pdata->no_dlr) if (DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask)) packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14, conn); else packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0, conn); else if( pdata->no_dlr && DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask)) warning(0, "CIMD2[%s]: dlr request make no sense while no-dlr set to true", octstr_get_cstr(conn->id)); /* Turn off reply path as default. * This avoids phones automatically asking for a reply */ if (msg->sms.rpi > 0) packet_add_int_parm(packet, P_REPLY_PATH, 1, conn); else packet_add_int_parm(packet, P_REPLY_PATH, 0, conn); /* Use binfo to set the tariff class */ if (octstr_len(msg->sms.binfo)) packet_add_parm(packet, P_INT, P_TARIFF_CLASS, msg->sms.binfo, conn); /* Set the protocol identifier if requested */ if (msg->sms.pid > 0) packet_add_int_parm(packet, P_PROTOCOL_IDENTIFIER, msg->sms.pid, conn); /* If there are more messages to the same destination, then set the * More Messages to Send flag. This allow faster delivery of many messages * to the same destination */ if (msg->sms.msg_left > 0) packet_add_int_parm(packet, P_MORE_MESSAGES_TO_SEND, 1, conn); else packet_add_int_parm(packet, P_MORE_MESSAGES_TO_SEND, 0, conn); truncated = 0; spaceleft = 140; if (octstr_len(msg->sms.udhdata)) { /* udhdata will be truncated and warned about if * it does not fit. */ packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata, conn); } if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) spaceleft = spaceleft * 8 / 7; if (spaceleft < 0) spaceleft = 0; text = octstr_duplicate(msg->sms.msgdata); if (octstr_len(text) > 0 && spaceleft == 0) { warning(0, "CIMD2[%s]: message filled up with UDH, no room for message text", octstr_get_cstr(conn->id)); } else if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) { if (octstr_len(text) > spaceleft) { truncated = octstr_len(text) - spaceleft; octstr_truncate(text, spaceleft); } packet_add_hex_parm(packet, P_USER_DATA_BINARY, text, conn); } else { /* Going from latin1 to GSM to CIMD2 may seem like a * detour, but it's the only way to get all the escape * codes right. */ charset_latin1_to_gsm(text); truncated = charset_gsm_truncate(text, spaceleft); convert_gsm_to_cimd2(text); packet_add_sms_parm(packet, P_USER_DATA, text, conn); } if (dcs != 0) packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs, conn); if (truncated > 0) { warning(0, "CIMD2[%s]: truncating message text to fit in %d characters.", octstr_get_cstr(conn->id), spaceleft); } octstr_destroy(text); return packet;}/***************************************************************************//* Protocol functions. These implement various transactions. *//***************************************************************************//* Give this packet a proper sequence number for sending. */static void packet_set_send_sequence(struct packet *packet, PrivData *pdata){ gw_assert(pdata != NULL); /* LOGIN packets always have sequence number 001 */ if (packet->operation == LOGIN) pdata->send_seq = 1; /* Send sequence numbers are always odd, receiving are always even */ gw_assert(pdata->send_seq % 2 == 1); packet_set_sequence(packet, pdata->send_seq); pdata->send_seq += 2; if (pdata->send_seq > 256) pdata->send_seq = 1;}static struct packet *cimd2_get_packet(PrivData *pdata, Octstr **ts){ struct packet *packet = NULL; gw_assert(pdata != NULL); /* If packet is already available, don't try to read anything */ packet = packet_extract(pdata->inbuffer, pdata->conn); while (packet == NULL) { if (read_available(pdata->socket, RESPONSE_TIMEOUT) != 1) { warning(0, "CIMD2[%s]: SMSC is not responding", octstr_get_cstr(pdata->conn->id)); return NULL; } if (octstr_append_from_socket(pdata->inbuffer, pdata->socket) <= 0) { error(0, "CIMD2[%s]: cimd2_get_packet: read failed", octstr_get_cstr(pdata->conn->id)); return NULL; } packet = packet_extract(pdata->inbuffer, pdata->conn); } packet_check(packet,pdata->conn); packet_check_can_receive(packet,pdata->conn); debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>", octstr_get_cstr(pdata->conn->id), octstr_get_cstr(packet->data)); if (ts) *ts = packet_get_parm(packet,P_MC_TIMESTAMP); if (pdata->keepalive > 0) pdata->next_ping = time(NULL) + pdata->keepalive; return packet;}/* Acknowledge a request. The CIMD 2 spec only defines positive responses * to the server, because the server is perfect. */static void cimd2_send_response(struct packet *request, PrivData *pdata){ struct packet *response; gw_assert(request != NULL); gw_assert(request->operation < RESPONSE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -