/* 
 * $Id: URLTool.cpp,v 1.1 2005/10/19 14:05:44 ozawa Exp $
 *
 * Copyright 2005- ONGS Inc. All rights reserved.
 * 
 * author: Masanori OZAWA (ozawa@ongs.co.jp)
 * version: $Revision: 1.1 $
 *
 * 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 ONGS 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 ONGS 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.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the ONGS Inc.
 * 
 */

#include "xdtp.h"
#include "URLTool.h"

#include "URI.h"
#include "URLTool_Nano.h"
#include "URLTool_Fetch.h"

#ifndef WITHOUT_GDK
#include "gdk-pixbuf/gdk-pixbuf.h"
#endif

using namespace XDTP;

URLTool::URLTool()
{
}

URLTool::~URLTool()
{
}

/**
 * URLで参照するリソースを取得します。
 * 
 * @param url リソースを識別するURL
 * @param buf 取得したバッファへのアドレスを記録する。
 *            不要になった段階で free する必要があります。
 * @param len 取得したバッファのバイト長。
 * @return 成功した場合は true を戻す。
 */
bool URLTool::getResource(const Glib::ustring& url, char** buf, int* len)
{
    const URLTYPE type = parseURLType(url);
    RefPtr<URLTool> pTool = buildURLTool();
    
    if (URL_UNKNOWN == type) {
        return false;
    }
    
    GByteArray* array = g_byte_array_new();
    if (!array) {
        THROW("Out of memory error!");
    }

    if (!pTool->openResource(url, type)) {
        g_byte_array_free(array, TRUE);
        return false;
    }

    int read = -1;
    char data[4096];
    
    try {
        while (read = pTool->readResource(data, sizeof(data)), 0 < read) {
            g_byte_array_append(array, (guint8*)data, read);
        }
        pTool->closeResource();
    }
    catch (...) {
        read = -1;
    }
    
    if (0 > read) {
        g_byte_array_free(array, TRUE);
        return false;
    }
    
    *buf = (char*)array->data;
    *len = array->len;
    g_byte_array_free(array, FALSE);
    
    return true;
}

/**
 * URLで参照するリソースを取得します。
 * 
 * @param url リソースを識別するURL
 * @param file 出力先ファイル名
 * @return 成功した場合は true を戻す。
 *         失敗した場合でもファイルが作成されることがあります。
 *         この場合、ファイルの中身は不定です。
 */
bool URLTool::getResource2File(const Glib::ustring& url, const Glib::ustring& file)
{
    const URLTYPE type = parseURLType(url);
    
    if (URL_UNKNOWN == type) {
        return false;
    }
    
    bool bError = false;
    RefPtr<URLTool> pTool = buildURLTool();
    if (!pTool->openResource(url, type)) {
        return false;
    }
    
    try {
        Glib::RefPtr<Glib::IOChannel> io = Glib::IOChannel::create_from_file(file, "w");
        io->set_encoding(); // set to binary
        
        gssize read = -1;
        gsize write = 0;
        char data[4096];
        
        while (read = pTool->readResource(data, sizeof(data)), 0 < read) {
            if (Glib::IO_STATUS_NORMAL != io->write(data, read, write)) {
                THROW("I/O error.");
            }
        }
        
        io->close();
        pTool->closeResource();
    }
    catch (...) {
        bError = true;
    }
    
    return (bError ? false : true);
}

/**
 * URLで参照するリソースを取得します。
 * 
 * @param url リソースを識別するURL
 * @param fd 出力先ファイルディスクリプタ
 * @return 成功した場合は true を戻す。
 */
bool URLTool::getResource2File(const Glib::ustring& url, int fd)
{
    const URLTYPE type = parseURLType(url);
    
    if (URL_UNKNOWN == type) {
        return false;
    }
    
    bool bError = false;
    RefPtr<URLTool> pTool = buildURLTool();
    if (!pTool->openResource(url, type)) {
        return false;
    }
    
    try {
        int read = -1;
        char data[4096];
        
        while (read = pTool->readResource(data, sizeof(data)), 0 < read) {
            if (read != write(fd, data, read)) {
                THROW("I/O error.");
            }
        }
        pTool->closeResource();
    }
    catch (...) {
        bError = true;
    }
    
    return (bError ? false : true);
}

/**
 * 画像情報のみを取得します。
 * 
 * @param url 画像を識別するURL
 * @param info 情報を格納する IMAGE_INFO 構造体
 * @return 取得に成功した場合は true を戻す。
 */
bool URLTool::getImageInfo(const Glib::ustring& url, IMAGE_INFO& info)
{
#ifndef WITHOUT_GDK
    const URLTYPE type = parseURLType(url);

    if (URL_FILE == type) {
        RefPtr<URI> uri = URI::parse(url);
        if (gdk_pixbuf_get_file_info(uri->getPath().c_str(), &info.width, &info.height)) {
            return true;
        }
    }
    else if (URL_UNKNOWN != type) {
        bool result = false;
        std::string tmpfile = Glib::build_filename(Glib::get_tmp_dir(), "_xdtp_temp.XXXXXX");
        int fd = Glib::mkstemp(tmpfile);
        
        if (0 <= fd) {
            result = URLTool::getResource2File(url, fd);
            close(fd);
        }
        
        if (result) {
            result = 
                (gdk_pixbuf_get_file_info(tmpfile.c_str(), &info.width, &info.height) ? true : false);
        }
        
        if (0 <= fd) {
            remove(tmpfile.c_str());
        }
        
        return result;
    }
#endif
    
    return false;
}

/**
 * リソースの情報を取得します。
 * サーバ側の応答コードを利用して情報を取得する場合があるため、
 * 実際とは異なる情報を取得する場合があります。
 * 
 * @param url リソースを識別するURL
 * @param info 情報を格納する FILE_INFO 構造体
 * @return 取得に成功した場合は true を戻す。
 */
bool URLTool::statResource(const Glib::ustring& url, FILE_INFO& info)
{
    RefPtr<URLTool> pTool = buildURLTool();
    bool result = pTool->statResourceCore(url, info);
    
    return result;
}

RefPtr<URLTool> URLTool::buildURLTool()
{
#ifdef WITH_FETCH
    RefPtr<URLTool> result(new URLTool_Fetch);
#else
    RefPtr<URLTool> result(new URLTool_Nano);
#endif

    return result;
}

URLTYPE URLTool::parseURLType(const Glib::ustring& url)
{
    URLTYPE result = URL_UNKNOWN;
    RefPtr<URI> uri = URI::parse(url);
    
    if (0 >= uri->getScheme().length()) {
        result = URL_FILE;  // no schema's default
    }
    else if (uri->isHttpScheme()) {
        result = URL_HTTP;
    }
    else if (uri->isFtpScheme()) {
        result = URL_FTP;
    }
    else if (uri->isFileScheme()) {
        result = URL_FILE;
    }
    
    return result;
}


syntax highlighted by Code2HTML, v. 0.9.1