/*
* Copyright (C) 2002-2004 Morten Brix Pedersen <morten@wtf.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <sys/types.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <sys/stat.h>
#define stat _stat
#endif
#include <iostream>
#include <sstream>
#include <glibmm/fileutils.h>
#include "ServerConnection.h"
#include "LostIRCApp.h"
#include "DCC.h"
void DCC::setStatus(Status s)
{
_status = s;
App->getDcc().statusChange(this);
}
DCC_Send_In::DCC_Send_In(const Glib::ustring& filename, const Glib::ustring& nick, unsigned long address, unsigned short port, unsigned long size)
: _outfile(), _filename(filename), _nick(nick), _address(address),
_port(port), _size(size), _pos(0)
{
setStatus(WAITING);
_socket.on_connected.connect(sigc::mem_fun(*this, &DCC_Send_In::on_connected));
_socket.on_error.connect(sigc::mem_fun(*this, &DCC_Send_In::on_connection_failed));
_socket.on_data_pending.connect(sigc::mem_fun(*this, &DCC_Send_In::onReadData));
_downloaddir = Glib::ustring(App->home) + "/.lostirc/downloads/";
#ifndef WIN32
mkdir(_downloaddir.c_str(), 0700);
#else
mkdir(_downloaddir.c_str());
#endif
_filename = _downloaddir + _filename;
if (Glib::file_test(_filename, Glib::FILE_TEST_EXISTS))
getUseableFilename(1);
}
void DCC_Send_In::on_connected(Glib::IOCondition cond)
{
FE::emit(FE::get(CLIENTMSG) << _("DCC connected. Receiving file..."), FE::CURRENT);
setStatus(ONGOING);
}
void DCC_Send_In::on_connection_failed(const char *str)
{
FE::emit(FE::get(CLIENTMSG) << _("Couldn't connect: ") << Util::convert_to_utf8(str), FE::CURRENT);
setStatus(FAIL);
}
void DCC_Send_In::start()
{
_outfile.open(_filename.c_str());
_socket.connect(_address, _port);
FE::emit(FE::get(CLIENTMSG) << _("Receiving from:") << _socket.getRemoteIP(), FE::CURRENT);
}
void DCC_Send_In::cancel()
{
setStatus(STOPPED);
_socket.disconnect();
_outfile.close();
}
void DCC_Send_In::onReadData()
{
#ifdef DEBUG
App->log << "DCC_Send_In::onReadData(): reading.." << std::endl;
#endif
try {
char buf[4096];
int received = 0;
if (_socket.receive(buf, 4095, received)) {
_pos += received;
if (_outfile.good())
_outfile.write(buf, received);
int tmp = 0;
unsigned long pos = htonl(_pos);
_socket.send(reinterpret_cast<char *>(&pos), 4, tmp);
#ifdef DEBUG
App->log << "DCC_Send_In::onReadData(): _pos: " << _pos << std::endl;
App->log << "DCC_Send_In::onReadData(): _size: " << _size << std::endl;
#endif
if (_pos >= _size) {
#ifdef DEBUG
App->log << "DCC_Send_In::onReadData(): done receiving!" << std::endl;
#endif
_outfile.close();
FE::emit(FE::get(CLIENTMSG) << _("File received successfully:") << _filename, FE::CURRENT);
setStatus(DONE);
_socket.disconnect();
}
}
} catch (SocketException &e) {
FE::emit(FE::get(CLIENTMSG) << _("Couldn't receive: ") << Util::convert_to_utf8(e.what()), FE::CURRENT);
setStatus(FAIL);
} catch (SocketDisconnected &e) {
FE::emit(FE::get(CLIENTMSG) << _("DCC connection closed."), FE::CURRENT);
_socket.disconnect();
}
}
void DCC_Send_In::getUseableFilename(int i)
{
std::stringstream ss;
Glib::ustring myint;
ss << i;
ss >> myint;
Glib::ustring newfilename = _filename + "." + myint;
if (Glib::file_test(newfilename, Glib::FILE_TEST_EXISTS))
getUseableFilename(++i);
else
_filename = newfilename;
}
DCC_Send_Out::DCC_Send_Out(const Glib::ustring& filename, const Glib::ustring& nick, ServerConnection *conn)
: _infile(), _filename(filename), _nick(nick), _pos(0)
{
setStatus(WAITING);
_socket.on_error.connect(sigc::mem_fun(*this, &DCC_Send_Out::on_bind_failed));
_socket.on_accepted_connection.connect(sigc::mem_fun(*this, &DCC_Send_Out::onAccept));
_socket.on_can_send_data.connect(sigc::mem_fun(*this, &DCC_Send_Out::onSendData));
Glib::ustring localip;
struct stat st;
stat(filename.c_str(), &st);
_size = st.st_size;
if (App->options.dccip->empty())
localip = conn->getLocalIP();
else
localip = App->options.dccip;
#ifdef DEBUG
App->log << "DCC_Send_Out::DCC_Send_Out(): size: " << st.st_size << std::endl;
App->log << "DCC_Send_Out::DCC_Send_Out(): ip: " << localip << std::endl;
#endif
if (_socket.bind(App->options.dccport)) {
std::ostringstream ss;
ss << "DCC SEND " << stripPath(_filename) << " " << ntohl(inet_addr(localip.c_str())) << " " << ntohs(_socket.getSockAddr().sin_port) << " " << _size;
conn->sendCtcp(nick, ss.str());
_infile.open(_filename.c_str());
FE::emit(FE::get(CLIENTMSG) << _("DCC SEND request sent. Sending from:") << localip, FE::CURRENT);
}
}
void DCC_Send_Out::cancel()
{
setStatus(STOPPED);
_socket.disconnect();
_infile.close();
}
void DCC_Send_Out::on_bind_failed(const char *str)
{
FE::emit(FE::get(CLIENTMSG) << _("Couldn't bind: ") << Util::convert_to_utf8(str), FE::CURRENT);
setStatus(FAIL);
// FIXME: add dcc-done?
}
void DCC_Send_Out::onAccept()
{
FE::emit(FE::get(CLIENTMSG) << _("Connection accepted."), FE::CURRENT);
setStatus(ONGOING);
}
void DCC_Send_Out::onSendData()
{
#ifdef DEBUG
App->log << "DCC_Send_Out::onSendData(): sending..." << std::endl;
#endif
char buf[1024];
_infile.read(buf, sizeof(buf));
int read_chars = _infile.gcount();
try {
int sent = 0;
if (_socket.send(buf, read_chars, sent)) {
_pos += read_chars;
#ifdef DEBUG
App->log << "DCC_Send_Out::onSendData(): sent: " << sent << std::endl;
App->log << "DCC_Send_Out::onSendData(): _pos: " << _pos << std::endl;
App->log << "DCC_Send_Out::onSendData(): _size: " << _size << std::endl;
#endif
if (_pos >= _size) {
#ifdef DEBUG
App->log << "DCC_Send_Out::onSendData(): done sending!" << std::endl;
#endif
_infile.close();
FE::emit(FE::get(CLIENTMSG) << _("File sent successfully:") << _filename, FE::CURRENT);
setStatus(DONE);
_socket.disconnect();
}
}
} catch (SocketException &e) {
FE::emit(FE::get(CLIENTMSG) << _("Couldn't send: ") << Util::convert_to_utf8(e.what()), FE::CURRENT);
setStatus(FAIL);
}
}
bool DCC_queue::start_dcc(int n)
{
std::map<int, DCC*>::const_iterator i = _dccs.find(n);
if (i != _dccs.end()) {
i->second->start();
return true;
}
return false;
}
int DCC_queue::addDccSendIn(const Glib::ustring& filename, const Glib::ustring& nick, unsigned long address, unsigned short port, unsigned long size)
{
if (size == 0) {
FE::emit(FE::get(CLIENTMSG) << _("Incoming file has zero size. Sender:") << nick, FE::CURRENT);
return 0;
} else {
DCC_Send_In *d = new DCC_Send_In(filename, nick, address, port, size);
d->_number_in_queue = ++_count;
_dccs[_count] = d;
App->fe->newDCC(d);
return _count;
}
}
int DCC_queue::addDccSendOut(const Glib::ustring& file, const Glib::ustring& nick, ServerConnection *conn)
{
Glib::ustring filename = expandHome(file);
if (!Glib::file_test(filename.c_str(), Glib::FILE_TEST_EXISTS)) {
FE::emit(FE::get(CLIENTMSG) << _("File not found: ") << filename, FE::CURRENT);
return 0;
} else {
DCC_Send_Out *d = new DCC_Send_Out(filename, nick, conn);
d->_number_in_queue = ++_count;
_dccs[_count] = d;
App->fe->newDCC(d);
return _count;
}
}
void DCC_queue::statusChange(DCC *dcc)
{
App->fe->dccStatusChanged(dcc);
}
Glib::ustring expandHome(const Glib::ustring& str)
{
if (!str.empty() && str.at(0) == '~') {
Glib::ustring new_str = App->home + str.substr(1);
return new_str;
}
return str;
}
Glib::ustring stripPath(const Glib::ustring& str)
{
return str.substr(str.find_last_of('/') + 1);
}
syntax highlighted by Code2HTML, v. 0.9.1