1#pike __REAL_VERSION__
2constant __version=0.1;
3constant __author="Marc Dirix <marc@electronics-design.nl";
4constant __components=({"Public.pmod/Protocols.pmod/Yate.pmod"});
5
6/*
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22#define YATE_DEBUG
23
24class Engine
25{
26   static Stdio.FILE yate_socket;
27
28   void create(void|string|object server, void|string role, void|int port)
29   {
30
31      if(server)
32      {
33         if(objectp(server))
34            yate_socket=server;
35         else if(port)
36         {
37            if(!yate_socket->connect(server, port))
38   	    error("Cannot open connection to: %s:%d\n",server,port);
39         }
40         else
41         {
42            if(!yate_socket->connect_unix(server))
43   	    error("Cannot open connection to: %s\n",server);
44         }
45      }
46      if(role && sizeof(role))
47         _yate_print("%%%%>connect:%s\n",role);
48      return;
49   }
50
51   /**
52    * Static function to output astring to Yate's stderr or logfile
53    * only if debugging was enabled.
54    * @param str String to output if yate_debug is set.
55   **/
56   void debug(string str, mixed ... args)
57   {
58   #ifdef YATE_DEBUG
59      this->output(str, @args);
60   #endif
61      return;
62   }
63
64   /**
65    * Private function to convert a string to its Yate escaped format
66    * @param str String to escape
67    * @return Yate escaped string
68    */
69   private string _yate_escape(string str)
70   {
71       return replace(str,
72       ({"%" , "\000" , "\001" , "\002" , "\003" , "\004" , "\005" ,
73       "\006" , "\007" , "\008" , "\009" , "\010" , "\011" , "\012" ,
74       "\013" , "\014" , "\015" , "\016" , "\017" , "\018" , "\019" ,
75       "\020" , "\021" , "\022" , "\023" , "\024" , "\025" , "\026" ,
76       "\027" , "\028" , "\029" , "\030" , "\031" , "\072" }),
77       ({"%%" , "%\100" , "%\101" , "%\102" , "%\103" , "%\104" , "%\105" ,
78       "%\106" , "%\107" , "%\108" , "%\109" , "%\110" , "%\111" , "%\112" ,
79       "%\113" , "%\114" , "%\115" , "%\116" , "%\117" , "%\118" , "%\119" ,
80       "%\120" , "%\121" , "%\122" , "%\123" , "%\124" , "%\125" , "%\126" ,
81       "%\127" , "%\128" , "%\129" , "%\130" , "%\131" , "%\172" }));
82   }
83
84   /**
85    * Static function to convert an Yate escaped string back to string
86    * @param str Yate escaped String
87    * @return unescaped string
88    */
89   private string _yate_unescape(string str)
90   {
91      string outp="";
92      for(int a=0 ; a < sizeof(str) ; a++ )
93      {
94         int c=str[a];
95         if(c == '%')
96	 {
97	    a++;
98	    c=str[a];
99	    if ( c != '%' )
100	       c = c-64;
101	 }
102	 outp+=sprintf("%c",c);
103      }
104      return outp;
105   }
106
107   /*
108    * install a Yate message handler
109    * @param name Name of the messages to handle
110    * @param priority (optional) Priority to insert in chain, default 100
111    * @param filtname (optional) Name of parameter to filter for
112    * @param filtvalue (optional) Matching value of filtered parameter
113    */
114   void install(string name, void|int priority, void|string filter_name,
115                                                       void|string filter_value)
116   {
117      string filter="";
118      name=_yate_escape(name);
119      if(zero_type(priority))
120         int priority=100;
121      if(filter_name)
122         filter=":"+_yate_escape(filter_name)+":"+_yate_escape(filter_value||"");
123      _yate_print("%%%%>install:%d:%s:%s\n",priority,name,filter);
124   }
125
126   /*
127    * Uninstall a Yate message handler
128    * @param name Name of the messages to handle
129    */
130   void uninstall(string name)
131   {
132      name=_yate_escape(name);
133      _yate_print("%%%%>install:%s\n",name);
134   }
135
136
137   /*
138    * Install a Yate message watcher
139    * @param name Name of the messages to watch
140    */
141   void watch(string name)
142   {
143      name=_yate_escape(name);
144      _yate_print("%%%%>watch:%s\n",name);
145   }
146
147   /*
148    * Uninstall a Yate message watcher
149    * @param name Name of the messages to stop watching
150    */
151   void unwatch(string name)
152   {
153      name=_yate_escape(name);
154      _yate_print("%%%%>unwatch:%s\n",name);
155   }
156
157   /*
158    * Dispatch the message to Yate for handling
159    * @param message Message object to dispatch
160    */
161   void dispatch(mapping message)
162   {
163      if(!message->type)
164         message->type="outgoing";
165      else if(message->type != "outgoing")
166      {
167         output("Pike bug: attempt to dispatch message type: %s\n",message->type);
168         return UNDEFINED;
169      }
170      string paramstring = "";
171      int origin=time();
172      if(message->params)
173         paramstring=_yate_mapping2string(message->params);
174      string id;
175      if(!message->id)
176      {
177         random_seed(getpid());
178	 id=(string) random(99999999);
179      }
180      _yate_print("%%%%>message:%s:%d:%s:%s%s\n",_yate_escape(id),origin,
181                  _yate_escape(message->name),_yate_escape(message->retval||""),
182   	       paramstring);
183   }
184
185
186   void acknowledge(mapping message)
187   {
188      if(message->type != "incoming")
189      {
190       output("Pike bug: attempt to acknowledge message type: %s\n",message->type);
191       return;
192      }
193      if(!message->handled)
194         message->handled="false";
195      string paramstring = "";
196      if(message->params)
197         paramstring=_yate_mapping2string(message->params);
198      _yate_print("%%%%<message:%s:%s:%s:%s%s\n",_yate_escape(message->id),
199                  _yate_escape(message->handled),_yate_escape(message->name),
200   	       _yate_escape(message->retval||""),paramstring);
201   }
202
203   void setlocal(string name, string value)
204   {
205      _yate_print("%%%%>setlocal:%s:%s\n",_yate_escape(name),_yate_escape(value));
206   }
207
208   mapping getevent()
209   {
210      string rawmessage;
211      if(yate_socket)
212      {
213        if(!(rawmessage=yate_socket->gets()))
214           return UNDEFINED;
215      }
216      else
217      {
218        if(!(rawmessage=Stdio.stdin->gets()))
219           return UNDEFINED;
220      }
221      rawmessage=replace(rawmessage,"\n","");
222      if(rawmessage == "")
223         return (["type":"empty"]);
224      array message_parts = rawmessage/":";
225      mapping message=([]);
226      switch (message_parts[0])
227      {
228         case "%%>message":
229        /*incoming message str_id:int_time:str_name:str_retval[:key=value...] */
230            message->ack=0;
231#ifdef YATE_DEBUG
232	    message->raw=rawmessage;
233#endif
234            message->type="incoming";
235            message->id=_yate_unescape(message_parts[1]);
236   	    message->retval=_yate_unescape(message_parts[4]);
237   	    message->name=_yate_unescape(message_parts[3]);
238   	    message->origin=0+(int) message_parts[2];
239   	    if(sizeof(message_parts) > 4)
240   	       message->params=_yate_array2mapping(message_parts[5..]);
241   	    break;
242         case "%%<message":
243            message->type="answer";
244   	    message->id=_yate_unescape(message_parts[1]);
245   	    message->retval=_yate_unescape(message_parts[4]);
246   	    message->name=_yate_unescape(message_parts[3]);
247   	    message->handled=_yate_unescape(message_parts[2]);
248   	    if(sizeof(message_parts) > 4)
249   	       message->params=_yate_array2mapping(message_parts[5..]);
250   	    break;
251         case "%%<install":
252         case "%%<uninstall":
253         /* [un]install answer num_priority:str_name:bool_success */
254            message->type=message_parts[0][3..];
255            message->name=_yate_unescape(message_parts[2]);
256   	    message->handled=_yate_unescape(message_parts[3]);
257   	    message->priority=(int) message_parts[1];
258   	    break;
259         case "%%<watch":
260         case "%%<unwatch":
261         /* [un]watch answer str_name:bool_success */
262            message->type=message_parts[0][3..];
263   	    message->name=_yate_unescape(message_parts[1]);
264   	    message->handled=_yate_unescape(message_parts[2]);
265   	    break;
266         case "%%<setlocal":
267         /* local parameter answer str_name:str_value:bool_success */
268            message->type=message_parts[0][3..];
269   	    message->name=_yate_unescape(message_parts[1]);
270   	    message->retval=_yate_unescape(message_parts[2]);
271   	    message->handled=_yate_unescape(message_parts[3]);
272   	    break;
273         case "Error in":
274            message->type="error";
275   	    break;
276         default:
277            message->type="error";
278            output("Unable to decode: %s",rawmessage);
279      }
280      return message;
281   }
282
283   void output(string str, mixed ... args)
284   {
285     if(yate_socket)
286        _yate_print("%%%%>output:"+str+"\n", @args);
287     else
288        Stdio.stderr->write(str+"\n", @args);
289   }
290
291   /* Internal function */
292   private string _yate_mapping2string(mapping params)
293   {
294      string param="";
295      foreach(indices(params), string key)
296      {
297        param+=":" + _yate_escape(key) + "=" + _yate_escape(params[key]);
298      }
299      return param;
300   }
301   /* Internal function */
302   private mapping _yate_array2mapping(array message_params)
303   {
304      mapping params=([]);
305      foreach(message_params, string param)
306      {
307        if(has_value(param,"="))
308        {
309          array p = param/"=";
310          params+=([_yate_unescape(p[0]):_yate_unescape(p[1])]);
311        }
312        else
313           params+=([_yate_unescape(param):""]);
314      }
315      return params;
316   }
317   /* Internal function */
318   private void _yate_print(string str, mixed ... args)
319   {
320      if(yate_socket)
321         yate_socket->write(sprintf(str,@args));
322      else
323         Stdio.stdout->write(sprintf(str,@args));
324   }
325};
326
327