diff --git a/astylerc b/astylerc index 7f14310574b90b9d41b3065853175a0edf06b0b6..d38cbbf0f6ed52b6e978ad7e191d9ca8f4102945 100644 --- a/astylerc +++ b/astylerc @@ -15,5 +15,4 @@ unpad-paren # Remove unwanted space around parentheses pad-header # Insert space padding after paren headers only (e.g. 'if', 'for', 'while'...) pad-oper # Insert space padding around operator formatted # only display files that have changed -recursive # recursively enter subdirs suffix=none # don't create backup files (that's what version control is for) diff --git a/daemon/src/account_schema.h b/daemon/src/account_schema.h index 91359c1d3f243b5a0fdd30cc831a62c81f3d6505..5e72f5f39b6ca1d3eef84f770c8448d448f609f2 100644 --- a/daemon/src/account_schema.h +++ b/daemon/src/account_schema.h @@ -67,6 +67,10 @@ static const char *const CONFIG_ACCOUNT_PASSWORD = "Account.passw static const char *const CONFIG_ACCOUNT_REALM = "Account.realm"; static const char *const CONFIG_ACCOUNT_DEFAULT_REALM = "*"; static const char *const CONFIG_ACCOUNT_USERAGENT = "Account.useragent"; +static const char *const CONFIG_ACCOUNT_AUDIO_PORT_MIN = "Account.audioPortMin"; +static const char *const CONFIG_ACCOUNT_AUDIO_PORT_MAX = "Account.audioPortMax"; +static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MIN = "Account.videoPortMin"; +static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MAX = "Account.videoPortMax"; static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface"; static const char *const CONFIG_INTERFACE = "Account.interface"; diff --git a/daemon/src/audio/alsa/alsalayer.cpp b/daemon/src/audio/alsa/alsalayer.cpp index 4fb286fd7319c0d2e0e539786af998c139028612..11319d0a17bff314e15879c323a23544371a56ff 100644 --- a/daemon/src/audio/alsa/alsalayer.cpp +++ b/daemon/src/audio/alsa/alsalayer.cpp @@ -602,8 +602,9 @@ AlsaLayer::getAudioDeviceIndexMap(bool getCapture) const snd_pcm_info_set_device(pcminfo , 0); snd_pcm_info_set_stream(pcminfo, getCapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK); - if (snd_ctl_pcm_info(handle ,pcminfo) < 0) { - DEBUG(" Cannot get info"); + int err; + if ((err = snd_ctl_pcm_info(handle ,pcminfo)) < 0) { + WARN("Cannot get info: %s", snd_strerror(err)); } else { DEBUG("card %i : %s [%s]", numCard, diff --git a/daemon/src/audio/audiortp/audio_rtp_record_handler.cpp b/daemon/src/audio/audiortp/audio_rtp_record_handler.cpp index f51a08d610f683cc23db5602c098095f44a2e14c..eef438ae5a2a421fa33ed0a5ea6e46956a9dffb8 100644 --- a/daemon/src/audio/audiortp/audio_rtp_record_handler.cpp +++ b/daemon/src/audio/audiortp/audio_rtp_record_handler.cpp @@ -87,6 +87,7 @@ DTMFEvent::DTMFEvent(char digit) : payload(), newevent(true), length(1000) payload.ebit = false; // end of event bit payload.rbit = false; // reserved bit payload.duration = 1; // duration for this event + payload.vol = 10; } AudioRtpRecord::AudioRtpRecord() : diff --git a/daemon/src/audio/audiortp/audio_rtp_session.cpp b/daemon/src/audio/audiortp/audio_rtp_session.cpp index 04bce53c96e37413917de5e9bfe566a781dbe191..2fe2c829379cef343079da6de39fbf63bfec186f 100644 --- a/daemon/src/audio/audiortp/audio_rtp_session.cpp +++ b/daemon/src/audio/audiortp/audio_rtp_session.cpp @@ -117,7 +117,8 @@ void AudioRtpSession::sendDtmfEvent() DEBUG("Send RTP Dtmf (%d)", dtmf.payload.event); const int increment = getIncrementForDTMF(); - timestamp_ += increment; + if (dtmf.newevent) + timestamp_ += increment; // discard equivalent size of audio processDataEncode(); @@ -129,7 +130,11 @@ void AudioRtpSession::sendDtmfEvent() if (dtmf.newevent) queue_.setMark(true); - queue_.sendImmediate(timestamp_, (const unsigned char *)(& (dtmf.payload)), sizeof(ost::RTPPacket::RFC2833Payload)); + // Send end packet three times (without changing it). Sequence number is + // incremented automatically by ccrtp, which is the correct behaviour. + const unsigned repetitions = dtmf.payload.ebit ? 3 : 1; + for (unsigned i = 0; i < repetitions; ++i) + queue_.sendImmediate(timestamp_, (const unsigned char *)(& (dtmf.payload)), sizeof(ost::RTPPacket::RFC2833Payload)); // This is no longer a new event if (dtmf.newevent) { @@ -145,7 +150,7 @@ void AudioRtpSession::sendDtmfEvent() dtmf.length -= increment; dtmf.payload.duration++; - // next packet is going to be the last one + // next packet is going to be the end packet (transmitted 3 times) if ((dtmf.length - increment) < increment) dtmf.payload.ebit = true; diff --git a/daemon/src/audio/pulseaudio/pulselayer.cpp b/daemon/src/audio/pulseaudio/pulselayer.cpp index 26f59c0b159e1546a90d43f883854f5f1142d6c7..17eb7e4bc5a041b100dee6eda7b778dd71bee87b 100644 --- a/daemon/src/audio/pulseaudio/pulselayer.cpp +++ b/daemon/src/audio/pulseaudio/pulselayer.cpp @@ -509,8 +509,8 @@ void PulseLayer::readFromMic() const char *data = NULL; size_t bytes; - size_t sample_size = record_->sampleSize(); - uint8_t channels = record_->channels(); + const size_t sample_size = record_->sampleSize(); + const uint8_t channels = record_->channels(); if (pa_stream_peek(record_->pulseStream() , (const void**) &data , &bytes) < 0 or !data) return; @@ -519,7 +519,9 @@ void PulseLayer::readFromMic() outfile.write((const char *)data, bytes); #endif - size_t samples = bytes / sample_size; + assert(channels); + assert(sample_size); + const size_t samples = bytes / sample_size / channels; AudioBuffer in(samples, channels, sampleRate_); in.deinterleave((SFLAudioSample*)data, samples, channels); @@ -527,22 +529,10 @@ void PulseLayer::readFromMic() unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer().getInternalSamplingRate(); bool resample = sampleRate_ != mainBufferSampleRate; - /*if (resample) { - double resampleFactor = (double) sampleRate_ / mainBufferSampleRate; - //bytes = (double) bytes * resampleFactor; - }*/ - - /*if (bytes > mic_buf_size_) { - mic_buf_size_ = bytes; - delete [] mic_buffer_; - mic_buffer_ = new SFLAudioSample[samples]; - }*/ - AudioBuffer * out = ∈ if (resample) { mic_buffer_.setSampleRate(mainBufferSampleRate); - //converter_.resample((SFLAudioSample*)data, mic_buffer_, samples, mainBufferSampleRate, sampleRate_, samples); converter_.resample(in, mic_buffer_); out = &mic_buffer_; } diff --git a/daemon/src/audio/samplerateconverter.cpp b/daemon/src/audio/samplerateconverter.cpp index 337e0df4f654b4c88b87bbeebcc57931434c22e4..fbc32c16647ade82c2c8db23bbacc2985c71ff44 100644 --- a/daemon/src/audio/samplerateconverter.cpp +++ b/daemon/src/audio/samplerateconverter.cpp @@ -32,7 +32,7 @@ #include "sfl_types.h" SamplerateConverter::SamplerateConverter(int freq, size_t channels /* = 1 */) : floatBufferIn_(), - floatBufferOut_(), samples_(0), channels_(channels), maxFreq_(freq), src_state_(0) + floatBufferOut_(), scratchBuffer_(), samples_(0), channels_(channels), maxFreq_(freq), src_state_(0) { int err; src_state_ = src_new(SRC_LINEAR, channels_, &err); @@ -41,6 +41,7 @@ SamplerateConverter::SamplerateConverter(int freq, size_t channels /* = 1 */) : floatBufferIn_.resize(samples_); floatBufferOut_.resize(samples_); + scratchBuffer_.resize(samples_); } SamplerateConverter::~SamplerateConverter() @@ -86,6 +87,7 @@ void SamplerateConverter::resample(const AudioBuffer &dataIn, AudioBuffer &dataO samples_ = std::max(inSamples, outSamples); floatBufferIn_.resize(inSamples); floatBufferOut_.resize(outSamples); + scratchBuffer_.resize(outSamples); SRC_DATA src_data; src_data.data_in = floatBufferIn_.data(); @@ -101,9 +103,7 @@ void SamplerateConverter::resample(const AudioBuffer &dataIn, AudioBuffer &dataO /* TODO: one-shot deinterleave and float-to-short conversion - currently using floatBufferIn_ as scratch */ - SFLAudioSample *scratch_buff = reinterpret_cast<SFLAudioSample *>(floatBufferIn_.data()); - src_float_to_short_array(floatBufferOut_.data(), scratch_buff, outSamples); - dataOut.deinterleave(scratch_buff, src_data.output_frames, nbChans); + src_float_to_short_array(floatBufferOut_.data(), scratchBuffer_.data(), outSamples); + dataOut.deinterleave(scratchBuffer_.data(), src_data.output_frames, nbChans); } diff --git a/daemon/src/audio/samplerateconverter.h b/daemon/src/audio/samplerateconverter.h index 2b533e208347e6359eaf78a511532118659b30e2..41f0e8be5e737940a77598aa000abdd9d4e19d24 100644 --- a/daemon/src/audio/samplerateconverter.h +++ b/daemon/src/audio/samplerateconverter.h @@ -78,6 +78,7 @@ class SamplerateConverter { /* temporary buffers */ std::vector<float> floatBufferIn_; std::vector<float> floatBufferOut_; + std::vector<SFLAudioSample> scratchBuffer_; size_t samples_; // size in samples of temporary buffers size_t channels_; // number of channels configured diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index 4fd90f795a516cb46ff57f0e6fbf1b2b5a4f275e..8b19bdf0ed11e2502c21504e7cbcae55989a3f0c 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -2466,9 +2466,14 @@ ManagerImpl::addAccount(const std::map<std::string, std::string>& details) /** @todo Deal with both the accountMap_ and the Configuration */ std::stringstream accountID; + std::string accountList(preferences.getAccountOrder()); accountID << "Account:" << time(NULL); std::string newAccountID(accountID.str()); + while (accountList.find(newAccountID) != std::string::npos) { + newAccountID += "1"; + } + // Get the type std::string accountType; @@ -2501,16 +2506,14 @@ ManagerImpl::addAccount(const std::map<std::string, std::string>& details) newAccount->setAccountDetails(details); - // Add the newly created account in the account order list - std::string accountList(preferences.getAccountOrder()); - newAccountID += "/"; + // Add the newly created account in the account order list if (not accountList.empty()) { // Prepend the new account - accountList.insert(0, newAccountID); + accountList.insert(0, newAccountID + "/"); preferences.setAccountOrder(accountList); } else { - accountList = newAccountID; + accountList = newAccountID + "/"; preferences.setAccountOrder(accountList); } @@ -2522,7 +2525,7 @@ ManagerImpl::addAccount(const std::map<std::string, std::string>& details) client_.getConfigurationManager()->accountsChanged(); - return accountID.str(); + return newAccountID; } void ManagerImpl::removeAccount(const std::string& accountID) diff --git a/daemon/src/sip/sdp.cpp b/daemon/src/sip/sdp.cpp index 64766b9dcb286a64905801ebc93f9aa001046302..ef92c84d5b0fea2d97b2c9b031392c211a4b1910 100644 --- a/daemon/src/sip/sdp.cpp +++ b/daemon/src/sip/sdp.cpp @@ -39,6 +39,7 @@ #include "manager.h" #include <algorithm> +#include "sipaccount.h" #ifdef HAVE_OPUS #include "audio/codecs/opus.h" @@ -64,7 +65,7 @@ Sdp::Sdp(pj_pool_t *pool) , video_codec_list_() , sessionAudioMedia_() , sessionVideoMedia_() - , localIpAddr_() + , publishedIpAddr_() , remoteIpAddr_() , localAudioDataPort_(0) , localAudioControlPort_(0) @@ -77,6 +78,14 @@ Sdp::Sdp(pj_pool_t *pool) , telephoneEventPayload_(101) // same as asterisk {} +Sdp::~Sdp() +{ + SIPAccount::releasePort(localAudioDataPort_); +#ifdef SFL_VIDEO + SIPAccount::releasePort(localVideoDataPort_); +#endif +} + namespace { bool hasPayload(const std::vector<sfl::AudioCodec*> &codecs, int pt) { @@ -258,7 +267,7 @@ Sdp::setMediaDescriptorLines(bool audio) #ifdef HAVE_OPUS // Opus sample rate is allways declared as 48000 and channel num is allways 2 in rtpmap as per // http://tools.ietf.org/html/draft-spittka-payload-rtp-opus-03#section-6.2 - if(payload == Opus::PAYLOAD_TYPE) { + if (payload == Opus::PAYLOAD_TYPE) { rtpmap.clock_rate = 48000; rtpmap.param.ptr = ((char* const)"2"); rtpmap.param.slen = 1; @@ -276,9 +285,9 @@ Sdp::setMediaDescriptorLines(bool audio) #ifdef HAVE_OPUS // Declare stereo support for opus - if(payload == Opus::PAYLOAD_TYPE) { + if (payload == Opus::PAYLOAD_TYPE) { std::ostringstream os; - os << "fmtp:" << payload << " stereo=1; sprop-stereo=" << (channels>1 ? 1 : 0); + os << "fmtp:" << payload << " stereo=1; sprop-stereo=" << (channels > 1); med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, os.str().c_str(), NULL); } #endif @@ -314,7 +323,7 @@ Sdp::setMediaDescriptorLines(bool audio) void Sdp::addRTCPAttribute(pjmedia_sdp_media *med) { std::ostringstream os; - os << localIpAddr_ << ":" << localAudioControlPort_; + os << publishedIpAddr_ << ":" << localAudioControlPort_; const std::string str(os.str()); pj_str_t input_str = pj_str((char*) str.c_str()); pj_sockaddr outputAddr; @@ -328,6 +337,18 @@ void Sdp::addRTCPAttribute(pjmedia_sdp_media *med) pjmedia_sdp_attr_add(&med->attr_count, med->attr, attr); } +void +Sdp::setPublishedIP(const std::string &ip_addr) +{ + publishedIpAddr_ = ip_addr; + if (localSession_) { + localSession_->origin.addr = pj_str((char*) publishedIpAddr_.c_str()); + localSession_->conn->addr = localSession_->origin.addr; + if (pjmedia_sdp_validate(localSession_) != PJ_SUCCESS) + ERROR("Could not validate SDP"); + } +} + void Sdp::updatePorts(const std::vector<pj_sockaddr_in> &sockets) { @@ -335,6 +356,20 @@ Sdp::updatePorts(const std::vector<pj_sockaddr_in> &sockets) localAudioControlPort_ = pj_ntohs(sockets[1].sin_port); localVideoDataPort_ = pj_ntohs(sockets[2].sin_port); localVideoControlPort_ = pj_ntohs(sockets[3].sin_port); + + if (localSession_) { + if (localSession_->media[0]) { + localSession_->media[0]->desc.port = localAudioDataPort_; + // update RTCP attribute + if (pjmedia_sdp_media_remove_all_attr(localSession_->media[0], "rtcp")) + addRTCPAttribute(localSession_->media[0]); + } + if (localSession_->media[1]) + localSession_->media[1]->desc.port = localVideoDataPort_; + + if (not pjmedia_sdp_validate(localSession_)) + ERROR("Could not validate SDP"); + } } @@ -415,7 +450,7 @@ int Sdp::createLocalSession(const vector<int> &selectedAudioCodecs, const vector localSession_->origin.id = tv.sec + 2208988800UL; localSession_->origin.net_type = pj_str((char*) "IN"); localSession_->origin.addr_type = pj_str((char*) "IP4"); - localSession_->origin.addr = pj_str((char*) localIpAddr_.c_str()); + localSession_->origin.addr = pj_str((char*) publishedIpAddr_.c_str()); localSession_->name = pj_str((char*) PACKAGE); diff --git a/daemon/src/sip/sdp.h b/daemon/src/sip/sdp.h index 1ac680aebceafe00238ffcff9bc4d9d310e91bab..f6c74f03e51d078e514f2bbb4db7aee887898493 100644 --- a/daemon/src/sip/sdp.h +++ b/daemon/src/sip/sdp.h @@ -69,6 +69,8 @@ class Sdp { */ Sdp(pj_pool_t *pool); + ~Sdp(); + /** * Accessor for the internal memory pool */ @@ -151,15 +153,13 @@ class Sdp { /* * Write accessor. Set the local IP address that will be used in the sdp session */ - void setLocalIP(const std::string &ip_addr) { - localIpAddr_ = ip_addr; - } + void setPublishedIP(const std::string &ip_addr); /* * Read accessor. Get the local IP address */ - std::string getLocalIP() const { - return localIpAddr_; + std::string getPublishedIP() const { + return publishedIpAddr_; } void setLocalPublishedAudioPort(int port) { @@ -309,7 +309,7 @@ class Sdp { std::vector<sfl::AudioCodec *> sessionAudioMedia_; std::vector<std::string> sessionVideoMedia_; - std::string localIpAddr_; + std::string publishedIpAddr_; std::string remoteIpAddr_; int localAudioDataPort_; diff --git a/daemon/src/sip/sip_utils.cpp b/daemon/src/sip/sip_utils.cpp index 82a3b07dd334ec9edd000bf0c75cbc1fbafbd717..27dcc33169900178438de9ced29781b449623a90 100644 --- a/daemon/src/sip/sip_utils.cpp +++ b/daemon/src/sip/sip_utils.cpp @@ -191,3 +191,16 @@ sip_utils::getIPList(const std::string &name) freeaddrinfo(result); return ipList; } + +void +sip_utils::addContactHeader(const std::string &contactStr, pjsip_tx_data *tdata) +{ + pj_str_t pjContact = pj_str((char*) contactStr.c_str()); + + pjsip_contact_hdr *contact = pjsip_contact_hdr_create(tdata->pool); + contact->uri = pjsip_parse_uri(tdata->pool, pjContact.ptr, + pjContact.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + // remove old contact header (if present) + pjsip_msg_find_remove_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact); +} diff --git a/daemon/src/sip/sip_utils.h b/daemon/src/sip/sip_utils.h index 91cf9e4b41d1064a1eea862c4e446ed2dc952323..32d531db998b5c0145a45c65b85c8fc58a444ab3 100644 --- a/daemon/src/sip/sip_utils.h +++ b/daemon/src/sip/sip_utils.h @@ -54,6 +54,8 @@ namespace sip_utils { std::string parseDisplayName(const char * buffer); std::vector<std::string> getIPList(const std::string &name); + + void addContactHeader(const std::string &contactStr, pjsip_tx_data *tdata); } #endif // SIP_UTILS_H_ diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp index 881571ff33f64522b2532807cfc8a79fc2948546..a52f57ff1783568d1512aeaae1bb54af6430babe 100644 --- a/daemon/src/sip/sipaccount.cpp +++ b/daemon/src/sip/sipaccount.cpp @@ -48,6 +48,8 @@ #include <sstream> #include <algorithm> #include <cstdlib> +#include <array> +#include <memory> #ifdef SFL_VIDEO @@ -67,6 +69,9 @@ const char *const TRUE_STR = "true"; const char *const FALSE_STR = "false"; } +// we force RTP ports to be even, so we only need HALF_MAX_PORT booleans +bool SIPAccount::portsInUse_[HALF_MAX_PORT]; + SIPAccount::SIPAccount(const std::string& accountID) : Account(accountID) , transport_(NULL) @@ -118,9 +123,9 @@ SIPAccount::SIPAccount(const std::string& accountID) , receivedParameter_("") , rPort_(-1) , via_addr_() - , audioPortRange_(16384, 32766) + , audioPortRange_({16384, 32766}) #ifdef SFL_VIDEO - , videoPortRange_(49152, 65534) + , videoPortRange_({49152, (MAX_PORT) - 2}) #endif { via_addr_.host.ptr = 0; @@ -131,11 +136,55 @@ SIPAccount::SIPAccount(const std::string& accountID) alias_ = IP2IP_PROFILE; } + SIPAccount::~SIPAccount() { delete presence_; } + +namespace { +std::array<std::unique_ptr<Conf::ScalarNode>, 2> +serializeRange(Conf::MappingNode &accountMap, const char *minKey, const char *maxKey, const std::pair<uint16_t, uint16_t> &range) +{ + using namespace Conf; + std::array<std::unique_ptr<ScalarNode>, 2> result; + + std::ostringstream os; + os << range.first; + result[0].reset(new ScalarNode(os.str())); + os.str(""); + accountMap.setKeyValue(minKey, result[0].get()); + + os << range.second; + ScalarNode portMax(os.str()); + result[1].reset(new ScalarNode(os.str())); + accountMap.setKeyValue(maxKey, result[1].get()); + return result; +} + + +void updateRange(int min, int max, std::pair<uint16_t, uint16_t> &range) +{ + if (min > 0 and (max > min) and max <= MAX_PORT - 2) { + range.first = min; + range.second = max; + } +} + +void +unserializeRange(const Conf::YamlNode &mapNode, const char *minKey, const char *maxKey, std::pair<uint16_t, uint16_t> &range) +{ + int tmpMin = 0; + int tmpMax = 0; + mapNode.getValue(minKey, &tmpMin); + mapNode.getValue(maxKey, &tmpMax); + updateRange(tmpMin, tmpMax, range); +} +} + + + void SIPAccount::serialize(Conf::YamlEmitter &emitter) { using namespace Conf; @@ -289,6 +338,11 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) ScalarNode userAgent(userAgent_); accountmap.setKeyValue(USER_AGENT_KEY, &userAgent); + auto audioPortNodes(serializeRange(accountmap, AUDIO_PORT_MIN_KEY, AUDIO_PORT_MAX_KEY, audioPortRange_)); +#ifdef SFL_VIDEO + auto videoPortNodes(serializeRange(accountmap, VIDEO_PORT_MIN_KEY, VIDEO_PORT_MAX_KEY, videoPortRange_)); +#endif + try { emitter.serializeAccount(&accountmap); } catch (const YamlEmitterException &e) { @@ -494,6 +548,11 @@ void SIPAccount::unserialize(const Conf::YamlNode &mapNode) tlsMap->getValue(TIMEOUT_KEY, &tlsNegotiationTimeoutMsec_); } mapNode.getValue(USER_AGENT_KEY, &userAgent_); + + unserializeRange(mapNode, AUDIO_PORT_MIN_KEY, AUDIO_PORT_MAX_KEY, audioPortRange_); +#ifdef SFL_VIDEO + unserializeRange(mapNode, VIDEO_PORT_MIN_KEY, VIDEO_PORT_MAX_KEY, videoPortRange_); +#endif } void SIPAccount::setAccountDetails(std::map<std::string, std::string> details) @@ -535,6 +594,15 @@ void SIPAccount::setAccountDetails(std::map<std::string, std::string> details) userAgent_ = details[CONFIG_ACCOUNT_USERAGENT]; keepAliveEnabled_ = details[CONFIG_KEEP_ALIVE_ENABLED] == TRUE_STR; + int tmpMin = atoi(details[CONFIG_ACCOUNT_AUDIO_PORT_MIN].c_str()); + int tmpMax = atoi(details[CONFIG_ACCOUNT_AUDIO_PORT_MAX].c_str()); + updateRange(tmpMin, tmpMax, audioPortRange_); +#ifdef SFL_VIDEO + tmpMin = atoi(details[CONFIG_ACCOUNT_VIDEO_PORT_MIN].c_str()); + tmpMax = atoi(details[CONFIG_ACCOUNT_VIDEO_PORT_MAX].c_str()); + updateRange(tmpMin, tmpMax, videoPortRange_); +#endif + // srtp settings srtpEnabled_ = details[CONFIG_SRTP_ENABLE] == TRUE_STR; srtpFallback_ = details[CONFIG_SRTP_RTP_FALLBACK] == TRUE_STR; @@ -590,6 +658,17 @@ static std::string retrievePassword(const std::map<std::string, std::string>& ma return ""; } +void +addRangeToDetails(std::map<std::string, std::string> &a, const char *minKey, const char *maxKey, const std::pair<uint16_t, uint16_t> &range) +{ + std::ostringstream os; + os << range.first; + a[minKey] = os.str(); + os.str(""); + os << range.second; + a[maxKey] = os.str(); +} + std::map<std::string, std::string> SIPAccount::getAccountDetails() const { std::map<std::string, std::string> a; @@ -643,6 +722,11 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() const a[CONFIG_ACCOUNT_ROUTESET] = serviceRoute_; a[CONFIG_ACCOUNT_USERAGENT] = userAgent_; + addRangeToDetails(a, CONFIG_ACCOUNT_AUDIO_PORT_MIN, CONFIG_ACCOUNT_AUDIO_PORT_MAX, audioPortRange_); +#ifdef SFL_VIDEO + addRangeToDetails(a, CONFIG_ACCOUNT_VIDEO_PORT_MIN, CONFIG_ACCOUNT_VIDEO_PORT_MAX, videoPortRange_); +#endif + std::stringstream registrationExpireStr; registrationExpireStr << registrationExpire_; a[CONFIG_ACCOUNT_REGISTRATION_EXPIRE] = registrationExpireStr.str(); @@ -809,7 +893,8 @@ void SIPAccount::trimCiphers() { int sum = 0; int count = 0; - // PJSIP aborts if our cipher list exceeds 1010 characters + + // PJSIP aborts if our cipher list exceeds 1000 characters static const int MAX_CIPHERS_STRLEN = 1000; for (const auto &item : ciphers_) { @@ -1031,12 +1116,15 @@ std::string SIPAccount::getContactHeader() const link_->sipTransport.findLocalAddressFromTransport(transport_, transportType, address, port); - if (!receivedParameter_.empty()) + if (!receivedParameter_.empty()) { address = receivedParameter_; + DEBUG("Using received address %s", address.c_str()); + } - if (rPort_ != -1) { + if (rPort_ != -1 and rPort_ != 0) { portstr << rPort_; port = portstr.str(); + DEBUG("Using received port %s", port.c_str()); } // UDP does not require the transport specification @@ -1316,24 +1404,39 @@ bool SIPAccount::matches(const std::string &userName, const std::string &server, return false; } -namespace { - // returns even number in range [lower, upper] - unsigned int getRandomEvenNumber(const std::pair<unsigned, unsigned> &range) - { - const unsigned halfUpper = range.second * 0.5; - const unsigned halfLower = range.first * 0.5; - return 2 * (halfLower + rand() % (halfUpper - halfLower + 1)); - } +SIPPresence * SIPAccount::getPresence(){ + return presence_; +} + +// returns even number in range [lower, upper] +uint16_t +SIPAccount::getRandomEvenNumber(const std::pair<uint16_t, uint16_t> &range) +{ + const uint16_t halfUpper = range.second * 0.5; + const uint16_t halfLower = range.first * 0.5; + uint16_t result; + do { + result = 2 * (halfLower + rand() % (halfUpper - halfLower + 1)); + } while (portsInUse_[result / 2]); + + portsInUse_[result / 2] = true; + return result; +} + +void +SIPAccount::releasePort(uint16_t port) +{ + portsInUse_[port / 2] = false; } -unsigned +uint16_t SIPAccount::generateAudioPort() const { return getRandomEvenNumber(audioPortRange_); } #ifdef SFL_VIDEO -unsigned +uint16_t SIPAccount::generateVideoPort() const { return getRandomEvenNumber(videoPortRange_); diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h index c4b3c520c77e9e22805c622397c5db2761a2e451..e88eb66cb49888e486e7449b760b8c1611a385b6 100644 --- a/daemon/src/sip/sipaccount.h +++ b/daemon/src/sip/sipaccount.h @@ -92,6 +92,12 @@ namespace Conf { const char *const STUN_ENABLED_KEY = "stunEnabled"; const char *const STUN_SERVER_KEY = "stunServer"; const char *const CRED_KEY = "credential"; + const char *const AUDIO_PORT_MIN_KEY = "audioPortMin"; + const char *const AUDIO_PORT_MAX_KEY = "audioPortMax"; +#ifdef SFL_VIDEO + const char *const VIDEO_PORT_MIN_KEY = "videoPortMin"; + const char *const VIDEO_PORT_MAX_KEY = "videoPortMax"; +#endif } class SIPVoIPLink; @@ -102,6 +108,8 @@ class SIPPresence; * @file sipaccount.h * @brief A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink) */ +enum {MAX_PORT = 65536}; +enum {HALF_MAX_PORT = MAX_PORT / 2}; class SIPAccount : public Account { public: @@ -129,6 +137,9 @@ class SIPAccount : public Account { */ bool isIP2IP() const; + static void + releasePort(uint16_t port); + /** * Serialize internal state of this account for configuration * @param YamlEmitter the configuration engine which generate the configuration file @@ -526,8 +537,9 @@ class SIPAccount : public Account { SIPPresence * getPresence(); unsigned generateAudioPort() const; + uint16_t generateAudioPort() const; #ifdef SFL_VIDEO - unsigned generateVideoPort() const; + uint16_t generateVideoPort() const; #endif private: @@ -786,14 +798,17 @@ class SIPAccount : public Account { /* * Port range for audio RTP ports */ - std::pair<unsigned, unsigned> audioPortRange_; + std::pair<uint16_t, uint16_t> audioPortRange_; #ifdef SFL_VIDEO /** * Port range for video RTP ports */ - std::pair<unsigned, unsigned> videoPortRange_; + std::pair<uint16_t, uint16_t> videoPortRange_; #endif + static bool portsInUse_[HALF_MAX_PORT]; + static uint16_t getRandomEvenNumber(const std::pair<uint16_t, uint16_t> &range); + }; #endif diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp index 5a1b7a0eb992eb8f714506fd4b2eb4f78ee3c415..da2768662bed7932445f0e42292e367268eaffe4 100644 --- a/daemon/src/sip/sipcall.cpp +++ b/daemon/src/sip/sipcall.cpp @@ -32,6 +32,7 @@ */ #include "sipcall.h" +#include "sip_utils.h" #include "logger.h" // for _debug #include "sdp.h" #include "manager.h" @@ -55,6 +56,7 @@ SIPCall::SIPCall(const std::string& id, Call::CallType type, #endif , pool_(pj_pool_create(&caching_pool->factory, id.c_str(), INITIAL_SIZE, INCREMENT_SIZE, NULL)) , local_sdp_(new Sdp(pool_)) + , contactHeader_() {} SIPCall::~SIPCall() @@ -63,6 +65,11 @@ SIPCall::~SIPCall() pj_pool_release(pool_); } +void SIPCall::setContactHeader(const std::string &contact) +{ + contactHeader_ = contact; +} + void SIPCall::answer() { pjsip_tx_data *tdata; @@ -73,6 +80,12 @@ void SIPCall::answer() if (pjsip_inv_answer(inv, PJSIP_SC_OK, NULL, !inv->neg ? local_sdp_->getLocalSdpSession() : NULL, &tdata) != PJ_SUCCESS) throw std::runtime_error("Could not init invite request answer (200 OK)"); + // contactStr must stay in scope as long as tdata + if (not contactHeader_.empty()) { + DEBUG("Answering with contact header: %s", contactHeader_.c_str()); + sip_utils::addContactHeader(contactHeader_, tdata); + } + if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS) throw std::runtime_error("Could not send invite request answer (200 OK)"); diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h index e97b6851c2892e00beb81a9c0932ec28e40a1f00..fa2f840b96f3e217e0089f64dd7d5d70439c74d5 100644 --- a/daemon/src/sip/sipcall.h +++ b/daemon/src/sip/sipcall.h @@ -108,6 +108,8 @@ class SIPCall : public Call { */ pjsip_inv_session *inv; + void setContactHeader(const std::string &contact); + private: // override of Call::getDetails std::map<std::string, std::string> @@ -140,6 +142,8 @@ class SIPCall : public Call { * The SDP session */ Sdp *local_sdp_; + + std::string contactHeader_; }; #endif // __SIPCALL_H__ diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp index 85f7c19bf2cf3142d17916d97d4899594d16df50..8a7167eed66351da5b0c1d40f24d90c907bb3dc4 100644 --- a/daemon/src/sip/siptransport.cpp +++ b/daemon/src/sip/siptransport.cpp @@ -204,6 +204,12 @@ pj_status_t SipTransport::createStunResolver(pj_str_t serverName, pj_uint16_t po pj_stun_config_init(&stunCfg, &cp_->factory, 0, pjsip_endpt_get_ioqueue(endpt_), pjsip_endpt_get_timer_heap(endpt_)); + pj_status_t status = pj_stun_config_check_valid(&stunCfg); + if (status != PJ_SUCCESS) { + ERROR("STUN config is not valid"); + return status; + } + static const pj_stun_sock_cb stun_sock_cb = { stun_sock_on_rx_data_cb, NULL, @@ -211,7 +217,7 @@ pj_status_t SipTransport::createStunResolver(pj_str_t serverName, pj_uint16_t po }; pj_stun_sock *stun_sock = NULL; - pj_status_t status = pj_stun_sock_create(&stunCfg, + status = pj_stun_sock_create(&stunCfg, stunResolverName.c_str(), pj_AF_INET(), &stun_sock_cb, NULL, NULL, &stun_sock); diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index 1fd38cc484d5f5999045a76449d30b35291e0daa..afb21800250469d16488f5f4d147b3dd7305ac22 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -229,23 +229,14 @@ void updateSDPFromSTUN(SIPCall &call, SIPAccount &account, const SipTransport &t stunPorts.resize(4); account.setPublishedAddress(pj_inet_ntoa(stunPorts[0].sin_addr)); + // published IP MUST be updated first, since RTCP depends on it + call.getLocalSDP()->setPublishedIP(account.getPublishedAddress()); call.getLocalSDP()->updatePorts(stunPorts); } catch (const std::runtime_error &e) { ERROR("%s", e.what()); } } -void -addContactHeader(const std::string &contactStr, pjsip_tx_data *tdata) -{ - pj_str_t pjContact = pj_str((char*) contactStr.c_str()); - - pjsip_contact_hdr *contact = pjsip_contact_hdr_create(tdata->pool); - contact->uri = pjsip_parse_uri(tdata->pool, pjContact.ptr, - pjContact.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact); -} - pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) { @@ -357,7 +348,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) setCallMediaLocal(call, addrToUse); - call->getLocalSDP()->setLocalIP(addrSdp); + call->getLocalSDP()->setPublishedIP(addrSdp); call->getAudioRtp().initConfig(); try { @@ -482,7 +473,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) // contactStr must stay in scope as long as tdata const std::string contactStr(account->getContactHeader()); - addContactHeader(contactStr, tdata); + sip_utils::addContactHeader(contactStr, tdata); if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS) { ERROR("Could not send msg for invite"); @@ -959,7 +950,7 @@ Call *SIPVoIPLink::SIPNewIpToIpCall(const std::string& id, const std::string& to // Building the local SDP offer Sdp *localSDP = call->getLocalSDP(); - localSDP->setLocalIP(localAddress); + localSDP->setPublishedIP(localAddress); const bool created = localSDP->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); if (not created or not SIPStartCall(call)) { @@ -1028,7 +1019,7 @@ Call *SIPVoIPLink::newRegisteredAccountCall(const std::string& id, const std::st call->initRecFilename(toUrl); Sdp *localSDP = call->getLocalSDP(); - localSDP->setLocalIP(addrSdp); + localSDP->setPublishedIP(addrSdp); const bool created = localSDP->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); if (not created or not SIPStartCall(call)) { @@ -1046,13 +1037,22 @@ SIPVoIPLink::answer(Call *call) return; SIPCall *sipCall = static_cast<SIPCall*>(call); + SIPAccount *account = Manager::instance().getSipAccount(sipCall->getAccountId()); + if (!account) { + ERROR("Could not find account %s", sipCall->getAccountId().c_str()); + return; + } + if (!sipCall->inv->neg) { WARN("Negotiator is NULL, we've received an INVITE without an SDP"); - pjmedia_sdp_session *dummy; + pjmedia_sdp_session *dummy = 0; sdp_create_offer_cb(sipCall->inv, &dummy); + if (account->isStunEnabled()) + updateSDPFromSTUN(*sipCall, *account, SIPVoIPLink::instance()->sipTransport); } - call->answer(); + sipCall->setContactHeader(account->getContactHeader()); + sipCall->answer(); } namespace { @@ -1110,7 +1110,7 @@ SIPVoIPLink::hangup(const std::string& id, int reason) // contactStr must stay in scope as long as tdata const std::string contactStr(account->getContactHeader()); - addContactHeader(contactStr, tdata); + sip_utils::addContactHeader(contactStr, tdata); if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS) return; @@ -1821,7 +1821,7 @@ void sdp_create_offer_cb(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer) setCallMediaLocal(call, localAddress); Sdp *localSDP = call->getLocalSDP(); - localSDP->setLocalIP(addrSdp); + localSDP->setPublishedIP(addrSdp); const bool created = localSDP->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); if (created) *p_offer = localSDP->getLocalSdpSession(); @@ -2391,10 +2391,9 @@ void setCallMediaLocal(SIPCall* call, const std::string &localIP) #ifdef SFL_VIDEO if (call->getLocalVideoPort() == 0) { // https://projects.savoirfairelinux.com/issues/17498 - unsigned int callLocalVideoPort; - do - callLocalVideoPort = account->generateVideoPort(); - while (call->getLocalAudioPort() == callLocalVideoPort); + const unsigned int callLocalVideoPort = account->generateVideoPort(); + // this should already be guaranteed by SIPAccount + assert(call->getLocalAudioPort() != callLocalVideoPort); call->setLocalVideoPort(callLocalVideoPort); call->getLocalSDP()->setLocalPublishedVideoPort(callLocalVideoPort); diff --git a/daemon/src/video/shm_sink.cpp b/daemon/src/video/shm_sink.cpp index ab3c4f5937a9727cd57663aaee712f0199a729b9..7ff67e2a6c6137719ecc5e50307ca347e50c146e 100644 --- a/daemon/src/video/shm_sink.cpp +++ b/daemon/src/video/shm_sink.cpp @@ -146,7 +146,7 @@ SHMSink::stop() bool SHMSink::resize_area(size_t desired_length) { - if (desired_length < shm_area_len_) + if (desired_length <= shm_area_len_) return true; shm_unlock(); diff --git a/daemon/src/video/video_rtp_session.cpp b/daemon/src/video/video_rtp_session.cpp index a3f44ea2ab478c3a2b83778577b208513ede4221..e9cc9ad76ca0cdd86ef3fd07d4e018ec087b38d5 100644 --- a/daemon/src/video/video_rtp_session.cpp +++ b/daemon/src/video/video_rtp_session.cpp @@ -131,7 +131,7 @@ void VideoRtpSession::start(int localPort) try { socketPair_.reset(new SocketPair(txArgs_["destination"].c_str(), localPort)); } catch (const std::runtime_error &e) { - ERROR("Socket creation failed: %s", e.what()); + ERROR("Socket creation failed on port %d: %s", localPort, e.what()); return; } diff --git a/daemon/test/sdptest.cpp b/daemon/test/sdptest.cpp index 6020471f802997ab038b195c2aedeef968ef1ff7..428df49f826904301f39fc44c6543a0d9693b349 100644 --- a/daemon/test/sdptest.cpp +++ b/daemon/test/sdptest.cpp @@ -141,7 +141,7 @@ void SDPTest::testInitialOfferFirstCodec() { std::cout << "------------ SDPTest::testInitialOfferFirstCodec --------------" << std::endl; - CPPUNIT_ASSERT(session_->getLocalIP().empty()); + CPPUNIT_ASSERT(session_->getPublishedIP().empty()); CPPUNIT_ASSERT(session_->getRemoteIP().empty()); std::vector<int> codecSelection; @@ -152,7 +152,7 @@ void SDPTest::testInitialOfferFirstCodec() std::vector<std::map<std::string, std::string> > videoCodecs(createVideoCodecs()); - session_->setLocalIP(LOCALHOST); + session_->setPublishedIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); session_->createOffer(codecSelection, videoCodecs); @@ -165,7 +165,7 @@ void SDPTest::testInitialOfferFirstCodec() session_->setMediaTransportInfoFromRemoteSdp(); - CPPUNIT_ASSERT(session_->getLocalIP() == LOCALHOST); + CPPUNIT_ASSERT(session_->getPublishedIP() == LOCALHOST); CPPUNIT_ASSERT(session_->getRemoteIP() == "host.example.com"); } @@ -173,7 +173,7 @@ void SDPTest::testInitialAnswerFirstCodec() { std::cout << "------------ SDPTest::testInitialAnswerFirstCodec -------------" << std::endl; - CPPUNIT_ASSERT(session_->getLocalIP().empty()); + CPPUNIT_ASSERT(session_->getPublishedIP().empty()); CPPUNIT_ASSERT(session_->getRemoteIP().empty()); std::vector<int> codecSelection; @@ -185,7 +185,7 @@ void SDPTest::testInitialAnswerFirstCodec() pjmedia_sdp_parse(testPool_, (char*) sdp_offer1, strlen(sdp_offer1), &remoteOffer); - session_->setLocalIP(LOCALHOST); + session_->setPublishedIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); session_->receiveOffer(remoteOffer, codecSelection, createVideoCodecs()); @@ -194,7 +194,7 @@ void SDPTest::testInitialAnswerFirstCodec() session_->setMediaTransportInfoFromRemoteSdp(); - CPPUNIT_ASSERT(session_->getLocalIP() == LOCALHOST); + CPPUNIT_ASSERT(session_->getPublishedIP() == LOCALHOST); CPPUNIT_ASSERT(session_->getRemoteIP() == "host.example.com"); } @@ -202,7 +202,7 @@ void SDPTest::testInitialOfferLastCodec() { std::cout << "------------ SDPTest::testInitialOfferLastCodec --------------------" << std::endl; - CPPUNIT_ASSERT(session_->getLocalIP().empty()); + CPPUNIT_ASSERT(session_->getPublishedIP().empty()); CPPUNIT_ASSERT(session_->getRemoteIP().empty()); std::vector<int> codecSelection; @@ -211,7 +211,7 @@ void SDPTest::testInitialOfferLastCodec() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); - session_->setLocalIP(LOCALHOST); + session_->setPublishedIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); session_->createOffer(codecSelection, createVideoCodecs()); @@ -224,7 +224,7 @@ void SDPTest::testInitialOfferLastCodec() session_->setMediaTransportInfoFromRemoteSdp(); - CPPUNIT_ASSERT(session_->getLocalIP() == LOCALHOST); + CPPUNIT_ASSERT(session_->getPublishedIP() == LOCALHOST); CPPUNIT_ASSERT(session_->getRemoteIP() == "host.example.com"); } @@ -232,7 +232,7 @@ void SDPTest::testInitialAnswerLastCodec() { std::cout << "------------ SDPTest::testInitialAnswerLastCodec ------------" << std::endl; - CPPUNIT_ASSERT(session_->getLocalIP().empty()); + CPPUNIT_ASSERT(session_->getPublishedIP().empty()); CPPUNIT_ASSERT(session_->getRemoteIP().empty()); std::vector<int> codecSelection; @@ -244,7 +244,7 @@ void SDPTest::testInitialAnswerLastCodec() pjmedia_sdp_parse(testPool_, (char*)sdp_offer2, strlen(sdp_offer2), &remoteOffer); - session_->setLocalIP(LOCALHOST); + session_->setPublishedIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); session_->receiveOffer(remoteOffer, codecSelection, createVideoCodecs()); @@ -253,7 +253,7 @@ void SDPTest::testInitialAnswerLastCodec() session_->setMediaTransportInfoFromRemoteSdp(); - CPPUNIT_ASSERT(session_->getLocalIP() == LOCALHOST); + CPPUNIT_ASSERT(session_->getPublishedIP() == LOCALHOST); CPPUNIT_ASSERT(session_->getRemoteIP() == "host.example.com"); } @@ -262,7 +262,7 @@ void SDPTest::testReinvite() { std::cout << "------------ SDPTest::testReinvite --------------------" << std::endl; - CPPUNIT_ASSERT(session_->getLocalIP().empty()); + CPPUNIT_ASSERT(session_->getPublishedIP().empty()); CPPUNIT_ASSERT(session_->getRemoteIP().empty()); std::vector<int> codecSelection; @@ -270,7 +270,7 @@ void SDPTest::testReinvite() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); - session_->setLocalIP(LOCALHOST); + session_->setPublishedIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); std::vector<std::map<std::string, std::string> > videoCodecs(createVideoCodecs()); @@ -285,7 +285,7 @@ void SDPTest::testReinvite() session_->setMediaTransportInfoFromRemoteSdp(); - CPPUNIT_ASSERT(session_->getLocalIP() == LOCALHOST); + CPPUNIT_ASSERT(session_->getPublishedIP() == LOCALHOST); CPPUNIT_ASSERT(session_->getRemoteIP() == "host.example.com"); std::vector<sfl::AudioCodec*> codecs; session_->getSessionAudioMedia(codecs); diff --git a/gnome/po/LINGUAS b/gnome/po/LINGUAS index e828f7e15dd6e88561f03a444d463ca26d9d5f56..0fbd7e4fa4c0dbc777940e477c4db02545a267b4 100644 --- a/gnome/po/LINGUAS +++ b/gnome/po/LINGUAS @@ -24,6 +24,7 @@ sv te tr uz +vi zh_CN zh_HK zh_TW diff --git a/gnome/po/vi.po b/gnome/po/vi.po new file mode 100644 index 0000000000000000000000000000000000000000..9ac49a04ad82aaad7b66b28e7ee6fe6a96e19be2 --- /dev/null +++ b/gnome/po/vi.po @@ -0,0 +1,1315 @@ +# Vietnamese translation for sflphone +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the sflphone package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: sflphone\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2013-06-12 16:32-0400\n" +"PO-Revision-Date: 2013-08-15 18:57+0000\n" +"Last-Translator: Tho Duy Nguyen <nguyenduytho@gmail.com>\n" +"Language-Team: Vietnamese <vi@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-08-15 19:28+0000\n" +"X-Generator: Launchpad (build 16723)\n" + +#: ../src/accountlist.c:155 +msgid "Registered" +msgstr "Đã đăng ký" + +#: ../src/accountlist.c:157 +msgid "Not Registered" +msgstr "Chưa đăng ký" + +#: ../src/accountlist.c:159 +msgid "Trying..." +msgstr "Đang thử" + +#: ../src/accountlist.c:161 ../src/sflnotify.c:170 ../src/sflnotify.c:183 +#, c-format +msgid "Error" +msgstr "Lỗi" + +#: ../src/accountlist.c:163 +msgid "Authentication Failed" +msgstr "Xác thực thất bại" + +#: ../src/accountlist.c:165 +msgid "Network unreachable" +msgstr "Mạng không thể tiếp cận" + +#: ../src/accountlist.c:167 +msgid "Host unreachable" +msgstr "Máy không thể tiếp cận" + +#: ../src/accountlist.c:169 +msgid "Service unavailable" +msgstr "Dịch vụ không sẵn sàng" + +#: ../src/accountlist.c:171 +msgid "Not acceptable" +msgstr "Không thích hợp" + +#: ../src/accountlist.c:173 +msgid "Stun server invalid" +msgstr "Máy chủ Stun không xác định" + +#: ../src/accountlist.c:175 +msgid "Ready" +msgstr "Sẵn sàng" + +#: ../src/accountlist.c:177 +msgid "Invalid" +msgstr "Không hợp lệ" + +#: ../src/actions.c:136 +msgid "Using account" +msgstr "Sử dụng tài khoản" + +#: ../src/actions.c:140 +#, c-format +msgid "No registered accounts" +msgstr "Tài khoản chưa được đăng ký" + +#: ../src/actions.c:500 +#, c-format +msgid "Direct SIP call" +msgstr "Gọi trực tiếp bằng SIP" + +#. less than 1 week +#. less than 1 day +#: ../src/callable_obj.c:306 +msgid "today at %R" +msgstr "hôm nay lúc %R" + +#. less than 2 days +#: ../src/callable_obj.c:309 +msgid "yesterday at %R" +msgstr "hôm qua lúc %R" + +#. between 2 days and 1 week +#: ../src/callable_obj.c:311 +msgid "%A at %R" +msgstr "%A lúc %R" + +#. more than 1 week +#: ../src/callable_obj.c:315 +msgid "%x at %R" +msgstr "%x lúc %R" + +#: ../src/config/accountconfigdialog.c:218 +msgid "_Auto-answer calls" +msgstr "Tự động trả lời cuộc gọi" + +#: ../src/config/accountconfigdialog.c:238 +msgid "Account Parameters" +msgstr "Tham số tài khoản" + +#. alias field +#: ../src/config/accountconfigdialog.c:247 ../src/config/assistant.c:305 +#: ../src/config/assistant.c:403 +msgid "_Alias" +msgstr "Bí d_anh:" + +#: ../src/config/accountconfigdialog.c:258 +msgid "_Protocol" +msgstr "_Phương thức" + +#. Should never come here, add debug message. +#: ../src/config/accountconfigdialog.c:275 +msgid "Unknown" +msgstr "Không rõ" + +#. server field +#: ../src/config/accountconfigdialog.c:290 ../src/config/assistant.c:313 +#: ../src/config/assistant.c:411 +msgid "_Host name" +msgstr "_Tên máy chủ" + +#. username field +#: ../src/config/accountconfigdialog.c:300 ../src/config/assistant.c:321 +#: ../src/config/assistant.c:419 +msgid "_User name" +msgstr "_Tên người dùng" + +#. password field +#: ../src/config/accountconfigdialog.c:321 ../src/config/assistant.c:331 +#: ../src/config/assistant.c:428 +msgid "_Password" +msgstr "Mật _khẩu" + +#: ../src/config/accountconfigdialog.c:340 ../src/config/assistant.c:340 +#: ../src/config/assistant.c:437 +msgid "Show password" +msgstr "Hiển thị mật khẩu" + +#: ../src/config/accountconfigdialog.c:345 +msgid "_Proxy" +msgstr "_Proxy" + +#. voicemail number field +#: ../src/config/accountconfigdialog.c:355 ../src/config/assistant.c:345 +#: ../src/config/assistant.c:442 +msgid "_Voicemail number" +msgstr "_Số hộp thư thoại" + +#: ../src/config/accountconfigdialog.c:366 +msgid "_User-agent" +msgstr "_User-agent" + +#: ../src/config/accountconfigdialog.c:419 +msgid "Authentication" +msgstr "Xác thực" + +#: ../src/config/accountconfigdialog.c:420 +msgid "Secret" +msgstr "Bí mật" + +#: ../src/config/accountconfigdialog.c:669 +msgid "Credential" +msgstr "Ủy nhiệm" + +#: ../src/config/accountconfigdialog.c:701 +msgid "Authentication name" +msgstr "Tên xác thực" + +#: ../src/config/accountconfigdialog.c:712 +msgid "Password" +msgstr "Mật khẩu" + +#: ../src/config/accountconfigdialog.c:764 +#: ../src/config/accountconfigdialog.c:1380 +msgid "Security" +msgstr "Bảo mật" + +#: ../src/config/accountconfigdialog.c:780 +msgid "Use TLS transport(sips)" +msgstr "Dùng giao thức TLS (SIPS)" + +#. ZRTP subsection +#: ../src/config/accountconfigdialog.c:788 +msgid "SRTP key exchange" +msgstr "Trao đổi mã SRTP" + +#: ../src/config/accountconfigdialog.c:794 +msgid "Disabled" +msgstr "Tắt" + +#: ../src/config/accountconfigdialog.c:858 +msgid "Registration" +msgstr "Đăng ký" + +#: ../src/config/accountconfigdialog.c:862 +msgid "Registration expire" +msgstr "Đăng ký đã hết hạn" + +#: ../src/config/accountconfigdialog.c:885 +msgid "Network Interface" +msgstr "Giao diện mạng" + +#: ../src/config/accountconfigdialog.c:895 +msgid "Local address" +msgstr "Địa chỉ cục bộ" + +#. Local port widget +#: ../src/config/accountconfigdialog.c:928 +msgid "Local port" +msgstr "Cổng mạng cục bộ" + +#: ../src/config/accountconfigdialog.c:969 +#: ../src/config/accountconfigdialog.c:1004 +msgid "Published address" +msgstr "Địa chỉ công cộng" + +#: ../src/config/accountconfigdialog.c:973 +msgid "Using STUN" +msgstr "Sử dụng STUN" + +#: ../src/config/accountconfigdialog.c:980 +msgid "STUN server URL" +msgstr "URL máy chủ STUN" + +#: ../src/config/accountconfigdialog.c:988 +msgid "Same as local parameters" +msgstr "Giống như tham số cục bộ" + +#: ../src/config/accountconfigdialog.c:992 +msgid "Set published address and port:" +msgstr "Thiết lập địa chỉ và cổng mạng công cộng" + +#: ../src/config/accountconfigdialog.c:1013 +msgid "Published port" +msgstr "Cổng mạng công khai" + +#. Box for the audiocodecs +#: ../src/config/accountconfigdialog.c:1076 +#: ../src/config/accountconfigdialog.c:1361 +#: ../src/config/preferencesdialog.c:418 +msgid "Audio" +msgstr "Âm thanh" + +#: ../src/config/accountconfigdialog.c:1088 +msgid "DTMF" +msgstr "DTMF" + +#: ../src/config/accountconfigdialog.c:1092 +msgid "RTP" +msgstr "RTP" + +#: ../src/config/accountconfigdialog.c:1098 +msgid "SIP" +msgstr "SIP" + +#: ../src/config/accountconfigdialog.c:1106 +msgid "Ringtones" +msgstr "Nhạc chuông" + +#: ../src/config/accountconfigdialog.c:1109 +msgid "Choose a ringtone" +msgstr "Chọn nhạc chuông" + +#: ../src/config/accountconfigdialog.c:1112 +msgid "_Enable ringtones" +msgstr "_Bật nhạc chuông" + +#: ../src/config/accountconfigdialog.c:1125 +msgid "Audio Files" +msgstr "Tệp âm thanh" + +#. Box for the videocodecs +#: ../src/config/accountconfigdialog.c:1149 +#: ../src/config/accountconfigdialog.c:1367 +#: ../src/config/preferencesdialog.c:424 +msgid "Video" +msgstr "Phim" + +#: ../src/config/accountconfigdialog.c:1166 +#, c-format +msgid "" +"This profile is used when you want to reach a remote peer simply by typing a " +"sip URI such as <b>sip:remotepeer</b>. The settings you define here will " +"also be used if no account can be matched to an incoming or outgoing call." +msgstr "" +"Cấu hình này được dùng khi bạn muốn gọi bằng cái gõ URI sip ví dụ như " +"<b>sip:remotepeer</b>. Các thiết lập mà bạn định ra ở đây cũng được dùng nếu " +"như không có tài khỏan tương ứng với các cuộc gọi đi và đến." + +#: ../src/config/accountconfigdialog.c:1334 +msgid "Account settings" +msgstr "Các thiết lập tài khoản" + +#: ../src/config/accountconfigdialog.c:1355 +msgid "Basic" +msgstr "Cơ bản" + +#: ../src/config/accountconfigdialog.c:1375 +msgid "Advanced" +msgstr "Tiên tiến" + +#: ../src/config/accountconfigdialog.c:1385 +msgid "Network" +msgstr "mạng" + +#: ../src/config/accountlistconfigdialog.c:487 +msgid "Protocol" +msgstr "Giao thức" + +#: ../src/config/accountlistconfigdialog.c:497 +msgid "Status" +msgstr "Trạng thái" + +#: ../src/config/accountlistconfigdialog.c:595 +#, c-format +msgid "Server returned \"%s\" (%d)" +msgstr "Máy chủ trả lời \"%s\" (%d)" + +#: ../src/config/accountlistconfigdialog.c:617 +msgid "Accounts" +msgstr "Các tài khoản" + +#: ../src/config/accountlistconfigdialog.c:626 +msgid "Configured Accounts" +msgstr "Các tài khoản đã thiết lập" + +#: ../src/config/accountlistconfigdialog.c:652 +msgid "You have no active account" +msgstr "Bạn không có tài khoản có hiệu lực" + +#: ../src/config/addressbook-config.c:313 +#: ../src/config/preferencesdialog.c:412 ../src/config/shortcuts-config.c:117 +msgid "General" +msgstr "General" + +#. PHOTO DISPLAY +#: ../src/config/addressbook-config.c:317 +msgid "_Use Evolution address books" +msgstr "_Sử dụng danh bạ của Evolution" + +#: ../src/config/addressbook-config.c:324 +msgid "Download limit :" +msgstr "Giới hạn tải xuống" + +#: ../src/config/addressbook-config.c:332 +msgid "cards" +msgstr "thẻ" + +#. PHOTO DISPLAY +#: ../src/config/addressbook-config.c:337 +msgid "_Display contact photo if available" +msgstr "_Hiển thị hình của liên lạc nếu có" + +#. Fields +#: ../src/config/addressbook-config.c:344 +msgid "Fields from Evolution's address books" +msgstr "Các trường từ danh bạ Evolution" + +#: ../src/config/addressbook-config.c:347 +msgid "_Work" +msgstr "_Chạy" + +#: ../src/config/addressbook-config.c:353 +msgid "_Home" +msgstr "_Thư mục chính" + +#: ../src/config/addressbook-config.c:359 +msgid "_Mobile" +msgstr "_Mobile" + +#. Address Book +#: ../src/config/addressbook-config.c:365 +msgid "Address Books" +msgstr "Danh bạ" + +#: ../src/config/addressbook-config.c:369 +msgid "Select which Evolution address books to use" +msgstr "Hãy chọn trong các danh bạ Evolution" + +#: ../src/config/addressbook-config.c:402 ../src/config/audioconf.c:538 +#: ../src/config/videoconf.c:492 +msgid "Name" +msgstr "Tên:" + +#: ../src/config/assistant.c:76 +msgid "This assistant is now finished." +msgstr "Sự hỗ trợ này kết thúc ở đây" + +#: ../src/config/assistant.c:79 +msgid "" +"You can at any time check your registration state or modify your accounts " +"parameters in the Options/Accounts window." +msgstr "" +"Bạn có thể kiểm tra tình trạng đăng ký hoặc thay đổi các tham số tài khoản " +"trong cửa sổLựa chọn/Tài khoản" + +#: ../src/config/assistant.c:81 +msgid "Alias" +msgstr "Bí danh" + +#: ../src/config/assistant.c:82 +msgid "Server" +msgstr "Máy chủ" + +#: ../src/config/assistant.c:83 +msgid "Username" +msgstr "Tên người dùng" + +#: ../src/config/assistant.c:84 +msgid "Security: " +msgstr "Bảo mật " + +#: ../src/config/assistant.c:88 +msgid "SRTP/ZRTP draft-zimmermann" +msgstr "SRTP/ZRTP draft-zimmermann" + +#: ../src/config/assistant.c:90 +msgid "None" +msgstr "Không có" + +#: ../src/config/assistant.c:226 +msgid "SFLphone account creation wizard" +msgstr "Trợ giúp tạo tài khoản SFLphone" + +#: ../src/config/assistant.c:251 +msgid "Welcome to the Account creation wizard of SFLphone!" +msgstr "Chào mừng bạn đến với trợ giúp tạo tài khoản SFLphone" + +#: ../src/config/assistant.c:252 +msgid "This installation wizard will help you configure an account." +msgstr "Sự trợ giúp bạn cấu hình một tài khoản" + +#: ../src/config/assistant.c:264 +msgid "VoIP Protocols" +msgstr "Các giao thức VoIP" + +#: ../src/config/assistant.c:264 +msgid "Select an account type" +msgstr "Hãy lựa chọn một kiểu tài khoản" + +#: ../src/config/assistant.c:266 +msgid "SIP (Session Initiation Protocol)" +msgstr "SIP (Session Initiation Protocol)" + +#: ../src/config/assistant.c:268 +msgid "IAX2 (InterAsterix Exchange)" +msgstr "IAX2 (InterAsterix Exchange)" + +#: ../src/config/assistant.c:280 +msgid "Account" +msgstr "Tài khoản" + +#: ../src/config/assistant.c:280 +msgid "Please select one of the following options" +msgstr "Hãy chọn một trong các lựa chọn sau" + +#: ../src/config/assistant.c:282 +msgid "" +"Create a free SIP/IAX2 account on sflphone.org \n" +"(For testing purpose only)" +msgstr "Tạo một tài khoản SIP/IAX2 miễn phí tại sflphone.org" + +#: ../src/config/assistant.c:284 +msgid "Register an existing SIP or IAX2 account" +msgstr "Nhập vào một tài khoản SIP hoặc IAX2 đã có" + +#: ../src/config/assistant.c:297 +msgid "SIP account settings" +msgstr "Thiết lập tài khoản SIP" + +#: ../src/config/assistant.c:297 ../src/config/assistant.c:395 +msgid "Please fill the following information" +msgstr "Hãy điền vào các thông tin sau" + +#. Security options +#: ../src/config/assistant.c:353 ../src/config/assistant.c:382 +msgid "Secure communications with _ZRTP" +msgstr "Bảo mật liên lạc với _ZRTP" + +#: ../src/config/assistant.c:366 +msgid "Optional email address" +msgstr "Địa chỉ thư điện tử tùy chọn" + +#: ../src/config/assistant.c:366 +msgid "This email address will be used to send your voicemail messages." +msgstr "Địa chỉ thư điện tử sau sẽ được dùng để gửi các tin thư thoại" + +#. email field +#: ../src/config/assistant.c:374 +msgid "_Email address" +msgstr "_Thư điện tử" + +#: ../src/config/assistant.c:395 +msgid "IAX2 account settings" +msgstr "Thiết lập tài khoản IAX2" + +#: ../src/config/assistant.c:460 +msgid "Network Address Translation (NAT)" +msgstr "Network Address Translation (NAT)" + +#: ../src/config/assistant.c:460 +msgid "You should probably enable this if you are behind a firewall." +msgstr "Có thể bạn phải bật tính năng này nếu bạn ở sau tường lửa." + +#. enable +#: ../src/config/assistant.c:469 +msgid "E_nable STUN" +msgstr "_Bật STUN" + +#. server address +#: ../src/config/assistant.c:476 +msgid "_STUN server" +msgstr "Máy chủ _STUN" + +#: ../src/config/assistant.c:491 +msgid "Account Registration" +msgstr "Đăng ký tài khoản" + +#: ../src/config/assistant.c:491 +msgid "Congratulations!" +msgstr "Xin chúc mừng!" + +#: ../src/config/audioconf.c:543 +msgid "Frequency" +msgstr "Tần số" + +#: ../src/config/audioconf.c:548 +msgid "Bitrate" +msgstr "Bitrate" + +#: ../src/config/audioconf.c:591 +msgid "ALSA plugin" +msgstr "ALSA plugin" + +#: ../src/config/audioconf.c:612 ../src/config/audioconf.c:693 +msgid "Output" +msgstr "Đầu ra" + +#: ../src/config/audioconf.c:633 ../src/config/audioconf.c:715 +msgid "Input" +msgstr "Đầu vào" + +#: ../src/config/audioconf.c:653 ../src/config/audioconf.c:735 +msgid "Ringtone" +msgstr "Nhạc chuông" + +#: ../src/config/audioconf.c:852 +msgid "Sound Manager" +msgstr "Trình quản lý âm thanh" + +#: ../src/config/audioconf.c:859 +msgid "_Pulseaudio" +msgstr "_Pulseaudio" + +#. Box for the Pulse configuration +#: ../src/config/audioconf.c:864 +msgid "Pulseaudio settings" +msgstr "Thiết lập Pulseaudio" + +#: ../src/config/audioconf.c:868 +msgid "_ALSA" +msgstr "_ALSA" + +#. Box for the ALSA configuration +#: ../src/config/audioconf.c:874 +msgid "ALSA settings" +msgstr "Thiết lập ALSA" + +#: ../src/config/audioconf.c:889 +msgid "Recordings" +msgstr "Bản thu" + +#. label +#: ../src/config/audioconf.c:893 +msgid "Destination folder" +msgstr "Thư mục đích" + +#. folder chooser button +#: ../src/config/audioconf.c:897 +msgid "Select a folder" +msgstr "Chọn một thư mục" + +#: ../src/config/audioconf.c:908 +msgid "_Always recording" +msgstr "_Luôn ghi âm" + +#. Box for the voice enhancement configuration +#: ../src/config/audioconf.c:915 +msgid "Voice enhancement settings" +msgstr "Các thiết lập cải thiện âm" + +#: ../src/config/audioconf.c:918 +msgid "_Noise Reduction" +msgstr "_Giảm nhiễu" + +#: ../src/config/audioconf.c:932 +msgid "_Echo Cancellation" +msgstr "Giảm _Echo" + +#: ../src/config/hooks-config.c:151 +msgid "URL Argument" +msgstr "Tham số URL" + +#: ../src/config/hooks-config.c:160 +msgid "Trigger on specific _SIP header" +msgstr "Bật khi có header đặc biệt _SIP" + +#: ../src/config/hooks-config.c:169 +msgid "Trigger on _IAX2 URL" +msgstr "Bật khi có URL _IAX2" + +#: ../src/config/hooks-config.c:175 +msgid "Command to _run" +msgstr "Chạy _lệnh" + +#: ../src/config/hooks-config.c:183 +msgid "Phone number rewriting" +msgstr "Viết lại số điện thoại" + +#: ../src/config/hooks-config.c:187 +msgid "_Prefix dialed numbers with" +msgstr "_Thêm vào trước số điện thoại" + +#: ../src/config/hooks-config.c:198 +msgid "Messaging" +msgstr "Lời nhắn" + +#: ../src/config/hooks-config.c:202 +msgid "Open URL in" +msgstr "Mở URL trong" + +#: ../src/config/preferencesdialog.c:179 +msgid "Desktop Notifications" +msgstr "Thông báo trên desktop" + +#. Notification All +#: ../src/config/preferencesdialog.c:183 +msgid "_Enable notifications" +msgstr "_Bật thông báo" + +#. Window Behaviour frame +#: ../src/config/preferencesdialog.c:189 +msgid "Window Behaviour" +msgstr "Hành vi cửa sổ" + +#: ../src/config/preferencesdialog.c:196 +msgid "Bring SFLphone to foreground on incoming calls" +msgstr "Đưa SFLphone lên trên nếu có cuộc gọi đến" + +#. System Tray option frame +#: ../src/config/preferencesdialog.c:203 +msgid "System Tray Icon (Legacy)" +msgstr "System Tray Icon (Legacy)" + +#: ../src/config/preferencesdialog.c:210 +msgid "Show SFLphone in the system tray" +msgstr "Hiện SFLphone trong system tray" + +#: ../src/config/preferencesdialog.c:216 +msgid "_Popup main window on incoming call" +msgstr "_Bật cửa sổ chính khi có cuộc gọi đến" + +#: ../src/config/preferencesdialog.c:221 +msgid "Ne_ver popup main window" +msgstr "_Không bao giờ bật cửa sổ chính" + +#. HISTORY CONFIGURATION +#: ../src/config/preferencesdialog.c:237 +msgid "Calls History" +msgstr "Nhật ký cuộc gọi" + +#: ../src/config/preferencesdialog.c:241 +msgid "_Keep my history for at least" +msgstr "_Hãy giữ lại lược sử ít nhất" + +#: ../src/config/preferencesdialog.c:254 +msgid "days" +msgstr "ngày" + +#. INSTANT MESSAGING CONFIGURATION +#: ../src/config/preferencesdialog.c:258 +msgid "Instant Messaging" +msgstr "Tin nhắn tức thời" + +#: ../src/config/preferencesdialog.c:262 +msgid "Enable instant messaging" +msgstr "Bật tin nhắn tức thời" + +#: ../src/config/preferencesdialog.c:373 +msgid "Preferences" +msgstr "Tùy chọn" + +#: ../src/config/preferencesdialog.c:430 +msgid "Hooks" +msgstr "Hooks" + +#: ../src/config/preferencesdialog.c:435 +msgid "Shortcuts" +msgstr "Đường tắt" + +#: ../src/config/preferencesdialog.c:441 +msgid "Address Book" +msgstr "Sổ danh bạ" + +#: ../src/config/shortcuts-config.c:119 +msgid "Be careful: these shortcuts might override system-wide shortcuts." +msgstr "" +"Hãy cẩn thận: các đường tắt này sẽ thay đổi các đường tắt của hệ thống" + +#: ../src/config/tlsadvanceddialog.c:55 +msgid "Advanced options for TLS" +msgstr "Các lựa chọn cao cấp cho TLS" + +#: ../src/config/tlsadvanceddialog.c:72 +msgid "TLS transport" +msgstr "Giao thức TLS" + +#: ../src/config/tlsadvanceddialog.c:76 +#, c-format +msgid "" +"TLS transport can be used along with UDP for those calls that would\n" +"require secure sip transactions (aka SIPS). You can configure a different\n" +"TLS transport for each account. However, each of them will run on a " +"dedicated\n" +"port, different one from each other\n" +msgstr "" +"Giao thức TLS có để được sử dụng bên cạnh giao thức UDP cho các cuộc gọi\n" +"cần mã hóa các trao đổi SIP (SIPS). Bạn có thể thiết lập giao thức TLS \n" +"cho các tài khoản khác nhau. Tuy nhiên, mỗi tài khoản sẽ được chạy trên \n" +"các cổng mạng khác nhau.\n" + +#: ../src/config/tlsadvanceddialog.c:120 +msgid "Global TLS listener (all accounts)" +msgstr "Nghe TLS tổng quát (cho tất cả các tài khoản)" + +#: ../src/config/tlsadvanceddialog.c:130 +msgid "Certificate of Authority list" +msgstr "Danh sách Certificate of Authority" + +#: ../src/config/tlsadvanceddialog.c:133 +msgid "Choose a CA list file (optional)" +msgstr "Chọn một tệp danh sách CA (tùy ý)" + +#: ../src/config/tlsadvanceddialog.c:144 +msgid "Public endpoint certificate file" +msgstr "Tệp Public endpoint certificate" + +#: ../src/config/tlsadvanceddialog.c:147 +msgid "Choose a public endpoint certificate (optional)" +msgstr "Chọn một public endpoint certificate (tùy ý)" + +#: ../src/config/tlsadvanceddialog.c:165 +msgid "Choose a private key file (optional)" +msgstr "Chọn một tệp khóa cá nhân" + +#: ../src/config/tlsadvanceddialog.c:180 +msgid "Password for the private key" +msgstr "Mật mã cho khóa cá nhân" + +#: ../src/config/tlsadvanceddialog.c:195 +msgid "TLS protocol method" +msgstr "Phương pháp của giao thức TLS" + +#: ../src/config/tlsadvanceddialog.c:223 +msgid "TLS cipher list" +msgstr "Danh sách mật mã TLS" + +#: ../src/config/tlsadvanceddialog.c:232 +msgid "Server name instance for outgoing TLS connection" +msgstr "Tên của máy chủ cá biệt cho kết nối đi ra TLS" + +#: ../src/config/tlsadvanceddialog.c:240 +msgid "Negotiation timeout (sec:msec)" +msgstr "Thời hạn cho sự dàn xếp (giây:micro giây)" + +#: ../src/config/tlsadvanceddialog.c:257 +msgid "Verify incoming certificates, as a server" +msgstr "Kiểm tra các certificate mới đển, với phương diện máy chủ" + +#: ../src/config/tlsadvanceddialog.c:263 +msgid "Verify certificates from answer, as a client" +msgstr "Kiểm tra các certificate trả lời, với phương diện máy khách" + +#: ../src/config/tlsadvanceddialog.c:269 +msgid "Require certificate for incoming tls connections" +msgstr "Cần có certificate cho các kết nối đến TLS" + +#: ../src/config/videoconf.c:499 +msgid "Bitrate (kbps)" +msgstr "Bitrate (kbps)" + +#: ../src/config/videoconf.c:506 +msgid "Parameters" +msgstr "các tham số" + +#: ../src/config/videoconf.c:808 +msgid "No devices found" +msgstr "Không tìm thấy thiết bị nào" + +#. Set choices of input devices +#: ../src/config/videoconf.c:819 +msgid "Device" +msgstr "Thiết Bị" + +#. Set choices of input +#: ../src/config/videoconf.c:834 +msgid "Channel" +msgstr "Kênh" + +#. Set choices of sizes +#: ../src/config/videoconf.c:848 +msgid "Size" +msgstr "Cỡ" + +#. Set choices of rates +#: ../src/config/videoconf.c:862 +msgid "Rate" +msgstr "Mức độ" + +#: ../src/config/videoconf.c:888 +msgid "Video Manager" +msgstr "Trình quản lý Video" + +#: ../src/config/videoconf.c:891 +msgid "Video4Linux2" +msgstr "Video4Linux2" + +#: ../src/config/videoconf.c:897 +msgid "Preview" +msgstr "Xem thử" + +#: ../src/config/zrtpadvanceddialog.c:52 +msgid "ZRTP Options" +msgstr "Lựa chọn ZRTP" + +#: ../src/config/zrtpadvanceddialog.c:69 +msgid "Send Hello Hash in S_DP" +msgstr "Gửi Hello Hash trong S_DP" + +#: ../src/config/zrtpadvanceddialog.c:74 +msgid "Ask User to Confirm SAS" +msgstr "Hỏi người dùng xác nhận SAS" + +#: ../src/config/zrtpadvanceddialog.c:79 +msgid "_Warn if ZRTP not supported" +msgstr "_Cảnh báo nếu không hỗ trợ ZRTP" + +#: ../src/config/zrtpadvanceddialog.c:84 +msgid "Display SAS once for hold events" +msgstr "Hiển thị SAS khi có sự kiện đang chờ" + +#: ../src/config/zrtpadvanceddialog.c:118 +msgid "SDES Options" +msgstr "Lựa chọn SDES" + +#: ../src/config/zrtpadvanceddialog.c:134 +msgid "Fallback on RTP on SDES failure" +msgstr "Quay về RTP nếu không sử dụng được SDES" + +#: ../src/contacts/searchbar.c:226 +msgid "Search all" +msgstr "Tìm tất cả" + +#: ../src/contacts/searchbar.c:227 ../src/contacts/searchbar.c:235 +#: ../src/contacts/searchbar.c:243 ../src/contacts/searchbar.c:251 +msgid "Click here to change the search type" +msgstr "Nhấn vào đây để sửa đổi kiểu tìm kiếm" + +#: ../src/contacts/searchbar.c:234 +msgid "Search by missed call" +msgstr "Tìm các cuộc gọi nhỡ" + +#: ../src/contacts/searchbar.c:242 +msgid "Search by incoming call" +msgstr "Tìm các cuộc gọi đến" + +#: ../src/contacts/searchbar.c:250 +msgid "Search by outgoing call" +msgstr "Tìm các cuộc gọi đi" + +#: ../src/dbus/dbus.c:575 +msgid "ALSA notification: Error while opening playback device" +msgstr "Thông báo ALSA: Lỗi khi mở thiết bị đọc" + +#: ../src/dbus/dbus.c:578 +msgid "ALSA notification: Error while opening capture device" +msgstr "Thông báo ALSA: Lỗi khi mở thiết bị ghi" + +#: ../src/dbus/dbus.c:581 +msgid "Pulseaudio notification: Pulseaudio is not running" +msgstr "Thông báo Pulseaudio: Hiện thời Pulseaudio đang bị tắt" + +#: ../src/dbus/dbus.c:584 +msgid "Codecs notification: Codecs not found" +msgstr "Thông báo Codecs: không tìm thấy Codecs" + +#: ../src/mainwindow.c:138 +msgid "There is one call in progress." +msgstr "Đang thực hiện một cuộc gọi" + +#: ../src/mainwindow.c:140 +msgid "There are calls in progress." +msgstr "Đang thực hiện các cuộc gọi" + +#: ../src/mainwindow.c:145 +msgid "Do you still want to quit?" +msgstr "Bạn vẫn muốn thoát ra?" + +#: ../src/mainwindow.c:494 +#, c-format +msgid "ZRTP is not supported by peer %s\n" +msgstr "Kết nối không hỗ trợ ZRTP %s\n" + +#: ../src/mainwindow.c:496 +msgid "Secure Communication Unavailable" +msgstr "Không tồn tại liên lạc an toàn" + +#: ../src/mainwindow.c:499 ../src/mainwindow.c:525 +msgid "Continue" +msgstr "Tiếp" + +#: ../src/mainwindow.c:500 ../src/mainwindow.c:526 ../src/mainwindow.c:543 +msgid "Stop Call" +msgstr "Ngưng cuộc gọi" + +#: ../src/mainwindow.c:518 +#, c-format +msgid "" +"A %s error forced the call with %s to fall under unencrypted mode.\n" +"Exact reason: %s\n" +msgstr "" +"Lỗi %s bắt buộc cuộc gọi với %s chuyển về phương thức không mã hóa.\n" +"Lý do chính xác: %s\n" + +#: ../src/mainwindow.c:522 +msgid "ZRTP negotiation failed" +msgstr "Dàn xếp ZRTP thất bại" + +#: ../src/mainwindow.c:536 +#, c-format +msgid "" +"%s wants to stop using secure communication. Confirm will resume " +"conversation without SRTP.\n" +msgstr "" +"%s muốn ngưng sử dụng liên lạc an toàn. Hãy xác nhận việc tiếp tục đàm thoại " +"với SRTP\n" + +#: ../src/mainwindow.c:538 +msgid "Confirm Go Clear" +msgstr "Xác nhận Go Clear" + +#: ../src/mainwindow.c:541 +msgid "Confirm" +msgstr "Xác nhận" + +#: ../src/sflphone_client.c:53 +#, c-format +msgid "" +"%s\n" +"Run '%s --help' to see a full list of available command line options.\n" +msgstr "" +"%s\n" +"Chạy '%s --help' để xem danh sách tuỳ chọn dòng lệnh.\n" + +#: ../src/sflphone_client.c:70 +msgid "SFLphone Error" +msgstr "Lỗi SFLphone" + +#: ../src/sflphone_options.c:60 +msgid "Enable debug" +msgstr "Bật gỡ lỗi" + +#: ../src/sflphone_options.c:67 +msgid "- GNOME client for SFLPhone" +msgstr "- Chương trình khách GNOME cho SFLphone" + +#: ../src/sflnotify.c:93 +#, c-format +msgid "%s says:" +msgstr "%s báo:" + +#. the account is different from NULL +#: ../src/sflnotify.c:115 ../src/sflnotify.c:135 +#, c-format +msgid "%s account : %s" +msgstr "Tài khoản %s: %s" + +#: ../src/sflnotify.c:120 +#, c-format +msgid "<i>From</i> %s" +msgstr "<i>Từ</i> %s" + +#. the account is different from NULL +#: ../src/sflnotify.c:153 +#, c-format +msgid "Calling with %s account <i>%s</i>" +msgstr "Gọi với tài khoản %s <i>%s</i>" + +#: ../src/sflnotify.c:157 +#, c-format +msgid "Current account" +msgstr "Tài khoản hiện tại" + +#: ../src/sflnotify.c:169 +#, c-format +msgid "You have no accounts set up" +msgstr "Bạn không có tài khoàn nào được thiết lập" + +#: ../src/sflnotify.c:182 +#, c-format +msgid "You have no registered accounts" +msgstr "Bạn không có tài khoản nào được đăng ký" + +#: ../src/sflnotify.c:196 +#, c-format +msgid "" +"<i>With:</i> %s \n" +"using %s" +msgstr "" +"<i>Với:</i> %s \n" +"dùng %s" + +#: ../src/sflnotify.c:210 +#, c-format +msgid "%s does not support ZRTP." +msgstr "%s không hỗ trợ ZRTP" + +#: ../src/sflnotify.c:224 +#, c-format +msgid "ZRTP negotiation failed with %s" +msgstr "Dàn xếp ZRTP thất bại với %s" + +#: ../src/sflnotify.c:238 +#, c-format +msgid "<i>With:</i> %s" +msgstr "<i>Với:</i> %s" + +#: ../src/sliders.c:231 +msgid "Speakers volume" +msgstr "Âm lượng" + +#: ../src/sliders.c:231 +msgid "Mic volume" +msgstr "Âm lượng cho micro" + +#: ../src/statusicon.c:132 +msgid "_Show main window" +msgstr "_Hiển thị cửa sổ chính" + +#: ../src/statusicon.c:139 ../src/uimanager.c:994 ../src/uimanager.c:1366 +#: ../src/uimanager.c:1435 +msgid "_Hang up" +msgstr "_Ngừng" + +#: ../src/statusicon.c:190 +msgid "SFLphone" +msgstr "SFLphone" + +#: ../src/uimanager.c:436 +msgid "No address book selected" +msgstr "Chưa có sổ danh bạ nào được lựa chọn" + +#: ../src/uimanager.c:456 ../src/uimanager.c:1123 +msgid "Address book" +msgstr "Sổ danh bạ" + +#: ../src/uimanager.c:483 +#, c-format +msgid "Voicemail(%i)" +msgstr "Thư thoại(%i)" + +#: ../src/uimanager.c:563 +msgid "SFLphone is a VoIP client compatible with SIP and IAX2 protocols." +msgstr "" +"SFLphone là một chương trình khách VoIP tương thích với các giao thức SIP và " +"IAX2" + +#: ../src/uimanager.c:566 +msgid "About SFLphone" +msgstr "Nói về SFLphone" + +#: ../src/uimanager.c:935 ../src/uimanager.c:1014 +msgid "Voicemail" +msgstr "Thư thoại" + +#. Call Menu +#: ../src/uimanager.c:984 +msgid "Call" +msgstr "Gọi" + +#: ../src/uimanager.c:986 ../src/uimanager.c:1531 +msgid "_New call" +msgstr "Cuộc gọi _mới" + +#: ../src/uimanager.c:987 +msgid "Place a new call" +msgstr "Gọi mới" + +#: ../src/uimanager.c:990 ../src/uimanager.c:1355 +msgid "_Pick up" +msgstr "_Nhấc máy" + +#: ../src/uimanager.c:991 +msgid "Answer the call" +msgstr "Trả lời cuộc gọi" + +#: ../src/uimanager.c:995 +msgid "Finish the call" +msgstr "Kết thúc cuộc gọi" + +#: ../src/uimanager.c:998 +msgid "O_n hold" +msgstr "_Chờ" + +#: ../src/uimanager.c:999 +msgid "Place the call on hold" +msgstr "Chuyển cuộc gọi sang chờ" + +#: ../src/uimanager.c:1002 +msgid "O_ff hold" +msgstr "_Hết chờ" + +#: ../src/uimanager.c:1003 +msgid "Place the call off hold" +msgstr "Chuyển cuộc gọi sang không chờ" + +#: ../src/uimanager.c:1006 ../src/uimanager.c:1420 +msgid "Send _message" +msgstr "Gửi _thông điệp" + +#: ../src/uimanager.c:1007 +msgid "Send message" +msgstr "Gửi thông điệp" + +#: ../src/uimanager.c:1010 +msgid "Configuration _Assistant" +msgstr "Trợ lý cấu hình" + +#: ../src/uimanager.c:1011 +msgid "Run the configuration assistant" +msgstr "Chạy Trợ lý Cấu hình" + +#: ../src/uimanager.c:1015 +msgid "Call your voicemail" +msgstr "Gọi hộp thư thoại của bạn" + +#: ../src/uimanager.c:1018 +msgid "_Close" +msgstr "Đón_g" + +#: ../src/uimanager.c:1019 +msgid "Minimize to system tray" +msgstr "Thu nhỏ về khay hệ thống" + +#: ../src/uimanager.c:1022 +msgid "_Quit" +msgstr "_Thoát" + +#: ../src/uimanager.c:1023 +msgid "Quit the program" +msgstr "Thoát khỏi chương trình" + +#. Edit Menu +#: ../src/uimanager.c:1027 +msgid "_Edit" +msgstr "_Sửa" + +#: ../src/uimanager.c:1029 +msgid "_Copy" +msgstr "_Sao chép" + +#: ../src/uimanager.c:1030 +msgid "Copy the selection" +msgstr "Sao chép vùng chọn" + +#: ../src/uimanager.c:1033 +msgid "_Paste" +msgstr "Riêng" + +#: ../src/uimanager.c:1034 +msgid "Paste the clipboard" +msgstr "Dán bảng lưu tạm" + +#: ../src/uimanager.c:1037 +msgid "Clear _history" +msgstr "_Xóa lược sử" + +#: ../src/uimanager.c:1038 +msgid "Clear the call history" +msgstr "Xóa lược sử các cuộc gọi" + +#: ../src/uimanager.c:1041 +msgid "_Accounts" +msgstr "Tài _khoản" + +#: ../src/uimanager.c:1042 +msgid "Edit your accounts" +msgstr "Sửa đổi tài khoản của bạn" + +#: ../src/uimanager.c:1045 +msgid "_Preferences" +msgstr "Tù_y thích" + +#: ../src/uimanager.c:1046 +msgid "Change your preferences" +msgstr "Thay đổi sở thích của bạn" + +#. View Menu +#: ../src/uimanager.c:1050 +msgid "_View" +msgstr "_Xem" + +#. Help menu +#: ../src/uimanager.c:1053 +msgid "_Help" +msgstr "_Trợ giúp" + +#: ../src/uimanager.c:1054 +msgid "Contents" +msgstr "Nội dung" + +#: ../src/uimanager.c:1055 +msgid "Open the manual" +msgstr "Mở sổ tay hướng dẫn" + +#: ../src/uimanager.c:1057 +msgid "About this application" +msgstr "Giới thiệu về ứng dụng này" + +#: ../src/uimanager.c:1116 +msgid "_Transfer" +msgstr "_Chuyển" + +#: ../src/uimanager.c:1116 +msgid "Transfer the call" +msgstr "Chuyển cuộc gọi" + +#: ../src/uimanager.c:1117 ../src/uimanager.c:1388 +msgid "_Record" +msgstr "Th_u" + +#: ../src/uimanager.c:1117 +msgid "Record the current conversation" +msgstr "Thu cuộc gọi hiện tại" + +#: ../src/uimanager.c:1118 ../src/uimanager.c:1400 +msgid "_Mute" +msgstr "_Tắt" + +#: ../src/uimanager.c:1118 +msgid "Mute microphone for this call" +msgstr "Tắt micro cho cuộc gọi này" + +#: ../src/uimanager.c:1119 +msgid "_Show toolbar" +msgstr "_Hiện thanh công cụ" + +#: ../src/uimanager.c:1119 +msgid "Show the toolbar" +msgstr "Hiện thanh công cụ" + +#: ../src/uimanager.c:1120 +msgid "_Dialpad" +msgstr "Vùng _quay số" + +#: ../src/uimanager.c:1120 +msgid "Show the dialpad" +msgstr "Hiện thị vùng quay số" + +#: ../src/uimanager.c:1121 +msgid "_Volume controls" +msgstr "_Kiểm soát âm lượng" + +#: ../src/uimanager.c:1121 +msgid "Show the volume controls" +msgstr "Hiển thị kiểm soát âm lượng" + +#: ../src/uimanager.c:1122 +msgid "_History" +msgstr "_Lược sử" + +#: ../src/uimanager.c:1122 +msgid "Calls history" +msgstr "Lược sử cuộc gọi" + +#: ../src/uimanager.c:1123 +msgid "_Address book" +msgstr "_Danh bạ" + +#: ../src/uimanager.c:1377 ../src/uimanager.c:1444 +msgid "On _Hold" +msgstr "Đang _chờ" + +#: ../src/uimanager.c:1481 +msgid "_Call back" +msgstr "_Gọi lại" + +#: ../src/uimanager.c:1589 +msgid "Edit phone number" +msgstr "Nhập số điện thoại" + +#: ../src/uimanager.c:1600 +msgid "Edit the phone number before making a call" +msgstr "Nhập số điện thoại trước khi gọi" diff --git a/gnome/src/account_schema.h b/gnome/src/account_schema.h index 008c5776129a2760b96f870280e94cfaa926995f..5e72f5f39b6ca1d3eef84f770c8448d448f609f2 100644 --- a/gnome/src/account_schema.h +++ b/gnome/src/account_schema.h @@ -45,6 +45,7 @@ static const char *const CONFIG_ACCOUNT_TYPE = "Account.type" static const char *const CONFIG_ACCOUNT_ALIAS = "Account.alias"; static const char *const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox"; static const char *const CONFIG_ACCOUNT_ENABLE = "Account.enable"; +static const char *const CONFIG_ACCOUNT_AUTOANSWER = "Account.autoAnswer"; static const char *const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire"; static const char *const CONFIG_ACCOUNT_REGISTRATION_STATUS = "Account.registrationStatus"; static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_CODE = "Account.registrationCode"; @@ -57,6 +58,7 @@ static const char *const CONFIG_KEEP_ALIVE_ENABLED = "Account.keepA static const char *const CONFIG_DEFAULT_REGISTRATION_EXPIRE = "60"; +static const char *const CONFIG_DEFAULT_RINGTONE_ENABLED = "true"; static const char *const CONFIG_ACCOUNT_HOSTNAME = "Account.hostname"; static const char *const CONFIG_ACCOUNT_USERNAME = "Account.username"; @@ -65,7 +67,10 @@ static const char *const CONFIG_ACCOUNT_PASSWORD = "Account.passw static const char *const CONFIG_ACCOUNT_REALM = "Account.realm"; static const char *const CONFIG_ACCOUNT_DEFAULT_REALM = "*"; static const char *const CONFIG_ACCOUNT_USERAGENT = "Account.useragent"; -static const char *const CONFIG_ACCOUNT_AUTOANSWER = "Account.autoAnswer"; +static const char *const CONFIG_ACCOUNT_AUDIO_PORT_MIN = "Account.audioPortMin"; +static const char *const CONFIG_ACCOUNT_AUDIO_PORT_MAX = "Account.audioPortMax"; +static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MIN = "Account.videoPortMin"; +static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MAX = "Account.videoPortMax"; static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface"; static const char *const CONFIG_INTERFACE = "Account.interface"; diff --git a/gnome/src/config/accountconfigdialog.c b/gnome/src/config/accountconfigdialog.c index c38e7a34bfe642150b48b0d6697f0a4f7668b838..6d6b973a6091a1029f42a4578e2153e39aa75c57 100644 --- a/gnome/src/config/accountconfigdialog.c +++ b/gnome/src/config/accountconfigdialog.c @@ -97,6 +97,12 @@ static GtkWidget *security_tab; static GtkWidget *advanced_tab; static GtkWidget *overrtp; static GtkWidget *ringtone_seekslider; +static GtkWidget *audio_port_min_spin_box; +static GtkWidget *audio_port_max_spin_box; +#ifdef SFL_VIDEO +static GtkWidget *video_port_min_spin_box; +static GtkWidget *video_port_max_spin_box; +#endif typedef struct OptionsData { account_t *account; @@ -1029,6 +1035,40 @@ GtkWidget* create_published_address(const account_t *account) return frame; } +static void +add_port_spin_button(const account_t *account, GtkWidget *grid, GtkWidget **spin, + const gchar *key, const gchar *label_text, int left, int top) +{ + gchar *value = NULL; + + if (account) + value = account_lookup(account, key); + + *spin = gtk_spin_button_new_with_range(1024, 65535, 1); + GtkWidget *label = gtk_label_new_with_mnemonic(label_text); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), *spin); + gtk_grid_attach(GTK_GRID(grid), label, left, top, 1, 1); + if (value) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(*spin), g_ascii_strtod(value, NULL)); + gtk_grid_attach(GTK_GRID(grid), *spin, left + 1, top, 1, 1); +} + +static GtkWidget* +create_port_ranges(const account_t *account, const gchar * title, + GtkWidget **min, GtkWidget **max, + const gchar *key_min, const gchar *key_max, int top) +{ + GtkWidget *grid, *frame; + gnome_main_section_new_with_grid(title, &frame, &grid); + gtk_container_set_border_width(GTK_CONTAINER(grid), 10); + gtk_grid_set_row_spacing(GTK_GRID(grid), 5); + + add_port_spin_button(account, grid, min, key_min, _("Min"), 0, top); + add_port_spin_button(account, grid, max, key_max, _("Max"), 2, top); + + return frame; +} + GtkWidget* create_advanced_tab(const account_t *account) { // Build the advanced tab, to appear on the account configuration panel @@ -1047,6 +1087,18 @@ GtkWidget* create_advanced_tab(const account_t *account) frame = create_published_address(account); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + frame = create_port_ranges(account, _("Audio RTP Port Range"), + &audio_port_min_spin_box, &audio_port_max_spin_box, + CONFIG_ACCOUNT_AUDIO_PORT_MIN, CONFIG_ACCOUNT_AUDIO_PORT_MAX, 0); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + +#ifdef SFL_VIDEO + frame = create_port_ranges(account, _("Video RTP Port Range"), + &video_port_min_spin_box, &video_port_max_spin_box, + CONFIG_ACCOUNT_VIDEO_PORT_MIN, CONFIG_ACCOUNT_VIDEO_PORT_MAX, 0); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); +#endif + gtk_widget_show_all(vbox); use_stun_cb(use_stun_check_box, NULL); @@ -1257,6 +1309,17 @@ static void update_account_from_basic_tab(account_t *account) account_replace(account, CONFIG_PUBLISHED_ADDRESS, published_address); } + + account_replace(account, CONFIG_ACCOUNT_AUDIO_PORT_MIN, + gtk_entry_get_text(GTK_ENTRY(audio_port_min_spin_box))); + account_replace(account, CONFIG_ACCOUNT_AUDIO_PORT_MAX, + gtk_entry_get_text(GTK_ENTRY(audio_port_max_spin_box))); +#ifdef SFL_VIDEO + account_replace(account, CONFIG_ACCOUNT_VIDEO_PORT_MIN, + gtk_entry_get_text(GTK_ENTRY(video_port_min_spin_box))); + account_replace(account, CONFIG_ACCOUNT_VIDEO_PORT_MAX, + gtk_entry_get_text(GTK_ENTRY(video_port_max_spin_box))); +#endif } if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(overrtp))) { diff --git a/gnome/src/config/utils.c b/gnome/src/config/utils.c index ec03d2d02acc4014b54796b0a33d5a9d8b4d2559..3d06c2899950df81ca5e595755eefb498492b4e7 100644 --- a/gnome/src/config/utils.c +++ b/gnome/src/config/utils.c @@ -31,7 +31,7 @@ #include "utils.h" #include "sflphone_const.h" -void gnome_main_section_new_with_grid(gchar *title, GtkWidget **frame, GtkWidget **grid) +void gnome_main_section_new_with_grid(const gchar *title, GtkWidget **frame, GtkWidget **grid) { PangoAttrList *attrs = pango_attr_list_new(); PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD); diff --git a/gnome/src/config/utils.h b/gnome/src/config/utils.h index 728d62532247deec20929c45d6d5147e2bbb7ae0..2da55ebd064cdf97222091e248a57d9ca2353c6f 100644 --- a/gnome/src/config/utils.h +++ b/gnome/src/config/utils.h @@ -34,7 +34,7 @@ #include <gtk/gtk.h> GtkWidget *gnome_main_section_new(const gchar * const title); -void gnome_main_section_new_with_grid(gchar *title, GtkWidget**, GtkWidget**); +void gnome_main_section_new_with_grid(const gchar *title, GtkWidget**, GtkWidget**); GtkWidget *gnome_info_bar (gchar *message, GtkMessageType type); #endif // _UTILS_