diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c index 9c7fd778..ee702f5d 100644 --- a/app/src/main/cpp/baresip.c +++ b/app/src/main/cpp/baresip.c @@ -994,6 +994,64 @@ Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1port(JNIEnv *env, jobject return res; } +JNIEXPORT jstring JNICALL +Java_com_tutpro_baresip_AccountKt_account_1stun_1user(JNIEnv *env, jobject thiz, jstring jAcc) +{ + const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0); + struct account *acc = (struct account *) strtoul(native_acc, NULL, 10); + (*env)->ReleaseStringUTFChars(env, jAcc, native_acc); + if (acc) { + const char *stun_user = account_stun_user(acc); + if (stun_user) return (*env)->NewStringUTF(env, stun_user); + } + return (*env)->NewStringUTF(env, ""); +} + +JNIEXPORT jint JNICALL +Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1user(JNIEnv *env, jobject thiz, + jstring jAcc, jstring jUser) { + const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0); + struct account *acc = (struct account *)strtoul(native_acc, NULL, 10); + (*env)->ReleaseStringUTFChars(env, jAcc, native_acc); + const char *user = (*env)->GetStringUTFChars(env, jUser, 0); + int res; + if (strlen(user) > 0) + res = account_set_stun_user(acc, user); + else + res = account_set_stun_user(acc, NULL); + (*env)->ReleaseStringUTFChars(env, jUser, user); + return res; +} + +JNIEXPORT jstring JNICALL +Java_com_tutpro_baresip_AccountKt_account_1stun_1pass(JNIEnv *env, jobject thiz, jstring jAcc) +{ + const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0); + struct account *acc = (struct account *) strtoul(native_acc, NULL, 10); + (*env)->ReleaseStringUTFChars(env, jAcc, native_acc); + if (acc) { + const char *stun_pass = account_stun_pass(acc); + if (stun_pass) return (*env)->NewStringUTF(env, stun_pass); + } + return (*env)->NewStringUTF(env, ""); +} + +JNIEXPORT jint JNICALL +Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1pass(JNIEnv *env, jobject thiz, + jstring jAcc, jstring jPass) { + const char *native_acc = (*env)->GetStringUTFChars(env, jAcc, 0); + struct account *acc = (struct account *)strtoul(native_acc, NULL, 10); + (*env)->ReleaseStringUTFChars(env, jAcc, native_acc); + const char *pass = (*env)->GetStringUTFChars(env, jPass, 0); + int res; + if (strlen(pass) > 0) + res = account_set_stun_pass(acc, pass); + else + res = account_set_stun_pass(acc, NULL); + (*env)->ReleaseStringUTFChars(env, jPass, pass); + return res; +} + JNIEXPORT jint JNICALL Java_com_tutpro_baresip_AccountKt_account_1set_1mwi(JNIEnv *env, jobject thiz, jstring javaAcc, jstring javaValue) { diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt index a68c4593..4f838d4b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Account.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -14,6 +14,8 @@ class Account(val accp: String) { var outbound = ArrayList() var mediaNat = account_medianat(accp) var stunServer = "" + var stunUser = account_stun_user(accp) + var stunPass = account_stun_pass(accp) var audioCodec = ArrayList() var videoCodec = ArrayList() var regint = account_regint(accp) @@ -92,7 +94,11 @@ class Account(val accp: String) { if (mediaNat != "") res = res + ";medianat=${mediaNat}" - if (stunServer != "") res = res + ";stunserver=\"stun:${stunServer}\"" + if (stunServer != "") res = res + ";stunserver=\"${stunServer}\"" + + if (stunUser != "") res = res + ";stunuser=\"${stunUser}\"" + + if (stunPass != "") res = res + ";stunpass=\"${stunPass}\"" if (audioCodec.size > 0) { var first = true @@ -243,6 +249,10 @@ external fun account_stun_host(acc: String): String external fun account_stun_port(acc: String): Int external fun account_set_stun_host(acc: String, host: String): Int external fun account_set_stun_port(acc: String, port: Int): Int +external fun account_stun_user(acc: String): String +external fun account_set_stun_user(acc: String, user: String): Int +external fun account_stun_pass(acc: String): String +external fun account_set_stun_pass(acc: String, pass: String): Int external fun account_mediaenc(acc: String): String external fun account_set_mediaenc(acc: String, mediaenc: String): Int external fun account_medianat(acc: String): String diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt index d2d46295..00a495fa 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt @@ -24,6 +24,8 @@ class AccountActivity : AppCompatActivity() { internal lateinit var outbound2: EditText internal lateinit var mediaNat: String internal lateinit var stunServer: EditText + internal lateinit var stunUser: EditText + internal lateinit var stunPass: EditText internal lateinit var regCheck: CheckBox internal lateinit var mediaEnc: String internal lateinit var ipV6MediaCheck: CheckBox @@ -78,8 +80,8 @@ class AccountActivity : AppCompatActivity() { mediaNat = acc.mediaNat val mediaNatSpinner = findViewById(R.id.mediaNatSpinner) as Spinner - val mediaNatKeys = arrayListOf("stun", "ice", "") - val mediaNatVals = arrayListOf("STUN", "ICE", "-") + val mediaNatKeys = arrayListOf("stun", "turn", "ice", "") + val mediaNatVals = arrayListOf("STUN", "TURN", "ICE", "-") var keyIx = mediaNatKeys.indexOf(acc.mediaNat) var keyVal = mediaNatVals.elementAt(keyIx) mediaNatKeys.removeAt(keyIx) @@ -94,6 +96,8 @@ class AccountActivity : AppCompatActivity() { override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { mediaNat = mediaNatKeys[mediaNatVals.indexOf(parent.selectedItem.toString())] stunServer.isEnabled = mediaNat != "" + stunUser.isEnabled = mediaNat != "" + stunPass.isEnabled = mediaNat != "" } override fun onNothingSelected(parent: AdapterView<*>) { } @@ -103,6 +107,14 @@ class AccountActivity : AppCompatActivity() { stunServer.setText(acc.stunServer) stunServer.isEnabled = mediaNat != "" + stunUser = findViewById(R.id.StunUser) as EditText + stunUser.setText(acc.stunUser) + stunUser.isEnabled = mediaNat != "" + + stunPass = findViewById(R.id.StunPass) as EditText + stunPass.setText(acc.stunPass) + stunPass.isEnabled = mediaNat != "" + mediaEnc = acc.mediaEnc val mediaEncSpinner = findViewById(R.id.mediaEncSpinner) as Spinner val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "") @@ -312,31 +324,64 @@ class AccountActivity : AppCompatActivity() { } if (mediaNat != "") { - val newStunServer = stunServer.text.toString().trim() + var newStunServer = stunServer.text.toString().trim() + if (newStunServer == "") + newStunServer = resources.getString(R.string.stun_server_default) if (acc.stunServer != newStunServer) { - if (!Utils.checkHostPort(newStunServer)) { + if (!Utils.checkStunUri(newStunServer)) { Utils.alertView(this, getString(R.string.notice), String.format(getString(R.string.invalid_stun_server), newStunServer)) return false } + val p = newStunServer.split(":") var host = "" - var port = 0 - if (newStunServer != "") { - val hostPort = newStunServer.split(":") - host = hostPort[0] - if (hostPort.size == 2) port = hostPort[1].toInt() + val port: Int + if (p.size == 2) { + host = newStunServer + port = 0 + } else { + host = newStunServer.substringBeforeLast(":") + port = newStunServer.substringAfterLast(":").toInt() } if ((account_set_stun_host(acc.accp, host) == 0) && (account_set_stun_port(acc.accp, port) == 0)) { acc.stunServer = account_stun_host(acc.accp) if (port != 0) acc.stunServer += ":" + account_stun_port(acc.accp).toString() - Log.d("Baresip", "New StunServer is '${acc.stunServer}'") + Log.d("Baresip", "New STUN/TURN server URI is '${acc.stunServer}'") save = true } else { - Log.e("Baresip", "Setting of StunServer failed") + Log.e("Baresip", "Setting of STUN/TURN URI server failed") } } + val su = stunUser.text.toString().trim() + if (Account.checkAuthUser(su)) { + if (account_set_stun_user(acc.accp, su) == 0) { + acc.stunUser = account_stun_user(acc.accp); + Log.d("Baresip", "New STUN/TURN user is ${acc.stunUser}") + save = true + } else { + Log.e("Baresip", "Setting of STUN/TURN user failed") + } + } else { + Utils.alertView(this, getString(R.string.notice), + String.format(getString(R.string.invalid_stun_username), su)) + return false + } + val sp = stunPass.text.toString().trim() + if (sp.isEmpty() || Account.checkAuthPass(sp)) { + if (account_set_stun_pass(acc.accp, sp) == 0) { + acc.stunPass = account_stun_pass(acc.accp); + Log.d("Baresip", "New stun pass is ${acc.stunPass}") + save = true + } else { + Log.e("Baresip", "Setting of stun pass failed") + } + } else { + Utils.alertView(this, getString(R.string.notice), + String.format(getString(R.string.invalid_stun_password), sp)) + return false + } } if (mediaEnc != acc.mediaEnc) { @@ -469,6 +514,14 @@ class AccountActivity : AppCompatActivity() { Utils.alertView(this, getString(R.string.stun_server), getString(R.string.stun_server_help)) } + findViewById(R.id.StunUserTitle) as TextView -> { + Utils.alertView(this, getString(R.string.stun_username), + getString(R.string.stun_username_help)) + } + findViewById(R.id.StunPassTitle) as TextView -> { + Utils.alertView(this, getString(R.string.stun_password), + getString(R.string.stun_password_help)) + } findViewById(R.id.MediaEncTitle) as TextView -> { Utils.alertView(this, getString(R.string.media_encryption), getString(R.string.media_encryption_help)) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index 0c8b6c8a..2ef2fefc 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -125,6 +125,13 @@ object Utils { return checkUriUser(aorUser(aor)) && checkDomain(aorDomain(aor)) } + fun checkStunUri(uri: String): Boolean { + if (!uri.startsWith("stun:") && !uri.startsWith("stuns:") && + !uri.startsWith("turn:") && !uri.startsWith("turns:")) + return false + return checkHostPort(uri.substringAfter(":")) + } + private fun checkPortTransport(portTransport: String): Boolean { val pt = portTransport.split(";transport=") if (pt.count() == 1) diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 737440e3..52c49724 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -154,6 +154,24 @@ android:text="@string/audio_codecs" > + + + + + + + android:text="@string/stun_username" > - - + android:layout_width="fill_parent" + android:hint="@string/user_id" + android:textSize="18sp" + android:inputType="textEmailAddress" + android:autofillHints="username" > + + + + + + + - STUN-palvelin - STUN-palvelimen muotoa palvelin[:portti] - oleva osoite. Oletusarvo on \'stun.l.google.com:19302\', joka - osoittaa Google:n julkiseen STUN-palvelimeen. Käyttäjätunnusta ja - salasanaa ei voi tällä hetkellä antaa. + STUN/TURN-palvelin + STUN/TURN-palvelimen muotoa \'kaava:palvelin[:portti]\' + oleva osoite, missä kaava on \'stun\', \'stuns\', \'turn\' tai \'turns\'. Oletusarvo on + \'stun:stun.l.google.com:19302\', joka osoittaa Google:n julkiseen STUN-palvelimeen. - Virheellinen STUN-palvelin \'%1$s\' + Virheellinen STUN/TURN-palvelimen URI \'%1$s\' + STUN/TURN-käyttäjätunnus + Käyttäjätunnus jos STUN/TURN-palvelin vaatii sellaisen + Virheellinen käyttäjätunnus \'%1$s\' + STUN/TURN-salasana + Salasana jos STUN/TURN-palvelin vaatii sellaisen + Virheellinen salasana \'%1$s\' Median salaus Valitsee median salausprotokollan (vapaaehtoinen). \n • ZRTP (suositeltu) tarkoittaa, että ZRTP-salausta yritetään neuvotella sen jälkeen, kun puhelu on alkanut. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc94fff9..37c011e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,13 +78,19 @@ (Session Traversal Utilities for NAT, RFC 5389) and ICE (Interactive Connectivity Establishment, RFC 5245). - STUN Server - A STUN Server of form host[:port]. - Factory default value is \'stun.l.google.com:19302\', pointing to public Google STUN server. - Username and password are currently not supported. + STUN/TURN Server + A STUN/TURN Server URI of form scheme:host[:port], where scheme + is \'stun\', \'stuns\', \'turn\', or \'turns\'. Factory default value is + \'stun:stun.l.google.com:19302\', pointing to public Google STUN server. - Invalid STUN Server \'%1$s\' - stun.l.google.com:19302 + Invalid STUN/TURN Server URI \'%1$s\' + stun:stun.l.google.com:19302 + STUN/TURN Username + Username if required by STUN/TURN server + Invalid Username \'%1$s\' + STUN/TURN Password + Password if required by STUN/TURN server + Invalid Password \'%1$s\' Media Encryption Selects media transport encryption protocol (if any). \n • ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after