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