KLUPD::CoreError KLUPD::UpdateInfo::parse(const Path &path,
                                          FileInfo &fileInfo,
                                          const ChangeStateCheck &changeStateCheck,
                                          const FileVector &localFilesToCheckAgainst,
                                          const bool suppressSuccessLogMessages)
{
    // index is already parsed, files already attached
    if(fileAlreadyParsed(fileInfo))
        return CORE_NO_ERROR;

    const Path fullFileName = path + fileInfo.m_filename;

    if(!suppressSuccessLogMessages)
        TRACE_MESSAGE2("Parsing XML index file '%S'", fullFileName.toWideChar());

    std::vector<unsigned char> buffer;
    const CoreError readFileResult = LocalFile(fullFileName, pLog).read(buffer);
    if(!KLUPD::isSuccess(readFileResult))
    {
        TRACE_MESSAGE3("Failed to read XML index file '%S', result '%S'",
            fullFileName.toWideChar(), KLUPD::toString(readFileResult).toWideChar());
        return readFileResult;
    }
    if(buffer.empty())
    {
        TRACE_MESSAGE3("Index XML file '%S' is corrupted, file size is %d", fullFileName.toWideChar(), buffer.size());
        return CORE_UPDATE_DESCRIPTION_DAMAGED;
    }

    // a file list obtained from XML
    FileVector fileList;
    IndexFileXMLParser xmler(fileInfo.m_filename.m_value, fileList, m_signature6Checker, fileInfo.m_relativeURLPath, m_retranslationMode, m_callbacks, pLog);

    XmlReader xmlReader(reinterpret_cast<const char *>(&buffer[0]), buffer.size());
    if(!xmler.parseXMLRecursively(xmlReader, 0)
        || fileList.empty())
    {
        TRACE_MESSAGE3("\tXML file parse error '%S', file number found %d",
            fullFileName.toWideChar(), fileList.size());
        return CORE_UPDATE_DESCRIPTION_DAMAGED;
    }
    m_parsedIndexCache.push_back(fileInfo);

    xmler.GetUpdateDate(fileInfo.m_strUpdateDate);
    xmler.GetBlackDate(fileInfo.m_strBlackDate);

    // save variables to write into settings storage
    std::vector<UpdatedSettings> updatedSettings = xmler.updatedSettings();
    m_updatedSettings.insert(m_updatedSettings.end(), updatedSettings.begin(), updatedSettings.end());

    // copy to match list only those items which suits download criteria
    for(FileVector::iterator fileIter = fileList.begin(); fileIter != fileList.end(); ++fileIter)
    {
        // check download filters criteria should be performed
        NoCaseString reasonNotMatch;
        if(!matchesSettings(reasonNotMatch, *fileIter))
        {
            if(!suppressSuccessLogMessages)
            {
                TRACE_MESSAGE3("\tFile filtered, download criteria does not matches: '%S', %S",
                    reasonNotMatch.toWideChar(), fileIter->toString().toWideChar());
            }
            continue;
        }

        // check download mandatory criteria
        reasonNotMatch.erase();
        if(!isEntryRequired(reasonNotMatch, *fileIter, m_filterFlags))
        {
            if(!suppressSuccessLogMessages)
            {
                TRACE_MESSAGE3("\tFile filtered, mandatory criteria does not matches: '%S', %S",
                    reasonNotMatch.toWideChar(), fileIter->toString().toWideChar());
            }
            continue;
        }

        // setup transaction folders
        fileIter->m_transactionInformation.m_currentLocation = m_callbacks.productFolder(*fileIter, m_retranslationMode);
        fileIter->m_transactionInformation.m_newLocation = m_callbacks.temporaryFolder(*fileIter);
        NoCaseString statusExplanations;
        fileIter->m_transactionInformation.m_changeStatus = getFileStatusAgainstLocal(*fileIter,
            changeStateCheck, localFilesToCheckAgainst, statusExplanations);

        // insert file with filtering duplicates
        bool fileDuplicate = false;

        // insert black list into the beginning of list, because it should be checked before downloading other bases
        if(fileIter->m_type == FileInfo::blackList)
            m_matchFileList.insertNewInTheBeginOfListOrUpdateTheSame(*fileIter, *fileIter, fileDuplicate, m_retranslationMode);
        else
            m_matchFileList.insertNewOrUpdateTheSame(*fileIter, *fileIter, fileDuplicate, m_retranslationMode);


        if(!suppressSuccessLogMessages)
        {
            // to avoid empty brackets output to trace
            if(!statusExplanations.empty())
                statusExplanations = NoCaseString(L"(") + statusExplanations + L")";

            if(fileDuplicate)
            {
                TRACE_MESSAGE3("\tDuplicate file information updated: %S %S",
                    fileIter->toString().toWideChar(), statusExplanations.toWideChar());
            }
            else
            {
                TRACE_MESSAGE3("\tFile matches download criteria: %S %S",
                    fileIter->toString().toWideChar(), statusExplanations.toWideChar());
            }
        }
    }

    return CORE_NO_ERROR;
}
KLUPD::FileInfo::ChangeStatus KLUPD::UpdateInfo::getFileStatusAgainstLocal(const FileInfo &etalon,
    const ChangeStateCheck &changeStateCheck, const FileVector &localFilesToCheckAgainst, NoCaseString &statusExplanations)const
{
    if(changeStateCheck == noStateChangeCheck)
        return FileInfo::unchanged;

    statusExplanations.erase();

    LocalFile fileInProductFolder(m_callbacks.productFolder(etalon, m_retranslationMode) + etalon.m_filename);

    //////////////////////////////////////////////////////////////////////////
    // check if file in product folder exists
    if(!fileInProductFolder.exists())
    {
        statusExplanations = L"original files does not exist";
        return FileInfo::added;
    }

    // inlined file is already downloaded together with index file, can be only compared if it changed
    if(etalon.isInlined())
    {
        std::vector<unsigned char> buffer;
        const CoreError readInlinedFileResult = fileInProductFolder.read(buffer);
        if(!isSuccess(readInlinedFileResult))
        {
            TRACE_MESSAGE3("Failed to detect change state for inlined file '%S', file operation error '%S'",
                fileInProductFolder.m_name.toWideChar(), toString(readInlinedFileResult).toWideChar());
            statusExplanations = L"inlined file can not be read";
            return FileInfo::added;
        }

        if(buffer == etalon.m_inlinedContent)
        {
            statusExplanations = L"inlined file is not changed";
            return FileInfo::unchanged;
        }
        else
        {
            statusExplanations = L"inlined file is not modified";
            return FileInfo::modified;
        }
    }


    //////////////////////////////////////////////////////////////////////////
    // check against signature 5
    if(!etalon.m_signature5.empty())
    {
        if(checkInsideSignature5AgainstEtalon(fileInProductFolder.m_name, etalon.m_signature5, pLog))
        {
            statusExplanations = L"signature 5 is not changed";
            return FileInfo::unchanged;
        }
        else
        {
            statusExplanations = L"signature 5 is changed";
            return FileInfo::modified;
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // check file against md5
    if(!etalon.m_md5.empty())
    {
        std::vector<unsigned char> md5;
        if(!calcMD5Hash(fileInProductFolder.m_name, md5))
        {
            statusExplanations = L"failed to calculate md5 hash";
            return FileInfo::added;
        }

        if(md5 == etalon.m_md5)
        {
            statusExplanations = L"md5 unchanged";
            return FileInfo::unchanged;
        }
        else
        {
            statusExplanations = L"md5 changed";
            return FileInfo::modified;
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // check date

    statusExplanations += NoCaseString(L"date from index '") + etalon.m_dateFromIndex + L"', "
        + (localFilesToCheckAgainst.empty() ? L"local bases are not consistent" : L"local bases are consistent");

    if(!etalon.m_dateFromIndex.empty()
        // date check is only performed if file presents in consistent local file set,
        // otherwise file may belong to other Update set and must be updated
        && !localFilesToCheckAgainst.empty())
    {
        FileInfo::ChangeStatus modifiedIfDatesAreIgnored = FileInfo::unchanged;

        // check if index date is newer is only performed for Kaspersky sources
        if(changeStateCheck == checkStateButIgnoreDate)
        {
            modifiedIfDatesAreIgnored = FileInfo::modified;
            statusExplanations = L", dates are ignored for user-specified sources";
        }

        FileInfo localConsistentFile;
        if(localFilesToCheckAgainst.findTheSameFile(etalon, localConsistentFile, m_retranslationMode))
        {
            bool bIndex = etalon.isIndex();
            const NoCaseString& strLocalFileDate = bIndex ? localConsistentFile.m_strUpdateDate : localConsistentFile.m_dateFromIndex;
            NoCaseString strFileType(bIndex ? L"index" : L"non-index"); // for trace

            if(!strLocalFileDate.empty())
            {
                // if expected date on source is less, then file is skipped
                unsigned long nEtalonDate = updateDateStringToSequenceNumber(etalon.m_dateFromIndex);
                unsigned long nLocalFileDate = updateDateStringToSequenceNumber(strLocalFileDate);
                if(nEtalonDate < nLocalFileDate)
                {
                    statusExplanations += NoCaseString(L", ") + FileInfo::toString(modifiedIfDatesAreIgnored).toWideChar() +
                        L" " + strFileType + L" file is found in local consistent set with date '" + localConsistentFile.m_dateFromIndex + L"'";
                    return modifiedIfDatesAreIgnored;
                }
                else if( bIndex || nEtalonDate > nLocalFileDate)
                {
                    statusExplanations += NoCaseString(L", modified ") + strFileType + L" file is found in local consistent set, date from file '" + strLocalFileDate + L"'";
                    return FileInfo::modified;
                }
                else
                    statusExplanations +=  NoCaseString(L", ") + strFileType + L" file is found in local consistent set with same date";
            }
            else
                statusExplanations += NoCaseString(L", ") + strFileType + L" file is found in local consistent set, but there is no date in index specified";
        }
        else
            statusExplanations += L", file is not found in local consistent set";
    }

    //////////////////////////////////////////////////////////////////////////
    // check against signature 6

    bool insideSignature = false;
    NoCaseString errorMessage;
    if(!m_signature6Checker.checkSignature(fileInProductFolder.m_name, insideSignature, errorMessage))
    {
        statusExplanations += NoCaseString(L", signature 6 check changed (") + errorMessage + L")";
        return FileInfo::modified;
    }

    // file may contain inside signature6, then there is no way to determine whether
     //  file is different on source, and that is why must always be downloaded
    if(insideSignature)
    {
        statusExplanations += L", inside signature 6 check unchanged (file needs being downloaded)";
        return FileInfo::modified;
    }
    else
    {
        statusExplanations += L", signature 6 check unchanged";
        return FileInfo::unchanged;
    }
}