void FetchClientPrivate::write_ready()
{
    if (m_postSize == 0)
        return;
    char buff[4096];
    unsigned tail = m_postSize;
    if (tail > sizeof(buff))
        tail = sizeof(buff);
    const char *data = m_client->read_data(buff, tail);
    if (data == NULL){
        m_socket->error_state("Read error");
        return;
    }
    m_postSize -= tail;
    m_socket->writeBuffer.pack(data, tail);
    m_socket->write();
    if (m_speed){
        m_sendSize += tail;
        time_t now;
        time(&now);
        if ((unsigned)now != m_sendTime){
            m_sendTime = now;
            m_sendSize = 0;
        }
        if (m_sendSize > (m_speed << 18)){
            m_socket->pause(1);
            return;
        }
    }
}
void FetchClientPrivate::packet_ready()
{
    if (m_socket->readBuffer.readPos() == m_socket->readBuffer.writePos())
        return;
    for (;;){
        if (m_state == Data){
            unsigned size = m_socket->readBuffer.writePos() - m_socket->readBuffer.readPos();
            if (size){
                if (!m_client->write_data(m_socket->readBuffer.data(m_socket->readBuffer.readPos()), size)){
                    m_socket->error_state("Write error");
                    return;
                }
            }
            m_received += size;
            if (m_received >= m_size){
                m_state = Done;
                m_socket->error_state("");
                return;
            }
            m_socket->readBuffer.init(0);
            m_socket->readBuffer.packetStart();
            return;
        }
        log_packet(m_socket->readBuffer, false, HTTPPacket);
        string line;
        string opt;
        if (!read_line(line)){
            m_socket->readBuffer.init(0);
            m_socket->readBuffer.packetStart();
            return;
        }
        switch (m_state){
        case None:
#ifdef USE_OPENSSL
        case SSLConnect:
#endif
            if (getToken(line, ' ').substr(0, 5) != "HTTP/"){
                m_socket->error_state("Bad HTTP answer");
                return;
            }
            m_code = atol(getToken(line, ' ').c_str());
            m_state = Header;
            break;
        case Header:
            if (line.empty()){
                m_state = Data;
                break;
            }
            m_hIn += line;
            m_hIn += '\x00';
            opt = getToken(line, ':');
            if (opt == "Content-Length"){
                const char *p;
                for (p = line.c_str(); *p; p++)
                    if (*p != ' ')
                        break;
                m_size = atol(p);
            }
            if ((opt == "Location") && m_bRedirect){
                const char *p;
                for (p = line.c_str(); *p; p++)
                    if (*p != ' ')
                        break;
                string proto;
                string host;
                string user;
                string pass;
                string uri;
                string extra;
                unsigned short port;
                if (!FetchClient::crackUrl(p, proto, host, port, user, pass, uri, extra)){
                    FetchClient::crackUrl(m_uri.c_str(), proto, host, port, user, pass, uri, extra);
                    extra = "";
                    if (*p == '/'){
                        uri = p;
                    }else{
                        int n = uri.find_last_of('/');
                        uri = uri.substr(0, n + 1);
                        uri += p;
                    }
                }
                m_uri = proto;
                m_uri += "://";
                m_uri += host;
                m_uri += ":";
                m_uri += number(port);
                m_uri += uri;
                if (!extra.empty()){
                    m_uri += "?";
                    m_uri += extra;
                }
                m_state = Redirect;
                m_socket->close();
                m_socket->error_state("");
                return;
            }
            break;
        default:
            break;
        }
    }
}
void FetchClientPrivate::connect_ready()
{
#ifdef USE_OPENSSL
    if ((m_state == None) & m_bHTTPS){
        m_socket->setRaw(true);
        m_socket->readBuffer.init(0);
        HTTPSClient *https = new HTTPSClient(m_socket->socket());
        if (!https->init()){
            m_socket->error_state("Can't initialize HTTPS");
            return;
        }
        m_state = SSLConnect;
        m_socket->setSocket(https);
        https->connect();
        https->process();
        return;
    }
#endif
    log(L_DEBUG, "HTTP connect ready");
    m_socket->setRaw(true);
    m_socket->writeBuffer.packetStart();

    string proto;
    string host;
    string user;
    string pass;
    string uri;
    string extra;
    unsigned short port;
    FetchClient::crackUrl(m_uri.c_str(), proto, host, port, user, pass, uri, extra);
    if (!extra.empty()){
        uri += "?";
        uri += extra;
    }
    unsigned postSize = m_client->post_size();
    m_socket->writeBuffer
    << ((postSize != NO_POSTSIZE) ? "POST " : "GET ")
    << uri.c_str()
    << " HTTP/1.0\r\n";
    if (!findHeader("Host"))
        m_socket->writeBuffer
        << "Host: "
        << host.c_str()
        << "\r\n";
    if (!findHeader("User-Agent"))
        m_socket->writeBuffer
        << "User-Agent: " << FetchManager::manager->user_agent.c_str() << "\r\n";
    if (!findHeader("Authorization") && !user.empty())
        m_socket->writeBuffer
        << "Authorization: basic "
        << basic_auth(user.c_str(), pass.c_str()).c_str()
        << "\r\n";
    if (postSize != NO_POSTSIZE){
        if (!findHeader("Content-Length"))
            m_socket->writeBuffer
            << "Content-Length: "
            << number(postSize).c_str()
            << "\r\n";
        m_postSize = postSize;
    }
    for (HEADERS_MAP::iterator it = m_hOut.begin(); it != m_hOut.end(); ++it){
        m_socket->writeBuffer
        << (*it).first.c_str()
        << ": "
        << (*it).second.c_str()
        << "\r\n";
    }
    m_socket->writeBuffer
    << "\r\n";
    log_packet(m_socket->writeBuffer, true, HTTPPacket);
    m_socket->write();
    m_socket->readBuffer.init(0);
    m_socket->readBuffer.packetStart();
}
void FetchClientPrivate::_fetch(const char *headers, Buffer *postData, bool bRedirect)
{
    stop();
    m_bDone = false;
    m_data.init(0);
    m_data.packetStart();
    m_postData  = postData;
    m_bRedirect = bRedirect;
    m_postSize  = 0;
    m_sendTime	= 0;
    m_sendSize  = 0;
#ifdef WIN32
    m_thread	= NULL;
#endif
    if (headers){
        string head = headers;
        while (!head.empty()){
            string header = getToken(head, '\n');
            string key = getToken(header, ':');
            unsigned n;
            for (n = 0; n < header.length(); n++)
                if (header[n] != ' ')
                    break;
            header = header.substr(n);
            addHeader(key.c_str(), header.c_str());
        }
    }
#ifdef WIN32
    m_errCode = 0;
    if (hInet){
        m_state  = Data;
        m_thread = new FetchThread(this);
        m_thread->start();
        return;
    }
#endif
    m_received = 0;
    m_socket = new ClientSocket(this);
#ifdef USE_OPENSSL
    m_bHTTPS = false;
#endif
    string proto;
    string host;
    string user;
    string pass;
    string uri;
    string extra;
    unsigned short port;
    if (!FetchClient::crackUrl(m_uri.c_str(), proto, host, port, user, pass, uri, extra)){
        m_socket->error_state("Bad URL");
        return;
    }
    if (proto != "http"){
#ifdef USE_OPENSSL
        if (proto == "https"){
            m_bHTTPS = true;
        }else{
#endif
            log(L_WARN, "Unsupported protocol %s", m_uri.c_str());
            return;
#ifdef USE_OPENSSL
        }
#endif
    }
    log(L_DEBUG, "Start connect %s:%u", host.c_str(), port);
    m_socket->connect(host.c_str(), port, (TCPClient*)(-1));
}