今天想起来很久以前碰到的一个ATL BUG- -| 回首页 | 2004年索引 | - -新浪灌水机一部

其实WAP Push没有那么神秘- -

                                      

只要写一个XML文件,然后用WBXML编码就可以了。下面是一个Sample

<si>

  <indication href="http://wap.fractalist.cn/" action="signal-high">

    挑战脉动,挑战你我

  </indication

</si>

这个XML的DTD定义在http://www.openmobilealliance.org/tech/dtd/si.dtd

WMXML定义在http://www.w3.org/1999/06/NOTE-wbxml-19990624/

编码后作为二进制短消息发送给用户,记得讲tp_udhi设为1就可以了

WBXML编码的代码如下:

#include <map>
#include <vector>
#include <string>
#include <time.h>
#pragma warning(disable : 4267)
#include <StringHelper.h>
#pragma warning(default : 4267)
#include <boost/cstdint.hpp>

#include <sstream>
#include <iomanip>

using boost::uint8_t;
using std::cout;
using std::endl;
using std::setw;
using std::hex;
using std::setfill;

/// <summary>
/// Series of well known constants and static uint8_t values used when encoding
/// a document to WBXML
/// </summary>
class WBXML
{
public:
 static const uint8_t CHAR_NULL = 0x00;

 static const uint8_t VERSION_1_1 = 0x01;
 static const uint8_t VERSION_1_2 = 0x02;

 static const uint8_t CHARSET_UTF_8 = 0x6A;

 static const uint8_t TAGTOKEN_END = 0x01;
 static const uint8_t TOKEN_INLINE_STRING_FOLLOWS = 0x03;
 static const uint8_t TOKEN_OPAQUEDATA_FOLLOWS = 0xC3;

 static uint8_t setTagTokenIndications(uint8_t token, bool hasAttributes, bool hasContent)
 {
  if (hasAttributes)
   token |= 0xC0;
  if (hasContent)
   token |= 0x40;

  return token;
 }
};

/// <summary>
/// Encapsulates the Service Indication WAP Push instruction.
/// Full documentation can be found at http://www.openmobilealliance.org/tech/affiliates/wap/wap-167-serviceind-20010731-a.pdf?doc=wap-167-serviceind-20010731-a.pdf
/// </summary>
class ServiceIndication
{
public:
 // Allowed values of the action attribute of the indication tag
 enum Action {NotSet = -1, signal_none = 0, signal_low, signal_medium, signal_high, Delete};
 // Well known DTD token
 static const uint8_t DOCUMENT_DTD_ServiceIndication = 0x05;   // ServiceIndication 1.0 Public Identifier

 // Tag Tokens
 static const uint8_t TAGTOKEN_si = 0x5;
 static const uint8_t TAGTOKEN_indication = 0x6;
 static const uint8_t TAGTOKEN_info = 0x7;
 static const uint8_t TAGTOKEN_item = 0x8;

 // Attribute Tokens
 static const uint8_t ATTRIBUTESTARTTOKEN_action[5];
 static const uint8_t ATTRIBUTESTARTTOKEN_created = 0xA;
 static const uint8_t ATTRIBUTESTARTTOKEN_href = 0xB;
 static const uint8_t ATTRIBUTESTARTTOKEN_href_http = 0xC;   // http://
 static const uint8_t ATTRIBUTESTARTTOKEN_href_http_www = 0xD; // http://www.
 static const uint8_t ATTRIBUTESTARTTOKEN_href_https = 0xE;   // https://
 static const uint8_t ATTRIBUTESTARTTOKEN_href_https_www = 0xE; // https://www.
 static const uint8_t ATTRIBUTESTARTTOKEN_si_expires = 0x10;
 static const uint8_t ATTRIBUTESTARTTOKEN_si_id = 0x11;
 static const uint8_t ATTRIBUTESTARTTOKEN_class = 0x12;

 // Attribute Value Tokens
 static const uint8_t ATTRIBUTEVALUETOKEN_com = 0x85;      // .com/
 static const uint8_t ATTRIBUTEVALUETOKEN_edu = 0x86;      // .edu/
 static const uint8_t ATTRIBUTEVALUETOKEN_net = 0x87;      // .net/
 static const uint8_t ATTRIBUTEVALUETOKEN_org = 0x88;      // .org/

private:
 static std::map<std::string, uint8_t> m_hrefStartTokens;
 static std::map<std::string, uint8_t> m_attributeValueTokens;
 static int initializeServiceIndication()
 {
  m_hrefStartTokens["https://www."] = ATTRIBUTESTARTTOKEN_href_https_www;
  m_hrefStartTokens["http://www."] = ATTRIBUTESTARTTOKEN_href_http_www;
  m_hrefStartTokens["https://"] = ATTRIBUTESTARTTOKEN_href_https;
  m_hrefStartTokens["http://"] = ATTRIBUTESTARTTOKEN_href_http;

  m_attributeValueTokens[".com/"] = ATTRIBUTEVALUETOKEN_com;
  m_attributeValueTokens[".edu/"] = ATTRIBUTEVALUETOKEN_edu;
  m_attributeValueTokens[".net/"] = ATTRIBUTEVALUETOKEN_net;
  m_attributeValueTokens[".org/"] = ATTRIBUTEVALUETOKEN_org;
  return 0;
 }

 std::string m_href;
 std::string m_text;
 time_t m_createdAt;
 time_t m_expiresAt;
 Action m_action;

public:
 ServiceIndication(const std::string & href, const std::string & text, Action action) :
   m_href(href), m_text(text), m_action(action)
 {
  static int __unused = initializeServiceIndication();
 } 

 ServiceIndication(const std::string & href, const std::string & text, time_t createdAt, time_t expiresAt) :
  m_href(href), m_text(text), m_action(Action::NotSet), m_createdAt(createdAt), m_expiresAt(expiresAt)
 {
 } 

 ServiceIndication(const std::string & href, const std::string & text, time_t createdAt, time_t expiresAt, Action action) :
  m_href(href), m_text(text), m_action(action), m_createdAt(createdAt), m_expiresAt(expiresAt)
 {
 }

 /// <summary>
 /// Generates a uint8_t array comprising the encoded Service Indication
 /// </summary>
 /// <returns>The encoded body</returns>
 //01 05 6a 00 45 c6 0c 03 77 61 702e6672616374616c6973742e636e0008 01034120574150205075736820
 //746f20746865204672616374616c6973742073697465000101

 //01 05 6a 00 45 c6 0b 03 68 74 74703a2f2f7761702e6672616374616c6973742e636e0008010341205741
 //50205075736820746f20746865204672616374616c6973742073697465000101

 std::vector<uint8_t> getWBXMLBytes() const
 {
  std::vector<uint8_t> vec;
  vec.push_back(WBXML::VERSION_1_1);
  vec.push_back(DOCUMENT_DTD_ServiceIndication);
  vec.push_back(WBXML::CHARSET_UTF_8);
  vec.push_back(WBXML::CHAR_NULL);

  // start xml doc
  vec.push_back(WBXML::SetTagTokenIndications(TAGTOKEN_si, false, true));
  vec.push_back(WBXML::SetTagTokenIndications(TAGTOKEN_indication, true , true));

  // href attribute
  // this attribute has some well known start tokens that
  // are contained within a static hashtable. Iterate through
  // the table and chose the token.
  size_t i = 0;
  uint8_t hrefTagToken = ATTRIBUTESTARTTOKEN_href;
  for(std::map<std::string, uint8_t>::const_iterator it = m_hrefStartTokens.begin(); it != m_hrefStartTokens.end(); ++it)
  {
   const std::string & startString = (*it).first;
   if(m_href.find(startString) == 0)
   {
    hrefTagToken = (*it).second;
    i = startString.length();
    break;
   }
  }
  vec.push_back(hrefTagToken);

  writeInlineString(vec, m_href.substr(i));
  /* 
  * Date elements removed as does not seem to be supported
  * by all handsets, or I'm doing it incorrectly, or it's a version 1.2
  * thing.

  // created attrbute
  stream.WriteByte(ATTRIBUTESTARTTOKEN_created);
  WriteDate(stream, this.CreatedAt);

  // si-expires attrbute
  stream.WriteByte(ATTRIBUTESTARTTOKEN_si_expires);
  WriteDate(stream, this.ExpiresAt);
  */
  // action attibute
  if (m_action != Action::NotSet)
   vec.push_back(getActionToken(m_action));

  // close indication element attributes
  vec.push_back(WBXML::TAGTOKEN_END);

  // text of indication element
  writeInlineString(vec, m_text);

  // close indication element
  vec.push_back(WBXML::TAGTOKEN_END);
  // close si element
  vec.push_back(WBXML::TAGTOKEN_END);

  return vec;
 }


private:
 /// <summary>
 /// Gets the token for the action attribute
 /// </summary>
 /// <param name="action">Interruption level instruction to the handset</param>
 /// <returns>well known uint8_t value for the action attribute</returns>
 uint8_t getActionToken(Action action) const
 {
  return ATTRIBUTESTARTTOKEN_action[(int)action];
 }

 /// <summary>
 /// Encodes an inline string into the stream using UTF8 encoding
 /// </summary>
 /// <param name="stream">The target stream</param>
 /// <param name="text">The text to write</param>
 void writeInlineString(std::vector<uint8_t> & vec, const std::string & text) const
 {
  // indicate that the follow bytes comprise a string
  vec.push_back(WBXML::TOKEN_INLINE_STRING_FOLLOWS);

  // write character bytes
  std::string utf8Text = stringutil::w2utf8(stringutil::a2w(text));
  copy(utf8Text.begin(), utf8Text.end(), back_inserter(vec));

  // end is indicated by a null uint8_t
  vec.push_back(WBXML::CHAR_NULL);
 }
 /// <summary>
 /// Encodes the DateTime to the stream.
 /// DateTimes are encoded as Opaque Data with each number in the string represented
 /// by its 4-bit binary value
 /// eg: 1999-04-30 06:40:00
 /// is encoded as 199904300640.
 /// Trailing zero values are not included.
 /// </summary>
 /// <param name="stream">Target stream</param>
 /// <param name="date">DateTime to encode</param>
 void writeDate(std::vector<uint8_t> & vec, time_t date) const
 {
  struct tm * gm = gmtime(&date);
  uint8_t buffer[7];

  buffer[0] = (uint8_t)( (gm->tm_year + 1900) / 100);
  buffer[1] = (uint8_t)((gm->tm_year + 1900) % 100);
  buffer[2] = (uint8_t)(gm->tm_mon + 1);
  buffer[3] = (uint8_t)(gm->tm_mday);

  uint8_t dateLength = 4;

  if(gm->tm_hour != 0)
  {
   buffer[4] = (uint8_t)gm->tm_hour;
   dateLength = 5;
  }

  if(gm->tm_min != 0)
  {
   buffer[5] = (uint8_t)gm->tm_min;
   dateLength = 6;
  }

  if(gm->tm_sec != 0)
  {
   buffer[6] = (uint8_t)gm->tm_sec;
   dateLength = 7;
  }

  // write to stream
  vec.push_back(WBXML::TOKEN_OPAQUEDATA_FOLLOWS);
  vec.push_back(dateLength);
  copy(buffer, buffer + dateLength, back_inserter(vec));
 }
};

const uint8_t ServiceIndication::ATTRIBUTESTARTTOKEN_action[5] = {0x5, 0x6, 0x7, 0x8, 0x9};
std::map<std::string, uint8_t> ServiceIndication::m_hrefStartTokens = std::map<std::string, uint8_t>();
std::map<std::string, uint8_t> ServiceIndication::m_attributeValueTokens = std::map<std::string, uint8_t>();

/// <summary>
/// Well known values used when generating WSP (Wireless Session Protocol) headers
/// </summary>
class WSP
{
public:
 static const uint8_t TRANSACTIONID_CONNECTIONLESSWSP = 0x25;

 static const uint8_t PDUTYPE_PUSH = 0x06;

 static const uint8_t HEADER_CONTENTLENGTH = 0x8D;

 static const uint8_t HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8[4];

 static const uint8_t HEADER_APPLICATIONTYPE = 0xaf;
 static const uint8_t HEADER_APPLICATIONTYPE_x_wap_application_id_w2 = 0x82;

 static const uint8_t HEADER_PUSHFLAG[2];

};

const uint8_t WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8[4] = {0x03,0xAE,0x81,0xEA};
const uint8_t WSP::HEADER_PUSHFLAG[2] = {0xB4, 0x84};

/// <summary>
/// Well known values used when generating a WDP (Wireless Datagram Protocol) header
/// </summary>
class WDP
{
public:
 static const uint8_t INFORMATIONELEMENT_IDENTIFIER_APPLICATIONPORT = 0x05;
};

/// <summary>
/// Encapsulates an SMS WAP Push message
/// </summary>
class PushMessage
{
private:
 // Ports for the WDP information element, instructing the handset which
 // application to load on receving the message
 static const uint8_t WDP_DESTINATIONPORT[2];
 static const uint8_t WDP_SOURCEPORT[2];

 ServiceIndication serviceIndication;

public:
 PushMessage(const std::string & href, const std::string & text) :
  serviceIndication(href, text, ServiceIndication::signal_high)
 {
 }

 /// <summary>
 /// Generates the body of the SMS message
 /// </summary>
 /// <returns>uint8_t array</returns>
 std::vector<uint8_t> getSMSBytes() const
 {
  std::vector<uint8_t> vec;

  getWDPHeaderBytes(vec);
#ifdef _DEBUG
  cout << "getWDPHeaderBytes\n";
  for(size_t i = 0; i < vec.size(); ++i)
  {
   cout << hex << setw(2) << setfill('0') << ((int)vec[i]);
  }
  cout << "\n" << endl;
  size_t start = vec.size();
#endif
  getPDUBytes(vec);
#ifdef _DEBUG
  cout << "getPDUBytes\n";
  for(size_t i = start; i < vec.size(); ++i)
  {
   cout << hex << setw(2) << setfill('0') << ((int)vec[i]);
  }
  cout << "\n" << endl;
#endif

  return vec;
 }

 /// <summary>
 /// Generates the PDU (Protocol Data Unit) comprising the encoded Service Indication
 /// and the WSP (Wireless Session Protocol) headers
 /// </summary>
 /// <returns>uint8_t array comprising the PDU</returns>
 void getPDUBytes(std::vector<uint8_t> & vec) const
 {
  std::vector<uint8_t> body = serviceIndication.getWBXMLBytes();

  std::vector<uint8_t> header = getWSPHeaderBytes((uint8_t)body.size());
#ifdef _DEBUG
  cout << "getWSPHeaderBytes\n";
  for(size_t i = 0; i < header.size(); ++i)
  {
   cout << hex << setw(2) << setfill('0') << ((int)header[i]);
  }
  cout << "\n" << endl;

  cout << "serviceIndication.getWBXMLBytes\n";
  for(size_t i = 0; i < body.size(); ++i)
  {
   cout << hex << setw(2) << setfill('0') << ((int)body[i]);
  }
  cout << "\n" << endl;
#endif

  copy(header.begin(), header.end(), back_inserter(vec));
  copy(body.begin(), body.end(), back_inserter(vec));
 }

 /// <summary>
 /// Generates the WSP (Wireless Session Protocol) headers with the well known
 /// uint8_t values specfic to a Service Indication
 /// </summary>
 /// <param name="contentLength">the length of the encoded Service Indication</param>
 /// <returns>uint8_t array comprising the headers</returns>
 //25060a03ae81eaaf828dc1b484
 //25060a03ae81eaaf828dc8b484

 std::vector<uint8_t> getWSPHeaderBytes(uint8_t contentLength) const
 {
  std::vector<uint8_t> vec;

  vec.push_back(WSP::TRANSACTIONID_CONNECTIONLESSWSP);
  vec.push_back(WSP::PDUTYPE_PUSH);

  uint8_t headerLength = sizeof(WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8) / sizeof(WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8[0])
   + sizeof(WSP::HEADER_APPLICATIONTYPE)
   + sizeof(WSP::HEADER_APPLICATIONTYPE_x_wap_application_id_w2)
   + sizeof(WSP::HEADER_CONTENTLENGTH)
   + 1
   + sizeof(WSP::HEADER_PUSHFLAG) / sizeof(WSP::HEADER_PUSHFLAG[0]);
  vec.push_back(headerLength);

  copy(WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8
   , WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8 + sizeof(WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8) / sizeof(WSP::HEADER_CONTENTTYPE_application_vnd_wap_sic_utf_8[0])
   , back_inserter(vec));


  vec.push_back(WSP::HEADER_APPLICATIONTYPE);
  vec.push_back(WSP::HEADER_APPLICATIONTYPE_x_wap_application_id_w2);

  vec.push_back(WSP::HEADER_CONTENTLENGTH);
  vec.push_back((uint8_t)(contentLength + 128));

  copy(WSP::HEADER_PUSHFLAG
   , WSP::HEADER_PUSHFLAG + sizeof(WSP::HEADER_PUSHFLAG) / sizeof(WSP::HEADER_PUSHFLAG[0])
   , back_inserter(vec));

  return vec;
 }

 /// <summary>
 /// Generates the WDP (Wireless Datagram Protocol) or UDH (User Data Header) for the
 /// SMS message. In the case comprising the Application Port information element
 /// indicating to the handset which application to start on receipt of the message
 /// </summary>
 /// <returns>uint8_t array comprising the header</returns>
 void getWDPHeaderBytes(std::vector<uint8_t> & vec) const
 {
  uint8_t headerLength = sizeof(WDP::INFORMATIONELEMENT_IDENTIFIER_APPLICATIONPORT)
   + 1
   + sizeof(WDP_DESTINATIONPORT) / sizeof(WDP_DESTINATIONPORT[0])
   + sizeof(WDP_SOURCEPORT) / sizeof(WDP_SOURCEPORT[0]);
  vec.push_back(headerLength);

  vec.push_back(WDP::INFORMATIONELEMENT_IDENTIFIER_APPLICATIONPORT);
  vec.push_back((uint8_t)(sizeof(WDP_DESTINATIONPORT) / sizeof(WDP_DESTINATIONPORT[0]) + sizeof(WDP_SOURCEPORT) / sizeof(WDP_SOURCEPORT[0])));
  copy(WDP_DESTINATIONPORT
   , WDP_DESTINATIONPORT + sizeof(WDP_DESTINATIONPORT) / sizeof(WDP_DESTINATIONPORT[0])
   , back_inserter(vec));
  copy(WDP_SOURCEPORT
   , WDP_SOURCEPORT + sizeof(WDP_SOURCEPORT) / sizeof(WDP_SOURCEPORT[0])
   , back_inserter(vec));
 }
};

const uint8_t PushMessage::WDP_DESTINATIONPORT[] = {0x0b, 0x84};
const uint8_t PushMessage::WDP_SOURCEPORT[] = {0x23, 0xf0};

- 作者: noho 2004年06月17日, 星期四 22:22

Trackback

你可以使用这个链接引用该篇日志 http://publishblog.blogdriver.com/blog/tb.b?diaryID=204457

回复

评论内容: