summaryrefslogtreecommitdiffstats
path: root/src/line.h
blob: 216712aa201448eb1d70e1a2d7ca7159eb5841b5 (plain)
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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/*
    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef _LINE_H
#define _LINE_H

#include <list>
#include <string>
#include "call_history.h"
#include "dialog.h"
#include "id_object.h"
#include "phone.h"
#include "protocol.h"
#include "user.h"
#include "audio/audio_codecs.h"
#include "sockets/url.h"
#include "parser/request.h"
#include "parser/response.h"
#include "stun/stun.h"

using namespace std;

// Forward declarations
class t_dialog;
class t_phone;

// Info about the current call.
// This info can be used by the user interface to render the
// call state to the user.
class t_call_info {
public:
	mutable t_mutex	mutex;
	t_url			from_uri;
	string			from_display;
	
	// Override of display for presentation to user, e.g. name from
	// address book lookup.
	string			from_display_override;
	
	string			from_organization;
	t_url			to_uri;
	string			to_display;
	string			to_organization;
	string			subject;
	bool			dtmf_supported;
	bool			dtmf_inband; // DTMF must be sent inband
	bool			dtmf_info; // DTMF must be sent via SIP INFO
	t_hdr_referred_by	hdr_referred_by;

	// The reason phrase of the last received provisional response
	// on an outgoing INVITE.
	string		last_provisional_reason;

	t_audio_codec	send_codec;
	t_audio_codec	recv_codec;
	bool		refer_supported;
	
	t_call_info();
	t_call_info(const t_call_info&);
	t_call_info& operator=(const t_call_info&);

	void clear(void);
	
	// Get the from display name to show to the user.
	string get_from_display_presentation(void) const;
};

class t_line : public t_id_object {
	friend class t_phone;
	
private:
	t_line_state		state;
	t_line_substate		substate;
	bool			is_on_hold;
	bool			is_muted;
	bool			hide_user; // Anonymous call
	
	// Indicates if a call is a consultation for a transfer
	bool			is_transfer_consult;
	
	// The line about which this consultation handles.
	unsigned short		consult_transfer_from_line;
	
	// Indicates if this call is to be transferred after consultation.
	bool			to_be_transferred;
	
	// After consultation this line should be transferred to the
	// transfer_to_line.
	unsigned short		consult_transfer_to_line;
	
	// Indicates if media encryption should be negotiated.
	bool			try_to_encrypt;
	
	// Indicates if call must be auto answered
	bool			auto_answer;

	// Line number (starting from 0)
	// The number of a line may change when it moves from the user lines
	// to the pool of dying lines. So a line number cannot be used as
	// unique line identification over longer times.
	unsigned short		line_number;

	// The phone that owns this line
	t_phone			*phone;

	// Dialog for which no response with a to-tag has been received.
	// Formally this is not a dialog yet.
	t_dialog		*open_dialog;

	// Dialogs for which a response (1XX/2XX) with a to-tag has
	// been received.
	list<t_dialog *>	pending_dialogs;

	// Outgoing call: The first dialog for which a 2XX has been received.
	// Incoming call: Dialog created by an incoming INVITE
	t_dialog		*active_dialog;

	// Currently not used.
	list<t_dialog *>	dying_dialogs;

	// Timers
	t_object_id		id_invite_comp;
	t_object_id		id_no_answer;

	// Call info
	t_call_info		call_info;

	/** RTP port to be used for this line. */
	unsigned short		rtp_port;
	
	/**
	 * Phone user using the line.
	 * This member is only set when the line is not idle.
	 * An idle line is not associated with a user.
	 * @note The line object does not own the phone user.
	 *       Therefor the line object must never delete the phone user.
	 */
	t_phone_user		*phone_user;
	
	// The incoming call script can return a specific ring tone
	// to be played for an incoming call. This ring tone is
	// stored here. If there is no specific ring tone to be played
	// then this attribute is empty
	string			user_defined_ringtone;
	
	// Indicates if the line must go to seized state when it
	// becomes idle.
	bool			keep_seized;

	// Find a dialog from the list that matches the response.
	t_dialog *match_response(t_response *r,
				const list<t_dialog *> &l) const;
	t_dialog *match_response(StunMessage *r, t_tuid tuid,
				const list<t_dialog *> &l) const;
	t_dialog *match_call_id_tags(const string &call_id,
		const string &to_tag, const string &from_tag,
		const list<t_dialog *> &l) const;

	// Get the dialog with id == did. If dialog does not exist
	// then NULL is returned.
	t_dialog *get_dialog(t_object_id did) const;

	// Clean up terminated dialogs
	void cleanup(void);

	// Cleanup all open and pending dialogs
	void cleanup_open_pending(void);
	
	// Forcefully cleanup all dialogs
	void cleanup_forced(void);
	
	// Cleanup state for a transfer with consultation.
	// If the call on this line is a consult, then the consult state of 
	// the line that is to be transferred will be cleaned too.
	// If the call on this line is to be transferred, then the consult
	// state of the consultation line will be cleared too.
	void cleanup_transfer_consult_state(void);

public:
	// Call history record
	t_call_record		call_hist_record;
	
	t_line(t_phone *_phone, unsigned short _line_number);
	~t_line();

	t_line_state get_state(void) const;
	t_line_substate get_substate(void) const;
	t_refer_state get_refer_state(void) const;

	// Timer operations
	void start_timer(t_line_timer timer, t_object_id did = 0);
	void stop_timer(t_line_timer timer, t_object_id did = 0);

	/** @name Actions */
	//@{
	/**
	 * Send INIVTE request.
	 * @param pu The phone user making this call.
	 * @param to_uri The URI to be used a request-URI and To header URI
	 * @param to_display Display name for To header.
	 * @param subject If not empty, this string will go into the Subject header.
	 * @param hdr_referred_by The Reffered-By header to be put in the INVITE.
	 * @param hdr_replaces The Replaces header to be put in the INVITE.
	 * @param hdr_require Required extensions to be put in the Require header.
	 * @param hdr_request_disposition Request-Disposition header to be put in the INVITE.
	 * @param anonymous Inidicates if the INVITE should be sent anonymous.
	 *
	 * @pre The line is idle.
	 */
	void invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,
		const string &subject, const t_hdr_referred_by &hdr_referred_by,
		const t_hdr_replaces &hdr_replaces, const t_hdr_require &hdr_require,
		const t_hdr_request_disposition &hdr_request_disposition,
		bool anonymous);
		
	/**
	 * Send INIVTE request.
	 * @param pu The phone user making this call.
	 * @param to_uri The URI to be used a request-URI and To header URI
	 * @param to_display Display name for To header.
	 * @param subject If not empty, this string will go into the Subject header.
	 * @param no_fork If true, put a no-fork request disposition in the outgoing INVITE
	 * @param anonymous Inidicates if the INVITE should be sent anonymous.
	 *
	 * @pre The line is idle.
	 */
	void invite(t_phone_user *pu, const t_url &to_uri, const string &to_display,
		const string &subject, bool no_fork, bool anonymous);
		
	void answer(void);
	void reject(void);
	void redirect(const list<t_display_url> &destinations, int code, string reason = "");
	void end_call(void);
	void send_dtmf(char digit, bool inband, bool info);
	//@}

	// OPTIONS inside dialog
	void options(void);

	bool hold(bool rtponly = false); // returns false if call cannot be put on hold
	void retrieve(void);
	
	// Kill all RTP stream associated with this line
	void kill_rtp(void);
	
	void refer(const t_url &uri, const string &display);

	// Mute/unmute a call
	// - enable = true -> mute
	// - enable = false -> unmute
	void mute(bool enable);

	/** @name Handle incoming responses */
	//@{
	void recvd_provisional(t_response *r, t_tuid tuid, t_tid tid);
	void recvd_success(t_response *r, t_tuid tuid, t_tid tid);
	void recvd_redirect(t_response *r, t_tuid tuid, t_tid tid);
	void recvd_client_error(t_response *r, t_tuid tuid, t_tid tid);
	void recvd_server_error(t_response *r, t_tuid tuid, t_tid tid);
	void recvd_global_error(t_response *r, t_tuid tuid, t_tid tid);
	//@}

	/** @name Handle incoming requests */
	//@{
	void recvd_invite(t_phone_user *pu, t_request *r, t_tid tid, const string &ringtone);
	void recvd_ack(t_request *r, t_tid tid);
	void recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid);
	void recvd_bye(t_request *r, t_tid tid);
	void recvd_options(t_request *r, t_tid tid);
	void recvd_register(t_request *r, t_tid tid);
	void recvd_prack(t_request *r, t_tid tid);
	void recvd_subscribe(t_request *r, t_tid tid);
	void recvd_notify(t_request *r, t_tid tid);
	void recvd_info(t_request *r, t_tid tid);
	void recvd_message(t_request *r, t_tid tid);

	/**
	 * Process REFER request.
	 * @return true, if refer has been accepted sofar. The refer may still
	 * be rejected by the user.
	 * @return false, if the refer has been rejected.
	 */
	bool recvd_refer(t_request *r, t_tid tid);
	//@}
	
	// Handle the response from the user on the question for refer
	// permission. This response is received on the dialog that received
	// the REFER before.
	// The request (r) is the REFER request that was received.
	void recvd_refer_permission(bool permission, t_request *r);
	
	void recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid);

	void failure(t_failure failure, t_tid tid);

	void timeout(t_line_timer timer, t_object_id did);
	void timeout_sub(t_subscribe_timer timer, t_object_id did,
		const string &event_type, const string &event_id);

	// Return true if the response or request matches a dialog that
	// is owned by this line
	bool match(t_response *r, t_tuid tuid) const;
	bool match(t_request *r) const;
	bool match_cancel(t_request *r, t_tid target_tid) const;
	bool match(StunMessage *r, t_tuid tuid) const;
	
	/**
	 * RFC 3891 Match info from Replaces header
	 * Match call id, to-tag and from tag like an incoming request.
	 * @param call_id [in] The Call ID of the Replaces header.
	 * @param to_tag [in] to-tag of the Replaces header.
	 * @param from_tag [in] from-tag of the Replaces header.
	 * @param no_fork_req_disposition [in] Indicates if the incoming request
	 *	contains a no-fork request disposition.
	 * @param early_matched [out] When a match is found, early_matched 
	 * 	indicates if the match was on an early dialog.
	 * @return true if a match is found with an associated dialog.
	 */
	bool match_replaces(const string &call_id, const string &to_tag, 
		const string &from_tag, 
		bool no_fork_req_disposition,
		bool &early_matched) const;

	// Check if an incoming INVITE is a retransmission of an INVITE
	// that is already being processed by this line
	bool is_invite_retrans(t_request *r);

	// Process a retransmission of an incoming INVITE
	void process_invite_retrans(void);

	// Create user uri and contact uri
	string create_user_contact(const string &auto_ip) const;
	string create_user_uri(void) const;

	// Create a response to an OPTIONS request
	// Argument 'in-dialog' indicates if the OPTIONS response is
	// sent within a dialog.
	t_response *create_options_response(t_request *r,
					bool in_dialog = false) const;

	// Send a response/request
	void send_response(t_response *r, t_tuid tuid, t_tid tid);
	void send_request(t_request *r, t_tuid tuid);

	t_phone *get_phone(void) const;
	unsigned short get_line_number(void) const;
	bool get_is_on_hold(void) const;
	bool get_is_muted(void) const;
	bool get_hide_user(void) const;
	
	// If this is a transfer consult, then true will be returned and
	// lineno will be set to the line that must be transferred.
	bool get_is_transfer_consult(unsigned short &lineno) const;
	
	// When setting the transfer consult indication to true, the
	// line that must be transferred must be passed.
	void set_is_transfer_consult(bool enable, unsigned short lineno);
	
	// If this line is to be transferred after consultation, then
	// true will be returned and lineno will be set to the line
	// where this line should be transferred to.
	bool get_to_be_transferred(unsigned short &lineno) const;
	
	// When setting the to be transferred indication to true, the
	// line to which must be transferred must be passed.
	void set_to_be_transferred(bool enable, unsigned short lineno);
	
	bool get_is_encrypted(void) const;
	bool get_try_to_encrypt(void) const;
	bool get_auto_answer(void) const;
	void set_auto_answer(bool enable);
	bool is_refer_succeeded(void) const;
	bool has_media(void) const;
	
	/** @name Remote (target) uri/display */
	//@{
	/** 
	 * Get the remote target URI of the active dialog.
	 * @return Remote target URI. If there is no active dialog, then an
	 *         empty URI is returned.
	 */
	t_url get_remote_target_uri(void) const;
	
	/** 
	 * Get the remote target URI of the first pending dialog.
	 * @return Remote target URI. If there is no pending dialog, then an
	 *         empty URI is returned.
	 */
	t_url get_remote_target_uri_pending(void) const;
	
	/**
	 * Get the remote target display name of the active dialog.
	 * @return Remote target display name. If there is no active dialog,
	 *         then an empty string is returned.
	 */
	string get_remote_target_display(void) const;
	
	/**
	 * Get the remote target display name of the first pending dialog.
	 * @return Remote target display name. If there is no pending dialog,
	 *         then an empty string is returned.
	 */
	string get_remote_target_display_pending(void) const;
	
	/** 
	 * Get the remote URI of the active dialog.
	 * @return Remote URI. If there is no active dialog, then an
	 *         empty URI is returned.
	 */
	t_url get_remote_uri(void) const;
	
	/** 
	 * Get the remote URI of the first pending dialog.
	 * @return Remote URI. If there is no pending dialog, then an
	 *         empty URI is returned.
	 */
	t_url get_remote_uri_pending(void) const;
	
	/**
	 * Get the remote display name of the active dialog.
	 * @return Remote display name. If there is no active dialog,
	 *         then an empty string is returned.
	 */
	string get_remote_display(void) const;
	
	/**
	 * Get the remote display name of the first pending dialog.
	 * @return Remote display name. If there is no pending dialog,
	 *         then an empty string is returned.
	 */
	string get_remote_display_pending(void) const;
	//@}
	
	/** @name Call identification */
	//@{
	/**
	 * Get the call-id of the active dialog
	 * @return If there is no active dialog, then an empty string is returned.
	 */
	string get_call_id(void) const;
	
	/**
	 * Get the call-id of the first pending dialog
	 * @return If there is no pending dialog, then an empty string is returned.
	 */
	string get_call_id_pending(void) const;
	
	/**
	 * Get the local tag of the active dialog
	 * @return If there is no active dialog, then an empty string is returned.
	 */
	string get_local_tag(void) const;
	
	/**
	 * Get the local tag of the first pending dialog
	 * @return If there is no pending dialog, then an empty string is returned.
	 */
	string get_local_tag_pending(void) const;
	
	/**
	 * Get the remote tag of the active dialog
	 * @return If there is no active dialog, then an empty string is returned.
	 */
	string get_remote_tag(void) const;
	
	/**
	 * Get the remote tag of the first pending dialog
	 * @return If there is no pending dialog, then an empty string is returned.
	 */
	string get_remote_tag_pending(void) const;
	//@}
	
	// Returns true if the remote party of the active dialog supports
	// the extension.
	// If there is no active dialog, then false is returned.
	bool remote_extension_supported(const string &extension) const;

	// Seize the line. User wants to make an outgoing call, so
	// the line must be marked as busy, such that an incoming call
	// cannot take this line.
	// Returns false if seizure failed
	bool seize(void);

	// Unseize the line
	void unseize(void);

	// Return the (audio) session belonging to this line.
	// Returns NULL if there is no (audio) session
	t_session *get_session(void) const;
	t_audio_session *get_audio_session(void) const;

	void notify_refer_progress(t_response *r);

	// Called by dialog if retrieve/hold actions failed.
	void failed_retrieve(void);
	void failed_hold(void);

	// Called by dialog if retry of a retrieve after a glare (491 response)
	// succeeded.
	void retry_retrieve_succeeded(void);

	// Get the call info record
	t_call_info get_call_info(void) const;
	void ci_set_dtmf_supported(bool supported, bool inband = false, bool info = false);
	void ci_set_last_provisional_reason(const string &reason);
	void ci_set_send_codec(t_audio_codec codec);
	void ci_set_recv_codec(t_audio_codec codec);
	void ci_set_refer_supported(bool supported);

	// Initialize the RTP port for this line based on the settings
	// in the user profile.
	void init_rtp_port(void);

	/** Get the RTP port to be used for a call on this line. */
	unsigned short get_rtp_port(void) const;
	
	/**
	 * Get the user profile of the user using the phone.
	 * @return a pointer to the user object owned by the line.
	 * NOT a copy.
	 */
	t_user *get_user(void) const;
	
	/**
	 * Get the phone user using the phone.
	 * @return Pointer to the phone user.
	 */
	t_phone_user *get_phone_user(void) const;
	
	// Get the ring tone to be played for an incoming call
	string get_ringtone(void) const;
	
	// ZRTP actions
	void confirm_zrtp_sas(void);
	void reset_zrtp_sas_confirmation(void);
	void enable_zrtp(void);
	void zrtp_request_go_clear(void);
	void zrtp_go_clear_ok(void);
	
	// Force a line to the idle state (during termination of Twinkle)
	void force_idle(void);
	
	// Indicate if the line must be seized after releasing
	void set_keep_seized(bool seize);
	bool get_keep_seized(void) const;
	
	/**
	 * Get a dialog that has an active session (RTP stream).
	 * @return The dialog that has an active session.
	 * @return NULL, if there is no dialog with an active session.
	 * @note There can be at most 1 dialog with an active session.
	 */
	t_dialog *get_dialog_with_active_session(void) const;
};

#endif