1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define __BTSTACK_FILE__ "pbap_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "hci_cmd.h" 48 #include "btstack_run_loop.h" 49 #include "btstack_debug.h" 50 #include "hci.h" 51 #include "btstack_memory.h" 52 #include "hci_dump.h" 53 #include "l2cap.h" 54 #include "bluetooth_sdp.h" 55 #include "classic/sdp_client_rfcomm.h" 56 #include "btstack_event.h" 57 #include "md5.h" 58 #include "yxml.h" 59 60 #include "classic/obex.h" 61 #include "classic/obex_iterator.h" 62 #include "classic/goep_client.h" 63 #include "classic/pbap_client.h" 64 65 // 796135f0-f0c5-11d8-0966- 0800200c9a66 66 static const uint8_t pbap_uuid[] = { 0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66}; 67 68 const char * pbap_phonebook_type = "x-bt/phonebook"; 69 70 const char * pbap_vcard_listing_type = "x-bt/vcard-listing"; 71 const char * pbap_vcard_listing_name = "pb"; 72 73 // default 74 static uint32_t pbap_supported_features = 0x0000; 75 76 static int pbap_flow_control_enabled = 0; 77 78 typedef enum { 79 PBAP_INIT = 0, 80 PBAP_W4_GOEP_CONNECTION, 81 PBAP_W2_SEND_CONNECT_REQUEST, 82 PBAP_W4_CONNECT_RESPONSE, 83 PBAP_W4_USER_AUTHENTICATION, 84 PBAP_W2_SEND_AUTHENTICATED_CONNECT, 85 PBAP_CONNECT_RESPONSE_RECEIVED, 86 PBAP_CONNECTED, 87 // 88 PBAP_W2_SEND_DISCONNECT_REQUEST, 89 PBAP_W4_DISCONNECT_RESPONSE, 90 // 91 PBAP_W2_PULL_PHONEBOOK, 92 PBAP_W4_PHONEBOOK, 93 PBAP_W2_SET_PATH_ROOT, 94 PBAP_W4_SET_PATH_ROOT_COMPLETE, 95 PBAP_W2_SET_PATH_ELEMENT, 96 PBAP_W4_SET_PATH_ELEMENT_COMPLETE, 97 PBAP_W2_GET_PHONEBOOK_SIZE, 98 PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE, 99 // 100 PBAP_W2_GET_CARD_LIST, 101 PBAP_W4_GET_CARD_LIST_COMPLETE, 102 103 } pbap_state_t; 104 105 typedef enum { 106 SRM_DISABLED, 107 SRM_W4_CONFIRM, 108 SRM_ENABLED_BUT_WAITING, 109 SRM_ENABLED 110 } srm_state_t; 111 112 typedef struct pbap_client { 113 pbap_state_t state; 114 uint16_t cid; 115 bd_addr_t bd_addr; 116 hci_con_handle_t con_handle; 117 uint8_t incoming; 118 uint16_t goep_cid; 119 btstack_packet_handler_t client_handler; 120 int request_number; 121 srm_state_t srm_state; 122 int single_response_mode_parameter; 123 const char * current_folder; 124 const char * phone_number; 125 const char * phonebook_path; 126 uint16_t set_path_offset; 127 uint8_t authentication_options; 128 uint16_t authentication_nonce[16]; 129 const char * authentication_password; 130 yxml_t xml_parser; 131 uint8_t xml_buffer[50]; 132 } pbap_client_t; 133 134 static pbap_client_t _pbap_client; 135 static pbap_client_t * pbap_client = &_pbap_client; 136 137 static int pbap_client_next_triggered; 138 139 static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){ 140 uint8_t event[15]; 141 int pos = 0; 142 event[pos++] = HCI_EVENT_PBAP_META; 143 pos++; // skip len 144 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED; 145 little_endian_store_16(event,pos,context->cid); 146 pos+=2; 147 event[pos++] = status; 148 memcpy(&event[pos], context->bd_addr, 6); 149 pos += 6; 150 little_endian_store_16(event,pos,context->con_handle); 151 pos += 2; 152 event[pos++] = context->incoming; 153 event[1] = pos - 2; 154 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos); 155 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 156 } 157 158 static void pbap_client_emit_connection_closed_event(pbap_client_t * context){ 159 uint8_t event[5]; 160 int pos = 0; 161 event[pos++] = HCI_EVENT_PBAP_META; 162 pos++; // skip len 163 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED; 164 little_endian_store_16(event,pos,context->cid); 165 pos+=2; 166 event[1] = pos - 2; 167 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos); 168 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 169 } 170 171 static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){ 172 uint8_t event[6]; 173 int pos = 0; 174 event[pos++] = HCI_EVENT_PBAP_META; 175 pos++; // skip len 176 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED; 177 little_endian_store_16(event,pos,context->cid); 178 pos+=2; 179 event[pos++]= status; 180 event[1] = pos - 2; 181 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos); 182 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 183 } 184 185 static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){ 186 uint8_t event[8]; 187 int pos = 0; 188 event[pos++] = HCI_EVENT_PBAP_META; 189 pos++; // skip len 190 event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE; 191 little_endian_store_16(event,pos,context->cid); 192 pos+=2; 193 event[pos++] = status; 194 little_endian_store_16(event,pos, phonebook_size); 195 pos+=2; 196 event[1] = pos - 2; 197 if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos); 198 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 199 } 200 201 static void pbap_client_emit_authentication_event(pbap_client_t * context, uint8_t options){ 202 // split options 203 uint8_t user_id_required = options & 1 ? 1 : 0; 204 uint8_t full_access = options & 2 ? 1 : 0; 205 206 uint8_t event[7]; 207 int pos = 0; 208 event[pos++] = HCI_EVENT_PBAP_META; 209 pos++; // skip len 210 event[pos++] = PBAP_SUBEVENT_AUTHENTICATION_REQUEST; 211 little_endian_store_16(event,pos,context->cid); 212 pos+=2; 213 event[pos++] = user_id_required; 214 event[pos++] = full_access; 215 if (pos != sizeof(event)) log_error("pbap_client_emit_authentication_event size %u", pos); 216 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 217 } 218 219 static void pbap_client_emit_card_result_event(pbap_client_t * context, const char * name, const char * handle){ 220 uint8_t event[5 + PBAP_MAX_NAME_LEN + PBAP_MAX_HANDLE_LEN]; 221 int pos = 0; 222 event[pos++] = HCI_EVENT_PBAP_META; 223 pos++; // skip len 224 event[pos++] = PBAP_SUBEVENT_CARD_RESULT; 225 little_endian_store_16(event,pos,context->cid); 226 pos+=2; 227 int name_len = btstack_min(PBAP_MAX_NAME_LEN, strlen(name)); 228 event[pos++] = name_len; 229 memcpy(&event[pos], name, name_len); 230 pos += name_len; 231 int handle_len = btstack_min(PBAP_MAX_HANDLE_LEN, strlen(handle)); 232 event[pos++] = handle_len; 233 memcpy(&event[pos], handle, handle_len); 234 pos += handle_len; 235 event[1] = pos - 2; 236 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 237 } 238 239 static const uint8_t collon = (uint8_t) ':'; 240 241 static void pbap_handle_can_send_now(void){ 242 uint8_t path_element[20]; 243 uint16_t path_element_start; 244 uint16_t path_element_len; 245 uint8_t application_parameters[PBAP_MAX_PHONE_NUMBER_LEN + 10]; 246 uint8_t challenge_response[36]; 247 int i; 248 uint16_t phone_number_len; 249 250 MD5_CTX md5_ctx; 251 252 switch (pbap_client->state){ 253 case PBAP_W2_SEND_CONNECT_REQUEST: 254 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 255 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid); 256 // Mandatory if the PSE advertises a PbapSupportedFeatures attribute in its SDP record, else excluded. 257 if (goep_client_get_pbap_supported_features(pbap_client->goep_cid) != PBAP_FEATURES_NOT_PRESENT){ 258 application_parameters[0] = PBAP_APPLICATION_PARAMETER_PBAP_SUPPORTED_FEATURES; 259 application_parameters[1] = 4; 260 big_endian_store_32(application_parameters, 2, pbap_supported_features); 261 goep_client_add_header_application_parameters(pbap_client->goep_cid, 6, &application_parameters[0]); 262 } 263 pbap_client->state = PBAP_W4_CONNECT_RESPONSE; 264 goep_client_execute(pbap_client->goep_cid); 265 break; 266 case PBAP_W2_SEND_AUTHENTICATED_CONNECT: 267 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 268 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid); 269 // setup authentication challenge response 270 i = 0; 271 challenge_response[i++] = 0; // Tag Digest 272 challenge_response[i++] = 16; // Len 273 // calculate md5 274 MD5_Init(&md5_ctx); 275 MD5_Update(&md5_ctx, pbap_client->authentication_nonce, 16); 276 MD5_Update(&md5_ctx, &collon, 1); 277 MD5_Update(&md5_ctx, pbap_client->authentication_password, strlen(pbap_client->authentication_password)); 278 MD5_Final(&challenge_response[i], &md5_ctx); 279 i += 16; 280 challenge_response[i++] = 2; // Tag Nonce 281 challenge_response[i++] = 16; // Len 282 memcpy(&challenge_response[i], pbap_client->authentication_nonce, 16); 283 i += 16; 284 goep_client_add_header_challenge_response(pbap_client->goep_cid, i, challenge_response); 285 pbap_client->state = PBAP_W4_CONNECT_RESPONSE; 286 goep_client_execute(pbap_client->goep_cid); 287 break; 288 case PBAP_W2_SEND_DISCONNECT_REQUEST: 289 goep_client_create_disconnect_request(pbap_client->goep_cid); 290 pbap_client->state = PBAP_W4_DISCONNECT_RESPONSE; 291 goep_client_execute(pbap_client->goep_cid); 292 return; 293 case PBAP_W2_PULL_PHONEBOOK: 294 case PBAP_W2_GET_PHONEBOOK_SIZE: 295 goep_client_create_get_request(pbap_client->goep_cid); 296 if (pbap_client->request_number == 0){ 297 if (!pbap_flow_control_enabled){ 298 goep_client_add_header_srm_enable(pbap_client->goep_cid); 299 pbap_client->srm_state = SRM_W4_CONFIRM; 300 } 301 goep_client_add_header_type(pbap_client->goep_cid, pbap_phonebook_type); 302 goep_client_add_header_name(pbap_client->goep_cid, pbap_client->phonebook_path); 303 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){ 304 // Regular TLV wih 1-byte len 305 application_parameters[0] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT; 306 application_parameters[1] = 2; 307 big_endian_store_16(application_parameters, 2, 0); 308 goep_client_add_header_application_parameters(pbap_client->goep_cid, 4, &application_parameters[0]); 309 } else { 310 // 311 } 312 } 313 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){ 314 // state 315 pbap_client->state = PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE; 316 } else { 317 // state 318 pbap_client->state = PBAP_W4_PHONEBOOK; 319 } 320 // send packet 321 pbap_client->request_number++; 322 goep_client_execute(pbap_client->goep_cid); 323 break; 324 case PBAP_W2_GET_CARD_LIST: 325 goep_client_create_get_request(pbap_client->goep_cid); 326 if (pbap_client->request_number == 0){ 327 if (!pbap_flow_control_enabled){ 328 goep_client_add_header_srm_enable(pbap_client->goep_cid); 329 pbap_client->srm_state = SRM_W4_CONFIRM; 330 } 331 goep_client_add_header_type(pbap_client->goep_cid, pbap_vcard_listing_type); 332 goep_client_add_header_name(pbap_client->goep_cid, pbap_vcard_listing_name); 333 // Regular TLV wih 1-byte len 334 i = 0; 335 phone_number_len = btstack_min(PBAP_MAX_PHONE_NUMBER_LEN, strlen(pbap_client->phone_number)); 336 application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_VALUE; 337 application_parameters[i++] = phone_number_len; 338 memcpy(&application_parameters[i], pbap_client->phone_number, phone_number_len); 339 i += phone_number_len; 340 application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_PROPERTY; 341 application_parameters[i++] = 1; 342 application_parameters[i++] = 0x01; // Number 343 goep_client_add_header_application_parameters(pbap_client->goep_cid, i, &application_parameters[0]); 344 pbap_client->state = PBAP_W4_GET_CARD_LIST_COMPLETE; 345 } 346 // send packet 347 goep_client_execute(pbap_client->goep_cid); 348 break; 349 case PBAP_W2_SET_PATH_ROOT: 350 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 351 // On Android 4.2 Cyanogenmod, using "" as path fails 352 // goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == / 353 // state 354 pbap_client->state = PBAP_W4_SET_PATH_ROOT_COMPLETE; 355 // send packet 356 goep_client_execute(pbap_client->goep_cid); 357 break; 358 case PBAP_W2_SET_PATH_ELEMENT: 359 // find '/' or '\0' 360 path_element_start = pbap_client->set_path_offset; 361 while (pbap_client->current_folder[pbap_client->set_path_offset] != '\0' && 362 pbap_client->current_folder[pbap_client->set_path_offset] != '/'){ 363 pbap_client->set_path_offset++; 364 } 365 // skip / 366 if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){ 367 pbap_client->set_path_offset++; 368 } 369 path_element_len = pbap_client->set_path_offset-path_element_start; 370 memcpy(path_element, &pbap_client->current_folder[path_element_start], path_element_len); 371 path_element[path_element_len] = 0; 372 373 // detect end of path (after setting path_element) 374 if (pbap_client->current_folder[pbap_client->set_path_offset] == '\0'){ 375 pbap_client->current_folder = NULL; 376 } 377 378 log_info("Path element '%s', done %u", path_element, pbap_client->current_folder == NULL); 379 380 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 381 goep_client_add_header_name(pbap_client->goep_cid, (const char *) path_element); // next element 382 // state 383 pbap_client->state = PBAP_W4_SET_PATH_ELEMENT_COMPLETE; 384 // send packet 385 goep_client_execute(pbap_client->goep_cid); 386 break; 387 default: 388 break; 389 } 390 } 391 392 static void pbap_parse_authentication_challenge(pbap_client_t * context, const uint8_t * challenge_data, uint16_t challenge_len){ 393 // printf("Challenge: "); 394 // printf_hexdump(challenge_data, challenge_len); 395 int i; 396 // uint8_t charset_code = 0; 397 for (i=0 ; i<challenge_len ; ){ 398 int tag = challenge_data[i]; 399 int len = challenge_data[i + 1]; 400 i += 2; 401 switch (tag) { 402 case 0: 403 if (len != 0x10) { 404 log_error("Invalid OBEX digest len %u", len); 405 return; 406 } 407 memcpy(context->authentication_nonce, &challenge_data[i], 16); 408 // printf("Nonce: "); 409 // printf_hexdump(context->authentication_nonce, 16); 410 break; 411 case 1: 412 context->authentication_options = challenge_data[i]; 413 // printf("Options %u\n", context->authentication_options); 414 break; 415 case 2: 416 // TODO: handle charset 417 // charset_code = challenge_data[i]; 418 break; 419 } 420 i += len; 421 } 422 } 423 424 static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 425 426 UNUSED(channel); // ok: there is no channel 427 UNUSED(size); // ok: handling own geop events 428 429 obex_iterator_t it; 430 uint8_t status; 431 int srm_value; 432 int srmp_value; 433 int wait_for_user = 0; 434 switch (packet_type){ 435 case HCI_EVENT_PACKET: 436 switch (hci_event_packet_get_type(packet)) { 437 case HCI_EVENT_GOEP_META: 438 switch (hci_event_goep_meta_get_subevent_code(packet)){ 439 case GOEP_SUBEVENT_CONNECTION_OPENED: 440 status = goep_subevent_connection_opened_get_status(packet); 441 pbap_client->con_handle = goep_subevent_connection_opened_get_con_handle(packet); 442 pbap_client->incoming = goep_subevent_connection_opened_get_incoming(packet); 443 goep_subevent_connection_opened_get_bd_addr(packet, pbap_client->bd_addr); 444 if (status){ 445 log_info("pbap: connection failed %u", status); 446 pbap_client->state = PBAP_INIT; 447 pbap_client_emit_connected_event(pbap_client, status); 448 } else { 449 log_info("pbap: connection established"); 450 pbap_client->goep_cid = goep_subevent_connection_opened_get_goep_cid(packet); 451 pbap_client->state = PBAP_W2_SEND_CONNECT_REQUEST; 452 goep_client_request_can_send_now(pbap_client->goep_cid); 453 } 454 break; 455 case GOEP_SUBEVENT_CONNECTION_CLOSED: 456 if (pbap_client->state != PBAP_CONNECTED){ 457 pbap_client_emit_operation_complete_event(pbap_client, OBEX_DISCONNECTED); 458 } 459 pbap_client->state = PBAP_INIT; 460 pbap_client_emit_connection_closed_event(pbap_client); 461 break; 462 case GOEP_SUBEVENT_CAN_SEND_NOW: 463 pbap_handle_can_send_now(); 464 break; 465 } 466 break; 467 default: 468 break; 469 } 470 break; 471 case GOEP_DATA_PACKET: 472 // TODO: handle chunked data 473 obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); 474 switch (pbap_client->state){ 475 case PBAP_W4_CONNECT_RESPONSE: 476 switch (packet[0]){ 477 case OBEX_RESP_SUCCESS: 478 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 479 uint8_t hi = obex_iterator_get_hi(&it); 480 if (hi == OBEX_HEADER_CONNECTION_ID){ 481 goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it)); 482 } 483 } 484 pbap_client->state = PBAP_CONNECTED; 485 pbap_client_emit_connected_event(pbap_client, 0); 486 break; 487 case OBEX_RESP_UNAUTHORIZED: 488 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 489 uint8_t hi = obex_iterator_get_hi(&it); 490 if (hi == OBEX_HEADER_AUTHENTICATION_CHALLENGE){ 491 pbap_parse_authentication_challenge(pbap_client, obex_iterator_get_data(&it), obex_iterator_get_data_len(&it)); 492 } 493 } 494 pbap_client->state = PBAP_W4_USER_AUTHENTICATION; 495 pbap_client_emit_authentication_event(pbap_client, pbap_client->authentication_options); 496 break; 497 default: 498 log_info("pbap: obex connect failed, result 0x%02x", packet[0]); 499 pbap_client->state = PBAP_INIT; 500 pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED); 501 break; 502 } 503 break; 504 case PBAP_W4_DISCONNECT_RESPONSE: 505 goep_client_disconnect(pbap_client->goep_cid); 506 break; 507 case PBAP_W4_SET_PATH_ROOT_COMPLETE: 508 case PBAP_W4_SET_PATH_ELEMENT_COMPLETE: 509 log_info("set path root/path element complete, current folder %s, path offset %u", pbap_client->current_folder, pbap_client->set_path_offset); 510 if (packet[0] == OBEX_RESP_SUCCESS){ 511 if (pbap_client->current_folder){ 512 pbap_client->state = PBAP_W2_SET_PATH_ELEMENT; 513 goep_client_request_can_send_now(pbap_client->goep_cid); 514 } else { 515 pbap_client->state = PBAP_CONNECTED; 516 pbap_client_emit_operation_complete_event(pbap_client, 0); 517 } 518 } else if (packet[0] == OBEX_RESP_NOT_FOUND){ 519 pbap_client->state = PBAP_CONNECTED; 520 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND); 521 } else { 522 pbap_client->state = PBAP_CONNECTED; 523 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 524 } 525 break; 526 case PBAP_W4_PHONEBOOK: 527 pbap_client_next_triggered = 0; 528 wait_for_user = 0; 529 srm_value = OBEX_SRM_DISABLE; 530 srmp_value = OBEX_SRMP_NEXT; 531 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 532 uint8_t hi = obex_iterator_get_hi(&it); 533 uint16_t data_len = obex_iterator_get_data_len(&it); 534 const uint8_t * data = data = obex_iterator_get_data(&it); 535 switch (hi){ 536 case OBEX_HEADER_BODY: 537 case OBEX_HEADER_END_OF_BODY: 538 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len); 539 wait_for_user++; 540 if (wait_for_user > 1){ 541 log_error("wait_for_user %u", wait_for_user); 542 } 543 break; 544 case OBEX_HEADER_SINGLE_RESPONSE_MODE: 545 if (data_len != 1) break; 546 srm_value = *data; 547 break; 548 case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER: 549 if (data_len != 1) break; 550 srmp_value = *data; 551 break; 552 default: 553 break; 554 } 555 } 556 switch(packet[0]){ 557 case OBEX_RESP_CONTINUE: 558 switch (pbap_client->srm_state){ 559 case SRM_W4_CONFIRM: 560 switch (srm_value){ 561 case OBEX_SRM_ENABLE: 562 switch (srmp_value){ 563 case OBEX_SRMP_WAIT: 564 pbap_client->srm_state = SRM_ENABLED_BUT_WAITING; 565 break; 566 default: 567 pbap_client->srm_state = SRM_ENABLED; 568 break; 569 } 570 break; 571 default: 572 pbap_client->srm_state = SRM_DISABLED; 573 break; 574 } 575 break; 576 case SRM_ENABLED_BUT_WAITING: 577 switch (srmp_value){ 578 case OBEX_SRMP_WAIT: 579 pbap_client->srm_state = SRM_ENABLED_BUT_WAITING; 580 break; 581 default: 582 pbap_client->srm_state = SRM_ENABLED; 583 break; 584 } 585 break; 586 default: 587 break; 588 } 589 log_info("SRM state %u", pbap_client->srm_state); 590 if (pbap_client->srm_state == SRM_ENABLED) break; 591 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; 592 if (!wait_for_user || pbap_client_next_triggered) { 593 goep_client_request_can_send_now(pbap_client->goep_cid); 594 } 595 break; 596 case OBEX_RESP_SUCCESS: 597 pbap_client->state = PBAP_CONNECTED; 598 pbap_client_emit_operation_complete_event(pbap_client, 0); 599 break; 600 default: 601 log_info("unexpected response 0x%02x", packet[0]); 602 pbap_client->state = PBAP_CONNECTED; 603 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 604 break; 605 } 606 break; 607 case PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE: 608 pbap_client->state = PBAP_CONNECTED; 609 if (packet[0] == OBEX_RESP_SUCCESS){ 610 int have_size = 0; 611 uint16_t phonebook_size; 612 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 613 uint8_t hi = obex_iterator_get_hi(&it); 614 if (hi == OBEX_HEADER_APPLICATION_PARAMETERS){ 615 uint16_t data_len = obex_iterator_get_data_len(&it); 616 const uint8_t * data = obex_iterator_get_data(&it); 617 // iterate over application headers (TLV with 1 bytes len) 618 unsigned int i = 0; 619 while (i<data_len){ 620 uint8_t tag = data[i++]; 621 uint8_t len = data[i++]; 622 if (tag == PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE && len == 2){ 623 have_size = 1; 624 phonebook_size = big_endian_read_16(data, i); 625 } 626 i+=len; 627 } 628 } 629 } 630 if (have_size){ 631 pbap_client_emit_phonebook_size_event(pbap_client, 0, phonebook_size); 632 break; 633 } 634 } 635 pbap_client_emit_phonebook_size_event(pbap_client, OBEX_UNKNOWN_ERROR, 0); 636 break; 637 case PBAP_W4_GET_CARD_LIST_COMPLETE: 638 switch (packet[0]){ 639 case OBEX_RESP_CONTINUE: 640 pbap_client->state = PBAP_W2_GET_CARD_LIST; 641 goep_client_request_can_send_now(pbap_client->goep_cid); 642 break; 643 case OBEX_RESP_SUCCESS: 644 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 645 uint8_t hi = obex_iterator_get_hi(&it); 646 if (hi == OBEX_HEADER_END_OF_BODY){ 647 uint16_t data_len = obex_iterator_get_data_len(&it); 648 const uint8_t * data = obex_iterator_get_data(&it); 649 // now try parsing it 650 yxml_init(&pbap_client->xml_parser, pbap_client->xml_buffer, sizeof(pbap_client->xml_buffer)); 651 int card_found = 0; 652 int name_found = 0; 653 int handle_found = 0; 654 char name[PBAP_MAX_NAME_LEN]; 655 char handle[PBAP_MAX_HANDLE_LEN]; 656 name[0] = 0; 657 handle[0] = 0; 658 while (data_len--){ 659 yxml_ret_t r = yxml_parse(&pbap_client->xml_parser, *data++); 660 switch (r){ 661 case YXML_ELEMSTART: 662 card_found = strcmp("card", pbap_client->xml_parser.elem) == 0; 663 break; 664 case YXML_ELEMEND: 665 if (card_found){ 666 pbap_client_emit_card_result_event(pbap_client, name, handle); 667 } 668 card_found = 0; 669 break; 670 case YXML_ATTRSTART: 671 if (!card_found) break; 672 if (strcmp("name", pbap_client->xml_parser.attr) == 0){ 673 name_found = 1; 674 break; 675 } 676 if (strcmp("handle", pbap_client->xml_parser.attr) == 0){ 677 handle_found = 1; 678 break; 679 } 680 break; 681 case YXML_ATTRVAL: 682 if (name_found) { 683 // "In UTF-8, characters from the U+0000..U+10FFFF range (the UTF-16 accessible range) are encoded using sequences of 1 to 4 octets." 684 if (strlen(name) + 4 + 1 >= sizeof(name)) break; 685 strcat(name, pbap_client->xml_parser.data); 686 break; 687 } 688 if (handle_found) { 689 // "In UTF-8, characters from the U+0000..U+10FFFF range (the UTF-16 accessible range) are encoded using sequences of 1 to 4 octets." 690 if (strlen(handle) + 4 + 1 >= sizeof(handle)) break; 691 strcat(handle, pbap_client->xml_parser.data); 692 break; 693 } 694 break; 695 case YXML_ATTREND: 696 name_found = 0; 697 handle_found = 0; 698 break; 699 default: 700 break; 701 } 702 } 703 // 704 pbap_client->state = PBAP_CONNECTED; 705 pbap_client_emit_operation_complete_event(pbap_client, 0); 706 } 707 } 708 break; 709 case OBEX_RESP_NOT_ACCEPTABLE: 710 pbap_client->state = PBAP_CONNECTED; 711 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_ACCEPTABLE); 712 break; 713 default: 714 log_info("unexpected response 0x%02x", packet[0]); 715 pbap_client->state = PBAP_CONNECTED; 716 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 717 break; 718 } 719 break; 720 default: 721 break; 722 } 723 break; 724 default: 725 break; 726 } 727 } 728 729 void pbap_client_init(void){ 730 memset(pbap_client, 0, sizeof(pbap_client_t)); 731 pbap_client->state = PBAP_INIT; 732 pbap_client->cid = 1; 733 } 734 735 uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){ 736 if (pbap_client->state != PBAP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED; 737 pbap_client->state = PBAP_W4_GOEP_CONNECTION; 738 pbap_client->client_handler = handler; 739 uint8_t err = goep_client_create_connection(&pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, &pbap_client->goep_cid); 740 *out_cid = pbap_client->cid; 741 if (err) return err; 742 return 0; 743 } 744 745 uint8_t pbap_disconnect(uint16_t pbap_cid){ 746 UNUSED(pbap_cid); 747 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 748 pbap_client->state = PBAP_W2_SEND_DISCONNECT_REQUEST; 749 goep_client_request_can_send_now(pbap_client->goep_cid); 750 return 0; 751 } 752 753 uint8_t pbap_get_phonebook_size(uint16_t pbap_cid, const char * path){ 754 UNUSED(pbap_cid); 755 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 756 pbap_client->state = PBAP_W2_GET_PHONEBOOK_SIZE; 757 pbap_client->phonebook_path = path; 758 pbap_client->request_number = 0; 759 goep_client_request_can_send_now(pbap_client->goep_cid); 760 return 0; 761 } 762 763 uint8_t pbap_pull_phonebook(uint16_t pbap_cid, const char * path){ 764 UNUSED(pbap_cid); 765 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 766 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; 767 pbap_client->phonebook_path = path; 768 pbap_client->request_number = 0; 769 goep_client_request_can_send_now(pbap_client->goep_cid); 770 return 0; 771 } 772 773 uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){ 774 UNUSED(pbap_cid); 775 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 776 pbap_client->state = PBAP_W2_SET_PATH_ROOT; 777 pbap_client->current_folder = path; 778 pbap_client->set_path_offset = 0; 779 goep_client_request_can_send_now(pbap_client->goep_cid); 780 return 0; 781 } 782 783 uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password){ 784 UNUSED(pbap_cid); 785 if (pbap_client->state != PBAP_W4_USER_AUTHENTICATION) return BTSTACK_BUSY; 786 pbap_client->state = PBAP_W2_SEND_AUTHENTICATED_CONNECT; 787 pbap_client->authentication_password = password; 788 goep_client_request_can_send_now(pbap_client->goep_cid); 789 return 0; 790 } 791 792 uint8_t pbap_lookup_by_number(uint16_t pbap_cid, const char * phone_number){ 793 UNUSED(pbap_cid); 794 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 795 pbap_client->state = PBAP_W2_GET_CARD_LIST; 796 pbap_client->phone_number = phone_number; 797 goep_client_request_can_send_now(pbap_client->goep_cid); 798 return 0; 799 } 800 801 uint8_t pbap_next_packet(uint16_t pbap_cid){ 802 if (!pbap_flow_control_enabled) return 0; 803 804 // log_info("pbap_next_packet, state %x", pbap_client->state); 805 UNUSED(pbap_cid); 806 switch (pbap_client->state){ 807 case PBAP_W2_PULL_PHONEBOOK: 808 goep_client_request_can_send_now(pbap_client->goep_cid); 809 break; 810 case PBAP_W4_PHONEBOOK: 811 pbap_client_next_triggered = 1; 812 break; 813 default: 814 break; 815 } 816 return 0; 817 } 818 819 void pbap_set_flow_control_mode(int enable){ 820 pbap_flow_control_enabled = enable; 821 } 822