xref: /btstack/src/classic/obex_message_builder.c (revision 2eecdfbb4cc78ba9b8248423c4cb4064c8461896)
1 /*
2  * Copyright (C) 2019 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 BLUEKITCHEN
24  * GMBH 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__ "obex_message_builder.c"
39 
40 #include "btstack_config.h"
41 
42 #include <stdint.h>
43 #include <stdlib.h>
44 
45 #include "btstack_util.h"
46 #include "btstack_debug.h"
47 #include "classic/obex.h"
48 #include "classic/obex_message_builder.h"
49 
50 #if defined(ENABLE_LOG_DEBUG) || defined(ENABLE_LOG_ERROR)
51 #define LUT(which) [which] = #which
52 const char* lut_type[0xFF] = {
53 LUT(OBEX_HEADER_NAME),
54 LUT(OBEX_HEADER_DESCRIPTION),
55 LUT(OBEX_HEADER_IMG_HANDLE),
56 LUT(OBEX_HEADER_TYPE),
57 LUT(OBEX_HEADER_TIME_ISO_8601),
58 LUT(OBEX_HEADER_TARGET),
59 LUT(OBEX_HEADER_HTTP),
60 LUT(OBEX_HEADER_BODY),
61 LUT(OBEX_HEADER_END_OF_BODY),
62 LUT(OBEX_HEADER_WHO),
63 LUT(OBEX_HEADER_APPLICATION_PARAMETERS),
64 LUT(OBEX_HEADER_AUTHENTICATION_CHALLENGE),
65 LUT(OBEX_HEADER_AUTHENTICATION_RESPONSE),
66 LUT(OBEX_HEADER_OBJECT_CLASS),
67 LUT(OBEX_HEADER_IMG_DESCRIPTOR),
68 LUT(OBEX_HEADER_SINGLE_RESPONSE_MODE),
69 LUT(OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER),
70 LUT(OBEX_HEADER_COUNT),
71 LUT(OBEX_HEADER_LENGTH),
72 LUT(OBEX_HEADER_TIME_4_BYTE),
73 LUT(OBEX_HEADER_CONNECTION_ID),
74 };
75 #endif
76 
77 static uint8_t obex_message_builder_packet_init(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode_or_response_code){
78     if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
79     buffer[0] = opcode_or_response_code;
80     big_endian_store_16(buffer, 1, 3);
81     return ERROR_CODE_SUCCESS;
82 }
83 
84 static uint8_t obex_message_builder_packet_append(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t len){
85     uint16_t pos = big_endian_read_16(buffer, 1);
86 
87     log_debug("add type:0x%02x(%s) buffer_len:%u pos+len:%u pos:%u len:%u", data[0], lut_type[data[0]], buffer_len, pos + len, pos, len);
88 
89     if (buffer_len < pos + len) {
90         log_error("ERROR_CODE_MEMORY_CAPACITY_EXCEEDED type:0x%02x(%s) buffer_len:%u size:%u pos:%u len:%u", buffer[0], lut_type[buffer[0]], buffer_len, pos + len, pos, len);
91         return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
92     }
93 
94     (void)memcpy(&buffer[pos], data, len);
95     pos += len;
96     big_endian_store_16(buffer, 1, pos);
97      return ERROR_CODE_SUCCESS;
98 }
99 
100 uint16_t obex_message_builder_get_message_length(uint8_t * buffer){
101     return big_endian_read_16(buffer, 1);
102 }
103 
104 uint8_t obex_message_builder_header_add_byte(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint8_t value){
105     uint8_t header[2];
106     header[0] = header_type;
107     header[1] = value;
108     return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
109 }
110 
111 uint8_t obex_message_builder_header_add_word(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint32_t value){
112     uint8_t header[5];
113     header[0] = header_type;
114     big_endian_store_32(header, 1, value);
115     return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
116 }
117 
118 uint8_t obex_message_builder_header_add_variable(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length){
119     uint8_t header[3];
120     header[0] = header_type;
121     big_endian_store_16(header, 1, sizeof(header) + header_data_length);
122 
123     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
124     if (status != ERROR_CODE_SUCCESS) return status;
125 
126     return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length);
127 }
128 
129 uint8_t obex_message_builder_header_fillup_variable(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length, uint32_t * ret_length){
130     uint8_t header[3];
131     header[0] = header_type;
132     uint16_t pos = big_endian_read_16(buffer, 1);
133 
134     if (sizeof (header) + header_data_length + pos > buffer_len)
135         header_data_length = buffer_len - pos - sizeof (header);
136 
137     big_endian_store_16(header, 1, sizeof(header) + header_data_length);
138     *ret_length = 0;
139     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
140     if (status != ERROR_CODE_SUCCESS) return status;
141 
142     *ret_length = header_data_length;
143     return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length);
144 }
145 
146 static uint8_t obex_message_builder_header_add_connection_id(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
147     // add connection_id header if set, must be first header if used
148     if (obex_connection_id == OBEX_CONNECTION_ID_INVALID) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
149     return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_CONNECTION_ID, obex_connection_id);
150 }
151 
152 static inline uint8_t obex_message_builder_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode,
153     uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
154     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, opcode);
155     if (status != ERROR_CODE_SUCCESS) return status;
156 
157     uint8_t fields[4];
158     fields[0] = obex_version_number;
159     fields[1] = flags;
160     big_endian_store_16(fields, 2, maximum_obex_packet_length);
161     return obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields));
162 }
163 
164 uint8_t obex_message_builder_request_create_connect(uint8_t * buffer, uint16_t buffer_len,
165     uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
166 
167     return obex_message_builder_create_connect(buffer, buffer_len, OBEX_OPCODE_CONNECT, obex_version_number, flags, maximum_obex_packet_length);
168 }
169 
170 uint8_t obex_message_builder_response_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t obex_version_number,
171     uint8_t flags, uint16_t maximum_obex_packet_length, uint32_t obex_connection_id){
172 
173     uint8_t status = obex_message_builder_create_connect(buffer, buffer_len, OBEX_RESP_SUCCESS, obex_version_number, flags, maximum_obex_packet_length);
174     if (status != ERROR_CODE_SUCCESS) return status;
175     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
176 }
177 
178 uint8_t obex_message_builder_response_create_general(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){
179     return obex_message_builder_packet_init(buffer, buffer_len, response_code);
180 }
181 
182 uint8_t obex_message_builder_response_update_code(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){
183     if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
184     buffer[0] = response_code;
185     return ERROR_CODE_SUCCESS;
186 }
187 
188 uint8_t obex_message_builder_request_create_get(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
189     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
190     if (status != ERROR_CODE_SUCCESS) return status;
191     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
192 }
193 
194 uint8_t obex_message_builder_request_create_put(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
195     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK);
196     if (status != ERROR_CODE_SUCCESS) return status;
197 
198     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
199 }
200 
201 uint8_t obex_message_builder_request_create_set_path(uint8_t * buffer, uint16_t buffer_len, uint8_t flags, uint32_t obex_connection_id){
202     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_SETPATH);
203     if (status != ERROR_CODE_SUCCESS) return status;
204 
205     uint8_t fields[2];
206     fields[0] = flags;
207     fields[1] = 0;  // reserved
208     status = obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields));
209     if (status != ERROR_CODE_SUCCESS) return status;
210     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
211 }
212 
213 uint8_t obex_message_builder_request_create_abort(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
214     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_ABORT);
215     if (status != ERROR_CODE_SUCCESS) return status;
216     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
217 }
218 
219 uint8_t obex_message_builder_request_create_disconnect(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
220     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_DISCONNECT);
221     if (status != ERROR_CODE_SUCCESS) return status;
222     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
223 }
224 
225 uint8_t obex_message_builder_set_final_bit (uint8_t * buffer, uint16_t buffer_len, bool final){
226     if (buffer_len < 1) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
227     if (buffer[0] == OBEX_OPCODE_CONNECT ||
228         buffer[0] == OBEX_OPCODE_DISCONNECT ||
229         buffer[0] == OBEX_OPCODE_SETPATH ||
230         buffer[0] == OBEX_OPCODE_SESSION ||
231         buffer[0] == OBEX_OPCODE_ABORT){
232         return ERROR_CODE_COMMAND_DISALLOWED;
233     }
234     buffer[0] &= ~OBEX_OPCODE_FINAL_BIT_MASK;
235     buffer[0] |= (final ? OBEX_OPCODE_FINAL_BIT_MASK : 0);
236     return ERROR_CODE_SUCCESS;
237 }
238 
239 uint8_t obex_message_builder_header_add_srm_enable(uint8_t * buffer, uint16_t buffer_len){
240     log_debug("SRM header enabled");
241     return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE, OBEX_SRM_ENABLE);
242 }
243 
244 uint8_t obex_message_builder_header_add_srmp_wait(uint8_t* buffer, uint16_t buffer_len) {
245     return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER, OBEX_SRMP_WAIT);
246 }
247 
248 uint8_t obex_message_builder_header_add_target(uint8_t * buffer, uint16_t buffer_len, const uint8_t * target, uint16_t length){
249     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_TARGET, target, length);
250 }
251 
252 uint8_t obex_message_builder_header_add_application_parameters(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){
253     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_APPLICATION_PARAMETERS, data, length);
254 }
255 
256 uint8_t obex_message_builder_header_add_challenge_response(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){
257     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_AUTHENTICATION_RESPONSE, data, length);
258 }
259 
260 uint8_t obex_message_builder_header_add_who(uint8_t * buffer, uint16_t buffer_len, const uint8_t * who){
261     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_WHO, who, 16);
262 }
263 
264 uint8_t obex_message_builder_body_add_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length){
265     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length);
266 }
267 
268 uint8_t obex_message_builder_body_fillup_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length, uint32_t *ret_length){
269     return obex_message_builder_header_fillup_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length, ret_length);
270 }
271 
272 uint8_t obex_message_builder_get_header_name_len_from_strlen(uint16_t name_len) {
273 
274     // non-empty string have trailing \0
275     bool add_trailing_zero = name_len > 0;
276     return 1 + 2 + (name_len * 2) + (add_trailing_zero ? 2 : 0);
277 }
278 
279 uint8_t obex_message_builder_header_add_unicode_prefix(uint8_t * buffer, uint16_t buffer_len, uint8_t header_id, const char * name, uint16_t name_len){
280     // non-empty string have trailing \0
281     bool add_trailing_zero = name_len > 0;
282 
283     uint16_t header_len = obex_message_builder_get_header_name_len_from_strlen(name_len);
284     if (buffer_len < header_len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
285 
286     uint16_t pos = big_endian_read_16(buffer, 1);
287     buffer[pos++] = header_id;
288     big_endian_store_16(buffer, pos, header_len);
289     pos += 2;
290     int i;
291     // @note name[len] == 0
292     for (i = 0 ; i < name_len ; i++){
293         buffer[pos++] = 0;
294         buffer[pos++] = *name++;
295     }
296     if (add_trailing_zero){
297         buffer[pos++] = 0;
298         buffer[pos++] = 0;
299     }
300     big_endian_store_16(buffer, 1, pos);
301     return ERROR_CODE_SUCCESS;
302 }
303 
304 uint8_t obex_message_builder_header_add_name_prefix(uint8_t * buffer, uint16_t buffer_len, const char * name, uint16_t name_len){
305     return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len);
306 }
307 
308 uint8_t obex_message_builder_header_add_name(uint8_t * buffer, uint16_t buffer_len, const char * name){
309     uint16_t name_len = (uint16_t) strlen(name);
310     return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len);
311 }
312 
313 uint8_t obex_message_builder_get_header_type_len(char * type) {
314     if (type == NULL)
315         return 0;                // type_header is ommited
316 
317     return (uint8_t) ( 1            // Header Encoding + ID
318                      + 2            // Length
319                      + strlen(type) // length of string in bytes
320                      + 1);           // trailing \0
321 }
322 
323 uint8_t obex_message_builder_header_add_type(uint8_t * buffer, uint16_t buffer_len, const char * type){
324     uint8_t header[3];
325     header[0] = OBEX_HEADER_TYPE;
326     int len_incl_zero = (uint16_t) strlen(type) + 1;
327     big_endian_store_16(header, 1, 1 + 2 + len_incl_zero);
328 
329     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
330     if (status != ERROR_CODE_SUCCESS) return status;
331     return obex_message_builder_packet_append(buffer, buffer_len, (const uint8_t*)type, len_incl_zero);
332 }
333 
334 uint8_t obex_message_builder_header_add_length(uint8_t * buffer, uint16_t buffer_len, uint32_t length){
335     return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_LENGTH, length);
336 }
337