📄 dcc.c
字号:
/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * 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 * * Wayne Conrad, 3 Apr 1999: Color-coded DCC file transfer status windows * Bernhard Valenti <bernhard.valenti@gmx.net> 2000-11-21: Fixed DCC send behind nat * * 2001-03-08 Added support for getting "dcc_ip" config parameter. * Jim Seymour (jseymour@LinxNet.com) */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <errno.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#define WANTSOCKET#define WANTARPA#include "inet.h"#include "xchat.h"#include "perlc.h"#include "util.h"#include "plugin.h"#include "fe.h"#include "outbound.h"#include "inbound.h"#include "network.h"#include "text.h"#include "xchatc.h"char *dcctypes[] = { "SEND", "RECV", "CHAT", "CHAT" };struct dccstat_info dccstat[] = { {N_("Waiting"), 1 /*black */ }, {N_("Active"), 12 /*cyan */ }, {N_("Failed"), 4 /*red */ }, {N_("Done"), 3 /*green */ }, {N_("Connect"), 1 /*black */ }, {N_("Aborted"), 4 /*red */ },};static struct DCC *new_dcc (void);static voiddcc_calc_cps (struct DCC *dcc){ time_t sec; sec = time (0) - dcc->starttime; if (sec < 1) sec = 1; if (dcc->type == TYPE_SEND) dcc->cps = (dcc->ack - dcc->resumable) / sec; else dcc->cps = (dcc->pos - dcc->resumable) / sec;}/* this is called from xchat.c:xchat_misc_checks() every 2 seconds. */voiddcc_check_timeouts (void){ struct DCC *dcc; time_t tim = time (0); GSList *next, *list = dcc_list; while (list) { dcc = (struct DCC *) list->data; next = list->next; if (dcc->dccstat == STAT_ACTIVE) { dcc_calc_cps (dcc); switch (dcc->type) { case TYPE_SEND: fe_dcc_update_send (dcc); break; case TYPE_RECV: fe_dcc_update_recv (dcc); break; } } switch (dcc->dccstat) { case STAT_ACTIVE: if (dcc->type == TYPE_SEND || dcc->type == TYPE_RECV) { if (prefs.dccstalltimeout > 0) { if (tim - dcc->lasttime > prefs.dccstalltimeout) { EMIT_SIGNAL (XP_TE_DCCSTALL, dcc->serv->front_session, dcctypes[(int) dcc->type], file_part (dcc->file), dcc->nick, NULL, 0); dcc_close (dcc, 0, TRUE); } } } break; case STAT_QUEUED: if (dcc->type == TYPE_SEND || dcc->type == TYPE_CHATSEND) { if (tim - dcc->offertime > prefs.dcctimeout) { if (prefs.dcctimeout > 0) { EMIT_SIGNAL (XP_TE_DCCTOUT, dcc->serv->front_session, dcctypes[(int) dcc->type], file_part (dcc->file), dcc->nick, NULL, 0); dcc_close (dcc, 0, TRUE); } } } break; } list = next; }}static intdcc_connect_sok (struct DCC *dcc){ int sok; struct sockaddr_in addr; sok = socket (AF_INET, SOCK_STREAM, 0); if (sok == -1) return -1; memset (&addr, 0, sizeof (addr)); addr.sin_port = htons (dcc->port); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (dcc->addr); set_nonblocking (sok); connect (sok, (struct sockaddr *) &addr, sizeof (addr)); return sok;}static voidupdate_dcc_window (int type){ switch (type) { case TYPE_SEND: fe_dcc_update_send_win (); break; case TYPE_RECV: fe_dcc_update_recv_win (); break; case TYPE_CHATRECV: case TYPE_CHATSEND: fe_dcc_update_chat_win (); break; }}voiddcc_close (struct DCC *dcc, int dccstat, int destroy){ char type = dcc->type; if (dcc->wiotag) { fe_input_remove (dcc->wiotag); dcc->wiotag = 0; } if (dcc->iotag) { fe_input_remove (dcc->iotag); dcc->iotag = 0; } if (dcc->sok != -1) { closesocket (dcc->sok); dcc->sok = -1; } if (dcc->fp != -1) { close (dcc->fp); dcc->fp = -1; } dcc->dccstat = dccstat; if (dcc->dccchat) { free (dcc->dccchat); dcc->dccchat = NULL; } if (destroy) { dcc_list = g_slist_remove (dcc_list, dcc); if (dcc->file) free (dcc->file); if (dcc->destfile) free (dcc->destfile); free (dcc->nick); free (dcc); update_dcc_window (type); return; } switch (type) { case TYPE_SEND: fe_dcc_update_send (dcc); break; case TYPE_RECV: fe_dcc_update_recv (dcc); break; default: update_dcc_window (type); }}voiddcc_notify_kill (struct server *serv){ struct server *replaceserv = 0; struct DCC *dcc; GSList *list = dcc_list; if (serv_list) replaceserv = (struct server *) serv_list->data; while (list) { dcc = (struct DCC *) list->data; if (dcc->serv == serv) dcc->serv = replaceserv; list = list->next; }}struct DCC *dcc_write_chat (char *nick, char *text){#ifdef USE_TRANS unsigned char *tbuf;#define TRANS_STAT_BUF_LEN 1024 static unsigned char sbuf[TRANS_STAT_BUF_LEN];#endif struct DCC *dcc; int len; dcc = find_dcc (nick, "", TYPE_CHATRECV); if (!dcc) dcc = find_dcc (nick, "", TYPE_CHATSEND); if (dcc && dcc->dccstat == STAT_ACTIVE) { len = strlen (text); dcc->size += len;#ifdef USE_TRANS if (prefs.use_trans) { if (len >= TRANS_STAT_BUF_LEN) tbuf = malloc (len + 1); else tbuf = sbuf; if (!tbuf) { return 0; } strcpy (tbuf, text); user2serv (tbuf); send (dcc->sok, tbuf, len, 0); if (tbuf != sbuf) { free (tbuf); } } else {#endif send (dcc->sok, text, len, 0);#ifdef USE_TRANS }#endif send (dcc->sok, "\n", 1, 0); fe_dcc_update_chat_win (); return dcc; } return 0;}/* returns: 0 - ok 1 - the dcc is closed! */static intdcc_chat_line (struct DCC *dcc, char *line, char *tbuf){#ifdef USE_PERL int skip; char *host_n_nick_n_message; GSList *list = dcc_list; host_n_nick_n_message = malloc (strlen (dcc->nick) + strlen (line) + 26); sprintf (host_n_nick_n_message, "%s %d %s: %s", net_ip (dcc->addr), dcc->port, dcc->nick, line); skip = perl_dcc_chat (find_session_from_channel (dcc->nick, dcc->serv), dcc->serv, host_n_nick_n_message); free (host_n_nick_n_message); /* Perl code may have closed the chat. */ while (list) { if (((struct DCC *)list->data) == dcc) break; list = list->next; } if (list == 0) return 1; if (skip) return 0;#endif fe_checkurl (line); if (line[0] == 1 && !strncasecmp (line + 1, "ACTION", 6)) { session *sess; char *po = strchr (line + 8, '\001'); if (po) po[0] = 0; sess = find_session_from_channel (dcc->nick, dcc->serv); if (!sess) sess = dcc->serv->front_session; channel_action (sess, tbuf, dcc->serv->nick, dcc->nick, line + 8, FALSE); } else {#ifdef USE_TRANS if (prefs.use_trans) serv2user (line);#endif private_msg (dcc->serv, tbuf, dcc->nick, "", line); } return 0;}static gbooleandcc_read_chat (GIOChannel *source, GIOCondition condition, struct DCC *dcc){ int i, len, dead; char tbuf[1226]; char lbuf[1026]; unsigned char *temp; while (1) { len = recv (dcc->sok, lbuf, sizeof (lbuf) - 2, 0); if (len < 1) { if (len < 0) { if (would_block_again ()) return TRUE; } sprintf (tbuf, "%d", dcc->port); EMIT_SIGNAL (XP_TE_DCCCHATF, dcc->serv->front_session, dcc->nick, net_ip (dcc->addr), tbuf, NULL, 0); dcc_close (dcc, STAT_FAILED, FALSE); return TRUE; } i = 0; lbuf[len] = 0; while (i < len) { switch (lbuf[i]) { case '\r': break; case '\n': dcc->dccchat->linebuf[dcc->dccchat->pos] = 0; if (prefs.stripcolor) { temp = strip_color (dcc->dccchat->linebuf); dead = dcc_chat_line (dcc, temp, tbuf); free (temp); } else { dead = dcc_chat_line (dcc, dcc->dccchat->linebuf, tbuf); } if (dead) /* the dcc has been closed, don't use (DCC *)! */ return TRUE; dcc->pos += dcc->dccchat->pos; dcc->dccchat->pos = 0; fe_dcc_update_chat_win (); break; default: dcc->dccchat->linebuf[dcc->dccchat->pos] = lbuf[i]; if (dcc->dccchat->pos < 1022) dcc->dccchat->pos++; } i++; } }}static gbooleandcc_read (GIOChannel *source, GIOCondition condition, struct DCC *dcc){ char buf[4096]; guint32 pos; int n; if (dcc->fp == -1) { if (dcc->resumable) { dcc->fp = open (dcc->destfile, O_WRONLY | O_APPEND | OFLAGS); dcc->pos = dcc->resumable; dcc->ack = dcc->resumable; } else { if (access (dcc->destfile, F_OK) == 0) { n = 0; do { n++; sprintf (buf, "%s.%d", dcc->destfile, n); } while (access (buf, F_OK) == 0); EMIT_SIGNAL (XP_TE_DCCRENAME, dcc->serv->front_session, dcc->destfile, buf, NULL, NULL, 0); free (dcc->destfile); dcc->destfile = strdup (buf); } dcc->fp = open (dcc->destfile, OFLAGS | O_TRUNC | O_WRONLY | O_CREAT, prefs.dccpermissions); } } if (dcc->fp == -1) { EMIT_SIGNAL (XP_TE_DCCFILEERR, dcc->serv->front_session, dcc->destfile, NULL, NULL, NULL, 0); dcc_close (dcc, STAT_FAILED, FALSE); return TRUE; } while (1) { n = recv (dcc->sok, buf, sizeof (buf), 0); if (n < 1) { if (n < 0) { if (would_block_again ()) return TRUE; } EMIT_SIGNAL (XP_TE_DCCRECVERR, dcc->serv->front_session, dcc->file, dcc->destfile, dcc->nick, NULL, 0); dcc_close (dcc, STAT_FAILED, FALSE); return TRUE; } write (dcc->fp, buf, n); dcc->pos += n; pos = htonl (dcc->pos); send (dcc->sok, (char *) &pos, 4, 0); dcc->lasttime = time (0); dcc_calc_cps (dcc); if (dcc->pos >= dcc->size) { sprintf (buf, "%d", dcc->cps); dcc_close (dcc, STAT_DONE, FALSE); EMIT_SIGNAL (XP_TE_DCCRECVCOMP, dcc->serv->front_session, dcc->file, dcc->destfile, dcc->nick, buf, 0); return TRUE; } }}static gbooleandcc_connect_finished (GIOChannel *source, GIOCondition condition, struct DCC *dcc){ int er, sok = dcc->sok; char host[128]; struct sockaddr_in addr; fe_input_remove (dcc->iotag); dcc->iotag = 0; memset (&addr, 0, sizeof (addr)); addr.sin_port = htons (dcc->port); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (dcc->addr); /* check if it's already connected */ if (connect (sok, (struct sockaddr *) &addr, sizeof (addr)) != 0) { er = sock_error ();#ifndef WIN32 if (er != EISCONN)#else if (er != WSAEISCONN)#endif { EMIT_SIGNAL (XP_TE_DCCCONFAIL, dcc->serv->front_session, dcctypes[(int) dcc->type], dcc->nick, errorstring (er), NULL, 0); dcc->dccstat = STAT_FAILED; update_dcc_window (dcc->type); return TRUE; } } dcc->dccstat = STAT_ACTIVE; switch (dcc->type) { case TYPE_RECV: dcc->iotag = fe_input_add (dcc->sok, 1, 0, 1, dcc_read, dcc); break; case TYPE_CHATRECV: dcc->iotag = fe_input_add (dcc->sok, 1, 0, 1, dcc_read_chat, dcc); dcc->dccchat = malloc (sizeof (struct dcc_chat)); dcc->dccchat->pos = 0; break; } update_dcc_window (dcc->type); dcc->starttime = time (0); dcc->lasttime = dcc->starttime; snprintf (host, sizeof host, "%s:%d", net_ip (dcc->addr), dcc->port); EMIT_SIGNAL (XP_TE_DCCCON, dcc->serv->front_session, dcctypes[(int) dcc->type], dcc->nick, host, "to", 0); return TRUE;}static voiddcc_connect (struct DCC *dcc){ if (dcc->dccstat == STAT_CONNECTING) return; dcc->dccstat = STAT_CONNECTING; dcc->sok = dcc_connect_sok (dcc); if (dcc->sok == -1) { dcc->dccstat = STAT_FAILED; update_dcc_window (dcc->type); return; } dcc->iotag = fe_input_add (dcc->sok, 0, 1, 1, dcc_connect_finished, dcc); if (dcc->type == TYPE_RECV) fe_dcc_update_recv (dcc); else fe_dcc_update_chat_win ();}static gbooleandcc_send_data (GIOChannel *source, GIOCondition condition, struct DCC *dcc){ char *buf; int len, sent, sok = dcc->sok; if (prefs.dcc_blocksize < 1) /* this is too little! */ prefs.dcc_blocksize = 1024; if (prefs.dcc_blocksize > 102400) /* this is too much! */ prefs.dcc_blocksize = 102400; buf = malloc (prefs.dcc_blocksize); if (!buf) return TRUE; lseek (dcc->fp, dcc->pos, SEEK_SET); len = read (dcc->fp, buf, prefs.dcc_blocksize); if (len < 1) goto abortit; sent = send (sok, buf, len, 0); if (send < 0 && !(would_block ())) {abortit: EMIT_SIGNAL (XP_TE_DCCSENDFAIL, dcc->serv->front_session, file_part (dcc->file), dcc->nick, NULL, NULL, 0); dcc_close (dcc, STAT_FAILED, FALSE); free (buf); return TRUE; } if (sent > 0) { dcc->pos += sent; dcc->lasttime = time (0); dcc_calc_cps (dcc); } /* have we sent it all yet? */ if (dcc->pos >= dcc->size) { /* it's all sent now, so remove the WRITE/SEND handler */ if (dcc->wiotag) { fe_input_remove (dcc->wiotag); dcc->wiotag = 0; } } free (buf); return TRUE;}static gbooleandcc_read_ack (GIOChannel *source, GIOCondition condition, struct DCC *dcc){ int len; guint32 ack; char buf[16]; int sok = dcc->sok; len = recv (sok, (char *) &ack, 4, MSG_PEEK); if (len < 1) { if (len < 0) { if (would_block_again ()) return TRUE; } EMIT_SIGNAL (XP_TE_DCCSENDFAIL, dcc->serv->front_session, file_part (dcc->file), dcc->nick, NULL, NULL, 0); dcc_close (dcc, STAT_FAILED, FALSE); return TRUE; } if (len < 4) return TRUE; recv (sok, (char *) &ack, 4, 0); dcc->ack = ntohl (ack); /* fix for BitchX */ if (dcc->ack < dcc->resumable) dcc->ackoffset = TRUE; if (dcc->ackoffset) dcc->ack += dcc->resumable; if (!dcc->fastsend) { if (dcc->ack < dcc->pos)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -