2008/01/31

Recent entries from same category

  1. mixiの足跡APIをIronPython使ってプログラミングすると、どんなに短いソースになるかを実証する
  2. そうですか、APIは出てるんですか、じゃあこれでアプリケーションが作れるって事ですか、じゃぁ作って見ましょう。
  3. mixi API Service用ライブラリをCPython用に作ってみました。
  4. MixiAPI.pyを密かに画像アップロードに対応してみる
  5. mixiがAPI公開を渋っている訳

mixiの足跡APIをC++、libxml2、libcurlで書くと、どんなに長いソースになるかを実証する。
AtomPP/WSSEなんかやめちゃえ...

perlなら20数行だし、pythonで書いても大した事にはならないだろう...。
Basic認証ならまだしも、WSSEなんか使ったら敷居も高いし、派生アプリケーションが出てこなくなるのはもう分かってるはず。

C++で書くと、こんな事になるんだ...
#以下ソース
#例によって適当クオリティなので添削し放題です。

#include <iostream>
#include <string>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <curl/curl.h>

#ifndef uint8
#define uint8  unsigned char
#endif

#ifndef uint32
#define uint32 unsigned long int
#endif

#ifndef uint64
# ifdef _WIN32
#  ifdef __GNUC__
#   define uint64 unsigned __int64
#  else
#   define uint64 unsigned _int64
#  endif
# else
#  define uint64 unsigned long long
# endif
#endif

#ifndef byte
# define byte unsigned char
#endif

typedef struct
{
    uint32 total[2];
    uint32 state[5];
    uint8 buffer[64];
} sha1_context;

#define GET_UINT32(n,b,i)                       \
{                                               \
    (n) = ( (uint32) (b)[(i)    ] << 24 )       \
        | ( (uint32) (b)[(i) + 1] << 16 )       \
        | ( (uint32) (b)[(i) + 2] <<  8 )       \
        | ( (uint32) (b)[(i) + 3]       );      \
}

#define PUT_UINT32(n,b,i)                       \
{                                               \
    (b)[(i)    ] = (uint8) ( (n) >> 24 );       \
    (b)[(i) + 1] = (uint8) ( (n) >> 16 );       \
    (b)[(i) + 2] = (uint8) ( (n) >>  8 );       \
    (b)[(i) + 3] = (uint8) ( (n)       );       \
}

static void sha1_starts(
        sha1_context *ctx)
{
    ctx->total[0] = 0;
    ctx->total[1] = 0;

    ctx->state[0] = 0x67452301;
    ctx->state[1] = 0xEFCDAB89;
    ctx->state[2] = 0x98BADCFE;
    ctx->state[3] = 0x10325476;
    ctx->state[4] = 0xC3D2E1F0;
}

static void sha1_process(
        sha1_context *ctx, uint8 data[64])
{
    uint32 temp, W[16], A, B, C, D, E;

    GET_UINT32( W[0],  data,  0 );
    GET_UINT32( W[1],  data,  4 );
    GET_UINT32( W[2],  data,  8 );
    GET_UINT32( W[3],  data, 12 );
    GET_UINT32( W[4],  data, 16 );
    GET_UINT32( W[5],  data, 20 );
    GET_UINT32( W[6],  data, 24 );
    GET_UINT32( W[7],  data, 28 );
    GET_UINT32( W[8],  data, 32 );
    GET_UINT32( W[9],  data, 36 );
    GET_UINT32( W[10], data, 40 );
    GET_UINT32( W[11], data, 44 );
    GET_UINT32( W[12], data, 48 );
    GET_UINT32( W[13], data, 52 );
    GET_UINT32( W[14], data, 56 );
    GET_UINT32( W[15], data, 60 );

#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))

#define R(t)                                            \
    (                                                   \
    temp = W[(t -  3) & 0x0F] ^ W[(t - 8) & 0x0F] ^     \
           W[(t - 14) & 0x0F] ^ W[ t      & 0x0F],      \
    ( W[t & 0x0F] = S(temp,1) )                         \
    )

#define P(a,b,c,d,e,x)                                  \
    {                                                   \
    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \
    }

    A = ctx->state[0];
    B = ctx->state[1];
    C = ctx->state[2];
    D = ctx->state[3];
    E = ctx->state[4];

#define F(x,y,z) (z ^ (x & (y ^ z)))
#define K 0x5A827999

    P( A, B, C, D, E, W[0]  );
    P( E, A, B, C, D, W[1]  );
    P( D, E, A, B, C, W[2]  );
    P( C, D, E, A, B, W[3]  );
    P( B, C, D, E, A, W[4]  );
    P( A, B, C, D, E, W[5]  );
    P( E, A, B, C, D, W[6]  );
    P( D, E, A, B, C, W[7]  );
    P( C, D, E, A, B, W[8]  );
    P( B, C, D, E, A, W[9]  );
    P( A, B, C, D, E, W[10] );
    P( E, A, B, C, D, W[11] );
    P( D, E, A, B, C, W[12] );
    P( C, D, E, A, B, W[13] );
    P( B, C, D, E, A, W[14] );
    P( A, B, C, D, E, W[15] );
    P( E, A, B, C, D, R(16) );
    P( D, E, A, B, C, R(17) );
    P( C, D, E, A, B, R(18) );
    P( B, C, D, E, A, R(19) );

#undef K
#undef F

#define F(x,y,z) (x ^ y ^ z)
#define K 0x6ED9EBA1

    P( A, B, C, D, E, R(20) );
    P( E, A, B, C, D, R(21) );
    P( D, E, A, B, C, R(22) );
    P( C, D, E, A, B, R(23) );
    P( B, C, D, E, A, R(24) );
    P( A, B, C, D, E, R(25) );
    P( E, A, B, C, D, R(26) );
    P( D, E, A, B, C, R(27) );
    P( C, D, E, A, B, R(28) );
    P( B, C, D, E, A, R(29) );
    P( A, B, C, D, E, R(30) );
    P( E, A, B, C, D, R(31) );
    P( D, E, A, B, C, R(32) );
    P( C, D, E, A, B, R(33) );
    P( B, C, D, E, A, R(34) );
    P( A, B, C, D, E, R(35) );
    P( E, A, B, C, D, R(36) );
    P( D, E, A, B, C, R(37) );
    P( C, D, E, A, B, R(38) );
    P( B, C, D, E, A, R(39) );

#undef K
#undef F

#define F(x,y,z) ((x & y) | (z & (x | y)))
#define K 0x8F1BBCDC

    P( A, B, C, D, E, R(40) );
    P( E, A, B, C, D, R(41) );
    P( D, E, A, B, C, R(42) );
    P( C, D, E, A, B, R(43) );
    P( B, C, D, E, A, R(44) );
    P( A, B, C, D, E, R(45) );
    P( E, A, B, C, D, R(46) );
    P( D, E, A, B, C, R(47) );
    P( C, D, E, A, B, R(48) );
    P( B, C, D, E, A, R(49) );
    P( A, B, C, D, E, R(50) );
    P( E, A, B, C, D, R(51) );
    P( D, E, A, B, C, R(52) );
    P( C, D, E, A, B, R(53) );
    P( B, C, D, E, A, R(54) );
    P( A, B, C, D, E, R(55) );
    P( E, A, B, C, D, R(56) );
    P( D, E, A, B, C, R(57) );
    P( C, D, E, A, B, R(58) );
    P( B, C, D, E, A, R(59) );

#undef K
#undef F

#define F(x,y,z) (x ^ y ^ z)
#define K 0xCA62C1D6

    P( A, B, C, D, E, R(60) );
    P( E, A, B, C, D, R(61) );
    P( D, E, A, B, C, R(62) );
    P( C, D, E, A, B, R(63) );
    P( B, C, D, E, A, R(64) );
    P( A, B, C, D, E, R(65) );
    P( E, A, B, C, D, R(66) );
    P( D, E, A, B, C, R(67) );
    P( C, D, E, A, B, R(68) );
    P( B, C, D, E, A, R(69) );
    P( A, B, C, D, E, R(70) );
    P( E, A, B, C, D, R(71) );
    P( D, E, A, B, C, R(72) );
    P( C, D, E, A, B, R(73) );
    P( B, C, D, E, A, R(74) );
    P( A, B, C, D, E, R(75) );
    P( E, A, B, C, D, R(76) );
    P( D, E, A, B, C, R(77) );
    P( C, D, E, A, B, R(78) );
    P( B, C, D, E, A, R(79) );

#undef K
#undef F

    ctx->state[0] += A;
    ctx->state[1] += B;
    ctx->state[2] += C;
    ctx->state[3] += D;
    ctx->state[4] += E;
}

static void sha1_update(
        sha1_context *ctx,
        uint8 *input,
        uint32 length)
{
    uint32 left, fill;

    if (!length)
        return;

    left = ctx->total[0] & 0x3F;
    fill = 64 - left;

    ctx->total[0] += length;
    ctx->total[0] &= 0xFFFFFFFF;

    if (ctx->total[0] < length)
        ctx->total[1]++;

    if (left && length >= fill) {
        memcpy((void*)(ctx->buffer + left), (void*)input, fill);
        sha1_process(ctx, ctx->buffer);
        length -= fill;
        input  += fill;
        left = 0;
    }

    while (length >= 64) {
        sha1_process(ctx, input);
        length -= 64;
        input  += 64;
    }

    if (length)
        memcpy((void*)(ctx->buffer + left), (void *)input, length );
}

static uint8 sha1_padding[64] =
{
    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

static void sha1_finish(
        sha1_context *ctx,
        uint8 digest[20])
{
    uint32 last, padn;
    uint32 high, low;
    uint8 msglen[8];

    high = (ctx->total[0] >> 29)
         | (ctx->total[1] <<  3);
    low  = (ctx->total[0] <<  3);

    PUT_UINT32(high, msglen, 0);
    PUT_UINT32(low,  msglen, 4);

    last = ctx->total[0] & 0x3F;
    padn = (last < 56) ? (56 - last) : (120 - last);

    sha1_update(ctx, sha1_padding, padn);
    sha1_update(ctx, msglen, 8);

    PUT_UINT32(ctx->state[0], digest,  0);
    PUT_UINT32(ctx->state[1], digest,  4);
    PUT_UINT32(ctx->state[2], digest,  8);
    PUT_UINT32(ctx->state[3], digest, 12);
    PUT_UINT32(ctx->state[4], digest, 16);
}

static std::string sha1(
        const std::string& input)
{
    std::string digest;

    sha1_context ctx;
    sha1_starts(&ctx);
    sha1_update(&ctx, (uint8*)&input[0], input.size());
    digest.resize(20);
    sha1_finish(&ctx, (uint8*)&digest[0]);

    return digest;
}

static const char hex_table[] = "0123456789abcdef";

static std::string string_to_hex(
        const std::string& input)
{
    std::string temp;
    temp.resize(input.size() * 2);
    std::string::size_type i;
    std::string::const_iterator itr = input.begin();
    for (i = 0; itr != input.end(); itr++, i++)
    {
        temp[i++] = hex_table[(*itr & 0xF0) >> 4];
        temp[i] = hex_table[*itr & 0x0F];
    }
    return temp;
}

static const std::string base64_chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";
#define is_base64(c) ( \
        isalnum((unsigned char)c) || \
        ((unsigned char)c == '+') || \
        ((unsigned char)c == '/'))

static std::string base64_encode(
        unsigned char const* bytes_to_encode,
        unsigned int in_len)
{
    std::string ret;
    int i = 0;
    int j = 0;
    unsigned char char_array_3[3] = {0};
    unsigned char char_array_4[4] = {0};

    while (in_len--) {
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) {
            char_array_4[0] =  (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] =   char_array_3[2] & 0x3f;

            for (i = 0; i <4; i++)
                ret += base64_chars[char_array_4[i]];
            i = 0;
        }
    }

    if (i) {
        for(j = i; j < 3; j++)
            char_array_3[j] = '\0';

        char_array_4[0] =  (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] =   char_array_3[2] & 0x3f;

        for (j = 0; (j < i + 1); j++)
            ret += base64_chars[char_array_4[j]];

        while((i++ < 3))
            ret += '=';
    }

    return ret;
}

static std::string get_nonce()
{
    static bool initialized = false;
    if (!initialized)
        srand((unsigned int)time(NULL));
    char buf[256];
    sprintf(buf, "%d %d", time(NULL), rand());
    return string_to_hex(sha1(buf));
}

static std::string get_time()
{
    time_t timer;
    struct tm *date;
    timer = time(NULL);
    date = localtime(&timer);
    char buf[256];
    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", date);
    return buf;
}

static std::string responseBuf;
static int handle_returned_data(
        char* ptr,
        size_t size,
        size_t nmemb,
        void* stream)
{
    char* pbuf = new char[size*nmemb+1];
    memcpy(pbuf, ptr, size*nmemb);
    pbuf[size*nmemb] = 0;
    responseBuf += pbuf;
    delete[] pbuf;
    return size*nmemb;
}

static int register_namespaces(
        xmlXPathContextPtr xpathCtx,
        const xmlChar* nsList)
{
    xmlChar* nsListDup;
    xmlChar* prefix;
    xmlChar* href;
    xmlChar* next;

    nsListDup = xmlStrdup(nsList);
    if (nsListDup == NULL) return -1;  

    next = nsListDup;
    while(next != NULL) {
        while(isspace(*next)) next++;
        if((*next) == '\0') break;

        prefix = next;
        next = (xmlChar*)xmlStrchr(next, '=');
        if (!next) {
            xmlFree(nsListDup);
            return -1;
        }
        *(next++) = '\0';

        href = next;
        next = (xmlChar*)xmlStrchr(next, ' ');
        if (next) {
            *(next++) = '\0';    
        }

        if(xmlXPathRegisterNs(xpathCtx, prefix, href) != 0) {
            xmlFree(nsListDup);
            return -1;
        }
    }

    xmlFree(nsListDup);
    return(0);
}

static std::string query_xml(
        std::string& xml,
        std::string query,
        std::string ns = "")
{
    xmlDocPtr pDoc = xmlParseDoc((xmlChar*)xml.c_str());
    xmlAttrPtr pAttr = NULL;
    xmlXPathContextPtr xpathCtx = NULL;
    xmlXPathObjectPtr xpathObj=NULL;
    xmlNodeSetPtr nodes;
    std::string ret;

    int n;
    if (!pDoc) goto leave;

    xpathCtx = xmlXPathNewContext(pDoc);
    if (!xpathCtx) goto leave;

    if (ns.size()) {
        if (register_namespaces(xpathCtx, (xmlChar*)ns.c_str()) < 0)
            goto leave;
    }

    xpathObj = xmlXPathEvalExpression((xmlChar*)query.c_str(), xpathCtx);
    if (!xpathObj) goto leave;

    nodes = xpathObj->nodesetval;
    for(n = 0; n < xmlXPathNodeSetGetLength(nodes); n++) {
        xmlNodePtr node = nodes->nodeTab[n];
        if(node->type != XML_ATTRIBUTE_NODE && node->type != XML_ELEMENT_NODE && node->type != XML_CDATA_SECTION_NODE) continue;
        if (node->type == XML_CDATA_SECTION_NODE)
            ret = (char*)node->content;
        else
        if (node->children)
            ret = (char*)node->children->content;
        break;
    }

leave:
    if (xpathObj) xmlXPathFreeObject(xpathObj);
    if (xpathCtx) xmlXPathFreeContext(xpathCtx);
    if (pDoc) xmlFreeDoc(pDoc);
    return ret;
}

static std::string get_content(
        std::string url,
        std::string username,
        std::string password)
{
    std::string ret;
    std::string nonce = get_nonce();
    std::string post_time = get_time();
    std::string header;
    std::string base64nonce = base64_encode(
            (unsigned char const*)nonce.c_str(), nonce.size());
    std::string pass = nonce;
    pass += post_time;
    pass += password;
    pass = sha1(pass);
    std::string base64pass = base64_encode(
            (unsigned char const*)pass.c_str(), pass.size());
    std::string authorization_header;
    authorization_header += "UsernameToken ";
    authorization_header += "Username=\"";
    authorization_header += username + "\", ";
    authorization_header += "PasswordDigest=\"";
    authorization_header += base64pass + "\", ";
    authorization_header += "Created=\"";
    authorization_header += post_time + "\", ";
    authorization_header += "Nonce=\"";
    authorization_header += base64nonce + "\"";

    CURL* curl;
    CURLcode res;
    struct curl_slist *headerlist = NULL;
    long status = 0;

    curl = curl_easy_init();
    if (!curl) goto leave;

    header = "Content-Type: application/x.atom+xml";
    headerlist = curl_slist_append(headerlist, header.c_str());

    header = "X-WSSE: ";
    header += authorization_header.c_str();
    headerlist = curl_slist_append(headerlist, header.c_str());

    header = "Authorization: WSSE profile=\"UsernameToken\"";
    headerlist = curl_slist_append(headerlist, header.c_str());

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
    responseBuf = "";
    res = curl_easy_perform(curl);
#ifdef _DEBUG
    {
        FILE *fp = fopen("res.xml", "wb");
        fprintf(fp, "%s", responseBuf.c_str());
        fclose(fp);
    }
#endif
    if (res != CURLE_OK) goto leave;

    res = curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &status);
    if (res != CURLE_OK || status != 200) goto leave;

    ret = responseBuf;
leave:
    responseBuf = "";
    curl_easy_cleanup(curl);
    return ret;
}

#ifdef _WIN32
static std::string utf8_to_string(std::string str)
{
    std::string strRet;

    UINT codePage = CP_UTF8;
    const char* ptr = str.c_str();
    if (str[0] == (char)0xef && str[1] == (char)0xbb && str[2] == (char)0xbf)
        ptr += 3;
    size_t wcssize = MultiByteToWideChar(codePage, 0, ptr, -1,  NULL, 0);
    wchar_t* pszStr = new wchar_t[wcssize + 1];
    wcssize = MultiByteToWideChar(codePage, 0, ptr, -1, pszStr, wcssize + 1);
    pszStr[wcssize] = '\0';

    codePage = GetACP();
    size_t mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR)pszStr,-1,NULL,0,NULL,NULL);
    char* pszStrMB = new char[mbssize+1];
    mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR)pszStr, -1, pszStrMB, mbssize, NULL, NULL);
    pszStrMB[mbssize] = '\0';
    strRet = pszStrMB;
    delete [] pszStrMB;
    delete [] pszStr;

    return strRet;
}
#endif

static void dump_content(
        std::string& xml)
{
    xmlDocPtr pDoc = xmlParseDoc((xmlChar*)xml.c_str());
    xmlAttrPtr pAttr = NULL;
    xmlXPathContextPtr xpathCtx = NULL;
    xmlXPathObjectPtr xpathObj=NULL;
    xmlNodeSetPtr nodes;
    std::string ret;
    int n;

    if (!pDoc) goto leave;

    xpathCtx = xmlXPathNewContext(pDoc);
    if (!xpathCtx) goto leave;

    if (register_namespaces(xpathCtx, (xmlChar*)"Atom=http://www.w3.org/2005/Atom") < 0)
        goto leave;

    xpathObj = xmlXPathEvalExpression((xmlChar*)"/Atom:feed/Atom:entry", xpathCtx);
    if (!xpathObj) goto leave;

    nodes = xpathObj->nodesetval;
    for(n = 0; n < xmlXPathNodeSetGetLength(nodes); n++) {
        xmlNodePtr node = nodes->nodeTab[n]->children;
        while(node) {
            std::string name = (char*)node->name;
#ifdef _WIN32
            
            if (name == "title")
                std::cout << "name:" << utf8_to_string((char*)node->children->content) << std::endl;
#else
            if (name == "title")
                std::cout << "name:" << (char*)node->children->content << std::endl;
#endif
            if (name == "updated")
                std::cout << "time:" << (char*)node->children->content << std::endl;
            if (name == "link")
                std::cout << "link:" << (char*)xmlGetProp(node, (xmlChar*)"href") << std::endl;
            node = node->next;
        }
        std::cout << std::endl;
    }

leave:
    if (xpathObj) xmlXPathFreeObject(xpathObj);
    if (xpathCtx) xmlXPathFreeContext(xpathCtx);
    if (pDoc) xmlFreeDoc(pDoc);
}

int main(int argc, char* argv[])
{
    std::string username;
    std::string password;
    if (argc != 3) return -1;

    username = argv[1];
    password = argv[2];
    std::string xml, value;

    xml = get_content(
            "http://mixi.jp/atom/tracks",
            username,
            password);

    value = query_xml(
            xml,
            "/app:service/app:workspace/app:collection/@href",
            "app=http://purl.org/atom/app#");

    xml = get_content(
            value,
            username,
            password);

    dump_content(xml);
    return 0;
}

これでSTLを使えなかったとしたら...ガクガク((((;゚Д゚))))ブルブル
Posted at by