multipart.c
Go to the documentation of this file.
1 
9 #include "multipart.h"
11 #include "mcl_core/mcl_memory.h"
12 #include "mcl_core/mcl_random.h"
14 #include "json.h"
15 
16 static const char boundary_sign[] = "--";
17 static const char content_type[] = "Content-Type: ";
18 static const char boundary_key[] = ";boundary=";
19 static const char endline[] = "\r\n\r\n";
20 static const char boundary_characters[] = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
21 static const char content_type_multipart_related[] = "multipart/related";
22 static const char content_type_meta_json[] = "application/vnd.siemens.mindsphere.meta+json";
23 static const char content_type_application_json[] = "application/json";
24 
25 #define BOUNDARY_SIGN_LENGTH (sizeof(boundary_sign) - 1)
26 #define CONTENT_TYPE_LENGTH (sizeof(content_type) - 1)
27 #define BOUNDARY_KEY_LENGTH (sizeof(boundary_key) - 1)
28 #define DOUBLE_ENDLINE_LENGTH (sizeof(endline) - 1)
29 #define ENDLINE_LENGTH (DOUBLE_ENDLINE_LENGTH / 2)
30 #define BOUNDARY_LENGTH 20
31 #define CONTENT_TYPE_MULTIPART_RELATED_LENGTH (sizeof(content_type_multipart_related) - 1)
32 #define CONTENT_TYPE_META_JSON_LENGTH (sizeof(content_type_meta_json) - 1)
33 #define CONTENT_TYPE_APPLICATION_JSON_LENGTH (sizeof(content_type_application_json) - 1)
34 
35 #define MULTIPART_BASE_SIZE (5 * BOUNDARY_SIGN_LENGTH + 5 * BOUNDARY_LENGTH + 3 * CONTENT_TYPE_LENGTH + CONTENT_TYPE_MULTIPART_RELATED_LENGTH \
36  + BOUNDARY_KEY_LENGTH + CONTENT_TYPE_META_JSON_LENGTH + 12 * ENDLINE_LENGTH)
37 
38 #define MULTIPART_CLOSE_LENGTH (BOUNDARY_LENGTH + 2 * BOUNDARY_SIGN_LENGTH)
39 
43 typedef enum
44 {
66 
68 {
69  // MULTIPART_STATE_OPEN_MAIN_BOUNDARY
71  // MULTIPART_STATE_CONTENT_TYPE_KEY_FOR_MULTIPART_RELATED
73  // MULTIPART_STATE_CONTENT_TYPE_VALUE_MULTIPART_RELATED
75  // MULTIPART_STATE_BOUNDARY_KEY
77  // MULTIPART_STATE_BOUNDARY_VALUE_SUB_BOUNDARY
79  // MULTIPART_STATE_DOUBLE_ENDLINE_FIRST
81  // MULTIPART_STATE_OPEN_SUB_BOUNDARY_FIRST
82  BOUNDARY_SIGN_LENGTH + BOUNDARY_LENGTH + ENDLINE_LENGTH,
83  // MULTIPART_STATE_CONTENT_TYPE_KEY_FOR_META_JSON
85  // MULTIPART_STATE_CONTENT_TYPE_VALUE_META_JSON
87  // MULTIPART_STATE_DOUBLE_ENDLINE_SECOND
89  // MULTIPART_STATE_ITEM_META_JSON (variable, give as 0)
90  0,
91  // MULTIPART_STATE_ENDLINE_FIRST
93  // MULTIPART_STATE_OPEN_SUB_BOUNDARY_SECOND
94  BOUNDARY_SIGN_LENGTH + BOUNDARY_LENGTH + ENDLINE_LENGTH,
95  // MULTIPART_STATE_CONTENT_TYPE_KEY_FOR_PAYLOAD
97  // MULTIPART_STATE_CONTENT_TYPE_VALUE_PAYLOAD (variable, give as 0)
98  0,
99  // MULTIPART_STATE_DOUBLE_ENDLINE_THIRD
101  // MULTIPART_STATE_ITEM_PAYLOAD (variable, give as 0)
102  0,
103  // MULTIPART_STATE_ENDLINE_SECOND
105  // MULTIPART_STATE_CLOSE_SUB_BOUNDARY
106  (2 * BOUNDARY_SIGN_LENGTH) + BOUNDARY_LENGTH + ENDLINE_LENGTH
107 };
108 
109 static void _add_boundary(char *buffer, const char *boundary, mcl_bool_t add_and_close);
110 
111 static mcl_error_t _add_item_meta_json(char **position, mcl_size_t *remaining_size, void *item);
112 
113 static mcl_error_t _add_item_payload_content_type(char **position, mcl_size_t *remaining_size, const char *payload_content_type);
114 
115 static mcl_error_t _add_item_payload(char **position, mcl_size_t *remaining_size, void *item, multipart_add_payload_callback callback, void *user_context);
116 
117 mcl_error_t multipart_add_tuple(char *buffer, mcl_size_t *buffer_size, void *item, const char *boundary)
118 {
119  mcl_error_t code;
120 
121  MCL_DEBUG_ENTRY("char *buffer = <%p>, mcl_size_t *buffer_size, void *item = <%p>, const char *boundary = <%p>", buffer, buffer_size, item, boundary);
122 
123  // Add tuple.
124  code = multipart_add_tuple_with_callback(buffer, buffer_size, item, boundary, MCL_NULL, MCL_NULL, MCL_NULL);
125 
126  MCL_DEBUG_LEAVE("retVal = <%d>", code);
127  return code;
128 }
129 
130 mcl_error_t multipart_add_tuple_with_callback(char *buffer, mcl_size_t *buffer_size, void *item, const char *boundary, const char *payload_content_type,
131  multipart_add_payload_callback callback, void *user_context)
132 {
133  mcl_error_t code;
134  char *sub_boundary = MCL_NULL;
135  char *position = buffer;
136  mcl_size_t remaining_size = *buffer_size;
138 
139  MCL_DEBUG_ENTRY("char *buffer = <%p>, void *item = <%p>, const char *boundary = <%p>, const char *payload_content_type = <%p>, "\
140  "multipart_add_payload_callback callback = <%p>, void *user_context = <%p>", buffer, item, boundary, payload_content_type, callback, user_context);
141 
142  code = multipart_generate_boundary(&sub_boundary);
143 
144  while (MULTIPART_STATE_END != multipart_state)
145  {
146  if ((0 == remaining_size) || (_space_needed_for_state[multipart_state] > remaining_size))
147  {
148  code = MCL_FAIL;
149  break;
150  }
151 
152  switch (multipart_state)
153  {
155  _add_boundary(position, boundary, MCL_FALSE);
156  break;
157 
162  break;
163 
166  break;
167 
170  break;
171 
173  mcl_string_util_memcpy(position, sub_boundary, BOUNDARY_LENGTH);
174  break;
175 
180  break;
181 
184  _add_boundary(position, sub_boundary, MCL_FALSE);
185  break;
186 
188  _add_boundary(position, sub_boundary, MCL_TRUE);
189  break;
190 
193  break;
194 
196  code = _add_item_meta_json(&position, &remaining_size, item);
197  break;
198 
202  break;
203 
205  code = _add_item_payload_content_type(&position, &remaining_size, payload_content_type);
206  break;
207 
209  code = _add_item_payload(&position, &remaining_size, item, callback, user_context);
210  break;
211 
212  default:
213  break;
214  }
215 
216  if (MCL_OK != code)
217  {
218  break;
219  }
220 
221  remaining_size -= _space_needed_for_state[multipart_state];
222  position += _space_needed_for_state[multipart_state];
223  ++multipart_state;
224  }
225 
226  MCL_FREE(sub_boundary);
227 
228  if (MCL_OK == code)
229  {
230  *buffer_size = remaining_size;
231  }
232 
233  MCL_DEBUG_LEAVE("retVal = <%d>", code);
234  return code;
235 }
236 
237 mcl_size_t multipart_get_tuple_size(void *item, const char *payload_content_type)
238 {
240 
241  MCL_DEBUG_ENTRY("void *item = <%p>, const char *payload_content_type = <%p>", item, payload_content_type);
242 
243  size += json_get_item_size(item);
244  size += mcl_string_util_strlen(payload_content_type);
245 
246  MCL_DEBUG_LEAVE("retVal = <%lu>", size);
247  return size;
248 }
249 
250 mcl_error_t multipart_close(char *buffer, mcl_size_t *buffer_size, const char *boundary)
251 {
252  mcl_error_t code = MCL_OK;
253 
254  MCL_DEBUG_ENTRY("char *buffer, mcl_size_t *buffer_size = <%p>, const char *boundary = <%s>", buffer, buffer_size, boundary);
255 
256  if (*buffer_size >= MULTIPART_CLOSE_LENGTH)
257  {
261 
262  (*buffer_size) -= MULTIPART_CLOSE_LENGTH;
263  }
264  else
265  {
266  code = MCL_FAIL;
267  }
268 
269  MCL_DEBUG_LEAVE("retVal = <%d>", code);
270  return code;
271 }
272 
274 {
275  MCL_DEBUG_ENTRY("<void>");
276  MCL_DEBUG_LEAVE("retVal = <%lu>", (long unsigned) MULTIPART_CLOSE_LENGTH);
277  return MULTIPART_CLOSE_LENGTH;
278 }
279 
281 {
282  mcl_error_t code;
283  mcl_size_t index;
284 
285  MCL_DEBUG_ENTRY("char **boundary = <%p>", boundary);
286 
287  // Allocate for boundary string.
288  *boundary = MCL_MALLOC(BOUNDARY_LENGTH + 1);
289  MCL_ASSERT_CODE_MESSAGE(MCL_NULL != *boundary, MCL_OUT_OF_MEMORY, "Memory can not be allocated for boundary.");
290 
291  // Make sure boundary string is null terminated.
292  (*boundary)[BOUNDARY_LENGTH] = MCL_NULL_CHAR;
293 
294  // Generate random numbers for boundary characters.
295  code = mcl_random_generate_bytes((unsigned char *) *boundary, BOUNDARY_LENGTH);
296  MCL_ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, MCL_FREE(*boundary), code, "Random number generation for boundary failed.");
297 
298  // Pick boundary characters randomly.
299  for (index = 0; index < BOUNDARY_LENGTH; ++index)
300  {
301  (*boundary)[index] = boundary_characters[((mcl_uint8_t) (*boundary)[index]) % (sizeof(boundary_characters) - 1)];
302  }
303 
304  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
305  return MCL_OK;
306 }
307 
308 static void _add_boundary(char *buffer, const char *boundary, mcl_bool_t add_and_close)
309 {
310  char *position = buffer;
311 
312  MCL_DEBUG_ENTRY("char *buffer = <%p>, const char *boundary = <%p>, mcl_bool_t add_and_close = <%d>", buffer, boundary, add_and_close);
313 
315  position += BOUNDARY_SIGN_LENGTH;
316 
317  mcl_string_util_memcpy(position, boundary, BOUNDARY_LENGTH);
318  position += BOUNDARY_LENGTH;
319 
320  if (MCL_TRUE == add_and_close)
321  {
323  position += BOUNDARY_SIGN_LENGTH;
324  }
325 
327 
328  MCL_DEBUG_LEAVE("retVal = <void>");
329 }
330 
331 static mcl_error_t _add_item_meta_json(char **position, mcl_size_t *remaining_size, void *item)
332 {
333  mcl_error_t code;
334  char *item_meta_json = MCL_NULL;
335 
336  code = json_from_item_meta(item, &item_meta_json);
337 
338  if (MCL_OK == code)
339  {
340  mcl_size_t item_meta_json_length = mcl_string_util_strlen(item_meta_json);
341 
342  if (*remaining_size >= item_meta_json_length)
343  {
344  mcl_string_util_memcpy(*position, item_meta_json, item_meta_json_length);
345  (*remaining_size) -= item_meta_json_length;
346  (*position) += item_meta_json_length;
347  }
348  else
349  {
350  code = MCL_FAIL;
351  }
352  }
353 
354  MCL_FREE(item_meta_json);
355 
356  MCL_DEBUG_LEAVE("retVal = <%d>", code);
357  return code;
358 }
359 
360 static mcl_error_t _add_item_payload_content_type(char **position, mcl_size_t *remaining_size, const char *payload_content_type)
361 {
362  mcl_error_t code = MCL_OK;
363  mcl_size_t length;
364  const char *source;
365 
366  if (MCL_NULL == payload_content_type)
367  {
370  }
371  else
372  {
373  length = mcl_string_util_strlen(payload_content_type);
374  source = payload_content_type;
375  }
376 
377  if (*remaining_size >= length)
378  {
379  mcl_string_util_memcpy(*position, source, length);
380  (*position) += length;
381  (*remaining_size) -= length;
382  }
383  else
384  {
385  code = MCL_FAIL;
386  }
387 
388  return code;
389 }
390 
391 static mcl_error_t _add_item_payload(char **position, mcl_size_t *remaining_size, void *item, multipart_add_payload_callback callback, void *user_context)
392 {
393  mcl_error_t code = MCL_OK;
394 
395  if (MCL_NULL == callback)
396  {
397  char *payload = MCL_NULL;
398 
399  code = json_from_item_payload(item, &payload);
400 
401  if (MCL_OK == code)
402  {
403  mcl_size_t payload_length = mcl_string_util_strlen(payload);
404 
405  if (*remaining_size >= payload_length)
406  {
407  mcl_string_util_memcpy(*position, payload, payload_length);
408  (*position) += payload_length;
409  (*remaining_size) -= payload_length;
410  }
411  else
412  {
413  code = MCL_FAIL;
414  }
415 
416  MCL_FREE(payload);
417  }
418  }
419  else
420  {
421  mcl_size_t callback_value;
422 
423  do
424  {
425  callback_value = callback(*position, 1, *remaining_size, user_context);
426  (*position) += callback_value;
427  (*remaining_size) -= callback_value;
428  } while ((0 != callback_value) && (0 != *remaining_size));
429 
430  if ((0 == *remaining_size) && (0 != callback_value))
431  {
432  code = MCL_FAIL;
433  }
434  }
435 
436  return code;
437 }
static const char boundary_characters[]
Definition: multipart.c:20
size_t mcl_size_t
mcl_size_t json_get_item_size(void *item)
Definition: json.c:429
mcl_error_t json_from_item_meta(void *item, char **json_string)
Definition: json.c:326
MCL_OK
static const char boundary_sign[]
Definition: multipart.c:16
mcl_error_t multipart_add_tuple(char *buffer, mcl_size_t *buffer_size, void *item, const char *boundary)
Definition: multipart.c:117
mcl_size_t multipart_get_overhead_size(void)
Definition: multipart.c:273
mcl_size_t multipart_get_tuple_size(void *item, const char *payload_content_type)
Definition: multipart.c:237
mcl_int32_t mcl_error_t
static void _add_boundary(char *buffer, const char *boundary, mcl_bool_t add_and_close)
Definition: multipart.c:308
static const mcl_size_t _space_needed_for_state[MULTIPART_STATE_END]
Definition: multipart.c:67
#define ENDLINE_LENGTH
Definition: multipart.c:29
#define MCL_DEBUG_ENTRY(...)
static mcl_error_t _add_item_meta_json(char **position, mcl_size_t *remaining_size, void *item)
Definition: multipart.c:331
static const char boundary_key[]
Definition: multipart.c:18
static mcl_error_t _add_item_payload(char **position, mcl_size_t *remaining_size, void *item, multipart_add_payload_callback callback, void *user_context)
Definition: multipart.c:391
#define DOUBLE_ENDLINE_LENGTH
Definition: multipart.c:28
#define MCL_FALSE
mcl_error_t multipart_add_tuple_with_callback(char *buffer, mcl_size_t *buffer_size, void *item, const char *boundary, const char *payload_content_type, multipart_add_payload_callback callback, void *user_context)
Definition: multipart.c:130
MCL_CORE_EXPORT mcl_size_t mcl_string_util_strlen(const char *buffer)
#define MULTIPART_BASE_SIZE
Definition: multipart.c:35
#define MCL_ASSERT_CODE_MESSAGE(condition, return_code,...)
#define MCL_NULL
#define CONTENT_TYPE_LENGTH
Definition: multipart.c:26
Json module header file.
mcl_size_t(* multipart_add_payload_callback)(char *buffer, mcl_size_t size, mcl_size_t count, void *user_context)
Definition: multipart.h:30
static const char content_type_application_json[]
Definition: multipart.c:23
Common module interface header file.
#define MCL_FREE(p)
#define BOUNDARY_SIGN_LENGTH
Definition: multipart.c:25
uint8_t mcl_uint8_t
static mcl_error_t _add_item_payload_content_type(char **position, mcl_size_t *remaining_size, const char *payload_content_type)
Definition: multipart.c:360
static const char content_type_meta_json[]
Definition: multipart.c:22
E_MULTIPART_STATE
Definition: multipart.c:43
#define CONTENT_TYPE_MULTIPART_RELATED_LENGTH
Definition: multipart.c:31
#define MCL_ASSERT_STATEMENT_CODE_MESSAGE(condition, statement, return_code,...)
MCL_CORE_EXPORT void mcl_string_util_memcpy(void *destination, const void *source, mcl_size_t count)
static const char content_type[]
Definition: multipart.c:17
#define MULTIPART_CLOSE_LENGTH
Definition: multipart.c:38
mcl_error_t multipart_close(char *buffer, mcl_size_t *buffer_size, const char *boundary)
Definition: multipart.c:250
mcl_error_t multipart_generate_boundary(char **boundary)
Definition: multipart.c:280
mcl_uint8_t mcl_bool_t
static const char content_type_multipart_related[]
Definition: multipart.c:21
MCL_CORE_EXPORT mcl_error_t mcl_random_generate_bytes(unsigned char *buffer, mcl_size_t size)
#define CONTENT_TYPE_META_JSON_LENGTH
Definition: multipart.c:32
MCL_OUT_OF_MEMORY
static const char endline[]
Definition: multipart.c:19
#define BOUNDARY_LENGTH
Definition: multipart.c:30
#define BOUNDARY_KEY_LENGTH
Definition: multipart.c:27
#define MCL_MALLOC(bytes)
mcl_error_t json_from_item_payload(void *item, char **json_string)
Definition: json.c:370
#define MCL_DEBUG_LEAVE(...)
#define MCL_TRUE
MCL_FAIL
#define CONTENT_TYPE_APPLICATION_JSON_LENGTH
Definition: multipart.c:33
#define MCL_NULL_CHAR
Multipart module header file.