home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Divers
/
yagirc-0.51.tar.gz
/
yagirc-0.51.tar
/
yagirc-0.51
/
dcc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-05-11
|
18KB
|
712 lines
/*
dcc.c : DCC functions
Copyright (C) 1998 Timo Sirainen
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <glib.h>
#include "os.h"
#include "dcc.h"
#include "gui.h"
#include "irc.h"
#include "txt.h"
#include "events.h"
#include "network.h"
#include "params.h"
#include "script.h"
#include "misc.h"
GList *dcclist;
/* Get next parameter */
static char *get_param(char **data)
{
char *pos;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(*data != NULL, NULL);
pos = *data;
while (**data != '\0' && **data != ' ') (*data)++;
if (**data == ' ') *(*data)++ = '\0';
return pos;
}
/* Initialize DCC */
void dcc_init(void)
{
dcclist = NULL;
}
/* Deinitialize DCC */
void dcc_deinit(void)
{
GList *tmp;
DCC_REC *dcc;
for (tmp = g_list_first(dcclist); tmp != NULL; tmp = tmp->next)
{
dcc = (DCC_REC *) tmp->data;
if (!dcc->handle != -1) net_disconnect(dcc->handle);
g_free(dcc->nick);
g_free(dcc->arg);
if (dcc->addr != NULL) g_free(dcc->addr);
g_free(dcc);
}
g_list_free(dcclist); dcclist = NULL;
}
/* Create new DCC record */
static DCC_REC *dcc_create(int type, int handle, char *nick, char *arg)
{
DCC_REC *dcc;
g_return_val_if_fail(nick != NULL, NULL);
g_return_val_if_fail(arg != NULL, NULL);
dcc = (DCC_REC *) g_malloc(sizeof(DCC_REC));
memset(dcc, 0, sizeof(DCC_REC));
dcc->type = type;
dcc->arg = g_strdup(arg);
dcc->nick = g_strdup(nick);
dcc->handle = handle;
/*dcc->addr = NULL;
dcc->port = 0;
dcc->buf = NULL;
dcc->starttime = 0;*/
dcclist = g_list_append(dcclist, dcc);
return dcc;
}
/* Destroy DCC record */
static void dcc_destroy(DCC_REC *dcc, int dealloc)
{
g_return_if_fail(dcc != NULL);
if (dcc->fhandle != -1) { close(dcc->fhandle); dcc->fhandle = -1; }
if (dcc->handle != -1) { net_disconnect(dcc->handle); dcc->handle = -1; }
g_free(dcc->nick); dcc->nick = NULL;
g_free(dcc->arg); dcc->arg = NULL;
if (dcc->buf != NULL) { g_free(dcc->buf); dcc->buf = NULL; }
if (dcc->addr != NULL) { g_free(dcc->addr); dcc->addr = NULL; }
if (dcc->tag != -1) { gui_input_remove(dcc->tag); dcc->tag = -1; }
dcclist = g_list_remove(dcclist, dcc);
if (dealloc) g_free(dcc);
}
/* Close specified DCC record */
static int dcc_find_close(char *nick, int type, char *fname)
{
DCC_REC *dcc;
g_return_val_if_fail(nick != NULL, 0);
g_return_val_if_fail(fname != NULL, 0);
dcc = dcc_find_item(type, nick, fname);
if (dcc == NULL) return 0;
gui_dcc_force_close(dcc);
dcc_destroy(dcc, 1);
return 1;
}
#if 0
/* Return size in bytes/kB/MB depending how big file is */
static char *bytes2str(char *buf, long bytes)
{
g_return_val_if_fail(buf != NULL, NULL);
if (bytes < 1024)
sprintf(buf, "%lu bytes", bytes);
else if (bytes < 1024*1024)
sprintf(buf, "%0.2f kB", bytes/1024.0);
else
sprintf(buf, "%0.2f MB", bytes/(float)(1024*1024));
return buf;
}
#endif
/* input function: DCC GET received data */
static void dcc_receive(DCC_REC *dcc)
{
char buf[1024];
int len;
long recd;
g_return_if_fail(dcc != NULL);
if (dcc->starttime == 0) dcc->starttime = time(NULL);
for (;;)
{
len = net_receive(dcc->handle, buf, sizeof(buf));
if (len == 0) break;
if (len < 0)
{
/* socket closed - transmit complete (or other side died..) */
dcc_destroy(dcc, 0);
gui_dcc_close(dcc);
return;
}
write(dcc->fhandle, buf, len);
dcc->transfd += len;
}
/* send number of total bytes received */
recd = htonl(dcc->transfd);
net_transmit(dcc->handle, (char *) &recd, 4);
/* Update GUI window */
gui_dcc_update(dcc);
}
/* /DCC GET command */
int dcc_get(char *data)
{
GList *tmp;
DCC_REC *dcc;
char *str;
g_return_val_if_fail(data != NULL, RET_ERR_PARAM);
if (*data == '\0') return RET_NOT_ENOUGH_PARAMS;
for (tmp = g_list_first(dcclist); tmp != NULL; tmp = tmp->next)
{
dcc = (DCC_REC *) tmp->data;
if (dcc->type == DCC_TYPE_GET && dcc->handle == -1 &&
strcasecmp(dcc->nick, data) == 0)
{
/* found! */
break;
}
}
if (tmp == NULL)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_GET_NOT_FOUND, data);
return RET_ERROR;
}
dcc->handle = net_connect(dcc->addr, dcc->port);
if (dcc->handle == -1)
{
/* error connecting */
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CONNECT_ERROR, dcc->addr, dcc->port);
dcc_destroy(dcc, 1);
}
else
{
/* ok connect */
str = strrchr(dcc->arg, '/');
if (str == NULL) str = dcc->arg; else str++;
dcc->fhandle = open(str, O_WRONLY | O_TRUNC | O_CREAT, FILE_CREATE_MODE);
if (dcc->handle == -1)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CANT_CREATE, str);
return RET_ERROR;
}
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_GET_CONNECTED,
dcc->arg, dcc->nick, dcc->addr, dcc->port);
dcc->transfd = 0;
gui_dcc_init(dcc);
dcc->tag = gui_input_add(dcc->handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_receive, dcc);
}
return RET_OK;
}
/* Abort DCC transfer */
int dcc_abort(DCC_REC *dcc)
{
g_return_val_if_fail(dcc != NULL, 0);
dcc_destroy(dcc, 0);
gui_dcc_close(dcc);
return 1;
}
/* DCC SEND - send more data */
static void dcc_send_data(DCC_REC *dcc)
{
char buf[1024];
int n;
g_return_if_fail(dcc != NULL);
n = read(dcc->fhandle, buf, sizeof(buf));
if (n <= 0)
{
/* end of file .. or some error .. */
dcc_abort(dcc);
return;
}
dcc->transfd += net_transmit(dcc->handle, buf, n);
gui_dcc_update(dcc);
}
/* input function: DCC SEND received some data */
static void dcc_send_func(DCC_REC *dcc)
{
long bytes;
int ret;
g_return_if_fail(dcc != NULL);
/* we need to get 4 bytes.. */
ret = net_receive(dcc->handle, dcc->buf+dcc->bufpos, 4-dcc->bufpos);
if (ret == -1)
{
dcc_abort(dcc);
return;
}
dcc->bufpos += ret;
if (dcc->bufpos < 4)
{
/* those 4 bytes haven't came yet, wait more.. */
return;
}
dcc->bufpos = 0;
bytes = 0; memcpy(&bytes, dcc->buf, 4); bytes = ntohl(bytes);
if (bytes == dcc->transfd)
{
/* ok, all bytes are sent - send more */
dcc_send_data(dcc);
}
}
/* input function: DCC SEND - someone tried to connect to our socket */
static void dcc_send_init(DCC_REC *dcc)
{
char addr[40];
int handle, port;
g_return_if_fail(dcc != NULL);
/* accept connection */
handle = net_accept(dcc->handle, addr, &port);
if (handle == -1) return;
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_SEND_CONNECTED,
dcc->arg, dcc->nick, addr, port);
gui_input_remove(dcc->tag);
close(dcc->handle);
dcc->handle = handle;
dcc->addr = g_strdup(addr);
dcc->port = port;
dcc->transfd = 0;
dcc->buf = (char *) g_malloc(4);
dcc->bufpos = 0;
dcc->starttime = time(NULL);
dcc->tag = gui_input_add(handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_send_func, dcc);
/* send first bytes */
dcc_send_data(dcc);
}
/* /DCC SEND command */
int dcc_send(char *data)
{
char tmp[512], addr[50], *target, *fname, *ptr;
int fh, h, port;
long fsize;
DCC_REC *dcc;
g_return_val_if_fail(data != NULL, RET_ERR_PARAM);
if (curwin->defserv == NULL) return RET_NOT_CONNECTED;
target = get_param(&data);
fname = get_param(&data);
if (dcc_find_item(DCC_TYPE_SEND, target, fname))
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_SEND_EXISTS, fname, target);
return RET_ERROR;
}
ptr = convhome(fname);
fh = open(ptr, O_RDONLY);
g_free(ptr);
if (fh == -1)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_SEND_NOT_FOUND, fname);
return RET_ERROR;
}
fsize = lseek(fh, 0, SEEK_END);
lseek(fh, 0, SEEK_SET);
/* get the IP address we use with IRC server */
if (!net_getsockname(curwin->defserv->handle, addr, NULL))
{
close(fh);
return RET_ERR_GETSOCKNAME;
}
/* start listening in any port */
port = 0;
h = net_listen(addr, &port);
if (h == -1)
{
close(fh);
return RET_ERR_LISTEN;
}
/* skip path */
ptr = strrchr(fname, '/');
if (ptr != NULL) fname = ptr+1;
dcc = dcc_create(DCC_TYPE_SEND, h, target, fname);
dcc->size = fsize;
dcc->fhandle = fh;
dcc->tag = gui_input_add(h, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_send_init, dcc);
/* send DCC request */
sprintf(tmp, "PRIVMSG %s :\001DCC SEND %s %lu %d %lu\001",
target, fname, (unsigned long) htonl(inet_addr(addr)), port, fsize);
irc_send_cmd(curwin->defserv, tmp);
gui_dcc_init(dcc);
return RET_OK;
}
/* input function: DCC CHAT received some data.. */
static void dcc_chat_input(DCC_REC *dcc)
{
char tmp[512];
int n;
g_return_if_fail(dcc != NULL);
do
{
n = read_line(1, dcc->handle, tmp, dcc->buf, 512, &dcc->bufpos);
if (n == -1)
{
/* connection lost */
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CHAT_DISCONNECTED, dcc->nick);
dcc_destroy(dcc, 1);
return;
}
if (n > 0)
{
dcc->transfd += n;
#ifdef USE_SCRIPT
script_event(NULL, dcc->nick, dcc->addr, "CTCP", tmp);
#endif
gui_dcc_chat_write(dcc, tmp);
}
}
while (n > 0);
}
/* input function: DCC CHAT - someone tried to connect to our socket */
static void dcc_chat_listen(DCC_REC *dcc)
{
char addr[40];
int handle, port;
g_return_if_fail(dcc != NULL);
/* accept connection */
handle = net_accept(dcc->handle, addr, &port);
if (handle == -1) return;
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CHAT_CONNECTED,
dcc->nick, addr, port);
gui_input_remove(dcc->tag);
close(dcc->handle);
dcc->handle = handle;
dcc->addr = g_strdup(addr);
dcc->port = port;
dcc->transfd = 0;
dcc->buf = (char *) g_malloc(512);
dcc->bufpos = 0;
dcc->starttime = time(NULL);
dcc->tag = gui_input_add(handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_chat_input, dcc);
}
/* /DCC CHAT command */
int dcc_chat(char *data)
{
DCC_REC *dcc;
int port, handle;
char str[512], addr[50];
g_return_val_if_fail(data != NULL, 2);
dcc = dcc_find_item(DCC_TYPE_CHAT, data, NULL);
if (dcc != NULL)
{
/* found from dcc list - so we're the connecting side.. */
if (dcc->handle != -1)
{
/* already connected.. */
return RET_OK;
}
dcc->handle = net_connect(dcc->addr, dcc->port);
if (dcc->handle == -1)
{
/* error connecting */
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CONNECT_ERROR, dcc->addr, dcc->port);
dcc_destroy(dcc, 1);
}
else
{
/* connect ok. */
dcc->buf = (char *) g_malloc(512);
gui_dcc_chat_init(dcc);
dcc->tag = gui_input_add(dcc->handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_chat_input, dcc);
}
return RET_OK;
}
/* send dcc chat request */
if (curwin->defserv == NULL) return RET_NOT_CONNECTED;
if (!net_getsockname(curwin->defserv->handle, addr, NULL))
return RET_ERR_GETSOCKNAME;
port = 0;
handle = net_listen(addr, &port);
if (handle == -1) return RET_ERR_LISTEN;
dcc = dcc_create(DCC_TYPE_CHAT, handle, data, "chat");
dcc->handle = handle;
dcc->tag = gui_input_add(dcc->handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) dcc_chat_listen, dcc);
/* send the request */
sprintf(str, "PRIVMSG %s :\001DCC CHAT CHAT %lu %d\001",
data, (unsigned long) htonl(inet_addr(addr)), port);
irc_send_cmd(curwin->defserv, str);
return RET_OK;
}
/* Send text to DCC chat */
int dcc_chat_write(DCC_REC *dcc, char *str)
{
g_return_val_if_fail(dcc != NULL, 0);
g_return_val_if_fail(str != NULL, 0);
net_transmit(dcc->handle, str, strlen(str));
net_transmit(dcc->handle, "\r\n", 2);
return 1;
}
/* /DCC LIST */
int dcc_list(char *data)
{
DCC_REC *dcc;
GList *tmp;
g_return_val_if_fail(data != NULL, RET_ERR_PARAM);
drawtext(curwin, TXT_TYPE_DCC, "%!DCC%! connections (under work..)\n");
for (tmp = g_list_first(dcclist); tmp != NULL; tmp = tmp->next)
{
dcc = (DCC_REC *) tmp->data;
drawtext(curwin, TXT_TYPE_DCC, " %s: (%d) %s (%lu/%lu)\n", dcc->nick, dcc->type,
dcc->arg, dcc->transfd, dcc->size);
}
return RET_OK;
}
/* /DCC CLOSE */
int dcc_close(char *data)
{
DCC_REC *dcc, *found;
GList *tmp;
char *type, *nick, *arg;
int itype;
g_return_val_if_fail(data != NULL, RET_ERR_PARAM);
type = get_param(&data);
nick = get_param(&data);
arg = get_param(&data);
strupr(type);
if (strcmp(type, "CHAT") == 0)
itype = DCC_TYPE_CHAT;
else if (strcmp(type, "GET") == 0)
itype = DCC_TYPE_GET;
else if (strcmp(type, "SEND") == 0)
itype = DCC_TYPE_SEND;
else
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_UNKNOWN_TYPE, type);
return RET_ERROR;
}
dcc = found = NULL;
for (tmp = g_list_first(dcclist); tmp != NULL; tmp = tmp->next)
{
dcc = (DCC_REC *) tmp->data;
if (dcc->type == itype && strcasecmp(nick, dcc->nick) == 0)
{
if (found != NULL)
{
/* multiple matches found - quit */
return RET_MULTIPLE_MATCHES;
}
found = dcc;
if (*arg != '\0' && strcmp(arg, dcc->arg) == 0)
{
/* identical match */
break;
}
}
}
if (found != NULL)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CLOSE, type, dcc->nick, dcc->arg);
dcc_destroy(found, 1);
}
return RET_OK;
}
/* Find DCC record, arg can be NULL */
DCC_REC *dcc_find_item(int type, char *nick, char *arg)
{
DCC_REC *dcc;
GList *tmp;
g_return_val_if_fail(nick != NULL, NULL);
for (tmp = g_list_first(dcclist); tmp != NULL; tmp = tmp->next)
{
dcc = (DCC_REC *) tmp->data;
if (dcc->type == type && strcasecmp(dcc->nick, nick) == 0 &&
(arg == NULL || strcmp(dcc->arg, arg) == 0))
return dcc;
}
return NULL;
}
/* Handle DCC CTCP commands */
int dcc_handle_ctcp(char *sender, char *data)
{
char *type, *arg;
int port;
unsigned long size, addr;
DCC_REC *dcc;
g_return_val_if_fail(data != NULL, 1);
g_return_val_if_fail(sender != NULL, 1);
type = event_get_param(&data);
arg = event_get_param(&data);
if (sscanf(event_get_param(&data), "%lu", &addr) != 1) addr = 0;
if (sscanf(event_get_param(&data), "%d", &port) != 1) port = 0;
if (sscanf(event_get_param(&data), "%lu", &size) != 1) size = 0;
addr = (long) ntohl(addr);
dcc = dcc_create(0, -1, sender, arg);
dcc->fhandle = -1;
dcc->handle = -1;
dcc->addr = g_strdup(inet_ntoa(*((struct in_addr *) &addr)));
dcc->port = port;
dcc->tag = -1;
dcc->size = size;
if (strcasecmp(type, "SEND") == 0)
{
dcc->type = DCC_TYPE_GET;
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_SEND, type, sender, dcc->addr, port, arg, size);
}
else if (strcasecmp(type, "CHAT") == 0)
{
dcc->type = DCC_TYPE_CHAT;
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_CHAT, sender, dcc->addr, port);
}
else
{
/* unknown DCC command */
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_UNKNOWN_CTCP, type, sender, dcc->addr, port, arg, size);
dcc_destroy(dcc, 1); dcc = NULL;
}
return 0;
}
/* Handle DCC replies */
int dcc_reply(char *sender, char *data)
{
char *cmd;
g_return_val_if_fail(data != NULL, 0);
g_return_val_if_fail(sender != NULL, 0);
cmd = get_param(&data);
if (strcasecmp(cmd, "REJECT") == 0)
{
cmd = get_param(&data);
if (strcasecmp(cmd, "GET") == 0)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_REJECTED, "GET", sender, data);
dcc_find_close(sender, DCC_TYPE_GET, data);
}
else if (strcasecmp(cmd, "SEND") == 0)
{
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_REJECTED, "SEND", sender, data);
dcc_find_close(sender, DCC_TYPE_SEND, data);
}
}
else
{
/* unknown DCC reply */
drawtext(curwin, TXT_TYPE_DCC, IRCTXT_DCC_UNKNOWN_REPLY, cmd, sender, data);
}
return 1;
}