/* ARISA - XDCC functions * Copyright (C) 2003, 2004 Carl Ritson * * 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 "arisa.h" #include static int qsort_strcmp(const void *a, const void *b) { return irc_strcasecmp(*((char **)a),*((char **)b)); } void xdcc_info(network_t *net, channel_t *chan, const char *host, const char *hostmask, const char *nick) { char **names = NULL, **msgs; pqueue_t to_check; packlist_t *p; int i,j, no_names = 0; time_t now = xtime(); //D("net: %p, chan: %p, host: \"%s\", nick: \"%s\"", // net,chan,host,nick); if(nick == NULL) return; pqueue_init(&to_check,0); LOCK(global); for(i = 0; i < global->no_packlists; ++i) { LOCK(global->packlists[i]); if(global->packlists[i]->deleted == 0) pqueue_push_back(&to_check,global->packlists[i]); UNLOCK(global->packlists[i]); } UNLOCK(global); i = 0; while((p = pqueue_pop_front(&to_check)) != NULL) { LOCK(p); if(p->deleted != 0 || p->nolist > now) { UNLOCK(p); continue; } UNLOCK(p); if(packlist_check_access(p,net,host,hostmask,nick)) { LOCK(p); PTRARR_ADD(&names,&no_names,xstrdup(p->name)); UNLOCK(p); i++; } } if(i == 0) // don't bothered return; LOCK(net); msgs = xdcc_info_msgs(names,no_names,&j,net->settings->info_line); UNLOCK(net); strarr_free(&names,&no_names); for(i = 0; i < j; ++i) { irc_send_notice(net,nick,msgs[i]); xfree(msgs[i]); } if(msgs != NULL) xfree(msgs); } char **xdcc_info_msgs(char **names, int no_names, int *no_ret, const char *info) { char buffer[512],info_line[382],**ret = NULL; int pos = 0, info_size = 0; int i,j; *no_ret = 0; if(info != NULL) { colourise(COLOUR_IRC,info_line,sizeof(info_line),info); info_size = strlen(info_line); } if(no_names > 1) qsort(names,no_names,sizeof(char *),qsort_strcmp); for(j = 0; j < no_names; j = i) { pos = IMIN( snprintf(buffer,sizeof(buffer), "%s(%sXDCC%s)%s Trigger:%s(%s/msg $BOT xdcc list %s)%s Lists:%s(%s", IRC_BOLD,IRC_BOLD,IRC_BOLD,IRC_BOLD, IRC_BOLD,IRC_BOLD,IRC_BOLD,IRC_BOLD, IRC_BOLD,IRC_BOLD), sizeof(buffer)-1 ); for(i = j; i < no_names && (pos < sizeof(buffer) - 100); ++i) { pos += IMIN( snprintf(buffer+pos,sizeof(buffer)-pos, "%s%s", (i != j ? ", " : ""),names[i]), sizeof(buffer)-pos-1 ); } if(i == no_names) { if(info_size == 0 || (pos + info_size) > (sizeof(buffer) - 100)) { snprintf(buffer+pos,sizeof(buffer)-pos, "%s)%s =arisa=",IRC_BOLD,IRC_BOLD); } else { snprintf(buffer+pos,sizeof(buffer)-pos, "%s)%s Info:%s(%s%s%s)%s =arisa=", IRC_BOLD,IRC_BOLD,IRC_BOLD,IRC_BOLD, info_line, IRC_BOLD,IRC_BOLD ); } } PTRARR_ADD(&ret,no_ret,xstrdup(buffer)); } if(info_size != 0 && (pos + info_size) > (sizeof(buffer)-100)) { snprintf(buffer,sizeof(buffer), "%s(%sXDCC%s)%s Info:%s(%s%s%s)%s =arisa=", IRC_BOLD,IRC_BOLD,IRC_BOLD,IRC_BOLD, IRC_BOLD,IRC_BOLD, info_line, IRC_BOLD,IRC_BOLD ); PTRARR_ADD(&ret,no_ret,xstrdup(buffer)); } return ret; } void xdcc_remove(network_t *net, const char *nick, const char *label) { /* Here we use a 256 buffer, so if someone sends a maliciously long label it won't affect any IRC things below. */ char buffer[256]; int rem = 0; rem = queue_remove(NULL,NULL,net,nick,NULL,label,NULL); LOCK(net); if(rem == 1 && label != NULL) { LOGP(L_XDC,"XDCC REMOVE by [%s:%s] Success: for %s", net->name,nick,label); snprintf(buffer,sizeof(buffer), "Removed queued send for %s",label); } else if(rem > 0) { LOGP(L_XDC,"XDCC REMOVE by [%s:%s] Success: %d queued sends removed", net->name,nick,rem); snprintf(buffer,sizeof(buffer), "Removed %d queued sends",rem); } else if(label != NULL) { LOGP(L_XDC,"XDCC REMOVE by [%s:%s] Failed: for %s", net->name,nick,label); snprintf(buffer,sizeof(buffer), "You have no queued sends for %s",label); } else { LOGP(L_XDC,"XDCC REMOVE by [%s:%s] Failed: no queued sends", net->name,nick); snprintf(buffer,sizeof(buffer), "You have no queued sends to remove"); } UNLOCK(net); irc_send_notice(net,nick,buffer); } void xdcc_list(network_t *net, packlist_t *list, const char *host, const char *hostmask, const char *nick) { char buffer[512]; char list_name[128], nolistr[256], *p; time_t nolist, now = xtime(); responsetype_t response; listtype_t list_type; LOCK(list); xstrncpy(list_name,list->name,sizeof(list_name)); nolist = list->nolist; xstrncpy(nolistr,list->nolist_reason != NULL ? list->nolist_reason : "", sizeof(nolistr)); list_type = list->type; response = list->response; UNLOCK(list); if(!packlist_check_access(list,net,host,hostmask,nick)) { LOCK(net); LOGP(L_XDC,"XDCC LIST of %s to [%s:%s] Denied: Insufficient Access", list_name,net->name,nick); UNLOCK(net); xdcc_info(net,NULL,host,hostmask,nick); //snprintf(buffer,sizeof(buffer),"Invalid list: %s",list_name); //irc_send_notice(net,nick,buffer); return; } if(nolist >= now) { LOCK(net); LOGP(L_XDC,"XDCC LIST of %s to [%s:%s] Denied: Listing Disabled", list_name,net->name,nick); UNLOCK(net); snprintf(buffer,sizeof(buffer), "XDCC LIST is presently disabled for list: %s%s%s%s", list_name, nolistr[0] != '\0' ? " (" : "", nolistr, nolistr[0] != '\0' ? ")" : ""); irc_send_notice(net,nick,buffer); return; } LOCK(list); if(ignore_test(list->ignore_list,hostmask)) { UNLOCK(list); LOCK(net); LOGP(L_XDC,"XDCC LIST of %s to [%s:%s] Denied: By Ignore List", list_name,net->name,nick); UNLOCK(net); LOCK(list); LOCK(list->ignore_list); snprintf(buffer,sizeof(buffer), "You may not list %s right now; either you have exceeded the list frequency of %d list(s) per %d seconds, or the operator has explicitly Denied you from listing this packlist.", list_name, list->ignore_list->trigger, list->ignore_list->period); UNLOCK(list->ignore_list); UNLOCK(list); irc_send_notice(net,nick,buffer); return; } UNLOCK(list); if(list_type != LIST_CHAT) { pqueue_t listing; pqueue_init(&listing,0); packlist_xdl_generate(list,&listing,list_type,COLOUR_IRC, net,NULL,"",""); LOCK(net); LOGP(L_XDC,"XDCC LIST of %s sent to [%s:%s]", list_name,net->name,nick); UNLOCK(net); while((p = pqueue_pop_front(&listing)) != NULL) { if(response == RESPONSE_NOTICE) irc_send_notice(net,nick,p); else if(response == RESPONSE_PRIVMSG) irc_send_msg(net,nick,p); xfree(p); } } else if(list_type == LIST_CHAT) { if(chat_list_open(net,host,hostmask,nick,list) != 0) { LOCK(net); LOGP(L_XDC,"XDCC LIST of %s to [%s:%s] Failed: No Chat Slots available", list_name,net->name,nick); UNLOCK(net); irc_send_notice(net,nick,"Sorry, there are no available DCC chat slots available at this time."); } /* chat_list_open logs the initialization of a chat */ } } static int pick_pack(packlist_t *list, const char *pack, int *matches_ptr) { int i, matches, matched, len; if(pack[0] == '#') { for(i = 1; pack[i] != '\0'; ++i) { if(!(isdigit(pack[i]) || isspace(pack[i]))) break; } if(pack[i] == '\0') { matched = atoi(pack+1); return matched > 0 ? matched : 0; } } /* Cut any white space off the end of the identifier, and find the * length for partial matching. */ for(len = strlen(pack) - 1; len > 0; --len) { if(!(isspace(pack[len]))) break; } len += 1; for(i = 0, matches = 0, matched = 0; i < list->no_packs; ++i) { pack_t *p = list->packs[i]; RLOCK(p); if(strncasecmp(p->label,pack,len) == 0) { matched = i+1; matches++; } else if(p->hashed && (len == 8 || len == 32)) { if(strncasecmp(p->crc,pack,IMAX(8,len)) == 0 || strncasecmp(p->md5,pack,32) == 0) { matched = i+1; matches++; } } RUNLOCK(p); } if(matches > 1) { if(matches_ptr != NULL) *matches_ptr = matches; matched = atoi(pack); matched = matched > 0 ? matched : -1; } else if(matches == 0) { matched = atoi(pack); matched = matched > 0 ? matched : 0; } return matched; } void xdcc_send(network_t *net, packlist_t *list, const char *host, const char *hostmask, const char *nick, const char *pack) { char buffer[512]; char list_name[128], nosendr[256], *p; time_t nosend, now = xtime(); pack_t *spack; send_t *ssend; queue_t *queue; int pack_no,tmp,retries; if(pack == NULL) return; LOCK(list); xstrncpy(list_name,list->name,sizeof(list_name)); nosend = list->nosend; xstrncpy(nosendr,list->nosend_reason != NULL ? list->nosend_reason : "", sizeof(nosendr)); UNLOCK(list); if(!packlist_check_access(list,net,host,hostmask,nick)) { LOCK(net); LOGP(L_XDC,"XDCC SEND from %s to [%s:%s] Denied: Insufficient Access", list_name,net->name,nick); UNLOCK(net); snprintf(buffer,sizeof(buffer),"Invalid list: %s",list_name); irc_send_notice(net,nick,buffer); return; } if(nosend >= now) { LOCK(net); LOGP(L_XDC,"XDCC SEND from %s to [%s:%s] Denied: Sending Disabled", list_name,net->name,nick); UNLOCK(net); snprintf(buffer,sizeof(buffer), "XDCC SEND is presently disabled for list: %s%s%s%s", list_name, nosendr[0] != '\0' ? " (" : "", nosendr, nosendr[0] != '\0' ? ")" : ""); irc_send_notice(net,nick,buffer); return; } LOCK(list); if((pack_no = pick_pack(list,pack,&tmp)) == -1) { UNLOCK(list); LOCK(net); LOGP(L_XDC,"XDCC SEND from %s to [%s:%s] Denied: Ambiguous Pack \"%s\"", list_name,net->name,nick,pack); UNLOCK(net); snprintf(buffer,sizeof(buffer), "The identifier \"%s\" matches %d packs in the list %s. Please use a more specific identifier.", pack,tmp,list_name); irc_send_notice(net,nick,buffer); return; } if(pack_no < 1 || pack_no > list->no_packs) { UNLOCK(list); LOCK(net); LOGP(L_XDC,"XDCC SEND from %s to [%s:%s] Denied: Invalid Pack \"%s\"", list_name,net->name,nick,pack); UNLOCK(net); snprintf(buffer,sizeof(buffer), "\"%s\" is not a valid pack for list: %s.", pack,list_name); irc_send_notice(net,nick,buffer); return; } spack = pack_clone(list->packs[pack_no - 1]); queue = list->queue; retries = list->retries; UNLOCK(list); ssend = alloc_send(); ssend->pack = spack; ssend->network = net; ssend->host = xstrdup(host); ssend->nick = xstrdup(nick); ssend->queue = queue; ssend->retries = retries; pack_ref(spack); tmp = queue_enqueue(queue,ssend,0,&p); if(tmp == 0) { LOCK(net); LOGP(L_XDC,"XDCC SEND %s:%d to [%s:%s]", list_name,pack_no,net->name,nick); UNLOCK(net); /* No need to notify user since the send will be active * in a moment, or so we hope. */ } else if(tmp > 0) { LOCK(net); LOGP(L_XDC,"XDCC SEND %s:%d queued for [%s:%s], postion: %d", list_name,pack_no,net->name,nick,tmp); UNLOCK(net); RLOCK(spack); snprintf(buffer,sizeof(buffer), "You are queued in position %d for \"%s\", to remove this queue type \"/msg $BOT XDCC REMOVE %s\". To check the status of your queues on this list type \"/msg $BOT XDCC QUERY\". This send will be attempted %d time%s.", tmp, spack->label,spack->label, retries+1, (retries+1) > 1 ? "s" : ""); RUNLOCK(spack); irc_send_notice(net,nick,buffer); } else { LOCK(net); LOGP(L_XDC,"XDCC SEND %s:%d to [%s:%s] Denied: %s", list_name,pack_no, net->name,nick,p); UNLOCK(net); RLOCK(spack); snprintf(buffer,sizeof(buffer), "Send of \"%s\" Denied: %s", spack->label,p); RUNLOCK(spack); free_send(ssend); irc_send_notice(net,nick,buffer); } pack_deref(spack); } void xdcc_query(network_t *net, packlist_t *list, const char *host, const char *hostmask, const char *nick) { char buffer[512]; char list_name[128], *p; queue_t *q; pqueue_t queues; int i,j; pqueue_init(&queues,0); if(list != NULL) { LOCK(list); xstrncpy(list_name,list->name,sizeof(list_name)); UNLOCK(list); if(!packlist_check_access(list,net,host,hostmask,nick)) { LOCK(net); LOGP(L_XDC,"XDCC QUERY for %s by [%s:%s] Denied: Insufficient Access", list_name,net->name,nick); UNLOCK(net); snprintf(buffer,sizeof(buffer),"Invalid list: %s",list_name); irc_send_notice(net,nick,buffer); return; } LOCK(list); q = list->queue; UNLOCK(list); LOCK(q); for(i = 0; i < q->no_qsends; ++i) { LOCK(q->qsends[i]); if(q->qsends[i]->network == net && irc_strcasecmp(q->qsends[i]->nick,nick) == 0) { RLOCK(q->qsends[i]->pack); snprintf(buffer,sizeof(buffer), "You are queued for \"%s\", present position %d of %d, \"/msg $BOT XDCC REMOVE %s\" to remove this queue. This queue has %d attempt%s remaining.", q->qsends[i]->pack->label, i+1,q->no_qsends, q->qsends[i]->pack->label, q->qsends[i]->retries+1, (q->qsends[i]->retries+1) > 0 ? "s":"" ); RUNLOCK(q->qsends[i]->pack); pqueue_push_back(&queues,xstrdup(buffer)); } UNLOCK(q->qsends[i]); } UNLOCK(q); } else { LOCK(global); for(i = 0; i < global->no_queues; ++i) { q = global->queues[i]; LOCK(q); for(j = 0; j < q->no_qsends; ++j) { LOCK(q->qsends[j]); if(q->qsends[j]->network == net && irc_strcasecmp(q->qsends[j]->nick,nick) == 0) { RLOCK(q->qsends[j]->pack); snprintf(buffer,sizeof(buffer), "You are queued for \"%s\", present position %d of %d, \"/msg $BOT XDCC REMOVE %s\" to remove this queue. This queue has %d attempt%s remaining.", q->qsends[j]->pack->label, j+1,q->no_qsends, q->qsends[j]->pack->label, q->qsends[j]->retries+1, (q->qsends[j]->retries+1) > 0 ? "s":"" ); RUNLOCK(q->qsends[j]->pack); pqueue_push_back(&queues,xstrdup(buffer)); } UNLOCK(q->qsends[j]); } UNLOCK(q); } UNLOCK(global); } LOCK(net); LOGP(L_XDC,"XDCC QUERY by [%s:%s], %d matchs", net->name,nick,pqueue_length(&queues)); UNLOCK(net); if(pqueue_length(&queues) == 0) { irc_send_notice(net,nick,"No queues found"); } else { while((p = pqueue_pop_front(&queues)) != NULL) { irc_send_notice(net,nick,p); xfree(p); } } } void xdcc_admin(network_t *net, const char *host, const char *hostmask, const char *nick, const char *data) { char buffer[512]; int i,match; user_t *user; if(data == NULL) user = NULL; else user = user_find(data); if(user == NULL) { LOCK(net); LOGP(L_ERR,"XDCC ADMIN to [%s:%s] Denied: Invalid User", net->name,nick); UNLOCK(net); snprintf(buffer,sizeof(buffer), "You must specify a valid user to perform XDCC ADMIN"); irc_send_notice(net,nick,buffer); return; } LOCK(user); for(i = 0, match = 0; i < user->no_hosts && !match; ++i) { if(irc_hostmask_match(host,user->hosts[i])) match = 1; } UNLOCK(user); if(!match) { LOCK(net); LOG(L_ERR,"XDCC ADMIN to [%s:%s] Denied: Invalid Host: %s", net->name,nick,host); UNLOCK(net); snprintf(buffer,sizeof(buffer), "Your host (%s) is not permitted to perform XDCC ADMIN", host); irc_send_notice(net,nick,buffer); } else { admin_open(-1,net,host,nick,user); } } void xdcc_cmd(network_t *net, const char *host, const char *hostmask, const char *nick, const char *cmd) { char buffer[512], obuffer[256]; char **tokens, *lcmd = NULL, *list_name = NULL, *data = NULL; packlist_t *list = NULL; int no_tokens; xstrncpy(buffer,cmd,sizeof(buffer)); irc_strip_colours(buffer); no_tokens = 3; tokens = split_opt(buffer,buffer,&no_tokens); lcmd = no_tokens >= 2 ? tokens[1] : NULL; data = no_tokens >= 3 ? tokens[2] : NULL; if(tokens != NULL) xfree(tokens); if(no_tokens < 2) return; if(irc_strcasecmp(lcmd,"list") == 0 || irc_strcasecmp(lcmd,"query") == 0) { list_name = data; } else if(irc_strcasecmp(lcmd,"send") == 0 && data != NULL) { no_tokens = 2; tokens = split_opt(data,data,&no_tokens); list_name = no_tokens >= 1 ? tokens[0] : NULL; data = no_tokens >= 2 ? tokens[1] : NULL; if(tokens != NULL) xfree(tokens); } if(list_name != NULL) list = packlist_find(list_name); if(irc_strcasecmp(lcmd,"list") == 0) { if(list != NULL) xdcc_list(net,list,host,hostmask,nick); else xdcc_info(net,NULL,host,hostmask,nick); } else if(irc_strcasecmp(lcmd,"send") == 0) { if(list != NULL) xdcc_send(net,list,host,hostmask,nick, (data == NULL ? "0" : data)); else { LOCK(net); LOGP(L_XDC, "XDCC SEND to [%s:%s] Denied: Invalid List", net->name,nick); UNLOCK(net); if(list_name != NULL) snprintf(obuffer,sizeof(obuffer), "Invalid list: %s",list_name); else snprintf(obuffer,sizeof(obuffer), "Invalid list"); irc_send_notice(net,nick,obuffer); } } else if(irc_strcasecmp(lcmd,"remove") == 0) { xdcc_remove(net,nick,data); } else if(irc_strcasecmp(lcmd,"query") == 0) { xdcc_query(net,list,host,hostmask,nick); } else if(irc_strcasecmp(lcmd,"info") == 0) { xdcc_info(net,NULL,host,hostmask,nick); } else if(irc_strcasecmp(lcmd,"admin") == 0) { xdcc_admin(net,host,hostmask,nick,data); } else { LOCK(net); LOGP(L_XDC,"Invalid XDCC command %s from [%s:%s]", lcmd,net->name,nick); UNLOCK(net); snprintf(obuffer,sizeof(obuffer), "Invalid XDCC command \"%s\"",lcmd); irc_send_notice(net,nick,obuffer); } }