<commit_before>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageDecoder.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkPackBits.h"

#include "gif_lib.h"

class SkGIFImageDecoder : public SkImageDecoder {
public:
    virtual Format getFormat() const {
        return kGIF_Format;
    }

protected:
    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
};

static const uint8_t gStartingIterlaceYValue[] = {
    0, 4, 2, 1
};
static const uint8_t gDeltaIterlaceYValue[] = {
    8, 8, 4, 2
};

/*  Implement the GIF interlace algorithm in an iterator.
    1) grab every 8th line beginning at 0
    2) grab every 8th line beginning at 4
    3) grab every 4th line beginning at 2
    4) grab every 2nd line beginning at 1
*/
class GifInterlaceIter {
public:
    GifInterlaceIter(int height) : fHeight(height) {
        fStartYPtr = gStartingIterlaceYValue;
        fDeltaYPtr = gDeltaIterlaceYValue;

        fCurrY = *fStartYPtr++;
        fDeltaY = *fDeltaYPtr++;
    }

    int currY() const {
        SkASSERT(fStartYPtr);
        SkASSERT(fDeltaYPtr);
        return fCurrY;
    }

    void next() {
        SkASSERT(fStartYPtr);
        SkASSERT(fDeltaYPtr);

        int y = fCurrY + fDeltaY;
        // We went from an if statement to a while loop so that we iterate
        // through fStartYPtr until a valid row is found. This is so that images
        // that are smaller than 5x5 will not trash memory.
        while (y >= fHeight) {
            if (gStartingIterlaceYValue +
                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
                // we done
                SkDEBUGCODE(fStartYPtr = NULL;)
                SkDEBUGCODE(fDeltaYPtr = NULL;)
                y = 0;
            } else {
                y = *fStartYPtr++;
                fDeltaY = *fDeltaYPtr++;
            }
        }
        fCurrY = y;
    }

private:
    const int fHeight;
    int fCurrY;
    int fDeltaY;
    const uint8_t* fStartYPtr;
    const uint8_t* fDeltaYPtr;
};

///////////////////////////////////////////////////////////////////////////////

//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)

static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
                              int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

void CheckFreeExtension(SavedImage* Image) {
    if (Image->ExtensionBlocks) {
#if GIFLIB_MAJOR < 5
        FreeExtension(Image);
#else
        GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
#endif
    }
}

// return NULL on failure
static const ColorMapObject* find_colormap(const GifFileType* gif) {
    const ColorMapObject* cmap = gif->Image.ColorMap;
    if (NULL == cmap) {
        cmap = gif->SColorMap;
    }

    if (NULL == cmap) {
        // no colormap found
        return NULL;
    }
    // some sanity checks
    if (cmap && ((unsigned)cmap->ColorCount > 256 ||
                 cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
        cmap = NULL;
    }
    return cmap;
}

// return -1 if not found (i.e. we're completely opaque)
static int find_transpIndex(const SavedImage& image, int colorCount) {
    int transpIndex = -1;
    for (int i = 0; i < image.ExtensionBlockCount; ++i) {
        const ExtensionBlock* eb = image.ExtensionBlocks + i;
        if (eb->Function == 0xF9 && eb->ByteCount == 4) {
            if (eb->Bytes[0] & 1) {
                transpIndex = (unsigned char)eb->Bytes[3];
                // check for valid transpIndex
                if (transpIndex >= colorCount) {
                    transpIndex = -1;
                }
                break;
            }
        }
    }
    return transpIndex;
}

static bool error_return(GifFileType* gif, const SkBitmap& bm,
                         const char msg[]) {
#if 0
    SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
             msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
#endif
    return false;
}

bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
#if GIFLIB_MAJOR < 5
    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
#else
    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
#endif
    if (NULL == gif) {
        return error_return(gif, *bm, "DGifOpen");
    }

    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);

    SavedImage temp_save;
    temp_save.ExtensionBlocks=NULL;
    temp_save.ExtensionBlockCount=0;
    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);

    int width, height;
    GifRecordType recType;
    GifByteType *extData;
#if GIFLIB_MAJOR >= 5
    int extFunction;
#endif
    int transpIndex = -1;   // -1 means we don't have it (yet)

    do {
        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
            return error_return(gif, *bm, "DGifGetRecordType");
        }

        switch (recType) {
        case IMAGE_DESC_RECORD_TYPE: {
            if (DGifGetImageDesc(gif) == GIF_ERROR) {
                return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
            }

            if (gif->ImageCount < 1) {    // sanity check
                return error_return(gif, *bm, "ImageCount < 1");
            }

            width = gif->SWidth;
            height = gif->SHeight;
            if (width <= 0 || height <= 0 ||
                !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
                                           width, height)) {
                return error_return(gif, *bm, "chooseFromOneChoice");
            }

            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
                bm->setConfig(SkBitmap::kIndex8_Config, width, height);
                return true;
            }
#ifdef SK_BUILD_FOR_ANDROID
            // No Bitmap reuse supported for this format
            if (!bm->isNull()) {
                return false;
            }
#endif

            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
            const GifImageDesc& desc = image->ImageDesc;

            // check for valid descriptor
            if (   (desc.Top | desc.Left) < 0 ||
                    desc.Left + desc.Width > width ||
                    desc.Top + desc.Height > height) {
                return error_return(gif, *bm, "TopLeft");
            }

            // now we decode the colortable
            int colorCount = 0;
            {
                const ColorMapObject* cmap = find_colormap(gif);
                if (NULL == cmap) {
                    return error_return(gif, *bm, "null cmap");
                }

                colorCount = cmap->ColorCount;
                SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
                SkPMColor* colorPtr = ctable->lockColors();
                for (int index = 0; index < colorCount; index++)
                    colorPtr[index] = SkPackARGB32(0xFF,
                                                   cmap->Colors[index].Red,
                                                   cmap->Colors[index].Green,
                                                   cmap->Colors[index].Blue);

                transpIndex = find_transpIndex(temp_save, colorCount);
                if (transpIndex < 0)
                    ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
                else
                    colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
                ctable->unlockColors(true);

                SkAutoUnref aurts(ctable);
                if (!this->allocPixelRef(bm, ctable)) {
                    return error_return(gif, *bm, "allocPixelRef");
                }
            }

            SkAutoLockPixels alp(*bm);

            // time to decode the scanlines
            //
            uint8_t*  scanline = bm->getAddr8(0, 0);
            const int rowBytes = bm->rowBytes();
            const int innerWidth = desc.Width;
            const int innerHeight = desc.Height;

            // abort if either inner dimension is <= 0
            if (innerWidth <= 0 || innerHeight <= 0) {
                return error_return(gif, *bm, "non-pos inner width/height");
            }

            // are we only a subset of the total bounds?
            if ((desc.Top | desc.Left) > 0 ||
                 innerWidth < width || innerHeight < height)
            {
                int fill;
                if (transpIndex >= 0) {
                    fill = transpIndex;
                } else {
                    fill = gif->SBackGroundColor;
                }
                // check for valid fill index/color
                if (static_cast<unsigned>(fill) >=
                        static_cast<unsigned>(colorCount)) {
                    fill = 0;
                }
                memset(scanline, fill, bm->getSize());
                // bump our starting address
                scanline += desc.Top * rowBytes + desc.Left;
            }

            // now decode each scanline
            if (gif->Image.Interlace)
            {
                GifInterlaceIter iter(innerHeight);
                for (int y = 0; y < innerHeight; y++)
                {
                    uint8_t* row = scanline + iter.currY() * rowBytes;
                    if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
                        return error_return(gif, *bm, "interlace DGifGetLine");
                    }
                    iter.next();
                }
            }
            else
            {
                // easy, non-interlace case
                for (int y = 0; y < innerHeight; y++) {
                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
                        return error_return(gif, *bm, "DGifGetLine");
                    }
                    scanline += rowBytes;
                }
            }
            goto DONE;
            } break;

        case EXTENSION_RECORD_TYPE:
#if GIFLIB_MAJOR < 5
            if (DGifGetExtension(gif, &temp_save.Function,
                                 &extData) == GIF_ERROR) {
#else
            if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
#endif
                return error_return(gif, *bm, "DGifGetExtension");
            }

            while (extData != NULL) {
                /* Create an extension block with our data */
#if GIFLIB_MAJOR < 5
                if (AddExtensionBlock(&temp_save, extData[0],
                                      &extData[1]) == GIF_ERROR) {
#else
                if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
                                         &gif->ExtensionBlocks,
                                         extFunction,
                                         extData[0],
                                         &extData[1]) == GIF_ERROR) {
#endif
                    return error_return(gif, *bm, "AddExtensionBlock");
                }
                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
                    return error_return(gif, *bm, "DGifGetExtensionNext");
                }
#if GIFLIB_MAJOR < 5
                temp_save.Function = 0;
#endif
            }
            break;

        case TERMINATE_RECORD_TYPE:
            break;

        default:	/* Should be trapped by DGifGetRecordType */
            break;
        }
    } while (recType != TERMINATE_RECORD_TYPE);

DONE:
    return true;
}

///////////////////////////////////////////////////////////////////////////////
DEFINE_DECODER_CREATOR(GIFImageDecoder);
///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            return SkNEW(SkGIFImageDecoder);
        }
    }
    return NULL;
}

static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
<commit_msg>skia: Fix Codenomicon image open failure.<commit_after>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageDecoder.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkPackBits.h"

#include "gif_lib.h"

class SkGIFImageDecoder : public SkImageDecoder {
public:
    virtual Format getFormat() const {
        return kGIF_Format;
    }

protected:
    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
};

static const uint8_t gStartingIterlaceYValue[] = {
    0, 4, 2, 1
};
static const uint8_t gDeltaIterlaceYValue[] = {
    8, 8, 4, 2
};

/*  Implement the GIF interlace algorithm in an iterator.
    1) grab every 8th line beginning at 0
    2) grab every 8th line beginning at 4
    3) grab every 4th line beginning at 2
    4) grab every 2nd line beginning at 1
*/
class GifInterlaceIter {
public:
    GifInterlaceIter(int height) : fHeight(height) {
        fStartYPtr = gStartingIterlaceYValue;
        fDeltaYPtr = gDeltaIterlaceYValue;

        fCurrY = *fStartYPtr++;
        fDeltaY = *fDeltaYPtr++;
    }

    int currY() const {
        SkASSERT(fStartYPtr);
        SkASSERT(fDeltaYPtr);
        return fCurrY;
    }

    void next() {
        SkASSERT(fStartYPtr);
        SkASSERT(fDeltaYPtr);

        int y = fCurrY + fDeltaY;
        // We went from an if statement to a while loop so that we iterate
        // through fStartYPtr until a valid row is found. This is so that images
        // that are smaller than 5x5 will not trash memory.
        while (y >= fHeight) {
            if (gStartingIterlaceYValue +
                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
                // we done
                SkDEBUGCODE(fStartYPtr = NULL;)
                SkDEBUGCODE(fDeltaYPtr = NULL;)
                y = 0;
            } else {
                y = *fStartYPtr++;
                fDeltaY = *fDeltaYPtr++;
            }
        }
        fCurrY = y;
    }

private:
    const int fHeight;
    int fCurrY;
    int fDeltaY;
    const uint8_t* fStartYPtr;
    const uint8_t* fDeltaYPtr;
};

///////////////////////////////////////////////////////////////////////////////

//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)

static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
                              int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

void CheckFreeExtension(SavedImage* Image) {
    if (Image->ExtensionBlocks) {
#if GIFLIB_MAJOR < 5
        FreeExtension(Image);
#else
        GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
#endif
    }
}

// return NULL on failure
static const ColorMapObject* find_colormap(const GifFileType* gif) {
    const ColorMapObject* cmap = gif->Image.ColorMap;
    if (NULL == cmap) {
        cmap = gif->SColorMap;
    }

    if (NULL == cmap) {
        // no colormap found
        return NULL;
    }
    // some sanity checks
    if (cmap && ((unsigned)cmap->ColorCount > 256 ||
                 cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
        cmap = NULL;
    }
    return cmap;
}

// return -1 if not found (i.e. we're completely opaque)
static int find_transpIndex(const SavedImage& image, int colorCount) {
    int transpIndex = -1;
    for (int i = 0; i < image.ExtensionBlockCount; ++i) {
        const ExtensionBlock* eb = image.ExtensionBlocks + i;
        if (eb->Function == 0xF9 && eb->ByteCount == 4) {
            if (eb->Bytes[0] & 1) {
                transpIndex = (unsigned char)eb->Bytes[3];
                // check for valid transpIndex
                if (transpIndex >= colorCount) {
                    transpIndex = -1;
                }
                break;
            }
        }
    }
    return transpIndex;
}

static bool error_return(GifFileType* gif, const SkBitmap& bm,
                         const char msg[]) {
#if 0
    SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
             msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
#endif
    return false;
}

bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
#if GIFLIB_MAJOR < 5
    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
#else
    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
#endif
    if (NULL == gif) {
        return error_return(gif, *bm, "DGifOpen");
    }

    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);

    SavedImage temp_save;
    temp_save.ExtensionBlocks=NULL;
    temp_save.ExtensionBlockCount=0;
    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);

    int width, height;
    GifRecordType recType;
    GifByteType *extData;
#if GIFLIB_MAJOR >= 5
    int extFunction;
#endif
    int transpIndex = -1;   // -1 means we don't have it (yet)

    do {
        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
            return error_return(gif, *bm, "DGifGetRecordType");
        }

        switch (recType) {
        case IMAGE_DESC_RECORD_TYPE: {
            if (DGifGetImageDesc(gif) == GIF_ERROR) {
                return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
            }

            if (gif->ImageCount < 1) {    // sanity check
                return error_return(gif, *bm, "ImageCount < 1");
            }

            width = gif->SWidth;
            height = gif->SHeight;
            if (width <= 0 || height <= 0 ||
                !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
                                           width, height)) {
                return error_return(gif, *bm, "chooseFromOneChoice");
            }

            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
                bm->setConfig(SkBitmap::kIndex8_Config, width, height);
                return true;
            }
#ifdef SK_BUILD_FOR_ANDROID
            // No Bitmap reuse supported for this format
            if (!bm->isNull()) {
                return false;
            }
#endif

            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
            const GifImageDesc& desc = image->ImageDesc;

            // check for valid descriptor
            if (   (desc.Top | desc.Left) < 0 ||
                    desc.Left + desc.Width > width ||
                    desc.Top + desc.Height > height) {
                return error_return(gif, *bm, "TopLeft");
            }

            // now we decode the colortable
            int colorCount = 0;
            {
                const ColorMapObject* cmap = find_colormap(gif);
                if (NULL == cmap) {
                    return error_return(gif, *bm, "null cmap");
                }

                colorCount = cmap->ColorCount;
                SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
                SkPMColor* colorPtr = ctable->lockColors();
                for (int index = 0; index < colorCount; index++)
                    colorPtr[index] = SkPackARGB32(0xFF,
                                                   cmap->Colors[index].Red,
                                                   cmap->Colors[index].Green,
                                                   cmap->Colors[index].Blue);

                transpIndex = find_transpIndex(temp_save, colorCount);
                if (transpIndex < 0)
                    ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
                else
                    colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
                ctable->unlockColors(true);

                SkAutoUnref aurts(ctable);
                if (!this->allocPixelRef(bm, ctable)) {
                    return error_return(gif, *bm, "allocPixelRef");
                }
            }

            SkAutoLockPixels alp(*bm);

            // time to decode the scanlines
            //
            uint8_t*  scanline = bm->getAddr8(0, 0);
            const int rowBytes = bm->rowBytes();
            const int innerWidth = desc.Width;
            const int innerHeight = desc.Height;

            // abort if either inner dimension is <= 0
            if (innerWidth <= 0 || innerHeight <= 0) {
                return error_return(gif, *bm, "non-pos inner width/height");
            }

            // are we only a subset of the total bounds?
            if ((desc.Top | desc.Left) > 0 ||
                 innerWidth < width || innerHeight < height)
            {
                int fill;
                if (transpIndex >= 0) {
                    fill = transpIndex;
                } else {
                    fill = gif->SBackGroundColor;
                }
                // check for valid fill index/color
                if (static_cast<unsigned>(fill) >=
                        static_cast<unsigned>(colorCount)) {
                    fill = 0;
                }
                memset(scanline, fill, bm->getSize());
                // bump our starting address
                scanline += desc.Top * rowBytes + desc.Left;
            }

            // now decode each scanline
            if (gif->Image.Interlace)
            {
                GifInterlaceIter iter(innerHeight);
                for (int y = 0; y < innerHeight; y++)
                {
                    uint8_t* row = scanline + iter.currY() * rowBytes;
                    if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
                        return error_return(gif, *bm, "interlace DGifGetLine");
                    }
                    iter.next();
                }
            }
            else
            {
                // easy, non-interlace case
                for (int y = 0; y < innerHeight; y++) {
                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
                        return error_return(gif, *bm, "DGifGetLine");
                    }
                    scanline += rowBytes;
                }
            }
        }
        return true;

        case EXTENSION_RECORD_TYPE:
#if GIFLIB_MAJOR < 5
            if (DGifGetExtension(gif, &temp_save.Function,
                                 &extData) == GIF_ERROR) {
#else
            if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
#endif
                return error_return(gif, *bm, "DGifGetExtension");
            }

            while (extData != NULL) {
                /* Create an extension block with our data */
#if GIFLIB_MAJOR < 5
                if (AddExtensionBlock(&temp_save, extData[0],
                                      &extData[1]) == GIF_ERROR) {
#else
                if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
                                         &gif->ExtensionBlocks,
                                         extFunction,
                                         extData[0],
                                         &extData[1]) == GIF_ERROR) {
#endif
                    return error_return(gif, *bm, "AddExtensionBlock");
                }
                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
                    return error_return(gif, *bm, "DGifGetExtensionNext");
                }
#if GIFLIB_MAJOR < 5
                temp_save.Function = 0;
#endif
            }
            break;

        case TERMINATE_RECORD_TYPE:
            break;

        default:	/* Should be trapped by DGifGetRecordType */
            break;
        }
    } while (recType != TERMINATE_RECORD_TYPE);
    // Return error, since the image descriptor record type is not present
    return error_return(gif, *bm, "no image descriptor");
}

///////////////////////////////////////////////////////////////////////////////
DEFINE_DECODER_CREATOR(GIFImageDecoder);
///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            return SkNEW(SkGIFImageDecoder);
        }
    }
    return NULL;
}

static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "stdafx.h"
#include "config.h"

#include "WebFrame.h"
#include "WebFramePrivate.h"
#include "WebView.h"
#include "WebHost.h"

#include "Document.h"
#include "FrameView.h"
#include "FrameWin.h"
#include "GraphicsContext.h"
#include "Page.h"
#include "RenderFrame.h"
#include "cairo.h"
#include "cairo-win32.h"
#include "TransferJob.h"
#include "TransferJobWin.h"

#include <io.h>
#include <fcntl.h>
#include <direct.h>
#include <WinInet.h>

using namespace WebCore;

namespace WebKit {

class WebFramePrivate::WebFrameData {
public:
    WebFrameData() { }
    ~WebFrameData() { }

    RefPtr<Frame> frame;
    RefPtr<FrameView> frameView;
    WebView* webView;
    WebHost* m_host;
};

WebFramePrivate::WebFramePrivate(char* name, WebView* view, WebHost* host)
: d(new WebFramePrivate::WebFrameData)
{
    d->m_host = host;
    d->webView = view;
    Page* page = new Page();
    Frame* frame = new FrameWin(page, 0, this);
    d->frame = frame;
    frame->deref(); // Frames are created with a refcount of 1.  Release this ref, since we've assigned it to a RefPtr
    page->setMainFrame(frame);
    FrameView* frameView = new FrameView(frame);
    d->frameView = frameView;
    frameView->deref(); // FrameViews are created with a refcount of 1.  Release this ref, since we've assigned it to a RefPtr
    d->frame->setView(frameView);
    d->frameView->setWindowHandle(view->windowHandle());
}

WebFramePrivate::~WebFramePrivate()
{
    delete d->frame->page();
    delete d;
}

void WebFramePrivate::loadFilePath(char* path)
{
    char URL[2048];
    strcpy(URL, "file://localhost/");
    strncat(URL, path, 2020);

    d->frame->didOpenURL(URL);
    d->frame->begin(URL);
    HANDLE fileHandle = CreateFileA(path, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    bool result = false;
    DWORD bytesRead = 0;

    do {
      const int bufferSize = 8193;
      char buffer[bufferSize];
      result = ReadFile(fileHandle, &buffer, bufferSize - 1, &bytesRead, 0);
      buffer[bytesRead] = '\0';
      d->frame->write(buffer);
      // Check for end of file.
    } while (result && bytesRead);

    CloseHandle(fileHandle);

    d->frame->end();
}

void WebFramePrivate::loadHTMLString(char* html, char* baseURL)
{
    d->frame->begin();
    d->frame->write(html);
    d->frame->end();
}

void WebFramePrivate::openURL(const DeprecatedString& str)
{
    loadURL(str.ascii());
}

void WebFramePrivate::submitForm(const String& method, const KURL& url, const FormData* submitFormData)
{
    // FIXME: This is a dumb implementation, doesn't handle subframes, etc.
    d->frame->didOpenURL(url);
    d->frame->begin(url);
    TransferJob* job;
    if (method == "GET" || !submitFormData)
        job = new TransferJob(this, method, url);
    else
        job = new TransferJob(this, method, url, *submitFormData);
    job->start(d->frame->document()->docLoader());
    if (d->m_host) {
        DeprecatedCString urlString = url.prettyURL().utf8();
        d->m_host->updateLocationBar(urlString);
    }
}

void WebFramePrivate::loadURL(const char* URL)
{
    d->frame->didOpenURL(URL);
    d->frame->begin(URL);
    WebCore::TransferJob* job = new TransferJob(this, "GET", URL);
    job->start(d->frame->document()->docLoader());
    if (d->m_host)
        d->m_host->updateLocationBar(URL);
}

void WebFramePrivate::getURL(char* url, int length)
{
    if (length <= 0)
        return;
    *url = 0;

    DeprecatedCString urlString = d->frame->url().prettyURL().utf8();
    const char* srcURL = urlString;
    if (srcURL) {
        int srcURLLength = strlen(srcURL);
        if (length > srcURLLength)
            strcpy(url, srcURL);
    }
}

void WebFramePrivate::reload()
{
    if (!d->frame->url().url().startsWith("javascript:", false))
        d->frame->scheduleLocationChange(d->frame->url().url(), d->frame->referrer(), true/*lock history*/, true/*userGesture*/);
}

void WebFramePrivate::receivedData(WebCore::TransferJob*, const char* data, int length)
{
    d->frame->write(data, length);
}

void WebFramePrivate::receivedAllData(WebCore::TransferJob* job, WebCore::PlatformData data)
{
    if (d->m_host)
        (d->m_host->loadEnd)(data->loaded, data->error, data->errorString);

    d->frame->end();
}

void WebFramePrivate::paint()
{
    d->frameView->layout();

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(d->webView->windowHandle(), &ps);
    cairo_surface_t* finalSurface = cairo_win32_surface_create(hdc);
    cairo_surface_t* surface = cairo_surface_create_similar(finalSurface,
                                                            CAIRO_CONTENT_COLOR_ALPHA,
                                                            ps.rcPaint.right - ps.rcPaint.left,
                                                            ps.rcPaint.bottom - ps.rcPaint.top);

    cairo_t* context = cairo_create(surface);
    GraphicsContext gc(context);

    IntRect documentDirtyRect = ps.rcPaint;
    documentDirtyRect.move(d->frameView->contentsX(), d->frameView->contentsY());

    // FIXME: We have to set the transform using both cairo and GDI until we use cairo for text.
    HDC surfaceDC = cairo_win32_surface_get_dc(surface);
    SaveDC(surfaceDC);
    OffsetViewportOrgEx(surfaceDC, -d->frameView->contentsX() - ps.rcPaint.left,
                                   -d->frameView->contentsY() - ps.rcPaint.top, 0);
    cairo_translate(context, -d->frameView->contentsX() - ps.rcPaint.left,
                             -d->frameView->contentsY() - ps.rcPaint.top);
    d->frame->paint(&gc, documentDirtyRect);
    RestoreDC(surfaceDC, -1);

    cairo_destroy(context);
    context = cairo_create(finalSurface);
    cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
    cairo_set_source_surface(context, surface, ps.rcPaint.left, ps.rcPaint.top);
    cairo_rectangle(context, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
    cairo_fill(context);
    cairo_destroy(context);

    cairo_surface_destroy(surface);
    cairo_surface_destroy(finalSurface);

    EndPaint(d->webView->windowHandle(), &ps);
}

WebCore::Frame* WebFramePrivate::impl()
{
    return d->frame.get();
}

WebFramePrivate* WebFramePrivate::toPrivate()
{
    return this;
}

}

<commit_msg>Make sure the font options set on the context match what we draw.<commit_after>/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "stdafx.h"
#include "config.h"

#include "WebFrame.h"
#include "WebFramePrivate.h"
#include "WebView.h"
#include "WebHost.h"

#include "Document.h"
#include "FrameView.h"
#include "FrameWin.h"
#include "GraphicsContext.h"
#include "Page.h"
#include "RenderFrame.h"
#include "cairo.h"
#include "cairo-win32.h"
#include "TransferJob.h"
#include "TransferJobWin.h"

#include <io.h>
#include <fcntl.h>
#include <direct.h>
#include <WinInet.h>

using namespace WebCore;

namespace WebKit {

class WebFramePrivate::WebFrameData {
public:
    WebFrameData() { }
    ~WebFrameData() { }

    RefPtr<Frame> frame;
    RefPtr<FrameView> frameView;
    WebView* webView;
    WebHost* m_host;
};

WebFramePrivate::WebFramePrivate(char* name, WebView* view, WebHost* host)
: d(new WebFramePrivate::WebFrameData)
{
    d->m_host = host;
    d->webView = view;
    Page* page = new Page();
    Frame* frame = new FrameWin(page, 0, this);
    d->frame = frame;
    frame->deref(); // Frames are created with a refcount of 1.  Release this ref, since we've assigned it to a RefPtr
    page->setMainFrame(frame);
    FrameView* frameView = new FrameView(frame);
    d->frameView = frameView;
    frameView->deref(); // FrameViews are created with a refcount of 1.  Release this ref, since we've assigned it to a RefPtr
    d->frame->setView(frameView);
    d->frameView->setWindowHandle(view->windowHandle());
}

WebFramePrivate::~WebFramePrivate()
{
    delete d->frame->page();
    delete d;
}

void WebFramePrivate::loadFilePath(char* path)
{
    char URL[2048];
    strcpy(URL, "file://localhost/");
    strncat(URL, path, 2020);

    d->frame->didOpenURL(URL);
    d->frame->begin(URL);
    HANDLE fileHandle = CreateFileA(path, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    bool result = false;
    DWORD bytesRead = 0;

    do {
      const int bufferSize = 8193;
      char buffer[bufferSize];
      result = ReadFile(fileHandle, &buffer, bufferSize - 1, &bytesRead, 0);
      buffer[bytesRead] = '\0';
      d->frame->write(buffer);
      // Check for end of file.
    } while (result && bytesRead);

    CloseHandle(fileHandle);

    d->frame->end();
}

void WebFramePrivate::loadHTMLString(char* html, char* baseURL)
{
    d->frame->begin();
    d->frame->write(html);
    d->frame->end();
}

void WebFramePrivate::openURL(const DeprecatedString& str)
{
    loadURL(str.ascii());
}

void WebFramePrivate::submitForm(const String& method, const KURL& url, const FormData* submitFormData)
{
    // FIXME: This is a dumb implementation, doesn't handle subframes, etc.
    d->frame->didOpenURL(url);
    d->frame->begin(url);
    TransferJob* job;
    if (method == "GET" || !submitFormData)
        job = new TransferJob(this, method, url);
    else
        job = new TransferJob(this, method, url, *submitFormData);
    job->start(d->frame->document()->docLoader());
    if (d->m_host) {
        DeprecatedCString urlString = url.prettyURL().utf8();
        d->m_host->updateLocationBar(urlString);
    }
}

void WebFramePrivate::loadURL(const char* URL)
{
    d->frame->didOpenURL(URL);
    d->frame->begin(URL);
    WebCore::TransferJob* job = new TransferJob(this, "GET", URL);
    job->start(d->frame->document()->docLoader());
    if (d->m_host)
        d->m_host->updateLocationBar(URL);
}

void WebFramePrivate::getURL(char* url, int length)
{
    if (length <= 0)
        return;
    *url = 0;

    DeprecatedCString urlString = d->frame->url().prettyURL().utf8();
    const char* srcURL = urlString;
    if (srcURL) {
        int srcURLLength = strlen(srcURL);
        if (length > srcURLLength)
            strcpy(url, srcURL);
    }
}

void WebFramePrivate::reload()
{
    if (!d->frame->url().url().startsWith("javascript:", false))
        d->frame->scheduleLocationChange(d->frame->url().url(), d->frame->referrer(), true/*lock history*/, true/*userGesture*/);
}

void WebFramePrivate::receivedData(WebCore::TransferJob*, const char* data, int length)
{
    d->frame->write(data, length);
}

void WebFramePrivate::receivedAllData(WebCore::TransferJob* job, WebCore::PlatformData data)
{
    if (d->m_host && data)
        (d->m_host->loadEnd)(data->loaded, data->error, data->errorString);

    d->frame->end();
}

void WebFramePrivate::paint()
{
    d->frameView->layout();

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(d->webView->windowHandle(), &ps);
    cairo_surface_t* finalSurface = cairo_win32_surface_create(hdc);
    cairo_surface_t* surface = cairo_surface_create_similar(finalSurface,
                                                            CAIRO_CONTENT_COLOR_ALPHA,
                                                            ps.rcPaint.right - ps.rcPaint.left,
                                                            ps.rcPaint.bottom - ps.rcPaint.top);

    cairo_t* context = cairo_create(surface);
    static cairo_font_options_t* fontOptions;
    if (!fontOptions)
        // Force ClearType-level quality.
        fontOptions = cairo_font_options_create();
    cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL);
    cairo_set_font_options(context, fontOptions);
    GraphicsContext gc(context);

    IntRect documentDirtyRect = ps.rcPaint;
    documentDirtyRect.move(d->frameView->contentsX(), d->frameView->contentsY());

    // FIXME: We have to set the transform using both cairo and GDI until we use cairo for text.
    HDC surfaceDC = cairo_win32_surface_get_dc(surface);
    SaveDC(surfaceDC);
    OffsetViewportOrgEx(surfaceDC, -d->frameView->contentsX() - ps.rcPaint.left,
                                   -d->frameView->contentsY() - ps.rcPaint.top, 0);
    cairo_translate(context, -d->frameView->contentsX() - ps.rcPaint.left,
                             -d->frameView->contentsY() - ps.rcPaint.top);
    d->frame->paint(&gc, documentDirtyRect);
    RestoreDC(surfaceDC, -1);

    cairo_destroy(context);
    context = cairo_create(finalSurface);
    cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
    cairo_set_source_surface(context, surface, ps.rcPaint.left, ps.rcPaint.top);
    cairo_rectangle(context, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
    cairo_fill(context);
    cairo_destroy(context);

    cairo_surface_destroy(surface);
    cairo_surface_destroy(finalSurface);

    EndPaint(d->webView->windowHandle(), &ps);
}

WebCore::Frame* WebFramePrivate::impl()
{
    return d->frame.get();
}

WebFramePrivate* WebFramePrivate::toPrivate()
{
    return this;
}

}

<|endoftext|>


<commit_before>//
// WinAPI
//
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>
#include <Shlwapi.h>
#include <initguid.h>
#include <winioctl.h>
#include "XUSB.h"

//
// STL
//
#include <string>
#include <codecvt>
#include <locale>
#include <map>
#include <iostream>
#include <fstream>

//
// JSON
//
#include <json/json.h>

//
// Hooking
//
#include <detours/detours.h>

//
// Logging
//
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/fmt/bin_to_hex.h>

using convert_t = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_t, wchar_t> strconverter;
std::once_flag g_init;
std::string g_dllDir;

static decltype(SetupDiEnumDeviceInterfaces) *real_SetupDiEnumDeviceInterfaces = SetupDiEnumDeviceInterfaces;
static decltype(DeviceIoControl) *real_DeviceIoControl = DeviceIoControl;
static decltype(CreateFileA) *real_CreateFileA = CreateFileA;
static decltype(CreateFileW) *real_CreateFileW = CreateFileW;
static decltype(WriteFile)* real_WriteFile = WriteFile;
static decltype(CloseHandle)* real_CloseHandle = CloseHandle;
static decltype(GetOverlappedResult)* real_GetOverlappedResult = GetOverlappedResult;

static std::map<HANDLE, std::string> g_handleToPath;
static std::map<DWORD, std::string> g_ioctlMap;

//
// Hooks SetupDiEnumDeviceInterfaces() API
//
BOOL WINAPI DetourSetupDiEnumDeviceInterfaces(
	HDEVINFO DeviceInfoSet,
	PSP_DEVINFO_DATA DeviceInfoData,
	const GUID* InterfaceClassGuid,
	DWORD MemberIndex,
	PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("SetupDiEnumDeviceInterfaces");

	auto retval = real_SetupDiEnumDeviceInterfaces(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex,
	                                               DeviceInterfaceData);

	_logger->info("InterfaceClassGuid = {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}, "
	              "success = {}, error = 0x{:08X}",
	              InterfaceClassGuid->Data1, InterfaceClassGuid->Data2, InterfaceClassGuid->Data3,
	              InterfaceClassGuid->Data4[0], InterfaceClassGuid->Data4[1], InterfaceClassGuid->Data4[2],
	              InterfaceClassGuid->Data4[3],
	              InterfaceClassGuid->Data4[4], InterfaceClassGuid->Data4[5], InterfaceClassGuid->Data4[6],
	              InterfaceClassGuid->Data4[7],
	              retval ? "true" : "false",
				  retval ? ERROR_SUCCESS : GetLastError());

	return retval;
}

//
// Hooks CreateFileA() API
//
HANDLE WINAPI DetourCreateFileA(
	LPCSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileA");
	std::string path(lpFileName);

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileA(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks CreateFileW() API
//
HANDLE WINAPI DetourCreateFileW(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileW");
	std::string path(strconverter.to_bytes(lpFileName));

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileW(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks WriteFile() API
//
BOOL WINAPI DetourWriteFile(
	HANDLE       hFile,
	LPCVOID      lpBuffer,
	DWORD        nNumberOfBytesToWrite,
	LPDWORD      lpNumberOfBytesWritten,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("WriteFile");

	const PUCHAR charInBuf = PUCHAR(lpBuffer);
	DWORD tmpBytesWritten;

	const auto ret =  real_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &tmpBytesWritten, lpOverlapped);
	const auto error = GetLastError();

	if (lpNumberOfBytesWritten)
		*lpNumberOfBytesWritten = tmpBytesWritten;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	const auto bufSize = std::min(nNumberOfBytesToWrite, tmpBytesWritten);
	const std::vector<char> inBuffer(charInBuf, charInBuf + bufSize);

	// Prevent the logger from causing a crash via exception when it double-detours WriteFile
	try
	{
		_logger->info("success = {}, lastError = 0x{:08X}, path = {} ({:04d}) -> {:Xpn}",
			ret ? "true" : "false",
			ret ? ERROR_SUCCESS : error,
			path,
			bufSize,
			spdlog::to_hex(inBuffer)
		);
	}
	catch (...)
	{ }

	return ret;
}

BOOL DetourCloseHandle(
	HANDLE hObject
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CloseHandle");

	const auto ret = real_CloseHandle(hObject);

	std::string path = "Unknown";
	if (g_handleToPath.count(hObject))
	{
		path = g_handleToPath[hObject];
		g_handleToPath.erase(hObject);
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("handle = {}, path = {}", hObject, path);

	return ret;
}

BOOL WINAPI DetourGetOverlappedResult(
	HANDLE       hFile,
	LPOVERLAPPED lpOverlapped,
	LPDWORD      lpNumberOfBytesTransferred,
	BOOL         bWait
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("GetOverlappedResult");
	DWORD tmpBytesTransferred;

	const auto ret = real_GetOverlappedResult(hFile, lpOverlapped, &tmpBytesTransferred, bWait);
	const auto error = GetLastError();

	if (lpNumberOfBytesTransferred)
		*lpNumberOfBytesTransferred = tmpBytesTransferred;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("success = {}, lastError = 0x{:08X}, bytesTransferred = {}, path = {}",
		ret ? "true" : "false",
		ret ? ERROR_SUCCESS : error,
		tmpBytesTransferred,
		path
	);

	return ret;
}

//
// Hooks DeviceIoControl() API
//
BOOL WINAPI DetourDeviceIoControl(
	HANDLE hDevice,
	DWORD dwIoControlCode,
	LPVOID lpInBuffer,
	DWORD nInBufferSize,
	LPVOID lpOutBuffer,
	DWORD nOutBufferSize,
	LPDWORD lpBytesReturned,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("DeviceIoControl");

	const PUCHAR charInBuf = static_cast<PUCHAR>(lpInBuffer);
	const std::vector<char> inBuffer(charInBuf, charInBuf + nInBufferSize);

	DWORD tmpBytesReturned;

	const auto retval = real_DeviceIoControl(
		hDevice,
		dwIoControlCode,
		lpInBuffer,
		nInBufferSize,
		lpOutBuffer,
		nOutBufferSize,
		&tmpBytesReturned, // might be null, use our own variable
		lpOverlapped
	);

	if (lpBytesReturned)
		*lpBytesReturned = tmpBytesReturned;

	std::string path = "Unknown";
	if (g_handleToPath.count(hDevice))
	{
		path = g_handleToPath[hDevice];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return retval;
	}
#endif

	if (g_ioctlMap.count(dwIoControlCode))
	{
		_logger->info("[I] [{}] path = {} ({:04d}) -> {:Xpn}",
		              g_ioctlMap[dwIoControlCode],
		              path,
		              nInBufferSize,
		              spdlog::to_hex(inBuffer)
		);
	}

	if (lpOutBuffer && nOutBufferSize > 0)
	{
		const PUCHAR charOutBuf = static_cast<PUCHAR>(lpOutBuffer);
		const auto bufSize = std::min(nOutBufferSize, tmpBytesReturned);
		const std::vector<char> outBuffer(charOutBuf, charOutBuf + bufSize);

		if (g_ioctlMap.count(dwIoControlCode))
		{
			_logger->info("[O] [{}] path = {} ({:04d}) -> {:Xpn}",
			              g_ioctlMap[dwIoControlCode],
			              path,
			              bufSize,
			              spdlog::to_hex(outBuffer)
			);
		}
	}

	return retval;
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI DllMain(HINSTANCE dll_handle, DWORD reason, LPVOID reserved)
{
	if (DetourIsHelperProcess())
	{
		return TRUE;
	}

	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
		{
			CHAR dllPath[MAX_PATH];

			GetModuleFileNameA((HINSTANCE)&__ImageBase, dllPath, MAX_PATH);
			PathRemoveFileSpecA(dllPath);
			g_dllDir = std::string(dllPath);

			auto logger = spdlog::basic_logger_mt(
				"XInputHooker",
				g_dllDir + "\\XInputHooker.log"
			);

#if _DEBUG
			spdlog::set_level(spdlog::level::debug);
			logger->flush_on(spdlog::level::debug);
#else
			logger->flush_on(spdlog::level::info);
#endif

			set_default_logger(logger);

			//
			// Load known IOCTL code definitions
			//
			Json::Value root;
			std::ifstream ifs(g_dllDir + "\\ioctls.json");
			ifs >> root;

			for (auto& i : root)
			{
				g_ioctlMap[std::stoul(i["HexValue"].asString(), nullptr, 16)] = i["Ioctl"].asString();
			}
		}

		DisableThreadLibraryCalls(dll_handle);
		DetourRestoreAfterWith();

		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourAttach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourAttach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourAttach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourAttach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourAttach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourAttach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();

		break;

	case DLL_PROCESS_DETACH:
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourDetach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourDetach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourDetach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourDetach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourDetach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourDetach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();
		break;
	}
	return TRUE;
}
<commit_msg>Add ReadFile detour<commit_after>//
// WinAPI
//
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>
#include <Shlwapi.h>
#include <initguid.h>
#include <winioctl.h>
#include "XUSB.h"

//
// STL
//
#include <string>
#include <codecvt>
#include <locale>
#include <map>
#include <iostream>
#include <fstream>

//
// JSON
//
#include <json/json.h>

//
// Hooking
//
#include <detours/detours.h>

//
// Logging
//
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/fmt/bin_to_hex.h>

using convert_t = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_t, wchar_t> strconverter;
std::once_flag g_init;
std::string g_dllDir;

static decltype(SetupDiEnumDeviceInterfaces) *real_SetupDiEnumDeviceInterfaces = SetupDiEnumDeviceInterfaces;
static decltype(DeviceIoControl) *real_DeviceIoControl = DeviceIoControl;
static decltype(CreateFileA) *real_CreateFileA = CreateFileA;
static decltype(CreateFileW) *real_CreateFileW = CreateFileW;
static decltype(ReadFile)* real_ReadFile = ReadFile;
static decltype(WriteFile)* real_WriteFile = WriteFile;
static decltype(CloseHandle)* real_CloseHandle = CloseHandle;
static decltype(GetOverlappedResult)* real_GetOverlappedResult = GetOverlappedResult;

static std::map<HANDLE, std::string> g_handleToPath;
static std::map<DWORD, std::string> g_ioctlMap;

//
// Hooks SetupDiEnumDeviceInterfaces() API
//
BOOL WINAPI DetourSetupDiEnumDeviceInterfaces(
	HDEVINFO DeviceInfoSet,
	PSP_DEVINFO_DATA DeviceInfoData,
	const GUID* InterfaceClassGuid,
	DWORD MemberIndex,
	PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("SetupDiEnumDeviceInterfaces");

	auto retval = real_SetupDiEnumDeviceInterfaces(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex,
	                                               DeviceInterfaceData);

	_logger->info("InterfaceClassGuid = {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}, "
	              "success = {}, error = 0x{:08X}",
	              InterfaceClassGuid->Data1, InterfaceClassGuid->Data2, InterfaceClassGuid->Data3,
	              InterfaceClassGuid->Data4[0], InterfaceClassGuid->Data4[1], InterfaceClassGuid->Data4[2],
	              InterfaceClassGuid->Data4[3],
	              InterfaceClassGuid->Data4[4], InterfaceClassGuid->Data4[5], InterfaceClassGuid->Data4[6],
	              InterfaceClassGuid->Data4[7],
	              retval ? "true" : "false",
				  retval ? ERROR_SUCCESS : GetLastError());

	return retval;
}

//
// Hooks CreateFileA() API
//
HANDLE WINAPI DetourCreateFileA(
	LPCSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileA");
	std::string path(lpFileName);

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileA(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks CreateFileW() API
//
HANDLE WINAPI DetourCreateFileW(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileW");
	std::string path(strconverter.to_bytes(lpFileName));

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileW(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks ReadFile() API
//
BOOL WINAPI DetourReadFile(
	HANDLE       hFile,
	LPVOID       lpBuffer,
	DWORD        nNumberOfBytesToRead,
	LPDWORD      lpNumberOfBytesRead,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("ReadFile");

	const PUCHAR charInBuf = PUCHAR(lpBuffer);
	DWORD tmpBytesRead;

	const auto ret =  real_ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, &tmpBytesRead, lpOverlapped);
	const auto error = GetLastError();

	if (lpNumberOfBytesRead)
		*lpNumberOfBytesRead = tmpBytesRead;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	const auto bufSize = std::min(nNumberOfBytesToRead, tmpBytesRead);
	const std::vector<char> outBuffer(charInBuf, charInBuf + bufSize);

	_logger->info("success = {}, lastError = 0x{:08X}, path = {} ({:04d}) -> {:Xpn}",
		ret ? "true" : "false",
		ret ? ERROR_SUCCESS : error,
		path,
		bufSize,
		spdlog::to_hex(outBuffer)
	);

	return ret;
}

//
// Hooks WriteFile() API
//
BOOL WINAPI DetourWriteFile(
	HANDLE       hFile,
	LPCVOID      lpBuffer,
	DWORD        nNumberOfBytesToWrite,
	LPDWORD      lpNumberOfBytesWritten,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("WriteFile");

	const PUCHAR charInBuf = PUCHAR(lpBuffer);
	DWORD tmpBytesWritten;

	const auto ret =  real_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &tmpBytesWritten, lpOverlapped);
	const auto error = GetLastError();

	if (lpNumberOfBytesWritten)
		*lpNumberOfBytesWritten = tmpBytesWritten;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	const auto bufSize = std::min(nNumberOfBytesToWrite, tmpBytesWritten);
	const std::vector<char> inBuffer(charInBuf, charInBuf + bufSize);

	// Prevent the logger from causing a crash via exception when it double-detours WriteFile
	try
	{
		_logger->info("success = {}, lastError = 0x{:08X}, path = {} ({:04d}) -> {:Xpn}",
			ret ? "true" : "false",
			ret ? ERROR_SUCCESS : error,
			path,
			bufSize,
			spdlog::to_hex(inBuffer)
		);
	}
	catch (...)
	{ }

	return ret;
}

BOOL DetourCloseHandle(
	HANDLE hObject
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CloseHandle");

	const auto ret = real_CloseHandle(hObject);

	std::string path = "Unknown";
	if (g_handleToPath.count(hObject))
	{
		path = g_handleToPath[hObject];
		g_handleToPath.erase(hObject);
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("handle = {}, path = {}", hObject, path);

	return ret;
}

BOOL WINAPI DetourGetOverlappedResult(
	HANDLE       hFile,
	LPOVERLAPPED lpOverlapped,
	LPDWORD      lpNumberOfBytesTransferred,
	BOOL         bWait
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("GetOverlappedResult");
	DWORD tmpBytesTransferred;

	const auto ret = real_GetOverlappedResult(hFile, lpOverlapped, &tmpBytesTransferred, bWait);
	const auto error = GetLastError();

	if (lpNumberOfBytesTransferred)
		*lpNumberOfBytesTransferred = tmpBytesTransferred;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("success = {}, lastError = 0x{:08X}, bytesTransferred = {}, path = {}",
		ret ? "true" : "false",
		ret ? ERROR_SUCCESS : error,
		tmpBytesTransferred,
		path
	);

	return ret;
}

//
// Hooks DeviceIoControl() API
//
BOOL WINAPI DetourDeviceIoControl(
	HANDLE hDevice,
	DWORD dwIoControlCode,
	LPVOID lpInBuffer,
	DWORD nInBufferSize,
	LPVOID lpOutBuffer,
	DWORD nOutBufferSize,
	LPDWORD lpBytesReturned,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("DeviceIoControl");

	const PUCHAR charInBuf = static_cast<PUCHAR>(lpInBuffer);
	const std::vector<char> inBuffer(charInBuf, charInBuf + nInBufferSize);

	DWORD tmpBytesReturned;

	const auto retval = real_DeviceIoControl(
		hDevice,
		dwIoControlCode,
		lpInBuffer,
		nInBufferSize,
		lpOutBuffer,
		nOutBufferSize,
		&tmpBytesReturned, // might be null, use our own variable
		lpOverlapped
	);

	if (lpBytesReturned)
		*lpBytesReturned = tmpBytesReturned;

	std::string path = "Unknown";
	if (g_handleToPath.count(hDevice))
	{
		path = g_handleToPath[hDevice];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return retval;
	}
#endif

	if (g_ioctlMap.count(dwIoControlCode))
	{
		_logger->info("[I] [{}] path = {} ({:04d}) -> {:Xpn}",
		              g_ioctlMap[dwIoControlCode],
		              path,
		              nInBufferSize,
		              spdlog::to_hex(inBuffer)
		);
	}

	if (lpOutBuffer && nOutBufferSize > 0)
	{
		const PUCHAR charOutBuf = static_cast<PUCHAR>(lpOutBuffer);
		const auto bufSize = std::min(nOutBufferSize, tmpBytesReturned);
		const std::vector<char> outBuffer(charOutBuf, charOutBuf + bufSize);

		if (g_ioctlMap.count(dwIoControlCode))
		{
			_logger->info("[O] [{}] path = {} ({:04d}) -> {:Xpn}",
			              g_ioctlMap[dwIoControlCode],
			              path,
			              bufSize,
			              spdlog::to_hex(outBuffer)
			);
		}
	}

	return retval;
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI DllMain(HINSTANCE dll_handle, DWORD reason, LPVOID reserved)
{
	if (DetourIsHelperProcess())
	{
		return TRUE;
	}

	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
		{
			CHAR dllPath[MAX_PATH];

			GetModuleFileNameA((HINSTANCE)&__ImageBase, dllPath, MAX_PATH);
			PathRemoveFileSpecA(dllPath);
			g_dllDir = std::string(dllPath);

			auto logger = spdlog::basic_logger_mt(
				"XInputHooker",
				g_dllDir + "\\XInputHooker.log"
			);

#if _DEBUG
			spdlog::set_level(spdlog::level::debug);
			logger->flush_on(spdlog::level::debug);
#else
			logger->flush_on(spdlog::level::info);
#endif

			set_default_logger(logger);

			//
			// Load known IOCTL code definitions
			//
			Json::Value root;
			std::ifstream ifs(g_dllDir + "\\ioctls.json");
			ifs >> root;

			for (auto& i : root)
			{
				g_ioctlMap[std::stoul(i["HexValue"].asString(), nullptr, 16)] = i["Ioctl"].asString();
			}
		}

		DisableThreadLibraryCalls(dll_handle);
		DetourRestoreAfterWith();

		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourAttach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourAttach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourAttach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourAttach((PVOID*)&real_ReadFile, DetourReadFile);
		DetourAttach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourAttach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourAttach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();

		break;

	case DLL_PROCESS_DETACH:
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourDetach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourDetach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourDetach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourDetach((PVOID*)&real_ReadFile, DetourReadFile);
		DetourDetach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourDetach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourDetach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();
		break;
	}
	return TRUE;
}
<|endoftext|>


<commit_before>/*
 * This file is part of Peli - universal JSON interaction library
 * Copyright (C) 2014  Alexey Chernov <4ernov@gmail.com>
 *
 * Peli is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <cassert>

#include "json/value.h"

#include "json/detail/special_chars.h"

#include "json/detail/parser/object.h"
#include "json/detail/parser/array.h"
#include "json/detail/parser/string.h"
#include "json/detail/parser/number.h"
#include "json/detail/parser/boolean.h"
#include "json/detail/parser/null.h"

#include "json/detail/parser/stream_routines.h"

template<typename Ch> peli::json::value peli::json::detail::parser::tokenizer::tok(std::basic_streambuf<Ch>* rdbuf)
{
	using namespace peli::json;
	using namespace peli::detail;
	using namespace peli::json::detail;
	using namespace peli::json::detail::parser;

	skip_whitespace(rdbuf);

	typename std::basic_streambuf<Ch>::int_type next_char = rdbuf->sgetc();

	switch (next_char)
	{
	case special_chars::left_curly:
		return value(parser<basic_object<Ch>>::parse(rdbuf));

	case special_chars::left_square:
		return value(parser<array>::parse(rdbuf));

	case special_chars::quote:
		return value(parser<std::basic_string<Ch>>::parse(rdbuf));

	case special_chars::n:
		parser<void>::parse(rdbuf);
		return value();

	case special_chars::t:
	case special_chars::f:
		return value(parser<bool>::parse(rdbuf));

	default:
		return value(parser<number>::parse(rdbuf));
	}

	assert("No way here");
}

template<typename Ch, typename Alloc> peli::json::value peli::json::detail::parser::tokenizer::gentle_stream(std::basic_istream<Ch, Alloc>& is)
{
	if (!typename std::basic_istream<Ch, Alloc>::sentry(is, true))
	{
		throw std::invalid_argument("");
	}

	return peli::json::detail::parser::tokenizer::tok(is.rdbuf());
}
<commit_msg>Fix include<commit_after>/*
 * This file is part of Peli - universal JSON interaction library
 * Copyright (C) 2014  Alexey Chernov <4ernov@gmail.com>
 *
 * Peli is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <cassert>

#include "peli/json/value.h"

#include "json/detail/special_chars.h"

#include "json/detail/parser/object.h"
#include "json/detail/parser/array.h"
#include "json/detail/parser/string.h"
#include "json/detail/parser/number.h"
#include "json/detail/parser/boolean.h"
#include "json/detail/parser/null.h"

#include "json/detail/parser/stream_routines.h"

template<typename Ch> peli::json::value peli::json::detail::parser::tokenizer::tok(std::basic_streambuf<Ch>* rdbuf)
{
	using namespace peli::json;
	using namespace peli::detail;
	using namespace peli::json::detail;
	using namespace peli::json::detail::parser;

	skip_whitespace(rdbuf);

	typename std::basic_streambuf<Ch>::int_type next_char = rdbuf->sgetc();

	switch (next_char)
	{
	case special_chars::left_curly:
		return value(parser<basic_object<Ch>>::parse(rdbuf));

	case special_chars::left_square:
		return value(parser<array>::parse(rdbuf));

	case special_chars::quote:
		return value(parser<std::basic_string<Ch>>::parse(rdbuf));

	case special_chars::n:
		parser<void>::parse(rdbuf);
		return value();

	case special_chars::t:
	case special_chars::f:
		return value(parser<bool>::parse(rdbuf));

	default:
		return value(parser<number>::parse(rdbuf));
	}

	assert("No way here");
}

template<typename Ch, typename Alloc> peli::json::value peli::json::detail::parser::tokenizer::gentle_stream(std::basic_istream<Ch, Alloc>& is)
{
	if (!typename std::basic_istream<Ch, Alloc>::sentry(is, true))
	{
		throw std::invalid_argument("");
	}

	return peli::json::detail::parser::tokenizer::tok(is.rdbuf());
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/time.hpp" // for total_seconds

#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node.hpp>
#include <libtorrent/session_status.hpp>
#include "libtorrent/broadcast_socket.hpp" // for cidr_distance

#include <boost/bind.hpp>

namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(traversal)
#endif

observer_ptr traversal_algorithm::new_observer(void* ptr
	, udp::endpoint const& ep, node_id const& id)
{
	observer_ptr o(new (ptr) null_observer(boost::intrusive_ptr<traversal_algorithm>(this), ep, id));
#ifdef TORRENT_DEBUG
	o->m_in_constructor = false;
#endif
	return o;
}

traversal_algorithm::traversal_algorithm(
	node_impl& node
	, node_id target)
	: m_ref_count(0)
	, m_node(node)
	, m_target(target)
	, m_invoke_count(0)
	, m_branch_factor(3)
	, m_responses(0)
	, m_timeouts(0)
	, m_num_target_nodes(m_node.m_table.bucket_size() * 2)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(traversal) << " [" << this << "] new traversal process. Target: " << target;
#endif
}

bool compare_ip_cidr(observer_ptr const& lhs, observer_ptr const& rhs)
{
	TORRENT_ASSERT(lhs->target_addr().is_v4() == rhs->target_addr().is_v4());
	// the number of bits in the IPs that may match. If
	// more bits that this matches, something suspicious is
	// going on and we shouldn't add the second one to our
	// routing table
	int cutoff = rhs->target_addr().is_v4() ? 4 : 64;
	int dist = cidr_distance(lhs->target_addr(), rhs->target_addr());
	return dist <= cutoff;
}

void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
{
	TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer));
	void* ptr = m_node.m_rpc.allocate_observer();
	if (ptr == 0)
	{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << "[" << this << ":" << name()
			<< "] failed to allocate memory for observer. aborting!";
#endif
		done();
		return;
	}
	observer_ptr o = new_observer(ptr, addr, id);
	if (id.is_all_zeros())
	{
		o->set_id(generate_id());
		o->flags |= observer::flag_no_id;
	}

	o->flags |= flags;

	std::vector<observer_ptr>::iterator i = std::lower_bound(
		m_results.begin()
		, m_results.end()
		, o
		, boost::bind(
			compare_ref
			, boost::bind(&observer::id, _1)
			, boost::bind(&observer::id, _2)
			, m_target
		)
	);

	if (i == m_results.end() || (*i)->id() != id)
	{
		if (m_node.settings().restrict_search_ips)
		{
			// don't allow multiple entries from IPs very close to each other
			std::vector<observer_ptr>::iterator j = std::find_if(
				m_results.begin(), m_results.end(), boost::bind(&compare_ip_cidr, _1, o));

			if (j != m_results.end())
			{
				// we already have a node in this search with an IP very
				// close to this one. We know that it's not the same, because
				// it claims a different node-ID. Ignore this to avoid attacks
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(traversal) << "ignoring DHT search entry: " << o->id()
				<< " " << o->target_addr()
				<< " existing node: "
				<< (*j)->id() << " " << (*j)->target_addr();
#endif
				return;
			}
		}
		TORRENT_ASSERT(std::find_if(m_results.begin(), m_results.end()
			, boost::bind(&observer::id, _1) == id) == m_results.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << "[" << this << ":" << name()
			<< "] adding result: " << id << " " << addr;
#endif
		i = m_results.insert(i, o);
	}

	if (m_results.size() > 100) m_results.resize(100);
}

void traversal_algorithm::start()
{
	// in case the routing table is empty, use the
	// router nodes in the table
	if (m_results.empty()) add_router_entries();
	init();
	add_requests();
}

void* traversal_algorithm::allocate_observer()
{
	return m_node.m_rpc.allocate_observer();
}

void traversal_algorithm::free_observer(void* ptr)
{
	m_node.m_rpc.free_observer(ptr);
}

void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	if (id.is_all_zeros())
		TORRENT_LOG(traversal) << time_now_string() << "[" << this << ":" << name()
			<< "] WARNING: node returned a list which included a node with id 0";
#endif
	add_entry(id, addr, 0);
}

void traversal_algorithm::finished(observer_ptr o)
{
#ifdef TORRENT_DEBUG
	std::vector<observer_ptr>::iterator i = std::find(
		m_results.begin(), m_results.end(), o);

	TORRENT_ASSERT(i != m_results.end() || m_results.size() == 100);
#endif

	// if this flag is set, it means we increased the
	// branch factor for it, and we should restore it
	if (o->flags & observer::flag_short_timeout)
		--m_branch_factor;

	TORRENT_ASSERT(o->flags & observer::flag_queried);
	o->flags |= observer::flag_alive;

	++m_responses;
	--m_invoke_count;
	TORRENT_ASSERT(m_invoke_count >= 0);
	add_requests();
	if (m_invoke_count == 0) done();
}

// prevent request means that the total number of requests has
// overflown. This query failed because it was the oldest one.
// So, if this is true, don't make another request
void traversal_algorithm::failed(observer_ptr o, int flags)
{
	TORRENT_ASSERT(m_invoke_count >= 0);

	if (m_results.empty()) return;

	TORRENT_ASSERT(o->flags & observer::flag_queried);
	if (flags & short_timeout)
	{
		// short timeout means that it has been more than
		// two seconds since we sent the request, and that
		// we'll most likely not get a response. But, in case
		// we do get a late response, keep the handler
		// around for some more, but open up the slot
		// by increasing the branch factor
		if ((o->flags & observer::flag_short_timeout) == 0)
			++m_branch_factor;
		o->flags |= observer::flag_short_timeout;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name()
			<< "] first chance timeout: "
			<< o->id() << " " << o->target_ep()
			<< " branch-factor: " << m_branch_factor
			<< " invoke-count: " << m_invoke_count;
#endif
	}
	else
	{
		o->flags |= observer::flag_failed;
		// if this flag is set, it means we increased the
		// branch factor for it, and we should restore it
		if (o->flags & observer::flag_short_timeout)
			--m_branch_factor;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name()
			<< "] failed: " << o->id() << " " << o->target_ep()
			<< " branch-factor: " << m_branch_factor
			<< " invoke-count: " << m_invoke_count;
#endif
		// don't tell the routing table about
		// node ids that we just generated ourself
		if ((o->flags & observer::flag_no_id) == 0)
			m_node.m_table.node_failed(o->id(), o->target_ep());
		++m_timeouts;
		--m_invoke_count;
		TORRENT_ASSERT(m_invoke_count >= 0);
	}

	if (flags & prevent_request)
	{
		--m_branch_factor;
		if (m_branch_factor <= 0) m_branch_factor = 1;
	}
	add_requests();
	if (m_invoke_count == 0) done();
}

void traversal_algorithm::done()
{
	// delete all our references to the observer objects so
	// they will in turn release the traversal algorithm
	m_results.clear();
}

namespace
{
	bool bitwise_nand(unsigned char lhs, unsigned char rhs)
	{
		return (lhs & rhs) == 0;
	}
}

void traversal_algorithm::add_requests()
{
	int results_target = m_num_target_nodes;

	// Find the first node that hasn't already been queried.
	for (std::vector<observer_ptr>::iterator i = m_results.begin()
		, end(m_results.end()); i != end
		&& results_target > 0 && m_invoke_count < m_branch_factor; ++i)
	{
		if ((*i)->flags & observer::flag_alive) --results_target;
		if ((*i)->flags & observer::flag_queried) continue;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name() << "]"
			<< " nodes-left: " << (m_results.end() - i)
			<< " invoke-count: " << m_invoke_count
			<< " branch-factor: " << m_branch_factor;
#endif

		if (invoke(*i))
		{
			TORRENT_ASSERT(m_invoke_count >= 0);
			++m_invoke_count;
			(*i)->flags |= observer::flag_queried;
		}
	}
}

void traversal_algorithm::add_router_entries()
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(traversal) << " using router nodes to initiate traversal algorithm. "
		<< std::distance(m_node.m_table.router_begin(), m_node.m_table.router_end()) << " routers";
#endif
	for (routing_table::router_iterator i = m_node.m_table.router_begin()
		, end(m_node.m_table.router_end()); i != end; ++i)
	{
		add_entry(node_id(0), *i, observer::flag_initial);
	}
}

void traversal_algorithm::init()
{
	// update the last activity of this bucket
	m_node.m_table.touch_bucket(m_target);
	m_branch_factor = m_node.branch_factor();
	m_node.add_traversal_algorithm(this);
}

traversal_algorithm::~traversal_algorithm()
{
	m_node.remove_traversal_algorithm(this);
}

void traversal_algorithm::status(dht_lookup& l)
{
	l.timeouts = m_timeouts;
	l.responses = m_responses;
	l.outstanding_requests = m_invoke_count;
	l.branch_factor = m_branch_factor;
	l.type = name();
	l.nodes_left = 0;
	l.first_timeout = 0;

	int last_sent = INT_MAX;
	ptime now = time_now();
	for (std::vector<observer_ptr>::iterator i = m_results.begin()
		, end(m_results.end()); i != end; ++i)
	{
		observer& o = **i;
		if (o.flags & observer::flag_queried)
		{
			last_sent = (std::min)(last_sent, total_seconds(now - o.sent()));
			if (o.has_short_timeout()) ++l.first_timeout;
			continue;
		}
		++l.nodes_left;
	}
	l.last_sent = last_sent;
}

} } // namespace libtorrent::dht

<commit_msg>fix DHT cidr check<commit_after>/*

Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/time.hpp" // for total_seconds

#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node.hpp>
#include <libtorrent/session_status.hpp>
#include "libtorrent/broadcast_socket.hpp" // for cidr_distance

#include <boost/bind.hpp>

namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(traversal)
#endif

observer_ptr traversal_algorithm::new_observer(void* ptr
	, udp::endpoint const& ep, node_id const& id)
{
	observer_ptr o(new (ptr) null_observer(boost::intrusive_ptr<traversal_algorithm>(this), ep, id));
#ifdef TORRENT_DEBUG
	o->m_in_constructor = false;
#endif
	return o;
}

traversal_algorithm::traversal_algorithm(
	node_impl& node
	, node_id target)
	: m_ref_count(0)
	, m_node(node)
	, m_target(target)
	, m_invoke_count(0)
	, m_branch_factor(3)
	, m_responses(0)
	, m_timeouts(0)
	, m_num_target_nodes(m_node.m_table.bucket_size() * 2)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(traversal) << " [" << this << "] new traversal process. Target: " << target;
#endif
}

// returns true of lhs and rhs are too close to each other to appear
// in the same DHT search under different node IDs
bool compare_ip_cidr(observer_ptr const& lhs, observer_ptr const& rhs)
{
	if (lhs->target_addr().is_v4() != rhs->target_addr().is_v4())
		return false;
	// the number of bits in the IPs that may match. If
	// more bits that this matches, something suspicious is
	// going on and we shouldn't add the second one to our
	// routing table
	int cutoff = rhs->target_addr().is_v4() ? 4 : 64;
	int dist = cidr_distance(lhs->target_addr(), rhs->target_addr());
	return dist <= cutoff;
}

void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
{
	TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer));
	void* ptr = m_node.m_rpc.allocate_observer();
	if (ptr == 0)
	{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << "[" << this << ":" << name()
			<< "] failed to allocate memory for observer. aborting!";
#endif
		done();
		return;
	}
	observer_ptr o = new_observer(ptr, addr, id);
	if (id.is_all_zeros())
	{
		o->set_id(generate_id());
		o->flags |= observer::flag_no_id;
	}

	o->flags |= flags;

	std::vector<observer_ptr>::iterator i = std::lower_bound(
		m_results.begin()
		, m_results.end()
		, o
		, boost::bind(
			compare_ref
			, boost::bind(&observer::id, _1)
			, boost::bind(&observer::id, _2)
			, m_target
		)
	);

	if (i == m_results.end() || (*i)->id() != id)
	{
		if (m_node.settings().restrict_search_ips)
		{
			// don't allow multiple entries from IPs very close to each other
			std::vector<observer_ptr>::iterator j = std::find_if(
				m_results.begin(), m_results.end(), boost::bind(&compare_ip_cidr, _1, o));

			if (j != m_results.end())
			{
				// we already have a node in this search with an IP very
				// close to this one. We know that it's not the same, because
				// it claims a different node-ID. Ignore this to avoid attacks
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(traversal) << "ignoring DHT search entry: " << o->id()
				<< " " << o->target_addr()
				<< " existing node: "
				<< (*j)->id() << " " << (*j)->target_addr();
#endif
				return;
			}
		}
		TORRENT_ASSERT(std::find_if(m_results.begin(), m_results.end()
			, boost::bind(&observer::id, _1) == id) == m_results.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << "[" << this << ":" << name()
			<< "] adding result: " << id << " " << addr;
#endif
		i = m_results.insert(i, o);
	}

	if (m_results.size() > 100) m_results.resize(100);
}

void traversal_algorithm::start()
{
	// in case the routing table is empty, use the
	// router nodes in the table
	if (m_results.empty()) add_router_entries();
	init();
	add_requests();
}

void* traversal_algorithm::allocate_observer()
{
	return m_node.m_rpc.allocate_observer();
}

void traversal_algorithm::free_observer(void* ptr)
{
	m_node.m_rpc.free_observer(ptr);
}

void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	if (id.is_all_zeros())
		TORRENT_LOG(traversal) << time_now_string() << "[" << this << ":" << name()
			<< "] WARNING: node returned a list which included a node with id 0";
#endif
	add_entry(id, addr, 0);
}

void traversal_algorithm::finished(observer_ptr o)
{
#ifdef TORRENT_DEBUG
	std::vector<observer_ptr>::iterator i = std::find(
		m_results.begin(), m_results.end(), o);

	TORRENT_ASSERT(i != m_results.end() || m_results.size() == 100);
#endif

	// if this flag is set, it means we increased the
	// branch factor for it, and we should restore it
	if (o->flags & observer::flag_short_timeout)
		--m_branch_factor;

	TORRENT_ASSERT(o->flags & observer::flag_queried);
	o->flags |= observer::flag_alive;

	++m_responses;
	--m_invoke_count;
	TORRENT_ASSERT(m_invoke_count >= 0);
	add_requests();
	if (m_invoke_count == 0) done();
}

// prevent request means that the total number of requests has
// overflown. This query failed because it was the oldest one.
// So, if this is true, don't make another request
void traversal_algorithm::failed(observer_ptr o, int flags)
{
	TORRENT_ASSERT(m_invoke_count >= 0);

	if (m_results.empty()) return;

	TORRENT_ASSERT(o->flags & observer::flag_queried);
	if (flags & short_timeout)
	{
		// short timeout means that it has been more than
		// two seconds since we sent the request, and that
		// we'll most likely not get a response. But, in case
		// we do get a late response, keep the handler
		// around for some more, but open up the slot
		// by increasing the branch factor
		if ((o->flags & observer::flag_short_timeout) == 0)
			++m_branch_factor;
		o->flags |= observer::flag_short_timeout;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name()
			<< "] first chance timeout: "
			<< o->id() << " " << o->target_ep()
			<< " branch-factor: " << m_branch_factor
			<< " invoke-count: " << m_invoke_count;
#endif
	}
	else
	{
		o->flags |= observer::flag_failed;
		// if this flag is set, it means we increased the
		// branch factor for it, and we should restore it
		if (o->flags & observer::flag_short_timeout)
			--m_branch_factor;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name()
			<< "] failed: " << o->id() << " " << o->target_ep()
			<< " branch-factor: " << m_branch_factor
			<< " invoke-count: " << m_invoke_count;
#endif
		// don't tell the routing table about
		// node ids that we just generated ourself
		if ((o->flags & observer::flag_no_id) == 0)
			m_node.m_table.node_failed(o->id(), o->target_ep());
		++m_timeouts;
		--m_invoke_count;
		TORRENT_ASSERT(m_invoke_count >= 0);
	}

	if (flags & prevent_request)
	{
		--m_branch_factor;
		if (m_branch_factor <= 0) m_branch_factor = 1;
	}
	add_requests();
	if (m_invoke_count == 0) done();
}

void traversal_algorithm::done()
{
	// delete all our references to the observer objects so
	// they will in turn release the traversal algorithm
	m_results.clear();
}

namespace
{
	bool bitwise_nand(unsigned char lhs, unsigned char rhs)
	{
		return (lhs & rhs) == 0;
	}
}

void traversal_algorithm::add_requests()
{
	int results_target = m_num_target_nodes;

	// Find the first node that hasn't already been queried.
	for (std::vector<observer_ptr>::iterator i = m_results.begin()
		, end(m_results.end()); i != end
		&& results_target > 0 && m_invoke_count < m_branch_factor; ++i)
	{
		if ((*i)->flags & observer::flag_alive) --results_target;
		if ((*i)->flags & observer::flag_queried) continue;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(traversal) << " [" << this << ":" << name() << "]"
			<< " nodes-left: " << (m_results.end() - i)
			<< " invoke-count: " << m_invoke_count
			<< " branch-factor: " << m_branch_factor;
#endif

		if (invoke(*i))
		{
			TORRENT_ASSERT(m_invoke_count >= 0);
			++m_invoke_count;
			(*i)->flags |= observer::flag_queried;
		}
	}
}

void traversal_algorithm::add_router_entries()
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(traversal) << " using router nodes to initiate traversal algorithm. "
		<< std::distance(m_node.m_table.router_begin(), m_node.m_table.router_end()) << " routers";
#endif
	for (routing_table::router_iterator i = m_node.m_table.router_begin()
		, end(m_node.m_table.router_end()); i != end; ++i)
	{
		add_entry(node_id(0), *i, observer::flag_initial);
	}
}

void traversal_algorithm::init()
{
	// update the last activity of this bucket
	m_node.m_table.touch_bucket(m_target);
	m_branch_factor = m_node.branch_factor();
	m_node.add_traversal_algorithm(this);
}

traversal_algorithm::~traversal_algorithm()
{
	m_node.remove_traversal_algorithm(this);
}

void traversal_algorithm::status(dht_lookup& l)
{
	l.timeouts = m_timeouts;
	l.responses = m_responses;
	l.outstanding_requests = m_invoke_count;
	l.branch_factor = m_branch_factor;
	l.type = name();
	l.nodes_left = 0;
	l.first_timeout = 0;

	int last_sent = INT_MAX;
	ptime now = time_now();
	for (std::vector<observer_ptr>::iterator i = m_results.begin()
		, end(m_results.end()); i != end; ++i)
	{
		observer& o = **i;
		if (o.flags & observer::flag_queried)
		{
			last_sent = (std::min)(last_sent, total_seconds(now - o.sent()));
			if (o.has_short_timeout()) ++l.first_timeout;
			continue;
		}
		++l.nodes_left;
	}
	l.last_sent = last_sent;
}

} } // namespace libtorrent::dht

<|endoftext|>


<commit_before>/*
    Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org>

    This file is part of the KDE project

    This library is free software you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    aint with this library see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <QDebug>

#include "GeoParser.h"
#include "GeoTagHandler.h"

// Set to a value greather than 0, to dump parent node chain while parsing
#define DUMP_PARENT_STACK 1

GeoParser::GeoParser(GeoDataGenericSourceType source)
    : QXmlStreamReader()
    , m_document(0)
    , m_source(source)
{
}

GeoParser::~GeoParser()
{
}

#if DUMP_PARENT_STACK > 0
static void dumpParentStack(const QString& name, int size, bool close)
{
    static int depth = 0;

    if (!close)
        depth++;

    QString result;
    for (int i = 0; i < depth; ++i)
        result += ' ';

    if (close) {
        depth--;
        result += "</";
    } else
        result += '<';

    result += name + "> stack size " + QString::number(size);
    fprintf(stderr, "%s\n", qPrintable(result));
}
#endif

bool GeoParser::read(QIODevice* device)
{
    // Assert previous document got released.
    Q_ASSERT(!m_document);
    m_document = createDocument();
    Q_ASSERT(m_document);

    // Set data source
    setDevice(device);

    // Start parsing
    while (!atEnd()) {
        readNext();

        if (isStartElement()) {
            if (isValidDocumentElement()) {
#if DUMP_PARENT_STACK > 0
                dumpParentStack(name().toString(), m_nodeStack.size(), false);
#endif

                parseDocument();
                break;
            } else
                raiseDocumentElementError();
        }
    }

    if (error())
        qDebug() << "[GeoParser::read] -> Error occurred:" << errorString();

    return !error();
}

bool GeoParser::isValidElement(const QString& tagName) const
{
    return name() == tagName;
}

GeoStackItem GeoParser::parentElement(unsigned int depth)
{
    QStack<GeoStackItem>::const_iterator it = m_nodeStack.constEnd() - 1;

    if (it - depth < m_nodeStack.constBegin())
        return GeoStackItem();

    return *(it - depth);
}

void GeoParser::parseDocument()
{
//    Commented out to be able to execute "parser.readElementText()"
//    Q_ASSERT(isStartElement());

    while (!atEnd()) {
        readNext();
        GeoTagHandler::QualifiedName qName(name().toString(), namespaceUri().toString());

        if (isEndElement()) {
            if (!isValidDocumentElement())
                m_nodeStack.pop();

#if DUMP_PARENT_STACK > 0
            dumpParentStack(name().toString(), m_nodeStack.size(), true);
#endif
            break;
        }

        if (isStartElement()) {
            GeoStackItem stackItem(qName, 0);

            if (const GeoTagHandler* handler = GeoTagHandler::recognizes(qName))
                stackItem.assignNode(handler->parse(*this));

            m_nodeStack.push(stackItem);

#if DUMP_PARENT_STACK > 0
            dumpParentStack(name().toString(), m_nodeStack.size(), false);
#endif

            parseDocument();
        }
    }
}

void GeoParser::raiseDocumentElementError()
{
    raiseError(QObject::tr("File format unrecognized"));
}

GeoDocument* GeoParser::releaseDocument()
{
    GeoDocument* document = m_document;
    m_document = 0;
    return document;
}
<commit_msg>Readd assertion, fix parsing problem, when tag handlers called readElementText().<commit_after>/*
    Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org>

    This file is part of the KDE project

    This library is free software you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    aint with this library see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <QDebug>

#include "GeoParser.h"
#include "GeoTagHandler.h"

// Set to a value greather than 0, to dump parent node chain while parsing
#define DUMP_PARENT_STACK 1

GeoParser::GeoParser(GeoDataGenericSourceType source)
    : QXmlStreamReader()
    , m_document(0)
    , m_source(source)
{
}

GeoParser::~GeoParser()
{
}

#if DUMP_PARENT_STACK > 0
static void dumpParentStack(const QString& name, int size, bool close)
{
    static int depth = 0;

    if (!close)
        depth++;

    QString result;
    for (int i = 0; i < depth; ++i)
        result += ' ';

    if (close) {
        depth--;
        result += "</";
    } else
        result += '<';

    result += name + "> stack size " + QString::number(size);
    fprintf(stderr, "%s\n", qPrintable(result));
}
#endif

bool GeoParser::read(QIODevice* device)
{
    // Assert previous document got released.
    Q_ASSERT(!m_document);
    m_document = createDocument();
    Q_ASSERT(m_document);

    // Set data source
    setDevice(device);

    // Start parsing
    while (!atEnd()) {
        readNext();

        if (isStartElement()) {
            if (isValidDocumentElement()) {
#if DUMP_PARENT_STACK > 0
                dumpParentStack(name().toString(), m_nodeStack.size(), false);
#endif

                parseDocument();

                if (!m_nodeStack.isEmpty())
                    raiseError(QObject::tr("Parsing failed. Still %1 unclosed tags after document end.").arg(m_nodeStack.size()));
            } else
                raiseDocumentElementError();
        }
    }

    if (error())
        qDebug() << "[GeoParser::read] -> Error occurred:" << errorString();

    return !error();
}

bool GeoParser::isValidElement(const QString& tagName) const
{
    return name() == tagName;
}

GeoStackItem GeoParser::parentElement(unsigned int depth)
{
    QStack<GeoStackItem>::const_iterator it = m_nodeStack.constEnd() - 1;

    if (it - depth < m_nodeStack.constBegin())
        return GeoStackItem();

    return *(it - depth);
}

void GeoParser::parseDocument()
{
    Q_ASSERT(isStartElement());

    while (!atEnd()) {
        readNext();
        GeoTagHandler::QualifiedName qName(name().toString(), namespaceUri().toString());

        if (isEndElement()) {
            if (!isValidDocumentElement())
                m_nodeStack.pop();

#if DUMP_PARENT_STACK > 0
            dumpParentStack(name().toString(), m_nodeStack.size(), true);
#endif
            break;
        }

        if (isStartElement()) {
            GeoStackItem stackItem(qName, 0);
            bool processChildren = true;

            if (const GeoTagHandler* handler = GeoTagHandler::recognizes(qName)) {
                stackItem.assignNode(handler->parse(*this));
                processChildren = !isEndElement();
            }

            m_nodeStack.push(stackItem);

#if DUMP_PARENT_STACK > 0
            dumpParentStack(name().toString(), m_nodeStack.size(), false);
#endif

            if (processChildren)
                parseDocument();
            else {
                m_nodeStack.pop();

#if DUMP_PARENT_STACK > 0
                dumpParentStack(name().toString(), m_nodeStack.size(), true);
#endif
            }
        }
    }
}

void GeoParser::raiseDocumentElementError()
{
    raiseError(QObject::tr("File format unrecognized"));
}

GeoDocument* GeoParser::releaseDocument()
{
    GeoDocument* document = m_document;
    m_document = 0;
    return document;
}
<|endoftext|>