/*
    Copyright (C) 2005-2007  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, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "twinkle_config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/soundcard.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <cstring>
#include "sys_settings.h"
#include "translator.h"
#include "user.h"
#include "userintf.h"
#include "util.h"

// Share directory containing files applicable to all users
#define DIR_SHARE	DATADIR

// Lock file to guarantee that a user is running the application only once
#define LOCK_FILENAME	"twinkle.lck"

// System config file
#define SYS_CONFIG_FILE	"twinkle.sys"

// Field names in the config file
// AUDIO fields
#define FLD_DEV_RINGTONE	"dev_ringtone"
#define FLD_DEV_SPEAKER		"dev_speaker"
#define FLD_DEV_MIC		"dev_mic"
#define FLD_VALIDATE_AUDIO_DEV	"validate_audio_dev"
#define FLD_AU_REDUCE_NOISE_MIC	"au_reduce_noise_mic"
#define FLD_ALSA_PLAY_PERIOD_SIZE	"alsa_play_period_size"
#define FLD_ALSA_CAPTURE_PERIOD_SIZE	"alsa_capture_period_size"
#define FLD_OSS_FRAGMENT_SIZE	"oss_fragment_size"

// LOG fields
#define FLD_LOG_MAX_SIZE	"log_max_size"
#define FLD_LOG_SHOW_SIP	"log_show_sip"
#define FLD_LOG_SHOW_STUN	"log_show_stun"
#define FLD_LOG_SHOW_MEMORY	"log_show_memory"
#define FLD_LOG_SHOW_DEBUG	"log_show_debug"

// GUI settings
#define FLD_GUI_USE_SYSTRAY	"gui_use_systray"
#define FLD_GUI_HIDE_ON_CLOSE	"gui_hide_on_close"
#define FLD_GUI_AUTO_SHOW_INCOMING	"gui_auto_show_incoming"
#define FLD_GUI_AUTO_SHOW_TIMEOUT	"gui_auto_show_timeout"

// Address book settings
#define FLD_AB_SHOW_SIP_ONLY	"ab_show_sip_only"
#define FLD_AB_LOOKUP_NAME	"ab_lookup_name"
#define FLD_AB_OVERRIDE_DISPLAY	"ab_override_display"
#define FLD_AB_LOOKUP_PHOTO	"ab_lookup_photo"

// Call history fields
#define FLD_CH_MAX_SIZE		"ch_max_size"

// Service settings
#define FLD_CALL_WAITING	"call_waiting"
#define FLD_HANGUP_BOTH_3WAY	"hangup_both_3way"

// Startup settings
#define FLD_START_USER_PROFILE	"start_user_profile"
#define FLD_START_USER_HOST	"start_user_host"
#define FLD_START_USER_NIC	"start_user_nic"
#define FLD_START_HIDDEN	"start_hidden"

// Network settings
#define FLD_SIP_UDP_PORT	"sip_udp_port"
#define FLD_RTP_PORT		"rtp_port"

// Ring tone settings
#define FLD_PLAY_RINGTONE	"play_ringtone"
#define FLD_RINGTONE_FILE	"ringtone_file"
#define FLD_PLAY_RINGBACK	"play_ringback"
#define FLD_RINGBACK_FILE	"ringback_file"

// Persistent storage for user interface state
#define FLD_LAST_USED_PROFILE	"last_used_profile"
#define FLD_REDIAL_URL		"redial_url"
#define FLD_REDIAL_DISPLAY	"redial_display"
#define FLD_REDIAL_SUBJECT	"redial_subject"
#define FLD_REDIAL_PROFILE	"redial_profile"
#define FLD_REDIAL_HIDE_USER	"redial_hide_user"
#define FLD_DIAL_HISTORY	"dial_history"
#define FLD_SHOW_DISPLAY	"show_display"
#define FLD_COMPACT_LINE_STATUS	"compact_line_status"
#define FLD_SHOW_BUDDY_LIST	"show_buddy_list"
#define FLD_WARN_HIDE_USER	"warn_hide_user"

/////////////////////////
// class t_audio_device
/////////////////////////

string t_audio_device::get_description(void) const {
	string s = device;
	if (type == OSS) {
		s = "OSS: " + s;
		if (sym_link.size() > 0) {
			s += " -> ";
			s += sym_link;
		}
		
		if (name.size() > 0) {
			s += ": ";
			s += name;
		}
	} else if (type == ALSA) {
		s = "ALSA: " + s;
		if (!name.empty()) {
			s += ": ";
			s += name;
		}
	} else {
		s = "Unknown: " + s;
	}
	
	return s;
}

string t_audio_device::get_settings_value(void) const {
	string s;
	
	switch (type) {
	case OSS:
		s = PFX_OSS;
		break;
	case ALSA:
		s = PFX_ALSA;
		break;
	default:
		assert(false);
	}
	
	s += device;
	return s;
}


/////////////////////////
// class t_sys_settings
/////////////////////////

t_sys_settings::t_sys_settings() {
	dir_share = DIR_SHARE;
	filename = string(DIR_HOME);
	filename += "/";
	filename += USER_DIR;
	filename += "/";
	filename += SYS_CONFIG_FILE;
	
	// OSS Default settings
	dev_ringtone = audio_device();
	dev_speaker = audio_device();
	dev_mic = audio_device();
	validate_audio_dev = true;
	au_reduce_noise_mic = true;
	alsa_play_period_size = 128;
	alsa_capture_period_size = 32;
	oss_fragment_size = 128;
	
	log_max_size = 5;
	log_show_sip = true;
	log_show_stun = true;
	log_show_memory = true;
	log_show_debug = false;
	
	gui_use_systray = true;
	gui_hide_on_close = true;
	gui_auto_show_incoming = false;
	gui_auto_show_timeout = 10;
	
	ab_show_sip_only = false;
	ab_lookup_name = true;
	ab_override_display = true;
	ab_lookup_photo = true;
	
	ch_max_size = 50;
	
	call_waiting = true;
	hangup_both_3way = true;
	
	start_user_profiles.clear();
	start_user_host.clear();
	start_user_nic.clear();
	start_hidden = false;
	
	config_sip_udp_port = 5060;
	active_sip_udp_port = 0;
	override_sip_udp_port = 0;
	rtp_port = 8000;
	override_rtp_port = 0;
	
	play_ringtone = true;
	ringtone_file.clear();
	play_ringback = true;
	ringback_file.clear();
	
	last_used_profile.clear();
	redial_url.set_url("");
	redial_display.clear();
	redial_subject.clear();
	redial_profile.clear();
	redial_hide_user = false;
	dial_history.clear();
	show_display = true;
	compact_line_status = false;
	show_buddy_list = true;
	warn_hide_user = true;
}

// Getters
t_audio_device t_sys_settings::get_dev_ringtone(void) const {
	t_audio_device result;
	mtx_sys.lock();
	result = dev_ringtone;
	mtx_sys.unlock();
	return result;	
}

t_audio_device t_sys_settings::get_dev_speaker(void) const {
	t_audio_device result;
	mtx_sys.lock();
	result = dev_speaker;
	mtx_sys.unlock();
	return result;	
}

t_audio_device t_sys_settings::get_dev_mic(void) const {
	t_audio_device result;
	mtx_sys.lock();
	result = dev_mic;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_validate_audio_dev(void) const {
	bool result;
	mtx_sys.lock();
	result = validate_audio_dev;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_au_reduce_noise_mic(void) const {
	bool result;
	mtx_sys.lock();
	result = au_reduce_noise_mic;
	mtx_sys.unlock();
	return result;	
}

int t_sys_settings::get_alsa_play_period_size(void) const {
	int result;
	mtx_sys.lock();
	result = alsa_play_period_size;
	mtx_sys.unlock();
	return result;	
}

int t_sys_settings::get_alsa_capture_period_size(void) const {
	int result;
	mtx_sys.lock();
	result = alsa_capture_period_size;
	mtx_sys.unlock();
	return result;	
}

int t_sys_settings::get_oss_fragment_size(void) const {
	int result;
	mtx_sys.lock();
	result = oss_fragment_size;
	mtx_sys.unlock();
	return result;	
}

unsigned short t_sys_settings::get_log_max_size(void) const {
	unsigned short result;
	mtx_sys.lock();
	result = log_max_size;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_log_show_sip(void) const {
	bool result;
	mtx_sys.lock();
	result = log_show_sip;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_log_show_stun(void) const {
	bool result;
	mtx_sys.lock();
	result = log_show_stun;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_log_show_memory(void) const {
	bool result;
	mtx_sys.lock();
	result = log_show_memory;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_log_show_debug(void) const {
	bool result;
	mtx_sys.lock();
	result = log_show_debug;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_gui_use_systray(void) const {
	bool result;
	mtx_sys.lock();
	result = gui_use_systray;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_gui_auto_show_incoming(void) const {
	bool result;
	mtx_sys.lock();
	result = gui_auto_show_incoming;
	mtx_sys.unlock();
	return result;
}

int t_sys_settings::get_gui_auto_show_timeout(void) const {
	int result;
	mtx_sys.lock();
	result = gui_auto_show_timeout;
	mtx_sys.unlock();
	return result;
}

bool t_sys_settings::get_gui_hide_on_close(void) const {
	bool result;
	mtx_sys.lock();
	result = gui_hide_on_close;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_ab_show_sip_only(void) const {
	bool result;
	mtx_sys.lock();
	result = ab_show_sip_only;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_ab_lookup_name(void) const {
	bool result;
	mtx_sys.lock();
	result = ab_lookup_name;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_ab_override_display(void) const {
	bool result;
	mtx_sys.lock();
	result = ab_override_display;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_ab_lookup_photo(void) const {
	bool result;
	mtx_sys.lock();
	result = ab_lookup_photo;
	mtx_sys.unlock();
	return result;	
}

int t_sys_settings::get_ch_max_size(void) const {
	int result;
	mtx_sys.lock();
	result = ch_max_size;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_call_waiting(void) const {
	bool result;
	mtx_sys.lock();
	result = call_waiting;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_hangup_both_3way(void) const {
	bool result;
	mtx_sys.lock();
	result = hangup_both_3way;
	mtx_sys.unlock();
	return result;	
}

list<string> t_sys_settings::get_start_user_profiles(void) const {
	list<string> result;
	mtx_sys.lock();
	result = start_user_profiles;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_start_user_host(void) const {
	string result;
	mtx_sys.lock();
	result = start_user_host;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_start_user_nic(void) const {
	string result;
	mtx_sys.lock();
	result = start_user_nic;
	mtx_sys.unlock();
	return result;
}

bool t_sys_settings::get_start_hidden(void) const {
	bool result;
	mtx_sys.lock();
	result = start_hidden;
	mtx_sys.unlock();
	return result;	
}

unsigned short t_sys_settings::get_config_sip_udp_port(void) const {
	unsigned short result;
	mtx_sys.lock();
	result = config_sip_udp_port;
	mtx_sys.unlock();
	return result;	
}

unsigned short t_sys_settings::get_rtp_port(void) const {
	unsigned short result;
	mtx_sys.lock();
	if (override_rtp_port > 0) {
		result = override_rtp_port;
	} else {
		result = rtp_port;
	}
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_play_ringtone(void) const {
	bool result;
	mtx_sys.lock();
	result = play_ringtone;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_ringtone_file(void) const {
	string result;
	mtx_sys.lock();
	result = ringtone_file;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_play_ringback(void) const {
	bool result;
	mtx_sys.lock();
	result = play_ringback;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_ringback_file(void) const {
	string result;
	mtx_sys.lock();
	result = ringback_file;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_last_used_profile(void) const {
	string result;
	mtx_sys.lock();
	result = last_used_profile;
	mtx_sys.unlock();
	return result;	
}

t_url t_sys_settings::get_redial_url(void) const {
	t_url result;
	mtx_sys.lock();
	result = redial_url;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_redial_display(void) const {
	string result;
	mtx_sys.lock();
	result = redial_display;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_redial_subject(void) const {
	string result;
	mtx_sys.lock();
	result = redial_subject;
	mtx_sys.unlock();
	return result;	
}

string t_sys_settings::get_redial_profile(void) const {
	string result;
	mtx_sys.lock();
	result = redial_profile;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_redial_hide_user(void) const {
	bool result;
	mtx_sys.lock();
	result = redial_hide_user;
	mtx_sys.unlock();
	return result;
}

list<string> t_sys_settings::get_dial_history(void) const {
	list<string> result;
	mtx_sys.lock();
	result = dial_history;
	mtx_sys.unlock();
	return result;	
}

bool t_sys_settings::get_show_display(void) const {
	bool result;
	mtx_sys.lock();
	result = show_display;
	mtx_sys.unlock();
	return result;
}

bool t_sys_settings::get_compact_line_status(void) const {
	bool result;
	mtx_sys.lock();
	result = compact_line_status;
	mtx_sys.unlock();
	return result;
}

bool t_sys_settings::get_show_buddy_list(void) const {
	bool result;
	mtx_sys.lock();
	result = show_buddy_list;
	mtx_sys.unlock();
	return result;
}

bool t_sys_settings::get_warn_hide_user(void) const {
	bool result;
	mtx_sys.lock();
	result = warn_hide_user;
	mtx_sys.unlock();
	return result;
}


// Setters
void t_sys_settings::set_dev_ringtone(const t_audio_device &dev) {
	mtx_sys.lock();
	dev_ringtone = dev;
	mtx_sys.unlock();
}

void t_sys_settings::set_dev_speaker(const t_audio_device &dev) {
	mtx_sys.lock();
	dev_speaker = dev;
	mtx_sys.unlock();
}

void t_sys_settings::set_dev_mic(const t_audio_device &dev) {
	mtx_sys.lock();
	dev_mic = dev;
	mtx_sys.unlock();
}

void t_sys_settings::set_validate_audio_dev(bool b) {
	mtx_sys.lock();
	validate_audio_dev = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_au_reduce_noise_mic(bool b) {
	mtx_sys.lock();
	au_reduce_noise_mic = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_alsa_play_period_size(int size) {
	mtx_sys.lock();
	alsa_play_period_size = size;
	mtx_sys.unlock();
}

void t_sys_settings::set_alsa_capture_period_size(int size) {
	mtx_sys.lock();
	alsa_capture_period_size = size;
	mtx_sys.unlock();
}

void t_sys_settings::set_oss_fragment_size(int size) {
	mtx_sys.lock();
	oss_fragment_size = size;
	mtx_sys.unlock();
}

void t_sys_settings::set_log_max_size(unsigned short size) {
	mtx_sys.lock();
	log_max_size = size;
	mtx_sys.unlock();
}

void t_sys_settings::set_log_show_sip(bool b) {
	mtx_sys.lock();
	log_show_sip = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_log_show_stun(bool b) {
	mtx_sys.lock();
	log_show_stun = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_log_show_memory(bool b) {
	mtx_sys.lock();
	log_show_memory = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_log_show_debug(bool b) {
	mtx_sys.lock();
	log_show_debug = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_gui_use_systray(bool b) {
	mtx_sys.lock();
	gui_use_systray = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_gui_hide_on_close(bool b) {
	mtx_sys.lock();
	gui_hide_on_close = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_gui_auto_show_incoming(bool b) {
	mtx_sys.lock();
	gui_auto_show_incoming = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_gui_auto_show_timeout(int timeout) {
	mtx_sys.lock();
	gui_auto_show_timeout = timeout;
	mtx_sys.unlock();
}

void t_sys_settings::set_ab_show_sip_only(bool b) {
	mtx_sys.lock();
	ab_show_sip_only = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ab_lookup_name(bool b) {
	mtx_sys.lock();
	ab_lookup_name = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ab_override_display(bool b) {
	mtx_sys.lock();
	ab_override_display = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ab_lookup_photo(bool b) {
	mtx_sys.lock();
	ab_lookup_photo = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ch_max_size(int size) {
	mtx_sys.lock();
	ch_max_size = size;
	mtx_sys.unlock();
}

void t_sys_settings::set_call_waiting(bool b) {
	mtx_sys.lock();
	call_waiting = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_hangup_both_3way(bool b) {
	mtx_sys.lock();
	hangup_both_3way = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_start_user_profiles(const list<string> &profiles) {
	mtx_sys.lock();
	start_user_profiles = profiles;
	mtx_sys.unlock();
}

void t_sys_settings::set_start_user_host(const string &host) {
	mtx_sys.lock();
	start_user_host = host;
	mtx_sys.unlock();
}

void t_sys_settings::set_start_user_nic(const string &dev) {
	mtx_sys.lock();
	start_user_nic = dev;
	mtx_sys.unlock();
}

void t_sys_settings::set_start_hidden(bool b) {
	mtx_sys.lock();
	start_hidden = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_config_sip_udp_port(unsigned short port) {
	mtx_sys.lock();
	config_sip_udp_port = port;
	mtx_sys.unlock();
}

void t_sys_settings::set_override_sip_udp_port(unsigned short port) {
	mtx_sys.lock();
	override_sip_udp_port = port;
	mtx_sys.unlock();
}

void t_sys_settings::set_rtp_port(unsigned short port) {
	mtx_sys.lock();
	rtp_port = port;
	mtx_sys.unlock();
}

void t_sys_settings::set_override_rtp_port(unsigned short port) {
	mtx_sys.lock();
	override_rtp_port = port;
	mtx_sys.unlock();
}

void t_sys_settings::set_play_ringtone(bool b) {
	mtx_sys.lock();
	play_ringtone = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ringtone_file(const string &file) {
	mtx_sys.lock();
	ringtone_file = file;
	mtx_sys.unlock();
}

void t_sys_settings::set_play_ringback(bool b) {
	mtx_sys.lock();
	play_ringback = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_ringback_file(const string &file) {
	mtx_sys.lock();
	ringback_file = file;
	mtx_sys.unlock();
}

void t_sys_settings::set_last_used_profile(const string &profile) {
	mtx_sys.lock();
	last_used_profile = profile;
	mtx_sys.unlock();
}

void t_sys_settings::set_redial_url(const t_url &url) {
	mtx_sys.lock();
	redial_url = url;
	mtx_sys.unlock();
}

void t_sys_settings::set_redial_display(const string &display) {
	mtx_sys.lock();
	redial_display = display;
	mtx_sys.unlock();
}

void t_sys_settings::set_redial_subject(const string &subject) {
	mtx_sys.lock();
	redial_subject = subject;
	mtx_sys.unlock();
}

void t_sys_settings::set_redial_profile(const string &profile) {
	mtx_sys.lock();
	redial_profile = profile;
	mtx_sys.unlock();
}

void t_sys_settings::set_redial_hide_user(const bool b) {
	mtx_sys.lock();
	redial_hide_user = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_dial_history(const list<string> &history) {
	mtx_sys.lock();
	dial_history = history;
	mtx_sys.unlock();
}

void t_sys_settings::set_show_display(bool b) {
	mtx_sys.lock();
	show_display = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_compact_line_status(bool b) {
	mtx_sys.lock();
	compact_line_status = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_show_buddy_list(bool b) {
	mtx_sys.lock();
	show_buddy_list = b;
	mtx_sys.unlock();
}

void t_sys_settings::set_warn_hide_user(bool b) {
	mtx_sys.lock();
	warn_hide_user = b;
	mtx_sys.unlock();
}


string t_sys_settings::about(bool html) const {
	string s = PRODUCT_NAME;
	s += ' ';
	s += PRODUCT_VERSION;
	s += " - ";
	s += get_product_date();
	if (html) s += "<BR>";
	s += "\n";
	
	s += "Copyright (C) 2005-2007  ";
	s += PRODUCT_AUTHOR;
	if (html) s += "<BR>";
	s += "\n";

	s += "http://www.twinklephone.com";
	if (html) s += "<BR><BR>";
	s += "\n\n";
	
	string options_built = get_options_built();
	if (!options_built.empty()) {
		s += TRANSLATE("Built with support for:");
		s += " ";
		s += options_built;
		if (html) s += "<BR><BR>";
		s += "\n\n";
	}
	
	s += TRANSLATE("Contributions:");
	if (html) s += "<BR>";
	s += "\n";
	
	if (html) {
		s += "* ALSA - Rickard Petz&auml;ll";
		s += "<BR>";
	} else {
		s += "* ALSA - Rickard Petzall";
	}
	s += "\n";
	s += "* ZRTP/SRTP - Werner Dittmann";
	if (html) s += "<BR><BR>";
	s += "\n\n";

	s += TRANSLATE("This software contains the following software from 3rd parties:");		
	if (html) s += "<BR>";
	s += "\n";

	s += TRANSLATE("* GSM codec from Jutta Degener and Carsten Bormann, University of Berlin");
	if (html) s += "<BR>";
	s += "\n";

	s += TRANSLATE("* G.711/G.726 codecs from Sun Microsystems (public domain)");	
	if (html) s += "<BR>";
	s += "\n";
	
#ifdef HAVE_ILBC
	s += TRANSLATE("* iLBC implementation from RFC 3951 (www.ilbcfreeware.org)");	
	if (html) s += "<BR>";
	s += "\n";
#endif
	
	s += TRANSLATE("* Parts of the STUN project at http://sourceforge.net/projects/stun");
	if (html) s += "<BR>";
	s += "\n";
	
	s += TRANSLATE("* Parts of libsrv at http://libsrv.sourceforge.net/");
	if (html) s += "<BR>";
	s += "\n";
	
	if (html) s += "<BR>";
	s += "\n";
	
	s += TRANSLATE("For RTP the following dynamic libraries are linked:");
	if (html) s += "<BR>";
	s += "\n";
	
	s += "* GNU ccRTP - http://www.gnu.org/software/ccrtp";
	if (html) s += "<BR>";
	s += "\n";

	s += "* GNU CommonC++ - http://www.gnu.org/software/commoncpp";
	if (html) s += "<BR><BR>";
	s += "\n\n";
	
	// Display information about translator only on non-english version.
	string translated_by = TRANSLATE("Translated to english by <your name>");
	if (translated_by != "Translated to english by <your name>") {
		s += translated_by;
		if (html) s += "<BR><BR>";
		s += "\n\n";
	}
	
	s += PRODUCT_NAME;
	s += " comes with ABSOLUTELY NO WARRANTY.";
	if (html) s += "<BR>";
	s += "\n";
	
	s += "This program is free software; you can redistribute it and/or modify";
	if (html) s += "<BR>";
	s += "\n";
	
	s += "it under the terms of the GNU General Public License as published by";
	if (html) s += "<BR>";
	s += "\n";
	
	s += "the Free Software Foundation; either version 2 of the License, or";
	if (html) s += "<BR>";
	s += "\n";
	
	s += "(at your option) any later version.";
	if (html) s += "<BR>";
	s += "\n";
	
	return s;
}

string t_sys_settings::get_product_date(void) const {
	struct tm t;
	t.tm_sec = 0;
	t.tm_min = 0;
	t.tm_hour = 0;
	
	vector<string> l = split(PRODUCT_DATE, ' ');
	assert(l.size() == 3);
	t.tm_mon = str2month_full(l[0]);
	t.tm_mday = atoi(l[1].c_str());
	t.tm_year = atoi(l[2].c_str()) - 1900;
	
	char buf[64];
	strftime(buf, 64, "%d %B %Y", &t);
	return string(buf);
}

string t_sys_settings::get_options_built(void) const {
	string options_built;
#ifdef HAVE_LIBASOUND
	if (!options_built.empty()) options_built += ", ";
	options_built += "ALSA";
#endif
#ifdef HAVE_KDE
	if (!options_built.empty()) options_built += ", ";
	options_built += "KDE";
#endif
#ifdef HAVE_SPEEX
	if (!options_built.empty()) options_built += ", ";
	options_built += "Speex";
#endif
#ifdef HAVE_ILBC
	if (!options_built.empty()) options_built += ", ";
	options_built += "iLBC";
#endif
#ifdef HAVE_ZRTP
	if (!options_built.empty()) options_built += ", ";
	options_built += "ZRTP";
#endif

	return options_built;
}

bool t_sys_settings::check_environment(string &error_msg) const {
	struct stat stat_buf;
	string filename, dirname;
	
	mtx_sys.lock();

	// Check if share directory exists
	if (stat(dir_share.c_str(), &stat_buf) != 0) {
		error_msg = TRANSLATE("Directory %1 does not exist.");
		error_msg = replace_first(error_msg, "%1", dir_share);
		mtx_sys.unlock();
		return false;
	}

	// Check if audio file for ring tone exist
	filename = dir_share;
	filename += '/';
	filename += FILE_RINGTONE;
	ifstream f_ringtone(filename.c_str());
	if (!f_ringtone) {
		error_msg = TRANSLATE("Cannot open file %1 .");
		error_msg = replace_first(error_msg, "%1", filename);
		mtx_sys.unlock();
		return false;
	}

	// Check if audio file for ring back exist
	filename = dir_share;
	filename += '/';
	filename += FILE_RINGBACK;
	ifstream f_ringback(filename.c_str());
	if (!f_ringback) {
		error_msg = TRANSLATE("Cannot open file %1 .");
		error_msg = replace_first(error_msg, "%1", filename);
		mtx_sys.unlock();
		return false;
	}

	// Check if $HOME is set correctly
	if (string(DIR_HOME) == "") {
		error_msg = TRANSLATE("%1 is not set to your home directory.");
		error_msg = replace_first(error_msg, "%1", "$HOME");
		mtx_sys.unlock();
		return false;
	}
	if (stat(DIR_HOME, &stat_buf) != 0) {
		error_msg = TRANSLATE("Directory %1 (%2) does not exist.");
		error_msg = replace_first(error_msg, "%1", DIR_HOME);
		error_msg = replace_first(error_msg, "%2", "$HOME");	
		mtx_sys.unlock();
		return false;
	}

	// Check if user directory exists
	dirname = DIR_HOME;
	dirname += '/';
	dirname += DIR_USER;
	if (stat(dirname.c_str(), &stat_buf) != 0) {
		// User directory does not exist. Create it now.
		if (mkdir(dirname.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
			// Failed to create the user directory
			error_msg = TRANSLATE("Cannot create directory %1 .");
			error_msg = replace_first(error_msg, "%1", dirname);
			mtx_sys.unlock();
			return false;
		}
	}

	mtx_sys.unlock();
	return true;
}

void t_sys_settings::set_dir_share(const string &dir) {
	mtx_sys.lock();
	dir_share = dir;
	mtx_sys.unlock();
}

string t_sys_settings::get_dir_share(void) const {
	string result;
	mtx_sys.lock();
	result = dir_share;
	mtx_sys.unlock();
	return result;
}

string t_sys_settings::get_dir_lang(void) const {
	string result = get_dir_share();
	result += "/lang";
	return result;
}

string t_sys_settings::get_dir_user(void) const {
	string dir = DIR_HOME;
	dir += "/";
	dir += DIR_USER;
	
	return dir;
}

bool t_sys_settings::create_lock_file(string &error_msg, bool &already_running) const {
	struct stat stat_buf;
	string lck_filename;
	already_running = false;

        lck_filename = DIR_HOME;
        lck_filename += "/";
        lck_filename += DIR_USER;
        lck_filename += "/";
        lck_filename += LOCK_FILENAME;

	// Check if a lock file already exists
	if (stat(lck_filename.c_str(), &stat_buf) == 0) {
		ifstream f(lck_filename.c_str());
		if (!f) {
			error_msg =  TRANSLATE("Lock file %1 already exist, but cannot be opened.");
			error_msg = replace_first(error_msg, "%1", lck_filename);
			return false;
		}

		// Check if lock is stale
		pid_t lock_pid;
		f >> lock_pid;
		if (kill(lock_pid, 0) == 0) {
			// The pid in the lock file exists, so Twinkle is
			// already running.
			already_running = true;
			error_msg = TRANSLATE("%1 is already running.\nLock file %2 already exists.");
			error_msg = replace_first(error_msg, "%1", PRODUCT_NAME);
			error_msg = replace_first(error_msg, "%2", lck_filename);
			return false;
		}

		// The lock is stale; delete it
		f.close();
		unlink(lck_filename.c_str());
	}

	// Create lock file
	ofstream f(lck_filename.c_str());
	if (!f) {
		error_msg = TRANSLATE("Cannot create %1 .");
		error_msg = replace_first(error_msg, "%1", lck_filename);
		return false;
	}

	f << getpid();
	if (!f.good()) {
		error_msg = TRANSLATE("Cannot write to %1 .");
		error_msg = replace_first(error_msg, "%1", lck_filename);
		return false;
	}

	return true;
}

void t_sys_settings::delete_lock_file(void) const {
	string lck_filename;

        lck_filename = DIR_HOME;
        lck_filename += "/";
        lck_filename += DIR_USER;
        lck_filename += "/";
        lck_filename += LOCK_FILENAME;

	unlink(lck_filename.c_str());
}

bool t_sys_settings::read_config(string &error_msg) {
	struct stat stat_buf;
	
	mtx_sys.lock();
	
	// Check if config file exists
	if (stat(filename.c_str(), &stat_buf) != 0) {
		mtx_sys.unlock();
		return true;
	}
	
	// Open config file
	ifstream config(filename.c_str());
	if (!config) {
		error_msg = TRANSLATE("Cannot open file for reading: %1");
		error_msg = replace_first(error_msg, "%1", filename);
		mtx_sys.unlock();
		return false;
	}
	
	// Read and parse config file.
	while (!config.eof()) {
		string line;
		getline(config, line);

		// Check if read operation succeeded
		if (!config.good() && !config.eof()) {
			error_msg = TRANSLATE("File system error while reading file %1 .");
			error_msg = replace_first(error_msg, "%1", filename);
			mtx_sys.unlock();
			return false;
		}

		line = trim(line);

		// Skip empty lines
		if (line.size() == 0) continue;

		// Skip comment lines
		if (line[0] == '#') continue;

		vector<string> l = split_on_first(line, '=');
		if (l.size() != 2) {
			error_msg = TRANSLATE("Syntax error in file %1 .");
			error_msg = replace_first(error_msg, "%1", filename);
			error_msg += "\n";
			error_msg += line;
			mtx_sys.unlock();
			return false;
		}

		string parameter = trim(l[0]);
		string value = trim(l[1]);

		if (parameter == FLD_DEV_RINGTONE) {
			dev_ringtone = audio_device(value);
		} else if (parameter == FLD_DEV_SPEAKER) {
			dev_speaker = audio_device(value);
		} else if (parameter == FLD_DEV_MIC) {
			dev_mic = audio_device(value);
		} else if (parameter == FLD_VALIDATE_AUDIO_DEV) {
			validate_audio_dev = yesno2bool(value);
		} else if (parameter == FLD_AU_REDUCE_NOISE_MIC) {
			au_reduce_noise_mic = yesno2bool(value);
		} else if (parameter == FLD_ALSA_PLAY_PERIOD_SIZE) {
			alsa_play_period_size = atoi(value.c_str());
		} else if (parameter == FLD_ALSA_CAPTURE_PERIOD_SIZE) {
			alsa_capture_period_size = atoi(value.c_str());
		} else if (parameter == FLD_OSS_FRAGMENT_SIZE) {
			oss_fragment_size = atoi(value.c_str());
		} else if (parameter == FLD_LOG_MAX_SIZE) {
			log_max_size = atoi(value.c_str());
		} else if (parameter == FLD_LOG_SHOW_SIP) {
			log_show_sip = yesno2bool(value);
		} else if (parameter == FLD_LOG_SHOW_STUN) {
			log_show_stun = yesno2bool(value);
		} else if (parameter == FLD_LOG_SHOW_MEMORY) {
			log_show_memory = yesno2bool(value);
		} else if (parameter == FLD_LOG_SHOW_DEBUG) {
			log_show_debug = yesno2bool(value);
		} else if (parameter == FLD_GUI_USE_SYSTRAY) {
			gui_use_systray = yesno2bool(value);
		} else if (parameter == FLD_GUI_HIDE_ON_CLOSE) {
			gui_hide_on_close = yesno2bool(value);
		} else if (parameter == FLD_GUI_AUTO_SHOW_INCOMING) {
			gui_auto_show_incoming = yesno2bool(value);
		} else if (parameter == FLD_GUI_AUTO_SHOW_TIMEOUT) {
			gui_auto_show_timeout = atoi(value.c_str());
		} else if (parameter == FLD_AB_SHOW_SIP_ONLY) {
			ab_show_sip_only = yesno2bool(value);
		} else if (parameter == FLD_AB_LOOKUP_NAME) {
			ab_lookup_name = yesno2bool(value);
		} else if (parameter == FLD_AB_OVERRIDE_DISPLAY) {
			ab_override_display = yesno2bool(value);
		} else if (parameter == FLD_AB_LOOKUP_PHOTO) {
			ab_lookup_photo = yesno2bool(value);
		} else if (parameter == FLD_CH_MAX_SIZE) {
			ch_max_size = atoi(value.c_str());
		} else if (parameter == FLD_CALL_WAITING) {
			call_waiting = yesno2bool(value);
		} else if (parameter == FLD_HANGUP_BOTH_3WAY) {
			hangup_both_3way = yesno2bool(value);
		} else if (parameter == FLD_START_USER_PROFILE) {
			if (!value.empty()) start_user_profiles.push_back(value);
		} else if (parameter == FLD_START_USER_HOST) {
			start_user_host = value;
		} else if (parameter == FLD_START_USER_NIC) {
			start_user_nic = value;
		} else if (parameter == FLD_START_HIDDEN) {
			start_hidden = yesno2bool(value);
		} else if (parameter == FLD_SIP_UDP_PORT) {
			config_sip_udp_port = atoi(value.c_str());
		} else if (parameter == FLD_RTP_PORT) {
			rtp_port = atoi(value.c_str());
		} else if (parameter == FLD_PLAY_RINGTONE) {
			play_ringtone = yesno2bool(value);
		} else if (parameter == FLD_RINGTONE_FILE) {
			ringtone_file = value;
		} else if (parameter == FLD_PLAY_RINGBACK) {
			play_ringback = yesno2bool(value);
		} else if (parameter == FLD_RINGBACK_FILE) {
			ringback_file = value;
		} else if (parameter == FLD_LAST_USED_PROFILE) {
			last_used_profile = value;
		} else if (parameter == FLD_REDIAL_URL) {
			redial_url.set_url(value);
			if (!redial_url.is_valid()) {
				redial_url.set_url("");
			}
		} else if (parameter == FLD_REDIAL_DISPLAY) {
			redial_display = value;
		} else if (parameter == FLD_REDIAL_SUBJECT) {
			redial_subject = value;
		} else if (parameter == FLD_REDIAL_PROFILE) {
			redial_profile = value;
		} else if (parameter == FLD_REDIAL_HIDE_USER) {
			redial_hide_user = yesno2bool(value);
		} else if (parameter == FLD_DIAL_HISTORY) {
			dial_history.push_back(value);
		} else if (parameter == FLD_SHOW_DISPLAY) {
			show_display = yesno2bool(value);
		} else if (parameter == FLD_COMPACT_LINE_STATUS) {
			//compact_line_status = yesno2bool(value);
		} else if (parameter == FLD_SHOW_BUDDY_LIST) {
			show_buddy_list = yesno2bool(value);
		} else if (parameter == FLD_WARN_HIDE_USER) {
			warn_hide_user = yesno2bool(value);
		}
			
		// Unknown field names are skipped.
	}
		
	mtx_sys.unlock();
	return true;
}

bool t_sys_settings::write_config(string &error_msg) {
	struct stat stat_buf;
	
	mtx_sys.lock();
	
	// Make a backup of the file if we are editing an existing file, so
	// that can be restored when writing fails.
	string f_backup = filename + '~';
	if (stat(filename.c_str(), &stat_buf) == 0) {
		if (rename(filename.c_str(), f_backup.c_str()) != 0) {
			string err = get_error_str(errno);
			error_msg = TRANSLATE("Failed to backup %1 to %2");
			error_msg = replace_first(error_msg, "%1", filename);
			error_msg = replace_first(error_msg, "%2", f_backup);
			error_msg += "\n";
			error_msg += err;
			mtx_sys.unlock();
			return false;
		}
	}
	
	// Open file
	ofstream config(filename.c_str());
	if (!config) {
		error_msg = TRANSLATE("Cannot open file for writing: %1");
		error_msg = replace_first(error_msg, "%1", filename);
		mtx_sys.unlock();
		return false;
	}
	
	// Write AUDIO settings
	config << "# AUDIO\n";
	config << FLD_DEV_RINGTONE << '=' << dev_ringtone.get_settings_value() << endl;
	config << FLD_DEV_SPEAKER << '=' << dev_speaker.get_settings_value() << endl;
	config << FLD_DEV_MIC << '=' << dev_mic.get_settings_value() << endl;
	config << FLD_VALIDATE_AUDIO_DEV << '=' << bool2yesno(validate_audio_dev) << endl;
	config << FLD_AU_REDUCE_NOISE_MIC << '=' << bool2yesno(au_reduce_noise_mic) << endl;
	config << FLD_ALSA_PLAY_PERIOD_SIZE << '=' << alsa_play_period_size << endl;
	config << FLD_ALSA_CAPTURE_PERIOD_SIZE << '=' << alsa_capture_period_size << endl;
	config << FLD_OSS_FRAGMENT_SIZE << '=' << oss_fragment_size << endl;
	config << endl;
	
	// Write LOG settings
	config << "# LOG\n";
	config << FLD_LOG_MAX_SIZE << '=' << log_max_size << endl;
	config << FLD_LOG_SHOW_SIP << '=' << bool2yesno(log_show_sip) << endl;
	config << FLD_LOG_SHOW_STUN << '=' << bool2yesno(log_show_stun) << endl;
	config << FLD_LOG_SHOW_MEMORY << '=' << bool2yesno(log_show_memory) << endl;
	config << FLD_LOG_SHOW_DEBUG << '=' << bool2yesno(log_show_debug) << endl;
	config << endl;
	
	// Write GUI settings
	config << "# GUI\n";
	config << FLD_GUI_USE_SYSTRAY << '=' << bool2yesno(gui_use_systray) << endl;
	config << FLD_GUI_HIDE_ON_CLOSE << '=' << bool2yesno(gui_hide_on_close) << endl;
	config << FLD_GUI_AUTO_SHOW_INCOMING << '=' << bool2yesno(gui_auto_show_incoming) << endl;
	config << FLD_GUI_AUTO_SHOW_TIMEOUT << '=' << gui_auto_show_timeout << endl;
	config << endl;
	
	// Write address book settings
	config << "# Address book\n";
	config << FLD_AB_SHOW_SIP_ONLY << '=' << bool2yesno(ab_show_sip_only) << endl;
	config << FLD_AB_LOOKUP_NAME << '=' << bool2yesno(ab_lookup_name) << endl;
	config << FLD_AB_OVERRIDE_DISPLAY << '=' << bool2yesno(ab_override_display) << endl;
	config << FLD_AB_LOOKUP_PHOTO << '=' << bool2yesno(ab_lookup_photo) << endl;
	config << endl;
	
	// Write call history settings
	config << "# Call history\n";
	config << FLD_CH_MAX_SIZE << '=' << ch_max_size << endl;
	config << endl;
	
	// Write service settings
	config << "# Services\n";
	config << FLD_CALL_WAITING << '=' << bool2yesno(call_waiting) << endl;
	config << FLD_HANGUP_BOTH_3WAY << '=' << bool2yesno(hangup_both_3way) << endl;
	config << endl;
	
	// Write startup settings
	config << "# Startup\n";
	
	for (list<string>::iterator i = start_user_profiles.begin();
	     i != start_user_profiles.end(); i++)
	{
		config << FLD_START_USER_PROFILE << '=' << *i << endl;
	}
	config << FLD_START_USER_HOST << '=' << start_user_host << endl;
	config << FLD_START_USER_NIC  << '=' << start_user_nic << endl;
	config << FLD_START_HIDDEN << '=' << bool2yesno(start_hidden) << endl;
	config << endl;
	
	// Write network settings
	config << "# Network\n";
	config << FLD_SIP_UDP_PORT << '=' << config_sip_udp_port << endl;
	config << FLD_RTP_PORT << '=' << rtp_port << endl;
	config << endl;
	
	// Write ring tone settings
	config << "# Ring tones\n";
	config << FLD_PLAY_RINGTONE << '=' << bool2yesno(play_ringtone) << endl;
	config << FLD_RINGTONE_FILE << '=' << ringtone_file << endl;
	config << FLD_PLAY_RINGBACK << '=' << bool2yesno(play_ringback) << endl;
	config << FLD_RINGBACK_FILE << '=' << ringback_file << endl;
	config << endl;
	
	// Write persistent user interface state
	config << "# Persistent user interface state\n";
	config << FLD_LAST_USED_PROFILE << '=' << last_used_profile << endl;
	config << FLD_REDIAL_URL << '=' << redial_url.encode() << endl;
	config << FLD_REDIAL_DISPLAY << '=' << redial_display << endl; 
	config << FLD_REDIAL_SUBJECT << '=' << redial_subject << endl;
	config << FLD_REDIAL_PROFILE << '=' << redial_profile << endl;
	config << FLD_REDIAL_HIDE_USER << '=' << bool2yesno(redial_hide_user) << endl;
	config << FLD_SHOW_DISPLAY << '=' << bool2yesno(show_display) << endl;
	//config << FLD_COMPACT_LINE_STATUS << '=' << bool2yesno(compact_line_status) << endl;
	config << FLD_SHOW_BUDDY_LIST << '=' << bool2yesno(show_buddy_list) << endl;
	config << FLD_WARN_HIDE_USER << '=' << bool2yesno(warn_hide_user) << endl;
	
	for (list<string>::iterator i = dial_history.begin();
	     i != dial_history.end(); i++)
	{
		config << FLD_DIAL_HISTORY << '=' << *i << endl;
	}
	
	config << endl;
	
	// Check if writing succeeded
	if (!config.good()) {
		// Restore backup
		config.close();
		rename(f_backup.c_str(), filename.c_str());

		error_msg = TRANSLATE("File system error while writing file %1 .");
		error_msg = replace_first(error_msg, "%1", filename);
		mtx_sys.unlock();
		return false;
	}
	
	mtx_sys.unlock();
	return true;
}

list<t_audio_device> t_sys_settings::get_oss_devices(bool playback) const {
	struct stat stat_buf;
	list<t_audio_device> l;
	
	for (int i = -1; i <= 15; i ++) {
		string dev = "/dev/dsp";
		if (i >= 0) dev += int2str(i);
		t_audio_device oss_dev;
		oss_dev.type = t_audio_device::OSS;
		
		// Check if device exists
		if (stat(dev.c_str(), &stat_buf) != 0) continue;
		
		oss_dev.device = dev;
		
		// Get sound card name
		int fd;
		
		if (playback) {
			fd = open(dev.c_str(), O_WRONLY | O_NONBLOCK);
		} else {
			fd = open(dev.c_str(), O_RDONLY | O_NONBLOCK);
		}
		
		if (fd >= 0) {
			struct mixer_info soundcard_info;
			if (ioctl(fd, SOUND_MIXER_INFO, &soundcard_info) != -1) {
				oss_dev.name = "";
				oss_dev.name += soundcard_info.name;
				oss_dev.name += " (";
				oss_dev.name += soundcard_info.id;
				oss_dev.name += ")";
			}
			
			close(fd);
		} else {
			if (errno == EBUSY) {
				oss_dev.name = TRANSLATE("unknown name (device is busy)");
			} else {
				// Device is not available.
				continue;
			}
		}
		
		// Check if the device is a symbolic link
		char buf[32];
		int len_link;
		if ((len_link = readlink(dev.c_str(), buf, 31)) != -1) {
			buf[len_link] = 0;
			oss_dev.sym_link = buf;
		}
		oss_dev.type = t_audio_device::OSS;
		l.push_back(oss_dev);
	}
	
	// If no OSS devices can be found (this should not happen), then
	// just add /dev/dsp as the default device.
	if (l.empty()) {
		t_audio_device oss_dev;
		oss_dev.device = "/dev/dsp";
		oss_dev.type = t_audio_device::OSS;
		l.push_back(oss_dev);
	}
	
	// Add other device option
	t_audio_device other_dev;
	other_dev.device = DEV_OTHER;
	other_dev.type = t_audio_device::OSS;
	l.push_back(other_dev);
	
	return l;
}

#ifdef HAVE_LIBASOUND
// Defined in audio_device.cpp
void alsa_fill_soundcards(list<t_audio_device>& l, bool playback);

list<t_audio_device> t_sys_settings::get_alsa_devices(bool playback) const {
	t_audio_device defaultDevice;
	defaultDevice.device = "default";
	defaultDevice.name = TRANSLATE("Default device");
	defaultDevice.type = t_audio_device::ALSA;
	list<t_audio_device> l;
	l.push_back(defaultDevice);
	
	alsa_fill_soundcards(l, playback);
	
	// Add other device option
	t_audio_device other_dev;
	other_dev.device = DEV_OTHER;
	other_dev.type = t_audio_device::ALSA;
	l.push_back(other_dev);
	
	return l;
}
#endif

list<t_audio_device> t_sys_settings::get_audio_devices(bool playback) const {
	list<t_audio_device> d, d0;
	
#ifdef HAVE_LIBASOUND
	d = get_alsa_devices(playback);
#endif
	d0 = get_oss_devices(playback);
	d.insert(d.end(), d0.begin(), d0.end());
	return d;
}

bool t_sys_settings::equal_audio_dev(const t_audio_device &dev1, const t_audio_device &dev2) const {
	if (dev1.type == t_audio_device::OSS) {
		if (dev2.type != t_audio_device::OSS) return false;
		if (dev1.device == dev2.device) return true;
		
		char symlink1[32], symlink2[32];
		int len_link1, len_link2;
		
		len_link1 = readlink(dev1.device.c_str(), symlink1, 31);
		len_link2 = readlink(dev2.device.c_str(), symlink2, 31);
	
		if (len_link1 > 0) {
			symlink1[len_link1] = 0;
			string symdev1 = "/dev/";
			symdev1 += symlink1;
			if (len_link2 > 0) {
				symlink2[len_link2] = 0;
				string symdev2 = "/dev/";
				symdev2 += symlink2;
				return symdev1 == symdev2;
			} else {
				return dev2.device == symdev1;
			}
		} else {
			if (len_link2 > 0) {
				symlink2[len_link2] = 0;
				string symdev2 = "/dev/";
				symdev2 += symlink2;
				return dev1.device == symdev2;
			}
		}
	} else if (dev1.type == t_audio_device::ALSA) {
		if (dev2.type != t_audio_device::ALSA) return false;
		return dev1.device == dev2.device;
	}
		
	return false;
}


t_audio_device t_sys_settings::audio_device(string device) {
	t_audio_device d;

	if (device.empty()) device = DEV_DSP; //This is the default device
	
	if (device.substr(0, strlen(PFX_OSS)) == PFX_OSS) {
		// OSS device
		d.device = device.substr(strlen(PFX_OSS));
		d.type = t_audio_device::OSS;
		d.name = "";
		char symlink[32];
		int len_link = readlink(device.c_str(), symlink, 31);
		if(len_link > 0) {
			d.sym_link = symlink;
		}
	} else if (device.substr(0, strlen(PFX_ALSA)) == PFX_ALSA) {
		// ALSA device
		d.device = device.substr(strlen(PFX_ALSA));
		d.type = t_audio_device::ALSA;
		d.name = "";
		d.sym_link = "";
	} else {
		// Assume it is an OSS device. Version 0.2.1 and lower
		// only supported OSS and the value only consisted of
		// the device name without "oss:"
		d.device = device;
		d.type = t_audio_device::OSS;
		d.name = "";
		char symlink[32];
		int len_link = readlink(device.c_str(), symlink, 31);
		if(len_link > 0) {
			d.sym_link = symlink;
		}
	}
	
	return d;	
}

bool t_sys_settings::exec_audio_validation(bool ringtone, bool speaker, bool mic, 
	string &error_msg) const 
{
	error_msg.clear();
	if (!validate_audio_dev) return true;
	
	bool valid = true;
	bool full_duplex = speaker && mic && equal_audio_dev(dev_speaker, dev_mic);
	
	if (ringtone && !t_audio_io::validate(dev_ringtone, true, false)) {
		string msg = TRANSLATE("Cannot access the ring tone device (%1).");
		error_msg += replace_first(msg, "%1", dev_ringtone.get_description());
		error_msg += "\n";
		valid = false;
	}
	if (speaker && !t_audio_io::validate(dev_speaker, true, full_duplex)) {
		string msg = TRANSLATE("Cannot access the speaker (%1).");
		error_msg += replace_first(msg, "%1", dev_speaker.get_description());
		error_msg += "\n";
		valid = false;
	}
	if (mic && !t_audio_io::validate(dev_mic, full_duplex, true)) {
		string msg = TRANSLATE("Cannot access the microphone (%1).");
		error_msg += replace_first(msg, "%1", dev_mic.get_description());
		error_msg += "\n";
		valid = false;
	}
	
	return valid;
}

unsigned short t_sys_settings::get_sip_udp_port(bool force_active) {
	mtx_sys.lock();
	
	// The configured port becomes the active port after first
	// usage of the port.
	if (!active_sip_udp_port || force_active) {
		if (override_sip_udp_port > 0) {
			// The port provided on the command line overrides
			// the configured port.
			active_sip_udp_port = override_sip_udp_port;
		} else {
			active_sip_udp_port = config_sip_udp_port;
		}
	}
	
	mtx_sys.unlock();
	return active_sip_udp_port;
}


syntax highlighted by Code2HTML, v. 0.9.1