% DCC stuff... Veree basic... % initialized in init.sl: %dcc_dir = sprintf("%s/dl/", getenv("HOME")); variable dccRblok = 2048; % usually 512 variable dccTblok = 2048; typedef struct { sock, type, nick, user, host, hip, port, file, lfile, fds, size, count, secs, progress } DCC_Type; private define dcc_kill(p) { variable r = 0,dcc = p.v; () = clr_action(dcc.sock); if (is_prefix(dcc.type,"CHAT")) flip_remove("=" + dcc.nick); irc_destroy_pm(dcc.progress); % 0 is a NOP if (dcc.fds != NULL) { () = clr_action(dcc.fds); if (dcc.type == "SEND"){ if (dcc.port >= 0) r = set_wrdep(dcc.sock,dcc.fds,0); }else{ r = set_wrdep(dcc.fds,dcc.sock,0); } if (r) irc_Inff("failed clr_wrdep: %s",dcc.type); else () = vf_close(dcc.fds); % should we delete incomplete GET lfile here? } !if (r) () = vf_close(dcc.sock); s_del(p); } define dcc_expire() { variable p0,p,now,ct = 0; now = unix_time(); for (p = dcc_pending.f; p != NULL; p = p.n) { if (now - p.v.secs > 120) { irc_Inff("[DCC] Autoclosing pending %s from %s", p.v.type, p.v.nick); s_del(p); ct++; !if (dcc_pending.ct) break; } if (p.n == dcc_pending.f) break; } for (p = dcc_list.f; p != NULL; p = p.n) { if (now - p.v.secs > 120) { irc_Inff("[DCC] Killing idle dcc with %s", p.v.nick); dcc_kill(p); ct++; !if (dcc_list.ct) break; } if (p.n == dcc_list.f) break; } } define chat_expire() { variable p0,p,now,ct = 0; now = unix_time(); for (p = chat_pending.f; p != NULL; p = p.n) { if (now - p.v.secs > 120) { irc_Inff("[CHAT] Autoclosing pending %s from %s", p.v.type, p.v.nick); s_del(p); ct++; !if (chat_pending.ct) break; } if (p.n == chat_pending.f) break; } for (p = chat_list.f; p != NULL; p = p.n) { if (andelse {now - p.v.secs > 120} {p.v.port == -1}) { irc_Inff("[CHAT] Killing unaccepted chat with %s", p.v.nick); dcc_kill(p); ct++; !if (chat_list.ct) break; } if (p.n == chat_list.f) break; } } % handles read(sock) on /dcc get define Hdcc_r(sock,fds,which,p) { variable dcc = p.v; variable chk, data, resid; % irc_Inff("Getting more %s from %s@%s...",dcc.file,dcc.nick,dcc.host); data = dcc.size - dcc.count; if (data > dccRblok) data = dccRblok; chk = copy_bytes(dcc.fds, sock, data); % generally gets the x bytes if(chk <= 0) { if (chk) irc_Inff("[DCC] GET from %s errored out.", dcc.nick); else irc_Inff("[DCC] GET from %s closed by peer.", dcc.nick); % The dcc.lfile should probably be closed & deleted here dcc_kill(p); return; } dcc.count += chk; irc_set_pm(dcc.progress, dcc.count); % irc_Inff("[DCC] %d out of %d recvd. (%d in last chunk).", % dcc.count, dcc.size, chk); dcc.secs = unix_time(); % update activity time variable ar = Char_Type[4]; array_put_u32(ar,0,dcc.count); if (vf_write(sock, ar) < 4) % needs TCP_NODELAY on socket, IMHO irc_Info("[DCC] failed to send ack"); resid = dcc.size - dcc.count; if (resid > 0) { if(resid < dccRblok) () = set_rmode(sock,-1,resid); % above reduction of threshhold is necessary else the wr-action % will never initiate return; } irc_Inff("[DCC] GET from %s complete... saved as %s", dcc.nick, dcc.lfile); dcc_kill(p); return; } % handles write(sock) on /dcc send define Hdcc_s1(fds,sock,which,p) { variable dcc = p.v; variable chk, data; data = dcc.size - dcc.count; if (data > dccTblok) data = dccTblok; chk = copy_bytes(sock, fds, data); if(chk <= 0) { if (chk == 0) irc_Inff("[DCC] EOF: %d out of %d", dcc.count, dcc.size); else irc_Inff("[DCC] file copy error for %s", dcc.file); irc_Inff("[DCC] SEND to %s closing.", dcc.nick); dcc_kill(p); } else { dcc.count += chk; irc_set_pm(dcc.progress, dcc.count); dcc.secs = unix_time(); % update activity time % irc_Inff("[DCC] %d out of %d sent. (%d in last chunk).", % dcc.count, dcc.size, chk); if(dcc.count >= dcc.size) { irc_Inff("[DCC] SEND %s to %s: DTx complete, waiting for term seq", dcc.file, dcc.nick); % no more action.1 (sending) () = set_action(sock, 1, NULL); %() = set_wrdep(sock, dcc.fds, 0); // will be done by kill } } } % handles read(sock) on /dcc send -- % ie the 4-byte integer ack's define Hdcc_s0(sock,which,p) { variable dcc = p.v; variable ar = Char_Type[4]; if (vf_read(sock,ar) < 4) { % some kinda error irc_Inff("[DCC] SEND to %s closed x(%d/%d).", dcc.nick,dcc.count,dcc.size); irc_Info("[DCC] SEND Because error reading data connection"); } else { variable chk = array_get_u32(ar,0); dcc.secs = unix_time(); % irc_Inff("[DCC] %d of %d recieved by peer.", chk, dcc.size); if(chk < dcc.size) return; irc_Inff("[DCC] SEND to %s terminated (%d/%d).", dcc.nick,dcc.count,dcc.size); } dcc_kill(p); } % Hdcc_l % This is a supposedly readable socket. Hmmm. % Actually, it's listening, so it needs to be told to % accept a new one, and close this down if it's the right host. % Note that we authenticate by hostnames, too... % Just as we ignore the DCC host info for security, % we scrutinize the host connecting. % Could just be an enterprising scanner. % handles the listening socket for a /dcc get define Hdcc_l(sock,which,p) { variable dcc = p.v; variable accept = tcp_accept(sock); if(accept == NULL) { irc_Info("[DCC] Fail: Can't accept incoming connection."); ctcp_send_reply(dcc.nick, "DCC", sprintf("REJECT GET %s", dcc.file)); dcc_kill(p); return; } variable remote, port, rem_ip; remote = (); port = (); (rem_ip,) = get_remote_ipp(accept); %irc_Inff("[DCC] Connection from %s:%d", remote, port); if (dcc.hip != rem_ip) { irc_Inff("[DCC] Fail: Wrong host %s for %s, dropping.",remote,dcc.nick); () = vf_close(accept); return; } () = clr_action(sock); () = vf_close(sock); % update struct dcc for new sending socket () = set_rmode(accept,-1,4); % set read-threshold = 4 bytes dcc.sock = accept; % was sock % dcc.nick % dcc.user % dcc.host here, is same as remote dcc.port = port; % this tells kill that it's an accepted SEND % dcc.count % dcc.size already set % dcc.file % dcc.fds dcc.progress = irc_create_pm(dcc.file, dcc.size); dcc.secs = unix_time(); % update activity time % Announce to user... irc_Inff("[DCC] Connection from %s.", dcc.nick); () = set_action(accept, 0, "Hdcc_s0", p); variable r = set_wrdep(accept,dcc.fds,1); if (r) irc_Inff("[DCC] SENDA wrdep returned %d", r); else () = set_action(accept, 1, "Hdcc_s1", p); } define cmnd_dcc_get(nick,file) { variable p,dcc; p = s_find_n1(dcc_pending.f,&NC0_cmp,nick); if (p == NULL) { irc_Inff("[DCC] no file offered by %s",nick); return; } dcc = p.v; s_del(p); % delete from dcc_pending variable sock,h,i; (i,h) = host_of_nick(nick); if (i == NULL) { i = gethostbyname(dcc.host); }else{ if (h != dcc.host){ irc_Inff("[DCC] Fail: %s vs %s",h,dcc.host); return; } } if(i == -1) { irc_Inff("[DCC] Fail: Can't resolve: %s.",dcc.host); return; } dcc.hip = i; sock = tcp_connect(i, dcc.port); if(sock == NULL) { irc_Inff("[DCC] Fail: Can't connect to: %s:%d.",dcc.host,dcc.port); return; } i = dccRblok; if (i > dcc.size) i = dcc.size; () = set_rmode(sock,-1,i); % binary, threshold=min(dccRblok,size) % file-wr-triggers only after i bytes arrive i = set_nodelay(sock,1); if (i < 0) { irc_Info("[DCC] Can't set TCP_NODELAY on socket"); if (i == -1) { i = (); irc_Inff("[DCC] Error: %s",i); } } dcc.sock = sock; % next is an auto-rename of the target-file % fix me! need to keep original filename for REJECT variable fds,lfile = dcc.file; i = 0; for (i = 0; i < 20; i++) { fds = vf_open(dcc_dir + lfile, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fds != NULL) break; lfile = sprintf("%s_%d", dcc.file, i); } if (fds == NULL) { irc_Inff("[DCC] Fail: can't create target %s%s: %s", dcc_dir,dcc.file,vf_strerr(vf_errno)); () = vf_close(sock); return; } dcc.lfile = lfile; dcc.fds = fds; irc_Inff("[DCC] Accepting [file: %s] from %s",dcc.file,dcc.nick); dcc.progress = irc_create_pm(dcc.file, dcc.size); dcc.secs = unix_time(); p = s_putlast(dcc_list,dcc); % () = put4bytes(sock, dccTblok);[sjb] didn't see any need for this in dcc proto variable r; r = set_wrdep(dcc.fds, sock, 1); % create the dep-link from fds to sock if (r) irc_Inff("[DCC] GET wrdep returned %d", r); else () = set_action(dcc.fds, 1, "Hdcc_r", p); } define ctcp_DCC_SEND(from, text, isnotice) { irc_Inff("[DCC] SEND from <%s>", from); if(isnotice) { irc_Info("[DCC] Fail: was ctcp NOTICE"); return; } variable f_nick,f_user,f_host; (f_nick,f_user,f_host) = v_split(from,"!@"); !if (strlen(f_nick) and strlen(f_user) and strlen(f_host)) { irc_Info("[DCC] Fail: malformed ."); return; } variable file, hostint, port, size, t_file, i; (file, hostint, port, size,) = v_split(text," "); t_file = extract_file(file); !if (strlen(t_file) and is_digits(hostint) and is_digits(port) and is_digits(size) ) { irc_Inff("[DCC] Fail: invalid param among (file,host,port,size)=(%s,%s,%s,%s)", file,hostint,port,size); return; } port = integer(port); size = integer(size); irc_Inff("[DCC] - [file: %s] [%d bytes] (port:%d)", t_file, size, port); if(port < 1024) { irc_Info("[DCC] Port too low, ignoring."); return; } if(size <= 0) { irc_Info("[DCC] File Size 0, ignoring."); return; } variable dcc = @DCC_Type; %dcc.sock = NULL; dcc.type = "GET"; dcc.nick = f_nick; dcc.user = f_user; dcc.host = f_host; dcc.port = port; dcc.size = size; dcc.count = 0; dcc.file = t_file; dcc.secs = unix_time(); dcc.progress = 0; % This is the place to suspend until we accept... () = s_putlast(dcc_pending,dcc); % hmm, we might just put in a 'pending' list irc_beep(); } % handles read(sock) on /dcc chat -- % define Hchat(sock,which,p) { variable dcc = p.v; variable buff; if (vf_read(sock,&buff) > 0) { buff = chomp(buff); irc_say(buff, "=" + dcc.nick, irc_nickname); %irc_Inff("<=%s> %s", dcc.nick, buff); dcc.secs = unix_time(); return; } % some kinda error irc_Inff("[CHAT] Connection with %s closed.", dcc.nick); dcc_kill(p); } define chat_msg(targ,rest) { variable nick,p,r,l; nick = chop_1st(targ); p = s_find_n1(chat_list.f,&NC0_cmp,nick); if (andelse {p != NULL} {p.v.port > 0}) { irc_say(rest, irc_nickname, targ); rest = rest + "\r\n"; l = strlen(rest); r = vf_write(p.v.sock,rest); if (r == l) return; irc_Inff("[CHAT] Incomplete write to =%s (%d/%d)",nick,r,l); return; } irc_Inff("[CHAT] No established chat with %s",nick); } private define find_chat_pending(params) { variable nick,p; (nick,) = v_split(strtrim(params)," "); if(nick == NullString) { if (chat_pending.ct == 1) return chat_pending.f; p = NULL; if (chat_pending.ct > 1) { irc_Inff("[CHAT] Please specify nick"); return p; } }else{ p = s_find_n1(chat_pending.f,&NC0_cmp,nick); } if (p == NULL) irc_Inff("[CHAT] none offered by %s",nick); return p; } % /chat nick ... to accept chat offer from nick define command_CHAT(params) { variable p,dcc; p = find_chat_pending(params); if (p == NULL) return; dcc = p.v; s_del(p); % delete from chat_pending variable sock,h,i; (i,h) = host_of_nick(p.v.nick); if (i == NULL) { i = gethostbyname(dcc.host); }else{ if (h != dcc.host){ irc_Inff("[DCC] Fail: %s vs %s",h,dcc.host); return; } } if(i == -1) { irc_Inff("[CHAT] Fail: Can't resolve: %s.",dcc.host); return; } dcc.hip = i; sock = tcp_connect(i, dcc.port); if(sock == NULL) { irc_Inff("[CHAT] Fail: Can't connect to: %s:%d.",dcc.host,dcc.port); return; } () = set_rmode(sock,'\n',1024); % line-oriented, threshold=1024 i = set_nodelay(sock,1); if (i < 0) { irc_Info("[CHAT] Can't set TCP_NODELAY on socket"); if (i == -1) { i = (); irc_Inff("[CHAT] Error: %s",i); } } dcc.sock = sock; dcc.fds = NULL; irc_Inff("[CHAT] Accepting CHAT from %s",dcc.nick); dcc.progress = 0; dcc.secs = unix_time; p = s_putlast(chat_list,dcc); () = set_action(sock, 0, "Hchat", p); flip_put("=" + dcc.nick); } define command_NOCHAT(params) { variable p,dcc; params = strtrim(params); if (params == NullString) { if (irc_target[0] == '=') params = chop_1st(irc_target); else{ irc_info("[CHAT] Please specify target of /nochat",irc_target); return; } } p = s_find_n1(chat_list.f,&NC0_cmp,params); if (p != NULL) { irc_info(sprintf("[CHAT] Closing chat with %s",p.v.nick),irc_target); dcc_kill(p); return; } p = find_chat_pending(params); if (p != NULL) { dcc = p.v; s_del(p); % delete from chat_pending ctcp_send_reply(dcc.nick, "DCC", "REJECT CHAT chat"); return; } irc_info(sprintf("[CHAT] No chat activity with %s",params),irc_target); } % from ctcp dcc reject define ctcp_DCC_REJECT(from, text, isnotice) { variable type, rest, file, nick, p0, p; !if(isnotice) { irc_Inff("[DCC] REJECT as Request from %s not understood.", from); return; } nick = extract_nick(from); (type,rest) = v_split(text," "); type = strup(type); if (type == "CHAT") { irc_Inff("[CHAT] REJECT from %s: %s", nick, rest); p = s_find_n1(chat_list.f,&NC0_cmp,nick); if (p != NULL) dcc_kill(p); return; % unfinished? } file = extract_file(rest); dcc_list.f; dcc_pending.f; do { p0 = (); p = p0; do { p = s_find_n1(p,&NC0_cmp,nick); if (p == NULL) break; if(andelse {p.v.file == file} {p.v.type == type} ) { irc_Inff("[DCC] REJECT on DCC %s from %s of %s", type, nick, file); irc_Info("[DCC] Closing socket"); if (p.l == dcc_list) dcc_kill(p); else {s_del(p);pop();} return; } p = p.n; } while (p != p0); } while (p0 != dcc_list.f); irc_Inff("[DCC] Bogus reject from %s of %s?", nick, file); } define ctcp_DCC_CHAT(from, text, isnotice) { irc_Inff("[CHAT] offer from <%s>", from); if(isnotice) { irc_Info("[CHAT] Fail: was ctcp NOTICE"); return; } variable f_nick,f_user,f_host; (f_nick,f_user,f_host) = v_split(from,"!@"); !if (strlen(f_nick) and strlen(f_user) and strlen(f_host)) { irc_Info("[CHAT] Fail: malformed ."); return; } variable p = s_find_n1(chat_pending.f,&NC0_cmp,f_nick); if (p == NULL) p = s_find_n1(chat_list.f,&NC0_cmp,f_nick); if (p != NULL) { irc_Inff("[CHAT] Ignoring redundant offer from %s",f_nick); return; } variable junk, ip, port, i; (junk, ip, port, ) = v_split(text," "); !if (is_digits(ip) and is_digits(port)) { irc_Inff("[CHAT] Fail: invalid (ip,port)=(%s,%s)", ip,port); return; } ip = integer(ip); port = integer(port); irc_Inff("[CHAT] offer is from (%d:%d)", ip, port); if(port < 1024) { irc_Info("[CHAT] Port number too low, ignoring."); return; } variable dcc = @DCC_Type; %dcc.sock = NULL; dcc.type = "CHAT"; dcc.nick = f_nick; dcc.user = f_user; dcc.host = f_host; dcc.port = port; dcc.size = -1; dcc.count = 0; %dcc.file = NULL; dcc.secs = unix_time(); dcc.progress = 0; % Put in the chat_pending list () = s_putlast(chat_pending,dcc); irc_beep(); } define cmd_CTCP_DCC(from, text, isnotice) { variable function, command, text1; (command,text1) = v_split(text," "); function = "ctcp_DCC_" + command; if(is_defined(function)) { % irc_Inff("[DCC] %s", text1); from; text1; isnotice; eval(function); } else { irc_Inff("[DCC] Unknown DCC subcommand \"%s\" from %s", text, from); } } % handles the listening socket for a /dcc chat define Hchat_l(sock,which,p) { variable dcc = p.v; variable accept = tcp_accept(sock); if(accept == NULL) { irc_Info("[CHAT] Fail: Can't accept incoming connection."); dcc_kill(p); return; } variable remote, port, ip1; remote = (); port = (); (ip1,) = get_remote_ipp(accept); %irc_Inff("[CHAT] Connection from %s:%d", remote, port); if (dcc.hip != ip1) { irc_Inff("[CHAT] Fail: Wrong host %s for %s, dropping.",remote,dcc.nick); () = vf_close(accept); return; } () = clr_action(sock); () = vf_close(sock); % update struct dcc for new socket () = set_rmode(accept,'\n',1024); % line-oriented dcc.sock = accept; % was sock % dcc.nick % dcc.user % dcc.host here, is same as remote dcc.port = port; % this ? % dcc.count % dcc.size already set % dcc.file % dcc.fds dcc.secs = unix_time(); % update activity time % Announce to user... irc_Inff("[CHAT] Connection from %s.", dcc.nick); () = set_action(accept, 0, "Hchat", p); flip_put("=" + dcc.nick); irc_beep(); } % /dcc chat nick to initiate (offer) a chat define cmnd_dcc_chat(nick,rest) { variable hip,host; (hip,host) = host_of_nick(nick); if (host == NullString) { command_WHO(nick); irc_Inff("[CHAT] Fail: Please retry after /WHO response for %s:", nick); return; } variable p = s_find_n1(chat_list.f,&NC0_cmp,nick); if (p != NULL) { irc_Inff("[CHAT] Already have established chat with %s",nick); return; } if (hip == -1) { irc_Inff("[CHAT] Fail: Can't resolve %s", host); return; } variable lsock, lport, dcc; irc_Inff("[CHAT] Offering CHAT to %s", nick); lsock = tcp_listen(); if(lsock == NULL) { irc_Info("[CHAT] Failed to open listening socket."); return; } lport = (); dcc = @DCC_Type; dcc.sock = lsock; dcc.type = "CHATL"; dcc.nick = nick; %dcc.file = NULL; dcc.size = -1; dcc.count = 0; dcc.host = host; dcc.hip = hip; dcc.port = -1; % for benefit of dcc_kill() and chat_msg() dcc.secs = unix_time(); dcc.progress = 0; ;dcc.fds = NULL; p = s_putlast(chat_list,dcc); () = set_action(lsock, 0, "Hchat_l", p); % Mmmmmmmmmm... DCC... variable ip; (ip,) = get_local_ipp(irc_server); irc_send_string(sprintf("PRIVMSG %s :DCC CHAT chat %u %u\r\n", nick, ip, lport) ); } define cmnd_dcc_send(nick,file) { variable hip, host; (hip,host) = host_of_nick(nick); if (host == NullString) { command_WHO(nick); irc_Inff("[DCC] Fail: Please retry after /WHO response for %s:", nick); return; } if (hip == -1) { irc_Inff("[DCC] Fail: Can't resolve %s", host); return; } variable st, size, lsock, lport, dcc, p; irc_Inff("[DCC] Sending %s to %s", file, nick); st = stat_file(file); if (st == NULL) { irc_Inff("[DCC] %s - Stat failed", file); return; } % might wanna check whether it's readable too. size = st.st_size; !if(size) { irc_Inff("[DCC] %s - zero length file?", file); return; } lsock = tcp_listen(); if(lsock == NULL) { irc_Info("[DCC] Failed to open listening socket."); return; } lport = (); dcc = @DCC_Type; dcc.sock = lsock; dcc.type = "SEND"; dcc.nick = nick; dcc.file = file; dcc.size = size; dcc.count = 0; dcc.host = host; dcc.hip = hip; dcc.port = -1; dcc.secs = unix_time(); dcc.progress = 0; dcc.fds = vf_open(dcc.file, O_RDONLY, 0600); if (dcc.fds == NULL) { irc_Inff("Can't read-open %s",dcc.file); () = vf_close(lsock); return; } () = set_rmode(dcc.fds,-1,dccTblok); p = s_putlast(dcc_list,dcc); () = set_action(lsock, 0, "Hdcc_l", p); % Mmmmmmmmmm... DCC... variable ip; (ip,) = get_local_ipp(irc_server); irc_send_string(sprintf("PRIVMSG %s :DCC SEND %s %u %u %u\r\n", nick, file, ip, lport, size) ); } define cmnd_dcc_close(type,rest) { variable nick,p; type = strup(type); if (type != "CHAT") return; % unfinished (nick,rest) = v_split(rest," "); p = s_find_n1(chat_list.f,&NC0_cmp,nick); if (p != NULL) { irc_Inff("[CHAT] Closing chat with %s",p.v.nick); dcc_kill(p); }else{ irc_Inff("[CHAT] No established chat with %s",nick); } } add_cmnd("dcc"); define command_DCC(text) { variable function, command, nick, text2; (command,nick,text2) = v_split(text," "); irc_nick_action(nick, sprintf("DCC %s",command)); function = "cmnd_dcc_" + command; if(is_defined(function)) { % irc_Inff("[DCC] %s %s", nick, text2); nick; text2; eval(function); } else { irc_Inff("[DCC] Unknown /DCC subcommand %s", command); } }