fixes & improvements
[lcr.git] / vbox.cpp
1 /*****************************************************************************\
2 **                                                                           **
3 ** Linux Call Router                                                         **
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];
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                                 /* recording start */
145                                 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);
146                         } else // else!!
147                         if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT)
148                         {
149                                 /* send release */
150                                 message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
151                                 message->param.disconnectinfo.cause = 16;
152                                 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
153                                 message_put(message);
154                                 /* recording is close during destruction */
155                                 delete this;
156                                 return(-1); /* must return because port is gone */
157                         }
158                 } else
159                 {
160                         if (p_record)
161                                 record(buffer, tosend, 0); // from down
162                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
163                         message->param.data.len = tosend;
164                         memcpy(message->param.data.data, buffer, tosend);
165                         message_put(message);
166                 }
167         }
168
169         return(1);
170 }
171
172
173 /*
174  * endpoint sends messages to the vbox port
175  */
176 int VBoxPort::message_epoint(unsigned long epoint_id, int message_id, union parameter *param)
177 {
178         struct message *message;
179         class Endpoint *epoint;
180         char filename[256], *c;
181
182         if (Port::message_epoint(epoint_id, message_id, param))
183                 return(1);
184
185         epoint = find_epoint_id(epoint_id);
186         if (!epoint)
187         {
188                 PDEBUG(DEBUG_EPOINT|DEBUG_VBOX, "PORT(%s) no endpoint object found where the message is from.\n", p_name);
189                 return(0);
190         }
191
192         switch(message_id)
193         {
194                 case MESSAGE_DATA:
195                 record(param->data.data, param->data.len, 1); // from up
196                 return(1);
197
198                 case MESSAGE_DISCONNECT: /* call has been disconnected */
199                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received disconnect cause=%d\n", p_name, p_callerinfo.id, param->disconnectinfo.cause);
200
201                 new_state(PORT_STATE_OUT_DISCONNECT);
202
203                 while(p_epointlist)
204                 {
205                         message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
206                         message->param.disconnectinfo.cause = CAUSE_NORMAL;
207                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
208                         message_put(message);
209                         /* remove epoint */
210                         free_epointlist(p_epointlist);
211                 }
212                 /* recording is close during destruction */
213                 delete this;
214                 return(-1); /* must return because port is gone */
215                 break;
216
217                 case MESSAGE_RELEASE: /* release vbox port */
218                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received release\n", p_name, p_callerinfo.id);
219
220                 /* we are done */
221                 /* recording is close during destruction */
222                 delete this;
223                 return(-1); /* must return because port is gone */
224                 break;
225
226                 case MESSAGE_SETUP: /* dial-out command received from epoint, answer with connect */
227                 /* get apppbx */
228                 memcpy(&p_vbox_ext, &((class EndpointAppPBX *)(epoint->ep_app))->e_ext, sizeof(p_vbox_ext));
229                 /* extract optional announcement file */
230                 if ((c = strchr(param->setup.dialinginfo.id, ',')))
231                 {
232                         if (c[1] == '/')
233                                 SPRINT(filename, c+1);
234                         else
235                                 SPRINT(filename, "%s/%s/%s/vbox/%s", INSTALL_DATA, options.extensions_dir, p_vbox_ext.number);
236                         *c = '\0';
237                 } else
238                 {
239                         SPRINT(filename, "%s/%s/%s/vbox/announcement", INSTALL_DATA, options.extensions_dir, p_vbox_ext.number);
240                 }
241                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port received setup from '%s' to '%s'\n", p_name, param->setup.callerinfo.id, param->setup.dialinginfo.id);
242                 memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
243                 memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
244                 /* link relation */
245                 if (p_epointlist)
246                         FATAL("PORT(%s) Epoint pointer is set in idle state, how bad!!\n", p_name);
247                 epointlist_new(epoint_id);
248
249                 /* copy setup infos to port */
250                 SCPY(p_vbox_extension, param->setup.dialinginfo.id);
251
252                 /* create connect info */
253                 SCPY(p_connectinfo.id, p_vbox_extension);
254                 p_connectinfo.itype = INFO_ITYPE_VBOX;
255                 p_connectinfo.present = INFO_PRESENT_ALLOWED;
256                 p_connectinfo.screen = INFO_SCREEN_NETWORK;
257
258                 /* connect unless we can send announcement while ringing */
259                 if (!p_vbox_ext.vbox_free)
260                 {
261                         /* send connect message */
262                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_CONNECT);
263                         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
264                         message_put(message);
265                         new_state(PORT_STATE_CONNECT);
266                 } else 
267                 {
268                         /* send alerting message */
269                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
270                         message_put(message);
271                         new_state(PORT_STATE_IN_ALERTING);
272                 }
273
274                 /* play the announcement */
275                 if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0)
276                 {
277                         fhuse++;
278                 } 
279                 /* start recording if desired */
280                 p_vbox_mode = p_vbox_ext.vbox_mode;
281                 p_vbox_record_limit = p_vbox_ext.vbox_time;
282                 if (!p_vbox_announce_fh || p_vbox_mode==VBOX_MODE_PARALLEL)
283                 {
284                         PDEBUG(DEBUG_VBOX, "PORT(%s) parallel mode OR no announcement found at: '%s' so we start recording now.\n", p_name, filename);
285                         /* recording start */
286                         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);
287                 }
288                 break;
289
290                 default:
291                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
292         }
293
294         return(0);
295 }
296
297