Source cleanup.
[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 "main.h"
17
18 /* note: recording log is written at endpoint */
19
20 /*
21  * initialize vbox port
22  */
23 VBoxPort::VBoxPort(int type, struct port_settings *settings) : Port(type, "vbox", settings)
24 {
25         p_vbox_timeout = 0;
26         p_vbox_announce_fh = -1;
27         p_vbox_audio_start = 0;
28         p_vbox_audio_transferred = 0;
29         p_vbox_record_start = 0;
30         p_vbox_record_limit = 0;
31 }
32
33
34 /*
35  * destructor
36  */
37 VBoxPort::~VBoxPort()
38 {
39         if (p_vbox_announce_fh >= 0) {
40                 close(p_vbox_announce_fh);
41                 p_vbox_announce_fh = -1;
42                 fhuse--;
43         }
44 }
45
46
47 static void vbox_trace_header(class VBoxPort *vbox, const char *message, int direction)
48 {
49         /* init trace with given values */
50         start_trace(-1,
51                     NULL,
52                     vbox?numberrize_callerinfo(vbox->p_callerinfo.id, vbox->p_callerinfo.ntype, options.national, options.international):NULL,
53                     vbox?vbox->p_dialinginfo.id:NULL,
54                     direction,
55                     CATEGORY_CH,
56                     vbox?vbox->p_serial:0,
57                     message);
58 }
59
60
61 /*
62  * handler of vbox
63  */
64 int VBoxPort::handler(void)
65 {
66         struct lcr_msg  *message;
67         unsigned int    tosend;
68         unsigned char   buffer[ISDN_TRANSMIT];
69         time_t          currenttime;
70         class Endpoint  *epoint;
71         int             ret;
72
73         if ((ret = Port::handler()))
74                 return(ret);
75
76         if (p_vbox_record_start && p_vbox_record_limit) {
77                 time(&currenttime);
78                 if (currenttime > (p_vbox_record_limit+p_vbox_record_start)) {
79                         while(p_epointlist) {
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                                 vbox_trace_header(this, "RELEASE from VBox (recoding limit reached)", DIRECTION_IN);
86                                 add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
87                                 add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
88                                 end_trace();
89                                 /* remove epoint */
90                                 free_epointlist(p_epointlist);
91                         }
92                         /* recording is close during destruction */
93                         delete this;
94                         return(-1); /* must return because port is gone */
95                 }
96         }
97
98         /* set time the first time */
99         if (p_vbox_audio_start < 1) {
100                 p_vbox_audio_start = now_d;
101                 return(0);
102         }
103         
104         /* calculate the number of bytes */
105         tosend = (unsigned int)((now_d-p_vbox_audio_start)*8000) - p_vbox_audio_transferred;
106
107         /* wait for more */
108         if (tosend < sizeof(buffer))
109                 return(0);
110         tosend = sizeof(buffer);
111
112         /* add the number of samples elapsed */
113         p_vbox_audio_transferred += tosend;
114
115         /* if announcement is currently played, send audio data */
116         if (p_vbox_announce_fh >=0) {
117                 tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1);
118                 if (tosend <= 0) {
119                         /* end of file */
120                         close(p_vbox_announce_fh);
121                         p_vbox_announce_fh = -1;
122                         fhuse--;
123
124                         time(&currenttime);
125                         p_vbox_record_start = currenttime;
126
127                         /* connect if not already */
128                         epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist));
129                         if (epoint) {
130                                 /* if we sent our announcement during ringing, we must now connect */
131                                 if (p_vbox_ext.vbox_free) {
132                                         /* send connect message */
133                                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
134                                         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
135                                         message_put(message);
136                                         vbox_trace_header(this, "CONNECT from VBox (announcement is over)", DIRECTION_IN);
137                                         end_trace();
138                                         new_state(PORT_STATE_CONNECT);
139                                 }
140                         }
141
142                         /* start recording, if not already */
143                         if (p_vbox_mode == VBOX_MODE_NORMAL) {
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                                 vbox_trace_header(this, "RECORDING (announcement is over)", DIRECTION_IN);
147                                 end_trace();
148                         } else // else!!
149                         if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT) {
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                                 vbox_trace_header(this, "RELEASE from VBox (after annoucement)", DIRECTION_IN);
156                                 add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
157                                 add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
158                                 end_trace();
159                                 /* recording is close during destruction */
160                                 delete this;
161                                 return(-1); /* must return because port is gone */
162                         }
163                 } else {
164                         if (p_record)
165                                 record(buffer, tosend, 0); // from down
166                         message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
167                         message->param.data.len = tosend;
168                         memcpy(message->param.data.data, buffer, tosend);
169                         message_put(message);
170                 }
171         }
172
173         return(1);
174 }
175
176
177 /*
178  * endpoint sends messages to the vbox port
179  */
180 int VBoxPort::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
181 {
182         struct lcr_msg *message;
183         class Endpoint *epoint;
184         char filename[256], *c;
185
186         if (Port::message_epoint(epoint_id, message_id, param))
187                 return(1);
188
189         epoint = find_epoint_id(epoint_id);
190         if (!epoint) {
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                 case MESSAGE_DATA:
197                 record(param->data.data, param->data.len, 1); // from up
198                 return(1);
199
200                 case MESSAGE_DISCONNECT: /* call has been disconnected */
201                 new_state(PORT_STATE_OUT_DISCONNECT);
202                 vbox_trace_header(this, "DISCONNECT to VBox", DIRECTION_OUT);
203                 add_trace("cause", "value", "%d", param->disconnectinfo.cause);
204                 add_trace("cause", "location", "%d", param->disconnectinfo.location);
205                 end_trace();
206
207                 while(p_epointlist) {
208                         message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
209                         message->param.disconnectinfo.cause = CAUSE_NORMAL;
210                         message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
211                         message_put(message);
212                         vbox_trace_header(this, "RELEASE from VBox (after disconnect)", DIRECTION_IN);
213                         add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
214                         add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
215                         end_trace();
216                         /* remove epoint */
217                         free_epointlist(p_epointlist);
218                 }
219                 /* recording is close during destruction */
220                 delete this;
221                 return(-1); /* must return because port is gone */
222                 break;
223
224                 case MESSAGE_RELEASE: /* release vbox port */
225                 vbox_trace_header(this, "RELEASE to VBox", DIRECTION_OUT);
226                 add_trace("cause", "value", "%d", param->disconnectinfo.cause);
227                 add_trace("cause", "location", "%d", param->disconnectinfo.location);
228                 end_trace();
229
230                 /* we are done */
231                 /* recording is close during destruction */
232                 delete this;
233                 return(-1); /* must return because port is gone */
234                 break;
235
236                 case MESSAGE_SETUP: /* dial-out command received from epoint, answer with connect */
237                 /* get apppbx */
238                 memcpy(&p_vbox_ext, &((class EndpointAppPBX *)(epoint->ep_app))->e_ext, sizeof(p_vbox_ext));
239                 /* extract optional announcement file */
240                 if ((c = strchr(param->setup.dialinginfo.id, ','))) {
241                         if (c[1] == '/')
242                                 SPRINT(filename, c+1);
243                         else
244                                 SPRINT(filename, "%s/%s/vbox/%s", EXTENSION_DATA, p_vbox_ext.number);
245                         *c = '\0';
246                 } else {
247                         SPRINT(filename, "%s/%s/vbox/announcement", EXTENSION_DATA, p_vbox_ext.number);
248                 }
249                 vbox_trace_header(this, "SETUP to VBox", DIRECTION_OUT);
250                 add_trace("from", "id", "%s", param->setup.callerinfo.id);
251                 add_trace("to", "box", "%s", param->setup.dialinginfo.id);
252                 end_trace();
253                 memcpy(&p_dialinginfo, &param->setup.dialinginfo, sizeof(p_dialinginfo));
254                 memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
255                 memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
256                 /* link relation */
257                 if (p_epointlist)
258                         FATAL("PORT(%s) Epoint pointer is set in idle state, how bad!!\n", p_name);
259                 epointlist_new(epoint_id);
260
261                 /* copy setup infos to port */
262                 SCPY(p_vbox_extension, param->setup.dialinginfo.id);
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_ext.vbox_free) {
272                         /* send connect message */
273                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_CONNECT);
274                         memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
275                         message_put(message);
276                         vbox_trace_header(this, "CONNECT from VBox (after setup)", DIRECTION_IN);
277                         end_trace();
278                         new_state(PORT_STATE_CONNECT);
279                 } else {
280                         /* send alerting message */
281                         message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
282                         message_put(message);
283                         vbox_trace_header(this, "ALERTING from VBox (play announcement before connect)", DIRECTION_IN);
284                         end_trace();
285                         new_state(PORT_STATE_IN_ALERTING);
286                 }
287
288                 /* play the announcement */
289                 if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0) {
290                         fhuse++;
291                 } 
292                 vbox_trace_header(this, "ANNOUNCEMENT", DIRECTION_OUT);
293                 add_trace("file", "name", "%s", filename);
294                 add_trace("file", "exists", "%s", (p_vbox_announce_fh>=0)?"yes":"no");
295                 end_trace();
296                 /* start recording if desired */
297                 p_vbox_mode = p_vbox_ext.vbox_mode;
298                 p_vbox_record_limit = p_vbox_ext.vbox_time;
299                 if (p_vbox_announce_fh<0 || p_vbox_mode==VBOX_MODE_PARALLEL) {
300                         /* recording start */
301                         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);
302                         vbox_trace_header(this, "RECORDING", DIRECTION_IN);
303                         end_trace();
304                 }
305                 break;
306
307                 default:
308                 PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
309         }
310
311         return(0);
312 }
313
314