1 /*******************************************************************************
2 
3     KHOMP generic endpoint/channel library.
4     Copyright (C) 2007-2010 Khomp Ind. & Com.
5 
6   The contents of this file are subject to the Mozilla Public License
7   Version 1.1 (the "License"); you may not use this file except in compliance
8   with the License. You may obtain a copy of the License at
9   http://www.mozilla.org/MPL/
10 
11   Software distributed under the License is distributed on an "AS IS" basis,
12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13   the specific language governing rights and limitations under the License.
14 
15   Alternatively, the contents of this file may be used under the terms of the
16   "GNU Lesser General Public License 2.1" license (the “LGPL" License), in which
17   case the provisions of "LGPL License" are applicable instead of those above.
18 
19   If you wish to allow use of your version of this file only under the terms of
20   the LGPL License and not to allow others to use your version of this file
21   under the MPL, indicate your decision by deleting the provisions above and
22   replace them with the notice and other provisions required by the LGPL
23   License. If you do not delete the provisions above, a recipient may use your
24   version of this file under either the MPL or the LGPL License.
25 
26   The LGPL header follows below:
27 
28     This library is free software; you can redistribute it and/or
29     modify it under the terms of the GNU Lesser General Public
30     License as published by the Free Software Foundation; either
31     version 2.1 of the License, or (at your option) any later version.
32 
33     This library is distributed in the hope that it will be useful,
34     but WITHOUT ANY WARRANTY; without even the implied warranty of
35     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36     Lesser General Public License for more details.
37 
38     You should have received a copy of the GNU Lesser General Public License
39     along with this library; if not, write to the Free Software Foundation,
40     Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
41 
42 *******************************************************************************/
43 
44 #include <regex.hpp>
45 #include "spec.h"
46 #include "logger.h"
47 #include "khomp_pvt.h"
48 #include "khomp_pvt_kxe1.h"
49 
50 /************************** Forward declaration *******************************/
51 static SpecRetType processSpecAtom(std::string &, SpecFlagsType &, SpecFunType &);
52 static SpecRetType processSpecAtoms(std::string &, SpecFlagsType &, SpecFunType &);
53 
54 static bool processCallChannelString(std::string &, Board::KhompPvt *&, int *, bool need_free = true);
55 
56 /******************************************************************************/
57 
processSpecAtom(std::string & atom,SpecFlagsType & flags,SpecFunType & fun)58 static SpecRetType processSpecAtom(std::string & atom, SpecFlagsType & flags, SpecFunType & fun)
59 {
60 	std::string allocstr = Strings::trim(atom);
61 
62 	DBG(FUNC, D("allocation string 'atom': %s") % allocstr.c_str());
63 
64     /* check if is a group */
65    	if (allocstr.size() >= 1 && (allocstr[0] == 'g' || allocstr[0] == 'G'))
66     {
67    		std::string group_name = allocstr.substr(1);
68 
69 		GroupToDestMapType::iterator it = Opt::_groups.find(group_name);
70 
71 		if (it == Opt::_groups.end())
72 		{
73 			LOG(ERROR, FMT("invalid dial string '%s': no valid group found!") % allocstr.c_str());
74         	return SPR_FAIL;
75 		}
76 
77 		allocstr = it->second;
78 		return processSpecAtoms(allocstr, flags, fun);
79     }
80 
81 	Regex::Match what(allocstr, Globals::regex_allocation);
82 
83 	if (!what.matched())
84 	{
85 		LOG(ERROR, FMT("invalid dial string '%s': this is not a valid expression.") % allocstr.c_str());
86 		return SPR_FAIL;
87 	}
88 
89 	bool reverse = true;
90 
91 	try
92 	{
93 
94 		unsigned long int board_id = UINT_MAX;
95 
96 		if (what.matched(3)) //matched [bB]
97 		{
98 			board_id = Strings::toulong(what.submatch(5));
99 
100 			DBG(FUNC, D("board matched: %d") % board_id);
101 
102 
103 			if (board_id >= Globals::k3lapi.device_count())
104 			{
105             	LOG(ERROR, FMT("invalid dial string '%s': no such board '%d'.") % allocstr.c_str() % board_id);
106 				return SPR_FAIL;
107 			}
108 
109 			switch ((what.submatch(4))[0])
110 			{
111 				case 'b': reverse = false; break;
112 				case 'B': reverse = true;  break;
113 			}
114 		}
115 
116 		else if (what.matched(6)) //matched [sS]
117 		{
118 			unsigned int serial_id = Strings::toulong(what.submatch(8));
119 
120 			DBG(FUNC, D("serial matched: %d") % serial_id);
121 
122 			for (unsigned int i = 0; i < Globals::k3lapi.device_count(); i++)
123 			{
124 
125 				K3L_DEVICE_CONFIG conf = Globals::k3lapi.device_config(i);
126 
127 				unsigned int tmp = Strings::toulong((const char *)conf.SerialNumber);
128 
129 				if (tmp == serial_id)
130 				{
131 					board_id = i;
132 					break;
133 				}
134 			}
135 
136 			if (board_id == UINT_MAX)
137 			{
138    	        	LOG(ERROR, FMT("invalid dial string '%s': there is no board with serial '%04d'.") % allocstr.c_str() % serial_id);
139 				return SPR_FAIL;
140 			}
141 
142 			switch ((what.submatch(7))[0])
143 			{
144 				case 's': reverse = false; break;
145 				case 'S': reverse = true;  break;
146 			}
147 		}
148 		else if (what.matched(14)) //matched [rR]
149 		{
150 			std::string base_addr = what.submatch(16);
151 
152 			unsigned int branch_id = Strings::toulong(base_addr);
153 
154 			if (what.matched(17))
155 			{
156 				unsigned int branch2_id = Strings::toulong(what.submatch(18));
157 
158 				DBG(FUNC, D("branch range matched (%d to %d)") % branch_id % branch2_id);
159 
160 				switch ((what.submatch(15))[0])
161 				{
162 					case 'r': reverse = false; break;
163 					case 'R': reverse = true;  break;
164 				}
165 
166 				if (!reverse)
167 				{
168 					for (unsigned int i = branch_id; i <= branch2_id; i++)
169 					{
170 						std::string call_addr = BoardE1::KhompPvtFXS::padOrig(base_addr, i - branch_id);
171 						BranchToObjectMapType::iterator i = Opt::_fxs_branch_map.find(call_addr);
172 
173 						if (i == Opt::_fxs_branch_map.end())
174 						{
175    	    			    	LOG(WARNING, FMT("invalid value '%s': there is no such branch number.") % call_addr);
176 							return SPR_FAIL;
177 						}
178 
179 						if (!fun(i->second.first, i->second.second, flags))
180 							return SPR_SUCCESS;
181 					}
182 				}
183 				else
184 				{
185 					for (unsigned int i = branch2_id; i >= branch_id; i--)
186 					{
187 						std::string call_addr = BoardE1::KhompPvtFXS::padOrig(base_addr, i - branch_id);
188 
189 						BranchToObjectMapType::iterator i = Opt::_fxs_branch_map.find(call_addr);
190 
191 						if (i == Opt::_fxs_branch_map.end())
192 						{
193    	    			    	LOG(WARNING, FMT("invalid value '%s': there is no such branch number.") % call_addr);
194 							return SPR_FAIL;
195 						}
196 
197 						if (!fun(i->second.first, i->second.second, flags))
198 							return SPR_SUCCESS;
199 					}
200 				}
201 			}
202 			else
203 			{
204 				DBG(FUNC, D("branch matched: %s") % base_addr);
205 
206 				BranchToObjectMapType::iterator i = Opt::_fxs_branch_map.find(base_addr);
207 
208 				if (i == Opt::_fxs_branch_map.end())
209 				{
210    	    	    	LOG(WARNING, FMT("invalid value '%s': there is no such branch number.") % base_addr);
211 					return SPR_FAIL;
212 				}
213 
214 				if (!fun(i->second.first, i->second.second, flags))
215 					return SPR_SUCCESS;
216 			}
217 		}
218 
219 		else
220 		{
221            	LOG(ERROR, FMT("invalid dial string '%s': unknown allocation method.") % allocstr.c_str());
222 			return SPR_FAIL;
223 		}
224 
225 		if (what.matched(9)) // matched something about links/channels [cClL]n|[cC]n-m
226 		{
227 			DBG(FUNC, D("channel/link matched"));
228 			unsigned long int object_id = Strings::toulong(what.submatch(11));
229 
230 			if (what.matched(12))
231 			{
232 
233 				DBG(FUNC, D("channel range matched")); // matched [cC]n-m
234 
235 				if ((what.submatch(10))[0] != 'c' && (what.submatch(10))[0] != 'C')
236 				{
237 					LOG(ERROR, FMT("invalid dial string '%s': range just allowed for channels.") % allocstr.c_str());
238 					return SPR_FAIL;
239 				}
240 
241 				unsigned long int object2_id = Strings::toulong(what.submatch(13));
242 
243 				DBG(FUNC, D("(d=%d,lo=%d,up=%d,r=%s) c") % board_id % object_id % object2_id % (reverse ? "true" : "false"));
244 
245 				if (reverse)
246 				{
247 					for (unsigned int obj = std::min<unsigned int>(object2_id + 1, Globals::k3lapi.channel_count(board_id)); obj > 0 && obj > object_id; obj--)
248 					{
249 						if (!fun(board_id, obj-1, flags))
250 							return SPR_SUCCESS;
251 					}
252 				}
253 				else
254 				{
255 					for (unsigned int obj = object_id; obj < std::min<unsigned int>(Globals::k3lapi.channel_count(board_id), object2_id + 1); obj++)
256 					{
257 						if (!fun(board_id, obj, flags))
258 							return SPR_SUCCESS;
259 					}
260 				}
261 
262 			}
263 
264 			else // matched [cClL]n
265 			{
266 				DBG(FUNC, D("individual channel/link matched"));
267 
268 				switch ((what.submatch(10))[0])
269 				{
270 					case 'C':
271 					case 'c':
272 						DBG(FUNC, D("individual channel matched"));
273 
274 						if (!fun(board_id, object_id, flags))
275 							return SPR_SUCCESS;
276 
277                         return SPR_CONTINUE;
278 
279 					case 'l':
280 					case 'L':
281 						DBG(FUNC, D("individual link matched"));
282 
283 						switch (Globals::k3lapi.device_type(board_id))
284 						{
285 							case kdtE1:
286 							case kdtPR:
287 							case kdtE1GW:
288 							case kdtE1IP:
289 							case kdtE1Spx:
290 #if K3L_AT_LEAST(2,1,0)
291 							case kdtE1FXSSpx:
292 #endif
293 							{
294 								unsigned int link_first = object_id * 30;
295 								unsigned int link_final = ((object_id + 1) * 30);
296 
297 								if (reverse)
298 								{
299 									for (unsigned int obj = std::min(link_final, Globals::k3lapi.channel_count(board_id)); obj > 0 && obj > link_first; obj--)
300 									{
301 										if (!fun(board_id, obj-1, flags))
302 											return SPR_SUCCESS;
303 									}
304 								}
305 								else
306 								{
307 									for (unsigned int obj = link_first; obj < std::min(Globals::k3lapi.channel_count(board_id), link_final); obj++)
308 									{
309 										if (!fun(board_id, obj, flags))
310 											return SPR_SUCCESS;
311 									}
312 								}
313                                 return SPR_CONTINUE;
314 
315 							}
316 
317 							default:
318 								LOG(ERROR, FMT("invalid dial string '%s': board '%d' does not have links.")
319 									% allocstr.c_str() % board_id);
320 								return SPR_FAIL;
321 						}
322 
323 					default:
324 						LOG(ERROR, FMT("invalid dial string '%s': invalid object specification.") % allocstr.c_str());
325 						return SPR_FAIL;
326 				}
327 			}
328 		}
329 		else if (what.matched(3) || what.matched(6)) // matched something about boards [bBsS]
330 		{
331 			if (reverse)
332 			{
333 				for (unsigned int obj = Globals::k3lapi.channel_count(board_id); obj > 0; obj--)
334 				{
335 					if (!fun(board_id, obj-1, flags))
336 						return SPR_SUCCESS;
337 		        }
338 	        }
339 			else
340 			{
341 				for (unsigned int obj = 0; obj < Globals::k3lapi.channel_count(board_id); obj++)
342 				{
343 					if (!fun(board_id, obj, flags))
344 						return SPR_SUCCESS;
345 				}
346 			}
347 		}
348 
349 	}
350 	catch (Strings::invalid_value e)
351 	{
352 		LOG(ERROR, FMT("invalid dial string '%s': invalid numeric value specified.") % allocstr.c_str());
353 		return SPR_FAIL;
354 	}
355     catch (Function::EmptyFunction & e)
356     {
357 		LOG(ERROR, FMT("invalid function."));
358 		return SPR_FAIL;
359     }
360 
361 	return SPR_CONTINUE;
362 }
363 
processSpecAtoms(std::string & gotatoms,SpecFlagsType & flags,SpecFunType & fun)364 static SpecRetType processSpecAtoms(std::string & gotatoms, SpecFlagsType & flags, SpecFunType & fun)
365 {
366 	std::string atoms(gotatoms);
367 
368 	DBG(FUNC, D("allocation string 'atoms': %s") % atoms);
369 
370 
371 	if (!atoms.empty() && (atoms.at(0) == '*'))  //so it is a "cyclical" allocation
372 	{
373 		atoms.erase(0, 1);
374 
375 		if (flags & SPF_FIRST)
376 		{
377 			if (!(flags & SPF_CYCLIC))
378 			{
379 				DBG(FUNC, D("got a cyclic/fair allocation (%s), priorizing less used channels...") % atoms);
380 				flags |= SPF_CYCLIC;
381 			}
382 		}
383 		else
384 		{
385 			DBG(FUNC, D("cyclic/fair allocation NOT at first string, ignoring..."));
386 		}
387 	}
388 
389 
390 	Strings::vector_type boundaries;
391 	Strings::tokenize(atoms, boundaries, "+");
392 
393     if (boundaries.size() < 1)
394     {
395         LOG(ERROR, FMT("invalid dial string '%s': no allocation string found!") % atoms);
396         return SPR_FAIL;
397     }
398 
399 	for (Strings::vector_type::iterator iter = boundaries.begin(); iter != boundaries.end(); iter++)
400 	{
401 		switch (processSpecAtom(*iter, flags, fun))
402 		{
403 			// if had some error processing dialstring, bail out..
404 			case SPR_FAIL:
405 				return SPR_FAIL;
406 
407 			// found someone? return ASAP!
408 			case SPR_SUCCESS:
409 				return SPR_SUCCESS;
410 
411 			// else, keep going..
412 			case SPR_CONTINUE:
413 				break;
414 		}
415 
416 		flags &= ~SPF_FIRST;
417 	}
418 
419 	/* found nothing, but this is NOT an error */
420 	return SPR_CONTINUE;
421 }
422 
423 struct funProcessCallChannelString
424 {
funProcessCallChannelStringfunProcessCallChannelString425 	funProcessCallChannelString(int *cause, bool need_free)
426 	: _cause(cause), _need_free(need_free),
427 	  _all_fail(true), //_fxs_only(true),
428 	  _pvt(NULL)
429 	{};
430 
operator ()funProcessCallChannelString431 	bool operator()(unsigned int dev, unsigned int obj, SpecFlagsType & flags)
432 	{
433         try
434         {
435     	    Board::KhompPvt *tmp = Board::get(dev, obj);
436 
437 	    	// used for cause definition
438 		    if (_all_fail)
439 			    _all_fail = (tmp ? !tmp->isOK() : true);
440         }
441         catch (K3LAPITraits::invalid_channel & err)
442         {
443             _all_fail = true;
444         }
445 
446 		//used for precise cause definition
447         //if (_fxs_only)
448 		//	_fxs_only = (tmp ? tmp->is_fxs() : false);
449 
450 		if (flags & SPF_CYCLIC)
451 		{
452 			Board::queueAddChannel(_channels, dev, obj);
453 			return true;
454 		}
455 		else
456 		{
457 			_pvt = Board::findFree(dev, obj, _need_free);
458 			return (_pvt == NULL);
459 		}
460 		return true;
461 	}
462 
pvtfunProcessCallChannelString463 	Board::KhompPvt * pvt(SpecFlagsType & flags)
464 	{
465 
466 		if ((flags & SPF_CYCLIC) && !_pvt)
467 		{
468 			 //we have no pvt 'till now, lets find a suitable one..
469 			_pvt = Board::queueFindFree(_channels);
470 		}
471 
472 		if (!_pvt && _cause && !(*_cause))
473 		{
474 
475 			if (_all_fail)
476 			{
477 				// all channels are in fail
478 				*_cause = SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
479 			}
480 			else
481 			{
482 				//if (_fxs_only)
483 				//	*_cause = SWITCH_CAUSE_USER_BUSY;
484 				//else
485 					*_cause = SWITCH_CAUSE_SWITCH_CONGESTION;
486 			}
487 		}
488 
489 		return _pvt;
490 	}
491 
492 	int   * _cause;
493 
494 	bool    _need_free;
495 
496 	bool    _all_fail;
497 	//bool    _fxs_only; //TODO: futuro implementar a parte de FXS
498 
499 	Board::KhompPvt          * _pvt;
500 	Board::PriorityCallQueue   _channels;
501 };
502 
503 struct funProcessSMSChannelString
504 {
505 
funProcessSMSChannelStringfunProcessSMSChannelString506     funProcessSMSChannelString(int *cause)
507     : _cause(cause), _all_fail(true), _pvt(NULL)
508     {};
509 
operator ()funProcessSMSChannelString510     bool operator()(unsigned int dev, unsigned int obj, SpecFlagsType & flags)
511     {
512         Board::KhompPvt *pvt = Board::findFree(dev, obj);
513 
514         if (pvt)
515         {
516             // found something? check if its GSM
517             if (pvt->application(SMS_CHECK, NULL, NULL))
518             {
519                 // used for cause definition
520                 if (_all_fail)
521                     _all_fail = (pvt ? !pvt->isOK() : true);
522 
523                 if (flags & SPF_CYCLIC)
524                 {
525                     Board::queueAddChannel(_channels, dev, obj);
526                     return true;
527                 }
528                 else
529                 {
530                     _pvt = pvt;
531                     return false;
532                 }
533             }
534             else
535             {
536                 // not gsm, return ASAP and stop search
537                 LOG(ERROR, PVT_FMT(pvt->target(), "channel is NOT a GSM channel! unable to send message!"));
538                 return false;
539             }
540         }
541 
542         // keep searching
543         return true;
544     }
545 
pvtfunProcessSMSChannelString546     Board::KhompPvt * pvt(SpecFlagsType & flags)
547     {
548         if ((flags & SPF_CYCLIC) && !_pvt)
549         {
550             // we have no pvt 'till now, lets find a suitable one..
551             _pvt = Board::queueFindFree(_channels);
552         }
553 
554         if (!_pvt && _cause && !(*_cause))
555         {
556             if (_all_fail)
557             {
558                 // all channels are in fail
559                 *_cause = SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
560             }
561             else
562             {
563                 // otherwise, congestion..
564                 *_cause = SWITCH_CAUSE_SWITCH_CONGESTION;
565             }
566         }
567 
568         return _pvt;
569     }
570 
571     int                            * _cause;
572 
573     bool                             _need_free;
574 
575     bool                             _all_fail;
576 
577 	Board::KhompPvt          * _pvt;
578 	Board::PriorityCallQueue   _channels;
579 };
580 
581 struct FunProcessGroupString
582 {
583     /* used for group processing */
FunProcessGroupStringFunProcessGroupString584     FunProcessGroupString(std::string ctx)
585         : _ctx(ctx) {};
586 
FunProcessGroupStringFunProcessGroupString587     FunProcessGroupString(const FunProcessGroupString &o)
588         : _ctx(o._ctx) {};
589 
operator ()FunProcessGroupString590     bool operator()(unsigned int dev, unsigned int obj, SpecFlagsType & flags) const
591     {
592         try
593         {
594             Board::KhompPvt * pvt = Board::get(dev,obj);
595 
596             DBG(CONF, FMT("loading context %s for channel %d,%d") % _ctx % dev % obj);
597 
598             if (pvt) pvt->_group_context = _ctx;
599         }
600         catch (K3LAPITraits::invalid_channel & err)
601         {
602         }
603 
604         return true;
605     }
606 
607 
608     std::string _ctx;
609 };
610 
processCallChannelString(std::string & str,Board::KhompPvt * & pvt,int * cause,bool need_free)611 static bool processCallChannelString(std::string & str, Board::KhompPvt *& pvt, int * cause, bool need_free)
612 {
613 	funProcessCallChannelString  proc(cause, need_free);
614 
615 	SpecFlagsType   flags = SPF_FIRST;
616 	SpecFunType     fun(proc, false); //   = ReferenceWrapper < SpecFunType > (proc);
617 
618     bool ret = true;
619 
620 	switch (processSpecAtoms(str, flags, fun))
621 	{
622 		case SPR_FAIL:
623 			DBG(FUNC, D("SPR_FAIL: %p") % cause);
624 
625 			if (cause)
626 				*cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
627 
628 			ret = false;
629             break;
630 		case SPR_SUCCESS:
631 		case SPR_CONTINUE:
632             pvt = proc.pvt(flags);
633 			DBG(FUNC, D("pvt = %p") % pvt);
634 
635 			if (cause && !(*cause))
636             {
637     			if (!pvt)
638 			    	*cause = SWITCH_CAUSE_INTERWORKING;
639                 else
640 			    	*cause = SWITCH_CAUSE_SUCCESS;
641             }
642 
643 			ret = true;
644             break;
645 	}
646 
647 	return ret;
648 }
649 
processSMSChannelString(std::string & str,Board::KhompPvt * & pvt,int * cause)650 bool processSMSChannelString(std::string & str, Board::KhompPvt *& pvt, int *cause)
651 {
652     funProcessSMSChannelString proc(cause);
653 
654     SpecFlagsType flags = SPF_FIRST;
655     SpecFunType   fun(proc, false);
656 
657     switch (processSpecAtoms(str, flags, fun))
658     {
659         case SPR_FAIL:
660             DBG(FUNC, FMT("SPR_FAIL: %p") % cause);
661             if (cause)
662                 *cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
663             return false;
664 
665         case SPR_SUCCESS:
666         case SPR_CONTINUE:
667             pvt = proc.pvt(flags);
668             DBG(FUNC, FMT("pvt = %p") % pvt);
669 
670             if (cause && !(*cause))
671             {
672     			if (!pvt)
673 			    	*cause = SWITCH_CAUSE_INTERWORKING;
674                 else
675 			    	*cause = SWITCH_CAUSE_SUCCESS;
676             }
677 
678             return true;
679     }
680 
681     return true;
682 }
683 
processDialString(const char * dial_charv,int * cause)684 Board::KhompPvt * processDialString (const char *dial_charv, int *cause)
685 {
686 	DBG(FUNC, D("c (%p, %p)") % dial_charv % cause);
687 	std::string           dial_string(dial_charv);
688 	Strings::vector_type  dial_args;
689 
690 	Strings::tokenize(dial_string, dial_args, "/");
691 
692 	Board::KhompPvt *pvt = NULL;
693 
694     DBG(FUNC, FMT("processing dial string [%d] : '%s'") % dial_args.size() % dial_string);
695 
696 	if ((dial_args.size() < 1 || dial_args.size() > 3))
697 	{
698 		LOG(ERROR, FMT("invalid dial string '%s': wrong number of separators ('/').") % dial_string);
699 		return NULL;
700 	}
701 
702 	bool dial_string_ok = processCallChannelString(dial_args[0], pvt, cause, true);
703 
704 	if (pvt == NULL)
705 	{
706 		if (dial_string_ok)
707             LOG(WARNING, "unable to allocate channel -- no free channel found!");
708 		return NULL;
709     }
710 
711     DBG(FUNC, PVT_FMT(pvt->target(), "pvt %p") % pvt);
712 
713 	unsigned int opt_size = (pvt->hasNumberDial() ? 3 : 2);
714 	unsigned int opt_arg  = opt_size - 1;
715 
716 	if (dial_args.size() == opt_size)
717 	{
718         Strings::vector_type options_args;
719         Strings::tokenize (dial_args[opt_arg], options_args, ":");
720 
721 		for (Strings::vector_type::iterator opt_arg = options_args.begin();
722 		     opt_arg != options_args.end(); opt_arg++)
723 		{
724 			std::string str = (*opt_arg);
725 
726 			Strings::vector_type option_item;
727 			Strings::tokenize (str, option_item, "=");
728 
729 			switch (option_item.size())
730 			{
731 				case 2:
732 				{
733 					std::string index (option_item[0]);
734 					std::string value (option_item[1]);
735 
736                     if(pvt->call()->process(index, value))
737 						continue;
738 
739 					break;
740 				}
741 
742 				case 1:
743 				{
744 		            std::string index (option_item[0]);
745 
746                     if(pvt->call()->process(index))
747 						continue;
748 
749 					break;
750 				}
751 
752 				default:
753 	            {
754 					LOG(ERROR, FMT("invalid option specification '%s'.") % str);
755 	                continue;
756 	            }
757 			}
758             LOG(ERROR, FMT("unknown option name '%s', ignoring...") % str);
759         }
760 	}
761 
762     if(pvt->hasNumberDial())
763     {
764 		if (dial_args.size() <= 1)
765 		{
766             LOG(ERROR, FMT("invalid dial string '%s': missing destination number!") % dial_string);
767 			return NULL;
768 		}
769 
770         std::string name ("dest");
771         std::string value (dial_args[1]);
772 
773         pvt->call()->process(name, value);
774 
775 		//pvt->call()->dest_addr = dial_args[1];
776 	}
777 
778     return pvt;
779 };
780 
processSMSString(const char * sms_charv,int * cause)781 Board::KhompPvt * processSMSString (const char *sms_charv, int *cause)
782 {
783     std::string           sms_string(sms_charv);
784     Strings::vector_type  sms_args;
785 
786     Strings::tokenize(sms_string, sms_args, "/|,", 3); // '/' is a backward compatibility feature!
787 
788     DBG(FUNC, FMT("processing SMS string [%d] : '%s'") % sms_args.size() % sms_string);
789 
790     if (sms_args.size () != 3)
791     {
792         LOG(ERROR, FMT("invalid dial string '%s': wrong number of separators.") % sms_string);
793         return NULL;
794     }
795 
796     Board::KhompPvt *pvt = NULL;
797 
798     bool dial_string_ok = processSMSChannelString(sms_args[0], pvt, cause);
799 
800     if (pvt == NULL)
801     {
802         if (dial_string_ok)
803         {
804             LOG(WARNING, "unable to allocate channel -- no free channel found!");
805         }
806 
807         return NULL;
808     }
809     else
810     {
811         if (!pvt->application(SMS_CHECK, NULL, NULL))
812         {
813             LOG(ERROR, PVT_FMT(pvt->target(), "allocated channel is NOT a GSM channel! unable to send message!"));
814 
815             return NULL;
816         }
817     }
818 
819 
820 /*
821     std::string dest(sms_args[1]);
822 
823     bool conf = false;
824 
825     if (dest[0] == '!')
826     {
827         dest.erase(0,1);
828         conf = true;
829     }
830 
831     if (dest[dest.size()-1] == '!')
832     {
833         dest.erase(dest.size()-1,1);
834         conf = true;
835     }
836 
837     // get options/values
838     pvt->send_sms.sms_dest = dest;
839     pvt->send_sms.sms_conf = conf;
840     pvt->send_sms.sms_body = sms_args[2];
841 */
842 
843     return pvt;
844 };
845 
processGroupString()846 void processGroupString()
847 {
848     for (GroupToDestMapType::iterator i = Opt::_groups.begin(); i != Opt::_groups.end(); i++)
849     {
850         const std::string & name = (*i).first;
851         std::string & opts = (*i).second;
852 
853         Strings::vector_type tokens;
854         Strings::tokenize(opts, tokens, ",:", 2);
855 
856         if (tokens.size() != 2 && tokens.size() != 1)
857         {
858             LOG(WARNING, FMT("wrong number of arguments at group '%s', ignoring group!\n") % name.c_str());
859             opts.clear();
860             continue;
861         }
862 
863         if (tokens.size() < 2)
864             continue;
865         FunProcessGroupString    proc(tokens[1]);
866 
867         SpecFlagsType   flags = SPF_FIRST;
868         SpecFunType     fun(proc, false);
869 
870         switch (processSpecAtoms(tokens[0], flags, fun))
871         {
872             case SPR_CONTINUE:
873                 // remove context from spec
874                 opts = tokens[0];
875 
876                 // log this!
877                 DBG(CONF, FMT("group '%s' is now '%s', with context '%s'...")
878                         % name % tokens[0] % tokens[1]);
879                 break;
880 
881             default:
882                 LOG(WARNING, FMT("skipping group '%s', bad configuration!\n") % name.c_str());
883 
884                 // "zero" group
885                 opts.clear();
886 
887                 // log this!
888                 DBG(CONF, FMT("group '%s' have misconfigured options, ignoring...") % name);
889                 break;
890         }
891     }
892 }
893 
894