From 2b26d6cf26b067bb472be8bddccf1ecfe8973695 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 11 Nov 2010 10:23:37 +0300 Subject: [PATCH] Checking in forgotten smsc.cpp and smsc.h files. --- public-trunk/smqueue/smsc.cpp | 394 ++++++++++++++++++++++++++++++++++ public-trunk/smqueue/smsc.h | 39 ++++ 2 files changed, 433 insertions(+) create mode 100644 public-trunk/smqueue/smsc.cpp create mode 100644 public-trunk/smqueue/smsc.h diff --git a/public-trunk/smqueue/smsc.cpp b/public-trunk/smqueue/smsc.cpp new file mode 100644 index 0000000..60b7711 --- /dev/null +++ b/public-trunk/smqueue/smsc.cpp @@ -0,0 +1,394 @@ +/* + * SMSC.h - SMS Center implementation for OpenBTS. + * Written by Alexander Chemeris, 2010. + * + * Copyright 2010 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * See the COPYING file in the main directory for details. + */ + +//#include +//#include +//#include +#include +//#include +//#include "ControlCommon.h" +#include +#include + +using namespace std; +using namespace GSM; +//using namespace Control; + +//#include "SIPInterface.h" +//#include "SIPUtility.h" +//#include "SIPMessage.h" +//#include "SIPEngine.h" +//using namespace SIP; + +//#include "CollectMSInfo.h" + +#include "smsc.h" + +// FORWARD DECLARATIONS +void set_to_for_smsc(const char *address, short_msg_p_list::iterator &smsg); + +/**@name Functions for transmitting through various gateways. */ +//@{ + + +/** Substitute spaces with +'s for compatibility with some protocols. */ +void convertText(char *dest, const char *src) +{ + // FIXME -- We should just do full "percent encoding" here. + while (*src != '\0') { + if (*src == ' ') *dest++ = '+'; + else *dest++ = *src; + src++; + } + *dest = '\0'; +} + + + +/** Send SMS via and HTTP interface. */ +short_code_action sendHTTP(const char* destination, const std::string &message) +{ + char convMessage[message.length()+2]; + convertText(convMessage,message.data()); + char command[2048]; + // FIXME -- Check specs for a good timeout value here. + sprintf(command,"wget -T 5 -C -q -O - \"http://%s/http/%s&to=%s&text=%s\" >& /dev/null", + gConfig.getStr("SMS.HTTP.Gateway"), + gConfig.getStr("SMS.HTTP.AccessString"), + destination, convMessage); + LOG(DEBUG) << "MOSMS: sendHTTP sending with " << command; + + // HTTP "GET" method with wget. + // FIXME -- Look at the output of wget to check success. + FILE* wget = popen(command,"r"); + if (!wget) { + LOG(ALARM) << "cannot open wget with " << command; + return SCA_INTERNAL_ERROR; + } + pclose(wget); + return SCA_DONE; +} + + +/** Send e-mail with local sendmail program. */ +short_code_action sendEMail(const char* address, const char* body, const char* subject=NULL) +{ + // We're not checking for overflow because the TPDU can't be more than a few hundred bytes. + + // Build the command line. + // FIXME -- Use sendmail to have better header control. + char command[1024]; + if (subject) sprintf(command,"mail -s \"%s\" %s",subject,address); + else sprintf(command,"mail %s",address); + LOG(INFO) << "sending SMTP: \"" << body << "\" via \"" << command << "\""; + + // Send the mail. + FILE* mail = popen(command,"w"); + if (!mail) { + LOG(ALARM) << "cannot send mail with \"" << command << "\""; + return SCA_INTERNAL_ERROR; + } + // FIXME -- We should be sure body is 7-bit clean. + fprintf(mail,"%s",body); + if (pclose(mail) == -1) return SCA_INTERNAL_ERROR; + return SCA_DONE; +} + +short_code_action sendSIP_init(const char *imsi, const TLSubmit& submit, + const std::string &body, short_code_params *scp) +{ + const char *address = submit.DA().digits(); + const TLUserData& tl_ud = submit.UD(); + const char *from = scp->scp_qmsg_it->parsed->from->url->username; + LOG(INFO) << "from " << imsi << " to " << address; + + if (scp == NULL) + { + LOG(WARN) << "short_code_params is NULL. Error."; + return SCA_INTERNAL_ERROR; + } + + // START OF THE SIP PROCESSING + osip_message_t *omsg = scp->scp_qmsg_it->parsed; + + // Req.URI + osip_free(omsg->req_uri->username); + omsg->req_uri->username = (char *)osip_malloc (strlen(address)+1); + strcpy(omsg->req_uri->username, address); + + // To: + set_to_for_smsc(address, scp->scp_qmsg_it); + + // Let them know that parsed part has been changed. + scp->scp_qmsg_it->parsed_was_changed(); + + if (ISLOGGING(DEBUG)) { + // Call make_text_valid() is needed for debug only. + scp->scp_qmsg_it->make_text_valid(); + LOG(DEBUG) << "Updated SMS message: " << scp->scp_qmsg_it->text; + } + return SCA_RESTART_PROCESSING; +} + +void create_sms_delivery(const std::string &body, + const TLUserData &UD, + short_msg_p_list::iterator &smsg) +{ + RPData *rp_data_new = NULL; + TLDeliver *deliver = NULL; + const char *from = smsg->parsed->from->url->username; + + // HACK + // Check for "Easter Eggs" + // TL-PID + // See 03.40 9.2.3.9. + unsigned TLPID=0; + if (strncmp(body.data(),"#!TLPID",7)==0) sscanf(body.data(),"#!TLPID%d",&TLPID); + + // Generate RP-DATA with SMS-DELIVER + { + unsigned reference = random() % 255; + deliver = new TLDeliver(from,UD,TLPID); + rp_data_new = new RPData(reference, RPAddress(gConfig.getStr("SMS.FakeSrcSMSC")), + *deliver); + LOG(DEBUG) << "New RPData: " << *rp_data_new; + } + + // Replace RP-DATA in the message + delete smsg->rp_data; + delete smsg->tl_message; + smsg->rp_data = rp_data_new; + smsg->tl_message = deliver; +} + +void pack_tpdu(short_msg_p_list::iterator &smsg) +{ + // Pack RP-DATA to bitstream + RLFrame RPDU_new(smsg->rp_data->bitsNeeded()); + smsg->rp_data->write(RPDU_new); + LOG(DEBUG) << "New RLFrame: " << RPDU_new; + + // START OF THE SIP PROCESSING + osip_message_t *omsg = smsg->parsed; + + // Message body + osip_body_t *bod1 = (osip_body_t *)omsg->bodies.node->element; + osip_free(bod1->body); + ostringstream body_stream; + RPDU_new.hex(body_stream); + bod1->length = body_stream.str().length(); + bod1->body = (char *)osip_malloc (bod1->length+1); + strcpy(bod1->body, body_stream.str().data()); + + // Let them know that parsed part has been changed. + smsg->parsed_was_changed(); + + // It's SC->MS now + smsg->ms_to_sc = false; +} + +void set_to_for_smsc(const char *address, short_msg_p_list::iterator &smsg) +{ + osip_message_t *omsg = smsg->parsed; + + // Set To field + osip_free(omsg->to->url->username); + osip_free(omsg->to->displayname); + omsg->to->url->username = (char *)osip_malloc (strlen(address)+1); + omsg->to->displayname = (char *)osip_malloc (strlen(omsg->req_uri->username)+1); + strcpy(omsg->to->url->username, address); + strcpy(omsg->to->displayname, omsg->req_uri->username); + + // Let them know that parsed part has been changed. + smsg->parsed_was_changed(); +} + +//@} + + +/** Send a TPDU through whatever gateway is available. */ +short_code_action submitSMS(const char *imsi, const TLSubmit& submit, + const std::string &body, short_code_params *scp) +{ + LOG(INFO) << "from " << imsi << " message: " << submit; + const TLAddress& address = submit.DA(); + + // Check for direct e-mail address at start of message body. + // FIXME -- This doesn't really follow the spec. See GSM 03.40 3.8. + static const Regexp emailAddress("^[[:graph:]]+@[[:graph:]]+ "); + if (emailAddress.match(body.data())) { + // FIXME -- Get the sender's E.164 to put in the subject line. + char bodyCopy[body.length()+2]; + strcpy(bodyCopy,body.data()); + char* SMTPAddress = bodyCopy; + char* term = strchr(bodyCopy,' '); + // If term's NULL, the regexp is broken. + assert(term); + *term = '\0'; + char* SMTPPayload = term+1; + LOG(INFO) << "sending SMTP to " << SMTPAddress << ": " << SMTPPayload; + if (SMTPPayload) return sendEMail(SMTPAddress,SMTPPayload,"from OpenBTS gateway"); + else return sendEMail(SMTPAddress,"(empty)","from OpenBTS gateway"); + } + + // Send to smqueue or HTTP gateway, depending on what's defined in the config. + if (gConfig.defines("SMS.HTTP.Gateway")) + // If there is an external HTTP gateway, use it. + return sendHTTP(address.digits(), body); + else + // Otherwise, we are looking for a SIP interface to smqueue. + return sendSIP_init(imsi, submit, body, scp); +} + +short_code_action shortcode_smsc(const char *imsi, const char *msgtext, + short_code_params *scp) +{ + short_code_action return_action = SCA_TREAT_AS_ORDINARY; + short_msg_p_list::iterator smsg = scp->scp_qmsg_it; + + if (smsg->content_type != short_msg::VND_3GPP_SMS) + { + LOG(WARN) << "We support only TPDU-coded SMS messages. Plain text ones are to be supported later."; + return SCA_INTERNAL_ERROR; + } + if (smsg->tl_message == NULL) + { + LOG(WARN) << "TLMessage is not available. Error during SMS decoding occurred?"; + return SCA_INTERNAL_ERROR; + } + if (((TLMessage::MessageType)smsg->tl_message->MTI()) != TLMessage::SUBMIT) + { + LOG(WARN) << "TPDU must be of SMS-SUBMIT type, we have MTI=" + << (TLMessage::MessageType)smsg->tl_message->MTI(); + return SCA_INTERNAL_ERROR; + } + + return_action = submitSMS(imsi, *((TLSubmit*)smsg->tl_message), msgtext, scp); + + return return_action; +} + +bool pack_text_to_tpdu(const std::string &body, + short_msg_p_list::iterator &smsg) +{ + create_sms_delivery(body, TLUserData(body.data()), smsg); + pack_tpdu(smsg); + + // Set Content-Type field + const char *type = "application"; + const char *subtype = "vnd.3gpp.sms"; + osip_free(smsg->parsed->content_type->type); + osip_free(smsg->parsed->content_type->subtype); + smsg->parsed->content_type->type = (char *)osip_malloc (strlen(type)+1); + smsg->parsed->content_type->subtype = (char *)osip_malloc (strlen(subtype)+1); + strcpy(smsg->parsed->content_type->type, type); + strcpy(smsg->parsed->content_type->subtype, subtype); + + // Let them know that parsed part has been changed. + smsg->parsed_was_changed(); + + return true; +} + +bool recode_tpdu(const std::string &body, + short_msg_p_list::iterator &smsg) +{ + bool return_action = true; + + // Safety check + if (smsg->tl_message == NULL) + { + LOG(WARN) << "TLMessage is not available. Error during SMS decoding occurred?"; + return false; + } + + switch ((TLMessage::MessageType)smsg->tl_message->MTI()) { + case TLMessage::SUBMIT: { + TLSubmit *submit = (TLSubmit*)smsg->tl_message; + const TLUserData& tl_ud = submit->UD(); + create_sms_delivery(body, tl_ud, smsg); + pack_tpdu(smsg); + return_action = true; + break; + } + + case TLMessage::DELIVER_REPORT: + case TLMessage::STATUS_REPORT: + // TODO + LOG(WARN) << "TPDU must be of SMS-SUBMIT type, we have MTI=" + << (TLMessage::MessageType)smsg->tl_message->MTI(); + return false; + } + + // Now we have MS->SC message + smsg->ms_to_sc = false; + + return return_action; +} + +bool pack_sms_for_delivery(short_msg_p_list::iterator &smsg) +{ + bool return_action = true; + std::string msgtext; + + if (!smsg->need_repack) { + // Nothing to do. + return true; + } + + // Parse message if not parsed yet. + smsg->parse(); + + // Get message text in plain text form + msgtext = smsg->get_text(); + + switch (smsg->content_type) { + case short_msg::TEXT_PLAIN: + return_action = pack_text_to_tpdu(msgtext, smsg); + break; + + case short_msg::VND_3GPP_SMS: + // No need to recode if it's SC->MS already + if (smsg->ms_to_sc) + { + return_action = recode_tpdu(msgtext, smsg); + } + break; + + case short_msg::UNSUPPORTED_CONTENT: + default: + LOG(WARN) << "Unsupported SIP-messages content."; + return false; + } + + // Successful repack + if (return_action) { + smsg->need_repack = false; + } + + if (ISLOGGING(DEBUG)) { + // Call make_text_valid() is needed for debug only. + smsg->make_text_valid(); + LOG(DEBUG) << "Updated SMS message: " << smsg->text; + } + + return return_action; +} diff --git a/public-trunk/smqueue/smsc.h b/public-trunk/smqueue/smsc.h new file mode 100644 index 0000000..aa88c08 --- /dev/null +++ b/public-trunk/smqueue/smsc.h @@ -0,0 +1,39 @@ +/* + * SMSC.h - SMS Center implementation for OpenBTS. + * Written by Alexander Chemeris, 2010. + * + * Copyright 2010 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * See the COPYING file in the main directory for details. + */ + +#ifndef SMSC_H +#define SMSC_H + +#include "smqueue.h" + +using namespace SMqueue; + +/** Unhex and parse msgtext to RPData struct. + @param msgttext RPData encoded into hex-string. + @return Pointer to parsed RPData or NULL on failure. +*/ +RPData *hex2rpdata(const char *msgtext); + +enum short_code_action shortcode_smsc(const char *imsi, const char *msgtext, + short_code_params *scp); +bool pack_sms_for_delivery(short_msg_p_list::iterator &smsg); + +#endif