1 /*
2     etree.* - handles expression trees..
3     Copyright (C) 1999,2001-2004  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 "etree.h"
20 #include "cache.h"
21 #include "grouplist.h"
22 #include "myregex.h"
23 #include "misc.h"
24 #include "nget.h"
25 #include "strreps.h"
26 #include "getter.h"
27 #include <stack>
28 
29 #define MAKE_BINARY_OP(name,op) template <class T,class T2=T>			\
30 struct Op_ ## name {			\
31 	bool operator()(const T v1,const T2 v2) const {return v1 op v2;}	\
32 };
33 MAKE_BINARY_OP(gt,>);
34 MAKE_BINARY_OP(ge,>=);
35 MAKE_BINARY_OP(lt,<);
36 MAKE_BINARY_OP(le,<=);
37 MAKE_BINARY_OP(eq,==);
38 MAKE_BINARY_OP(ne,!=);
39 
40 //use real operators here (rather than a predComparison with Op template) to take advantage of short-circuit evaluation.
41 template <class ClassType>
42 class predAnd : public pred<ClassType> {
43 	private:
44 		pred<ClassType> *p1, *p2;
45 	public:
predAnd(pred<ClassType> * n1,pred<ClassType> * n2)46 		predAnd(pred<ClassType> *n1, pred<ClassType> *n2):p1(n1), p2(n2) { }
~predAnd()47 		virtual ~predAnd() {delete p1; delete p2;}
operator ()(ClassType * f) const48 		virtual bool operator()(ClassType* f) const {
49 			return (*p1)(f) && (*p2)(f);
50 		}
51 };
52 template <class ClassType>
53 class predOr : public pred<ClassType> {
54 	private:
55 		pred<ClassType> *p1, *p2;
56 	public:
predOr(pred<ClassType> * n1,pred<ClassType> * n2)57 		predOr(pred<ClassType> *n1, pred<ClassType> *n2):p1(n1), p2(n2) { }
~predOr()58 		virtual ~predOr() {delete p1; delete p2;}
operator ()(ClassType * f) const59 		virtual bool operator()(ClassType* f) const {
60 			return (*p1)(f) || (*p2)(f);
61 		}
62 };
63 
64 template <template <class A, class B> class Op, template <class D, class E> class Getter, class T, class ClassType, class T2=T>
65 class Comparison : public pred<ClassType> {
66 	private:
67 		typedef Getter<T, ClassType> getterT;
68 		Op<typename getterT::T,T2> op;
69 		getterT getter;
70 		T2 v;
71 	public:
Comparison(typename getterT::init_t gette,const T2 v2)72 		Comparison(typename getterT::init_t gette, const T2 v2):getter(gette), v(v2){}
operator ()(ClassType * f) const73 		virtual bool operator()(ClassType* f) const {
74 			return op(getter(f), v);
75 		}
76 };
77 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison(RetType (ClassType::* member),RetType v)78 pred<ClassType> *new_comparison(RetType (ClassType::*member), RetType v){
79 	return new Comparison<Op, MemGetter, RetType, ClassType>(member, v);
80 }
81 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison(RetType (ClassType::* memberf)(void)const,RetType v)82 pred<ClassType> *new_comparison(RetType (ClassType::*memberf)(void) const, RetType v){
83 	return new Comparison<Op, MemfuncGetter, RetType, ClassType>(memberf, v);
84 }
85 template <class ClassType, class getterT, class T2>
comparison(const string & opstr,getterT get,T2 v)86 pred<ClassType> *comparison(const string &opstr, getterT get, T2 v) {
87 	if      (opstr.compare("==")==0) return new_comparison<Op_eq,ClassType>(get, v);
88 	else if (opstr.compare("!=")==0) return new_comparison<Op_ne,ClassType>(get, v);
89 	else if (opstr.compare("<")==0)  return new_comparison<Op_lt,ClassType>(get, v);
90 	else if (opstr.compare("<=")==0) return new_comparison<Op_le,ClassType>(get, v);
91 	else if (opstr.compare(">")==0)  return new_comparison<Op_gt,ClassType>(get, v);
92 	else if (opstr.compare(">=")==0) return new_comparison<Op_ge,ClassType>(get, v);
93 	throw UserExFatal(Ex_INIT, "invalid op %s for comparison", opstr.c_str());
94 }
95 
96 //template <template <class A, class B> class Op, template <class D, class E, class F> class Getter, class getterT, class T>
97 template <template <class A, class B> class Op, template <class D, class E> class Getter, class T, class ClassType>
98 class Comparison_re : public pred<ClassType> {
99 	private:
100 		typedef Getter<T, ClassType> getterT;
101 		Op<typename getterT::T,const c_regex_nosub&> op;
102 		getterT getter;
103 		c_regex_nosub re;
104 	public:
Comparison_re(typename getterT::init_t gette,const char * pattern,int flags)105 		Comparison_re(typename getterT::init_t gette, const char *pattern, int flags):getter(gette), re(pattern, flags){}
operator ()(ClassType * f) const106 		virtual bool operator()(ClassType* f) const {
107 			return op(getter(f), re);
108 		}
109 };
110 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison_re(RetType (ClassType::* member),const char * pattern,int flags)111 pred<ClassType> *new_comparison_re(RetType (ClassType::*member), const char *pattern, int flags){
112 	return new Comparison_re<Op, MemGetter, RetType, ClassType>(member, pattern, flags);
113 }
114 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison_re(RetType (ClassType::* memberf)(void)const,const char * pattern,int flags)115 pred<ClassType> *new_comparison_re(RetType (ClassType::*memberf)(void) const, const char *pattern, int flags){
116 	return new Comparison_re<Op, MemfuncGetter, RetType, ClassType>(memberf, pattern, flags);
117 }
118 template <class ClassType, class getterT>
comparison_re(const string & opstr,getterT get,const char * pattern,int flags)119 pred<ClassType> *comparison_re(const string &opstr, getterT get, const char *pattern, int flags) {
120 	//typedef const string &T;
121 	if (opstr.compare("==")==0 || opstr.compare("=~")==0)
122 		return new_comparison_re<Op_eq,ClassType>(get, pattern, flags);
123 		//return new Comparison_re<Op_eq,Getter,const getterT,T>(get, pattern, flags);
124 	else if (opstr.compare("!=")==0 || opstr.compare("!~")==0)
125 		return new_comparison_re<Op_ne,ClassType>(get, pattern, flags);
126 		//return new Comparison_re<Op_ne,Getter,const getterT,T>(get, pattern, flags);
127 	throw UserExFatal(Ex_INIT, "invalid op %s for comparison_re", opstr.c_str());
128 }
129 
invert_op(string o)130 string invert_op(string o) {
131 	if (o == "<") return ">";
132 	else if (o == "<=") return ">=";
133 	else if (o == ">")  return "<";
134 	else if (o == ">=") return "<=";
135 	else return o;
136 }
137 
138 
139 template <class ClassType, pred<ClassType> *(*comparison_maker)(const string &i, const string *x, const string *y, int re_flags)>
make_pred(const arglist_t & e_parts,int gflags)140 pred<ClassType> * make_pred(const arglist_t &e_parts, int gflags) {
141 	const string *x=NULL,*y=NULL;
142 	arglist_t::const_iterator i=e_parts.begin();
143 	int re_flags = REG_EXTENDED | ((gflags&GETFILES_CASESENSITIVE)?0:REG_ICASE);
144 	pred<ClassType> * p=NULL;
145 	stack<pred<ClassType> *> pstack;
146 	for (;i!=e_parts.end();++i){
147 		if (!x){
148 			PDEBUG(DEBUG_MIN,"x %s",(*i).c_str());
149 			x=&(*i);
150 			if (*x=="&&" || *x=="and" || *x=="||" || *x=="or"){
151 				if (pstack.size()<2)
152 					throw UserExFatal(Ex_INIT, "not enough arguments for %s", x->c_str());
153 				pred<ClassType> *py=pstack.top(); pstack.pop();
154 				pred<ClassType> *px=pstack.top(); pstack.pop();
155 				if (x->compare("&&")==0 || x->compare("and")==0)
156 					p = new predAnd<ClassType>(px, py);
157 				else
158 					p = new predOr<ClassType>(px, py);
159 				pstack.push(p);
160 				x=NULL;
161 			}
162 		}else if (!y){
163 			PDEBUG(DEBUG_MIN,"y %s",(*i).c_str());
164 			y=&(*i);
165 		} else {
166 			PDEBUG(DEBUG_MIN,"z %s",(*i).c_str());
167 			p = comparison_maker((*i), x, y, re_flags);
168 			if (!p)
169 				throw UserExFatal(Ex_INIT, "no match type %s", x->c_str());
170 			pstack.push(p);
171 			x=y=NULL;
172 		}
173 	}
174 	if (pstack.size()>1 || x || y){
175 		throw UserExFatal(Ex_INIT, "unfinished expression");
176 	}
177 	if (pstack.empty())
178 		throw UserExFatal(Ex_INIT, "empty expression");
179 	return pstack.top();
180 }
181 
operator ==(const t_references & a,const c_regex_nosub & r)182 inline bool operator == (const t_references &a, const c_regex_nosub &r) {
183 	for (t_references::const_iterator i = a.begin(); i!=a.end(); ++i)
184 		if (*i == r)
185 			return true;
186 	return false;
187 }
operator !=(const t_references & a,const c_regex_nosub & r)188 inline bool operator != (const t_references &a, const c_regex_nosub &r) {
189 	return (!(a==r));
190 }
nntp_file_comparison_maker(const string & i,const string * x,const string * y,int re_flags)191 nntp_file_pred *nntp_file_comparison_maker(const string &i, const string *x, const string *y, int re_flags) {
192 	const char *n = x->c_str();
193 	if (strcasecmp(n, "subject")==0)
194 		return comparison_re<const c_nntp_file,string c_nntp_file::*>(i, &c_nntp_file::subject, y->c_str(), re_flags);
195 	else if (strcasecmp(n, "author")==0)
196 		return comparison_re<const c_nntp_file,string c_nntp_file::*>(i, &c_nntp_file::author, y->c_str(), re_flags);
197 	else if (strcasecmp(n, "mid")==0 || strcasecmp(n, "messageid")==0)
198 		return comparison_re<const c_nntp_file>(i, &c_nntp_file::bamid, y->c_str(), re_flags);
199 	else if (strcasecmp(n, "bytes")==0)
200 		return comparison<const c_nntp_file>(i, &c_nntp_file::bytes, atoul(y->c_str()));
201 	else if (strcasecmp(n, "lines")==0)
202 		return comparison<const c_nntp_file>(i, &c_nntp_file::lines, atoul(y->c_str()));
203 	else if (strcasecmp(n, "req")==0)
204 		return comparison<const c_nntp_file,int c_nntp_file::*>(i, &c_nntp_file::req, atoi(y->c_str()));
205 	else if (strcasecmp(n, "have")==0)
206 		return comparison<const c_nntp_file>(i, &c_nntp_file::have, atoi(y->c_str()));
207 	else if (strcasecmp(n, "date")==0)
208 		return comparison<const c_nntp_file>(i, &c_nntp_file::badate, decode_textdate(y->c_str()));
209 	else if (strcasecmp(n, "age")==0)
210 		//rather than taking a relative age and converting each date into a relative age and then comparing, decode_textage returns an absolute date (time_t) which simplifies comparisons, but to get intuitive usage we have to invert <,> operators.
211 		return comparison<const c_nntp_file>(invert_op(i), &c_nntp_file::badate, decode_textage(y->c_str()));
212 	else if (strcasecmp(n, "update")==0)
213 		return comparison<const c_nntp_file>(i, &c_nntp_file::update, decode_textdate(y->c_str()));
214 	else if (strcasecmp(n, "updateage")==0)
215 		return comparison<const c_nntp_file>(invert_op(i), &c_nntp_file::update, decode_textage(y->c_str()));
216 	else if (strcasecmp(n, "references")==0)
217 		return comparison_re<const c_nntp_file,t_references c_nntp_file::*>(i, &c_nntp_file::references, y->c_str(), re_flags);
218 	else
219 		return NULL;
220 }
221 
make_nntpfile_pred(const arglist_t & e_parts,int gflags)222 nntp_file_pred * make_nntpfile_pred(const arglist_t &e_parts, int gflags) {
223 	return make_pred<const c_nntp_file, nntp_file_comparison_maker>(e_parts, gflags);
224 }
make_nntpfile_pred(const char * optarg,int gflags)225 nntp_file_pred * make_nntpfile_pred(const char *optarg, int gflags) {
226 	arglist_t e_parts;
227 	parseargs(e_parts, optarg);
228 	return make_nntpfile_pred(e_parts, gflags);
229 }
230 
231 //Somewhat hacky way to allow desc keyword.  Works quite nicely, but this method won't be usable if more than a single attribute of the class needs to be tested.
operator ==(const t_server_group_description_map & m,const c_regex_nosub & r)232 inline bool operator == (const t_server_group_description_map &m, const c_regex_nosub &r) {
233 	for (t_server_group_description_map::const_iterator i = m.begin(); i!=m.end(); ++i)
234 		if (i->second->description == r)
235 			return true;
236 	return false;
237 }
operator !=(const t_server_group_description_map & m,const c_regex_nosub & r)238 inline bool operator != (const t_server_group_description_map &m, const c_regex_nosub &r) {
239 	return (!(m==r));
240 }
grouplist_comparison_maker(const string & i,const string * x,const string * y,int re_flags)241 nntp_grouplist_pred *grouplist_comparison_maker(const string &i, const string *x, const string *y, int re_flags) {
242 	const char *n = x->c_str();
243 	if (strcasecmp(n, "group")==0)
244 		return comparison_re<const c_group_availability>(i, &c_group_availability::groupname, y->c_str(), re_flags);
245 	else if (strcasecmp(n, "desc")==0)
246 		return comparison_re<const c_group_availability>(i, &c_group_availability::servergroups, y->c_str(), re_flags);
247 	else
248 		return NULL;
249 }
250 
make_grouplist_pred(const arglist_t & e_parts,int gflags)251 nntp_grouplist_pred * make_grouplist_pred(const arglist_t &e_parts, int gflags) {
252 	return make_pred<const c_group_availability, grouplist_comparison_maker>(e_parts, gflags);
253 }
make_grouplist_pred(const char * optarg,int gflags)254 nntp_grouplist_pred * make_grouplist_pred(const char *optarg, int gflags) {
255 	arglist_t e_parts;
256 	parseargs(e_parts, optarg);
257 	return make_grouplist_pred(e_parts, gflags);
258 }
259 
260