summaryrefslogtreecommitdiffstats
path: root/vbox.cpp
blob: bc2c36915451f48172fd1af9adc36cac12a7513b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*****************************************************************************\
**                                                                           **
** Linux Call Router                                                         **
**                                                                           **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg                                              **
**                                                                           **
** answering machine port                                                    **
**                                                                           **
** this is a child of the port class, which emulates the recorder function   **
** of an answering machine                                                   **
** it will directly answer to the setup message with a connect               **
**                                                                           **
\*****************************************************************************/ 

#include "main.h"

/* note: recording log is written at endpoint */

int announce_timer(struct lcr_timer *timer, void *instance, int index);
int record_timeout(struct lcr_timer *timer, void *instance, int index);

/*
 * initialize vbox port
 */
VBoxPort::VBoxPort(int type, struct port_settings *settings) : Port(type, "vbox", settings)
{
	p_vbox_timeout = 0;
	p_vbox_announce_fh = -1;
	p_vbox_audio_start = 0;
	p_vbox_audio_transferred = 0;
	p_vbox_record_limit = 0;
	memset(&p_vbox_announce_timer, 0, sizeof(p_vbox_announce_timer));
	add_timer(&p_vbox_announce_timer, announce_timer, this, 0);
	memset(&p_vbox_record_timeout, 0, sizeof(p_vbox_record_timeout));
	add_timer(&p_vbox_record_timeout, record_timeout, this, 0);
}


/*
 * destructor
 */
VBoxPort::~VBoxPort()
{
	del_timer(&p_vbox_announce_timer);
	del_timer(&p_vbox_record_timeout);
	if (p_vbox_announce_fh >= 0) {
		close(p_vbox_announce_fh);
		p_vbox_announce_fh = -1;
		fhuse--;
	}
}


static void vbox_trace_header(class VBoxPort *vbox, const char *message, int direction)
{
	/* init trace with given values */
	start_trace(-1,
		    NULL,
		    vbox?numberrize_callerinfo(vbox->p_callerinfo.id, vbox->p_callerinfo.ntype, options.national, options.international):NULL,
		    vbox?vbox->p_dialinginfo.id:NULL,
		    direction,
		    CATEGORY_CH,
		    vbox?vbox->p_serial:0,
		    message);
}


int record_timeout(struct lcr_timer *timer, void *instance, int index)
{
	class VBoxPort *vboxport = (class VBoxPort *)instance;
	struct lcr_msg	*message;

	while(vboxport->p_epointlist) {
		/* send release */
		message = message_create(vboxport->p_serial, vboxport->p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
		message->param.disconnectinfo.cause = 16;
		message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
		message_put(message);
		vbox_trace_header(vboxport, "RELEASE from VBox (recoding limit reached)", DIRECTION_IN);
		add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
		add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
		end_trace();
		/* remove epoint */
		vboxport->free_epointlist(vboxport->p_epointlist);
	}
	/* recording is close during destruction */
	delete vboxport;
	return 0;
}

int announce_timer(struct lcr_timer *timer, void *instance, int index)
{
	class VBoxPort *vboxport = (class VBoxPort *)instance;

	/* port my self destruct here */
	vboxport->send_announcement();

	return 0;
}

void VBoxPort::send_announcement(void)
{
	struct lcr_msg	*message;
	unsigned int	tosend;
	unsigned char	buffer[ISDN_TRANSMIT];
	class Endpoint	*epoint;
	int		temp;
	struct timeval current_time;
	long long	now;  /* Time in samples */

	/* don't restart timer, if announcement is played */
	if (p_vbox_announce_fh < 0)
		return;

	gettimeofday(&current_time, NULL);
	now = (current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec)/125;

	/* set time the first time */
	if (!p_vbox_audio_start)
		p_vbox_audio_start = now - ISDN_TRANSMIT;
	
	/* calculate the number of bytes */
	tosend = (unsigned int)(now - p_vbox_audio_start) - p_vbox_audio_transferred;
	if (tosend > sizeof(buffer))
		tosend = sizeof(buffer);

	/* schedule next event */
	temp = ISDN_TRANSMIT + ISDN_TRANSMIT - tosend;
	if (temp < 0)
		temp = 0;
	schedule_timer(&p_vbox_announce_timer, 0, temp*125);

	/* add the number of samples elapsed */
	p_vbox_audio_transferred += tosend;

	/* if announcement is currently played, send audio data */
	tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1);
	if (tosend <= 0) {
		/* end of file */
		close(p_vbox_announce_fh);
		p_vbox_announce_fh = -1;
		fhuse--;

		if (p_vbox_record_limit)
			schedule_timer(&p_vbox_record_timeout, p_vbox_record_limit, 0);

		/* connect if not already */
		epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist));
		if (epoint) {
			/* if we sent our announcement during ringing, we must now connect */
			if (p_vbox_ext.vbox_free) {
				/* send connect message */
				message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
				memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
				message_put(message);
				vbox_trace_header(this, "CONNECT from VBox (announcement is over)", DIRECTION_IN);
				end_trace();
				new_state(PORT_STATE_CONNECT);
			}
		}

		/* start recording, if not already */
		if (p_vbox_mode == VBOX_MODE_NORMAL) {
			/* recording start */
			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);
			vbox_trace_header(this, "RECORDING (announcement is over)", DIRECTION_IN);
			end_trace();
		} else // else!!
		if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT) {
			/* send release */
			message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
			message->param.disconnectinfo.cause = 16;
			message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
			message_put(message);
			vbox_trace_header(this, "RELEASE from VBox (after annoucement)", DIRECTION_IN);
			add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
			add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
			end_trace();
			/* recording is close during destruction */
			delete this;
			return; /* must return because port is gone */
		}
	} else {
		if (p_record)
			record(buffer, tosend, 0); // from down
		message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
		message->param.data.len = tosend;
		memcpy(message->param.data.data, buffer, tosend);
		message_put(message);
	}
}


/*
 * endpoint sends messages to the vbox port
 */
int VBoxPort::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
{
	struct lcr_msg *message;
	class Endpoint *epoint;
	char filename[256], *c;

	if (Port::message_epoint(epoint_id, message_id, param))
		return(1);

	epoint = find_epoint_id(epoint_id);
	if (!epoint) {
		PDEBUG(DEBUG_EPOINT|DEBUG_VBOX, "PORT(%s) no endpoint object found where the message is from.\n", p_name);
		return(0);
	}

	switch(message_id) {
		case MESSAGE_DATA:
		record(param->data.data, param->data.len, 1); // from up
		return(1);

		case MESSAGE_DISCONNECT: /* call has been disconnected */
		new_state(PORT_STATE_OUT_DISCONNECT);
		vbox_trace_header(this, "DISCONNECT to VBox", DIRECTION_OUT);
		add_trace("cause", "value", "%d", param->disconnectinfo.cause);
		add_trace("cause", "location", "%d", param->disconnectinfo.location);
		end_trace();

		while(p_epointlist) {
			message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
			message->param.disconnectinfo.cause = CAUSE_NORMAL;
			message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
			message_put(message);
			vbox_trace_header(this, "RELEASE from VBox (after disconnect)", DIRECTION_IN);
			add_trace("cause", "value", "%d", message->param.disconnectinfo.cause);
			add_trace("cause", "location", "%d", message->param.disconnectinfo.location);
			end_trace();
			/* remove epoint */
			free_epointlist(p_epointlist);
		}
		/* recording is close during destruction */
		delete this;
		return(-1); /* must return because port is gone */
		break;

		case MESSAGE_RELEASE: /* release vbox port */
		vbox_trace_header(this, "RELEASE to VBox", DIRECTION_OUT);
		add_trace("cause", "value", "%d", param->disconnectinfo.cause);
		add_trace("cause", "location", "%d", param->disconnectinfo.location);
		end_trace();

		/* we are done */
		/* recording is close during destruction */
		delete this;
		return(-1); /* must return because port is gone */
		break;

		case MESSAGE_SETUP: /* dial-out command received from epoint, answer with connect */
		/* get apppbx */
		memcpy(&p_vbox_ext, &((class EndpointAppPBX *)(epoint->ep_app))->e_ext, sizeof(p_vbox_ext));
		/* extract optional announcement file */
		if ((c = strchr(param->setup.dialinginfo.id, ','))) {
			if (c[1] == '/')
				SPRINT(filename, c+1);
			else
				SPRINT(filename, "%s/%s/vbox/%s", EXTENSION_DATA, p_vbox_ext.number);
			*c = '\0';
		} else {
			SPRINT(filename, "%s/%s/vbox/announcement", EXTENSION_DATA, p_vbox_ext.number);
		}
		vbox_trace_header(this, "SETUP to VBox", DIRECTION_OUT);
		add_trace("from", "id", "%s", param->setup.callerinfo.id);
		add_trace("to", "box", "%s", param->setup.dialinginfo.id);
		end_trace();
		memcpy(&p_dialinginfo, &param->setup.dialinginfo, sizeof(p_dialinginfo));
		memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
		memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
		/* link relation */
		if (p_epointlist)
			FATAL("PORT(%s) Epoint pointer is set in idle state, how bad!!\n", p_name);
		epointlist_new(epoint_id);

		/* copy setup infos to port */
		SCPY(p_vbox_extension, param->setup.dialinginfo.id);

		/* create connect info */
		SCPY(p_connectinfo.id, p_vbox_extension);
		p_connectinfo.itype = INFO_ITYPE_VBOX;
		p_connectinfo.present = INFO_PRESENT_ALLOWED;
		p_connectinfo.screen = INFO_SCREEN_NETWORK;

		/* connect unless we can send announcement while ringing */
		if (!p_vbox_ext.vbox_free) {
			/* send connect message */
			message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_CONNECT);
			memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
			message_put(message);
			vbox_trace_header(this, "CONNECT from VBox (after setup)", DIRECTION_IN);
			end_trace();
			new_state(PORT_STATE_CONNECT);
		} else {
			/* send alerting message */
			message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
			message_put(message);
			vbox_trace_header(this, "ALERTING from VBox (play announcement before connect)", DIRECTION_IN);
			end_trace();
			new_state(PORT_STATE_IN_ALERTING);
		}

		/* play the announcement */
		if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0) {
			fhuse++;
			schedule_timer(&p_vbox_announce_timer, 0, 300000);
		} 
		vbox_trace_header(this, "ANNOUNCEMENT", DIRECTION_OUT);
		add_trace("file", "name", "%s", filename);
		add_trace("file", "exists", "%s", (p_vbox_announce_fh>=0)?"yes":"no");
		end_trace();
		/* start recording if desired */
		p_vbox_mode = p_vbox_ext.vbox_mode;
		p_vbox_record_limit = p_vbox_ext.vbox_time;
		if (p_vbox_announce_fh<0 || p_vbox_mode==VBOX_MODE_PARALLEL) {
			/* recording start */
			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);
			vbox_trace_header(this, "RECORDING", DIRECTION_IN);
			end_trace();
		}
		break;

		default:
		PDEBUG(DEBUG_VBOX, "PORT(%s) vbox port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
	}

	return(0);
}