xref: /btstack/src/classic/obex_message_builder.c (revision ced70f9bfeafe291ec597a3a9cc862e39e0da3ce)
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 static uint8_t obex_message_builder_packet_init(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode_or_response_code){
51     if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
52     buffer[0] = opcode_or_response_code;
53     big_endian_store_16(buffer, 1, 3);
54     return ERROR_CODE_SUCCESS;
55 }
56 
57 static uint8_t obex_message_builder_packet_append(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t len){
58     uint16_t pos = big_endian_read_16(buffer, 1);
59     if (buffer_len < pos + len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
60     (void)memcpy(&buffer[pos], data, len);
61     pos += len;
62     big_endian_store_16(buffer, 1, pos);
63      return ERROR_CODE_SUCCESS;
64 }
65 
66 uint16_t obex_message_builder_get_message_length(uint8_t * buffer){
67     return big_endian_read_16(buffer, 1);
68 }
69 
70 uint8_t obex_message_builder_header_add_byte(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint8_t value){
71     uint8_t header[2];
72     header[0] = header_type;
73     header[1] = value;
74     return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
75 }
76 
77 uint8_t obex_message_builder_header_add_word(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint32_t value){
78     uint8_t header[5];
79     header[0] = header_type;
80     big_endian_store_32(header, 1, value);
81     return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
82 }
83 
84 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){
85     uint8_t header[3];
86     header[0] = header_type;
87     big_endian_store_16(header, 1, sizeof(header) + header_data_length);
88 
89     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
90     if (status != ERROR_CODE_SUCCESS) return status;
91 
92     return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length);
93 }
94 
95 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){
96     uint8_t header[3];
97     header[0] = header_type;
98     uint16_t pos = big_endian_read_16(buffer, 1);
99 
100     if (sizeof (header) + header_data_length + pos > buffer_len)
101         header_data_length = buffer_len - pos - sizeof (header);
102 
103     big_endian_store_16(header, 1, sizeof(header) + header_data_length);
104     *ret_length = 0;
105     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
106     if (status != ERROR_CODE_SUCCESS) return status;
107 
108     *ret_length = header_data_length;
109     return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length);
110 }
111 
112 static uint8_t obex_message_builder_header_add_connection_id(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
113     // add connection_id header if set, must be first header if used
114     if (obex_connection_id == OBEX_CONNECTION_ID_INVALID) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
115     return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_CONNECTION_ID, obex_connection_id);
116 }
117 
118 static inline uint8_t obex_message_builder_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode,
119     uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
120     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, opcode);
121     if (status != ERROR_CODE_SUCCESS) return status;
122 
123     uint8_t fields[4];
124     fields[0] = obex_version_number;
125     fields[1] = flags;
126     big_endian_store_16(fields, 2, maximum_obex_packet_length);
127     return obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields));
128 }
129 
130 uint8_t obex_message_builder_request_create_connect(uint8_t * buffer, uint16_t buffer_len,
131     uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
132 
133     return obex_message_builder_create_connect(buffer, buffer_len, OBEX_OPCODE_CONNECT, obex_version_number, flags, maximum_obex_packet_length);
134 }
135 
136 uint8_t obex_message_builder_response_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t obex_version_number,
137     uint8_t flags, uint16_t maximum_obex_packet_length, uint32_t obex_connection_id){
138 
139     uint8_t status = obex_message_builder_create_connect(buffer, buffer_len, OBEX_RESP_SUCCESS, obex_version_number, flags, maximum_obex_packet_length);
140     if (status != ERROR_CODE_SUCCESS) return status;
141     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
142 }
143 
144 uint8_t obex_message_builder_response_create_general(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){
145     return obex_message_builder_packet_init(buffer, buffer_len, response_code);
146 }
147 
148 uint8_t obex_message_builder_response_update_code(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){
149     if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
150     buffer[0] = response_code;
151     return ERROR_CODE_SUCCESS;
152 }
153 
154 uint8_t obex_message_builder_request_create_get(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
155     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
156     if (status != ERROR_CODE_SUCCESS) return status;
157     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
158 }
159 
160 uint8_t obex_message_builder_request_create_put(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
161     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK);
162     if (status != ERROR_CODE_SUCCESS) return status;
163 
164     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
165 }
166 
167 uint8_t obex_message_builder_request_create_set_path(uint8_t * buffer, uint16_t buffer_len, uint8_t flags, uint32_t obex_connection_id){
168     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_SETPATH);
169     if (status != ERROR_CODE_SUCCESS) return status;
170 
171     uint8_t fields[2];
172     fields[0] = flags;
173     fields[1] = 0;  // reserved
174     status = obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields));
175     if (status != ERROR_CODE_SUCCESS) return status;
176     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
177 }
178 
179 uint8_t obex_message_builder_request_create_abort(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
180     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_ABORT);
181     if (status != ERROR_CODE_SUCCESS) return status;
182     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
183 }
184 
185 uint8_t obex_message_builder_request_create_disconnect(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){
186     uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_DISCONNECT);
187     if (status != ERROR_CODE_SUCCESS) return status;
188     return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id);
189 }
190 
191 uint8_t obex_message_builder_set_final_bit (uint8_t * buffer, uint16_t buffer_len, bool final){
192     if (buffer_len < 1) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
193     if (buffer[0] == OBEX_OPCODE_CONNECT ||
194         buffer[0] == OBEX_OPCODE_DISCONNECT ||
195         buffer[0] == OBEX_OPCODE_SETPATH ||
196         buffer[0] == OBEX_OPCODE_SESSION ||
197         buffer[0] == OBEX_OPCODE_ABORT){
198         return ERROR_CODE_COMMAND_DISALLOWED;
199     }
200     buffer[0] &= ~OBEX_OPCODE_FINAL_BIT_MASK;
201     buffer[0] |= (final ? OBEX_OPCODE_FINAL_BIT_MASK : 0);
202     return ERROR_CODE_SUCCESS;
203 }
204 
205 uint8_t obex_message_builder_header_add_srm_enable(uint8_t * buffer, uint16_t buffer_len){
206     return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE, OBEX_SRM_ENABLE);
207 }
208 
209 uint8_t obex_message_builder_header_add_target(uint8_t * buffer, uint16_t buffer_len, const uint8_t * target, uint16_t length){
210     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_TARGET, target, length);
211 }
212 
213 uint8_t obex_message_builder_header_add_application_parameters(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){
214     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_APPLICATION_PARAMETERS, data, length);
215 }
216 
217 uint8_t obex_message_builder_header_add_challenge_response(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){
218     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_AUTHENTICATION_RESPONSE, data, length);
219 }
220 
221 uint8_t obex_message_builder_header_add_who(uint8_t * buffer, uint16_t buffer_len, const uint8_t * who){
222     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_WHO, who, 16);
223 }
224 
225 uint8_t obex_message_builder_body_add_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length){
226     return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length);
227 }
228 
229 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){
230     return obex_message_builder_header_fillup_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length, ret_length);
231 }
232 
233 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){
234     // non-empty string have trailing \0
235     bool add_trailing_zero = name_len > 0;
236 
237     uint16_t header_len = 1 + 2 + (name_len * 2) + (add_trailing_zero ? 2 : 0);
238     if (buffer_len < header_len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
239 
240     uint16_t pos = big_endian_read_16(buffer, 1);
241     buffer[pos++] = header_id;
242     big_endian_store_16(buffer, pos, header_len);
243     pos += 2;
244     int i;
245     // @note name[len] == 0
246     for (i = 0 ; i < name_len ; i++){
247         buffer[pos++] = 0;
248         buffer[pos++] = *name++;
249     }
250     if (add_trailing_zero){
251         buffer[pos++] = 0;
252         buffer[pos++] = 0;
253     }
254     big_endian_store_16(buffer, 1, pos);
255     return ERROR_CODE_SUCCESS;
256 }
257 
258 uint8_t obex_message_builder_header_add_name_prefix(uint8_t * buffer, uint16_t buffer_len, const char * name, uint16_t name_len){
259     return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len);
260 }
261 
262 uint8_t obex_message_builder_header_add_name(uint8_t * buffer, uint16_t buffer_len, const char * name){
263     uint16_t name_len = (uint16_t) strlen(name);
264     return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len);
265 }
266 
267 uint8_t obex_message_builder_header_add_type(uint8_t * buffer, uint16_t buffer_len, const char * type){
268     uint8_t header[3];
269     header[0] = OBEX_HEADER_TYPE;
270     int len_incl_zero = (uint16_t) strlen(type) + 1;
271     big_endian_store_16(header, 1, 1 + 2 + len_incl_zero);
272 
273     uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header));
274     if (status != ERROR_CODE_SUCCESS) return status;
275     return obex_message_builder_packet_append(buffer, buffer_len, (const uint8_t*)type, len_incl_zero);
276 }
277 
278 uint8_t obex_message_builder_header_add_length(uint8_t * buffer, uint16_t buffer_len, uint32_t length){
279     return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_LENGTH, length);
280 }
281