1 /**
2  * NamedList.cpp
3  * This file is part of the YATE Project http://YATE.null.ro
4  *
5  * Yet Another Telephony Engine - a fully featured software PBX and IVR
6  * Copyright (C) 2004-2014 Null Team
7  *
8  * This software is distributed under multiple licenses;
9  * see the COPYING file in the main directory for licensing
10  * information for this specific distribution.
11  *
12  * This use of this software may be subject to additional restrictions.
13  * See the LEGAL file in the main directory for details.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 
20 #include "yateclass.h"
21 
22 using namespace TelEngine;
23 
24 static const NamedList s_empty("");
25 
empty()26 const NamedList& NamedList::empty()
27 {
28     return s_empty;
29 }
30 
NamedList(const char * name)31 NamedList::NamedList(const char* name)
32     : String(name)
33 {
34 }
35 
NamedList(const NamedList & original)36 NamedList::NamedList(const NamedList& original)
37     : String(original)
38 {
39     ObjList* dest = &m_params;
40     for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
41 	const NamedString* p = static_cast<const NamedString*>(l->get());
42 	dest = dest->append(new NamedString(p->name(),*p));
43     }
44 }
45 
NamedList(const char * name,const NamedList & original,const String & prefix)46 NamedList::NamedList(const char* name, const NamedList& original, const String& prefix)
47     : String(name)
48 {
49     copySubParams(original,prefix);
50 }
51 
operator =(const NamedList & value)52 NamedList& NamedList::operator=(const NamedList& value)
53 {
54     String::operator=(value);
55     clearParams();
56     return copyParams(value);
57 }
58 
getObject(const String & name) const59 void* NamedList::getObject(const String& name) const
60 {
61     if (name == YATOM("NamedList"))
62 	return const_cast<NamedList*>(this);
63     return String::getObject(name);
64 }
65 
addParam(NamedString * param)66 NamedList& NamedList::addParam(NamedString* param)
67 {
68     XDebug(DebugInfo,"NamedList::addParam(%p) [\"%s\",\"%s\"]",
69         param,(param ? param->name().c_str() : ""),TelEngine::c_safe(param));
70     if (param)
71 	m_params.append(param);
72     return *this;
73 }
74 
addParam(const char * name,const char * value,bool emptyOK)75 NamedList& NamedList::addParam(const char* name, const char* value, bool emptyOK)
76 {
77     XDebug(DebugInfo,"NamedList::addParam(\"%s\",\"%s\",%s)",name,value,String::boolText(emptyOK));
78     if (emptyOK || !TelEngine::null(value))
79 	m_params.append(new NamedString(name, value));
80     return *this;
81 }
82 
setParam(const String & name,const char * value)83 NamedList& NamedList::setParam(const String& name, const char* value)
84 {
85     XDebug(DebugInfo,"NamedList::setParam(\"%s\",\"%s\")",name.c_str(),value);
86     ObjList *p = m_params.skipNull();
87     while (p) {
88         NamedString *s = static_cast<NamedString*>(p->get());
89         if (s->name() == name) {
90             *s = value;
91 	    return *this;
92 	}
93 	ObjList* next = p->skipNext();
94 	if (next)
95 	    p = next;
96 	else
97 	    break;
98     }
99     if (p)
100 	p->append(new NamedString(name,value));
101     else
102 	m_params.append(new NamedString(name,value));
103     return *this;
104 }
105 
clearParam(const String & name,char childSep)106 NamedList& NamedList::clearParam(const String& name, char childSep)
107 {
108     XDebug(DebugInfo,"NamedList::clearParam(\"%s\",'%.1s')",
109 	name.c_str(),&childSep);
110     String tmp;
111     if (childSep)
112 	tmp << name << childSep;
113     ObjList *p = &m_params;
114     while (p) {
115         NamedString *s = static_cast<NamedString *>(p->get());
116         if (s && ((s->name() == name) || s->name().startsWith(tmp)))
117             p->remove();
118 	else
119 	    p = p->next();
120     }
121     return *this;
122 }
123 
124 // Remove a specific parameter
clearParam(NamedString * param,bool delParam)125 NamedList& NamedList::clearParam(NamedString* param, bool delParam)
126 {
127     if (!param)
128 	return *this;
129     ObjList* o = m_params.find(param);
130     if (o)
131 	o->remove(delParam);
132     XDebug(DebugInfo,"NamedList::clearParam(%p) found=%p",param,o);
133     return *this;
134 }
135 
copyParam(const NamedList & original,const String & name,char childSep)136 NamedList& NamedList::copyParam(const NamedList& original, const String& name, char childSep)
137 {
138     XDebug(DebugInfo,"NamedList::copyParam(%p,\"%s\",'%.1s')",
139 	&original,name.c_str(),&childSep);
140     if (!childSep) {
141 	// faster and simpler - used in most cases
142 	const NamedString* s = original.getParam(name);
143 	return s ? setParam(name,*s) : clearParam(name);
144     }
145     clearParam(name,childSep);
146     String tmp;
147     tmp << name << childSep;
148     ObjList* dest = &m_params;
149     for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
150 	const NamedString* s = static_cast<const NamedString*>(l->get());
151         if ((s->name() == name) || s->name().startsWith(tmp))
152 	    dest = dest->append(new NamedString(s->name(),*s));
153     }
154     return *this;
155 }
156 
copyParams(const NamedList & original)157 NamedList& NamedList::copyParams(const NamedList& original)
158 {
159     XDebug(DebugInfo,"NamedList::copyParams(%p) [%p]",&original,this);
160     for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
161 	const NamedString* p = static_cast<const NamedString*>(l->get());
162 	setParam(p->name(),*p);
163     }
164     return *this;
165 }
166 
copyParams(const NamedList & original,ObjList * list,char childSep)167 NamedList& NamedList::copyParams(const NamedList& original, ObjList* list, char childSep)
168 {
169     XDebug(DebugInfo,"NamedList::copyParams(%p,%p,'%.1s') [%p]",
170 	&original,list,&childSep,this);
171     for (; list; list = list->next()) {
172 	GenObject* obj = list->get();
173 	if (!obj)
174 	    continue;
175 	String name = obj->toString();
176 	name.trimBlanks();
177 	if (name)
178 	    copyParam(original,name,childSep);
179     }
180     return *this;
181 }
182 
copyParams(const NamedList & original,const String & list,char childSep)183 NamedList& NamedList::copyParams(const NamedList& original, const String& list, char childSep)
184 {
185     XDebug(DebugInfo,"NamedList::copyParams(%p,\"%s\",'%.1s') [%p]",
186 	&original,list.c_str(),&childSep,this);
187     ObjList* l = list.split(',',false);
188     if (l) {
189 	copyParams(original,l,childSep);
190 	l->destruct();
191     }
192     return *this;
193 }
194 
copySubParams(const NamedList & original,const String & prefix,bool skipPrefix,bool replace)195 NamedList& NamedList::copySubParams(const NamedList& original, const String& prefix,
196     bool skipPrefix, bool replace)
197 {
198     XDebug(DebugInfo,"NamedList::copySubParams(%p,\"%s\",%s,%s) [%p]",
199 	&original,prefix.c_str(),String::boolText(skipPrefix),
200 	String::boolText(replace),this);
201     if (prefix) {
202 	unsigned int offs = skipPrefix ? prefix.length() : 0;
203 	ObjList* dest = &m_params;
204 	for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
205 	    const NamedString* s = static_cast<const NamedString*>(l->get());
206 	    if (s->name().startsWith(prefix)) {
207 		const char* name = s->name().c_str() + offs;
208 		if (!*name)
209 		    continue;
210 		if (!replace)
211 		    dest = dest->append(new NamedString(name,*s));
212 		else if (offs)
213 		    setParam(name,*s);
214 		else
215 		    setParam(s->name(),*s);
216 	    }
217 	}
218     }
219     return *this;
220 }
221 
hasSubParams(const char * prefix) const222 bool NamedList::hasSubParams(const char* prefix) const
223 {
224     XDebug(DebugInfo,"NamedList::hasSubParams(\"%s\") [%p]",prefix,this);
225     if (!TelEngine::null(prefix)) {
226 	for (const ObjList* l = m_params.skipNull(); l; l = l->skipNext()) {
227 	    const NamedString* s = static_cast<const NamedString*>(l->get());
228 	    if (s->name().startsWith(prefix))
229 		return true;
230 	}
231     }
232     return false;
233 }
234 
dump(String & str,const char * separator,char quote,bool force) const235 void NamedList::dump(String& str, const char* separator, char quote, bool force) const
236 {
237     if (force && str.null())
238 	str << separator;
239     str << quote << *this << quote;
240     const ObjList *p = m_params.skipNull();
241     for (; p; p = p->skipNext()) {
242         const NamedString* s = static_cast<const NamedString *>(p->get());
243 	String tmp;
244 	tmp << quote << s->name() << quote << "=" << quote << *s << quote;
245 	str.append(tmp,separator);
246     }
247 }
248 
getIndex(const NamedString * param) const249 int NamedList::getIndex(const NamedString* param) const
250 {
251     if (!param)
252 	return -1;
253     const ObjList *p = &m_params;
254     for (int i=0; p; p=p->next(),i++) {
255         if (static_cast<const NamedString *>(p->get()) == param)
256             return i;
257     }
258     return -1;
259 }
260 
getIndex(const String & name) const261 int NamedList::getIndex(const String& name) const
262 {
263     const ObjList *p = &m_params;
264     for (int i=0; p; p=p->next(),i++) {
265         NamedString *s = static_cast<NamedString *>(p->get());
266         if (s && (s->name() == name))
267             return i;
268     }
269     return -1;
270 }
271 
getParam(const String & name) const272 NamedString* NamedList::getParam(const String& name) const
273 {
274     XDebug(DebugInfo,"NamedList::getParam(\"%s\")",name.c_str());
275     const ObjList *p = m_params.skipNull();
276     for (; p; p=p->skipNext()) {
277         NamedString *s = static_cast<NamedString *>(p->get());
278         if (s->name() == name)
279             return s;
280     }
281     return 0;
282 }
283 
getParam(unsigned int index) const284 NamedString* NamedList::getParam(unsigned int index) const
285 {
286     XDebug(DebugInfo,"NamedList::getParam(%u)",index);
287     return static_cast<NamedString *>(m_params[index]);
288 }
289 
operator [](const String & name) const290 const String& NamedList::operator[](const String& name) const
291 {
292     const String* s = getParam(name);
293     return s ? *s : String::empty();
294 }
295 
getValue(const String & name,const char * defvalue) const296 const char* NamedList::getValue(const String& name, const char* defvalue) const
297 {
298     XDebug(DebugInfo,"NamedList::getValue(\"%s\",\"%s\")",name.c_str(),defvalue);
299     const NamedString *s = getParam(name);
300     return s ? s->c_str() : defvalue;
301 }
302 
getIntValue(const String & name,int defvalue,int minvalue,int maxvalue,bool clamp) const303 int NamedList::getIntValue(const String& name, int defvalue, int minvalue, int maxvalue,
304     bool clamp) const
305 {
306     const NamedString *s = getParam(name);
307     return s ? s->toInteger(defvalue,0,minvalue,maxvalue,clamp) : defvalue;
308 }
309 
getIntValue(const String & name,const TokenDict * tokens,int defvalue) const310 int NamedList::getIntValue(const String& name, const TokenDict* tokens, int defvalue) const
311 {
312     const NamedString *s = getParam(name);
313     return s ? s->toInteger(tokens,defvalue) : defvalue;
314 }
315 
getInt64Value(const String & name,int64_t defvalue,int64_t minvalue,int64_t maxvalue,bool clamp) const316 int64_t NamedList::getInt64Value(const String& name, int64_t defvalue, int64_t minvalue,
317     int64_t maxvalue, bool clamp) const
318 {
319     const NamedString *s = getParam(name);
320     return s ? s->toInt64(defvalue,0,minvalue,maxvalue,clamp) : defvalue;
321 }
322 
getUInt64Value(const String & name,uint64_t defvalue,uint64_t minvalue,uint64_t maxvalue,bool clamp) const323 uint64_t NamedList::getUInt64Value(const String& name, uint64_t defvalue, uint64_t minvalue,
324     uint64_t maxvalue, bool clamp) const
325 {
326     const NamedString *s = getParam(name);
327     return s ? s->toUInt64(defvalue,0,minvalue,maxvalue,clamp) : defvalue;
328 }
329 
getDoubleValue(const String & name,double defvalue) const330 double NamedList::getDoubleValue(const String& name, double defvalue) const
331 {
332     const NamedString *s = getParam(name);
333     return s ? s->toDouble(defvalue) : defvalue;
334 }
335 
getBoolValue(const String & name,bool defvalue) const336 bool NamedList::getBoolValue(const String& name, bool defvalue) const
337 {
338     const NamedString *s = getParam(name);
339     return s ? s->toBoolean(defvalue) : defvalue;
340 }
341 
replaceParams(String & str,bool sqlEsc,char extraEsc) const342 int NamedList::replaceParams(String& str, bool sqlEsc, char extraEsc) const
343 {
344     int p1 = 0;
345     int cnt = 0;
346     while ((p1 = str.find("${",p1)) >= 0) {
347 	int p2 = str.find('}',p1+2);
348 	if (p2 > 0) {
349 	    String def;
350 	    String tmp = str.substr(p1+2,p2-p1-2);
351 	    tmp.trimBlanks();
352 	    int pq = tmp.find('$');
353 	    if (pq >= 0) {
354 		// param is in ${<name>$<default>} format
355 		def = tmp.substr(pq+1).trimBlanks();
356 		tmp = tmp.substr(0,pq).trimBlanks();
357 	    }
358 	    DDebug(DebugAll,"NamedList replacing parameter '%s' [%p]",tmp.c_str(),this);
359 	    const String* ns = getParam(tmp);
360 	    if (ns) {
361 		if (sqlEsc) {
362 		    const DataBlock* data = 0;
363 		    if (ns->null()) {
364 			NamedPointer* np = YOBJECT(NamedPointer,ns);
365 			if (np)
366 			    data = YOBJECT(DataBlock,np->userData());
367 		    }
368 		    if (data)
369 			tmp = data->sqlEscape(extraEsc);
370 		    else
371 			tmp = ns->sqlEscape(extraEsc);
372 		}
373 		else
374 		    tmp = *ns;
375 	    }
376 	    else
377 		tmp = def;
378 	    str = str.substr(0,p1) + tmp + str.substr(p2+1);
379 	    // advance search offset past the string we just replaced
380 	    p1 += tmp.length();
381 	    cnt++;
382 	}
383 	else
384 	    return -1;
385     }
386     return cnt;
387 }
388 
389 /* vi: set ts=8 sw=4 sts=4 noet: */
390