Browse code

Merge branch 'master' into offer_answer

Raphael Coeffic authored on 18/02/2011 11:36:25
Showing 160 changed files
... ...
@@ -12,7 +12,7 @@ set( CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
12 12
 PROJECT (SEMS)
13 13
 
14 14
 SET(SEMS_MAJOR_VERSION 1)
15
-SET(SEMS_MINOR_VERSION 3)
15
+SET(SEMS_MINOR_VERSION 4)
16 16
 SET(SEMS_BUILD_VERSION 0)
17 17
 SET(SEMS_EXTRA_VERSION dev)
18 18
 
... ...
@@ -240,6 +240,27 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX)
240 240
 SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
241 241
 SET(CMAKE_C_FLAGS_DEBUG   "${CMAKE_C_FLAGS_DEBUG}   -D_DEBUG")
242 242
 
243
+# compile with session thread pool support?
244
+#      use this for very high concurrent call count
245
+#      applications (e.g. for signaling only)
246
+#      if compiled with thread pool, there will be a
247
+#      thread pool of configurable size processing the
248
+#      signaling and application logic of the calls.
249
+#      if compiled without thread pool support, every
250
+#      session will have its own thread.
251
+#
252
+#ADD_DEFINITIONS(-DSESSION_THREADPOOL)
253
+
254
+#ADD_DEFINITIONS(-DNO_THREADID_LOG)
255
+#ADD_DEFINITIONS(-DLOG_LOC_DATA_ATEND)
256
+
257
+# Support for long debug messages? (useful for debugging SIP messages' contents)
258
+#
259
+# disable for slight performance gain
260
+ADD_DEFINITIONS(-DLOG_BUFFER_LEN=2048)
261
+
262
+#ADD_DEFINITIONS(-DDEBUG_PLAYOUTBUF)
263
+
243 264
 SET(CMAKE_SHARED_LIBRARY_PREFIX "")
244 265
 
245 266
 ADD_SUBDIRECTORY (core)
... ...
@@ -86,7 +86,8 @@ USE_MONITORING=yes
86 86
 
87 87
 # Support for long debug messages? (useful for debugging SIP messages' contents)
88 88
 #
89
-#LONG_DEBUG_MESSAGE=yes
89
+# disable for slight performance gain
90
+LONG_DEBUG_MESSAGE=yes
90 91
 
91 92
 # Is this a debug build or not?
92 93
 debug=no
... ...
@@ -2,8 +2,6 @@ ADD_SUBDIRECTORY(ann_b2b)
2 2
 ADD_SUBDIRECTORY(announce_transfer)
3 3
 ADD_SUBDIRECTORY(announcement)
4 4
 ADD_SUBDIRECTORY(annrecorder)
5
-ADD_SUBDIRECTORY(auth_b2b)
6
-ADD_SUBDIRECTORY(call_timer)
7 5
 ADD_SUBDIRECTORY(callback)
8 6
 ADD_SUBDIRECTORY(click2dial)
9 7
 IF(PYTHONLIBS_FOUND)
... ...
@@ -45,8 +43,6 @@ ENDIF(PYTHONLIBS_FOUND)
45 43
 ADD_SUBDIRECTORY(reg_agent)
46 44
 ADD_SUBDIRECTORY(registrar_client)
47 45
 ADD_SUBDIRECTORY(sbc)
48
-ADD_SUBDIRECTORY(sst_b2b)
49
-ADD_SUBDIRECTORY(sw_prepaid_sip)
50 46
 #IF(PYTHONLIBS_FOUND)
51 47
 #	ADD_SUBDIRECTORY(twit)
52 48
 #ENDIF(PYTHONLIBS_FOUND)
53 49
deleted file mode 100644
... ...
@@ -1,331 +0,0 @@
1
-/*
2
- * Copyright (C) 2008 iptego GmbH
3
- * Based on the concept of mycc, Copyright (C) 2007 Sipwise GmbH
4
- * Based on the concept of sw_prepaid_sip, Copyright (C) 2002-2003 Fhg Fokus
5
- *
6
- * This file is part of SEMS, a free SIP media server.
7
- *
8
- * SEMS is free software; you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation; either version 2 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * For a license to use the sems software under conditions
14
- * other than those described here, or to purchase support for this
15
- * software, please contact iptel.org by e-mail at the following addresses:
16
- *    info@iptel.org
17
- *
18
- * SEMS is distributed in the hope that it will be useful,
19
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
- * GNU General Public License for more details.
22
- *
23
- * You should have received a copy of the GNU General Public License
24
- * along with this program; if not, write to the Free Software
25
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
- */
27
-
28
-#include "AuthB2B.h"
29
-
30
-#include "log.h"
31
-#include "AmUtils.h"
32
-#include "AmAudio.h"
33
-#include "AmPlugIn.h"
34
-#include "AmMediaProcessor.h"
35
-#include "AmConfigReader.h"
36
-#include "AmSessionContainer.h"
37
-
38
-string AuthB2BFactory::user;
39
-string AuthB2BFactory::domain;
40
-string AuthB2BFactory::pwd;
41
-
42
-EXPORT_SESSION_FACTORY(AuthB2BFactory,MOD_NAME);
43
-
44
-AuthB2BFactory::AuthB2BFactory(const string& _app_name)
45
-: AmSessionFactory(_app_name)
46
-{
47
-}
48
-
49
-
50
-int AuthB2BFactory::onLoad()
51
-{
52
-   AmConfigReader cfg;
53
-   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
54
-     INFO("No configuration for auth_b2b present (%s)\n",
55
-	  (AmConfig::ModConfigPath + string(MOD_NAME ".conf")).c_str()
56
-	  );
57
-   } else {
58
-     user = cfg.getParameter("user");
59
-     domain = cfg.getParameter("domain");
60
-     pwd = cfg.getParameter("pwd");
61
-   }
62
-
63
-  return 0;
64
-}
65
-
66
-
67
-AmSession* AuthB2BFactory::onInvite(const AmSipRequest& req)
68
-{
69
-  return new AuthB2BDialog();
70
-}
71
-
72
-
73
-AuthB2BDialog::AuthB2BDialog()
74
-: m_state(BB_Init),
75
-  AmB2BCallerSession()
76
-
77
-{
78
-  set_sip_relay_only(false);
79
-}
80
-
81
-
82
-AuthB2BDialog::~AuthB2BDialog()
83
-{
84
-}
85
-
86
-
87
-void AuthB2BDialog::onInvite(const AmSipRequest& req)
88
-{
89
-  // TODO: do reinvites get here? if so, don't set a timer then
90
-  // -> yes, they do.
91
-
92
-  // TODO: errors thrown as exception don't seem to trigger a reply?
93
-  // -> only in SessionFactory::onInvite they do. todo: move the logic to 
94
-  //    session factory 
95
-
96
-  // this will prevent us from being added to media processor
97
-  setInOut(NULL,NULL); 
98
-
99
-  if (AuthB2BFactory::user.empty()) {
100
-    string app_param = getHeader(req.hdrs, PARAM_HDR, true);
101
-
102
-    if (!app_param.length()) {
103
-      AmSession::Exception(500, "auth_b2b: parameters not found");
104
-    }
105
-    
106
-    domain = get_header_keyvalue(app_param,"d");
107
-    user = get_header_keyvalue(app_param,"u");
108
-    password = get_header_keyvalue(app_param,"p");
109
-  } else {
110
-    domain = AuthB2BFactory::domain;
111
-    user = AuthB2BFactory::user;
112
-    password = AuthB2BFactory::pwd;
113
-  }
114
-
115
-  from = "sip:"+user+"@"+domain;
116
-  to = "sip:"+req.user+"@"+domain;
117
-
118
-   // DBG("-----------------------------------------------------------------\n");
119
-   // DBG("domain = %s, user = %s, pwd = %s, from = %s, to = %s;",  
120
-   //     domain.c_str(), user.c_str(), password.c_str(), from.c_str(), to.c_str());
121
-   // DBG("-----------------------------------------------------------------\n");
122
-
123
-  m_state = BB_Dialing;
124
-
125
-  if(dlg.reply(req, 100, "Connecting") != 0) {
126
-    throw AmSession::Exception(500,"Failed to reply 100");
127
-  }
128
-
129
-  invite_req = req;
130
-
131
-  removeHeader(invite_req.hdrs,PARAM_HDR);
132
-  removeHeader(invite_req.hdrs,"P-App-Name");
133
-
134
-  //dlg.updateStatus(req);
135
-  recvd_req.insert(std::make_pair(req.cseq,req));
136
-  
137
-  set_sip_relay_only(true);
138
-  connectCallee("<" + to + ">", to, true);
139
-}
140
-
141
-
142
-void AuthB2BDialog::process(AmEvent* ev)
143
-{
144
-//   AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(ev);
145
-//   if(plugin_event && plugin_event->name == "timer_timeout") {
146
-//     int timer_id = plugin_event->data.get(0).asInt();
147
-//     if (timer_id == TIMERID_CREDIT_TIMEOUT) {
148
-//       DBG("timer timeout, no more credit\n");
149
-//       stopAccounting();
150
-//       terminateOtherLeg();
151
-//       terminateLeg();
152
-
153
-//       ev->processed = true;
154
-//       return;
155
-//     }
156
-//   }
157
-
158
-  AmB2BCallerSession::process(ev);
159
-}
160
-
161
-
162
-bool AuthB2BDialog::onOtherReply(const AmSipReply& reply)
163
-{
164
-  bool ret = false;
165
-
166
-  if ((m_state == BB_Dialing) && (reply.cseq == invite_req.cseq)) {
167
-    if (reply.code < 200) {
168
-      DBG("Callee is trying... code %d\n", reply.code);
169
-    }
170
-    else if(reply.code < 300) {
171
-      if(getCalleeStatus()  == Connected) {
172
-        m_state = BB_Connected;
173
-        setInOut(NULL, NULL);
174
-
175
-//         // set the call timer
176
-//      setTimer(TIMERID_CREDIT_TIMEOUT, m_credit);
177
-      }
178
-    }
179
-    else if(reply.code == 487 && (dlg.getStatus() < AmSipDialog::Connected)) {
180
-      DBG("Stopping leg A on 487 from B with 487\n");
181
-      dlg.reply(invite_req, 487, "Request terminated");
182
-      setStopped();
183
-      ret = true;
184
-    }
185
-    else if (dlg.getStatus() == AmSipDialog::Connected) {
186
-      DBG("Callee final error in connected state with code %d\n",reply.code);
187
-      terminateLeg();
188
-    }
189
-    else {
190
-      DBG("Callee final error with code %d\n",reply.code);
191
-      AmB2BCallerSession::onOtherReply(reply);
192
-    }
193
-  }
194
-  return ret;
195
-}
196
-
197
-
198
-void AuthB2BDialog::onOtherBye(const AmSipRequest& req)
199
-{
200
-//   stopAccounting();
201
-  AmB2BCallerSession::onOtherBye(req);
202
-}
203
-
204
-
205
-void AuthB2BDialog::onBye(const AmSipRequest& req)
206
-{
207
-  if (m_state == BB_Connected) {
208
-//     stopAccounting();
209
-  }
210
-
211
-  dlg.reply(req,200,"OK");
212
-
213
-  terminateOtherLeg();
214
-  setStopped();
215
-}
216
-
217
-
218
-void AuthB2BDialog::onCancel()
219
-{
220
-  if(dlg.getStatus() == AmSipDialog::Cancelling) {
221
-    terminateOtherLeg();
222
-    dlg.reply(invite_req, 487, "Request terminated");
223
-    setStopped();
224
-  }
225
-}
226
-
227
-void AuthB2BDialog::createCalleeSession()
228
-{
229
-  AuthB2BCalleeSession* callee_session = new AuthB2BCalleeSession(this, user, password);
230
-  
231
-  // adding auth handler
232
-  AmSessionEventHandlerFactory* uac_auth_f = 
233
-    AmPlugIn::instance()->getFactory4Seh("uac_auth");
234
-  if (NULL == uac_auth_f)  {
235
-    INFO("uac_auth module not loaded. uac auth NOT enabled for callee session.\n");
236
-  } else {
237
-    AmSessionEventHandler* h = uac_auth_f->getHandler(callee_session);
238
-
239
-    // we cannot use the generic AmSessionEventHandler hooks, 
240
-    // because the hooks don't work in AmB2BSession
241
-    callee_session->setAuthHandler(h);
242
-    DBG("uac auth enabled for callee session.\n");
243
-  }
244
-  
245
-  AmSipDialog& callee_dlg = callee_session->dlg;
246
-  
247
-  other_id = AmSession::getNewId();
248
-  
249
-  callee_dlg.local_tag    = other_id;
250
-  callee_dlg.callid       = AmSession::getNewId() + "@" + AmConfig::LocalIP;
251
-  
252
-  // this will be overwritten by ConnectLeg event 
253
-  callee_dlg.remote_party = dlg.local_party;
254
-  callee_dlg.remote_uri   = dlg.local_uri;
255
-
256
-  // if given as parameters, use these
257
-  callee_dlg.local_party  = from; 
258
-  callee_dlg.local_uri    = from; 
259
-  
260
-  DBG("Created B2BUA callee leg, From: %s\n",
261
-      from.c_str());
262
-
263
-  if (AmConfig::LogSessions) {
264
-    INFO("Starting B2B callee session %s\n",
265
-	 callee_session->getLocalTag().c_str());
266
-  }
267
-
268
-  MONITORING_LOG4(other_id.c_str(), 
269
-		  "dir",  "out",
270
-		  "from", callee_dlg.local_party.c_str(),
271
-		  "to",   callee_dlg.remote_party.c_str(),
272
-		  "ruri", callee_dlg.remote_uri.c_str());
273
-
274
-  callee_session->start();
275
-  
276
-  AmSessionContainer* sess_cont = AmSessionContainer::instance();
277
-  sess_cont->addSession(other_id,callee_session);
278
-}
279
-
280
-AuthB2BCalleeSession::AuthB2BCalleeSession(const AmB2BCallerSession* caller,
281
-					   const string& user, const string& pwd) 
282
-  : auth(NULL), 
283
-    credentials("", user, pwd), // domain (realm) is unused in credentials 
284
-    AmB2BCalleeSession(caller) {
285
-}
286
-
287
-AuthB2BCalleeSession::~AuthB2BCalleeSession() {
288
-  if (auth) 
289
-    delete auth;
290
-}
291
-
292
-inline UACAuthCred* AuthB2BCalleeSession::getCredentials() {
293
-  return &credentials;
294
-}
295
-
296
-void AuthB2BCalleeSession::onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status) {
297
-  if (NULL == auth) {    
298
-    AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
299
-    return;
300
-  }
301
-  
302
-  unsigned int cseq_before = dlg.cseq;
303
-  if (!auth->onSipReply(reply, old_dlg_status)) {
304
-    AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
305
-  } else {
306
-    if (cseq_before != dlg.cseq) {
307
-      DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
308
-          "updating relayed_req map\n", 
309
-	  reply.cseq, cseq_before);
310
-      TransMap::iterator it=relayed_req.find(reply.cseq);
311
-      if (it != relayed_req.end()) {
312
-	relayed_req[cseq_before] = it->second;
313
-	relayed_req.erase(it);
314
-      }
315
-    }
316
-  }
317
-}
318
-
319
-void AuthB2BCalleeSession::onSendRequest(const string& method, const string& content_type,
320
-			      const string& body, string& hdrs, int flags, unsigned int cseq)
321
-{
322
-  if (NULL != auth) {
323
-    DBG("auth->onSendRequest cseq = %d\n", cseq);
324
-    auth->onSendRequest(method, content_type,
325
-			body, hdrs, flags, cseq);
326
-  }
327
-  
328
-  AmB2BCalleeSession::onSendRequest(method, content_type,
329
-				     body, hdrs, flags, cseq);
330
-}
331
-
332 0
deleted file mode 100644
... ...
@@ -1,105 +0,0 @@
1
-/*
2
- * Copyright (C) 2008 iptego GmbH
3
- * Based on the concept of sw_prepaid_sip, Copyright (C) 2007 Sipwise GmbH
4
- * Based on the concept of mycc, Copyright (C) 2002-2003 Fhg Fokus
5
- *
6
- * This file is part of SEMS, a free SIP media server.
7
- *
8
- * SEMS is free software; you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation; either version 2 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * For a license to use the sems software under conditions
14
- * other than those described here, or to purchase support for this
15
- * software, please contact iptel.org by e-mail at the following addresses:
16
- *    info@iptel.org
17
- *
18
- * SEMS is distributed in the hope that it will be useful,
19
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
- * GNU General Public License for more details.
22
- *
23
- * You should have received a copy of the GNU General Public License
24
- * along with this program; if not, write to the Free Software
25
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
- */
27
-
28
-#ifndef _AUTH_B2B_H
29
-#define _AUTH_B2B_H
30
-
31
-#include "AmB2BSession.h"
32
-#include "ampi/UACAuthAPI.h"
33
-
34
-using std::string;
35
-
36
-class AuthB2BFactory: public AmSessionFactory
37
-{
38
-
39
- public:
40
-  AuthB2BFactory(const string& _app_name);
41
-  
42
-  int onLoad();
43
-  AmSession* onInvite(const AmSipRequest& req);
44
-  static string user;
45
-  static string domain;
46
-  static string pwd;
47
-
48
-};
49
-
50
-class AuthB2BDialog : public AmB2BCallerSession
51
-{
52
-  enum {
53
-    BB_Init = 0,
54
-    BB_Dialing,
55
-    BB_Connected,
56
-    BB_Teardown
57
-  } CallerState;
58
-
59
-  int m_state;
60
-
61
-  string domain;
62
-  string user;
63
-  string password;
64
-  string from;
65
-  string to;
66
-  
67
- public:
68
-
69
-  AuthB2BDialog();
70
-  ~AuthB2BDialog();
71
-  
72
-  void process(AmEvent* ev);
73
-  void onBye(const AmSipRequest& req);
74
-  void onInvite(const AmSipRequest& req);
75
-  void onCancel();
76
-  
77
-
78
- protected:
79
-  
80
-  bool onOtherReply(const AmSipReply& reply);
81
-  void onOtherBye(const AmSipRequest& req);
82
-
83
-  void createCalleeSession();
84
-};
85
-
86
-class AuthB2BCalleeSession 
87
-: public AmB2BCalleeSession, public CredentialHolder
88
-{
89
-  UACAuthCred credentials;
90
-  AmSessionEventHandler* auth;
91
-
92
- protected:
93
-  void onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status);
94
-  void onSendRequest(const string& method, const string& content_type,
95
-		     const string& body, string& hdrs, int flags, unsigned int cseq);
96
-
97
- public:
98
-  AuthB2BCalleeSession(const AmB2BCallerSession* caller, const string& user, const string& pwd); 
99
-  ~AuthB2BCalleeSession();
100
-
101
-  inline UACAuthCred* getCredentials();
102
-  
103
-  void setAuthHandler(AmSessionEventHandler* h) { auth = h; }
104
-};
105
-#endif                           
106 0
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-set (auth_b2b_SRCS
2
-AuthB2B.cpp
3
-)
4
-
5
-SET(sems_module_name auth_b2b)
6
-INCLUDE(${CMAKE_SOURCE_DIR}/cmake/module.rules.txt)
7 0
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-plug_in_name = auth_b2b
2
-
3
-module_ldflags =
4
-module_cflags  = -DMOD_NAME=\"$(plug_in_name)\"
5
-
6
-COREPATH ?= ../../core
7
-include $(COREPATH)/plug-in/Makefile.app_module
8 0
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-
2
-# Account to use on the outgoing call leg. If this is not set,
3
-# the account is taken from P-App-Param header (see Readme.auth_b2b)
4
-#
5
-# user=someuser
6
-# domain=somedomain.net
7
-# pwd=sompwd
8 0
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-
2
-# Account to use on the outgoing call leg. If this is not set,
3
-# the account is taken from P-App-Param header (see Readme.auth_b2b)
4
-#
5
-# user=someuser
6
-# domain=somedomain.net
7
-# pwd=sompwd
8 0
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-set (call_timer_SRCS
2
-CallTimer.cpp
3
-)
4
-
5
-SET(sems_module_name call_timer)
6
-INCLUDE(${CMAKE_SOURCE_DIR}/cmake/module.rules.txt)
7 0
deleted file mode 100644
... ...
@@ -1,234 +0,0 @@
1
-/*
2
- * Copyright (C) 2008 iptego GmbH
3
- * Based on the concept of auth_b2b, Copyright (C) 2008 iptego GmbH
4
- * Based on the concept of mycc, Copyright (C) 2007 Sipwise GmbH
5
- * Based on the concept of sw_prepaid_sip, Copyright (C) 2002-2003 Fhg Fokus
6
- *
7
- * This file is part of SEMS, a free SIP media server.
8
- *
9
- * SEMS is free software; you can redistribute it and/or modify
10
- * it under the terms of the GNU General Public License as published by
11
- * the Free Software Foundation; either version 2 of the License, or
12
- * (at your option) any later version.
13
- *
14
- * For a license to use the sems software under conditions
15
- * other than those described here, or to purchase support for this
16
- * software, please contact iptel.org by e-mail at the following addresses:
17
- *    info@iptel.org
18
- *
19
- * SEMS is distributed in the hope that it will be useful,
20
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
- * GNU General Public License for more details.
23
- *
24
- * You should have received a copy of the GNU General Public License
25
- * along with this program; if not, write to the Free Software
26
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
- */
28
-
29
-#include "CallTimer.h"
30
-
31
-#include "log.h"
32
-#include "AmUtils.h"
33
-#include "AmAudio.h"
34
-#include "AmPlugIn.h"
35
-#include "AmMediaProcessor.h"
36
-#include "AmConfigReader.h"
37
-#include "AmSessionContainer.h"
38
-
39
-#define TIMERID_CALL_TIMER 1
40
-
41
-#define DEFAULT_CALL_TIMER 1200  // 2 h default call timer
42
-
43
-unsigned int CallTimerFactory::DefaultCallTimer; 
44
-bool         CallTimerFactory::UseAppParam = true;
45
-
46
-EXPORT_SESSION_FACTORY(CallTimerFactory,MOD_NAME);
47
-
48
-CallTimerFactory::CallTimerFactory(const string& _app_name)
49
-: AmSessionFactory(_app_name)
50
-{
51
-}
52
-
53
-CallTimerFactory::~CallTimerFactory() {
54
-}
55
-
56
-
57
-
58
-int CallTimerFactory::onLoad()
59
-{
60
-  AmConfigReader cfg;
61
-  if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
62
-    DBG("using default timer of %d seconds\n", DEFAULT_CALL_TIMER);
63
-    DefaultCallTimer = DEFAULT_CALL_TIMER;
64
-  } else {
65
-    DefaultCallTimer  = cfg.getParameterInt("default_call_time", DEFAULT_CALL_TIMER);
66
-    UseAppParam = (cfg.getParameter("use_app_param") == "yes");
67
-  }
68
-
69
-  if (!AmSession::timersSupported()) {
70
-    ERROR("load session_timer plug-in for timers\n");
71
-    return -1;
72
-  }
73
-
74
-  return 0;
75
-}
76
-
77
-
78
-AmSession* CallTimerFactory::onInvite(const AmSipRequest& req)
79
-{
80
-  DBG(" *** creating new call timer session ***\n");
81
-
82
-  string app_param = getHeader(req.hdrs, PARAM_HDR, true);
83
-
84
-  unsigned int call_time = CallTimerFactory::DefaultCallTimer;
85
-
86
-  if (CallTimerFactory::UseAppParam) {
87
-    if (!app_param.length()) {
88
-      INFO("call_time: no call timer parameters found. "
89
-	   "Using default value of %d seconds\n", 
90
-	   CallTimerFactory::DefaultCallTimer);
91
-    } else {
92
-      string call_time_s = get_header_keyvalue(app_param,"t", "Timer");
93
-      
94
-      if (str2i(call_time_s, call_time)) {
95
-	WARN("Error decoding call time value '%s'. Using default time of %d\n",
96
-	     call_time_s.c_str(), call_time);
97
-      } 
98
-    }
99
-  }
100
-
101
-  DBG("using call timer %d seconds\n", call_time);
102
-
103
-  return new CallTimerDialog(call_time);
104
-}
105
-
106
-
107
-CallTimerDialog::CallTimerDialog(unsigned int call_time)
108
-: m_state(BB_Init),
109
-  call_time(call_time),
110
-  AmB2BCallerSession()
111
-
112
-{
113
-  set_sip_relay_only(false);
114
-}
115
-
116
-
117
-CallTimerDialog::~CallTimerDialog()
118
-{
119
-}
120
-
121
-void CallTimerDialog::onInvite(const AmSipRequest& req)
122
-{
123
-  if (dlg.getStatus() == AmSipDialog::Connected) {
124
-    DBG("not acting on re-Invite\n");
125
-    return;
126
-  }
127
-    
128
-  // this will prevent us from being added to media processor
129
-  setInOut(NULL,NULL);
130
-
131
-  m_state = BB_Dialing;
132
-
133
-  if(dlg.reply(req, 100, "Trying") != 0) {
134
-    throw AmSession::Exception(500,"Failed to reply 100");
135
-  }
136
-
137
-  invite_req = req;
138
-
139
-  // remove P-App-Name, P-App-Param header
140
-  removeHeader(invite_req.hdrs, "P-App-Param");
141
-  removeHeader(invite_req.hdrs, "P-App-Name");
142
-
143
-  connectCallee(invite_req.to, invite_req.r_uri, true);
144
-}
145
-
146
-void CallTimerDialog::process(AmEvent* ev)
147
-{
148
-  AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(ev);
149
-  if(plugin_event && plugin_event->name == "timer_timeout") {
150
-    int timer_id = plugin_event->data.get(0).asInt();
151
-    if (timer_id == TIMERID_CALL_TIMER) {
152
-      DBG("timer timeout.\n");
153
-      terminateOtherLeg();
154
-      dlg.bye();
155
-      terminateLeg();
156
-      ev->processed = true;
157
-      return;
158
-    }
159
-  }
160
-  
161
-  AmB2BCallerSession::process(ev);
162
-}
163
-
164
-
165
-bool CallTimerDialog::onOtherReply(const AmSipReply& reply)
166
-{
167
-  bool ret = false;
168
-
169
-  if (m_state == BB_Dialing) {
170
-    if (reply.code < 200) {
171
-      DBG("Callee is trying... code %d\n", reply.code);
172
-    }
173
-    else if(reply.code < 300) {
174
-      if(getCalleeStatus()  == Connected) {
175
-        m_state = BB_Connected;
176
-        setInOut(NULL, NULL);
177
-	// startAccounting();
178
-	// set the call timer
179
-	setTimer(TIMERID_CALL_TIMER, call_time);
180
-      }
181
-    }
182
-    else if(reply.code == 487 && dlg.getStatus() < AmSipDialog::Connected) {
183
-      DBG("Stopping leg A on 487 from B with 487\n");
184
-      dlg.reply(invite_req, 487, "Request terminated");
185
-      setStopped();
186
-      ret = true;
187
-    }
188
-    else if (dlg.getStatus() == AmSipDialog::Connected) {
189
-      DBG("Callee final error in connected state with code %d\n",reply.code);
190
-      terminateLeg();
191
-    }
192
-    else if (m_state == BB_Dialing) {
193
-      DBG("Callee final error with code %d\n",reply.code);
194
-      AmB2BCallerSession::onOtherReply(reply);
195
-      // reset into non-b2b mode to get possible INVITE again
196
-      sip_relay_only = false;
197
-    } else {
198
-      DBG("Callee final error with code %d\n",reply.code);
199
-      AmB2BCallerSession::onOtherReply(reply);
200
-    }
201
-  }
202
-  return ret;
203
-}
204
-
205
-
206
-void CallTimerDialog::onOtherBye(const AmSipRequest& req)
207
-{
208
-//   stopAccounting();
209
-  AmB2BCallerSession::onOtherBye(req);
210
-}
211
-
212
-
213
-void CallTimerDialog::onBye(const AmSipRequest& req)
214
-{
215
-  if (m_state == BB_Connected) {
216
-//     stopAccounting();
217
-  }
218
-  
219
-  dlg.reply(req,200,"OK");
220
-
221
-  terminateOtherLeg();
222
-  setStopped();
223
-}
224
-
225
-
226
-void CallTimerDialog::onCancel()
227
-{
228
-  if(dlg.getStatus() == AmSipDialog::Cancelling) {
229
-    terminateOtherLeg();
230
-    dlg.reply(invite_req, 487, "Request terminated");
231
-    setStopped();
232
-  }
233
-}
234
-
235 0
deleted file mode 100644
... ...
@@ -1,78 +0,0 @@
1
-/*
2
- * Copyright (C) 2008 iptego GmbH
3
- * Based on the concept of auth_b2b, Copyright (C) 2008 iptego GmbH
4
- * Based on the concept of sw_prepaid_sip, Copyright (C) 2007 Sipwise GmbH
5
- * Based on the concept of mycc, Copyright (C) 2002-2003 Fhg Fokus
6
- *
7
- * This file is part of SEMS, a free SIP media server.
8
- *
9
- * SEMS is free software; you can redistribute it and/or modify
10
- * it under the terms of the GNU General Public License as published by
11
- * the Free Software Foundation; either version 2 of the License, or
12
- * (at your option) any later version.
13
- *
14
- * For a license to use the sems software under conditions
15
- * other than those described here, or to purchase support for this
16
- * software, please contact iptel.org by e-mail at the following addresses:
17
- *    info@iptel.org
18
- *
19
- * SEMS is distributed in the hope that it will be useful,
20
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
- * GNU General Public License for more details.
23
- *
24
- * You should have received a copy of the GNU General Public License
25
- * along with this program; if not, write to the Free Software
26
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
- */
28
-
29
-#ifndef _AUTH_B2B_H
30
-#define _AUTH_B2B_H
31
-
32
-#include "AmB2BSession.h"
33
-
34
-using std::string;
35
-
36
-class CallTimerFactory: public AmSessionFactory
37
-{
38
- public:
39
-  static unsigned int DefaultCallTimer; 
40
-  static bool UseAppParam;
41
-
42
-  CallTimerFactory(const string& _app_name);
43
-  ~CallTimerFactory();
44
-
45
-  int onLoad();
46
-  AmSession* onInvite(const AmSipRequest& req);
47
-};
48
-
49
-class CallTimerDialog : public AmB2BCallerSession
50
-{
51
-  enum {
52
-    BB_Init = 0,
53
-    BB_Dialing,
54
-    BB_Connected,
55
-    BB_Teardown
56
-  } CallerState;
57
-
58
-  int m_state;
59
-  
60
-  unsigned int call_time;
61
-
62
- public:
63
-
64
-  CallTimerDialog(unsigned int call_time);
65
-  ~CallTimerDialog();
66
-  
67
-  void process(AmEvent* ev);
68
-  void onBye(const AmSipRequest& req);
69
-  void onInvite(const AmSipRequest& req);
70
-  void onCancel();
71
-  
72
-
73
- protected:
74
-  
75
-  bool onOtherReply(const AmSipReply& reply);
76
-  void onOtherBye(const AmSipRequest& req);
77
-};
78
-#endif                           
79 0
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-plug_in_name = call_timer
2
-
3
-module_ldflags =
4
-module_cflags  = -DMOD_NAME=\"$(plug_in_name)\"
5
-
6
-COREPATH ?= ../../core
7
-include $(COREPATH)/plug-in/Makefile.app_module
8 0
deleted file mode 100644
... ...
@@ -1,13 +0,0 @@
1
-
2
-#
3
-# use_app_param=[yes|no]
4
-#
5
-# sets whether App-Param header is used for call time value
6
-# (or default_call_time below)
7
-#
8
-use_app_param=yes
9
-
10
-#
11
-# call timer value used if not in P-App-Param. in seconds
12
-#
13
-default_call_time=1200
14 0
\ No newline at end of file
15 1
deleted file mode 100644
... ...
@@ -1,13 +0,0 @@
1
-
2
-#
3
-# use_app_param=[yes|no]
4
-#
5
-# sets whether App-Param header is used for call time value
6
-# (or default_call_time below)
7
-#
8
-use_app_param=yes
9
-
10
-#
11
-# call timer value used if not in P-App-Param. in seconds
12
-#
13
-default_call_time=1200
14 0
\ No newline at end of file
... ...
@@ -240,7 +240,7 @@ void C2DCallerDialog::createCalleeSession()
240 240
   other_id = AmSession::getNewId();
241 241
 
242 242
   callee_dlg.local_tag    = other_id;
243
-  callee_dlg.callid       = AmSession::getNewId() + "@" + AmConfig::LocalIP;
243
+  callee_dlg.callid       = AmSession::getNewId();
244 244
   callee_dlg.local_party  = dlg.local_party;
245 245
   callee_dlg.remote_party = dlg.remote_party;
246 246
   callee_dlg.remote_uri   = dlg.remote_uri;
... ...
@@ -783,7 +783,7 @@ void ConferenceDialog::createDialoutParticipant(const string& uri_user)
783 783
   AmSipDialog& dialout_dlg = dialout_session->dlg;
784 784
 
785 785
   dialout_dlg.local_tag    = dialout_id;
786
-  dialout_dlg.callid       = AmSession::getNewId() + "@" + AmConfig::LocalIP;
786
+  dialout_dlg.callid       = AmSession::getNewId();
787 787
 
788 788
   if (from_header.length() > 0) {
789 789
     dialout_dlg.local_party  = from_header;
... ...
@@ -793,16 +793,7 @@ void ConferenceDialog::createDialoutParticipant(const string& uri_user)
793 793
   dialout_dlg.remote_party = uri;
794 794
   dialout_dlg.remote_uri   = uri;
795 795
 
796
-  //string body;
797
-  //int local_port = dialout_session->RTPStream()->getLocalPort();
798
-  //dialout_session->sdp.genRequest(AmConfig::LocalIP,local_port,body);
799
-
800
-  if (extra_headers.length() == 0) {
801
-    extra_headers = "";
802
-  }
803
-
804
-  dialout_dlg.sendRequest("INVITE",
805
-			  "",""/*"application/sdp",body*/,
796
+  dialout_dlg.sendRequest(SIP_METH_INVITE,"","",
806 797
 			  extra_headers);
807 798
 
808 799
   dialout_session->start();
... ...
@@ -885,12 +876,7 @@ void ConferenceDialog::onSipRequest(const AmSipRequest& req)
885 876
   DBG("ConferenceDialog::onSipRequest: remote_party = %s\n",dlg.remote_party.c_str());
886 877
   DBG("ConferenceDialog::onSipRequest: remote_tag = %s\n",dlg.remote_tag.c_str());
887 878
 
888
-  //string body;
889
-  //int local_port = RTPStream()->getLocalPort();
890
-  //sdp.genRequest(AmConfig::LocalIP,local_port,body);
891
-  dlg.sendRequest("INVITE",
892
- 		  "","",//"application/sdp",body,
893
- 		  "");
879
+  dlg.sendRequest(SIP_METH_INVITE);
894 880
 
895 881
   transfer_req.reset(new AmSipRequest(req));
896 882
 
... ...
@@ -42,8 +42,6 @@
42 42
 #define MESSAGE_400 "Bad Request"
43 43
 #define MESSAGE_500 "Server Internal Error"
44 44
 
45
-#define separator ","
46
-
47 45
 typedef enum auth_result {
48 46
 	ERROR = -2 ,		/* Error occurred, a reply has been 
49 47
 						   sent out -> return 0 to the ser core */
... ...
@@ -26,12 +26,16 @@
26 26
  */
27 27
 #include "DSMChartReader.h"
28 28
 #include "log.h"
29
+#include "AmUtils.h"
29 30
 
30 31
 #include <dlfcn.h> // dlopen & friends
31 32
 
32 33
 #include <vector>
33 34
 using std::vector;
34 35
 
36
+#include <string>
37
+using std::string;
38
+
35 39
 DSMChartReader::DSMChartReader() {
36 40
 }
37 41
 
... ...
@@ -43,7 +47,7 @@ bool DSMChartReader::is_wsp(const char c) {
43 47
 }
44 48
 
45 49
 bool DSMChartReader::is_snt(const char c) {
46
-  return c== ';' || c == '{' || c == '}';
50
+  return c== ';' || c == '{' || c == '}' || c == '[' || c == ']';
47 51
 }
48 52
 
49 53
 string DSMChartReader::getToken(string str, size_t& pos) {
... ...
@@ -128,6 +132,74 @@ DSMAction* DSMChartReader::actionFromToken(const string& str) {
128 132
   return NULL;
129 133
 }
130 134
 
135
+DSMFunction* DSMChartReader::functionFromToken(const string& str) {
136
+  string cmd;
137
+  size_t b_pos = str.find('(');
138
+  if (b_pos != string::npos) {
139
+    cmd = str.substr(0, b_pos);
140
+  } else {
141
+    return NULL;
142
+  }
143
+
144
+  for (vector<DSMFunction*>::iterator it=funcs.begin(); it!= funcs.end(); it++) {
145
+    if((*it)->name == cmd) {
146
+        DBG("found function '%s' in fuction list\n", cmd.c_str());
147
+        return *it;
148
+    }
149
+  }
150
+  return NULL;
151
+}
152
+
153
+bool DSMChartReader::forFromToken(DSMArrayFor& af, const string& token) {
154
+  string forhdr = token;
155
+  if (forhdr.length() < 2 || forhdr[0] != '(' || forhdr[forhdr.length()-1] != ')') {
156
+    ERROR("syntax error in 'for %s': expected 'for (x in array)'\n",
157
+	  forhdr.c_str());
158
+    return false;
159
+  }
160
+  forhdr = forhdr.substr(1, forhdr.length()-2);
161
+  // q&d
162
+  vector<string> forh_v = explode(forhdr, " in ");
163
+  if (forh_v.size() != 2) {
164
+    ERROR("syntax error in 'for %s': expected 'for (x in array)' "
165
+	  "or 'for (k,v in struct)'\n",
166
+	  forhdr.c_str());
167
+    return false;
168
+  }
169
+
170
+  vector<string> kv = explode(forh_v[0], ",");
171
+  if (kv.size() == 2) {
172
+    af.for_type = DSMArrayFor::Struct;
173
+    af.k = kv[0];
174
+    af.v = kv[1];
175
+    af.array_struct = forh_v[1];
176
+    DBG("for (%s,%s in %s) {\n", af.k.c_str(), af.v.c_str(), af.array_struct.c_str());
177
+  } else if (forh_v[1].length() > 7 &&
178
+	forh_v[1].substr(0,6)=="range(" &&
179
+	forh_v[1][forh_v[1].length()-1] == ')') {
180
+    af.for_type = DSMArrayFor::Range;
181
+    string range_s = forh_v[1].substr(6, forh_v[1].length()-7);
182
+    vector<string> range_v = explode(range_s, ",");
183
+    if (range_v.size() == 2) {
184
+      af.v = trim(range_v[0], " ");
185
+      af.array_struct = trim(range_v[1], " ");
186
+    } else {
187
+      af.v = "0";
188
+      af.array_struct = trim(range_s, " ");
189
+    }
190
+    af.k = forh_v[0];
191
+    DBG("for (%s in range(%s, %s) {\n",
192
+	af.k.c_str(), af.v.c_str(), af.array_struct.c_str());
193
+  } else {
194
+    af.for_type = DSMArrayFor::Array;
195
+    af.array_struct = forh_v[1];
196
+    af.k = forh_v[0];
197
+    DBG("for (%s in %s) {\n", af.k.c_str(), af.array_struct.c_str());
198
+  }
199
+
200
+  return true;
201
+}
202
+
131 203
 DSMCondition* DSMChartReader::conditionFromToken(const string& str, bool invert) {
132 204
   for (vector<DSMModule*>::iterator it=
133 205
 	 mods.begin(); it!= mods.end(); it++) {
... ...
@@ -205,6 +277,11 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
205 277
       continue;
206 278
     }
207 279
     
280
+    if (token == "function") {
281
+      stack.push_back(new DSMFunction());
282
+      continue;
283
+    }
284
+
208 285
     if (token == "initial") {
209 286
       stack.push_back(new AttribInitial());
210 287
       continue;
... ...
@@ -219,16 +296,77 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
219 296
       stack.push_back(new DSMTransition());
220 297
       continue;
221 298
     }
222
-    
299
+
223 300
     if (stack.empty()) {
224 301
       if (token == ";")
225
-	continue;
302
+      	continue;
226 303
       ERROR("Without context I do not understand '%s'\n", token.c_str());
227 304
       return false;
228 305
     }
229 306
 
230 307
     DSMElement* stack_top = &(*stack.back());
231 308
     
309
+    DSMFunction* f = dynamic_cast<DSMFunction*>(stack_top);
310
+    
311
+    if (f) {
312
+      if (f->name.length()==0) {
313
+        size_t b_pos = token.find('(');
314
+        if (b_pos != string::npos) {
315
+          f->name = token.substr(0, b_pos);
316
+          continue;
317
+        } else {
318
+          ERROR("Parse error -- function declarations must have a name followed "
319
+		"by parentheses, e.g., 'function foo()'\n");
320
+          return false;
321
+        }
322
+      }
323
+          
324
+      if (token == "{") {
325
+      	stack.push_back(new ActionList(ActionList::AL_func));
326
+      	continue;
327
+      }
328
+      if (token == ";") {
329
+        owner->transferElem(f);
330
+        funcs.push_back(f);
331
+      	DBG("Adding DSMFunction '%s' to funcs\n", f->name.c_str());
332
+       	continue;
333
+      }
334
+      
335
+      DBG("Unknown token: %s\n", token.c_str());
336
+      return false;
337
+   }
338
+    
339
+    DSMConditionTree* ct = dynamic_cast<DSMConditionTree*>(stack_top);
340
+    if (ct) {
341
+      if (token == "[") {
342
+	DSMConditionList* cl = new DSMConditionList();
343
+	cl->is_if = true;
344
+	stack.push_back(cl);
345
+	continue;
346
+       }
347
+      if (token == "{") {
348
+      	stack.push_back(new ActionList(ActionList::AL_if));
349
+      	continue;
350
+      }
351
+
352
+      if (token == ";" || token == "}") {
353
+	stack.pop_back();
354
+	ActionList* al = dynamic_cast<ActionList*>(&(*stack.back()));
355
+	if (al) {
356
+	  owner->transferElem(ct);
357
+	  al->actions.push_back(ct);
358
+	} else {
359
+	  ERROR("no ActionList for DSMConditionTree\n");
360
+	  delete al;
361
+	  return false;
362
+	}
363
+       	continue;
364
+      }
365
+
366
+      ERROR("syntax error: got '%s' without context\n", token.c_str());
367
+      return false;
368
+    }
369
+    
232 370
     State* state = dynamic_cast<State*>(stack_top);
233 371
     if (state) {
234 372
       if (!state->name.length()) {
... ...
@@ -237,6 +375,7 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
237 375
 	continue;
238 376
       }
239 377
       if (token == "enter") {
378
+      DBG("adding 'enter' actions for state '%s'\n", state->name.c_str());
240 379
 	stack.push_back(new ActionList(ActionList::AL_enter));
241 380
 	continue;
242 381
       }
... ...
@@ -268,16 +407,64 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
268 407
       }
269 408
       if (token == "{") {
270 409
 	continue;
271
-      } 
410
+      }
411
+  	
272 412
       if ((token == "}") || (token == "->")) {
273
-	stack.pop_back();
274
-	if (stack.empty()) {
413
+      	stack.pop_back();
414
+        if (stack.empty()) {
275 415
 	  ERROR("no item for action list\n");
276 416
 	  delete al;
277 417
 	  return false;
418
+      	}
419
+
420
+	if (al->al_type == ActionList::AL_func) {
421
+	  DSMFunction* f = dynamic_cast<DSMFunction*>(&(*stack.back()));
422
+	  if (!f) {
423
+	    ERROR("no DSMFunction for action list\n");
424
+	    delete al;
425
+	    return false;
426
+	  }
427
+	  f->actions = al->actions;
428
+	  delete al;
429
+	  continue;
278 430
 	}
279
-	
280
-	if (al->al_type == ActionList::AL_enter || 
431
+
432
+	if (al->al_type == ActionList::AL_if ||
433
+	    al->al_type == ActionList::AL_else) {
434
+	  DSMConditionTree* ct = dynamic_cast<DSMConditionTree*>(&(*stack.back()));
435
+	  if (!ct) {
436
+	    ERROR("no DSMConditionTree for action list\n");
437
+	    delete al;
438
+	    return false;
439
+	  }
440
+
441
+	  if (al->al_type == ActionList::AL_if)
442
+	    ct->run_if_true = al->actions;
443
+	  else
444
+	    ct->run_if_false = al->actions;
445
+
446
+	  stack.pop_back();
447
+	  ActionList* al_parent = dynamic_cast<ActionList*>(&(*stack.back()));
448
+	  if (al_parent) {
449
+	    owner->transferElem(ct);
450
+	    al_parent->actions.push_back(ct);
451
+	  } else {
452
+	    ERROR("no ActionList for DSMConditionTree\n");
453
+	    delete al;
454
+	    return false;
455
+	  }
456
+	  if (al->al_type == ActionList::AL_if)
457
+	    DBG("} // end if\n");
458
+	  else
459
+	    DBG("} // end else\n");
460
+
461
+	  delete al;
462
+
463
+	  continue;
464
+
465
+	}
466
+
467
+	if (al->al_type == ActionList::AL_enter ||
281 468
 	    al->al_type == ActionList::AL_exit) {
282 469
 	  State* s = dynamic_cast<State*>(&(*stack.back()));
283 470
 	  if (!s) {
... ...
@@ -285,11 +472,17 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
285 472
 	    delete al;
286 473
 	    return false;
287 474
 	  }
288
-	  if (al->al_type == ActionList::AL_enter)
475
+
476
+	  if (al->al_type == ActionList::AL_enter) {
289 477
 	    s->pre_actions = al->actions;
290
-	  else if (al->al_type == ActionList::AL_exit)
478
+	  } else if (al->al_type == ActionList::AL_exit) {
291 479
 	    s->post_actions = al->actions;
292
-	} else if (al->al_type == ActionList::AL_trans) {
480
+          }
481
+	  delete al;
482
+	  continue;
483
+        }
484
+
485
+	if (al->al_type == ActionList::AL_trans) {
293 486
 	  DSMTransition* t = dynamic_cast<DSMTransition*>(&(*stack.back()));
294 487
 	  if (!t) {
295 488
 	    ERROR("no DSMTransition for action list\n");
... ...
@@ -297,80 +490,177 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart,
297 490
 	    return false;
298 491
 	  }
299 492
 	  t->actions = al->actions;
300
-	} else {
301
-	  ERROR("internal: unknown transition list type\n");
493
+	  delete al;
494
+	  continue;
495
+	}
496
+
497
+	if (al->al_type == ActionList::AL_for) {
498
+	  DSMArrayFor* af = dynamic_cast<DSMArrayFor*>(&(*stack.back()));
499
+	  if (!af) {
500
+	    ERROR("no DSMArrayFor for action list\n");
501
+	    delete al;
502
+	    return false;
503
+	  }
504
+	  af->actions = al->actions;
505
+	  
506
+	  stack.pop_back();
507
+	  
508
+	  ActionList* b_al = dynamic_cast<ActionList*>(&(*stack.back()));
509
+	  if (!b_al) {
510
+	    ERROR("internal error: no ActionList for 'for'\n");
511
+	    return false;
512
+	  }
513
+	  b_al->actions.push_back(af);
514
+	  DBG("} // end for (%s%s in %s) {\n",
515
+	      af->k.c_str(), af->v.empty() ? "" : (","+af->v).c_str(),
516
+	      af->array_struct.c_str());
517
+	  delete al;
518
+	  continue;
519
+	}
520
+
521
+	ERROR("internal: unknown transition list type\n");
522
+	return false;
523
+      }
524
+
525
+      if (token == "if") {
526
+	DBG("if ...\n");
527
+	// start condition tree
528
+	stack.push_back(new DSMConditionTree());
529
+	DSMConditionList* cl = new DSMConditionList();
530
+	cl->is_if = true;
531
+	stack.push_back(cl);
532
+	continue;
533
+      }
534
+
535
+      if (token == "else") {
536
+	DBG(" ... else ...\n");
537
+	DSMConditionTree* ct = dynamic_cast<DSMConditionTree*>(al->actions.back());
538
+	if (NULL == ct) {
539
+	  ERROR("syntax error: else without if block\n");
540
+	  return false;
541
+	}
542
+	stack.push_back(ct);
543
+	stack.push_back(new ActionList(ActionList::AL_else));
544
+	al->actions.pop_back();
545
+	continue;
546
+      }
547
+
548
+      if (token.substr(0, 3) == "for") {
549
+	// token is for loop
550
+	DSMArrayFor* af = new DSMArrayFor();
551
+	if (token.length() > 3) {
552
+	  if (!forFromToken(*af, token.substr(3)))
553
+	    return false;
554
+	}
555
+
556
+	stack.push_back(af);
557
+	continue;
558
+      }
559
+
560
+      DSMFunction* f = functionFromToken(token);
561
+      if (f) {
562
+	DBG("adding actions from function '%s'\n", f->name.c_str());
563
+	DBG("al.size is %zd before", al->actions.size());
564
+	for (vector<DSMElement*>::iterator it=f->actions.begin();
565
+	     it != f->actions.end(); it++) {
566
+	  DSMElement* a = *it;
567
+	  owner->transferElem(a);
568
+	  al->actions.push_back(a);
302 569
 	}
303
-	delete al;
570
+	DBG("al.size is %zd after", al->actions.size());
304 571
 	continue;
305 572
       }
306 573
 
307
-      // token is action
308
-      //       DBG("adding action '%s'\n", token.c_str());
574
+      DBG("adding action '%s'\n", token.c_str());
309 575
       DSMAction* a = actionFromToken(token);
310 576
       if (!a)
311 577
 	return false;
312 578
       owner->transferElem(a);
313 579
       al->actions.push_back(a);
314
-      continue;
315
-    }
580
+    } // actionlist
316 581
     
582
+     
317 583
     DSMConditionList* cl = dynamic_cast<DSMConditionList*>(stack_top);
318 584
     if (cl) {
319
-      if (token == ";")
320
-	continue;
585
+      if (token == ";" || token == "[")
586
+        continue;
321 587
 
322
-      if ((token == "{") || (token == "}")) {
323
-	// readability
324
-	continue;
325
-      } 
326
-
327
-      if ((token == "/") || (token == "->"))  {
328
-	// end of condition list
588
+      if (cl->is_if && token == "{") {
589
+	// end of condition list for if
329 590
 	stack.pop_back();
330
-	if (stack.empty()) {
331
-	  ERROR("no transition to apply conditions to\n");
332
-	  delete cl;
333
-	  return false;
334
-	}
335
-	DSMTransition* tr = dynamic_cast<DSMTransition*>(&(*stack.back()));
336