void Control::InCallMOSMSStarter(TransactionEntry *parallelCall) { GSM::LogicalChannel *hostChan = parallelCall->channel(); assert(hostChan); GSM::LogicalChannel *SACCH = hostChan->SACCH(); assert(SACCH); // Create a partial transaction record. TransactionEntry *newTransaction = new TransactionEntry( gConfig.getStr("SIP.Proxy.SMS").c_str(), parallelCall->subscriber(), SACCH); gTransactionTable.add(newTransaction); }
GSM::LogicalChannel* TransactionTable::findChannel(const L3MobileIdentity& mobileID) { // Yes, it's linear time. // Even in a 6-ARFCN system, it should rarely be more than a dozen entries. // Since clearDeadEntries is also linear, do that here, too. clearDeadEntries(); // Brtue force search. ScopedLock lock(mLock); for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) { if (itr->second->subscriber() != mobileID) continue; GSM::LogicalChannel* chan = itr->second->channel(); if (!chan) continue; if (chan->type() == FACCHType) return chan; if (chan->type() == SDCCHType) return chan; } return NULL; }
bool SIPInterface::checkInvite( osip_message_t * msg) { LOG(DEBUG); // This code dispatches new transactions coming from the network-side SIP interface. // All transactions originating here are going to be mobile-terminated. // Yes, this method is too long and needs to be broken up into smaller steps. // Is there even a method? const char *method = msg->sip_method; if (!method) return false; // Check for INVITE or MESSAGE methods. // Check channel availability now, too. GSM::ChannelType requiredChannel; bool channelAvailable = false; GSM::L3CMServiceType serviceType; // pretty sure strings are garbage collected string proxy = get_return_address(msg); if (strcmp(method,"INVITE") == 0) { // INVITE is for MTC. // Set the required channel type to match the assignment style. if (gConfig.defines("Control.VEA")) { // Very early assignment. requiredChannel = GSM::TCHFType; channelAvailable = gBTS.TCHAvailable(); } else { // Early assignment requiredChannel = GSM::SDCCHType; channelAvailable = gBTS.SDCCHAvailable() && gBTS.TCHAvailable(); } serviceType = L3CMServiceType::MobileTerminatedCall; } else if (strcmp(method,"MESSAGE") == 0) { // MESSAGE is for MTSMS. requiredChannel = GSM::SDCCHType; channelAvailable = gBTS.SDCCHAvailable(); serviceType = L3CMServiceType::MobileTerminatedShortMessage; } else { // Not a method handled here. LOG(DEBUG) << "non-initiating SIP method " << method; return false; } // Get request username (IMSI) from invite. const char* IMSI = extractIMSI(msg); if (!IMSI) { // FIXME -- Send appropriate error (404) on SIP interface. LOG(WARNING) << "Incoming INVITE/MESSAGE with no IMSI"; return false; } L3MobileIdentity mobileID(IMSI); // Get the SIP call ID. const char * callIDNum = extractCallID(msg); if (!callIDNum) { // FIXME -- Send appropriate error on SIP interface. LOG(WARNING) << "Incoming INVITE/MESSAGE with no call ID"; return false; } // Find any active transaction for this IMSI with an assigned TCH or SDCCH. GSM::LogicalChannel *chan = gTransactionTable.findChannel(mobileID); if (chan) { // If the type is TCH and the service is SMS, get the SACCH. // Otherwise, for now, just say chan=NULL. if (serviceType==L3CMServiceType::MobileTerminatedShortMessage && chan->type()==FACCHType) { chan = chan->SACCH(); } else { // FIXME -- This will change to support multiple transactions. chan = NULL; } } // Check SIP map. Repeated entry? Page again. if (mSIPMap.map().readNoBlock(callIDNum) != NULL) { TransactionEntry* transaction= gTransactionTable.find(mobileID,callIDNum); // There's a FIFO but no trasnaction record? if (!transaction) { LOG(WARNING) << "repeated INVITE/MESSAGE with no transaction record"; // Delete the bogus FIFO. mSIPMap.remove(callIDNum); return false; } // There is transaction already. Send trying, if appropriate. if (serviceType!=L3CMServiceType::MobileTerminatedShortMessage) transaction->MTCSendTrying(); // And if no channel is established yet, page again. if (!chan) { LOG(INFO) << "repeated SIP INVITE/MESSAGE, repaging for transaction " << *transaction; gBTS.pager().addID(mobileID,requiredChannel,*transaction); } return false; } // So we will need a new channel. // Check gBTS for channel availability. if (!chan && !channelAvailable) { // FIXME -- Send 503 "Service Unavailable" response on SIP interface. // Don't forget the retry-after header. LOG(NOTICE) << "MTC CONGESTION, no " << requiredChannel << " availble for assignment"; return false; } if (chan) { LOG(INFO) << "using existing channel " << chan->descriptiveString(); } else { LOG(INFO) << "set up MTC paging for channel=" << requiredChannel; } // Add an entry to the SIP Map to route inbound SIP messages. addCall(callIDNum); LOG(DEBUG) << "callIDNum " << callIDNum << " IMSI " << IMSI; // Get the caller ID if it's available. const char *callerID = ""; const char *callerHost = ""; osip_from_t *from = osip_message_get_from(msg); if (from) { osip_uri_t* url = osip_contact_get_url(from); if (url) { if (url->username) callerID = url->username; if (url->host) callerHost = url->host; } } else { LOG(NOTICE) << "INVITE with no From: username for " << mobileID; } LOG(DEBUG) << "callerID " << callerID << "@" << callerHost; // Build the transaction table entry. // This constructor sets TI automatically for an MT transaction. TransactionEntry *transaction = new TransactionEntry(proxy.c_str(),mobileID,chan,serviceType,callerID); // FIXME -- These parameters should be arguments to the constructor above. transaction->SIPUser(callIDNum,IMSI,callerID,callerHost); transaction->saveINVITE(msg,false); // Tell the sender we are trying. if (serviceType!=L3CMServiceType::MobileTerminatedShortMessage) transaction->MTCSendTrying(); // SMS? Get the text message body to deliver. if (serviceType == L3CMServiceType::MobileTerminatedShortMessage) { osip_body_t *body; osip_content_type_t *contentType; osip_message_get_body(msg,0,&body); contentType = osip_message_get_content_type(msg); const char *text = NULL; char *type = NULL; if (body) text = body->body; if (text) transaction->message(text, body->length); else LOG(NOTICE) << "MTSMS incoming MESSAGE method with no message body for " << mobileID; /* Ok, so osip does some funny stuff here. The MIME type is split into type and subType. Basically, text/plain becomes type=text, subType=plain. We need to put those together... */ if (contentType) { type = (char *)malloc(strlen(contentType->type)+strlen(contentType->subtype)+2); } if (type) { strcpy(type,contentType->type); strcat(type,"/"); strcat(type,contentType->subtype); transaction->messageType(type); free(type); } else LOG(NOTICE) << "MTSMS incoming MESSAGE method with no content type (or memory error) for " << mobileID; } LOG(INFO) << "MTC MTSMS make transaction and add to transaction table: "<< *transaction; gTransactionTable.add(transaction); // If there's an existing channel, skip the paging step. if (!chan) { // Add to paging list. LOG(DEBUG) << "MTC MTSMS new SIP invite, initial paging for mobile ID " << mobileID; gBTS.pager().addID(mobileID,requiredChannel,*transaction); } else { // Add a transaction to an existing channel. chan->addTransaction(transaction); // FIXME -- We need to write something into the channel to trigger the new transaction. // We need to send a message into the chan's dispatch loop, // becasue we can't block this thread to run the transaction. } return true; }