1 /*
2     server.* - nget configuration handling
3     Copyright (C) 2000-2003  Matthew Mueller <donut AT dakotacom.net>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 #include "server.h"
20 #include "strreps.h"
21 #include "nget.h"
22 #include "status.h"
23 #include <sys/types.h>
24 #include <dirent.h>
25 #include <algorithm>
26 
penalize(c_server::ptr server) const27 bool c_nget_config::penalize(c_server::ptr server) const {
28 	if (penaltystrikes<=0)
29 		return false;//penalization disabled
30 	++server->penalty_count;
31 	if (server->penalty_count == penaltystrikes) {
32 		server->penalty_time = initialpenalty;
33 	}
34 	else if (server->penalty_count > penaltystrikes) {
35 		server->penalty_time = (time_t)(server->penalty_time * penaltymultiplier);
36 	}
37 	server->last_penalty = time(NULL);
38 	PDEBUG(DEBUG_MED, "penalized %s: count %i, last %li, time %li", server->alias.c_str(), server->penalty_count, server->last_penalty, server->penalty_time);
39 	return server->penalty_count >= penaltystrikes;
40 }
41 
getserver(const string & name) const42 c_server::ptr c_nget_config::getserver(const string &name) const {
43 	serv_match_by_name name_matcher;
44 	name_matcher.mname=name.c_str();
45 	t_server_list::const_iterator sli=find_if(serv.begin(),serv.end(),name_matcher);
46 	if (sli!=serv.end())
47 		return (*sli).second;
48 	return NULL;
49 }
50 
parse_int_pair(const char * s,int * l,int * h)51 int parse_int_pair(const char *s, int *l, int *h){
52 	const char *p;
53 	char *erp;
54 	int i;
55 	if (!s || *s=='\0')return -1;
56 	p=strchr(s,',');
57 	if (p){
58 		int i2;
59 		p++;
60 		if (*p=='\0')return -1;
61 		i=strtol(s,&erp,0);
62 		if (*erp!=',')
63 			return -1;
64 		i2=strtol(p,&erp,0);
65 		if (*erp!='\0')
66 			return -1;
67 		if (i<=i2){
68 			*l=i;*h=i2;
69 		}else{
70 			*l=i2;*h=i;
71 		}
72 	}else{
73 		i=strtol(s,&erp,0);
74 		if (*erp!='\0')
75 			return -1;
76 		if (i<0)i=-i;
77 		*l=-i;*h=i;
78 	}
79 	return 0;
80 }
81 
c_server(ulong id,const CfgSection * ds)82 c_server::c_server(ulong id, const CfgSection *ds) : serverid(id), alias(ds->key) {
83 	addr = ds->gets("addr");
84 	user = ds->gets("user");
85 	pass = ds->gets("pass");
86 	shortname = ds->gets("shortname");
87 	if (shortname.empty()) shortname = alias[0];
88 	ds->get("idletimeout",idletimeout,1,INT_MAX,nconfig.idletimeout);
89 	ds->get("fullxover",fullxover,0,2,nconfig.fullxover);
90 	ds->get("maxstreaming",maxstreaming,0,INT_MAX,nconfig.maxstreaming);
91 	if (ds->getitem("linelenience")){
92 		const char *ll = ds->geta("linelenience");
93 		int l,h;
94 		if (!parse_int_pair(ll,&l,&h)){
95 			lineleniencelow=l;lineleniencehigh=h;
96 		}else{
97 			PERROR("%s: invalid linelenience %s",ds->name().c_str(),ll);
98 			set_user_error_status();
99 			lineleniencelow=lineleniencehigh=0;
100 		}
101 	}else{
102 		lineleniencelow=lineleniencehigh=0;
103 	}
104 
105 	penalty_count=0;
106 	last_penalty=0;
107 	penalty_time=0;
108 }
109 
setlist(const CfgSection * cfg,const CfgSection * hinfo,const CfgSection * pinfo,const CfgSection * ginfo)110 void c_nget_config::setlist(const CfgSection *cfg,const CfgSection *hinfo,const CfgSection *pinfo,const CfgSection *ginfo){
111 	c_server::ptr server;
112 	CfgSection_map::const_iterator dli;
113 	CfgItem_map::const_iterator dii;
114 	const CfgSection *ds;
115 	const CfgItem *di;
116 	ulong tul;
117 	//cfg
118 	assert(cfg);
119 	cfg->get("curservmult",curservmult);
120 	cfg->get("usegz",usegz,-1,9);
121 	cfg->get("fullxover", fullxover, 0, 2);
122 	cfg->get("fatal_user_errors", fatal_user_errors, false, true);
123 	cfg->get("autopar_optimistic", autopar_optimistic, false, true);
124 	cfg->get("unequal_line_error",unequal_line_error,0,1);
125 	cfg->get("maxstreaming",maxstreaming,0,INT_MAX);
126 	cfg->get("maxconnections",maxconnections,-1,INT_MAX);
127 	cfg->get("idletimeout",idletimeout,1,INT_MAX);
128 	cfg->get("penaltystrikes",penaltystrikes,-1,INT_MAX);
129 	cfg->get("initialpenalty",initialpenalty,1,INT_MAX);
130 	cfg->get("penaltymultiplier",penaltymultiplier,1.0f,1e100f);
131 	//halias
132 	assert(hinfo);
133 	for (dli=hinfo->sections_begin();dli!=hinfo->sections_end();++dli){
134 		ds=(*dli).second;
135 		assert(ds);
136 		if (!ds->getitem("addr")){
137 			PERROR("host %s no addr",ds->key.c_str());
138 			set_user_error_status();
139 			continue;
140 		}
141 		if (!ds->getitem("id")){
142 			PERROR("host %s no id",ds->key.c_str());
143 			set_user_error_status();
144 			continue;
145 		}
146 		if (!ds->get("id",tul,1UL,ULONG_MAX))
147 			continue;
148 		server = new c_server(tul, ds);
149 		serv.insert(t_server_list::value_type(server->serverid,server));
150 	}
151 	//hpriority
152 	if (pinfo)
153 		for (dli=pinfo->sections_begin();dli!=pinfo->sections_end();++dli){
154 			ds=(*dli).second;
155 			assert(ds);
156 			c_server_priority_grouping *pgrouping=new c_server_priority_grouping(ds->key);
157 			for (dii=ds->items_begin();dii!=ds->items_end();++dii){
158 				di=(*dii).second;
159 				if (di->key=="_level"){
160 					di->get(pgrouping->deflevel);
161 				}else if (di->key=="_glevel"){
162 					di->get(pgrouping->defglevel);
163 				}else{
164 					server=getserver(di->key);
165 					if (!server){
166 						PERROR("prio section %s, server %s not found",ds->key.c_str(),di->key.c_str());
167 						set_user_error_status();
168 						continue;
169 					}
170 					c_server_priority *sprio=new c_server_priority(server,atof(di->gets().c_str()));
171 					pgrouping->priorities.insert(t_server_priority_grouping::value_type(sprio->server,sprio));
172 				}
173 			}
174 			if (pgrouping->alias=="trustsizes")
175 				trustsizes=pgrouping;
176 			else
177 				prioritygroupings.insert(t_server_priority_grouping_list::value_type(pgrouping->alias.c_str(),pgrouping));
178 		}
179 	if (getpriogrouping("default")==NULL){
180 		c_server_priority_grouping *pgrouping=new c_server_priority_grouping("default");
181 		prioritygroupings.insert(t_server_priority_grouping_list::value_type(pgrouping->alias.c_str(),pgrouping));
182 	}
183 	if (trustsizes==NULL){
184 		trustsizes=new c_server_priority_grouping("trustsizes");
185 	}
186 	//galias
187 	if (ginfo) {
188 		for (dii=ginfo->items_begin();dii!=ginfo->items_end();++dii){
189 			di=(*dii).second;
190 			addgroup_or_metagroup(di->key,di->gets());
191 		}
192 		for (dli=ginfo->sections_begin();dli!=ginfo->sections_end();++dli){
193 			ds=(*dli).second;
194 			assert(ds);
195 			int susegz;
196 			ds->get("usegz",susegz,-1,9,-2);
197 			addgroup(ds->key,ds->gets("group"),ds->gets("prio"),susegz);
198 		}
199 	}
200 }
201 
addgroup_or_metagroup(const string & alias,const string & name)202 void c_nget_config::addgroup_or_metagroup(const string &alias, const string &name){
203 	if (name.find(',')!=string::npos)
204 		addmetagroup(alias,name);
205 	else
206 		addgroup(alias,name,"");
207 }
208 
addmetagroup(const string & alias,const string & name)209 void c_nget_config::addmetagroup(const string &alias, const string &name){
210 	metagroups.insert(t_metagroup_list::value_type(alias,name));
211 }
212 
addgroup(const string & alias,const string & name,string prio,int usegz)213 c_group_info::ptr c_nget_config::addgroup(const string &alias, const string &name, string prio, int usegz){
214 	if (prio.empty())prio="default";
215 	c_server_priority_grouping *priog=getpriogrouping(prio);
216 	if (!priog){
217 		printf("group %s(%s), prio %s not found\n",name.c_str(),alias.c_str(),prio.c_str());
218 		set_user_error_status();
219 		return NULL;
220 	}
221 	assert(priog);
222 	c_group_info::ptr group(new c_group_info(alias,name,priog,usegz));
223 	if (group){
224 		if (!alias.empty())
225 			groups.insert(t_group_info_list::value_type(group->alias.c_str(),group));
226 		groups.insert(t_group_info_list::value_type(group->group.c_str(),group));
227 	}
228 	return group;
229 }
230 
dogetallcachedgroups(vector<c_group_info::ptr> & groups)231 void c_nget_config::dogetallcachedgroups(vector<c_group_info::ptr> &groups) {
232 	DIR *dir=opendir(ngcachehome.c_str());
233 	struct dirent *de;
234 	if (!dir)
235 		throw PathExFatal(Ex_INIT,"opendir: %s(%i)",strerror(errno),errno);
236 	while ((de=readdir(dir))) {
237 		char *endp;
238 		if ((endp=strstr(de->d_name,",cache"))){
239 			string groupname(de->d_name, endp - de->d_name);
240 			groups.push_back(getgroup(groupname.c_str()));
241 		}
242 	}
243 	closedir(dir);
244 }
245 
dogetgroups(vector<c_group_info::ptr> & groups,const char * names)246 void c_nget_config::dogetgroups(vector<c_group_info::ptr> &groups, const char *names) {
247 	char *foo = new char[strlen(names)+1];
248 	char *cur = foo, *name = NULL;
249 	strcpy(foo, names);
250 	while ((name = goodstrtok(&cur, ','))) {
251 		if (strcmp(name,"*")==0)
252 			dogetallcachedgroups(groups);
253 		else {
254 			t_metagroup_list::const_iterator mgi=metagroups.find(name);
255 			if (mgi!=metagroups.end())
256 				dogetgroups(groups, mgi->second.c_str());
257 			else
258 				groups.push_back(getgroup(name));
259 		}
260 	}
261 	delete [] foo;
262 }
263 
getgroups(vector<c_group_info::ptr> & groups,const char * names)264 void c_nget_config::getgroups(vector<c_group_info::ptr> &groups, const char *names) {
265 	groups.clear();
266 	vector<c_group_info::ptr> tmpgroups;
267 	dogetgroups(tmpgroups, names);
268 	for (vector<c_group_info::ptr>::const_iterator gi=tmpgroups.begin(); gi!=tmpgroups.end(); ++gi)
269 		if (find(groups.begin(), groups.end(), *gi)==groups.end())
270 			groups.push_back(*gi); // return only unique groups
271 }
272