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