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