How to send MMS using SOAP MM7

Here’s an article describing how to implement MM7 connection to your service provider’s MMS center.

Perhaps for the start, I would like to tell the history of MM7 implementation. At first most vendors implement MM7 connection to send MMS content using their own proprietary library. E.g. One of biggest vendor uses PAP (Push Application Protocol) to send standard MMS content from VAS App to the subscriber. Basically it’s built on standard MIME Message class.

Good news, nowadays many vendor has dealt to implement their MM7 connection from VAS App using standard SOAP connection. Moreover, under this SOAP implementation, they can support SMIL class message. It’s a standard Markup Language to implement layouting in MMS content delivery. Also, using this SMIL implementation, we can present multi pages MMS that showed one after another with predefined duration.

Here’s the example of SMIL tag:

<smil xmlns=”http://www.w3.org/2001/SMIL20/Language”&gt;

<head>

<layout>      <root-layout width=”160″ height=”120″/>

<region id=”upper” top=”0″ left=”0″ />      <region id=”lower” top=”100″ left=”0″ />

</layout>

</head>

<body>

<par dur=”3s”>      <text region=”lower” src=”text0.txt” />      <img region=”upper” src=”zzz.JPG” />      <audio src=”01000000478.mid” />    </par>

<par dur=”5s”>      <text region=”lower” src=”text3.txt” />      <img region=”upper” src=”zzz.JPG” />      <audio src=”01000000478.mid” />    </par>

</body></smil>

For the art of software programming, I will put my example code here in Java from scratch. Actually you can code SOAP Message in Java using SAAJ standard library.

At first you need to make sure that you have variables that define the connection to the MMSC. They are MMSC IP, Port, VASID, VASPID, and Password. It can be defined from file configuration.

String mmsc_ip = Utility.getConfiguration(“MmscSoap.ip”); int mmsc_port = Integer.parseInt(Utility.getConfiguration(“MmscSoap.port”)); String mmsc_pwd = Utility.getConfiguration(“MmscSoap.pwd”); String mmsc_vaspid = Utility.getConfiguration(“MmscSoap.vaspid”); String mmsc_vasid = Utility.getConfiguration(“MmscSoap.vasid”);

Basically, SOAP connection is a like a HTTP socket connection using POST method. So, we need to define the HTTP header:

String header = “POST /vas_soap HTTP/1.1\n” +

“Host: “+mmsc_ip+”:”+mmsc_port+”\n” +

“Authorization: Basic “+toBASE64(mmsc_pwd,0)+”\n” +

“User-Agent: mm7submit/0.1\n” +   “Content-Type: multipart/related; boundary=\”–_=_NextPart_21B2_1EE0_E97.2593\”; type=\”text/xml\”; start=\”<mm7submit@localhost>\”\n” +

“Content-Length: “+data.length()+”\n” +

“Accept: */*\n” +

“SOAPAction: \”\”\n\n”;

You can specify either the connection is using HTTP/1.1 or HTTP/1.0. Also you can add more HTTP header there, depends on the server’s need.

MMS contents are simply the texts, multimedia contents (audio, video, and picture), and SMIL tags that defined how the contents presented. Multimedia contents are supposed to be sent as format Base64 Encoding. So, we’re going to put

import sun.misc.BASE64Encoder;

in top of the file.

For the contents’ location, this tutorial will search the contents based on the URL specified in the SMIL tags. Normally, SMIL tag should not be filled with URL. But, to simplify the solution, this code will accept SMIL with URL. Then it will string-process the SMIL to extracts the URL of the contents. Then, will put the content name based on the hashcode of the URL specified.

<img region=”upper” src=”http://www.google.com/images/logos/ps_logo2.png” />

will be replaced with:

<img region=”upper” src=”c63893e3754e335896165ed94d6ce67e” />

The code will differs multimedia into three different types (audio, video, and image). Then it will assume the Content-Type based on the suffix of the URL path.

The code will string-process the smil to get contents’ URL:

smil = URLDecoder.decode(smil);

Vector<String> gambars = new Vector<String>();

Pattern p = Pattern.compile( “<img[ *]region=.[^<>’\”]*.[ *]src=[‘\”]([^’\”<>]*)[‘\”][ *]/>”,  Pattern.DOTALL);
Matcher matcher = p.matcher(smil);

while (matcher.find()) {

String temp = matcher.group(1);

if(!gambars.contains(temp))

gambars.add(temp);

}

Vector<String> suaras = new Vector<String>();

p = Pattern.compile(“<audio[ *]src=[‘\”]([^’\”<>]*)[‘\”][ *]/>”, Pattern.DOTALL);
matcher = p.matcher(smil);

while (matcher.find()) {

String temp = matcher.group(1);

if(!suaras.contains(temp))

suaras.add(temp);

}

Vector<String> videos = new Vector<String>();

p = Pattern.compile(“<video[ *]src=[‘\”]([^’\”<>]*)[‘\”][ *]/>”, Pattern.DOTALL);
matcher = p.matcher(smil);

while (matcher.find()) {

String temp = matcher.group(1);

if(!videos.contains(temp))

videos.add(temp);

}

And here’s how we build the main HTTP body:

String data = “”+ “—-_=_NextPart_21B2_1EE0_E97.2593\n” +

“Content-Type: application/xml\n” +

“Content-ID: <mm7submit@localhost>\n” + “\n” +

“<?xml version=\”1.0\” encoding=\”UTF-8\”?>\n” +

“<SOAP-ENV:Envelope xmlns:SOAP-ENV=\”http://schemas.xmlsoap.org/soap/envelope/\” xmlns=\”http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-5-MM7-1-2\”>\n” +

“<SOAP-ENV:Header>\n” +

” <TransactionID SOAP-ENV:mustUnderstand=\”1\”>vas00001</TransactionID>\n” +

“</SOAP-ENV:Header>\n” +

“<SOAP-ENV:Body>\n” +

“<SubmitReq>\n” +

“<MM7Version>5.3.0</MM7Version>\n” +

“<SenderIdentification>\n” +

” <VASPID>”+mmsc_vaspid+”</VASPID>\n” +

” <VASID>”+mmsc_vasid+”</VASID>\n” +

” <SenderAddress><Number>”+ sender +”</Number></SenderAddress>\n” +

” </SenderIdentification>\n” +

” <Recipients>\n” +

” <To><Number>”+to+”</Number></To>\n” +

” </Recipients>\n” + ” <MessageClass>Informational</MessageClass>\n” +

” <DeliveryReport>false</DeliveryReport>\n” +

” <ReadReply>false</ReadReply>\n” +

” <Priority>Normal</Priority>\n” +

” <Subject>”+subject+”</Subject>\n” +

” <ChargedParty>Sender</ChargedParty>\n” +

” <Content href=\”cid:content@localhost\” allowAdaptations=\”true\”/>\n” +

“</SubmitReq>\n” + “</SOAP-ENV:Body>\n” +

“</SOAP-ENV:Envelope>\n\n” + “—-_=_NextPart_21B2_1EE0_E97.2593\n” +

“Content-Type: multipart/mixed; boundary=\”—-_=_NextPart_220A_232_2517.E38\”\n” +

“Content-ID: <content@localhost>\n” + “\n”;

String texts[] = text.split(“<next/>”);

for(int g = 0; g < texts.length; g++){

data+=”——_=_NextPart_220A_232_2517.E38\n” +

“Content-Type: text/plain; charset=\”us-ascii\”; Name=text”+g+”.txt\n” +

“Content-ID: <text”+g+”.txt>\n” +

“Content-Location: text”+g+”.txt\n\n” +

texts[g]+”\n” + “\n”;

}
String gambar_name[] = null;

int max_depth = 0;

if(gambars != null){

Log.getLogger().debug(“Parse image…”);

gambar_name = new String[gambars.size()];

for(int g = 0; g < gambars.size(); g++){

if(max_depth == g) max_depth++;

URL sourceURL1 = null;

try {

String url = gambars.get(g).replaceAll(” ” , “+”);

sourceURL1 = new URL(url);

byte[] byteArray = null;

HttpClient http = new HttpClient();

http.getParams().setSoTimeout(timeout);

if (proxyHost != null && proxyPort != null) {

Log.getLogger().info(“Using proxy = ” + proxyHost + “:” + proxyPort);

http.getHostConfiguration().setProxy(proxyHost, Integer.parseInt(proxyPort));

}

HttpMethod method = new GetMethod(url);

http.getParams().setParameter(“http.socket.timeout”, timeout);

http.getParams().setParameter(“http.connection.timeout”, timeout);

method.getParams().setParameter(“http.socket.timeout”, timeout);

method.getParams().setParameter(“http.connection.timeout”, timeout);

try {

int statusCode = http.executeMethod(method);

if (statusCode != HttpStatus.SC_OK && statusCode != 202) {

byteArray = null; Log.getLogger().debug(“Status HTTP:”+ statusCode);

} else { byteArray = method.getResponseBody(); }

} catch (Exception e) { Log.getLogger().error(“Error in httpGet”, e); byteArray = null; }

finally { method.releaseConnection(); }

String filename = sourceURL1.getFile();

BASE64Encoder encoder = new BASE64Encoder();

if(filename.contains(“/”)) filename = filename.substring(filename.lastIndexOf(“/”)+1);

gambar_name[g] = Integer.toString(filename.hashCode());

smil = replace(smil, url, gambar_name[g]);

String format = filename.substring(filename.lastIndexOf(“.”)).toLowerCase();

String type = “image/png”;

if(format.equals(“.gif”)) type = “image/gif”;

else if(format.equals(“.jpg”) || format.equals(“.jpeg”)) type = “image/jpeg”;

else if(format.equals(“.png”)) type = “image/png”;

else if(format.equals(“.tiff”)) type = “image/tiff”;

data+=”——_=_NextPart_220A_232_2517.E38\n” +

“Content-Type: “+type+”;

Name=”+gambar_name[g]+”\n” +

“Content-Transfer-Encoding: base64\n” +

“Content-ID: <“+gambar_name[g]+”>\n” +

“Content-Location: “+gambar_name[g]+”\n\n” +

encoder.encode(byteArray)+”\n” + “\n”;

} catch (Exception e) {// TODO Auto-generated catch block e.printStackTrace(); }

}

}

String suara_name[] = null;

if(suaras != null){

suara_name = new String[suaras.size()];

for(int g = 0; g < suaras.size(); g++){

if(max_depth == g) max_depth++;

URL sourceURL1 = null;

try {

String url = suaras.get(g).replaceAll(” ” , “+”);

sourceURL1 = new URL(url);

byte[] byteArray = null;

HttpClient http = new HttpClient();

http.getParams().setSoTimeout(timeout);

if (proxyHost != null && proxyPort != null) {

Log.getLogger().info(“Using proxy = ” + proxyHost + “:” + proxyPort);

http.getHostConfiguration().setProxy(proxyHost, Integer.parseInt(proxyPort));

}

HttpMethod method = new GetMethod(url);

http.getParams().setParameter(“http.socket.timeout”, timeout);

http.getParams().setParameter(“http.connection.timeout”, timeout);

method.getParams().setParameter(“http.socket.timeout”, timeout);

method.getParams().setParameter(“http.connection.timeout”, timeout);

try {

int statusCode = http.executeMethod(method);

if (statusCode != HttpStatus.SC_OK && statusCode != 202) {

byteArray = null;

Log.getLogger().debug(“Status HTTP:”+ statusCode);

} else { byteArray = method.getResponseBody(); }

} catch (Exception e) { Log.getLogger().error(“Error in httpGet”, e); byteArray = null; }

finally { method.releaseConnection(); }

String filename = sourceURL1.getFile();

BASE64Encoder encoder = new BASE64Encoder();

if(filename.contains(“/”)) filename = filename.substring(filename.lastIndexOf(“/”)+1);

suara_name[g] = Integer.toString(filename.hashCode());

smil = replace(smil, suaras.get(g), suara_name[g]);

String format = filename.substring(filename.lastIndexOf(“.”)).toLowerCase();

String type = “audio/wav”;

if(format.equals(“.wav”)) type = “audio/wav”;

else if(format.equals(“.mid”) || format.equals(“.midi”)) type = “audio/midi”;

else if(format.equals(“.mp3”)) type = “audio/mp3”;

else if(format.equals(“.3gp”)) type = “audio/3gpp”;

else if(format.equals(“.aac”)) type = “audio/aac”;

else if(format.equals(“.amr”)) type = “audio/amr”;

else if(format.equals(“.amr-wb”)) type = “audio/amr-wb”;

else if(format.equals(“.mpeg”) || format.equals(“.mpg”)) type = “audio/mpeg”;

else if(format.equals(“.mp4”)) type = “audio/mp4”;

else if(format.equals(“.rmv”) || format.equals(“.rmvb”)) type = “audio/rmv”;

else continue;

data+=”——_=_NextPart_220A_232_2517.E38\n” +

“Content-Type: “+type+”; Name=”+filename+”\n” +

“Content-Transfer-Encoding: base64\n” +

“Content-ID: <“+filename+”>\n” +

“Content-Location: “+filename+”\n\n” +

encoder.encode(byteArray)+”\n” + “\n”;

} catch (MalformedURLException e) {// TODO Auto-generated catch block e.printStackTrace(); }

catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

String video_name[] = null;

if(videos != null) {

video_name = new String[videos.size()];

for(int g = 0; g < videos.size(); g++) {

if(max_depth == g) max_depth++;

URL sourceURL1 = null;

try {

String url = videos.get(g).replaceAll(” ” , “+”);

sourceURL1 = new URL(url);

byte[] byteArray = null;

HttpClient http = new HttpClient();

http.getParams().setSoTimeout(timeout);

if (proxyHost != null && proxyPort != null) {

Log.getLogger().info(“Using proxy = ” + proxyHost + “:” + proxyPort);

http.getHostConfiguration().setProxy(proxyHost, Integer.parseInt(proxyPort));

}

HttpMethod method = new GetMethod(url);

http.getParams().setParameter(“http.socket.timeout”, timeout);

http.getParams().setParameter(“http.connection.timeout”, timeout);

method.getParams().setParameter(“http.socket.timeout”, timeout);

method.getParams().setParameter(“http.connection.timeout”, timeout);

try {

int statusCode = http.executeMethod(method);

if (statusCode != HttpStatus.SC_OK && statusCode != 202) {

byteArray = null;

Log.getLogger().debug(“Status HTTP:”+ statusCode);

} else { byteArray = method.getResponseBody(); }

}

catch (Exception e) { Log.getLogger().error(“Error in httpGet”, e); byteArray = null; }

finally { method.releaseConnection(); }

String filename = sourceURL1.getFile();

BASE64Encoder encoder = new BASE64Encoder();

if(filename.contains(“/”)) filename = filename.substring(filename.lastIndexOf(“/”)+1);

video_name[g] = Integer.toString(filename.hashCode());

smil = replace(smil, videos.get(g), video_name[g]);

String format = filename.substring(filename.lastIndexOf(“.”)).toLowerCase();

String type = “video/mpeg”;

if(format.equals(“mpeg”) || format.equals(“mpg”)) type = “video/mpeg”;

else if(format.equals(“.3gp”) || format.equals(“.3gpp”)) type = “video/3gpp”;

else if(format.equals(“.mp4”)) type = “video/mp4”;

else if(format.equals(“.rmv”) || format.equals(“.rmvb”)) type = “video/rmv”;

else continue;

data+=”——_=_NextPart_220A_232_2517.E38\n” +

“Content-Type: “+type+”; Name=”+filename+”\n” +

“Content-Transfer-Encoding: base64\n” +

“Content-ID: <“+filename+”>\n” +

“Content-Location: “+filename+”\n\n” +

encoder.encode(byteArray)+”\n” + “\n”;

} catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); }

catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }

}

}

if(use_smil != null && !use_smil.equals(“”) && !use_smil.equals(“false”)) {

if(use_smil.toLowerCase().equals(“default”)){

smil = “<smil><head><layout><root-layout/><region id=\”upper\” top=\”0\” left=\”0\” /><region id=\”lower\” top=\”100\” left=\”0\” /><region id=\”lowest\” top=\”200\” left=\”0\” /></layout></head><body>”;

for(int g = 0; g < max_depth; g++) {

smil += “<par dur=\”3s\”>”;

if(g < texts.length) smil += “<text region=\”lower\” src=\”text”+g+”.txt\” />”;

if(gambar_name != null && g < gambar_name.length) smil += “<img region=\”upper\” src=\””+gambar_name[g]+”\” />”;

if(video_name != null && g < video_name.length) smil += “<video region=\”lowest\” src=”+video_name[g]+” />”;

if(suara_name != null && g < suara_name.length) smil += “<audio src=”+suara_name[g]+” />”;

smil += “</par>”;

}

smil += “</body></smil>”;

}

data += “——_=_NextPart_220A_232_2517.E38\n” +

“Content-Type: application/smil; charset=\”us-ascii\”; Name=smil4.smil\n” +

“Content-ID: <smil4.smil>\n” +

“Content-Location: smil4.smil\n” + “\n” +

“<?xml version=\”1.0\” encoding=\”utf-8\”?>” +

“<!DOCTYPE smil PUBLIC \”-//3GPP//DTD SMIL rel5//EN\” \”http://www.3gpp.org/SMIL20/PSS5\”>” + smil + “\n”;

}

data += “——_=_NextPart_220A_232_2517.E38–\n” + “—-_=_NextPart_21B2_1EE0_E97.2593–\n\n”;

Finally, here’s the code to submit the header + contents (data) to the MMSC using HTTP Socket SOAP:

String dat;

try {

Socket so = new Socket(mmsc_ip, mmsc_port);

BufferedOutputStream bos = new BufferedOutputStream(so.getOutputStream());

String msg = header + data;

BufferedReader br2 = new BufferedReader(new InputStreamReader(so.getInputStream()));

bos.write(msg.getBytes());

bos.flush();

dat = “”;

Log.getLogger().debug(“MmsSOAP request :”+msg);

String temp;

while((temp = br2.readLine()) != null){

dat += temp;

}

so.close();

try {

String a = dat.substring(dat.indexOf(“<StatusCode>”) + 12, dat.indexOf(“</StatusCode>”));

Log.getLogger().info(“MmsSOAP response :”+a);

ret = Integer.parseInt(a);

} catch (Exception e) {  // TODO Auto-generated catch block  ret = -9;  e.printStackTrace();  } }

catch (UnknownHostException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }

catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }

For the response, normally MMSC will reply response String 1000 as Success.

Tags: , , , , , , , , , ,

About sisusilo

I was born after we celebrate youngman promise about their commitment to this country

8 responses to “How to send MMS using SOAP MM7”

  1. antonrifco says :

    haha. sayangnya ga laku mms brader. padahal powerful. keasyikan pake sms.

    sekarang malah mau ke-replace IT-based messaging system => BBM :d

  2. marqo says :

    mantap, ada dokumentasinya.. hehehe
    buat update malena nih..
    warisan bro anton

  3. ashish says :

    do you have complete zip or .rar file of compele codoe

  4. Lashan Sulalith Silva says :

    can u explain more about this codes step by step ? if u have complete code send me a link

  5. Suresh says :

    Can You Please send me the complete code for this. I tried to this with PHP and nusoap. for single attachment it is working fine. but for the multi attachments with smil it is not working.

  6. nitin says :

    do you have complete zip cor or .rar file of this code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: