1 /*
2 * uhub - A tiny ADC p2p connection hub
3 * Copyright (C) 2007-2014, Jan Vidar Krey
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "uhub.h"
21
22 #ifdef DEBUG
23 #define ADC_MSG_ASSERT(X) \
24 uhub_assert(X); \
25 uhub_assert(X->cache); \
26 uhub_assert(X->capacity); \
27 uhub_assert(X->length <= X->capacity); \
28 uhub_assert(X->references > 0); \
29 uhub_assert(X->length == strlen(X->cache));
30 #define ADC_MSG_NULL_ON_FREE
31 #else
32 #define ADC_MSG_ASSERT(X) do { } while(0)
33 #endif /* DEBUG */
34
35 #ifdef MSG_MEMORY_DEBUG
36 #undef msg_malloc
37 #undef msg_malloc_zero
38 #undef msg_free
39
msg_malloc(size_t size)40 static void* msg_malloc(size_t size)
41 {
42 void* ptr = valloc(size);
43 LOG_MEMORY("msg_malloc: %p %d", ptr, (int) size);
44 return ptr;
45 }
46
msg_malloc_zero(size_t size)47 static void* msg_malloc_zero(size_t size)
48 {
49 void* ptr = msg_malloc(size);
50 memset(ptr, 0, size);
51 return ptr;
52 }
53
msg_free(void * ptr)54 static void msg_free(void* ptr)
55 {
56 LOG_MEMORY("msg_free: %p", ptr);
57 hub_free(ptr);
58 }
59 #else
60 #define msg_malloc(X) hub_malloc(X)
61 #define msg_malloc_zero(X) hub_malloc_zero(X)
62 #define msg_free(X) hub_free(X)
63 #endif /* MSG_MEMORY_DEBUG */
64
msg_check_escapes(const char * string,size_t len)65 static int msg_check_escapes(const char* string, size_t len)
66 {
67 char* start = (char*) string;
68 while ((start = memchr(start, '\\', len - (start - string))))
69 {
70 if (start+1 == (string + len))
71 return 0;
72
73 switch (*(++start))
74 {
75 case '\\':
76 case 'n':
77 case 's':
78 /* Increment so we don't check the escaped
79 * character next time around. Not doing so
80 * leads to messages with escaped backslashes
81 * being incorrectly reported as having invalid
82 * escapes. */
83 ++start;
84 break;
85 default:
86 return 0;
87 }
88 }
89 return 1;
90 }
91
92
adc_msg_incref(struct adc_message * msg)93 struct adc_message* adc_msg_incref(struct adc_message* msg)
94 {
95 #ifndef ADC_MESSAGE_INCREF
96 msg->references++;
97 #ifdef MSG_MEMORY_DEBUG
98 adc_msg_protect(msg);
99 #endif
100 return msg;
101 #else
102 struct adc_message* copy = adc_msg_copy(msg);
103 return copy;
104 #endif
105 }
106
adc_msg_set_length(struct adc_message * msg,size_t len)107 static void adc_msg_set_length(struct adc_message* msg, size_t len)
108 {
109 msg->length = len;
110 }
111
adc_msg_grow(struct adc_message * msg,size_t size)112 static int adc_msg_grow(struct adc_message* msg, size_t size)
113 {
114 char* buf;
115 size_t newsize = 0;
116
117 if (!msg)
118 return 0;
119
120 if (msg->capacity > size)
121 return 1;
122
123 /* Make sure we align our data */
124 newsize = size;
125 newsize += 2; /* termination */
126 newsize += (newsize % sizeof(size_t)); /* alignment padding */
127
128 buf = msg_malloc_zero(newsize);
129 if (!buf)
130 return 0;
131
132 if (msg->cache)
133 {
134 memcpy(buf, msg->cache, msg->length);
135 msg_free(msg->cache);
136 }
137
138 msg->cache = buf;
139 msg->capacity = newsize;
140
141 return 1;
142 }
143
144 /* NOTE: msg must be unterminated here */
adc_msg_cache_append(struct adc_message * msg,const char * string,size_t len)145 static int adc_msg_cache_append(struct adc_message* msg, const char* string, size_t len)
146 {
147 if (!adc_msg_grow(msg, msg->length + len))
148 {
149 /* FIXME: OOM! */
150 return 0;
151 }
152
153 memcpy(&msg->cache[msg->length], string, len);
154 adc_msg_set_length(msg, msg->length + len);
155
156 uhub_assert(msg->capacity > msg->length);
157 msg->cache[msg->length] = 0;
158 return 1;
159 }
160
161 /**
162 * Returns position of the first argument of the message.
163 * Excludes mandatory arguments for the given type of message
164 * like source and target.
165 */
adc_msg_get_arg_offset(struct adc_message * msg)166 int adc_msg_get_arg_offset(struct adc_message* msg)
167 {
168 if (!msg || !msg->cache)
169 return -1;
170
171 switch (msg->cache[0])
172 {
173 /* These *SHOULD* never be seen on a hub */
174 case 'U':
175 case 'C':
176 return 4; /* Actually: 4 + strlen(cid). */
177
178 case 'I':
179 case 'H':
180 return 4;
181
182 case 'B':
183 return 9;
184
185 case 'F':
186 return (10 + (list_size(msg->feature_cast_include)*5) + (list_size(msg->feature_cast_exclude)*5));
187
188 case 'D':
189 case 'E':
190 return 14;
191 }
192 return -1;
193 }
194
195
adc_msg_is_empty(struct adc_message * msg)196 int adc_msg_is_empty(struct adc_message* msg)
197 {
198 int offset = adc_msg_get_arg_offset(msg);
199
200 if (offset == -1)
201 return -1;
202
203 if ((msg->length - 1) == (size_t) offset)
204 return 1;
205
206 return 0;
207 }
208
209
adc_msg_free(struct adc_message * msg)210 void adc_msg_free(struct adc_message* msg)
211 {
212 if (!msg) return;
213
214 ADC_MSG_ASSERT(msg);
215
216 msg->references--;
217
218 if (msg->references == 0)
219 {
220 #ifdef ADC_MSG_NULL_ON_FREE
221 if (msg->cache)
222 {
223 *msg->cache = 0;
224 }
225 #endif
226 msg_free(msg->cache);
227
228 if (msg->feature_cast_include)
229 {
230 list_clear(msg->feature_cast_include, &hub_free);
231 list_destroy(msg->feature_cast_include);
232 msg->feature_cast_include = 0;
233 }
234
235 if (msg->feature_cast_exclude)
236 {
237 list_clear(msg->feature_cast_exclude, &hub_free);
238 list_destroy(msg->feature_cast_exclude);
239 msg->feature_cast_exclude = 0;
240 }
241
242 msg_free(msg);
243 }
244 }
245
246
adc_msg_copy(const struct adc_message * cmd)247 struct adc_message* adc_msg_copy(const struct adc_message* cmd)
248 {
249 char* tmp = 0;
250 struct adc_message* copy = (struct adc_message*) msg_malloc_zero(sizeof(struct adc_message));
251 if (!copy) return NULL; /* OOM */
252
253 ADC_MSG_ASSERT(cmd);
254
255 /* deep copy */
256 copy->cmd = cmd->cmd;
257 copy->source = cmd->source;
258 copy->target = cmd->target;
259 copy->cache = 0;
260 copy->length = cmd->length;
261 copy->capacity = 0;
262 copy->priority = cmd->priority;
263 copy->references = 1;
264 copy->feature_cast_include = 0;
265 copy->feature_cast_exclude = 0;
266
267 if (!adc_msg_grow(copy, copy->length))
268 {
269 adc_msg_free(copy);
270 return NULL; /* OOM */
271 }
272
273 if (!copy->cache)
274 {
275 adc_msg_free(copy);
276 return NULL;
277 }
278
279 memcpy(copy->cache, cmd->cache, cmd->length);
280 copy->cache[copy->length] = 0;
281
282 if (cmd->feature_cast_include)
283 {
284 copy->feature_cast_include = list_create();
285 LIST_FOREACH(char*, tmp, cmd->feature_cast_include,
286 {
287 list_append(copy->feature_cast_include, hub_strdup(tmp));
288 });
289 }
290
291 if (cmd->feature_cast_exclude)
292 {
293 copy->feature_cast_exclude = list_create();
294 LIST_FOREACH(char*, tmp, cmd->feature_cast_exclude,
295 {
296 list_append(copy->feature_cast_exclude, hub_strdup(tmp));
297 });
298 }
299
300 ADC_MSG_ASSERT(copy);
301
302 return copy;
303 }
304
305
adc_msg_parse_verify(struct hub_user * u,const char * line,size_t length)306 struct adc_message* adc_msg_parse_verify(struct hub_user* u, const char* line, size_t length)
307 {
308 struct adc_message* command = adc_msg_parse(line, length);
309
310 if (!command)
311 return 0;
312
313 if (command->source && (!u || (command->source != u->id.sid && !auth_cred_is_unrestricted(u->credentials))))
314 {
315 LOG_DEBUG("Command does not match user's SID (command->source=%d, user->id.sid=%d)", command->source, (u ? u->id.sid : 0));
316 adc_msg_free(command);
317 return 0;
318 }
319
320 return command;
321 }
322
323
adc_msg_parse(const char * line,size_t length)324 struct adc_message* adc_msg_parse(const char* line, size_t length)
325 {
326 struct adc_message* command = (struct adc_message*) msg_malloc_zero(sizeof(struct adc_message));
327 char prefix = line[0];
328 size_t n = 0;
329 char temp_sid[5];
330 int ok = 1;
331 int need_terminate = 0;
332 struct linked_list* feature_cast_list;
333
334 if (command == NULL)
335 return NULL; /* OOM */
336
337 if (!is_printable_utf8(line, length))
338 {
339 LOG_DEBUG("Dropped message with non-printable UTF-8 characters.");
340 msg_free(command);
341 return NULL;
342 }
343
344 if (!msg_check_escapes(line, length))
345 {
346 LOG_DEBUG("Dropped message with invalid ADC escape.");
347 msg_free(command);
348 return NULL;
349 }
350
351 if (line[length-1] != '\n')
352 {
353 need_terminate = 1;
354 }
355
356 if (!adc_msg_grow(command, length + need_terminate))
357 {
358 msg_free(command);
359 return NULL; /* OOM */
360 }
361
362 adc_msg_set_length(command, length + need_terminate);
363 memcpy(command->cache, line, length);
364
365 /* Ensure we are zero terminated */
366 command->cache[length] = 0;
367 command->cache[length+need_terminate] = 0;
368
369 command->cmd = FOURCC(line[0], line[1], line[2], line[3]);
370 command->priority = 0;
371 command->references = 1;
372
373 switch (prefix)
374 {
375 case 'U':
376 case 'C':
377 /* these should never be seen on a hub */
378 ok = 0;
379 break;
380
381 case 'I':
382 case 'H':
383 ok = (length > 3);
384 break;
385
386 case 'B':
387 ok = (length > 8 &&
388 is_space(line[4]) &&
389 is_valid_base32_char(line[5]) &&
390 is_valid_base32_char(line[6]) &&
391 is_valid_base32_char(line[7]) &&
392 is_valid_base32_char(line[8]));
393
394 if (!ok) break;
395
396 temp_sid[0] = line[5];
397 temp_sid[1] = line[6];
398 temp_sid[2] = line[7];
399 temp_sid[3] = line[8];
400 temp_sid[4] = '\0';
401
402 command->source = string_to_sid(temp_sid);
403 break;
404
405 case 'F':
406 ok = (length > 8 &&
407 is_space(line[4]) &&
408 is_valid_base32_char(line[5]) &&
409 is_valid_base32_char(line[6]) &&
410 is_valid_base32_char(line[7]) &&
411 is_valid_base32_char(line[8]));
412
413 if (!ok) break;
414
415 temp_sid[0] = line[5];
416 temp_sid[1] = line[6];
417 temp_sid[2] = line[7];
418 temp_sid[3] = line[8];
419 temp_sid[4] = '\0';
420
421 command->source = string_to_sid(temp_sid);
422
423 /* Create feature cast lists */
424 command->feature_cast_include = list_create();
425 command->feature_cast_exclude = list_create();
426
427 if (!command->feature_cast_include || !command->feature_cast_exclude)
428 {
429 list_destroy(command->feature_cast_include);
430 list_destroy(command->feature_cast_exclude);
431 msg_free(command->cache);
432 msg_free(command);
433 return NULL; /* OOM */
434 }
435
436 n = 10;
437 while (line[n] == '+' || line[n] == '-')
438 {
439 if (line[n++] == '+')
440 feature_cast_list = command->feature_cast_include;
441 else
442 feature_cast_list = command->feature_cast_exclude;
443
444 temp_sid[0] = line[n++];
445 temp_sid[1] = line[n++];
446 temp_sid[2] = line[n++];
447 temp_sid[3] = line[n++];
448 temp_sid[4] = '\0';
449
450 list_append(feature_cast_list, hub_strdup(temp_sid));
451 }
452
453 if (n == 10)
454 ok = 0;
455 break;
456
457 case 'D':
458 case 'E':
459 ok = (length > 13 &&
460 is_space(line[4]) &&
461 is_valid_base32_char(line[5]) &&
462 is_valid_base32_char(line[6]) &&
463 is_valid_base32_char(line[7]) &&
464 is_valid_base32_char(line[8]) &&
465 is_space(line[9]) &&
466 is_valid_base32_char(line[10]) &&
467 is_valid_base32_char(line[11]) &&
468 is_valid_base32_char(line[12]) &&
469 is_valid_base32_char(line[13]));
470
471 if (!ok) break;
472
473 temp_sid[0] = line[5];
474 temp_sid[1] = line[6];
475 temp_sid[2] = line[7];
476 temp_sid[3] = line[8];
477 temp_sid[4] = '\0';
478
479 command->source = string_to_sid(temp_sid);
480
481 temp_sid[0] = line[10];
482 temp_sid[1] = line[11];
483 temp_sid[2] = line[12];
484 temp_sid[3] = line[13];
485 temp_sid[4] = '\0';
486
487 command->target = string_to_sid(temp_sid);
488 break;
489
490 default:
491 ok = 0;
492 }
493
494 if (need_terminate)
495 {
496 command->cache[length] = '\n';
497 }
498
499 if (!ok)
500 {
501 adc_msg_free(command);
502 return NULL;
503 }
504
505 /* At this point the arg_offset should point to a space, or the end of message */
506 n = adc_msg_get_arg_offset(command);
507 if (command->cache[n] == ' ')
508 {
509 if (command->cache[n+1] == ' ') ok = 0;
510 }
511 else if (command->cache[n] == '\n') ok = 1;
512 else ok = 0;
513
514 if (!ok)
515 {
516 adc_msg_free(command);
517 return NULL;
518 }
519
520 ADC_MSG_ASSERT(command);
521 return command;
522 }
523
524
adc_msg_create(const char * line)525 struct adc_message* adc_msg_create(const char* line)
526 {
527 return adc_msg_parse(line, strlen(line));
528 }
529
adc_msg_construct_source(fourcc_t fourcc,sid_t source,size_t size)530 struct adc_message* adc_msg_construct_source(fourcc_t fourcc, sid_t source, size_t size)
531 {
532 struct adc_message* msg = adc_msg_construct(fourcc, size + 4 + 1);
533 if (!msg)
534 return NULL;
535
536 adc_msg_add_argument(msg, sid_to_string(source));
537 msg->source = source;
538 return msg;
539 }
540
adc_msg_construct_source_dest(fourcc_t fourcc,sid_t source,sid_t dest,size_t size)541 struct adc_message* adc_msg_construct_source_dest(fourcc_t fourcc, sid_t source, sid_t dest, size_t size)
542 {
543 struct adc_message* msg = adc_msg_construct(fourcc, size + 4 + 4 + 1);
544 if (!msg)
545 return NULL;
546
547 adc_msg_add_argument(msg, sid_to_string(source));
548 adc_msg_add_argument(msg, sid_to_string(dest));
549 msg->source = source;
550 msg->target = dest;
551 return msg;
552 }
553
554
adc_msg_construct(fourcc_t fourcc,size_t size)555 struct adc_message* adc_msg_construct(fourcc_t fourcc, size_t size)
556 {
557 struct adc_message* msg = (struct adc_message*) msg_malloc_zero(sizeof(struct adc_message));
558 if (!msg)
559 return NULL; /* OOM */
560
561 if (!adc_msg_grow(msg, sizeof(fourcc) + size + 1))
562 {
563 msg_free(msg);
564 return NULL; /* OOM */
565 }
566
567 if (fourcc)
568 {
569 msg->cache[0] = (char) ((fourcc >> 24) & 0xff);
570 msg->cache[1] = (char) ((fourcc >> 16) & 0xff);
571 msg->cache[2] = (char) ((fourcc >> 8) & 0xff);
572 msg->cache[3] = (char) ((fourcc ) & 0xff);
573 msg->cache[4] = '\n';
574
575 /* Ensure we are zero terminated */
576 adc_msg_set_length(msg, 5);
577 msg->cache[msg->length] = 0;
578 }
579
580 msg->cmd = fourcc;
581 msg->priority = 0;
582 msg->references = 1;
583 return msg;
584 }
585
586
adc_msg_remove_named_argument(struct adc_message * cmd,const char prefix_[2])587 int adc_msg_remove_named_argument(struct adc_message* cmd, const char prefix_[2])
588 {
589 char* start;
590 char* end;
591 char* endInfo;
592 size_t endlen;
593 char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
594 int found = 0;
595 int arg_offset = adc_msg_get_arg_offset(cmd);
596 size_t temp_len = 0;
597
598 adc_msg_unterminate(cmd);
599
600 start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
601 while (start)
602 {
603 endInfo = &cmd->cache[cmd->length];
604
605 if (&start[0] < &endInfo[0])
606 {
607 end = memchr(&start[1], ' ', &endInfo[0]-&start[1]);
608 }
609 else
610 {
611 end = NULL;
612 }
613
614 if (end)
615 {
616
617 temp_len = &end[0] - &start[0]; // strlen(start);
618 endlen = strlen(end);
619
620 memmove(start, end, endlen);
621 start[endlen] = '\0';
622 found++;
623 adc_msg_set_length(cmd, cmd->length - temp_len);
624 }
625 else
626 {
627 found++;
628 adc_msg_set_length(cmd, cmd->length - strlen(start));
629 start[0] = '\0';
630 break;
631 }
632 start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
633 }
634
635 adc_msg_terminate(cmd);
636
637 return found;
638 }
639
640
adc_msg_has_named_argument(struct adc_message * cmd,const char prefix_[2])641 int adc_msg_has_named_argument(struct adc_message* cmd, const char prefix_[2])
642 {
643 int count = 0;
644 char* start;
645 char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
646 int arg_offset = adc_msg_get_arg_offset(cmd);
647
648 ADC_MSG_ASSERT(cmd);
649
650 start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
651 while (start)
652 {
653 count++;
654 if ((size_t) (&start[0] - &cmd->cache[0]) < 1+cmd->length)
655 start = memmem(&start[1], (&cmd->cache[cmd->length] - &start[0]), prefix, 3);
656 else
657 start = NULL;
658 }
659
660 return count;
661 }
662
663
adc_msg_get_named_argument(struct adc_message * cmd,const char prefix_[2])664 char* adc_msg_get_named_argument(struct adc_message* cmd, const char prefix_[2])
665 {
666 char* start;
667 char* end;
668 char* argument;
669 size_t length;
670 char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
671 int arg_offset = adc_msg_get_arg_offset(cmd);
672
673 ADC_MSG_ASSERT(cmd);
674
675 start = memmem(&cmd->cache[arg_offset], cmd->length - arg_offset, prefix, 3);
676 if (!start)
677 return NULL;
678
679 start = &start[3];
680 end = strchr(start, ' ');
681 if (!end) end = &cmd->cache[cmd->length];
682 length = &end[0] - &start[0];
683
684 argument = hub_strndup(start, length);
685
686 if (length > 0 && argument[length-1] == '\n')
687 {
688 argument[length-1] = 0;
689 }
690
691 return argument;
692 }
693
694
adc_msg_replace_named_argument(struct adc_message * cmd,const char prefix[2],const char * string)695 int adc_msg_replace_named_argument(struct adc_message* cmd, const char prefix[2], const char* string)
696 {
697 ADC_MSG_ASSERT(cmd);
698
699 while (adc_msg_has_named_argument(cmd, prefix))
700 {
701 adc_msg_remove_named_argument(cmd, prefix);
702 }
703
704 if (adc_msg_add_named_argument(cmd, prefix, string) == -1)
705 {
706 return -1;
707 }
708
709 ADC_MSG_ASSERT(cmd);
710
711 return 0;
712 }
713
714
adc_msg_terminate(struct adc_message * cmd)715 void adc_msg_terminate(struct adc_message* cmd)
716 {
717 if (cmd->cache[cmd->length - 1] != '\n')
718 {
719 adc_msg_cache_append(cmd, "\n", 1);
720 }
721 ADC_MSG_ASSERT(cmd);
722 }
723
724 /* FIXME: this looks bogus */
adc_msg_unterminate(struct adc_message * cmd)725 void adc_msg_unterminate(struct adc_message* cmd)
726 {
727 ADC_MSG_ASSERT(cmd);
728
729 if (cmd->length > 0 && cmd->cache[cmd->length-1] == '\n')
730 {
731 cmd->length--;
732 cmd->cache[cmd->length] = 0;
733 }
734 }
735
adc_msg_add_named_argument(struct adc_message * cmd,const char prefix[2],const char * string)736 int adc_msg_add_named_argument(struct adc_message* cmd, const char prefix[2], const char* string)
737 {
738 int ret = 0;
739 if (!string)
740 return -1;
741
742 ADC_MSG_ASSERT(cmd);
743
744 adc_msg_unterminate(cmd);
745 adc_msg_cache_append(cmd, " ", 1);
746 adc_msg_cache_append(cmd, prefix, 2);
747 adc_msg_cache_append(cmd, string, strlen(string));
748 adc_msg_terminate(cmd);
749 return ret;
750 }
751
adc_msg_add_named_argument_string(struct adc_message * cmd,const char prefix[2],const char * string)752 int adc_msg_add_named_argument_string(struct adc_message* cmd, const char prefix[2], const char* string)
753 {
754 char* escaped = adc_msg_escape(string);
755 int ret = adc_msg_add_named_argument(cmd, prefix, escaped);
756 hub_free(escaped);
757 return ret;
758 }
759
adc_msg_add_named_argument_int(struct adc_message * cmd,const char prefix[2],int num)760 int adc_msg_add_named_argument_int(struct adc_message* cmd, const char prefix[2], int num)
761 {
762 const char* s = uhub_itoa(num);
763 int ret = adc_msg_add_named_argument(cmd, prefix, s);
764 return ret;
765 }
766
adc_msg_add_named_argument_uint64(struct adc_message * cmd,const char prefix[2],uint64_t num)767 int adc_msg_add_named_argument_uint64(struct adc_message* cmd, const char prefix[2], uint64_t num)
768 {
769 const char* s = uhub_ulltoa(num);
770 int ret = adc_msg_add_named_argument(cmd, prefix, s);
771 return ret;
772 }
773
adc_msg_add_argument(struct adc_message * cmd,const char * string)774 int adc_msg_add_argument(struct adc_message* cmd, const char* string)
775 {
776 ADC_MSG_ASSERT(cmd);
777
778 adc_msg_unterminate(cmd);
779 adc_msg_cache_append(cmd, " ", 1);
780 adc_msg_cache_append(cmd, string, strlen(string));
781 adc_msg_terminate(cmd);
782 return 0;
783 }
784
785
adc_msg_get_argument(struct adc_message * cmd,int offset)786 char* adc_msg_get_argument(struct adc_message* cmd, int offset)
787 {
788 char* start;
789 char* end;
790 char* argument;
791 int count = 0;
792
793 ADC_MSG_ASSERT(cmd);
794
795 adc_msg_unterminate(cmd);
796
797 start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' ');
798 while (start)
799 {
800 end = strchr(&start[1], ' ');
801 if (count == offset)
802 {
803 if (end)
804 {
805 argument = hub_strndup(&start[1], (&end[0] - &start[1]));
806 }
807 else
808 {
809 argument = hub_strdup(&start[1]);
810 if (argument && *argument && argument[strlen(argument)-1] == '\n')
811 argument[strlen(argument)-1] = 0;
812 }
813
814 if (!argument)
815 return 0; // FIXME: OOM
816
817 if (*argument)
818 {
819 adc_msg_terminate(cmd);
820 return argument;
821 }
822 else
823 {
824 hub_free(argument);
825 }
826 }
827 count++;
828 start = end;
829 }
830
831 adc_msg_terminate(cmd);
832 return 0;
833 }
834
835 /**
836 * NOTE: Untested code.
837 */
adc_msg_get_argument_index(struct adc_message * cmd,const char prefix[2])838 int adc_msg_get_argument_index(struct adc_message* cmd, const char prefix[2])
839 {
840 char* start;
841 char* end;
842 int count = 0;
843
844 ADC_MSG_ASSERT(cmd);
845
846 adc_msg_unterminate(cmd);
847
848 start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' ');
849 while (start)
850 {
851 end = strchr(&start[1], ' ');
852 if (((&end[0] - &start[1]) > 2) && ((start[1] == prefix[0]) && (start[2] == prefix[1])))
853 {
854 adc_msg_terminate(cmd);
855 return count;
856 }
857 count++;
858 start = end;
859 }
860 adc_msg_terminate(cmd);
861 return -1;
862 }
863
864
865
adc_msg_escape_length(const char * str)866 int adc_msg_escape_length(const char* str)
867 {
868 int add = 0;
869 int n = 0;
870 for (; str[n]; n++)
871 if (str[n] == ' ' || str[n] == '\n' || str[n] == '\\') add++;
872 return n + add;
873 }
874
875
adc_msg_unescape_length(const char * str)876 int adc_msg_unescape_length(const char* str)
877 {
878 int add = 0;
879 int n = 0;
880 int escape = 0;
881 for (; str[n]; n++)
882 {
883 if (escape)
884 {
885 escape = 0;
886 }
887 else
888 {
889 if (str[n] == '\\')
890 {
891 escape = 1;
892 add++;
893 }
894 }
895 }
896 return n - add;
897 }
898
899
adc_msg_unescape(const char * string)900 char* adc_msg_unescape(const char* string)
901 {
902 char* new_string = msg_malloc(adc_msg_unescape_length(string)+1);
903 char* ptr = (char*) new_string;
904 char* str = (char*) string;
905 int escaped = 0;
906
907 while (*str)
908 {
909 if (escaped) {
910 if (*str == 's')
911 *ptr++ = ' ';
912 else if (*str == '\\')
913 *ptr++ = '\\';
914 else if (*str == 'n')
915 *ptr++ = '\n';
916 else
917 *ptr++ = *str;
918 escaped = 0;
919 } else {
920 if (*str == '\\')
921 escaped = 1;
922 else
923 *ptr++ = *str;
924 }
925 str++;
926 }
927 *ptr = 0;
928 return new_string;
929 }
930
adc_msg_unescape_to_target(const char * string,char * target,size_t target_size)931 int adc_msg_unescape_to_target(const char* string, char* target, size_t target_size)
932 {
933 size_t w = 0;
934 char* ptr = (char*) target;
935 char* str = (char*) string;
936 int escaped = 0;
937
938 while (*str && w < (target_size-1))
939 {
940 if (escaped) {
941 if (*str == 's')
942 *ptr++ = ' ';
943 else if (*str == '\\')
944 *ptr++ = '\\';
945 else if (*str == 'n')
946 *ptr++ = '\n';
947 else
948 *ptr++ = *str;
949 w++;
950 escaped = 0;
951 } else {
952 if (*str == '\\')
953 escaped = 1;
954 else
955 {
956 *ptr++ = *str;
957 w++;
958 }
959 }
960 str++;
961 }
962 *ptr = 0;
963 w++;
964 return w;
965 }
966
adc_msg_escape(const char * string)967 char* adc_msg_escape(const char* string)
968 {
969 char* str = hub_malloc(adc_msg_escape_length(string)+1);
970 size_t n = 0;
971 size_t i = 0;
972 for (i = 0; i < strlen(string); i++)
973 {
974 switch (string[i]) {
975 case '\\': /* fall through */
976 str[n++] = '\\';
977 str[n++] = '\\';
978 break;
979 case '\n':
980 str[n++] = '\\';
981 str[n++] = 'n';
982 break;
983 case ' ':
984 str[n++] = '\\';
985 str[n++] = 's';
986 break;
987 default:
988 str[n++] = string[i];
989 break;
990 }
991 }
992 str[n] = '\0';
993 return str;
994 }
995
996