/* ARISA - Interface and DCC Send/Recv/Chat 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 "user.h" /* internal function, doesn't take global or object locks */ static void set_default_interface(interface_t *in) { int i; for(i = 0; i < global->no_interfaces; ++i) { if(global->interfaces[i] != in) { LOCK(global->interfaces[i]); if(!global->interfaces[i]->deleted && global->interfaces[i]->type == in->type && global->interfaces[i]->def == 1) global->interfaces[i]->def = 1; UNLOCK(global->interfaces[i]); } } in->def = 1; } int interface_start(interface_t *in) { if(!thread_is_running(&(in->thread))) { thread_create(&(in->thread),interface_thread,in); return 0; } else return -1; } int interface_add(interface_t *in) { int i,no_same,ret = -1; LOCK(global); LOCK(in); for(i = 0,no_same = 0; i < global->no_interfaces; ++i) { LOCK(global->interfaces[i]); if(!global->interfaces[i]->deleted && strcmp(global->interfaces[i]->name,in->name) == 0) { LOGP(L_ERR,"Error trying to add duplicate interface: %s", in->name); UNLOCK(global->interfaces[i]); break; } if(!global->interfaces[i]->deleted && global->interfaces[i]->type == in->type) no_same++; UNLOCK(global->interfaces[i]); } if(i == global->no_interfaces) { PTRARR_ADD(&global->interfaces,&global->no_interfaces,in); validity_insert(in); LOGP(L_INF,"Added Interface %s",in->name); interface_start(in); if(no_same == 0) in->def = 1; // set this as default else if(in->def == 1) set_default_interface(in); ret = 0; } UNLOCK(global); if(ret == 0 && in->type == INTERFACE_RECV) access_verify_entries(in->recv.access); UNLOCK(in); if(ret == 0) se_notify_interface_add(in); return ret; } interface_t *interface_find(const char *name) { interface_t *ret = NULL; int i; if(name == NULL) return NULL; LOCK(global); for(i = 0; i < global->no_interfaces && ret == NULL; ++i) { LOCK(global->interfaces[i]); if(!global->interfaces[i]->deleted && strcmp(global->interfaces[i]->name,name) == 0) { ret = global->interfaces[i]; validity_insert(ret); } UNLOCK(global->interfaces[i]); } UNLOCK(global); return ret; } int interface_del(interface_t *in) { int i,j,okay = 1; interfacetype_t type; LOCK(in); type = in->type; UNLOCK(in); if(type == INTERFACE_SEND) { LOCK(in); for(i = 0; i < in->send.no_pools && okay; ++i) { LOCK(in->send.pools[i]); if(!in->send.pools[i]->deleted) { LOGP(L_ERR,"Error trying to remove SEND Interface with pools attached"); okay = 0; } UNLOCK(in->send.pools[i]); } UNLOCK(in); } else if(type == INTERFACE_CHAT) { LOCK(global); for(i = 0, j = 0; i < global->no_packlists; ++i) { LOCK(global->packlists[i]); if(!global->packlists[i]->deleted && global->packlists[i]->type == LIST_CHAT) j++; UNLOCK(global->packlists[i]); } if(j > 0) { for(i = 0, j = 0; i < global->no_interfaces; ++i) { LOCK(global->interfaces[i]); if(!global->interfaces[i]->deleted && global->interfaces[i]->type == INTERFACE_CHAT) j++; UNLOCK(global->interfaces[i]); } if(j < 2) { LOGP(L_ERR,"Error trying to remove only CHAT Interface while chat type packlists exist"); okay = 0; } else { for(i = 0, j = 0; i < global->no_packlists; ++i) { LOCK(global->packlists[i]); if(!global->packlists[i]->deleted && global->packlists[i]->type == LIST_CHAT && global->packlists[i]->chat_interface == in) global->packlists[i]->chat_interface = NULL; UNLOCK(global->packlists[i]); } } } UNLOCK(global); } if(!okay) return -1; LOCK(global); LOCK(in); if(in->def == 1) { for(i = 0,j = 0; i < global->no_interfaces && j == 0; ++i) { if(global->interfaces[i] != in) { LOCK(global->interfaces[i]); if(!global->interfaces[i]->deleted && global->interfaces[i]->type == in->type) global->interfaces[i]->def = j = 1; UNLOCK(global->interfaces[i]); } } in->def = 0; } if(in->deleted != 0) { // already deleted UNLOCK(in); UNLOCK(global); return 0; } in->deleted = xtime(); validity_delete(in); if(thread_is_running(&(in->thread))) thread_end(&(in->thread)); UNLOCK(in); UNLOCK(global); return 0; } void interface_purge(const time_t now) { interface_t **old = NULL; int no_old = 0; int i,j,deleted; //D("Interface Purge Begins"); LOCK(global); for(i = 0, deleted = 0; i < global->no_interfaces; ++i) { LOCK(global->interfaces[i]); if(IS_PURGE_TIME(global->interfaces[i]->deleted,now)) deleted++; UNLOCK(global->interfaces[i]); } if(deleted) { old = global->interfaces; no_old = global->no_interfaces; PTRARR_ALLOC(&(global->interfaces),&(global->no_interfaces), no_old - deleted); for(i = 0, j = 0; i < no_old; ++i) { LOCK(old[i]); if(!IS_PURGE_TIME(old[i]->deleted,now)) { global->interfaces[j++] = old[i]; UNLOCK(old[i]); old[i] = NULL; } else UNLOCK(old[i]); } } UNLOCK(global); if(deleted) { for(i = 0; i < no_old; ++i) { if(old[i] != NULL) { thread_join(&(old[i]->thread),NULL); free_interface(old[i]); } } } if(old != NULL) xfree(old); //D("Interface Purge Ends"); } void interface_nick_change(interface_t *in, network_t *net, const char *onick, const char *nnick) { int i; LOCK(in); if(in->type == INTERFACE_SEND) { for(i = 0; i < in->send.no_pools; ++i) pool_nick_change(in->send.pools[i],net,onick,nnick); } else if(in->type == INTERFACE_RECV) { for(i = 0; i < in->recv.no_uploads; ++i) { send_t *u = in->recv.uploads[i]; LOCK(u); if(u->network == net && irc_strcasecmp(u->nick,onick) == 0) { xfree(u->nick); u->nick = xstrdup(nnick); } UNLOCK(u); } } else if(in->type == INTERFACE_CHAT) { for(i = 0; i < in->chat.no_chats; ++i) { chat_t *c = in->chat.chats[i]; LOCK(c); if(c->network == net && irc_strcasecmp(c->nick,onick) == 0) { xfree(c->nick); c->nick = xstrdup(nnick); } UNLOCK(c); } } UNLOCK(in); } int interface_rename(interface_t *in, const char *name) { int i, ret = 0; if(in == NULL || name == NULL) return -1; LOCK(global); for(i = 0; i < global->no_interfaces && ret == 0; ++i) { LOCK(global->interfaces[i]); if(global->interfaces[i]->deleted == 0 && strcmp(global->interfaces[i]->name,name) == 0) ret = -1; UNLOCK(global->interfaces[i]); } if(ret == 0) { LOCK(in); xfree(in->name); in->name = xstrdup(name); UNLOCK(in); } UNLOCK(global); return ret; } int interface_set_default(interface_t *in) { interfacetype_t type; int i; LOCK(global); LOCK(in); type = in->type; if(in->deleted != 0) { UNLOCK(in); UNLOCK(global); return -1; } UNLOCK(in); for(i = 0; i < global->no_interfaces; ++i) { LOCK(global->interfaces[i]); if(global->interfaces[i]->type == type) { if(global->interfaces[i] != in) global->interfaces[i]->def = 0; else global->interfaces[i]->def = 1; } UNLOCK(global->interfaces[i]); } UNLOCK(global); return 0; } int interface_valid(interface_t *in) { int i; if(in == NULL) return 0; if(validity_test(in)) return 1; LOCK(global); for(i = 0; i < global->no_interfaces; ++i) { if(global->interfaces[i] == in) { LOCK(in); i = in->deleted; if(in->deleted == 0) validity_insert(in); UNLOCK(in); UNLOCK(global); if(i == 0) // in->deleted == 0 return 1; else return 0; } } UNLOCK(global); return 0; } int interface_chat_close(interface_t *in, const char *hostmask, network_t *net, const char *nick, chat_t *ptr, const char *reason) { int i,count; LOCK(in); if(in->type != INTERFACE_CHAT) { UNLOCK(in); return 0; } for(i = 0,count = 0; i < in->chat.no_chats; ++i) { chat_t *c = in->chat.chats[i]; LOCK(c); if(ptr != NULL) { if(c == ptr && c->state != STATE_COMPLETE && c->state != STATE_DELETED && c->state != STATE_ERROR) { c->state = STATE_DELETED; if(reason != NULL) c->reason = xstrdup(reason); count++; } } else if(irc_hostmask_match(c->host,hostmask) && c->state != STATE_COMPLETE && c->state != STATE_DELETED && c->state != STATE_ERROR) { if(nick == NULL && (net == NULL || c->network == net)) { c->state = STATE_DELETED; if(reason != NULL) c->reason = xstrdup(reason); count++; } else if(nick != NULL) { if(irc_strcasecmp(c->nick,nick) == 0 && (net == NULL || c->network == net)) { c->state = STATE_DELETED; if(reason != NULL) c->reason = xstrdup(reason); count++; } } } UNLOCK(c); } UNLOCK(in); return count; } int interface_upload_close(interface_t *in, const char *hostmask, network_t *net, const char *nick, const char *file, send_t *ptr, const char *reason) { int i,count; LOCK(in); if(in->type != INTERFACE_RECV) { UNLOCK(in); return 0; } for(i = 0, count = 0; i < in->recv.no_uploads; ++i) { send_t *u = in->recv.uploads[i]; LOCK(u); if((ptr == NULL && send_matchs(u,hostmask,net,nick,file,NULL)) || u == ptr) { u->state = STATE_DELETED; if(reason != NULL) { if(u->reason != NULL) xfree(u->reason); u->reason = xstrdup(reason); } count++; } UNLOCK(u); } UNLOCK(in); return count; } int interface_reset_counters(interface_t *in) { LOCK(in); in->record_rate = 0; in->stat_mib = 0; in->stat_carry = 0; UNLOCK(in); return 0; } /** Settings Data **/ static setting_t interface_generic_data[] = { {"mode", ST_ENUM, OFFSETOF(interface_t,mode), 0,NULL,NULL,interfacemode_to_str,str_to_interfacemode}, {"bandwidth", ST_UINT, OFFSETOF(interface_t,bandwidth), 0,"0",NULL,NULL,NULL}, {"snd-bufsize", ST_UINT, OFFSETOF(interface_t,snd_bufsize), 0,"0",NULL,NULL,NULL}, {"rcv-bufsize", ST_UINT, OFFSETOF(interface_t,rcv_bufsize), 0,"0",NULL,NULL,NULL}, {"advertise-ip",ST_NSTR, OFFSETOF(interface_t,advertise_ip), 0,NULL,NULL,NULL,NULL}, {"bind-ip", ST_NSTR, OFFSETOF(interface_t,bind_ip), 0,NULL,NULL,NULL,NULL}, {"accept-per-sec",ST_INT, OFFSETOF(interface_t,accept_per_sec), 0,"1","5",NULL,NULL}, {"secure-mode", ST_BOOL, OFFSETOF(interface_t,secure_mode), VALUE_BOOL_ONOFF,NULL,NULL,NULL,NULL}, {"dcc-port", ST_UINT, OFFSETOF(interface_t,dcc_port), 0,"0","65535",NULL,NULL}, {"no-ports", ST_UINT, OFFSETOF(interface_t,no_ports), 0,"0","100",NULL,NULL}, {"dccserver-port",ST_UINT, OFFSETOF(interface_t,dccserver_port), 0,"0","65535",NULL,NULL}, {"dcc-timeout", ST_UINT, OFFSETOF(interface_t,dcc_timeout), 0,"0",NULL,NULL,NULL}, {"dcc-packet", ST_UINT, OFFSETOF(interface_t,dcc_packet), 0,"512","65535",NULL,NULL}, {NULL} }; static setting_t interface_recv_data[] = { {"dir", ST_NSTR, OFFSETOF(interface_t,recv.dir), 0,NULL,NULL,NULL,NULL}, {"max-no-uploads",ST_INT, OFFSETOF(interface_t,recv.max_no_uploads), 0,"0",NULL,NULL,NULL}, {"max-size", ST_OFF, OFFSETOF(interface_t,recv.max_size), 0,"0",NULL,NULL,NULL}, {NULL} }; static setting_t interface_chat_data[] = { {"max-no-chats",ST_INT, OFFSETOF(interface_t,chat.max_no), 0,"0",NULL,NULL,NULL}, {NULL} }; int interface_apply_setting(interface_t *i, const char *name, const char *value) { value_t *v = value_string(name,value); int ret = -1; LOCK(i); if(i->type == INTERFACE_RECV) ret = apply_setting(interface_recv_data,i,v); else if(i->type == INTERFACE_CHAT) ret = apply_setting(interface_chat_data,i,v); if(ret == -1) ret = apply_setting(interface_generic_data,i,v); UNLOCK(i); xfree(v); return ret; } int interface_read_settings(interface_t *i, pqueue_t *out) { int ret; LOCK(i); ret = read_settings(interface_generic_data,i,out); if(ret != -1) { if(i->type == INTERFACE_RECV) ret = read_settings(interface_recv_data,i,out); else if(i->type == INTERFACE_CHAT) ret = read_settings(interface_chat_data,i,out); } UNLOCK(i); return ret; }