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