基于boost 的苹果apns消息推送实现(1)

1. 为了当时测试,做了2份C++代码实现,一份是基于boost的实现 较完整,一份是C++加Openssl实现(可以用,不少细节需要调整)
2. 本模块只涉及apns客户端部分
3. 涉及boost的主要模块有boost bind,boost asio,boost ssl,boost deadline_timer, boost 正则表达式
4. 有一点需要注意,最初使用的sslv23/sslv2/sslv3只能和apple 建立连接,但一直是handshake失败,郁闷了相当长一段时间
最后换tls连接,握手成功!
摘自apple的一段文档:
Provider-to-Service Connection Trust
Connection trust between a provider and APNs is also established through TLS peer-to-peer authentication. The procedure is similar to that described in Service-to-Device Connection Trust. The provider initiates a TLS connection, gets the server certificate from APNs, and validates that certificate. Then the provider sends its provider certificate to APNs, which validates it on its end. Once this procedure is complete, a secure TLS connection has been established; APNs is now satisfied that the connection has been made by a legitimate provider
5.苹果官方文档 https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW9
6. 这一篇文章是C++加 Opnessl原始代码实现:(正在写)

boost版本源码:
1. IS_SERVER_LOG_ERROR 宏是用来打日志,由于是项目中要用的模块,牵涉较多 ,就不提供此宏定义了**
2. 代码中接口 sub_utf8_string_of 用来截断utf8字符串,汉字是3个字节避免出现不完整截断汉字的情况
apple_apns_asio_ssl_client.hpp

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/regex.hpp>
#include <boost/timer.hpp>

#define IS_SERVER_LOG_ERROR 
//void      apple_apns_reconnect_handler    (const boost::system::error_code& error );
std::string apple_apns_ssl_password_callback( std::size_t max_length, boost::asio::ssl::context::password_purpose purpose);

// 返回开始地址和字节数;
// src_utf8_str_len 源字节数,-1 等效 strlen(src_utf8_str) ;
// max_bytes        获取的字符串所占的最大字节数(不含0字符);
//                  -1 表示截取到src_utf8_str_len所对应的最后一个字符;
std::pair<const char*, int> sub_utf8_string_of(const char* src_utf8_str, int src_utf8_str_len, int max_bytes);

class apple_apns_asio_ssl_client
{
public:
    apple_apns_asio_ssl_client( boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>& endpoint_iterator )
        : socket_(io_service, context)
        , endpoint_iterator_(endpoint_iterator)
        , reconnect_callback_impl_(io_service)
        , is_handshake_ok_(false)
        , is_handshake_ing_(false)
        , is_post_recv_req_(false)
        , is_post_reconnect_req_( false)
    {
        //ptr_io_service_ = &io_service;
        memset(&response_packet,0,sizeof(response_packet));
        socket_.set_verify_mode(boost::asio::ssl::verify_peer);
        socket_.set_verify_callback(
            boost::bind(&apple_apns_asio_ssl_client::verify_certificate, this, _1, _2));
    }

    bool verify_certificate(bool preverified,
        boost::asio::ssl::verify_context& ctx)
    {
        // The verify callback can be used to check whether the certificate that is
        // being presented is valid for the peer. For example, RFC 2818 describes
        // the steps involved in doing this for HTTPS. Consult the OpenSSL
        // documentation for more details. Note that the callback is called once
        // for each certificate in the certificate chain, starting from the root
        // certificate authority.

        // In this example we will simply print the certificate‘s subject name.
        char subject_name[256];
        X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());

        X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
        IS_SERVER_LOG_COUT_TRACE(string("Verifying").append(subject_name).append("\n").c_str() );

        char issuer_name[256] = {};
        X509_NAME_oneline(X509_get_issuer_name(cert),issuer_name,256);
        IS_SERVER_LOG_COUT_TRACE(string("issuer name: ").append(issuer_name).append("\n").c_str() );

        char peer_CN[256] = {0};
        X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, peer_CN, 255);

        int err = X509_STORE_CTX_get_error(ctx.native_handle());
        if ( err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_CERT_UNTRUSTED )
        {
            return 1;           // this is ok!
        }

        return preverified;
    }

    void handle_connect(const boost::system::error_code& error,
        boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
    {
        if (!error)
        {
            socket_.async_handshake(boost::asio::ssl::stream_base::client,
                boost::bind(&apple_apns_asio_ssl_client::handle_handshake, this,
                boost::asio::placeholders::error));

        }
        else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
        {
            //socket_.lowest_layer().close();
            boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
            socket_.lowest_layer().async_connect(endpoint,
                boost::bind(&apple_apns_asio_ssl_client::handle_connect, this,
                boost::asio::placeholders::error, ++endpoint_iterator));
        }
        else
        {
            //is_handshake_ing_ = false;
            set_reconnect_handler();
            //std::cout << "logic_asio_ssl_client connect failed: " << error.message() << "\n";
            IS_SERVER_LOG_ERROR(string("logic_asio_ssl_client connect failed: ").append(error.message()).c_str());
        }
    }

    void handle_handshake(const boost::system::error_code& error)
    {
        set_handshake_ing(false);
        if ( !error )
        {
            IS_SERVER_LOG_COUT_TRACE("apple_apns_asio_ssl_client Handshake success");
            set_handshale_ok(true);
            //auto_push_message();
        }
        else
        {
            IS_SERVER_LOG_FATAL_ERROR(string("apple_apns_asio_ssl_client Handshake failed").append(error.message()).c_str());
            set_reconnect_handler();
            //reconnect();
        }
    }

    void handle_write(const boost::system::error_code& error,
        size_t bytes_transferred)
    {
        if (!error)
        {
            async_recv();
            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Write success: %d transferred") % bytes_transferred ).c_str() );
        }
        else
        {
            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Write reconnect: %s") % error.message() ).c_str() );
            //if ( error.value() == 10054 ) {   // disconnect
                set_reconnect_handler();
                //reconnect();
            //}
        }
    }

    void handle_read(const boost::system::error_code& error,
        size_t bytes_transferred)
    {

        do
        {
            if ( error )
                break;
            if ( bytes_transferred == 0)
                break;

            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Read %d byte") % bytes_transferred ).c_str() );

            if ( bytes_transferred == 6)        // apple response
            {
                memcpy(&response_packet.command,    reply_+0, 1);
                memcpy(&response_packet.status,     reply_+1, 1);
                memcpy(&response_packet.identityfer,reply_+2, 4);
                break;
            }

            set_post_recv_req(false);
            return;

        }while(false);

        set_reconnect_handler();

        IS_SERVER_LOG_COUT_TRACE(
            boost::str( boost::format("apple_apns_asio_ssl_client Read failed: %s ") % error.message() ).c_str() );
    }

    void set_reconnect_handler( )
    {
        if ( is_post_reconnect_req() )      {
            return ;
        }

        set_post_reconnect_req( true );
        reconnect( );
        reconnect_callback_impl_.expires_from_now(boost::posix_time::seconds(5));
        reconnect_callback_ impl_.async_wait(boost::bind(&apple_apns_asio_ssl_client::reset_reconnect_flag,
            this, boost::asio::placeholders::error));
    }

    void reset_reconnect_flag(const boost::system::error_code& error )
    {
        set_post_reconnect_req( false );

        if ( error != boost::asio::error::operation_aborted )       // Timer was not cancelled, take necessary action.
        {
            //reconnect( );
        }
        else
        {
            IS_SERVER_LOG_ERROR(string("is.server.service.apple_apns_asio_ssl_client.reset_reconnect_flag ")
                .append(error.message()).c_str());
        }
    }

public:
    void auto_push_message( )
    {
        if ( !is_handshake_ok() )
            return;

        static boost::int32_t s_cur_time = clock();
        if ( clock() - s_cur_time >= 1000000)
        {
            //s_cur_time = clock( );
            //char szPayLoad[100] = { };
            //#pragma warning(disable:4996)
            //sprintf(szPayLoad,"中文测试 apple apns test by boost asio ssl %d",time(NULL));
            //#pragma warning(default:4996)
            //async_pushmessage("d2eb47674417c05c5a6f474bddef0391242e1c4d9ea3385e8f55c427d3c7d2ed",szPayLoad,apns_push_msg_reply ,1 ,time(NULL)+24*3600);
        }
    }

    void async_connect( )
    {
        if ( is_handshake_ing( ) || is_handshake_ok( ) )
            return ;

        set_handshake_ing(true);

        boost::asio::ip::tcp::resolver::iterator  endpoint_iterator = endpoint_iterator_;
        boost::asio::async_connect(socket_.lowest_layer(),endpoint_iterator,
            boost::bind(&apple_apns_asio_ssl_client::handle_connect, this,
            boost::asio::placeholders::error, ++endpoint_iterator));

    }

    bool async_send(const char* szbuffer ,boost::int32_t buffer_size)
    {
        if ( !szbuffer || buffer_size  <= 0)
            return false;

        boost::asio::async_write(socket_ ,
            boost::asio::buffer(szbuffer/*request_*/, buffer_size),
            boost::bind(&apple_apns_asio_ssl_client::handle_write, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

        return true;
    }

    bool async_recv( )
    {
        if ( is_post_recv_req() )       {
            IS_SERVER_LOG_FATAL_ERROR( boost::str( boost::format("apple_apns_asio_ssl_client has been posted") ).c_str() );
            return false;
        }

        set_post_recv_req(true); 
        socket_.async_read_some(
            boost::asio::buffer(reply_, max_len_receive),
            boost::bind(&apple_apns_asio_ssl_client::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

        return true;
    }

    void disconnect( )
    {
        set_handshale_ok(false);
        set_handshake_ing(false);
        set_post_recv_req(false);
        //socket_.get_io_service().stop();
        //socket_.get_io_service().reset();
        try
        {
            socket_.lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
            socket_.lowest_layer().close();
        }
        catch (std::exception* e)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch : ").
                append(e->what()).c_str() );
        }
        catch(boost::exception& e)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch : ").
                append( boost::diagnostic_information(e) ).c_str() );
        }
        catch(...)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch ").
                append("").c_str() );
        }

    }

    void reconnect( )
    {
        disconnect();
        async_connect();
    }

public:
    enum apns_push_msg_type
    {
        apns_push_msg_reply     = 1,        // apple will reply
        apns_push_msg_noreply   = 2,        // apple not reply
    };

private:
    enum 
    {
        max_len_receive     = 1024,         // max len to receive
        mus_len_command     = 1,            // required
        mus_len_identifier  = 4,            // optional
        mus_len_expiry      = 4,            // optional
        mus_len_tokenlen    = 2,            // required
        mus_len_token       = 32,           // required must be 32
        mus_len_payloadlen  = 2,            // required
        max_len_payload     = 256,          // required The payload must not exceed 256 bytes and must not be null-terminated
        max_len_apns_msg_noreply    = mus_len_command + mus_len_tokenlen + mus_len_token + mus_len_payloadlen + max_len_payload,
        max_len_apns_msg_reply      = max_len_apns_msg_noreply + mus_len_identifier + mus_len_expiry,
    };


    struct apns_error_response_packet
    {
        char command;
        char status;
        boost::uint32_t identityfer;
    };

    boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  socket_;
    boost::asio::ip::tcp::resolver::iterator                endpoint_iterator_;
    boost::asio::deadline_timer                             reconnect_callback_impl_;

    //typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> boost_asio_ssl_stream_socket;
    //boost::shared_ptr<boost_asio_ssl_stream_socket>   socket_;
    //char                          request_[max_len_receive];
    char                            reply_[max_len_receive];
    bool                            is_handshake_ok_;           // 握手成功
    bool                            is_handshake_ing_;          // 正在握手
    bool                            is_post_recv_req_;              // 
    bool                            is_post_reconnect_req_;     // 已经投递了重连请求
    apns_error_response_packet      response_packet;
    //boost::timer                  reconnect_interval_;
    //boost::asio::io_service*      ptr_io_service_;

public:
    bool is_handshake_ok       (       )    { return is_handshake_ok_;       }
    void set_handshale_ok      (bool b )    { is_handshake_ok_ = b;          }
    bool is_handshake_ing      (       )    { return is_handshake_ing_;      }
    void set_handshake_ing     (bool b )    { is_handshake_ing_ = b;         }
    bool is_post_recv_req      (       )    { return is_post_recv_req_;      }
    void set_post_recv_req     (bool b )    { is_post_recv_req_ = b;         }
    bool is_post_reconnect_req (       )    { return is_post_reconnect_req_; }
    void set_post_reconnect_req( bool b)    { is_post_reconnect_req_ = b;    }
public:

    // 字符串替换
    void fix_payload_message_symbol( string& fix_symbol, const string& strFind, const string& strReplace)
    {
        size_t nPosfind  = fix_symbol.find(strFind.c_str());
        while ( nPosfind != string::npos && nPosfind <= fix_symbol.length() )
        {
            fix_symbol.replace(nPosfind,1,strReplace.c_str());
            nPosfind = fix_symbol.find(strFind.c_str(),nPosfind + strReplace.length());
        }
    }

    // 由于截断有可能最后一个字符恰好是一个落单的反斜杠,造成不符合jason格式,推送失败
    boost::int32_t check_payload_single_backslash( char* payload_body_out, boost::int32_t payload_body_len )
    {
        if ( !payload_body_out || payload_body_len <= 0 )
            return false;

        for (int i = 0; i < payload_body_len; )
        {
            if ( payload_body_out[i] == ‘\\‘ && i+1 < payload_body_len )    // 还没到结尾
            {
                i += 2;     continue;
            }

            if ( payload_body_out[i] == ‘\\‘ && i+1 == payload_body_len )   // payload_body_out size is max_len_payload 所以 i+1 也不会越界 
            {
                payload_body_out[i] = ‘\0‘;
                return payload_body_len - 1;
            }

            ++i;
        }

        return payload_body_len;
    }

    // payload 不符合jason格式,会推送失败
    typedef const std::string   my_cstdstr;
    bool conform_payload_to_jason_format(my_cstdstr& strSource , std::string& strFromated)
    {
        // App 图片格式 <img src=\"/wx\" />
        //static my_cstdstr strFace_utf8    = ansi_to_utf8("<表情>");
        static my_cstdstr strFace_utf8 = "\74\350\241\250\346\203\205\76";      // <表情>

        // App 表情格式 <img src=\"http://221.228.248.245/t/2015-3-11/15/0_60916869_20150311000000151141_2d5651_19719\" />
        //static my_cstdstr strImage_utf8   = ansi_to_utf8("<图片>");
        static my_cstdstr strImage_utf8 = "\74\345\233\276\347\211\207\76";     // <图片>

        //boost_regex_format_string(strSource, "<img src=.+? />", strFace_utf8 , strImage_utf8, strFromated);
        boost::regex regExpFace ( "<img src=.+? />" /*,boost::regex::perl*/ );
        boost::regex regExpImage( "<img src=\"http://(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}.+? />" );
        strFromated = boost::regex_replace(strSource,   regExpImage,    strImage_utf8);     // regExpImage是regExpFace的子集,先替换掉。
        strFromated = boost::regex_replace(strFromated, regExpFace,     strFace_utf8);


        // 每一个反斜杠需要多加一个反斜杠去转译
        fix_payload_message_symbol(strFromated,string("\\"),string("\\\\"));

        // 把\n 和\r都替换成 \\n 和 \\r
        fix_payload_message_symbol(strFromated,string("\n"),string("\\n"));
        fix_payload_message_symbol(strFromated,string("\r"),string("\\r"));

        return true;
    }

    bool boost_regex_format_string( my_cstdstr& strSource, my_cstdstr& regExp , my_cstdstr& strReplce, my_cstdstr& strReplce2,std::string& strFromated)
    {
        boost::smatch   matchResults;
        boost::regex    boostRegExp( regExp /*,boost::regex::perl*/ );
        bool            hasFormated = false;
        std::string     strRepl;    
        strFromated                       = strSource;
        std::string::const_iterator start = strFromated.begin();
        std::string::const_iterator end   = strFromated.end();
        while (boost::regex_search(start, end, matchResults, boostRegExp))
        {
            boost::int32_t nPosBegin    = matchResults[0].first  - strFromated.begin();
            boost::int32_t nPosEnd      = matchResults[0].second - strFromated.begin();
            boost::int32_t nNumRep      = nPosEnd - nPosBegin;

            string substr(matchResults[0].first, matchResults[0].second);
            if ( boost::regex_search(substr, boost::regex("http://(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}")) )
                strRepl = strReplce2;
            else
                strRepl = strReplce;

            //cout << nPosBegin << ‘ ‘ << nPosEnd << ‘ ‘ << matchResults[0] << endl;
            if ( strRepl.length() <= nNumRep )
            {
                hasFormated = true;
                strFromated.replace(nPosBegin, nNumRep, strRepl.c_str());
            }

            //start = matchResults[0].second;       // 上个匹配的下一个字节,节约下一次匹配的时间,如果要替换源串的字串的话这种方式不可行
            start   = strFromated.begin();          // 再次重新开始匹配,对于小字符串效率影响不大
            end     = strFromated.end();            // update end   itr
            if ( start >= end ) break;
        }

        return hasFormated;
    }


    // apple push message format 1
    // type:  | command | token len(big endian) | token(binary) | payload len(big endian) |  payload |
    // buff:  |---------|-----------------------|---------------|-------------------------|----------|
    // byte:  |    1    |            2          |      32       |            2            |   <=256  |
    int async_pushmessage( const string& szDeviceToken, const string& szPayloadComment , apns_push_msg_type push_type = apns_push_msg_noreply ,
                                boost::uint32_t msgIdentityfer = 0, boost::uint32_t msgTimeExpiry = 0 )
    {
        if ( !is_handshake_ok() )
            return 0;

        if ( szDeviceToken.empty() || szPayloadComment.empty())
            return 0;

        string strPayloadCommentNew;
        conform_payload_to_jason_format(szPayloadComment, strPayloadCommentNew);

        // build the payload body
        char    payload_body[max_len_payload]         = { };
        size_t  payload_body_len = build_payload_body_str_utf8(strPayloadCommentNew, payload_body);
        if ( payload_body_len <=0 || payload_body_len > max_len_payload)    {
            IS_SERVER_LOG_FATAL_ERROR( boost::str( 
                boost::format("async_pushmessage_apns build_payload_body failed ! payload_body_len: %s") % payload_body_len ).c_str() );
            return 0;
        }

        char    tokenBytes[mus_len_token] = {};
        //char  message[max_len_apns_msg] = {};
        std::vector<char> vec_message;
        vec_message.reserve( max((boost::int32_t)max_len_apns_msg_reply, (boost::int32_t)max_len_apns_msg_noreply) );
        char    *message    = vec_message.data();
        char    *pointer    = vec_message.data();

        // 1. commnad
        unsigned char command = 0;
        if ( apns_push_msg_reply == push_type )
        {
            command = 1;
        }

        memcpy(pointer, &command, mus_len_command);  
        pointer += mus_len_command;

        // 1.1 optional
        if ( push_type == apns_push_msg_reply )
        {
            // identityfer
            boost::uint32_t identityfer = msgIdentityfer;
            memcpy(pointer, &identityfer, mus_len_identifier);  
            pointer += mus_len_identifier;

            // expiry
            boost::uint32_t tExpiry = msgTimeExpiry;
            memcpy(pointer, &tExpiry, mus_len_expiry);  
            pointer += mus_len_expiry;
        }

        // 2. token len
        //size_t  tokenLen  = strlen(szDeviceToken);
        unsigned short networkTokenLen  = htons((u_short)mus_len_token);        // big endian
        memcpy(pointer, &networkTokenLen, mus_len_tokenlen);   
        pointer += mus_len_tokenlen;

        // 3. token
        token2bytes(szDeviceToken.c_str(), tokenBytes);
        //strtol(szDeviceToken,(char**)(&tokenBytes),2);
        memcpy(pointer, tokenBytes, mus_len_token);
        pointer += mus_len_token;

        // 4. payload len
        unsigned short networkPayloadLen = htons((unsigned short)payload_body_len); // big endian
        memcpy(pointer, &networkPayloadLen, mus_len_payloadlen);  
        pointer += mus_len_payloadlen;

        // 5. payload
        memcpy(pointer, payload_body, payload_body_len);
        pointer += payload_body_len;

        // 6. async send
        int msgLength  = 0;
        msgLength = (int)(pointer - message);
        async_send(message, msgLength);

        return msgLength;
    }

    boost::int32_t build_payload_body_str_notutf8(const char* src_not_utf8_str, char* payload_body_out )
    {
        char    payload_format[]                    = "{\"aps\":{\"alert\":\"%s\",\"badge\":1,\"sound\":\"default\"}}";
        char    payload_comment_cutoff[max_len_payload] = { };
        boost::int32_t  payload_format_len          = strlen(payload_format);
        std::string szPayloadComment_utf8           = src_not_utf8_str;
        szPayloadComment_utf8 = boost::locale::conv::between(src_not_utf8_str,"UTF-8","GBK");       // 
        boost::int32_t  payload_comment_len         = szPayloadComment_utf8.length();
        boost::int32_t  payload_cutoff_len          = (payload_comment_len + payload_format_len - 2) - max_len_payload;
        boost::int32_t  payload_body_len            = 0;
        if ( payload_cutoff_len > 0)    {
    #ifdef __linux
        snprintf(payload_comment_cutoff, payload_comment_len - payload_cutoff_len, "%s",szPayloadComment_utf8.c_str());
    #else
        _snprintf(payload_comment_cutoff, payload_comment_len - payload_cutoff_len, "%s",szPayloadComment_utf8.c_str());
    #endif
            payload_body_len = max_len_payload;
        }
        else if ( payload_cutoff_len == 0)
        {
            payload_body_len = max_len_payload;
        }
        else
            memcpy(payload_comment_cutoff, szPayloadComment_utf8.c_str(), payload_comment_len);

    #ifdef __linux
        snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
    #else
        _snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
    #endif

        if ( payload_body_len <= 0) payload_body_len = strlen(payload_body_out);
        return payload_body_len;
    }

    boost::int32_t build_payload_body_str_utf8(const string& src_utf8_str, char* payload_body_out )
    {
        char    payload_format[]                    = 
            "{"
                "\"aps\":"
                    "{"
                        "\"alert\":\"%s\","
                        "\"badge\":1,"
                        "\"sound\":\"default\""
                    "}"
            "}";

        char    payload_comment_cutoff[max_len_payload] = { };
        boost::int32_t  payload_format_len          = strlen(payload_format);
        boost::int32_t  payload_comment_len         = src_utf8_str.length();
        boost::int32_t  payload_cutoff_len          = (payload_comment_len + payload_format_len - 2) - max_len_payload;     // ‘%s‘ is take up 2 bytes
        boost::int32_t  payload_body_len            = 0;
        std::pair<const char*, int> pair_src_utf8_str_cutoff;
        if ( payload_cutoff_len > 0)    
        {
            boost::int32_t new_comment_len          = payload_comment_len - payload_cutoff_len;
            pair_src_utf8_str_cutoff = sub_utf8_string_of(src_utf8_str.c_str(),src_utf8_str.length(), new_comment_len);
            memcpy(payload_comment_cutoff, pair_src_utf8_str_cutoff.first, pair_src_utf8_str_cutoff.second);
            // 由于截断有可能最后一个字符恰好是一个落单的反斜杠,造成不符合jason格式,推送失败。
            boost::int32_t fix_comment_len          = check_payload_single_backslash(payload_comment_cutoff, pair_src_utf8_str_cutoff.second);
            payload_body_len = payload_format_len + fix_comment_len - 2;        // ‘%s‘ is take up 2 bytes

        }
        else if ( payload_cutoff_len == 0)
        {
            memcpy(payload_comment_cutoff, src_utf8_str.c_str(), payload_comment_len);
            payload_body_len = max_len_payload;
        }
        else
        {
            memcpy(payload_comment_cutoff, src_utf8_str.c_str(), payload_comment_len);
        }

        #ifdef __linux
        snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
        #else
        _snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
        #endif

        if ( payload_body_len <= 0) payload_body_len = strlen(payload_body_out);        // payload_body_out could null-terminated
        return payload_body_len;
    }

    void token2bytes(const char *token, char *bytes)
    {
        int val;  
        while (*token) 
        {  
            #pragma warning(disable:4996)
            sscanf(token, "%2x", &val);  
            #pragma warning(default:4996)

            *(bytes++) = (char)val;  
            token += 2;  
            while (*token == ‘ ‘) {  
                ++token;                // skip space
            }  
        }  
    } 
};

std::string apple_apns_ssl_password_callback( std::size_t max_length, boost::asio::ssl::context::password_purpose purpose)
{
    const char* szPasswd = "hc123";
    //const char* szPasswd =  the_app().ref_config().get_config_apple_apns_password().c_str();
    std::cout<< "password_callback password: " << szPasswd <<std::endl;
    return szPasswd;
}

// 暂未实现,为编译通过直接进行截断
std::pair<const char*, int> sub_utf8_string_of(const char* src_utf8_str, int src_utf8_str_len, int max_bytes)
{
    int nDis = src_utf8_str_len - max_bytes;
    if ( nDis >= 0 )
        return std::pair<const char*, int>(src_utf8_str,src_utf8_str_len);

    return std::pair<const char*, int>(src_utf8_str, max_bytes);
}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。