2a9696236b2b9f83de380ef28faad858341c050c
[lcr.git] / vbox.cpp
1 /*****************************************************************************\
2 **                                                                           **
3 ** PBX4Linux                                                                 **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** answering machine port                                                    **
9 **                                                                           **
10 ** this is a child of the port class, which emulates the recorder function   **
11 ** of an answering machine                                                   **
12 ** it will directly answer to the setup message with a connect               **
13 **                                                                           **
14 \*****************************************************************************/ 
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <poll.h>
21 #include <errno.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include "main.h"
27
28 /* note: recording log is written at endpoint */
29
30 /*
31  * initialize vbox port
32  */
33 VBoxPort::VBoxPort(int type, struct port_settings *settings) : Port(type, "vbox", settings)
34 {
35         p_vbox_timeout = 0;
36         p_vbox_announce_fh = -1;
37         p_vbox_audio_start = 0;
38         p_vbox_audio_transferred = 0;
39         p_vbox_record_start = 0;
40         p_vbox_record_limit = 0;
41 }
42
43
44 /*
45  * destructor
46  */
47 VBoxPort::~VBoxPort()
48 {
49         if (p_vbox_announce_fh >= 0)
50         {
51                 close(p_vbox_announce_fh);
52                 p_vbox_announce_fh = -1;
53                 fhuse--;
54         }
55 }
56
57
58 /*
59  * handler of vbox
60  */
61 int VBoxPort::handler(void)
62 {
63         struct message  *message;
64         unsigned long   tosend;
65         signed short    buffer[128<<1];
66         time_t          currenttime;
67         class Endpoint  *epoint;
68
69         if (p_vbox_record_start && p_vbox_record_limit)
70         {
71                 time(&currenttime);
72                 if (currenttime > (p_vbox_record_limit+p_vbox_record_start))
73                 {
74                         while(p_epointlist)
75                         {
76                                 /* send release */
77                                 message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
78                                 message->param.disconnectinfo.cause = 16;
79                                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
80                                 message_put(message);
81                                 /* remove epoint */
82                                 free_epointlist(p_epointlist);
83                         }
84                         /* recording is close during destruction */
85                         delete this;
86                         return(-1); /* must return because port is gone */
87                 }
88         }
89
90         if (p_vbox_audio_start < 1)
91         {
92                 /* set time the first time */
93                 p_vbox_audio_start = now_d;
94         } else
95         {
96                 /* calculate the number of bytes */
97                 tosend = (unsigned long)((now_d-p_vbox_audio_start)*8000) - p_vbox_audio_transferred;
98
99                 /* wait for more */
100                 if (tosend < 32)
101                         return(0);
102
103                 /* too many samples, so we just process 128 bytes until the next call of handler() */
104                 if (tosend > 128)
105                         tosend = 128;
106
107                 /* dummy read, to clock record buffer */
108                 read_audio((unsigned char *)buffer, tosend, 0);
109
110                 /* add the number of samples elapsed */
111                 p_vbox_audio_transferred += tosend;
112
113                 /* if announcement is currently played, send audio data */
114                 if (p_vbox_announce_fh >=0)
115                 {
116                         /* read from announcement file */
117                         switch(p_vbox_announce_codec)
118                         {
119                                 case CODEC_LAW:
120                                 tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1);
121                                 break;
122                                 
123                                 case CODEC_MONO:
124                                 case CODEC_STEREO:
125                                 case CODEC_8BIT:
126                                 tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1);
127                                 break;
128                                 
129                                 default:
130                                 PERROR("correct codec not given.\n");
131                                 exit(-1);
132                         }
133                         if (tosend <= 0)
134                         {
135                                 /* end of file */
136                                 close(p_vbox_announce_fh);
137                                 p_vbox_announce_fh = -1;
138                                 fhuse--;
139
140                                 time(&currenttime);
141                                 p_vbox_record_start = currenttime;
142
143                                 /* connect if not already */
144                                 epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist));
145                                 if (epoint)
146                                 {
147                                         /* if we sent our announcement during ringing, we must now connect */
148                                         if (p_vbox_apppbx->e_ext.vbox_free)
149                                         {
150                                                 /* send connect message */
151                                                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
152                                                 memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
153                                                 message_put(message);
154                                                 new_state(PORT_STATE_CONNECT);
155                                         }
156                                 }
157
158                                 /* start recording, if not already */
159                                 if (p_vbox_mode == VBOX_MODE_NORMAL)
160                                 {
161                                         /* send recording start message */
162                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
163                                         message_put(message);
164                                 } else // else!!
165                                 if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT)
166                                 {
167                                         /* send release */
168                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
169                                         message->param.disconnectinfo.cause = 16;
170                                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
171                                         message_put(message);
172                                         /* recording is close during destruction */
173                                         delete this;
174                                         return(-1); /* must return because port is gone */
175                                 }
176                         } else
177                         {
178                                 switch(p_vbox_announce_codec)
179                                 {
180                                         case CODEC_LAW:
181                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
182                                         message->param.data.compressed = 1;
183                                         message->param.data.port_type = p_type;
184                                         message->param.data.port_id = p_serial;
185                                         message->param.data.len = tosend;
186                                         memcpy(message->param.data.data, buffer, tosend);
187                                         message_put(message);
188                                         break;
189
190                                         case CODEC_MONO:
191                                         case CODEC_STEREO:
192                                         case CODEC_8BIT:
193                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
194                                         message->param.data.compressed = 0;
195                                         message->param.data.port_type = p_type;
196                                         message->param.data.port_id = p_serial;
197                                         message->param.data.len = tosend<<1;
198                                         memcpy(message->param.data.data, buffer, tosend<<1);
199                                         message_put(message);
200                                         break;
201
202                                         default:
203                                         PERROR("correct announce_codec not given.\n");
204                                         exit(-1);
205                                 }
206                         }
207                 }
208
209         }
210         return(1);
211 }
212
213
214 /*
215  * endpoint sends messages to the vbox port
216  */
217 int VBoxPort::message_epoint(unsigned long epoint_id, int message_id, union parameter *param)
218 {
219         struct message *message;
220         class Endpoint *epoint;
221         char filename[256], *c;
222         class EndpointAppPBX *eapp;
223
224         epoint = find_epoint_id(epoint_id);
225         if (!epoint)
226         {
227                 PDEBUG(DEBUG_EPOINT|DEBUG_VBOX, "PORT(%s) no endpoint object found where the message is from.\n", p_name);
228                 return(0);
229         }
230
231         if (Port::message_epoint(epoint_id, message_id, param))
232                 return(1);
233
234         switch(message_id)
235         {
236                 case MESSAGE_DISCONNECT: /* call has been disconnected */
237                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received disconnect cause=%d\n", p_name, p_callerinfo.id, param->disconnectinfo.cause);
238
239                 new_state(PORT_STATE_OUT_DISCONNECT);
240
241                 while(p_epointlist)
242                 {
243                         message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
244                         message->param.disconnectinfo.cause = CAUSE_NORMAL;
245                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
246                         message_put(message);
247                         /* remove epoint */
248                         free_epointlist(p_epointlist);
249                 }
250                 /* recording is close during destruction */
251                 delete this;
252                 return(-1); /* must return because port is gone */
253                 break;
254
255                 case MESSAGE_RELEASE: /* release vbox port */
256                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received release\n", p_name, p_callerinfo.id);
257
258                 /* we are done */
259                 /* recording is close during destruction */
260                 delete this;
261                 return(-1); /* must return because port is gone */
262                 break;
263
264                 case MESSAGE_SETUP: /* dial-out command received from epoint, answer with connect */
265                 /* get apppbx */
266                 eapp = (class EndpointAppPBX *)(epoint->ep_app);
267                 p_vbox_apppbx = eapp;
268                 /* extract optional announcement file */
269                 if ((c = strchr(param->setup.dialinginfo.number, ',')))
270                 {
271                         if (c[1] == '/')
272                                 SPRINT(filename, c+1);
273                         else
274                                 SPRINT(filename, "%s/%s/%s/vbox/%s", INSTALL_DATA, options.extensions_dir, p_vbox_apppbx->e_terminal);
275                         *c = '\0';
276                 } else
277                 {
278                         SPRINT(filename, "%s/%s/%s/vbox/announcement", INSTALL_DATA, options.extensions_dir, p_vbox_apppbx->e_terminal);
279                 }
280                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port received setup from '%s' to '%s'\n", p_name, param->setup.callerinfo.id, param->setup.dialinginfo.number);
281                 memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
282                 memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
283                 /* link relation */
284                 if (p_epointlist)
285                 {
286                         PERROR("PORT(%s) software error: epoint pointer is set in idle state, how bad!! exitting.\n", p_name);
287                         exit(-1);
288                 }
289                 if (!(epointlist_new(epoint_id)))
290                 {
291                         PERROR("no memory for epointlist\n");
292                         exit(-1);
293                 }
294
295                 /* copy setup infos to port */
296                 SCPY(p_vbox_extension, param->setup.dialinginfo.number);
297
298                 /* create connect info */
299                 SCPY(p_connectinfo.id, p_vbox_extension);
300                 p_connectinfo.itype = INFO_ITYPE_VBOX;
301                 p_connectinfo.present = INFO_PRESENT_ALLOWED;
302                 p_connectinfo.screen = INFO_SCREEN_NETWORK;
303
304                 /* connect unless we can send announcement while ringing */
305                 if (!p_vbox_apppbx->e_ext.vbox_free)
306                 {
307                         /* send connect message */
308                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_CONNECT);
309                         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
310                         message_put(message);
311                         new_state(PORT_STATE_CONNECT);
312                 } else 
313                 {
314                         /* send alerting message */
315                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
316                         message_put(message);
317                         new_state(PORT_STATE_IN_ALERTING);
318                 }
319
320                 /* start recording during announcement */
321                 /* start parallel recording if desired */
322                 p_vbox_mode = p_vbox_apppbx->e_ext.vbox_mode;
323                 p_vbox_record_limit = p_vbox_apppbx->e_ext.vbox_time;
324                 if (p_vbox_mode == VBOX_MODE_PARALLEL)
325                 {
326                         /* send recording start message */
327                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
328                         message_put(message);
329                 }
330                 /* play the announcement */
331                 if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0)
332                 {
333                         fhuse++;
334                 } else
335                 {
336                         PDEBUG(DEBUG_VBOX, "PORT(%s) no announcement found at: '%s' so we start recording now.\n", p_name, filename);
337                         /* send recording start message */
338                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
339                         message_put(message);
340                 }
341                 break;
342
343                 default:
344                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
345         }
346
347         return(0);
348 }
349
350