backup
[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];
66         time_t          currenttime;
67         class Endpoint  *epoint;
68         int             ret;
69
70         if ((ret = Port::handler()))
71                 return(ret);
72
73         if (p_vbox_record_start && p_vbox_record_limit)
74         {
75                 time(&currenttime);
76                 if (currenttime > (p_vbox_record_limit+p_vbox_record_start))
77                 {
78                         while(p_epointlist)
79                         {
80                                 /* send release */
81                                 message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
82                                 message->param.disconnectinfo.cause = 16;
83                                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
84                                 message_put(message);
85                                 /* remove epoint */
86                                 free_epointlist(p_epointlist);
87                         }
88                         /* recording is close during destruction */
89                         delete this;
90                         return(-1); /* must return because port is gone */
91                 }
92         }
93
94         if (p_vbox_audio_start < 1)
95         {
96                 /* set time the first time */
97                 p_vbox_audio_start = now_d;
98         } else
99         {
100                 /* calculate the number of bytes */
101                 tosend = (unsigned long)((now_d-p_vbox_audio_start)*8000) - p_vbox_audio_transferred;
102
103                 /* wait for more */
104                 if (tosend < 32)
105                         return(Port::handler());
106
107                 /* too many samples, so we just process 128 bytes until the next call of handler() */
108                 if (tosend > 128)
109                         tosend = 128;
110
111                 /* dummy read, to clock record buffer */
112                 read_audio((unsigned char *)buffer, tosend, 0);
113
114                 /* add the number of samples elapsed */
115                 p_vbox_audio_transferred += tosend;
116
117                 /* if announcement is currently played, send audio data */
118                 if (p_vbox_announce_fh >=0)
119                 {
120                         tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1);
121                         if (tosend <= 0)
122                         {
123                                 /* end of file */
124                                 close(p_vbox_announce_fh);
125                                 p_vbox_announce_fh = -1;
126                                 fhuse--;
127
128                                 time(&currenttime);
129                                 p_vbox_record_start = currenttime;
130
131                                 /* connect if not already */
132                                 epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist));
133                                 if (epoint)
134                                 {
135                                         /* if we sent our announcement during ringing, we must now connect */
136                                         if (p_vbox_apppbx->e_ext.vbox_free)
137                                         {
138                                                 /* send connect message */
139                                                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
140                                                 memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
141                                                 message_put(message);
142                                                 new_state(PORT_STATE_CONNECT);
143                                         }
144                                 }
145
146                                 /* start recording, if not already */
147                                 if (p_vbox_mode == VBOX_MODE_NORMAL)
148                                 {
149                                         /* send recording start message */
150                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
151                                         message_put(message);
152                                 } else // else!!
153                                 if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT)
154                                 {
155                                         /* send release */
156                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
157                                         message->param.disconnectinfo.cause = 16;
158                                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
159                                         message_put(message);
160                                         /* recording is close during destruction */
161                                         delete this;
162                                         return(-1); /* must return because port is gone */
163                                 }
164                         } else
165                         {
166                                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
167                                 message->param.data.port_type = p_type;
168                                 message->param.data.port_id = p_serial;
169                                 message->param.data.len = tosend;
170                                 memcpy(message->param.data.data, buffer, tosend);
171                                 message_put(message);
172                         }
173                 }
174
175         }
176         return(1);
177 }
178
179
180 /*
181  * endpoint sends messages to the vbox port
182  */
183 int VBoxPort::message_epoint(unsigned long epoint_id, int message_id, union parameter *param)
184 {
185         struct message *message;
186         class Endpoint *epoint;
187         char filename[256], *c;
188         class EndpointAppPBX *eapp;
189
190         if (Port::message_epoint(epoint_id, message_id, param))
191                 return(1);
192
193         epoint = find_epoint_id(epoint_id);
194         if (!epoint)
195         {
196                 PDEBUG(DEBUG_EPOINT|DEBUG_VBOX, "PORT(%s) no endpoint object found where the message is from.\n", p_name);
197                 return(0);
198         }
199
200         switch(message_id)
201         {
202                 case MESSAGE_DISCONNECT: /* call has been disconnected */
203                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received disconnect cause=%d\n", p_name, p_callerinfo.id, param->disconnectinfo.cause);
204
205                 new_state(PORT_STATE_OUT_DISCONNECT);
206
207                 while(p_epointlist)
208                 {
209                         message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
210                         message->param.disconnectinfo.cause = CAUSE_NORMAL;
211                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
212                         message_put(message);
213                         /* remove epoint */
214                         free_epointlist(p_epointlist);
215                 }
216                 /* recording is close during destruction */
217                 delete this;
218                 return(-1); /* must return because port is gone */
219                 break;
220
221                 case MESSAGE_RELEASE: /* release vbox port */
222                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received release\n", p_name, p_callerinfo.id);
223
224                 /* we are done */
225                 /* recording is close during destruction */
226                 delete this;
227                 return(-1); /* must return because port is gone */
228                 break;
229
230                 case MESSAGE_SETUP: /* dial-out command received from epoint, answer with connect */
231                 /* get apppbx */
232                 eapp = (class EndpointAppPBX *)(epoint->ep_app);
233                 p_vbox_apppbx = eapp;
234                 /* extract optional announcement file */
235                 if ((c = strchr(param->setup.dialinginfo.number, ',')))
236                 {
237                         if (c[1] == '/')
238                                 SPRINT(filename, c+1);
239                         else
240                                 SPRINT(filename, "%s/%s/%s/vbox/%s", INSTALL_DATA, options.extensions_dir, p_vbox_apppbx->e_terminal);
241                         *c = '\0';
242                 } else
243                 {
244                         SPRINT(filename, "%s/%s/%s/vbox/announcement", INSTALL_DATA, options.extensions_dir, p_vbox_apppbx->e_terminal);
245                 }
246                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port received setup from '%s' to '%s'\n", p_name, param->setup.callerinfo.id, param->setup.dialinginfo.number);
247                 memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
248                 memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
249                 /* link relation */
250                 if (p_epointlist)
251                 {
252                         PERROR("PORT(%s) software error: epoint pointer is set in idle state, how bad!! exitting.\n", p_name);
253                         exit(-1);
254                 }
255                 if (!(epointlist_new(epoint_id)))
256                 {
257                         PERROR("no memory for epointlist\n");
258                         exit(-1);
259                 }
260
261                 /* copy setup infos to port */
262                 SCPY(p_vbox_extension, param->setup.dialinginfo.number);
263
264                 /* create connect info */
265                 SCPY(p_connectinfo.id, p_vbox_extension);
266                 p_connectinfo.itype = INFO_ITYPE_VBOX;
267                 p_connectinfo.present = INFO_PRESENT_ALLOWED;
268                 p_connectinfo.screen = INFO_SCREEN_NETWORK;
269
270                 /* connect unless we can send announcement while ringing */
271                 if (!p_vbox_apppbx->e_ext.vbox_free)
272                 {
273                         /* send connect message */
274                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_CONNECT);
275                         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
276                         message_put(message);
277                         new_state(PORT_STATE_CONNECT);
278                 } else 
279                 {
280                         /* send alerting message */
281                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
282                         message_put(message);
283                         new_state(PORT_STATE_IN_ALERTING);
284                 }
285
286                 /* start recording during announcement */
287                 /* start parallel recording if desired */
288                 p_vbox_mode = p_vbox_apppbx->e_ext.vbox_mode;
289                 p_vbox_record_limit = p_vbox_apppbx->e_ext.vbox_time;
290                 if (p_vbox_mode == VBOX_MODE_PARALLEL)
291                 {
292                         /* send recording start message */
293                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
294                         message_put(message);
295                 }
296                 /* play the announcement */
297                 if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0)
298                 {
299                         fhuse++;
300                 } else
301                 {
302                         PDEBUG(DEBUG_VBOX, "PORT(%s) no announcement found at: '%s' so we start recording now.\n", p_name, filename);
303                         /* send recording start message */
304                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_VBOX_RECORD);
305                         message_put(message);
306                 }
307                 break;
308
309                 default:
310                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
311         }
312
313         return(0);
314 }
315
316