xref: /btstack/src/classic/pbap_client.c (revision 9eb7827db84839e021eb308d2468bbb2f5789b6b)
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 typedef enum {
77     PBAP_INIT = 0,
78     PBAP_W4_GOEP_CONNECTION,
79     PBAP_W2_SEND_CONNECT_REQUEST,
80     PBAP_W4_CONNECT_RESPONSE,
81     PBAP_W4_USER_AUTHENTICATION,
82     PBAP_W2_SEND_AUTHENTICATED_CONNECT,
83     PBAP_CONNECT_RESPONSE_RECEIVED,
84     PBAP_CONNECTED,
85     //
86     PBAP_W2_SEND_DISCONNECT_REQUEST,
87     PBAP_W4_DISCONNECT_RESPONSE,
88     //
89     PBAP_W2_PULL_PHONEBOOK,
90     PBAP_W4_PHONEBOOK,
91     PBAP_W2_SET_PATH_ROOT,
92     PBAP_W4_SET_PATH_ROOT_COMPLETE,
93     PBAP_W2_SET_PATH_ELEMENT,
94     PBAP_W4_SET_PATH_ELEMENT_COMPLETE,
95     PBAP_W2_GET_PHONEBOOK_SIZE,
96     PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE,
97     //
98     PBAP_W2_GET_CARD_LIST,
99     PBAP_W4_GET_CARD_LIST_COMPLETE,
100 
101 } pbap_state_t;
102 
103 typedef enum {
104     SRM_DISABLED,
105     SRM_W4_CONFIRM,
106     SRM_ENABLED_BUT_WAITING,
107     SRM_ENABLED
108 } srm_state_t;
109 
110 typedef struct pbap_client {
111     pbap_state_t state;
112     uint16_t  cid;
113     bd_addr_t bd_addr;
114     hci_con_handle_t con_handle;
115     uint8_t   incoming;
116     uint16_t  goep_cid;
117     btstack_packet_handler_t client_handler;
118     int request_number;
119     srm_state_t srm_state;
120     int single_response_mode_parameter;
121     const char * current_folder;
122     const char * phone_number;
123     const char * phonebook_path;
124     uint16_t set_path_offset;
125     /* abort */
126     uint8_t  abort_operation;
127     /* authentication */
128     uint8_t  authentication_options;
129     uint16_t authentication_nonce[16];
130     const char * authentication_password;
131     /* xml parser */
132     yxml_t  xml_parser;
133     uint8_t xml_buffer[50];
134     /* flow control mode */
135     uint8_t flow_control_enabled;
136     uint8_t flow_next_triggered;
137 } pbap_client_t;
138 
139 static pbap_client_t _pbap_client;
140 static pbap_client_t * pbap_client = &_pbap_client;
141 
142 static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
143     uint8_t event[15];
144     int pos = 0;
145     event[pos++] = HCI_EVENT_PBAP_META;
146     pos++;  // skip len
147     event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED;
148     little_endian_store_16(event,pos,context->cid);
149     pos+=2;
150     event[pos++] = status;
151     memcpy(&event[pos], context->bd_addr, 6);
152     pos += 6;
153     little_endian_store_16(event,pos,context->con_handle);
154     pos += 2;
155     event[pos++] = context->incoming;
156     event[1] = pos - 2;
157     if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
158     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
159 }
160 
161 static void pbap_client_emit_connection_closed_event(pbap_client_t * context){
162     uint8_t event[5];
163     int pos = 0;
164     event[pos++] = HCI_EVENT_PBAP_META;
165     pos++;  // skip len
166     event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED;
167     little_endian_store_16(event,pos,context->cid);
168     pos+=2;
169     event[1] = pos - 2;
170     if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos);
171     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
172 }
173 
174 static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
175     uint8_t event[6];
176     int pos = 0;
177     event[pos++] = HCI_EVENT_PBAP_META;
178     pos++;  // skip len
179     event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED;
180     little_endian_store_16(event,pos,context->cid);
181     pos+=2;
182     event[pos++]= status;
183     event[1] = pos - 2;
184     if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos);
185     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
186 }
187 
188 static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){
189     uint8_t event[8];
190     int pos = 0;
191     event[pos++] = HCI_EVENT_PBAP_META;
192     pos++;  // skip len
193     event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE;
194     little_endian_store_16(event,pos,context->cid);
195     pos+=2;
196     event[pos++] = status;
197     little_endian_store_16(event,pos, phonebook_size);
198     pos+=2;
199     event[1] = pos - 2;
200     if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos);
201     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
202 }
203 
204 static void pbap_client_emit_authentication_event(pbap_client_t * context, uint8_t options){
205     // split options
206     uint8_t user_id_required = options & 1 ? 1 : 0;
207     uint8_t full_access      = options & 2 ? 1 : 0;
208 
209     uint8_t event[7];
210     int pos = 0;
211     event[pos++] = HCI_EVENT_PBAP_META;
212     pos++;  // skip len
213     event[pos++] = PBAP_SUBEVENT_AUTHENTICATION_REQUEST;
214     little_endian_store_16(event,pos,context->cid);
215     pos+=2;
216     event[pos++] = user_id_required;
217     event[pos++] = full_access;
218     if (pos != sizeof(event)) log_error("pbap_client_emit_authentication_event size %u", pos);
219     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
220 }
221 
222 static void pbap_client_emit_card_result_event(pbap_client_t * context, const char * name, const char * handle){
223     uint8_t event[5 + PBAP_MAX_NAME_LEN + PBAP_MAX_HANDLE_LEN];
224     int pos = 0;
225     event[pos++] = HCI_EVENT_PBAP_META;
226     pos++;  // skip len
227     event[pos++] = PBAP_SUBEVENT_CARD_RESULT;
228     little_endian_store_16(event,pos,context->cid);
229     pos+=2;
230     int name_len = btstack_min(PBAP_MAX_NAME_LEN, strlen(name));
231     event[pos++] = name_len;
232     memcpy(&event[pos], name, name_len);
233     pos += name_len;
234     int handle_len = btstack_min(PBAP_MAX_HANDLE_LEN, strlen(handle));
235     event[pos++] = handle_len;
236     memcpy(&event[pos], handle, handle_len);
237     pos += handle_len;
238     event[1] = pos - 2;
239     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
240 }
241 
242 static const uint8_t collon = (uint8_t) ':';
243 
244 static void pbap_handle_can_send_now(void){
245     uint8_t  path_element[20];
246     uint16_t path_element_start;
247     uint16_t path_element_len;
248     uint8_t  application_parameters[PBAP_MAX_PHONE_NUMBER_LEN + 10];
249     uint8_t  challenge_response[36];
250     int i;
251     uint16_t phone_number_len;
252 
253     MD5_CTX md5_ctx;
254 
255     if (pbap_client->abort_operation){
256         pbap_client->abort_operation = 0;
257         pbap_client->state = PBAP_CONNECTED;
258         goep_client_create_abort_request(pbap_client->goep_cid);
259         goep_client_execute(pbap_client->goep_cid);
260         return;
261     }
262 
263     switch (pbap_client->state){
264         case PBAP_W2_SEND_CONNECT_REQUEST:
265             goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
266             goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
267             // Mandatory if the PSE advertises a PbapSupportedFeatures attribute in its SDP record, else excluded.
268             if (goep_client_get_pbap_supported_features(pbap_client->goep_cid) != PBAP_FEATURES_NOT_PRESENT){
269                 application_parameters[0] = PBAP_APPLICATION_PARAMETER_PBAP_SUPPORTED_FEATURES;
270                 application_parameters[1] = 4;
271                 big_endian_store_32(application_parameters, 2, pbap_supported_features);
272                 goep_client_add_header_application_parameters(pbap_client->goep_cid, 6, &application_parameters[0]);
273             }
274             pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
275             goep_client_execute(pbap_client->goep_cid);
276             break;
277         case PBAP_W2_SEND_AUTHENTICATED_CONNECT:
278             goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
279             goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
280             // setup authentication challenge response
281             i = 0;
282             challenge_response[i++] = 0;  // Tag Digest
283             challenge_response[i++] = 16; // Len
284             // calculate md5
285             MD5_Init(&md5_ctx);
286             MD5_Update(&md5_ctx, pbap_client->authentication_nonce, 16);
287             MD5_Update(&md5_ctx, &collon, 1);
288             MD5_Update(&md5_ctx, pbap_client->authentication_password, strlen(pbap_client->authentication_password));
289             MD5_Final(&challenge_response[i], &md5_ctx);
290             i += 16;
291             challenge_response[i++] = 2;  // Tag Nonce
292             challenge_response[i++] = 16; // Len
293             memcpy(&challenge_response[i], pbap_client->authentication_nonce, 16);
294             i += 16;
295             goep_client_add_header_challenge_response(pbap_client->goep_cid, i, challenge_response);
296             pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
297             goep_client_execute(pbap_client->goep_cid);
298             break;
299         case PBAP_W2_SEND_DISCONNECT_REQUEST:
300             goep_client_create_disconnect_request(pbap_client->goep_cid);
301             pbap_client->state = PBAP_W4_DISCONNECT_RESPONSE;
302             goep_client_execute(pbap_client->goep_cid);
303             return;
304         case PBAP_W2_PULL_PHONEBOOK:
305         case PBAP_W2_GET_PHONEBOOK_SIZE:
306             goep_client_create_get_request(pbap_client->goep_cid);
307             if (pbap_client->request_number == 0){
308                 if (!pbap_client->flow_control_enabled){
309                     goep_client_add_header_srm_enable(pbap_client->goep_cid);
310                     pbap_client->srm_state = SRM_W4_CONFIRM;
311                 }
312                 goep_client_add_header_type(pbap_client->goep_cid, pbap_phonebook_type);
313                 goep_client_add_header_name(pbap_client->goep_cid, pbap_client->phonebook_path);
314                 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){
315                     // Regular TLV wih 1-byte len
316                     application_parameters[0] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT;
317                     application_parameters[1] = 2;
318                     big_endian_store_16(application_parameters, 2, 0);
319                     goep_client_add_header_application_parameters(pbap_client->goep_cid, 4, &application_parameters[0]);
320                 } else {
321                     //
322                 }
323             }
324             if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){
325                 // state
326                 pbap_client->state = PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE;
327             } else {
328                 // state
329                 pbap_client->state = PBAP_W4_PHONEBOOK;
330             }
331             // send packet
332             pbap_client->request_number++;
333             goep_client_execute(pbap_client->goep_cid);
334             break;
335         case PBAP_W2_GET_CARD_LIST:
336             goep_client_create_get_request(pbap_client->goep_cid);
337             if (pbap_client->request_number == 0){
338                 if (!pbap_client->flow_control_enabled){
339                     goep_client_add_header_srm_enable(pbap_client->goep_cid);
340                     pbap_client->srm_state = SRM_W4_CONFIRM;
341                 }
342                 goep_client_add_header_type(pbap_client->goep_cid, pbap_vcard_listing_type);
343                 goep_client_add_header_name(pbap_client->goep_cid, pbap_vcard_listing_name);
344                 // Regular TLV wih 1-byte len
345                 i = 0;
346                 phone_number_len = btstack_min(PBAP_MAX_PHONE_NUMBER_LEN, strlen(pbap_client->phone_number));
347                 application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_VALUE;
348                 application_parameters[i++] = phone_number_len;
349                 memcpy(&application_parameters[i], pbap_client->phone_number, phone_number_len);
350                 i += phone_number_len;
351                 application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_PROPERTY;
352                 application_parameters[i++] = 1;
353                 application_parameters[i++] = 0x01; // Number
354                 goep_client_add_header_application_parameters(pbap_client->goep_cid, i, &application_parameters[0]);
355                 pbap_client->state = PBAP_W4_GET_CARD_LIST_COMPLETE;
356             }
357             // send packet
358             goep_client_execute(pbap_client->goep_cid);
359             break;
360         case PBAP_W2_SET_PATH_ROOT:
361             goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory
362             // On Android 4.2 Cyanogenmod, using "" as path fails
363             // goep_client_add_header_name(pbap_client->goep_cid, "");     // empty == /
364             // state
365             pbap_client->state = PBAP_W4_SET_PATH_ROOT_COMPLETE;
366             // send packet
367             goep_client_execute(pbap_client->goep_cid);
368             break;
369         case PBAP_W2_SET_PATH_ELEMENT:
370             // find '/' or '\0'
371             path_element_start = pbap_client->set_path_offset;
372             while (pbap_client->current_folder[pbap_client->set_path_offset] != '\0' &&
373                 pbap_client->current_folder[pbap_client->set_path_offset] != '/'){
374                 pbap_client->set_path_offset++;
375             }
376             // skip /
377             if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){
378                 pbap_client->set_path_offset++;
379             }
380             path_element_len = pbap_client->set_path_offset-path_element_start;
381             memcpy(path_element, &pbap_client->current_folder[path_element_start], path_element_len);
382             path_element[path_element_len] = 0;
383 
384             // detect end of path (after setting path_element)
385             if (pbap_client->current_folder[pbap_client->set_path_offset] == '\0'){
386                 pbap_client->current_folder = NULL;
387             }
388 
389             log_info("Path element '%s', done %u", path_element, pbap_client->current_folder == NULL);
390 
391             goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory
392             goep_client_add_header_name(pbap_client->goep_cid, (const char *) path_element); // next element
393             // state
394             pbap_client->state = PBAP_W4_SET_PATH_ELEMENT_COMPLETE;
395             // send packet
396             goep_client_execute(pbap_client->goep_cid);
397             break;
398         default:
399             break;
400     }
401 }
402 
403 static void pbap_parse_authentication_challenge(pbap_client_t * context, const uint8_t * challenge_data, uint16_t challenge_len){
404     // printf("Challenge:  ");
405     // printf_hexdump(challenge_data, challenge_len);
406     int i;
407     // uint8_t charset_code = 0;
408     for (i=0 ; i<challenge_len ; ){
409         int tag = challenge_data[i];
410         int len = challenge_data[i + 1];
411         i += 2;
412         switch (tag) {
413             case 0:
414                 if (len != 0x10) {
415                     log_error("Invalid OBEX digest len %u", len);
416                     return;
417                 }
418                 memcpy(context->authentication_nonce, &challenge_data[i], 16);
419                 // printf("Nonce: ");
420                 // printf_hexdump(context->authentication_nonce, 16);
421                 break;
422             case 1:
423                 context->authentication_options = challenge_data[i];
424                 // printf("Options %u\n", context->authentication_options);
425                 break;
426             case 2:
427                 // TODO: handle charset
428                 // charset_code = challenge_data[i];
429                 break;
430         }
431         i += len;
432     }
433 }
434 
435 static void pbap_process_srm_headers(pbap_client_t * context, uint8_t *packet, uint16_t size){
436 
437     if (packet[0] != OBEX_RESP_CONTINUE) return;
438 
439     // get SRM and SRMP Headers
440     int srm_value = OBEX_SRM_DISABLE;
441     int srmp_value = OBEX_SRMP_NEXT;
442     obex_iterator_t it;
443     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(context->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
444         uint8_t hi = obex_iterator_get_hi(&it);
445         uint16_t     data_len = obex_iterator_get_data_len(&it);
446         const uint8_t  * data = data =  obex_iterator_get_data(&it);
447         switch (hi){
448             case OBEX_HEADER_SINGLE_RESPONSE_MODE:
449                 if (data_len != 1) break;
450                 srm_value = *data;
451                 break;
452             case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER:
453                 if (data_len != 1) break;
454                 srmp_value = *data;
455                 break;
456             default:
457                 break;
458         }
459     }
460 
461     // Update SRM state based on SRM haders
462     switch (context->srm_state){
463         case SRM_W4_CONFIRM:
464             switch (srm_value){
465                 case OBEX_SRM_ENABLE:
466                     switch (srmp_value){
467                         case OBEX_SRMP_WAIT:
468                             context->srm_state = SRM_ENABLED_BUT_WAITING;
469                             break;
470                         default:
471                             context->srm_state = SRM_ENABLED;
472                             break;
473                     }
474                     break;
475                 default:
476                     context->srm_state = SRM_DISABLED;
477                     break;
478             }
479             break;
480         case SRM_ENABLED_BUT_WAITING:
481             switch (srmp_value){
482                 case OBEX_SRMP_WAIT:
483                     context->srm_state = SRM_ENABLED_BUT_WAITING;
484                     break;
485                 default:
486                     context->srm_state = SRM_ENABLED;
487                     break;
488             }
489             break;
490         default:
491             break;
492     }
493     log_info("SRM state %u", context->srm_state);
494 }
495 
496 static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
497 
498     UNUSED(channel); // ok: there is no channel
499     UNUSED(size);    // ok: handling own geop events
500 
501     obex_iterator_t it;
502     uint8_t status;
503     int wait_for_user = 0;
504     switch (packet_type){
505         case HCI_EVENT_PACKET:
506             switch (hci_event_packet_get_type(packet)) {
507                 case HCI_EVENT_GOEP_META:
508                     switch (hci_event_goep_meta_get_subevent_code(packet)){
509                         case GOEP_SUBEVENT_CONNECTION_OPENED:
510                             status = goep_subevent_connection_opened_get_status(packet);
511                             pbap_client->con_handle = goep_subevent_connection_opened_get_con_handle(packet);
512                             pbap_client->incoming = goep_subevent_connection_opened_get_incoming(packet);
513                             goep_subevent_connection_opened_get_bd_addr(packet, pbap_client->bd_addr);
514                             if (status){
515                                 log_info("pbap: connection failed %u", status);
516                                 pbap_client->state = PBAP_INIT;
517                                 pbap_client_emit_connected_event(pbap_client, status);
518                             } else {
519                                 log_info("pbap: connection established");
520                                 pbap_client->goep_cid = goep_subevent_connection_opened_get_goep_cid(packet);
521                                 pbap_client->state = PBAP_W2_SEND_CONNECT_REQUEST;
522                                 goep_client_request_can_send_now(pbap_client->goep_cid);
523                             }
524                             break;
525                         case GOEP_SUBEVENT_CONNECTION_CLOSED:
526                             if (pbap_client->state != PBAP_CONNECTED){
527                                 pbap_client_emit_operation_complete_event(pbap_client, OBEX_DISCONNECTED);
528                             }
529                             pbap_client->state = PBAP_INIT;
530                             pbap_client_emit_connection_closed_event(pbap_client);
531                             break;
532                         case GOEP_SUBEVENT_CAN_SEND_NOW:
533                             pbap_handle_can_send_now();
534                             break;
535                     }
536                     break;
537                 default:
538                     break;
539             }
540             break;
541         case GOEP_DATA_PACKET:
542             // TODO: handle chunked data
543             obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size);
544             switch (pbap_client->state){
545                 case PBAP_W4_CONNECT_RESPONSE:
546                     switch (packet[0]){
547                         case OBEX_RESP_SUCCESS:
548                             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)){
549                                 uint8_t hi = obex_iterator_get_hi(&it);
550                                 if (hi == OBEX_HEADER_CONNECTION_ID){
551                                     goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it));
552                                 }
553                             }
554                             pbap_client->state = PBAP_CONNECTED;
555                             pbap_client_emit_connected_event(pbap_client, 0);
556                             break;
557                         case OBEX_RESP_UNAUTHORIZED:
558                             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)){
559                                 uint8_t hi = obex_iterator_get_hi(&it);
560                                 if (hi == OBEX_HEADER_AUTHENTICATION_CHALLENGE){
561                                     pbap_parse_authentication_challenge(pbap_client, obex_iterator_get_data(&it), obex_iterator_get_data_len(&it));
562                                 }
563                             }
564                             pbap_client->state = PBAP_W4_USER_AUTHENTICATION;
565                             pbap_client_emit_authentication_event(pbap_client, pbap_client->authentication_options);
566                             break;
567                         default:
568                             log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
569                             pbap_client->state = PBAP_INIT;
570                             pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED);
571                             break;
572                     }
573                     break;
574                 case PBAP_W4_DISCONNECT_RESPONSE:
575                         goep_client_disconnect(pbap_client->goep_cid);
576                         break;
577                 case PBAP_W4_SET_PATH_ROOT_COMPLETE:
578                 case PBAP_W4_SET_PATH_ELEMENT_COMPLETE:
579                     log_info("set path root/path element complete, current folder %s, path offset %u", pbap_client->current_folder, pbap_client->set_path_offset);
580                     if (packet[0] == OBEX_RESP_SUCCESS){
581                         if (pbap_client->current_folder){
582                             pbap_client->state = PBAP_W2_SET_PATH_ELEMENT;
583                             goep_client_request_can_send_now(pbap_client->goep_cid);
584                         } else {
585                             pbap_client->state = PBAP_CONNECTED;
586                             pbap_client_emit_operation_complete_event(pbap_client, 0);
587                         }
588                     } else if (packet[0] == OBEX_RESP_NOT_FOUND){
589                         pbap_client->state = PBAP_CONNECTED;
590                         pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND);
591                     } else {
592                         pbap_client->state = PBAP_CONNECTED;
593                         pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
594                     }
595                     break;
596                 case PBAP_W4_PHONEBOOK:
597                     pbap_client->flow_next_triggered = 0;
598                     wait_for_user = 0;
599                     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)){
600                         uint8_t hi = obex_iterator_get_hi(&it);
601                         uint16_t     data_len = obex_iterator_get_data_len(&it);
602                         const uint8_t  * data = data =  obex_iterator_get_data(&it);
603                         switch (hi){
604                             case OBEX_HEADER_BODY:
605                             case OBEX_HEADER_END_OF_BODY:
606                                 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len);
607                                 wait_for_user++;
608                                 if (wait_for_user > 1){
609                                     log_error("wait_for_user %u", wait_for_user);
610                                 }
611                                 break;
612                             default:
613                                 break;
614                         }
615                     }
616                     switch(packet[0]){
617                         case OBEX_RESP_CONTINUE:
618                             pbap_process_srm_headers(pbap_client, packet, size);
619                             if (pbap_client->srm_state ==  SRM_ENABLED) break;
620                             pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
621                             if (!wait_for_user || pbap_client->flow_next_triggered) {
622                                 goep_client_request_can_send_now(pbap_client->goep_cid);
623                             }
624                             break;
625                         case OBEX_RESP_SUCCESS:
626                             pbap_client->state = PBAP_CONNECTED;
627                             pbap_client_emit_operation_complete_event(pbap_client, 0);
628                             break;
629                         default:
630                             log_info("unexpected response 0x%02x", packet[0]);
631                             pbap_client->state = PBAP_CONNECTED;
632                             pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
633                             break;
634                     }
635                     break;
636                 case PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE:
637                     pbap_client->state = PBAP_CONNECTED;
638                     if (packet[0] == OBEX_RESP_SUCCESS){
639                         int have_size = 0;
640                         uint16_t phonebook_size;
641                         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)){
642                             uint8_t hi = obex_iterator_get_hi(&it);
643                             if (hi == OBEX_HEADER_APPLICATION_PARAMETERS){
644                                 uint16_t     data_len = obex_iterator_get_data_len(&it);
645                                 const uint8_t  * data =  obex_iterator_get_data(&it);
646                                 // iterate over application headers (TLV with 1 bytes len)
647                                 unsigned int i = 0;
648                                 while (i<data_len){
649                                     uint8_t tag = data[i++];
650                                     uint8_t len = data[i++];
651                                     if (tag == PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE && len == 2){
652                                         have_size = 1;
653                                         phonebook_size = big_endian_read_16(data, i);
654                                     }
655                                     i+=len;
656                                 }
657                             }
658                         }
659                         if (have_size){
660                             pbap_client_emit_phonebook_size_event(pbap_client, 0, phonebook_size);
661                             break;
662                         }
663                     }
664                     pbap_client_emit_phonebook_size_event(pbap_client, OBEX_UNKNOWN_ERROR, 0);
665                     break;
666                 case PBAP_W4_GET_CARD_LIST_COMPLETE:
667                     switch (packet[0]){
668                         case OBEX_RESP_CONTINUE:
669                             pbap_client->state = PBAP_W2_GET_CARD_LIST;
670                             goep_client_request_can_send_now(pbap_client->goep_cid);
671                             break;
672                         case OBEX_RESP_SUCCESS:
673                             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)){
674                                 uint8_t hi = obex_iterator_get_hi(&it);
675                                 if (hi == OBEX_HEADER_END_OF_BODY){
676                                     uint16_t     data_len = obex_iterator_get_data_len(&it);
677                                     const uint8_t  * data =  obex_iterator_get_data(&it);
678                                     // now try parsing it
679                                     yxml_init(&pbap_client->xml_parser, pbap_client->xml_buffer, sizeof(pbap_client->xml_buffer));
680                                     int card_found = 0;
681                                     int name_found = 0;
682                                     int handle_found = 0;
683                                     char name[PBAP_MAX_NAME_LEN];
684                                     char handle[PBAP_MAX_HANDLE_LEN];
685                                     name[0] = 0;
686                                     handle[0] = 0;
687                                     while (data_len--){
688                                         yxml_ret_t r = yxml_parse(&pbap_client->xml_parser, *data++);
689                                         switch (r){
690                                             case YXML_ELEMSTART:
691                                                 card_found = strcmp("card", pbap_client->xml_parser.elem) == 0;
692                                                 break;
693                                             case YXML_ELEMEND:
694                                                 if (card_found){
695                                                     pbap_client_emit_card_result_event(pbap_client, name, handle);
696                                                 }
697                                                 card_found = 0;
698                                                 break;
699                                             case YXML_ATTRSTART:
700                                                 if (!card_found) break;
701                                                 if (strcmp("name", pbap_client->xml_parser.attr) == 0){
702                                                     name_found = 1;
703                                                     break;
704                                                 }
705                                                 if (strcmp("handle", pbap_client->xml_parser.attr) == 0){
706                                                     handle_found = 1;
707                                                     break;
708                                                 }
709                                                 break;
710                                             case YXML_ATTRVAL:
711                                                 if (name_found) {
712                                                     // "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."
713                                                     if (strlen(name) + 4 + 1 >= sizeof(name)) break;
714                                                     strcat(name, pbap_client->xml_parser.data);
715                                                     break;
716                                                 }
717                                                 if (handle_found) {
718                                                     // "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."
719                                                     if (strlen(handle) + 4 + 1 >= sizeof(handle)) break;
720                                                     strcat(handle, pbap_client->xml_parser.data);
721                                                     break;
722                                                 }
723                                                 break;
724                                             case YXML_ATTREND:
725                                                 name_found = 0;
726                                                 handle_found = 0;
727                                                 break;
728                                             default:
729                                                 break;
730                                         }
731                                     }
732                                     //
733                                     pbap_client->state = PBAP_CONNECTED;
734                                     pbap_client_emit_operation_complete_event(pbap_client, 0);
735                                 }
736                             }
737                             break;
738                         case OBEX_RESP_NOT_ACCEPTABLE:
739                             pbap_client->state = PBAP_CONNECTED;
740                             pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_ACCEPTABLE);
741                             break;
742                         default:
743                             log_info("unexpected response 0x%02x", packet[0]);
744                             pbap_client->state = PBAP_CONNECTED;
745                             pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
746                             break;
747                     }
748                     break;
749                 default:
750                     break;
751             }
752             break;
753         default:
754             break;
755     }
756 }
757 
758 void pbap_client_init(void){
759     memset(pbap_client, 0, sizeof(pbap_client_t));
760     pbap_client->state = PBAP_INIT;
761     pbap_client->cid = 1;
762 }
763 
764 uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){
765     if (pbap_client->state != PBAP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED;
766     pbap_client->state = PBAP_W4_GOEP_CONNECTION;
767     pbap_client->client_handler = handler;
768     uint8_t err = goep_client_create_connection(&pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, &pbap_client->goep_cid);
769     *out_cid = pbap_client->cid;
770     if (err) return err;
771     return 0;
772 }
773 
774 uint8_t pbap_disconnect(uint16_t pbap_cid){
775     UNUSED(pbap_cid);
776     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
777     pbap_client->state = PBAP_W2_SEND_DISCONNECT_REQUEST;
778     goep_client_request_can_send_now(pbap_client->goep_cid);
779     return 0;
780 }
781 
782 uint8_t pbap_get_phonebook_size(uint16_t pbap_cid, const char * path){
783     UNUSED(pbap_cid);
784     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
785     pbap_client->state = PBAP_W2_GET_PHONEBOOK_SIZE;
786     pbap_client->phonebook_path = path;
787     pbap_client->request_number = 0;
788     goep_client_request_can_send_now(pbap_client->goep_cid);
789     return 0;
790 }
791 
792 uint8_t pbap_pull_phonebook(uint16_t pbap_cid, const char * path){
793     UNUSED(pbap_cid);
794     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
795     pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
796     pbap_client->phonebook_path = path;
797     pbap_client->request_number = 0;
798     goep_client_request_can_send_now(pbap_client->goep_cid);
799     return 0;
800 }
801 
802 uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
803     UNUSED(pbap_cid);
804     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
805     pbap_client->state = PBAP_W2_SET_PATH_ROOT;
806     pbap_client->current_folder = path;
807     pbap_client->set_path_offset = 0;
808     goep_client_request_can_send_now(pbap_client->goep_cid);
809     return 0;
810 }
811 
812 uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password){
813     UNUSED(pbap_cid);
814     if (pbap_client->state != PBAP_W4_USER_AUTHENTICATION) return BTSTACK_BUSY;
815     pbap_client->state = PBAP_W2_SEND_AUTHENTICATED_CONNECT;
816     pbap_client->authentication_password = password;
817     goep_client_request_can_send_now(pbap_client->goep_cid);
818     return 0;
819 }
820 
821 uint8_t pbap_lookup_by_number(uint16_t pbap_cid, const char * phone_number){
822     UNUSED(pbap_cid);
823     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
824     pbap_client->state = PBAP_W2_GET_CARD_LIST;
825     pbap_client->phone_number = phone_number;
826     goep_client_request_can_send_now(pbap_client->goep_cid);
827     return 0;
828 }
829 
830 uint8_t pbap_abort(uint16_t pbap_cid){
831     UNUSED(pbap_cid);
832     log_info("abort current operation, state 0x%02x", pbap_client->state);
833     pbap_client->abort_operation = 1;
834     goep_client_request_can_send_now(pbap_client->goep_cid);
835     return 0;
836 }
837 
838 uint8_t pbap_next_packet(uint16_t pbap_cid){
839     // log_info("pbap_next_packet, state %x", pbap_client->state);
840     UNUSED(pbap_cid);
841     if (!pbap_client->flow_control_enabled) return 0;
842     switch (pbap_client->state){
843         case PBAP_W2_PULL_PHONEBOOK:
844             goep_client_request_can_send_now(pbap_client->goep_cid);
845             break;
846         case PBAP_W4_PHONEBOOK:
847             pbap_client->flow_next_triggered = 1;
848             break;
849         default:
850             break;
851     }
852     return 0;
853 }
854 
855 uint8_t pbap_set_flow_control_mode(uint16_t pbap_cid, int enable){
856     UNUSED(pbap_cid);
857     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
858     pbap_client->flow_control_enabled = enable;
859     return 0;
860 }
861