Commit a9637056 authored by Christophe Deschamps's avatar Christophe Deschamps
Browse files

- Reuse local friend list to store device instead of removing/creating

- Aligned VCard format for local and remote VCards
- Implemented check of local vcard to harness recurring crash
- Enforce SIP uri checking to avoid empty device list
- Fixed a crash removing last device
parent 424531ec
Pipeline #44864 failed with stage
in 1 minute and 1 second
......@@ -28,8 +28,6 @@ import kotlinx.android.parcel.Parcelize
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.linhome.LinhomeApplication
import org.linhome.customisation.ActionTypes
import org.linhome.customisation.ActionsMethodTypes
import org.linhome.customisation.DeviceTypes
import org.linhome.store.StorageManager
import org.linhome.utils.DialogUtil
......@@ -61,7 +59,8 @@ data class Device(
friend.createVcard(name)
friend.vcard?.addExtendedProperty(vcard_device_type_header, type!!)
friend.vcard?.addSipAddress(address)
friend.vcard?.addExtendedProperty(vcard_action_method_type_header,actionsMethodType!!)
friend.vcard?.addExtendedProperty(vcard_action_method_type_header,
deviceActionMethodsTovCardActionMethods().get(actionsMethodType!!)!!)
actions?.forEach { it ->
friend.vcard?.addExtendedProperty(vcard_actions_list_header,it.type!! + ";" + it.code!!)
}
......@@ -86,8 +85,8 @@ data class Device(
card.uid?.let{it}?:xDigitsUUID(),
card.getExtendedPropertiesValuesByName(vcard_device_type_header).component1(),
card.fullName!!,
if (isRemotelyProvisionned) card.sipAddresses.component1()?.asStringUriOnly()!! else card.sipAddresses.component1()?.asString()!!,
if (isRemotelyProvisionned) serverActionMethodsToLocalMethods.get(card.getExtendedPropertiesValuesByName(vcard_action_method_type_header).component1()) else card.getExtendedPropertiesValuesByName(vcard_action_method_type_header).component1()!!,
card.sipAddresses.component1()?.asStringUriOnly()!!,
vCardActionMethodsToDeviceMethods.get(card.getExtendedPropertiesValuesByName(vcard_action_method_type_header).component1()),
ArrayList(),
isRemotelyProvisionned)
{
......@@ -167,8 +166,14 @@ data class Device(
const val vcard_device_type_header = "X-LINPHONE-ACCOUNT-TYPE"
const val vcard_actions_list_header = "X-LINPHONE-ACCOUNT-ACTION"
const val vcard_action_method_type_header = "X-LINPHONE-ACCOUNT-DTMF-PROTOCOL"
val serverActionMethodsToLocalMethods = mapOf( "sipinfo" to "method_dtmf_sip_info","rfc2833" to "method_dtmf_rfc_4733","sipmessage" to "method_sip_message") // Server side method names to local app names
val vCardActionMethodsToDeviceMethods = mapOf( "sipinfo" to "method_dtmf_sip_info","rfc2833" to "method_dtmf_rfc_4733","sipmessage" to "method_sip_message") // Server side method names to local app names
fun deviceActionMethodsTovCardActionMethods () : HashMap<String,String> {
var result = hashMapOf<String,String>()
vCardActionMethodsToDeviceMethods.forEach {
result.put(it.value,it.key)
}
return result
}
fun typeIconAsBitmap(type: String?): Bitmap? {
return type?.let {
......@@ -193,53 +198,6 @@ data class Device(
}
}
fun remoteVcardValid(card: Vcard?) : Boolean {
if (card == null) {
Log.e("[Device] vCard validation : card is null")
return false
}
val validType = card.getExtendedPropertiesValuesByName(vcard_device_type_header)
.component1()
?.let { typeKey ->
DeviceTypes.deviceTypeSupported(typeKey)
} ?: false
if (!validType) {
Log.e("[Device] vCard validation : invalid type ${
card.getExtendedPropertiesValuesByName(vcard_device_type_header).component1()
}")
return false
}
val validDtmf = card.getExtendedPropertiesValuesByName(vcard_action_method_type_header)
.component1()?.let { remoteDtmfMethod ->
serverActionMethodsToLocalMethods.get(remoteDtmfMethod)?.let { localDtmfMethod ->
ActionsMethodTypes.methodTypeIsSupported(localDtmfMethod)
}?:false
}?:false
if (!validDtmf) {
Log.e("[Device] vCard validation : invalid dtmf sending method ${
card.getExtendedPropertiesValuesByName(vcard_action_method_type_header)
.component1()
}")
return false
}
var validActions = true
card.getExtendedPropertiesValuesByName(vcard_actions_list_header).forEach { action ->
val components = action.split(";")
if (components.size == 2) {
validActions = validActions && ActionTypes.isValid(components.component1())
} else {
validActions = false
}
if (!validActions) {
Log.e("[Device] vCard validation : invalid action $action")
}
}
return validActions
}
}
fun hasThumbNail(): Boolean {
......
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linhome-android
* (see https://www.linhome.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linhome.linphonecore.extensions
import org.linhome.customisation.ActionTypes
import org.linhome.customisation.ActionsMethodTypes
import org.linhome.customisation.DeviceTypes
import org.linhome.entities.Device
import org.linphone.core.Vcard
import org.linphone.core.tools.Log
fun Vcard.isValid(): Boolean {
val card = this
val validType = card.getExtendedPropertiesValuesByName(Device.vcard_device_type_header)
.component1()
?.let { typeKey ->
DeviceTypes.deviceTypeSupported(typeKey)
} ?: false
if (!validType) {
Log.e("[Device] vCard validation : invalid type ${
card.getExtendedPropertiesValuesByName(Device.vcard_device_type_header).component1()
}")
return false
}
val validDtmf = card.getExtendedPropertiesValuesByName(Device.vcard_action_method_type_header)
.component1()?.let { remoteDtmfMethod ->
Device.vCardActionMethodsToDeviceMethods.get(remoteDtmfMethod)?.let { localDtmfMethod ->
ActionsMethodTypes.methodTypeIsSupported(localDtmfMethod)
}?:false
}?:false
if (!validDtmf) {
Log.e("[Device] vCard validation : invalid dtmf sending method ${
card.getExtendedPropertiesValuesByName(Device.vcard_action_method_type_header)
.component1()
}")
return false
}
var validActions = true
card.getExtendedPropertiesValuesByName(Device.vcard_actions_list_header).forEach { action ->
val components = action.split(";")
if (components.size == 2) {
validActions = validActions && ActionTypes.isValid(components.component1())
} else {
validActions = false
}
if (!validActions) {
Log.e("[Device] vCard validation : invalid action $action")
}
}
return validActions
}
......@@ -25,6 +25,7 @@ import org.linhome.LinhomeApplication
import org.linhome.entities.Action
import org.linhome.entities.Device
import org.linhome.linphonecore.extensions.getString
import org.linhome.linphonecore.extensions.isValid
import org.linhome.store.StorageManager.devicesXml
import org.linphone.core.*
import org.linphone.mediastream.Log
......@@ -62,15 +63,17 @@ object DeviceStore {
val result = ArrayList<Device>()
LinhomeApplication.coreContext.core.getFriendListByName(local_devices_fl_name)?.friends?.forEach { friend ->
val card = friend.vcard
if (card != null) {
if (card != null && card.isValid()) {
result.add(Device(card, false))
} else {
Log.e("[DeviceStore] unable to create device from card (card is null or invdalid) : ${friend.vcard?.asVcard4String()} ")
}
}
LinhomeApplication.coreContext.core.config.getString("misc","contacts-vcard-list",null)?.also { url ->
LinhomeApplication.coreContext.core.getFriendListByName(url)?.also { serverFriendList ->
serverFriendList.friends.forEach { friend ->
val card = friend.vcard
if (Device.remoteVcardValid(card)) {
if (card != null && card.isValid()) {
result.add(Device(card!!, true))
} else {
Log.e("[DeviceStore] received invalid or malformed vCard from remote : ${friend.vcard?.asVcard4String()} ")
......@@ -111,12 +114,15 @@ object DeviceStore {
fun saveLocalDevices() {
val core = LinhomeApplication.coreContext.core
val localDevicesFriendList:FriendList?
core.getFriendListByName(local_devices_fl_name)?.also {
core.removeFriendList(it)
var localDevicesFriendList = core.getFriendListByName(local_devices_fl_name)
if (localDevicesFriendList != null) {
localDevicesFriendList.friends.forEach {
localDevicesFriendList!!.removeFriend(it)
}
} else {
localDevicesFriendList = core.createFriendList()
localDevicesFriendList.displayName = local_devices_fl_name
}
localDevicesFriendList = core.createFriendList()
localDevicesFriendList.displayName = local_devices_fl_name
devices.sortWith(compareBy({ it.name }, { it.address }))
devices.forEach { device ->
......
......@@ -79,8 +79,10 @@ class DevicesFragment : GenericFragment() {
if (LinhomeApplication.instance.smartPhone()) {
devicesViewModel.selectedDevice.observe(viewLifecycleOwner, Observer { device ->
val actionDetail = DevicesFragmentDirections.deviceInfo(device!!)
mainactivity.navController.navigate(actionDetail)
device?.also {
val actionDetail = DevicesFragmentDirections.deviceInfo(it)
mainactivity.navController.navigate(actionDetail)
}
})
}
......
......@@ -117,6 +117,7 @@ class DeviceEditorFragment : GenericFragment() {
it.binding.code.validate()
}
DeviceStore.findDeviceByAddress(model.address.first.value)?.also {
cdlog("${args.device?.id} ${it.id}")
if (args.device?.id != it.id) {
binding.address.setError(
Texts.get(
......
......@@ -29,7 +29,7 @@ class ValidatorFactory {
val numberEmptyOrValidValidator = RegExpFormatValidator("^[0-9]*$", "invalid_number")
val numberNonEmptyOrValidValidator = RegExpFormatValidator("^[0-9]+$", "invalid_number")
val sipUri =
NonEmptyWithRegExpFormatValidator("^([^@]+)(?:@(.+))?$", "invalid_sip_uri")
NonEmptyWithRegExpFormatValidator("^(sip|sips):(.+)@(.+)\$", "invalid_sip_uri")
val actionCode = RegExpFormatValidator("^[0-9#*]+$", "invalid_action_code")
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment