xref: /trueos/lib/libasl/asl_msg.c (revision 541a2778bdd023e0af9a946a3c4490d1578a25d2)
1 /*
2  * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. Please obtain a copy of the License at
10  * http://www.opensource.apple.com/apsl/ and read it before using this
11  * file.
12  *
13  * The Original Code and all software distributed under the License are
14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18  * Please see the License for the specific language governing rights and
19  * limitations under the License.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23 
24 #include <string.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31 #include <regex.h>
32 #include <syslog.h>
33 #include <notify.h>
34 #include <errno.h>
35 #include <time.h>
36 #include <sys/time.h>
37 #include <asl.h>
38 #include <asl_object.h>
39 #include <asl_private.h>
40 #include <asl_core.h>
41 #include <sys/types.h>
42 #ifdef __FreeBSD__
43 #include <atomic_compat.h>
44 #else
45 #include <libkern/OSAtomic.h>
46 #endif
47 #include <asl_msg.h>
48 #include <asl_msg_list.h>
49 
50 #define TOKEN_NULL  0
51 #define TOKEN_OPEN  1
52 #define TOKEN_CLOSE 2
53 #define TOKEN_WORD  3
54 #define TOKEN_INT   4
55 
56 #define MFMT_RAW 0
57 #define MFMT_STD 1
58 #define MFMT_BSD 2
59 #define MFMT_XML 3
60 #define MFMT_STR 4
61 #define MFMT_MSG 5
62 
63 #define SEC_PER_HOUR 3600
64 
65 #define PAGE_OBJECT 1
66 
67 #define forever for(;;)
68 
69 #define streq(A, B) (strcmp(A, B) == 0)
70 #define streq_len(A, B, C) (strncmp(A, B, C) == 0)
71 #define strneq(A, B) (strcmp(A, B) != 0)
72 #define strcaseeq(A, B) (strcasecmp(A, B) == 0)
73 #define strcaseneq(A, B) (strcasecmp(A, B) != 0)
74 
75 #ifndef ASL_QUERY_OP_FALSE
76 #define ASL_QUERY_OP_FALSE 0
77 #endif
78 
79 #define AUX_0_TIME      0x00000001
80 #define AUX_0_TIME_NSEC 0x00000002
81 #define AUX_0_HOST      0x00000004
82 #define AUX_0_SENDER    0x00000008
83 #define AUX_0_FACILITY  0x00000010
84 #define AUX_0_PID       0x00000020
85 #define AUX_0_UID       0x00000040
86 #define AUX_0_GID       0x00000080
87 #define AUX_0_MSG       0x00000100
88 #define AUX_0_OPTION    0x00000200
89 #define AUX_0_LEVEL     0x00000400
90 
91 /* from asl_util.c */
92 int asl_is_utf8(const char *str);
93 uint8_t *asl_b64_encode(const uint8_t *buf, size_t len);
94 
95 void _asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg);
96 
97 #pragma mark -
98 #pragma mark standard message keys
99 
100 static const char *ASLStandardKey[] =
101 {
102 	ASL_KEY_TIME,
103 	ASL_KEY_TIME_NSEC,
104 	ASL_KEY_HOST,
105 	ASL_KEY_SENDER,
106 	ASL_KEY_FACILITY,
107 	ASL_KEY_PID,
108 	ASL_KEY_UID,
109 	ASL_KEY_GID,
110 	ASL_KEY_LEVEL,
111 	ASL_KEY_MSG,
112 	ASL_KEY_READ_UID,
113 	ASL_KEY_READ_GID,
114 	ASL_KEY_SESSION,
115 	ASL_KEY_REF_PID,
116 	ASL_KEY_REF_PROC,
117 	ASL_KEY_MSG_ID,
118 	ASL_KEY_EXPIRE_TIME,
119 	ASL_KEY_OPTION,
120 	ASL_KEY_FREE_NOTE
121 };
122 
123 static const char *MTStandardKey[] =
124 {
125 	"com.apple.message.domain",
126 	"com.apple.message.domain_scope",
127 	"com.apple.message.result",
128 	"com.apple.message.signature",
129 	"com.apple.message.signature2",
130 	"com.apple.message.signature3",
131 	"com.apple.message.success",
132 	"com.apple.message.uuid",
133 	"com.apple.message.value",
134 	"com.apple.message.value2",
135 	"com.apple.message.value3",
136 	"com.apple.message.value4",
137 	"com.apple.message.value5"
138 };
139 
140 static uint16_t
_asl_msg_std_key(const char * s,uint32_t len)141 _asl_msg_std_key(const char *s, uint32_t len)
142 {
143 	if ((len > 18) && (streq_len(s, "com.apple.message.", 18)))
144 	{
145 		if (streq(s + 18, "domain")) return ASL_MT_KEY_DOMAIN;
146 		else if (streq(s + 18, "domain_scope")) return ASL_MT_KEY_SCOPE;
147 		else if (streq(s + 18, "result")) return ASL_MT_KEY_RESULT;
148 		else if (streq(s + 18, "signature")) return ASL_MT_KEY_SIG;
149 		else if (streq(s + 18, "signature2")) return ASL_MT_KEY_SIG2;
150 		else if (streq(s + 18, "signature3")) return ASL_MT_KEY_SIG3;
151 		else if (streq(s + 18, "success")) return ASL_MT_KEY_SUCCESS;
152 		else if (streq(s + 18, "uuid")) return ASL_MT_KEY_UUID;
153 		else if (streq(s + 18, "value")) return ASL_MT_KEY_VAL;
154 		else if (streq(s + 18, "value2")) return ASL_MT_KEY_VAL2;
155 		else if (streq(s + 18, "value3")) return ASL_MT_KEY_VAL3;
156 		else if (streq(s + 18, "value4")) return ASL_MT_KEY_VAL4;
157 		else if (streq(s + 18, "value5")) return ASL_MT_KEY_VAL5;
158 
159 		return 0;
160 	}
161 
162 	switch (len)
163 	{
164 		case 3:
165 		{
166 			if streq(s, ASL_KEY_PID) return ASL_STD_KEY_PID;
167 			else if streq(s, ASL_KEY_UID) return ASL_STD_KEY_UID;
168 			else if streq(s, ASL_KEY_GID) return ASL_STD_KEY_GID;
169 		}
170 		case 4:
171 		{
172 			if streq(s, ASL_KEY_TIME) return ASL_STD_KEY_TIME;
173 			else if streq(s, ASL_KEY_HOST) return ASL_STD_KEY_HOST;
174 		}
175 		case 5:
176 		{
177 			if streq(s, ASL_KEY_LEVEL) return ASL_STD_KEY_LEVEL;
178 		}
179 		case 6:
180 		{
181 			if streq(s, ASL_KEY_SENDER) return ASL_STD_KEY_SENDER;
182 			else if streq(s, ASL_KEY_REF_PID) return ASL_STD_KEY_REF_PID;
183 		}
184 		case 7:
185 		{
186 			if streq(s, ASL_KEY_MSG) return ASL_STD_KEY_MESSAGE;
187 			else if streq(s, ASL_KEY_SESSION) return ASL_STD_KEY_SESSION;
188 			else if streq(s, ASL_KEY_READ_UID) return ASL_STD_KEY_READ_UID;
189 			else if streq(s, ASL_KEY_READ_GID) return ASL_STD_KEY_READ_GID;
190 			else if streq(s, ASL_KEY_REF_PROC) return ASL_STD_KEY_REF_PROC;
191 		}
192 		case 8:
193 		{
194 			if streq(s, ASL_KEY_FACILITY) return ASL_STD_KEY_FACILITY;
195 		}
196 		case 9:
197 		{
198 			if streq(s, ASL_KEY_OPTION) return ASL_STD_KEY_OPTION;
199 		}
200 		case 11:
201 		{
202 			if streq(s, ASL_KEY_TIME_NSEC) return ASL_STD_KEY_NANO;
203 		}
204 		case 12:
205 		{
206 			if streq(s, ASL_KEY_MSG_ID) return ASL_STD_KEY_MSG_ID;
207 		}
208 		case 13:
209 		{
210 			if streq(s, ASL_KEY_EXPIRE_TIME) return ASL_STD_KEY_EXPIRE;
211 		}
212 		case 14:
213 		{
214 			if streq(s, ASL_KEY_FREE_NOTE) return ASL_STD_KEY_FREE_NOTE;
215 		}
216 		default:
217 		{
218 			return 0;
219 		}
220 	}
221 
222 	return 0;
223 }
224 
225 #pragma mark -
226 #pragma mark asl_msg
227 
228 static asl_msg_t *
_asl_msg_make_page(void)229 _asl_msg_make_page(void)
230 {
231 	int i;
232 	asl_msg_t *out = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
233 
234 	if (out == NULL) return NULL;
235 
236 	for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
237 	{
238 		out->key[i] = ASL_MSG_SLOT_FREE;
239 		out->val[i] = ASL_MSG_SLOT_FREE;
240 	}
241 
242 	out->mem_size = sizeof(asl_msg_t);
243 
244 	return out;
245 }
246 
247 asl_msg_t *
asl_msg_retain(asl_msg_t * msg)248 asl_msg_retain(asl_msg_t *msg)
249 {
250 	if (msg == NULL) return NULL;
251 	asl_retain((asl_object_t)msg);
252 	return msg;
253 }
254 
255 void
asl_msg_release(asl_msg_t * msg)256 asl_msg_release(asl_msg_t *msg)
257 {
258 	if (msg == NULL) return;
259 	asl_release((asl_object_t)msg);
260 }
261 
262 static const char *
_asl_msg_slot_key(asl_msg_t * page,uint32_t slot)263 _asl_msg_slot_key(asl_msg_t *page, uint32_t slot)
264 {
265 	const char *out;
266 	uint16_t x;
267 
268 	if (page == NULL) return NULL;
269 	if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
270 	if (page->key[slot] == ASL_MSG_SLOT_FREE) return NULL;
271 
272 	switch (page->key[slot] & ASL_MSG_KV_MASK)
273 	{
274 		case ASL_MSG_KV_INLINE:
275 		{
276 			return page->data + page->key[slot];
277 		}
278 		case ASL_MSG_KV_DICT:
279 		{
280 			if ((page->key[slot] > ASL_STD_KEY_BASE) && (page->key[slot] <= ASL_STD_KEY_LAST))
281 			{
282 				x = page->key[slot] - ASL_STD_KEY_BASE - 1;
283 				return ASLStandardKey[x];
284 			}
285 			else if ((page->key[slot] > ASL_MT_KEY_BASE) && (page->key[slot] <= ASL_MT_KEY_LAST))
286 			{
287 				x = page->key[slot] - ASL_MT_KEY_BASE - 1;
288 				return MTStandardKey[x];
289 			}
290 
291 			return NULL;
292 		}
293 		case ASL_MSG_KV_EXTERN:
294 		{
295 			x = page->key[slot] & ASL_MSG_OFFSET_MASK;
296 			memcpy(&out, page->data + x, sizeof(char *));
297 			return out;
298 		}
299 	}
300 
301 	return NULL;
302 }
303 
304 static const char *
_asl_msg_slot_val(asl_msg_t * page,uint32_t slot)305 _asl_msg_slot_val(asl_msg_t *page, uint32_t slot)
306 {
307 	const char *out;
308 	uint16_t x, type;
309 
310 	if (page == NULL) return NULL;
311 	if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
312 
313 	if (page->val[slot] == ASL_MSG_SLOT_FREE) return NULL;
314 
315 	type = page->val[slot] & ASL_MSG_KV_MASK;
316 
317 	if (type == ASL_MSG_KV_INLINE)
318 	{
319 		return page->data + page->val[slot];
320 	}
321 	else if (type == ASL_MSG_KV_EXTERN)
322 	{
323 		x = page->val[slot] & ASL_MSG_OFFSET_MASK;
324 		memcpy(&out, page->data + x, sizeof(char *));
325 		return out;
326 	}
327 
328 	return NULL;
329 }
330 
331 /*
332  * asl_new: create a new log message.
333  */
334 asl_msg_t *
asl_msg_new(uint32_t type)335 asl_msg_new(uint32_t type)
336 {
337 	asl_msg_t *out;
338 
339 	out = _asl_msg_make_page();
340 	if (out == NULL) return NULL;
341 
342 	out->asl_type = type;
343 	out->refcount = 1;
344 
345 	return out;
346 }
347 
348 static void
_asl_msg_free_page(asl_msg_t * page)349 _asl_msg_free_page(asl_msg_t *page)
350 {
351 	uint32_t i;
352 	char *p;
353 
354 	if (page == NULL) return;
355 
356 	for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
357 	{
358 		if (page->key[i] == ASL_STD_KEY_FREE_NOTE)
359 		{
360 			const char *x = _asl_msg_slot_val(page, i);
361 			if (x != NULL) notify_post(x);
362 		}
363 
364 		if ((page->key[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
365 		{
366 			memcpy(&p, page->data + (page->key[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
367 			free(p);
368 		}
369 
370 		if ((page->val[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
371 		{
372 			memcpy(&p, page->data + (page->val[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
373 			free(p);
374 		}
375 	}
376 
377 	free(page);
378 }
379 
380 uint32_t
asl_msg_type(asl_msg_t * msg)381 asl_msg_type(asl_msg_t *msg)
382 {
383 	if (msg == NULL) return 0;
384 	return msg->asl_type;
385 }
386 
387 uint32_t
asl_msg_count(asl_msg_t * msg)388 asl_msg_count(asl_msg_t *msg)
389 {
390 	uint32_t total;
391 
392 	total = 0;
393 
394 	for (; msg != NULL; msg = msg->next) total += msg->count;
395 	return total;
396 }
397 
398 static void
_asl_msg_dump_kv(FILE * f,asl_msg_t * msg,uint16_t x)399 _asl_msg_dump_kv(FILE *f, asl_msg_t *msg, uint16_t x)
400 {
401 	if (x == ASL_MSG_SLOT_FREE)
402 	{
403 		fprintf(f, "-free-");
404 		return;
405 	}
406 
407 	if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
408 	{
409 		switch (x)
410 		{
411 			case ASL_STD_KEY_TIME: fprintf(f, "(dict: Time)"); return;
412 			case ASL_STD_KEY_NANO: fprintf(f, "(dict: Nano)"); return;
413 			case ASL_STD_KEY_HOST: fprintf(f, "(dict: Host)"); return;
414 			case ASL_STD_KEY_SENDER: fprintf(f, "(dict: Sender)"); return;
415 			case ASL_STD_KEY_FACILITY: fprintf(f, "(dict: Facility)"); return;
416 			case ASL_STD_KEY_PID: fprintf(f, "(dict: PID)"); return;
417 			case ASL_STD_KEY_UID: fprintf(f, "(dict: UID)"); return;
418 			case ASL_STD_KEY_GID: fprintf(f, "(dict: GID)"); return;
419 			case ASL_STD_KEY_LEVEL: fprintf(f, "(dict: Level)"); return;
420 			case ASL_STD_KEY_MESSAGE: fprintf(f, "(dict: Message)"); return;
421 			case ASL_STD_KEY_READ_UID: fprintf(f, "(dict: ReadUID)"); return;
422 			case ASL_STD_KEY_READ_GID: fprintf(f, "(dict: ReadGID)"); return;
423 			case ASL_STD_KEY_SESSION: fprintf(f, "(dict: Session)"); return;
424 			case ASL_STD_KEY_REF_PID: fprintf(f, "(dict: PID)"); return;
425 			case ASL_STD_KEY_REF_PROC: fprintf(f, "(dict: RefProc)"); return;
426 			case ASL_STD_KEY_MSG_ID: fprintf(f, "(dict: ASLMessageID)"); return;
427 			case ASL_STD_KEY_EXPIRE: fprintf(f, "(dict: Expire)"); return;
428 			case ASL_STD_KEY_OPTION: fprintf(f, "(dict: ASLOption)"); return;
429 			case ASL_MT_KEY_DOMAIN: fprintf(f, "(dict: com.apple.message.domain)"); return;
430 			case ASL_MT_KEY_SCOPE: fprintf(f, "(dict: com.apple.message.domain_scope)"); return;
431 			case ASL_MT_KEY_RESULT: fprintf(f, "(dict: com.apple.message.result)"); return;
432 			case ASL_MT_KEY_SIG: fprintf(f, "(dict: com.apple.message.signature)"); return;
433 			case ASL_MT_KEY_SIG2: fprintf(f, "(dict: com.apple.message.signature2)"); return;
434 			case ASL_MT_KEY_SIG3: fprintf(f, "(dict: com.apple.message.signature3)"); return;
435 			case ASL_MT_KEY_SUCCESS: fprintf(f, "(dict: com.apple.message.success)"); return;
436 			case ASL_MT_KEY_UUID: fprintf(f, "(dict: com.apple.message.uuid)"); return;
437 			case ASL_MT_KEY_VAL: fprintf(f, "(dict: com.apple.message.value)"); return;
438 			case ASL_MT_KEY_VAL2: fprintf(f, "(dict: com.apple.message.value2)"); return;
439 			case ASL_MT_KEY_VAL3: fprintf(f, "(dict: com.apple.message.value3)"); return;
440 			case ASL_MT_KEY_VAL4: fprintf(f, "(dict: com.apple.message.value4)"); return;
441 			case ASL_MT_KEY_VAL5: fprintf(f, "(dict: com.apple.message.value5)"); return;
442 		}
443 
444 		fprintf(f, "(dict: -unknown-)");
445 		return;
446 	}
447 
448 	if ((x & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
449 	{
450 		const char *c;
451 		size_t z = x & ASL_MSG_OFFSET_MASK;
452 		memcpy(&c, msg->data + z, sizeof(char *));
453 		fprintf(f, "(extern: %s)", c);
454 		return;
455 	}
456 
457 	fprintf(f, "%s", msg->data + x);
458 }
459 
460 void
_asl_msg_dump(FILE * f,const char * comment,asl_msg_t * msg)461 _asl_msg_dump(FILE *f, const char *comment, asl_msg_t *msg)
462 {
463 	int i, page1 = 1;
464 
465 	if (f == NULL) return;
466 	if (msg == NULL)
467 	{
468 		fprintf(f, "asl_msg %s: NULL\n", comment);
469 		return;
470 	}
471 
472 	while (msg != NULL)
473 	{
474 		if (page1 == 1)
475 		{
476 			fprintf(f, "asl_msg %s: %p\n", comment, msg);
477 			fprintf(f, "    refcount: %u\n", msg->refcount);
478 			fprintf(f, "    asl_type: %u\n", msg->asl_type);
479 			page1 = 0;
480 		}
481 		else
482 		{
483 			fprintf(f, "  page: %p\n", msg);
484 		}
485 
486 		fprintf(f, "    count: %u\n", msg->count);
487 		fprintf(f, "    data_size: %u\n", msg->data_size);
488 		fprintf(f, "    mem_size: %llu\n", msg->mem_size);
489 		fprintf(f, "    next: %p\n", msg->next);
490 
491 		for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
492 		{
493 			fprintf(f, "    slot[%d]: ", i);
494 			_asl_msg_dump_kv(f, msg, msg->key[i]);
495 			fprintf(f, " ");
496 			_asl_msg_dump_kv(f, msg, msg->val[i]);
497 			fprintf(f, " 0x%04x\n", msg->op[i]);
498 		}
499 
500 		msg = msg->next;
501 	}
502 }
503 
504 #pragma mark -
505 #pragma mark fetching contents
506 
507 /*
508  * Find the slot and page for an input key.
509  */
510 static uint32_t
_asl_msg_index(asl_msg_t * msg,const char * key,uint32_t * oslot,asl_msg_t ** opage)511 _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opage)
512 {
513 	uint32_t i, len, slot;
514 	uint16_t kx;
515 	asl_msg_t *page;
516 	const char *kp;
517 
518 	if (msg == NULL) return IndexNull;
519 	if (key == NULL) return IndexNull;
520 
521 	i = 0;
522 	slot = 0;
523 	if (oslot != NULL) *oslot = slot;
524 
525 	page = msg;
526 	if (opage != NULL) *opage = page;
527 
528 	len = strlen(key);
529 	kx = _asl_msg_std_key(key, len);
530 
531 	forever
532 	{
533 		if (page->key[slot] != ASL_MSG_SLOT_FREE)
534 		{
535 			if (kx != 0)
536 			{
537 				if (page->key[slot] == kx) return i;
538 			}
539 			else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
540 			{
541 				/* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */
542 			}
543 			else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
544 			{
545 				memcpy(&kp, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
546 				if (streq(key, kp)) return i;
547 			}
548 			else
549 			{
550 				kp = page->data + page->key[slot];
551 				if (streq(key, kp)) return i;
552 			}
553 		}
554 
555 		i++;
556 		slot++;
557 		if (oslot != NULL) *oslot = slot;
558 
559 		if (slot >= ASL_MSG_PAGE_SLOTS)
560 		{
561 			if (page->next == NULL) return IndexNull;
562 
563 			slot = 0;
564 			if (oslot != NULL) *oslot = slot;
565 
566 			page = page->next;
567 			if (opage != NULL) *opage = page;
568 		}
569 	}
570 
571 	return IndexNull;
572 }
573 
574 /*
575  * Find page and slot for an "index".
576  */
577 static int
_asl_msg_resolve_index(asl_msg_t * msg,uint32_t n,asl_msg_t ** page,uint32_t * slot)578 _asl_msg_resolve_index(asl_msg_t *msg, uint32_t n, asl_msg_t **page, uint32_t *slot)
579 {
580 	uint32_t i, sx;
581 	asl_msg_t *px;
582 
583 	if (msg == NULL) return -1;
584 
585 	*slot = IndexNull;
586 	*page = NULL;
587 
588 	sx = 0;
589 
590 	/* find page */
591 	for (px = msg; px != NULL; px = px->next)
592 	{
593 		if (n > (sx + px->count))
594 		{
595 			sx += px->count;
596 			continue;
597 		}
598 
599 		*page = px;
600 
601 		/* find slot */
602 		for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
603 		{
604 			if (px->key[i] != ASL_MSG_SLOT_FREE)
605 			{
606 				if (sx == n)
607 				{
608 					*slot = i;
609 					return 0;
610 				}
611 
612 				sx++;
613 			}
614 		}
615 	}
616 
617 	return -1;
618 }
619 
620 /*
621  * asl_msg_fetch: iterate over entries
622  * initial value of n should be 0.  Subseqent calls should use the last
623  * returned value.  Returns IndexNull when there are no more entries
624  * Sets the pointers for the next key, value, and op in the msg.
625  * The iterator encodes a page number and a slot number.
626  */
627 
628 uint32_t
asl_msg_fetch(asl_msg_t * msg,uint32_t x,const char ** keyout,const char ** valout,uint16_t * opout)629 asl_msg_fetch(asl_msg_t *msg, uint32_t x, const char **keyout, const char **valout, uint16_t *opout)
630 {
631 	uint32_t p, xpn, xsn;
632 	asl_msg_t *page = NULL;
633 
634 	if (msg == NULL) return IndexNull;
635 
636 	xsn = x >> 24;
637 	xpn = x & 0x00ffffff;
638 
639 	/* slot number 0xff means we have run out entries */
640 	if (xsn == 0x000000ff) return IndexNull;
641 
642 	page = msg;
643 	for (p = 0; p < xpn; p++)
644 	{
645 		page = page->next;
646 		if (page == NULL) return IndexNull;
647 	}
648 
649 	if (keyout != NULL) *keyout = _asl_msg_slot_key(page, xsn);
650 	if (valout != NULL) *valout = _asl_msg_slot_val(page, xsn);
651 	if (opout != NULL) *opout = (uint32_t)(page->op[xsn]);
652 
653 	/* advance to the next slot */
654 	forever
655 	{
656 		xsn++;
657 
658 		if (xsn >= ASL_MSG_PAGE_SLOTS)
659 		{
660 			if (page->next == NULL) return 0xff000000;
661 			xsn = 0;
662 			page = page->next;
663 			xpn++;
664 		}
665 
666 		if (page->key[xsn] != ASL_MSG_SLOT_FREE) return ((xsn << 24) | xpn);
667 	}
668 
669 	return IndexNull;
670 }
671 
672 int
asl_msg_lookup(asl_msg_t * msg,const char * key,const char ** valout,uint16_t * opout)673 asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint16_t *opout)
674 {
675 	uint32_t i, slot;
676 	asl_msg_t *page;
677 
678 	if (msg == NULL) return -1;
679 	if (valout != NULL) *valout = NULL;
680 	if (opout != NULL) *opout = 0;
681 
682 	slot = IndexNull;
683 	page = NULL;
684 
685 	i = _asl_msg_index(msg, key, &slot, &page);
686 	if (i == IndexNull) return -1;
687 
688 	if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
689 	if (opout != NULL) *opout = (uint32_t)(page->op[slot]);
690 
691 	return 0;
692 }
693 
694 const char *
asl_msg_get_val_for_key(asl_msg_t * msg,const char * key)695 asl_msg_get_val_for_key(asl_msg_t *msg, const char *key)
696 {
697 	uint32_t slot;
698 	asl_msg_t *page;
699 
700 	if (msg == NULL) return NULL;
701 
702 	slot = IndexNull;
703 	page = NULL;
704 
705 	if (_asl_msg_index(msg, key, &slot, &page) == IndexNull) return NULL;
706 
707 	return _asl_msg_slot_val(page, slot);
708 }
709 
710 const char *
asl_msg_key(asl_msg_t * msg,uint32_t n)711 asl_msg_key(asl_msg_t *msg, uint32_t n)
712 {
713 	uint32_t slot, i;
714 	asl_msg_t *page;
715 
716 	if (msg == NULL) return NULL;
717 
718 	i = 0;
719 	for (page = msg; page != NULL; page = page->next)
720 	{
721 		for (slot = 0; slot < ASL_MSG_PAGE_SLOTS; slot++)
722 		{
723 			if (page->key[slot] != ASL_MSG_SLOT_FREE)
724 			{
725 				if (i == n) return _asl_msg_slot_key(page, slot);
726 				i++;
727 			}
728 		}
729 	}
730 
731 	return NULL;
732 }
733 
734 #pragma mark -
735 #pragma mark adding and replacing contents
736 
737 static int
_asl_msg_new_key_val_op(asl_msg_t * msg,const char * key,const char * val,uint32_t op)738 _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
739 {
740 	uint32_t slot, keylen, vallen, total;
741 	uint64_t klen, vlen;
742 	uint16_t kx;
743 	asl_msg_t *page, *last;
744 	char *extkey, *extval;
745 
746 	if (msg == NULL) return -1;
747 	if (key == NULL) return -1;
748 
749 	extkey = NULL;
750 	extval = NULL;
751 
752 	keylen = strlen(key);
753 	klen = keylen;
754 	kx = _asl_msg_std_key(key, keylen);
755 
756 	if (kx == 0) keylen++;
757 	else keylen = 0;
758 
759 	total = keylen;
760 
761 	vallen = 0;
762 	if (val != NULL)
763 	{
764 		vallen = strlen(val) + 1;
765 		vlen = vallen;
766 		total += vallen;
767 	}
768 
769 	/* check if one or both of key and value must be "external" (in its own malloced space) */
770 	if (keylen > ASL_MSG_PAGE_DATA_SIZE)
771 	{
772 		extkey = strdup(key);
773 		keylen = sizeof(char *);
774 	}
775 
776 	if (vallen > ASL_MSG_PAGE_DATA_SIZE)
777 	{
778 		extval = strdup(val);
779 		vallen = sizeof(char *);
780 	}
781 
782 	total = keylen + vallen;
783 	if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extval == NULL) && (keylen > 0))
784 	{
785 		extval = strdup(val);
786 		vallen = sizeof(char *);
787 		total = keylen + vallen;
788 	}
789 
790 	if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extkey == NULL))
791 	{
792 		extkey = strdup(key);
793 		keylen = sizeof(char *);
794 		total = keylen + vallen;
795 	}
796 
797 	if (total > ASL_MSG_PAGE_DATA_SIZE)
798 	{
799 		/* can't happen, but... */
800 		if (extkey != NULL) free(extkey);
801 		if (extval != NULL) free(extval);
802 		return -1;
803 	}
804 
805 	/* find a page with space for the key and value and a free slot*/
806 	slot = 0;
807 	last = msg;
808 
809 	for (page = msg; page != NULL; page = page->next)
810 	{
811 		last = page;
812 
813 		if (total <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
814 		{
815 			/* check for a free slot */
816 			for (slot = 0; (slot < ASL_MSG_PAGE_SLOTS) && (page->key[slot] != ASL_MSG_SLOT_FREE); slot++);
817 			if (slot < ASL_MSG_PAGE_SLOTS) break;
818 		}
819 	}
820 
821 	if (page == NULL)
822 	{
823 		/* allocate a new page and attach it */
824 		page = _asl_msg_make_page();
825 		if (page == NULL)
826 		{
827 			if (extkey != NULL) free(extkey);
828 			if (extval != NULL) free(extval);
829 			return -1;
830 		}
831 
832 		last->next = page;
833 		slot = 0;
834 	}
835 
836 	/* copy key or external key pointer into page data */
837 	if (kx != 0)
838 	{
839 		page->key[slot] = kx;
840 	}
841 	else if (extkey == NULL)
842 	{
843 		page->key[slot] = page->data_size;
844 		memcpy(page->data + page->data_size, key, keylen);
845 	}
846 	else
847 	{
848 		page->key[slot] = page->data_size | ASL_MSG_KV_EXTERN;
849 		memcpy(page->data + page->data_size, &extkey, keylen);
850 		page->mem_size += klen;
851 	}
852 
853 	page->data_size += keylen;
854 
855 	/* copy val or external val pointer into page data */
856 	page->val[slot] = ASL_MSG_SLOT_FREE;
857 
858 	if (val != NULL)
859 	{
860 		if (extval == NULL)
861 		{
862 			page->val[slot] = page->data_size;
863 			memcpy(page->data + page->data_size, val, vallen);
864 		}
865 		else
866 		{
867 			page->val[slot] = page->data_size | ASL_MSG_KV_EXTERN;
868 			memcpy(page->data + page->data_size, &extval, vallen);
869 			page->mem_size += vlen;
870 		}
871 
872 		page->data_size += vallen;
873 	}
874 
875 	/* set op */
876 	page->op[slot] = (uint16_t)op;
877 
878 	/* update page count */
879 	page->count++;
880 
881 	return 0;
882 }
883 
884 /*
885  * Most of the code in asl_msg_set_key_val_op is concerned with trying to re-use
886  * space in an asl_msg_t page when given a new value for an existing key.
887  * If the key is new, we just call _asl_msg_new_key_val_op.
888  *
889  * Note that queries can have duplicate keys, so for ASL_TYPE_QUERY we just
890  * call through to _asl_msg_new_key_val_op.
891  */
892 static int
_asl_msg_set_kvo(asl_msg_t * msg,const char * key,const char * val,uint32_t op)893 _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
894 {
895 	uint32_t i, slot, newexternal;
896 	asl_msg_t *page;
897 	uint32_t intvallen, extvallen, newvallen;
898 	char *intval, *extval, *newval;
899 
900 	if (msg == NULL) return -1;
901 	if (key == NULL) return -1;
902 
903 	slot = IndexNull;
904 	page = NULL;
905 
906 	if ((msg->asl_type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page)))
907 	{
908 		/* add key */
909 		return _asl_msg_new_key_val_op(msg, key, val, op);
910 	}
911 
912 	intval = NULL;
913 	intvallen = 0;
914 
915 	extval = NULL;
916 	extvallen = 0;
917 
918 	if (page->val[slot] != ASL_MSG_SLOT_FREE)
919 	{
920 		if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
921 		{
922 			i = page->val[slot] & ASL_MSG_OFFSET_MASK;
923 			memcpy(&extval, page->data + i, sizeof(char *));
924 			extvallen = sizeof(char *);
925 		}
926 		else
927 		{
928 			intval = page->data + page->val[slot];
929 			intvallen = strlen(intval) + 1;
930 		}
931 	}
932 
933 	/* replace val and op for existing entry */
934 
935 	/* easy case  - remove val */
936 	if (val == NULL)
937 	{
938 		if (extval != NULL)
939 		{
940 			page->mem_size -= (strlen(extval) + 1);
941 			free(extval);
942 		}
943 
944 		page->val[slot] = ASL_MSG_SLOT_FREE;
945 		if (op != IndexNull) page->op[slot] = (uint16_t)op;
946 		return 0;
947 	}
948 
949 	/* trivial case - internal val doesn't change */
950 	if ((intval != NULL) && (streq(val, intval)))
951 	{
952 		if (op != IndexNull) page->op[slot] = (uint16_t)op;
953 		return 0;
954 	}
955 
956 	/* trivial case - external val doesn't change */
957 	if ((extval != NULL) && (streq(val, extval)))
958 	{
959 		if (op != IndexNull) page->op[slot] = (uint16_t)op;
960 		return 0;
961 	}
962 
963 	/*
964 	 * special case: we generally don't compress out holes in the data
965 	 * space, but if this is the last string in the currently used data space
966 	 * we can just back up the data_size and reset page->val[slot]
967 	 */
968 	i = page->val[slot] & ASL_MSG_OFFSET_MASK;
969 	if ((intval != NULL) && ((i + intvallen) == page->data_size))
970 	{
971 		page->val[slot] = ASL_MSG_SLOT_FREE;
972 		page->data_size -= intvallen;
973 		intval = NULL;
974 		intvallen = 0;
975 	}
976 	else if ((extval != NULL) && ((i + extvallen) == page->data_size))
977 	{
978 		page->val[slot] = ASL_MSG_SLOT_FREE;
979 		page->data_size -= extvallen;
980 		page->mem_size -= (strlen(extval) + 1);
981 		free(extval);
982 		extval = NULL;
983 		extvallen = 0;
984 	}
985 
986 	/* val changes - see if it needs to be external */
987 	newvallen = strlen(val) + 1;
988 	newexternal = 0;
989 
990 	if (newvallen > ASL_MSG_PAGE_DATA_SIZE)
991 	{
992 		newexternal = 1;
993 		newvallen = sizeof(char *);
994 	}
995 
996 	/* check if there is room to change val in place */
997 	if (((extval != NULL) && (newvallen <= extvallen)) || ((extval == NULL) && (newvallen <= intvallen)))
998 	{
999 		if (extval != NULL)
1000 		{
1001 			page->mem_size -= (strlen(extval) + 1);
1002 			free(extval);
1003 		}
1004 
1005 		extval = NULL;
1006 
1007 		/* we can re-use the space of the old value */
1008 		i = page->val[slot] & ASL_MSG_OFFSET_MASK;
1009 
1010 		if (newexternal == 1)
1011 		{
1012 			/* create an external val and copy in the new pointer */
1013 			newval = strdup(val);
1014 			if (newval == NULL) return -1;
1015 
1016 			page->mem_size += (strlen(newval) + 1);
1017 			page->val[slot] = i | ASL_MSG_KV_EXTERN;
1018 			memcpy(page->data + i, &newval, sizeof(char *));
1019 		}
1020 		else
1021 		{
1022 			/* new internal value */
1023 			page->val[slot] = i;
1024 			memcpy(page->data + i, val, newvallen);
1025 		}
1026 
1027 		if (op != IndexNull) page->op[slot] = (uint16_t)op;
1028 		return 0;
1029 	}
1030 
1031 	/* we're done with the old value if it is external - free it now */
1032 	if (extval != NULL)
1033 	{
1034 		page->mem_size -= (strlen(extval) + 1);
1035 		free(extval);
1036 	}
1037 
1038 	extval = NULL;
1039 
1040 	if (newvallen <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
1041 	{
1042 		/* can't re-use the old space, but there's room on the page */
1043 		i = page->data_size;
1044 		page->data_size += newvallen;
1045 
1046 		if (newexternal == 1)
1047 		{
1048 			/* create an external val and copy in the new pointer */
1049 			newval = strdup(val);
1050 			if (newval == NULL) return -1;
1051 
1052 			page->mem_size += (strlen(newval) + 1);
1053 			page->val[slot] = i | ASL_MSG_KV_EXTERN;
1054 			memcpy(page->data + i, &newval, sizeof(char *));
1055 		}
1056 		else
1057 		{
1058 			/* new internal value */
1059 			page->val[slot] = i;
1060 			memcpy(page->data + i, val, newvallen);
1061 		}
1062 
1063 		if (op != IndexNull) page->op[slot] = (uint16_t)op;
1064 		return 0;
1065 
1066 	}
1067 
1068 	/* no room on this page - free up existing entry and treat this as a new entry */
1069 	if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1070 	{
1071 		memcpy(&extval, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1072 		page->mem_size -= (strlen(extval) + 1);
1073 		free(extval);
1074 	}
1075 
1076 	page->key[slot] = ASL_MSG_SLOT_FREE;
1077 	page->val[slot] = ASL_MSG_SLOT_FREE;
1078 	page->op[slot] = 0;
1079 
1080 	return _asl_msg_new_key_val_op(msg, key, val, op);
1081 }
1082 
1083 int
asl_msg_set_key_val_op(asl_msg_t * msg,const char * key,const char * val,uint32_t op)1084 asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
1085 {
1086 	char *special, buf[512];
1087 	uint32_t i, len;
1088 	int status;
1089 
1090 	/* Special case handling */
1091 	special = NULL;
1092 
1093 	/* if query modifier is set but op is zero, default to equality test */
1094 	if ((op != 0) && ((op & ASL_QUERY_OP_TRUE) == 0)) op |= ASL_QUERY_OP_EQUAL;
1095 
1096 	/* convert "Level" values to "0" through "7" */
1097 	if (streq(key, ASL_KEY_LEVEL))
1098 	{
1099 		if (val == NULL) val = "7";
1100 		else if ((val[0] >= '0') && (val[0] <= '7') && (val[1] == '\0')) /* do nothing */;
1101 		else if (strcaseeq(val, ASL_STRING_EMERG)) val = "0";
1102 		else if (strcaseeq(val, ASL_STRING_ALERT)) val = "1";
1103 		else if (strcaseeq(val, ASL_STRING_CRIT)) val = "2";
1104 		else if (strcaseeq(val, ASL_STRING_ERR)) val = "3";
1105 		else if (strcaseeq(val, ASL_STRING_WARNING)) val = "4";
1106 		else if (strcaseeq(val, ASL_STRING_NOTICE)) val = "5";
1107 		else if (strcaseeq(val, ASL_STRING_INFO)) val = "6";
1108 		else if (strcaseeq(val, ASL_STRING_DEBUG)) val = "7";
1109 		else val = "7";
1110 	}
1111 
1112 	/* strip trailing newlines from "Message" values */
1113 	if ((streq(key, ASL_KEY_MSG)) && (val != NULL))
1114 	{
1115 		len = strlen(val);
1116 		i = len;
1117 		while ((i > 0) && (val[i - 1] == '\n')) i--;
1118 		if (i == 0) val = NULL;
1119 		else if (i < len)
1120 		{
1121 			/* use buf if it is big enough, else malloc a temporary buffer */
1122 			if (i < sizeof(buf))
1123 			{
1124 				memcpy(buf, val, i);
1125 				buf[i] = 0;
1126 				val = (const char *)buf;
1127 			}
1128 			else
1129 			{
1130 				special = malloc(i + 1);
1131 				if (special == NULL) return -1;
1132 				memcpy(special, val, i);
1133 				special[i] = 0;
1134 				val = (const char *)special;
1135 			}
1136 		}
1137 	}
1138 
1139 	status = _asl_msg_set_kvo(msg, key, val, op);
1140 
1141 	if (special != NULL) free(special);
1142 	return status;
1143 }
1144 
1145 int
asl_msg_set_key_val(asl_msg_t * msg,const char * key,const char * val)1146 asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val)
1147 {
1148 	return asl_msg_set_key_val_op(msg, key, val, 0);
1149 }
1150 
1151 static void
_asl_msg_unset_page_slot(asl_msg_t * page,uint32_t slot)1152 _asl_msg_unset_page_slot(asl_msg_t *page, uint32_t slot)
1153 {
1154 	char *ext;
1155 
1156 	if (page == NULL) return;
1157 	if (slot >= ASL_MSG_PAGE_SLOTS) return;
1158 	if (page->key[slot] == ASL_MSG_SLOT_FREE) return;
1159 
1160 	if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1161 	{
1162 		memcpy(&ext, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1163 		page->mem_size -= (strlen(ext) + 1);
1164 		free(ext);
1165 	}
1166 
1167 	if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
1168 	{
1169 		memcpy(&ext, page->data + (page->val[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
1170 		page->mem_size -= (strlen(ext) + 1);
1171 		free(ext);
1172 	}
1173 
1174 	page->key[slot] = ASL_MSG_SLOT_FREE;
1175 	page->val[slot] = ASL_MSG_SLOT_FREE;
1176 	page->op[slot] = 0;
1177 
1178 	page->count--;
1179 }
1180 
1181 /*
1182  * asl_msg_unset
1183  * Frees external key and val strings, but does not try to reclaim data space.
1184  */
1185 void
asl_msg_unset(asl_msg_t * msg,const char * key)1186 asl_msg_unset(asl_msg_t *msg, const char *key)
1187 {
1188 	uint32_t i, slot;
1189 	asl_msg_t *page;
1190 
1191 	if (msg == NULL) return;
1192 	if (key == NULL) return;
1193 
1194 	slot = IndexNull;
1195 	page = NULL;
1196 
1197 	i = _asl_msg_index(msg, key, &slot, &page);
1198 	if (i == IndexNull) return;
1199 
1200 	_asl_msg_unset_page_slot(page, slot);
1201 }
1202 
1203 void
asl_msg_unset_index(asl_msg_t * msg,uint32_t n)1204 asl_msg_unset_index(asl_msg_t *msg, uint32_t n)
1205 {
1206 	uint32_t slot = IndexNull;
1207 	asl_msg_t *page = NULL;
1208 
1209 	if (msg == NULL) return;
1210 
1211 	if  (0 != _asl_msg_resolve_index(msg, n, &page, &slot)) return;
1212 	_asl_msg_unset_page_slot(page, slot);
1213 }
1214 
1215 #pragma mark -
1216 #pragma mark copy and merge
1217 
1218 /*
1219  * Merge a key / val into a message (only ASL_TYPE_MSG).
1220  * Adds the key / val if the key is not found.
1221  * Does not replace the value if the key is found.
1222  */
1223 static void
_asl_msg_merge_key_val_op(asl_msg_t * msg,const char * key,const char * val,uint16_t op)1224 _asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint16_t op)
1225 {
1226 	uint32_t i, slot;
1227 	asl_msg_t *page;
1228 
1229 	if (msg == NULL) return;
1230 	if (key == NULL) return;
1231 
1232 	slot = IndexNull;
1233 	page = NULL;
1234 
1235 	i = _asl_msg_index(msg, key, &slot, &page);
1236 	if (i != IndexNull) return;
1237 
1238 	asl_msg_set_key_val_op(msg, key, val, op);
1239 }
1240 
1241 /*
1242  * Merge msg into target (does not replace existing keys).
1243  * Creates a new asl_msg_t if target is NULL.
1244  * Returns target.
1245  */
1246 asl_msg_t *
asl_msg_merge(asl_msg_t * target,asl_msg_t * msg)1247 asl_msg_merge(asl_msg_t *target, asl_msg_t *msg)
1248 {
1249 	uint32_t x, type, isnew = 0;
1250 	uint16_t op;
1251 	const char *key, *val;
1252 
1253 	if (msg == NULL) return target;
1254 
1255 	type = asl_get_type((asl_object_t)msg);
1256 
1257 	if (target == NULL)
1258 	{
1259 		isnew = 1;
1260 		target = asl_msg_new(type);
1261 	}
1262 
1263 	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
1264 	{
1265 		if (type == ASL_TYPE_MSG) op = 0;
1266 		if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op);
1267 		else _asl_msg_merge_key_val_op(target, key, val, op);
1268 	}
1269 
1270 	return target;
1271 }
1272 
1273 /*
1274  * replace key/value pairs from msg in target
1275  * Creates a new asl_msg_t if target is NULL.
1276  * Returns target.
1277  */
1278 static asl_msg_t *
asl_msg_replace(asl_msg_t * target,asl_msg_t * msg)1279 asl_msg_replace(asl_msg_t *target, asl_msg_t *msg)
1280 {
1281 	uint32_t x, type;
1282 	uint16_t op;
1283 	const char *key, *val;
1284 
1285 	if (msg == NULL) return target;
1286 
1287 	type = asl_get_type((asl_object_t)msg);
1288 
1289 	if (target == NULL) target = asl_msg_new(type);
1290 
1291 	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x =asl_msg_fetch(msg, x, &key, &val, &op))
1292 	{
1293 		if (type == ASL_TYPE_MSG) op = 0;
1294 		asl_msg_set_key_val_op(target, key, val, op);
1295 	}
1296 
1297 	return target;
1298 }
1299 
1300 
1301 /*
1302  * Copy msg.
1303  */
1304 asl_msg_t *
asl_msg_copy(asl_msg_t * msg)1305 asl_msg_copy(asl_msg_t *msg)
1306 {
1307 	return asl_msg_merge(NULL, msg);
1308 }
1309 
1310 #pragma mark -
1311 #pragma mark compare and test
1312 
1313 /*
1314  * Compare messages
1315  */
1316 static int
_asl_msg_equal(asl_msg_t * a,asl_msg_t * b)1317 _asl_msg_equal(asl_msg_t *a, asl_msg_t *b)
1318 {
1319 	uint32_t x;
1320 	uint16_t oa, ob;
1321 	const char *key, *va, *vb;
1322 
1323 	if (asl_msg_count(a) != asl_msg_count(b)) return 0;
1324 
1325 	key = NULL;
1326 	va = NULL;
1327 	oa = 0;
1328 
1329 	for (x = asl_msg_fetch(a, 0, &key, &va, &oa); x != IndexNull; x = asl_msg_fetch(a, x, &key, &va, &oa))
1330 	{
1331 		if (asl_msg_lookup(b, key, &vb, &ob) != 0) return 0;
1332 		if (strcmp(va, vb)) return 0;
1333 		if ((a->asl_type == ASL_TYPE_QUERY) && (oa != ob)) return 0;
1334 	}
1335 
1336 	return 1;
1337 }
1338 
1339 static int
_asl_isanumber(const char * s)1340 _asl_isanumber(const char *s)
1341 {
1342 	int i;
1343 
1344 	if (s == NULL) return 0;
1345 
1346 	i = 0;
1347 	if ((s[0] == '-') || (s[0] == '+')) i = 1;
1348 
1349 	if (s[i] == '\0') return 0;
1350 
1351 	for (; s[i] != '\0'; i++)
1352 	{
1353 		if (!isdigit(s[i])) return 0;
1354 	}
1355 
1356 	return 1;
1357 }
1358 
1359 static int
_asl_msg_basic_test(uint32_t op,const char * q,const char * m,uint32_t n)1360 _asl_msg_basic_test(uint32_t op, const char *q, const char *m, uint32_t n)
1361 {
1362 	int cmp;
1363 	uint32_t t;
1364 	int64_t nq, nm;
1365 	int rflags;
1366 	regex_t rex;
1367 
1368 	t = op & ASL_QUERY_OP_TRUE;
1369 
1370 	/* NULL value from query or message string fails */
1371 	if ((q == NULL) || (m == NULL)) return (t & ASL_QUERY_OP_NOT_EQUAL);
1372 
1373 	if (op & ASL_QUERY_OP_REGEX)
1374 	{
1375 		/* greater than or less than make no sense in substring search */
1376 		if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1377 
1378 		memset(&rex, 0, sizeof(regex_t));
1379 
1380 		rflags = REG_EXTENDED | REG_NOSUB;
1381 		if (op & ASL_QUERY_OP_CASEFOLD) rflags |= REG_ICASE;
1382 
1383 		/* A bad reqular expression matches nothing */
1384 		if (regcomp(&rex, q, rflags) != 0) return (t & ASL_QUERY_OP_NOT_EQUAL);
1385 
1386 		cmp = regexec(&rex, m, 0, NULL, 0);
1387 		regfree(&rex);
1388 
1389 		if (t == ASL_QUERY_OP_NOT_EQUAL) return (cmp != 0);
1390 		return (cmp == 0);
1391 	}
1392 
1393 	if (op & ASL_QUERY_OP_NUMERIC)
1394 	{
1395 		if (_asl_isanumber(q) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1396 		if (_asl_isanumber(m) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1397 
1398 		nq = atoll(q);
1399 		nm = atoll(m);
1400 
1401 		switch (t)
1402 		{
1403 			case ASL_QUERY_OP_EQUAL: return (nm == nq);
1404 			case ASL_QUERY_OP_GREATER: return (nm > nq);
1405 			case ASL_QUERY_OP_GREATER_EQUAL: return (nm >= nq);
1406 			case ASL_QUERY_OP_LESS: return (nm < nq);
1407 			case ASL_QUERY_OP_LESS_EQUAL: return (nm <= nq);
1408 			case ASL_QUERY_OP_NOT_EQUAL: return (nm != nq);
1409 			default: return (t == ASL_QUERY_OP_NOT_EQUAL);
1410 		}
1411 	}
1412 
1413 	cmp = 0;
1414 	if (op & ASL_QUERY_OP_CASEFOLD)
1415 	{
1416 		if (n == 0) cmp = strcasecmp(m, q);
1417 		else cmp = strncasecmp(m, q, n);
1418 	}
1419 	else
1420 	{
1421 		if (n == 0) cmp = strcmp(m, q);
1422 		else cmp = strncmp(m, q, n);
1423 	}
1424 
1425 	switch (t)
1426 	{
1427 		case ASL_QUERY_OP_EQUAL: return (cmp == 0);
1428 		case ASL_QUERY_OP_GREATER: return (cmp > 0);
1429 		case ASL_QUERY_OP_GREATER_EQUAL: return (cmp >= 0);
1430 		case ASL_QUERY_OP_LESS: return (cmp < 0);
1431 		case ASL_QUERY_OP_LESS_EQUAL: return (cmp <= 0);
1432 		case ASL_QUERY_OP_NOT_EQUAL: return (cmp != 0);
1433 	}
1434 
1435 	return (t == ASL_QUERY_OP_NOT_EQUAL);
1436 }
1437 
1438 static int
_asl_msg_test_substring(uint32_t op,const char * q,const char * m)1439 _asl_msg_test_substring(uint32_t op, const char *q, const char *m)
1440 {
1441 	uint32_t t, i, d, lm, lq, match, newop;
1442 
1443 	t = op & ASL_QUERY_OP_TRUE;
1444 
1445 	lm = 0;
1446 	if (m != NULL) lm = strlen(m);
1447 
1448 	lq = 0;
1449 	if (q != NULL) lq = strlen(q);
1450 
1451 	/* NULL is a substring of any string */
1452 	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1453 
1454 	/* A long string is defined to be not equal to a short string */
1455 	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1456 
1457 	/* greater than or less than make no sense in substring search */
1458 	if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1459 
1460 	/*
1461 	 * We scan the string doing an equality test.
1462 	 * If the input test is equality, we stop as soon as we hit a match.
1463 	 * Otherwise we keep scanning the whole message string.
1464 	 */
1465 	newop = op & 0xff0;
1466 	newop |= ASL_QUERY_OP_EQUAL;
1467 
1468 	match = 0;
1469 	d = lm - lq;
1470 	for (i = 0; i <= d; i++)
1471 	{
1472 		if (_asl_msg_basic_test(newop, q, m + i, lq) != 0)
1473 		{
1474 			if (t & ASL_QUERY_OP_EQUAL) return 1;
1475 			match++;
1476 		}
1477 	}
1478 
1479 	/* If the input test was for equality, no matches were found */
1480 	if (t & ASL_QUERY_OP_EQUAL) return 0;
1481 
1482 	/* The input test was for not equal.  Return true if no matches were found */
1483 	return (match == 0);
1484 }
1485 
1486 static int
_asl_msg_test_prefix(uint32_t op,const char * q,const char * m)1487 _asl_msg_test_prefix(uint32_t op, const char *q, const char *m)
1488 {
1489 	uint32_t lm, lq, t;
1490 
1491 	t = op & ASL_QUERY_OP_TRUE;
1492 
1493 	lm = 0;
1494 	if (m != NULL) lm = strlen(m);
1495 
1496 	lq = 0;
1497 	if (q != NULL) lq = strlen(q);
1498 
1499 	/* NULL is a prefix of any string */
1500 	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1501 
1502 	/* A long string is defined to be not equal to a short string */
1503 	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1504 
1505 	/* Compare two equal-length strings */
1506 	return _asl_msg_basic_test(op, q, m, lq);
1507 }
1508 
1509 static int
_asl_msg_test_suffix(uint32_t op,const char * q,const char * m)1510 _asl_msg_test_suffix(uint32_t op, const char *q, const char *m)
1511 {
1512 	uint32_t lm, lq, d, t;
1513 
1514 	t = op & ASL_QUERY_OP_TRUE;
1515 
1516 	lm = 0;
1517 	if (m != NULL) lm = strlen(m);
1518 
1519 	lq = 0;
1520 	if (q != NULL) lq = strlen(q);
1521 
1522 	/* NULL is a suffix of any string */
1523 	if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1524 
1525 	/* A long string is defined to be not equal to a short string */
1526 	if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1527 
1528 	/* Compare two equal-length strings */
1529 	d = lm - lq;
1530 	return _asl_msg_basic_test(op, q, m + d, lq);
1531 }
1532 
1533 /*
1534  * Splits out prefix, suffix, and substring tests.
1535  * Sends the rest to _asl_msg_basic_test().
1536  */
1537 static int
_asl_msg_test_expression(uint32_t op,const char * q,const char * m)1538 _asl_msg_test_expression(uint32_t op, const char *q, const char *m)
1539 {
1540 	uint32_t t;
1541 
1542 	t = op & ASL_QUERY_OP_TRUE;
1543 	if (t == ASL_QUERY_OP_TRUE) return 1;
1544 
1545 	if (op & ASL_QUERY_OP_PREFIX)
1546 	{
1547 		if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_substring(op, q, m);
1548 		return _asl_msg_test_prefix(op, q, m);
1549 	}
1550 	if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_suffix(op, q, m);
1551 
1552 	return _asl_msg_basic_test(op, q, m, 0);
1553 }
1554 
1555 /*
1556  * Special case for comparing time values.
1557  * If both inputs are time strings, this compares the time
1558  * value in seconds.  Otherwise it just does normal matching.
1559  */
1560 static int
_asl_msg_test_time_expression(uint32_t op,const char * q,const char * m)1561 _asl_msg_test_time_expression(uint32_t op, const char *q, const char *m)
1562 {
1563 	time_t tq, tm;
1564 	uint32_t t;
1565 
1566 	if ((op & ASL_QUERY_OP_PREFIX) || (op & ASL_QUERY_OP_SUFFIX) || (op & ASL_QUERY_OP_REGEX)) return _asl_msg_test_expression(op, q, m);
1567 	if ((q == NULL) || (m == NULL)) return _asl_msg_test_expression(op, q, m);
1568 
1569 	tq = asl_core_parse_time(q, NULL);
1570 	if (tq < 0) return _asl_msg_test_expression(op, q, m);
1571 
1572 	tm = asl_core_parse_time(m, NULL);
1573 	if (tm < 0) return _asl_msg_test_expression(op, q, m);
1574 
1575 	t = op & ASL_QUERY_OP_TRUE;
1576 
1577 	switch (t)
1578 	{
1579 		case ASL_QUERY_OP_FALSE:
1580 		{
1581 			return 0;
1582 		}
1583 		case ASL_QUERY_OP_EQUAL:
1584 		{
1585 			if (tm == tq) return 1;
1586 			return 0;
1587 		}
1588 		case ASL_QUERY_OP_GREATER:
1589 		{
1590 			if (tm > tq) return 1;
1591 			return 0;
1592 		}
1593 		case ASL_QUERY_OP_GREATER_EQUAL:
1594 		{
1595 			if (tm >= tq) return 1;
1596 			return 0;
1597 		}
1598 		case ASL_QUERY_OP_LESS:
1599 		{
1600 			if (tm < tq) return 1;
1601 			return 0;
1602 		}
1603 		case ASL_QUERY_OP_LESS_EQUAL:
1604 		{
1605 			if (tm <= tq) return 1;
1606 			return 0;
1607 		}
1608 		case ASL_QUERY_OP_NOT_EQUAL:
1609 		{
1610 			if (tm != tq) return 1;
1611 			return 0;
1612 		}
1613 		case ASL_QUERY_OP_TRUE:
1614 		{
1615 			return 1;
1616 		}
1617 	}
1618 
1619 	/* NOTREACHED */
1620 	return 0;
1621 }
1622 
1623 /* test a query against a message */
1624 __private_extern__ int
_asl_msg_test(asl_msg_t * q,asl_msg_t * m)1625 _asl_msg_test(asl_msg_t *q, asl_msg_t *m)
1626 {
1627 	uint32_t i, t, x;
1628 	uint16_t op;
1629 	int cmp;
1630 	const char *kq, *vq, *vm;
1631 
1632 	/*
1633 	 * Check each simple expression (key op val) separately.
1634 	 * The query suceeds (returns 1) if all simple expressions
1635 	 * succeed (i.e. AND the simple expressions).
1636 	 */
1637 
1638 	kq = NULL;
1639 	vq = NULL;
1640 	op = 0;
1641 
1642 	for (x = asl_msg_fetch(q, 0, &kq, &vq, &op); x != IndexNull; x = asl_msg_fetch(q, x, &kq, &vq, &op))
1643 	{
1644 		/* Find query key in the message */
1645 		vm = NULL;
1646 		i = asl_msg_lookup(m, kq, &vm, NULL);
1647 
1648 		/* ASL_QUERY_OP_TRUE tests if key is present in the message */
1649 		t = op & ASL_QUERY_OP_TRUE;
1650 		if (t == ASL_QUERY_OP_TRUE)
1651 		{
1652 			if (i != 0) return 0;
1653 			continue;
1654 		}
1655 
1656 		/* ASL_QUERY_OP_FALSE tests if the key is NOT present in the message */
1657 		if (t == ASL_QUERY_OP_FALSE)
1658 		{
1659 			if (i == 0) return 0;
1660 			continue;
1661 		}
1662 
1663 		if (i != 0)
1664 		{
1665 			/* the message does NOT have query key - fail unless we are testing not equal */
1666 			if (t == ASL_QUERY_OP_NOT_EQUAL) continue;
1667 			return 0;
1668 		}
1669 
1670 		cmp = 1;
1671 		if (streq(kq, ASL_KEY_TIME))
1672 		{
1673 			cmp = _asl_msg_test_time_expression(op, vq, vm);
1674 		}
1675 		else
1676 		{
1677 			cmp = _asl_msg_test_expression(op, vq, vm);
1678 		}
1679 
1680 		if (cmp == 0) return 0;
1681 	}
1682 
1683 	return 1;
1684 }
1685 
1686 /* returns 1 if a and b match, 0 otherwise */
1687 int
asl_msg_cmp(asl_msg_t * a,asl_msg_t * b)1688 asl_msg_cmp(asl_msg_t *a, asl_msg_t *b)
1689 {
1690 
1691 	if (a == NULL) return 0;
1692 	if (b == NULL) return 0;
1693 
1694 	if (a->asl_type == b->asl_type) return _asl_msg_equal(a, b);
1695 	if (a->asl_type == ASL_TYPE_QUERY) return _asl_msg_test(a, b);
1696 	return _asl_msg_test(b, a);
1697 }
1698 
1699 /*
1700  * Test a message against a query list.
1701  * Returns 1 if msg matches any query in the list, 0 otherwise.
1702  * Returns 1 if the query list is NULL or empty.
1703  */
1704 int
asl_msg_cmp_list(asl_msg_t * msg,asl_msg_list_t * list)1705 asl_msg_cmp_list(asl_msg_t *msg, asl_msg_list_t *list)
1706 {
1707 	uint32_t i;
1708 
1709 	if (msg == NULL) return 0;
1710 	if (list == NULL) return 1;
1711 	if (list->count == 0) return 1;
1712 
1713 	for (i = 0; i < list->count; i++)
1714 	{
1715 		if (_asl_msg_test(list->msg[i], msg)) return 1;
1716 	}
1717 
1718 	return 0;
1719 }
1720 
1721 #pragma mark -
1722 #pragma mark string representation
1723 
1724 static char *
_asl_time_string(const char * infmt,const char * str,const char * nano)1725 _asl_time_string(const char *infmt, const char *str, const char *nano)
1726 {
1727 	time_t tick, off;
1728 	struct tm stm;
1729 	char *ltime, *out, *p, *q;
1730 	char ltbuf[32], nanobuf[16], fmt[32], zstr[8];
1731 	time_t zh, zm;
1732 	uint32_t subsec = 0;
1733 	bool neg;
1734 
1735 	out = NULL;
1736 	memset(zstr, 0, sizeof(zstr));
1737 	zh = 0;
1738 	zm = 0;
1739 	off = 0;
1740 	neg = false;
1741 
1742 	/*
1743 	 * The longest infmt string we understand is "-hh:mm.N" (8 chars), so
1744 	 * it is safe to ignore the input if it doesn't fit in the temp buffer.
1745 	 * The default is local time zone format.
1746 	 */
1747 	if (infmt == NULL)
1748 	{
1749 		snprintf(fmt, sizeof(fmt), "local");
1750 	}
1751 	else if (strlen(infmt) >= sizeof (fmt))
1752 	{
1753 		snprintf(fmt, sizeof(fmt), "local");
1754 	}
1755 	else
1756 	{
1757 		snprintf(fmt, sizeof(fmt), "%s", infmt);
1758 
1759 		p = fmt;
1760 		q = strchr(fmt, '.');
1761 		if (q != NULL)
1762 		{
1763 			*q = '\0';
1764 			q++;
1765 			if (q != '\0') subsec = atoi(q);
1766 		}
1767 	}
1768 
1769 	nanobuf[0] = '\0';
1770 
1771 	if (subsec > 0)
1772 	{
1773 		uint32_t nsec = 0;
1774 		if (nano != NULL) nsec = atoi(nano);
1775 		snprintf(nanobuf, sizeof(nanobuf), ".%09u", nsec);
1776 		if (subsec > 9) subsec = 9;
1777 		nanobuf[subsec + 1] = '\0';
1778 	}
1779 
1780 	tick = 0;
1781 	if (str != NULL) tick = asl_core_parse_time(str, NULL);
1782 
1783 	if ((!strcasecmp(fmt, "lcl")) || (!strcasecmp(fmt, "local")))
1784 	{
1785 		ltime = ctime_r(&tick, ltbuf);
1786 		if (ltime == NULL) return NULL;
1787 		ltime[19] = '\0';
1788 		asprintf(&out, "%s%s", ltime + 4, nanobuf);
1789 		return out;
1790 	}
1791 
1792 	if ((!strcasecmp(fmt, "jz")) || (!strcasecmp(fmt, "iso8601")) || (!strcasecmp(fmt, "iso8601e")))
1793 	{
1794 		char sep = ' ';
1795 		if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1796 
1797 		if (NULL == localtime_r(&tick, &stm)) return NULL;
1798 
1799 		off = stm.tm_gmtoff;
1800 		if ((neg = (off < 0))) off *= -1;
1801 		zh = off / 3600;
1802 		off %= 3600;
1803 		zm = off / 60;
1804 
1805 		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1806 		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1807 
1808 		asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1809 		return out;
1810 	}
1811 
1812 	if (!strcasecmp(fmt, "iso8601b"))
1813 	{
1814 		if (NULL == localtime_r(&tick, &stm)) return NULL;
1815 
1816 		off = stm.tm_gmtoff;
1817 		if ((neg = (off < 0))) off *= -1;
1818 		zh = off / 3600;
1819 		off %= 3600;
1820 		zm = off / 60;
1821 
1822 		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1823 		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1824 
1825 		asprintf(&out, "%d%02d%02dT%02d%02d%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1826 		return out;
1827 	}
1828 
1829 	if ((!strcasecmp(fmt, "sec")) || (!strcasecmp(fmt, "raw")))
1830 	{
1831 		asprintf(&out, "%lu%s", tick, nanobuf);
1832 		return out;
1833 	}
1834 
1835 	if (!strcasecmp(fmt, "j"))
1836 	{
1837 		if (NULL == localtime_r(&tick, &stm)) return NULL;
1838 		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1839 		return out;
1840 	}
1841 
1842 	if ((!strcasecmp(fmt, "utc")) || (!strcasecmp(fmt, "zulu")) || (!strcasecmp(fmt, "iso8601z")) || (!strcasecmp(fmt, "iso8601ez")))
1843 	{
1844 		char sep = ' ';
1845 		if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1846 
1847 		if (NULL == gmtime_r(&tick, &stm)) return NULL;
1848 		asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1849 		return out;
1850 	}
1851 
1852 	if (!strcasecmp(fmt, "iso8601bz"))
1853 	{
1854 		if (NULL == gmtime_r(&tick, &stm)) return NULL;
1855 		asprintf(&out, "%d%02d%02dT%02d%02d%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1856 		return out;
1857 	}
1858 
1859 	if ((fmt[1] == '\0') && (((fmt[0] >= 'a') && (fmt[0] <= 'z')) || ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))))
1860 	{
1861 		char z = fmt[0];
1862 		if (z >= 'a') z -= 32;
1863 
1864 		if (z == 'Z') off = 0;
1865 		else if ((z >= 'A') && (z <= 'I')) off = ((z - 'A') + 1) * SEC_PER_HOUR;
1866 		else if ((z >= 'K') && (z <= 'M')) off = (z - 'A') * SEC_PER_HOUR;
1867 		else if ((z >= 'N') && (z <= 'Y')) off = ('M' - z) * SEC_PER_HOUR;
1868 		else return NULL;
1869 	}
1870 	else
1871 	{
1872 		if (fmt[0] == '-') neg = true;
1873 		if ((*p == '-') || (*p == '+')) p++;
1874 		if ((*p) >= '0' && (*p <= '9'))
1875 		{
1876 			zh = atoi(p);
1877 
1878 			p = strchr(p, ':');
1879 			if (p != NULL) zm = atoi(p + 1);
1880 		}
1881 		else
1882 		{
1883 			return NULL;
1884 		}
1885 
1886 		off = (zh * 3600) + (zm * 60);
1887 		if (neg) off *= -1;
1888 
1889 		if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1890 		else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1891 	}
1892 
1893 
1894 	tick += off;
1895 
1896 	memset(&stm, 0, sizeof (struct tm));
1897 	if (NULL == gmtime_r(&tick, &stm)) return NULL;
1898 
1899 	if ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))
1900 	{
1901 		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0]);
1902 	}
1903 	else if ((fmt[0] >= 'a') && (fmt[0] <= 'z'))
1904 	{
1905 		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0] - 32);
1906 	}
1907 	else
1908 	{
1909 		asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1910 	}
1911 
1912 	return out;
1913 }
1914 
1915 
1916 /* called from asl_format_message and _asl_send_message */
1917 __private_extern__ asl_string_t *
asl_msg_to_string_raw(uint32_t encoding,asl_msg_t * msg,const char * tfmt)1918 asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt)
1919 {
1920 	uint32_t i, x, count;
1921 	const char *key, *val, *nano;
1922 	asl_string_t *str;
1923 
1924 	if (msg == NULL) return NULL;
1925 
1926 	count = asl_msg_count(msg);
1927 	if (count == 0) return NULL;
1928 
1929 	str = asl_string_new(encoding);
1930 	if (str == NULL) return NULL;
1931 
1932 	key = NULL;
1933 	val = NULL;
1934 	i = 0;
1935 
1936 	nano = NULL;
1937 	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
1938 
1939 	for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
1940 	{
1941 		if (key == NULL) continue;
1942 
1943 		if (i > 0) asl_string_append_char_no_encoding(str, ' ');
1944 
1945 		asl_string_append_char_no_encoding(str, '[');
1946 		asl_string_append_asl_key(str, key);
1947 
1948 		if (!strcmp(key, ASL_KEY_TIME))
1949 		{
1950 			char *vtime = NULL;
1951 			asl_string_append_char_no_encoding(str, ' ');
1952 
1953 			if (val != NULL) vtime = _asl_time_string(tfmt, val, nano);
1954 
1955 			if (vtime != NULL)
1956 			{
1957 				asl_string_append_no_encoding(str, vtime);
1958 				free(vtime);
1959 			}
1960 			else
1961 			{
1962 				asl_string_append_char_no_encoding(str, '0');
1963 			}
1964 		}
1965 		else if (val != NULL)
1966 		{
1967 			asl_string_append_char_no_encoding(str, ' ');
1968 			asl_string_append(str, val);
1969 		}
1970 
1971 		asl_string_append_char_no_encoding(str, ']');
1972 
1973 		i++;
1974 	}
1975 
1976 	return str;
1977 }
1978 
1979 asl_string_t *
asl_string_append_asl_msg(asl_string_t * str,asl_msg_t * msg)1980 asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg)
1981 {
1982 	const char *key, *val;
1983 	uint32_t i, x;
1984 	uint16_t op;
1985 
1986 	if (msg == NULL) return str;
1987 
1988 	if (msg->asl_type == ASL_TYPE_QUERY) asl_string_append(str, "Q ");
1989 
1990 	i = 0;
1991 	for (x = asl_msg_fetch(msg, 0, &key, &val, &op); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, &op))
1992 	{
1993 		if (i != 0)	asl_string_append_char_no_encoding(str, ' ');
1994 		i++;
1995 
1996 		asl_string_append_char_no_encoding(str, '[');
1997 
1998 		if (msg->asl_type == ASL_TYPE_QUERY)
1999 		{
2000 			asl_string_append_op(str, op);
2001 			asl_string_append_char_no_encoding(str, ' ');
2002 		}
2003 
2004 		asl_string_append_asl_key(str, key);
2005 
2006 		if (val != NULL)
2007 		{
2008 			asl_string_append_char_no_encoding(str, ' ');
2009 			asl_string_append(str, val);
2010 		}
2011 
2012 		asl_string_append_char_no_encoding(str, ']');
2013 	}
2014 
2015 	return str;
2016 }
2017 
2018 char *
asl_msg_to_string(asl_msg_t * msg,uint32_t * len)2019 asl_msg_to_string(asl_msg_t *msg, uint32_t *len)
2020 {
2021 	char *out;
2022 	asl_string_t *str = asl_string_new(ASL_ENCODE_ASL);
2023 	if (str == NULL) return NULL;
2024 
2025 	str = asl_string_append_asl_msg(str, msg);
2026 	*len = asl_string_length(str);
2027 	out = asl_string_release_return_bytes(str);
2028 	return out;
2029 }
2030 
2031 static uint32_t
_asl_msg_op_from_string(char * o)2032 _asl_msg_op_from_string(char *o)
2033 {
2034 	uint32_t op, i;
2035 
2036 	op = ASL_QUERY_OP_NULL;
2037 
2038 	if (o == NULL) return op;
2039 
2040 	for (i = 0; o[i] != '\0'; i++)
2041 	{
2042 		if (o[i] == '.') return ASL_QUERY_OP_NULL;
2043 		if (o[i] == 'C') op |= ASL_QUERY_OP_CASEFOLD;
2044 		if (o[i] == 'R') op |= ASL_QUERY_OP_REGEX;
2045 		if (o[i] == 'N') op |= ASL_QUERY_OP_NUMERIC;
2046 		if (o[i] == 'S') op |= ASL_QUERY_OP_SUBSTRING;
2047 		if (o[i] == 'A') op |= ASL_QUERY_OP_PREFIX;
2048 		if (o[i] == 'Z') op |= ASL_QUERY_OP_SUFFIX;
2049 		if (o[i] == '<') op |= ASL_QUERY_OP_LESS;
2050 		if (o[i] == '>') op |= ASL_QUERY_OP_GREATER;
2051 		if (o[i] == '=') op |= ASL_QUERY_OP_EQUAL;
2052 		if (o[i] == '!') op |= ASL_QUERY_OP_NOT_EQUAL;
2053 		if (o[i] == 'T') op |= ASL_QUERY_OP_TRUE;
2054 	}
2055 
2056 	return op;
2057 }
2058 
2059 static char *
_asl_msg_get_next_word(char ** p,uint32_t * tt,uint32_t spacedel)2060 _asl_msg_get_next_word(char **p, uint32_t *tt, uint32_t spacedel)
2061 {
2062 	char *str, *out, c, oval;
2063 	uint32_t i, len, n, outlen;
2064 
2065 	*tt = TOKEN_NULL;
2066 
2067 	if (p == NULL) return NULL;
2068 	if (*p == NULL) return NULL;
2069 	if (**p == '\0') return NULL;
2070 
2071 	/* skip one space if it's there (word separator) */
2072 	if (**p == ' ') (*p)++;
2073 
2074 	/* skip leading white space */
2075 	if (spacedel != 0)
2076 	{
2077 		while ((**p == ' ') || (**p == '\t')) (*p)++;
2078 	}
2079 
2080 	if (**p == '\0') return NULL;
2081 	if (**p == '\n') return NULL;
2082 
2083 	str = *p;
2084 
2085 	/* opening [ */
2086 	if (**p == '[')
2087 	{
2088 		*tt = TOKEN_OPEN;
2089 
2090 		(*p)++;
2091 		out = malloc(2);
2092 		if (out == NULL) return NULL;
2093 
2094 		out[0] = '[';
2095 		out[1] = '\0';
2096 		return out;
2097 	}
2098 
2099 	/* scan for token and calulate it's length (input and decoded output len) */
2100 	len = 0;
2101 	outlen = 0;
2102 
2103 	forever
2104 	{
2105 		c = str[len];
2106 
2107 		/* stop scanning when we hit a delimiter */
2108 		if (((spacedel != 0) && (c == ' ')) || (c == ']') || (c == '\0')) break;
2109 
2110 		if (c == '\\')
2111 		{
2112 			len++;
2113 			c = str[len];
2114 			if ((c == 'a') || (c == 'b') || (c == 't') || (c == 'n') || (c == 'v') || (c == 'f') || (c == 'r') || (c == 's') || (c == '[') || (c == '\\') || (c == ']'))
2115 			{
2116 			}
2117 			else if (c == '^')
2118 			{
2119 				if (str[++len] == '\0') return NULL;
2120 			}
2121 			else if (c == 'M')
2122 			{
2123 				if (str[++len] == '\0') return NULL;
2124 				if (str[++len] == '\0') return NULL;
2125 			}
2126 			else if ((c >= '0') && (c <= '3'))
2127 			{
2128 				if (str[++len] == '\0') return NULL;
2129 				if (str[++len] == '\0') return NULL;
2130 			}
2131 			else
2132 			{
2133 				return NULL;
2134 			}
2135 		}
2136 
2137 		len++;
2138 		outlen++;
2139 	}
2140 
2141 	(*p) += len;
2142 
2143 	if ((len == 0) && (**p == ']'))
2144 	{
2145 		*tt = TOKEN_CLOSE;
2146 		(*p)++;
2147 		out = malloc(2);
2148 		if (out == NULL) return NULL;
2149 
2150 		out[0] = ']';
2151 		out[1] = '\0';
2152 		return out;
2153 	}
2154 
2155 	*tt = TOKEN_INT;
2156 
2157 	out = malloc(outlen + 1);
2158 	if (out == NULL) return NULL;
2159 
2160 	n = 0;
2161 	for (i = 0; i < len; i++)
2162 	{
2163 		c = str[i];
2164 
2165 		if (c == '\\')
2166 		{
2167 			*tt = TOKEN_WORD;
2168 
2169 			i++;
2170 			c = str[i];
2171 			if (c == 'a')
2172 			{
2173 				out[n++] = '\a';
2174 			}
2175 			else if (c == 'b')
2176 			{
2177 				out[n++] = '\b';
2178 			}
2179 			else if (c == 't')
2180 			{
2181 				out[n++] = '\t';
2182 			}
2183 			else if (c == 'n')
2184 			{
2185 				out[n++] = '\n';
2186 			}
2187 			else if (c == 'v')
2188 			{
2189 				out[n++] = '\v';
2190 			}
2191 			else if (c == 'f')
2192 			{
2193 				out[n++] = '\f';
2194 			}
2195 			else if (c == 'r')
2196 			{
2197 				out[n++] = '\r';
2198 			}
2199 			else if (c == 's')
2200 			{
2201 				out[n++] = ' ';
2202 			}
2203 			else if (c == '[')
2204 			{
2205 				out[n++] = '[';
2206 			}
2207 			else if (c == '\\')
2208 			{
2209 				out[n++] = '\\';
2210 			}
2211 			else if (c == ']')
2212 			{
2213 				out[n++] = ']';
2214 			}
2215 			else if (c == '^')
2216 			{
2217 				i++;
2218 				if (str[i] == '?') out[n++] = 127;
2219 				else out[n++] = str[i] - 64;
2220 			}
2221 			else if (c == 'M')
2222 			{
2223 				i++;
2224 				c = str[i];
2225 				if (c == '^')
2226 				{
2227 					i++;
2228 					if (str[i] == '?') out[n++] = 255;
2229 					else out[n++] = str[i] + 64;
2230 				}
2231 				else if (c == '-')
2232 				{
2233 					i++;
2234 					out[n++] = str[i] + 128;
2235 				}
2236 				else
2237 				{
2238 					*tt = TOKEN_NULL;
2239 					free(out);
2240 					return NULL;
2241 				}
2242 
2243 			}
2244 			else if ((c >= '0') && (c <= '3'))
2245 			{
2246 				oval = (c - '0') * 64;
2247 
2248 				i++;
2249 				c = str[i];
2250 				if ((c < '0') || (c > '7'))
2251 				{
2252 					*tt = TOKEN_NULL;
2253 					free(out);
2254 					return NULL;
2255 				}
2256 
2257 				oval += ((c - '0') * 8);
2258 
2259 				i++;
2260 				c = str[i];
2261 				if ((c < '0') || (c > '7'))
2262 				{
2263 					*tt = TOKEN_NULL;
2264 					free(out);
2265 					return NULL;
2266 				}
2267 
2268 				oval += (c - '0');
2269 
2270 				out[n++] = oval;
2271 			}
2272 			else
2273 			{
2274 				*tt = TOKEN_NULL;
2275 				free(out);
2276 				return NULL;
2277 			}
2278 		}
2279 		else
2280 		{
2281 
2282 			if ((c < '0') || (c > '9')) *tt = TOKEN_WORD;
2283 			out[n++] = c;
2284 		}
2285 	}
2286 
2287 	out[n] = '\0';
2288 
2289 	return out;
2290 }
2291 
2292 asl_msg_t *
asl_msg_from_string(const char * buf)2293 asl_msg_from_string(const char *buf)
2294 {
2295 	uint32_t tt, type, op;
2296 	char *k, *v, *o, *p;
2297 	asl_msg_t *out;
2298 
2299 	if (buf == NULL) return NULL;
2300 
2301 	type = ASL_TYPE_MSG;
2302 	p = (char *)buf;
2303 
2304 	k = _asl_msg_get_next_word(&p, &tt, 1);
2305 	if (k == NULL) return NULL;
2306 
2307 	if (streq(k, "Q"))
2308 	{
2309 		type = ASL_TYPE_QUERY;
2310 		free(k);
2311 
2312 		k = _asl_msg_get_next_word(&p, &tt, 1);
2313 	}
2314 	else if (tt == TOKEN_INT)
2315 	{
2316 		/* Leading integer is a string length - skip it */
2317 		free(k);
2318 		k = _asl_msg_get_next_word(&p, &tt, 1);
2319 		if (k == NULL) return NULL;
2320 	}
2321 
2322 	out = asl_msg_new(ASL_TYPE_MSG);
2323 	if (out == NULL) return NULL;
2324 
2325 	out->asl_type = type;
2326 
2327 	/* OPEN WORD [WORD [WORD]] CLOSE */
2328 	while (k != NULL)
2329 	{
2330 		op = ASL_QUERY_OP_NULL;
2331 
2332 		if (tt != TOKEN_OPEN)
2333 		{
2334 			asl_msg_release(out);
2335 			return NULL;
2336 		}
2337 
2338 		free(k);
2339 
2340 		/* get op for query type */
2341 		if (type == ASL_TYPE_QUERY)
2342 		{
2343 			o = _asl_msg_get_next_word(&p, &tt, 1);
2344 			if ((o == NULL) || (tt != TOKEN_WORD))
2345 			{
2346 				if (o != NULL) free(o);
2347 				asl_msg_release(out);
2348 				return NULL;
2349 			}
2350 
2351 			op = _asl_msg_op_from_string(o);
2352 			free(o);
2353 		}
2354 
2355 		k = _asl_msg_get_next_word(&p, &tt, 1);
2356 		if (tt == TOKEN_INT) tt = TOKEN_WORD;
2357 		if ((k == NULL) || (tt != TOKEN_WORD))
2358 		{
2359 			if (k != NULL) free(k);
2360 			asl_msg_release(out);
2361 			return NULL;
2362 		}
2363 
2364 		v = _asl_msg_get_next_word(&p, &tt, 0);
2365 		if (tt == TOKEN_INT) tt = TOKEN_WORD;
2366 		if (v == NULL)
2367 		{
2368 			asl_msg_set_key_val_op(out, k, NULL, op);
2369 			free(k);
2370 			break;
2371 		}
2372 
2373 		if (tt == TOKEN_CLOSE)
2374 		{
2375 			asl_msg_set_key_val_op(out, k, NULL, op);
2376 		}
2377 		else if (tt == TOKEN_WORD)
2378 		{
2379 			asl_msg_set_key_val_op(out, k, v, op);
2380 		}
2381 		else
2382 		{
2383 			if (k != NULL) free(k);
2384 			if (v != NULL) free(v);
2385 			asl_msg_release(out);
2386 			return NULL;
2387 		}
2388 
2389 		if (k != NULL) free(k);
2390 		if (v != NULL) free(v);
2391 
2392 		if (tt != TOKEN_CLOSE)
2393 		{
2394 			k = _asl_msg_get_next_word(&p, &tt, 1);
2395 			if (k == NULL) break;
2396 
2397 			if (tt != TOKEN_CLOSE)
2398 			{
2399 				asl_msg_release(out);
2400 				return NULL;
2401 			}
2402 
2403 			free(k);
2404 		}
2405 
2406 		k = _asl_msg_get_next_word(&p, &tt, 1);
2407 		if (k == NULL) break;
2408 	}
2409 
2410 	return out;
2411 }
2412 
2413 static const char *
_asl_level_string(int level)2414 _asl_level_string(int level)
2415 {
2416 	if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG;
2417 	if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT;
2418 	if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT;
2419 	if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR;
2420 	if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING;
2421 	if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE;
2422 	if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO;
2423 	if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG;
2424 	return "unknown";
2425 }
2426 
2427 static const char *
_asl_level_char(int level)2428 _asl_level_char(int level)
2429 {
2430 	if (level == ASL_LEVEL_EMERG) return "P";
2431 	if (level == ASL_LEVEL_ALERT) return "A";
2432 	if (level == ASL_LEVEL_CRIT) return "C";
2433 	if (level == ASL_LEVEL_ERR) return "E";
2434 	if (level == ASL_LEVEL_WARNING) return "W";
2435 	if (level == ASL_LEVEL_NOTICE) return "N";
2436 	if (level == ASL_LEVEL_INFO) return "I";
2437 	if (level == ASL_LEVEL_DEBUG) return "D";
2438 	return "?";
2439 }
2440 
2441 /*
2442  * Find the value for a key in a message and append a formatted value to str.
2443  * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)).
2444  * WARNING: modifies kf!
2445  */
2446 static asl_string_t *
_asl_string_append_value_for_key_format(asl_string_t * str,asl_msg_t * msg,char * kf,const char * tfmt)2447 _asl_string_append_value_for_key_format(asl_string_t *str, asl_msg_t *msg, char *kf, const char *tfmt)
2448 {
2449 	uint32_t i, get_fmt;
2450 	int status;
2451 	char *key, *fmt;
2452 	const char *mval, *nano;
2453 
2454 	if (str == NULL) return NULL;
2455 	if (msg == NULL) return str;
2456 	if (kf == NULL) return str;
2457 
2458 	key = NULL;
2459 	fmt = NULL;
2460 	get_fmt = 0;
2461 
2462 	for (i = 0; kf[i] != '\0'; i++)
2463 	{
2464 		if (kf[i] == ')')
2465 		{
2466 			kf[i] = '\0';
2467 			get_fmt = 1;
2468 		}
2469 		else if (kf[i] != '(')
2470 		{
2471 			if (key == NULL) key = kf + i;
2472 			else if ((get_fmt == 1) && (fmt == NULL)) fmt = kf + i;
2473 		}
2474 	}
2475 
2476 	if (key == NULL) return str;
2477 
2478 	nano = NULL;
2479 	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2480 
2481 	status = asl_msg_lookup(msg, key, &mval, NULL);
2482 	if ((status != 0) || (mval == NULL)) return str;
2483 
2484 	if (!strcmp(key, ASL_KEY_TIME))
2485 	{
2486 		char *fval = NULL;
2487 
2488 		/* format in $((Time)(fmt)) overrides tfmt */
2489 		if (fmt == NULL)
2490 		{
2491 			fval = _asl_time_string(tfmt, mval, nano);
2492 		}
2493 		else
2494 		{
2495 			fval = _asl_time_string(fmt, mval, nano);
2496 		}
2497 
2498 		if (fval != NULL)
2499 		{
2500 			asl_string_append_no_encoding(str, fval);
2501 			free(fval);
2502 		}
2503 		else
2504 		{
2505 			asl_string_append_char_no_encoding(str, '0');
2506 		}
2507 
2508 		return str;
2509 	}
2510 
2511 	/* Level: num str */
2512 	if (!strcmp(key, ASL_KEY_LEVEL))
2513 	{
2514 		if (fmt == NULL)
2515 		{
2516 			asl_string_append_no_encoding(str, mval);
2517 		}
2518 		else if (!strcmp(fmt, "str"))
2519 		{
2520 			mval = _asl_level_string(atoi(mval));
2521 			asl_string_append_no_encoding(str, mval);
2522 		}
2523 		else if (!strcmp(fmt, "char"))
2524 		{
2525 			mval = _asl_level_char(atoi(mval));
2526 			asl_string_append_no_encoding(str, mval);
2527 		}
2528 		else
2529 		{
2530 			asl_string_append_no_encoding(str, mval);
2531 		}
2532 
2533 		return str;
2534 	}
2535 
2536 	return asl_string_append(str, mval);
2537 }
2538 
2539 /*
2540  * format a message for printing
2541  * out parameter len returns string length including trailing NUL
2542  */
2543 char *
asl_format_message(asl_msg_t * msg,const char * mfmt,const char * tfmt,uint32_t text_encoding,uint32_t * len)2544 asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t text_encoding, uint32_t *len)
2545 {
2546 	char *out, *vtime, *k, c, skey[512], tfmt_ext[16];
2547 	const char *vhost, *vpid, *vsender, *vmessage, *vlevel, *vrefproc, *vrefpid, *v, *key, *val, *nano;
2548 	int i, j, l, mf, paren, oval, level;
2549 	uint32_t x, cursor;
2550 	asl_string_t *str;
2551 	uint8_t *b64;
2552 
2553 	out = NULL;
2554 	*len = 0;
2555 
2556 	if (msg == NULL) return NULL;
2557 
2558 	mf = MFMT_RAW;
2559 
2560 	if (mfmt == NULL) mf = MFMT_RAW;
2561 	else if (!strcmp(mfmt, ASL_MSG_FMT_RAW)) mf = MFMT_RAW;
2562 	else if (!strcmp(mfmt, ASL_MSG_FMT_STD)) mf = MFMT_STD;
2563 	else if (!strcmp(mfmt, ASL_MSG_FMT_BSD)) mf = MFMT_BSD;
2564 	else if (!strcmp(mfmt, ASL_MSG_FMT_XML)) mf = MFMT_XML;
2565 	else if (!strcmp(mfmt, ASL_MSG_FMT_MSG)) mf = MFMT_MSG;
2566 	else if ((!strncmp(mfmt, ASL_MSG_FMT_RAW, 3)) && (mfmt[3] == '.'))
2567 	{
2568 		mf = MFMT_RAW;
2569 		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2570 		{
2571 			snprintf(tfmt_ext, sizeof(tfmt_ext), "sec.%s", mfmt + 4);
2572 			tfmt = (const char *)tfmt_ext;
2573 		}
2574 	}
2575 	else if ((!strncmp(mfmt, ASL_MSG_FMT_STD, 3)) && (mfmt[3] == '.'))
2576 	{
2577 		mf = MFMT_STD;
2578 		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2579 		{
2580 			snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2581 			tfmt = (const char *)tfmt_ext;
2582 		}
2583 	}
2584 	else if ((!strncmp(mfmt, ASL_MSG_FMT_BSD, 3)) && (mfmt[3] == '.'))
2585 	{
2586 		mf = MFMT_BSD;
2587 		if ((tfmt == NULL) && (mfmt[4] != '\0'))
2588 		{
2589 			snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2590 			tfmt = (const char *)tfmt_ext;
2591 		}
2592 	}
2593 	else mf = MFMT_STR;
2594 
2595 	nano = NULL;
2596 	asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2597 
2598 	if (mf == MFMT_RAW)
2599 	{
2600 		str = asl_msg_to_string_raw(text_encoding, msg, tfmt);
2601 		asl_string_append_char_no_encoding(str, '\n');
2602 
2603 		*len = asl_string_length(str);
2604 		out = asl_string_release_return_bytes(str);
2605 		return out;
2606 	}
2607 
2608 	if (mf == MFMT_MSG)
2609 	{
2610 		vmessage = NULL;
2611 
2612 		if (asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL) != 0) return NULL;
2613 
2614 		str = asl_string_new(text_encoding);
2615 		if (str == NULL) return NULL;
2616 
2617 		asl_string_append(str, vmessage);
2618 		asl_string_append_char_no_encoding(str, '\n');
2619 
2620 		*len = asl_string_length(str);
2621 		out = asl_string_release_return_bytes(str);
2622 		return out;
2623 	}
2624 
2625 	if ((mf == MFMT_STD) || (mf == MFMT_BSD))
2626 	{
2627 		/* COMMON:  Mth dd hh:mm:ss host sender[pid] (refproc[refpid])*/
2628 		/* BSD:  <COMMON>: message */
2629 		/* STD:  <COMMON> <Level>: message */
2630 
2631 		v = NULL;
2632 		vtime = NULL;
2633 		vhost = NULL;
2634 		vsender = NULL;
2635 		vpid = NULL;
2636 		vmessage = NULL;
2637 		vlevel = NULL;
2638 		vrefproc = NULL;
2639 		vrefpid = NULL;
2640 
2641 		if (asl_msg_lookup(msg, ASL_KEY_TIME, &v, NULL) == 0)
2642 		{
2643 			vtime = _asl_time_string(tfmt, v, nano);
2644 		}
2645 
2646 		level = 7;
2647 		if (asl_msg_lookup(msg, ASL_KEY_LEVEL, &vlevel, NULL) == 0)
2648 		{
2649 			if (vlevel != NULL) level = atoi(vlevel);
2650 		}
2651 
2652 		if (asl_msg_lookup(msg, ASL_KEY_HOST, &vhost, NULL) == 0)
2653 		{
2654 			if (vhost == NULL) vhost = "unknown";
2655 		}
2656 
2657 		if (asl_msg_lookup(msg, ASL_KEY_SENDER, &vsender, NULL) == 0)
2658 		{
2659 			if (vsender == NULL) vsender = "unknown";
2660 		}
2661 
2662 		asl_msg_lookup(msg, ASL_KEY_PID, &vpid, NULL);
2663 		asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL);
2664 		asl_msg_lookup(msg, ASL_KEY_REF_PROC, &vrefproc, NULL);
2665 		asl_msg_lookup(msg, ASL_KEY_REF_PID, &vrefpid, NULL);
2666 
2667 		/* COMMON */
2668 		str = asl_string_new(text_encoding);
2669 		if (str == NULL) return NULL;
2670 
2671 		if (vtime != NULL)
2672 		{
2673 			asl_string_append(str, vtime);
2674 			free(vtime);
2675 		}
2676 		else
2677 		{
2678 			asl_string_append_char_no_encoding(str, '0');
2679 		}
2680 
2681 		asl_string_append_char_no_encoding(str, ' ');
2682 		asl_string_append(str, vhost);
2683 		asl_string_append_char_no_encoding(str, ' ');
2684 		asl_string_append(str, vsender);
2685 
2686 		if ((vpid != NULL) && (strcmp(vpid, "-1")))
2687 		{
2688 			asl_string_append_char_no_encoding(str, '[');
2689 			asl_string_append(str, vpid);
2690 			asl_string_append_char_no_encoding(str, ']');
2691 		}
2692 
2693 		if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_no_encoding(str, " (");
2694 
2695 		if (vrefproc != NULL) asl_string_append(str, vrefproc);
2696 		if (vrefpid != NULL)
2697 		{
2698 			asl_string_append_char_no_encoding(str, '[');
2699 			asl_string_append(str, vrefpid);
2700 			asl_string_append_char_no_encoding(str, ']');
2701 		}
2702 
2703 		if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_char_no_encoding(str, ')');
2704 
2705 		if (mf == MFMT_STD)
2706 		{
2707 			asl_string_append_no_encoding(str, " <");
2708 			asl_string_append(str, _asl_level_string(level));
2709 			asl_string_append_char_no_encoding(str, '>');
2710 		}
2711 
2712 		asl_string_append_no_encoding(str, ": ");
2713 		if (vmessage != NULL) asl_string_append(str, vmessage);
2714 		asl_string_append_char_no_encoding(str, '\n');
2715 
2716 		*len = asl_string_length(str);
2717 		out = asl_string_release_return_bytes(str);
2718 		return out;
2719 	}
2720 
2721 	if (mf == MFMT_XML)
2722 	{
2723 		str = asl_string_new(text_encoding);
2724 		if (str == NULL) return NULL;
2725 
2726 		asl_string_append_char_no_encoding(str, '\t');
2727 		asl_string_append_no_encoding(str, "<dict>");
2728 		asl_string_append_char_no_encoding(str, '\n');
2729 
2730 		for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
2731 		{
2732 			if (asl_is_utf8(key) == 1)
2733 			{
2734 				asl_string_append_xml_tag(str, "key", key);
2735 				if (!strcmp(key, ASL_KEY_TIME))
2736 				{
2737 					vtime = _asl_time_string(tfmt, val, nano);
2738 					if (vtime != NULL)
2739 					{
2740 						asl_string_append_xml_tag(str, "string", vtime);
2741 						free(vtime);
2742 					}
2743 					else
2744 					{
2745 						asl_string_append_xml_tag(str, "string", "0");
2746 					}
2747 				}
2748 				else
2749 				{
2750 					if (asl_is_utf8(val) == 1) asl_string_append_xml_tag(str, "string", val);
2751 					else
2752 					{
2753 						b64 = asl_b64_encode((uint8_t *)val, strlen(val));
2754 						asl_string_append_xml_tag(str, "data", (char *)b64);
2755 						free(b64);
2756 					}
2757 				}
2758 			}
2759 		}
2760 
2761 		asl_string_append_char_no_encoding(str, '\t');
2762 		asl_string_append_no_encoding(str, "</dict>");
2763 		asl_string_append_char_no_encoding(str, '\n');
2764 
2765 		*len = asl_string_length(str);
2766 		out = asl_string_release_return_bytes(str);
2767 		return out;
2768 	}
2769 
2770 	/*
2771 	 * Custom format
2772 	 * The format string may contain arbitrary characters.
2773 	 * Keys are identified by $Key or $(Key).  The value for
2774 	 * that key is substituted.  If there are alterate formats
2775 	 * for the value (for example a time may be formatted as
2776 	 * raw seconds, in UTC, or a local timezone), then the
2777 	 * key may be $((Key)(Format)).  "\$" prints a plain "$".
2778 	 */
2779 
2780 	str = asl_string_new(text_encoding);
2781 	if (str == NULL) return NULL;
2782 
2783 	/*
2784 	 * We need enough space to copy any keys found in mfmt.
2785 	 * The key obviously can't be longer than strlen(mfmt),
2786 	 * in fact, keys must be shorter, since there's at least a '$'
2787 	 * in front of the key, so we allocate a buffer with strlen(mfmt).
2788 	 * If strlen(mfmt) <= sizeof(skey), we use skey to avoid a malloc.
2789 	 */
2790 
2791 	x = strlen(mfmt);
2792 	if (x <= sizeof(skey))
2793 	{
2794 		k = skey;
2795 	}
2796 	else
2797 	{
2798 		k = malloc(x);
2799 		if (k == NULL) return NULL;
2800 	}
2801 
2802 	cursor = 0;
2803 
2804 	for (i = 0; mfmt[i] != '\0'; i++)
2805 	{
2806 		if (mfmt[i] == '$')
2807 		{
2808 			paren = 0;
2809 
2810 			/* scan key, (key) or ((key)(format)) */
2811 			for (j = i + 1; mfmt[j] != 0; j++)
2812 			{
2813 				if (mfmt[j] == '(')
2814 				{
2815 					paren++;
2816 				}
2817 				else if (mfmt[j] == ')')
2818 				{
2819 					if (paren > 0) paren--;
2820 					if (paren == 0)
2821 					{
2822 						j++;
2823 						break;
2824 					}
2825 				}
2826 				else if (((mfmt[j] == ' ') || (mfmt[j] == '\t')) && (paren == 0)) break;
2827 			}
2828 
2829 			/* mfmt[i + 1] is the first char of the key or a '('. mfmt[j] is one char past the end. */
2830 			l = j - (i + 1);
2831 			memcpy(k, mfmt+i+1, l);
2832 			k[l] = '\0';
2833 			_asl_string_append_value_for_key_format(str, msg, k, tfmt);
2834 
2835 			i = j - 1;
2836 			continue;
2837 		}
2838 
2839 		if (mfmt[i] == '\\')
2840 		{
2841 			i++;
2842 			if (mfmt[i] == '$') asl_string_append_char_no_encoding(str, '$');
2843 			else if (mfmt[i] == 'e') asl_string_append_char_no_encoding(str, '\e');
2844 			else if (mfmt[i] == 's') asl_string_append_char_no_encoding(str, ' ');
2845 			else if (mfmt[i] == 'a') asl_string_append_char_no_encoding(str, '\a');
2846 			else if (mfmt[i] == 'b') asl_string_append_char_no_encoding(str, '\b');
2847 			else if (mfmt[i] == 'f') asl_string_append_char_no_encoding(str, '\f');
2848 			else if (mfmt[i] == 'n') asl_string_append_char_no_encoding(str, '\n');
2849 			else if (mfmt[i] == 'r') asl_string_append_char_no_encoding(str, '\r');
2850 			else if (mfmt[i] == 't') asl_string_append_char_no_encoding(str, '\t');
2851 			else if (mfmt[i] == 'v') asl_string_append_char_no_encoding(str, '\v');
2852 			else if (mfmt[i] == '\'') asl_string_append_char_no_encoding(str, '\'');
2853 			else if (mfmt[i] == '\\') asl_string_append_char_no_encoding(str, '\\');
2854 			else if (isdigit(mfmt[i]))
2855 			{
2856 				oval = mfmt[i] - '0';
2857 				if (isdigit(mfmt[i+1]))
2858 				{
2859 					i++;
2860 					oval = (oval * 8) + (mfmt[i] - '0');
2861 					if (isdigit(mfmt[i+1]))
2862 					{
2863 						i++;
2864 						oval = (oval * 8) + (mfmt[i] - '0');
2865 					}
2866 				}
2867 				c = oval;
2868 				asl_string_append_char_no_encoding(str, c);
2869 			}
2870 			continue;
2871 		}
2872 
2873 		if (mfmt[i] == '\0') break;
2874 		asl_string_append_char_no_encoding(str, mfmt[i]);
2875 	}
2876 
2877 	if (k != skey) free(k);
2878 
2879 	asl_string_append_char_no_encoding(str, '\n');
2880 
2881 	*len = asl_string_length(str);
2882 	out = asl_string_release_return_bytes(str);
2883 	return out;
2884 }
2885 
2886 #pragma mark -
2887 #pragma mark asl_object support
2888 
2889 static asl_object_private_t *
_jump_alloc(uint32_t type)2890 _jump_alloc(uint32_t type)
2891 {
2892 	return (asl_object_private_t *)asl_msg_new(type);
2893 }
2894 
2895 static void
_jump_dealloc(asl_object_private_t * obj)2896 _jump_dealloc(asl_object_private_t *obj)
2897 {
2898 	asl_msg_t *msg = (asl_msg_t *)obj;
2899 	while (msg != NULL)
2900 	{
2901 		asl_msg_t *next = msg->next;
2902 		_asl_msg_free_page(msg);
2903 		msg = next;
2904 	}
2905 }
2906 
2907 static int
_jump_set_key_val_op(asl_object_private_t * obj,const char * key,const char * val,uint16_t op)2908 _jump_set_key_val_op(asl_object_private_t *obj, const char *key, const char *val, uint16_t op)
2909 {
2910 	uint32_t op32 = op;
2911 	int status = asl_msg_set_key_val_op((asl_msg_t *)obj, key, val, op32);
2912 	return (status == ASL_STATUS_OK) ? 0 : -1;
2913 }
2914 
2915 static void
_jump_unset_key(asl_object_private_t * obj,const char * key)2916 _jump_unset_key(asl_object_private_t *obj, const char *key)
2917 {
2918 	asl_msg_unset((asl_msg_t *)obj, key);
2919 }
2920 
2921 static int
_jump_get_val_op_for_key(asl_object_private_t * obj,const char * key,const char ** val,uint16_t * op)2922 _jump_get_val_op_for_key(asl_object_private_t *obj, const char *key, const char **val, uint16_t *op)
2923 {
2924 	return asl_msg_lookup((asl_msg_t *)obj, key, val, op);
2925 }
2926 
2927 static int
_jump_get_key_val_op_at_index(asl_object_private_t * obj,size_t n,const char ** key,const char ** val,uint16_t * op)2928 _jump_get_key_val_op_at_index(asl_object_private_t *obj, size_t n, const char **key, const char **val, uint16_t *op)
2929 {
2930 	uint32_t slot = IndexNull;
2931 	asl_msg_t *page = NULL;
2932 
2933 	if  (0 != _asl_msg_resolve_index((asl_msg_t *)obj, n, &page, &slot)) return -1;
2934 
2935 	if (key != NULL) *key = _asl_msg_slot_key(page, slot);
2936 	if (val != NULL) *val = _asl_msg_slot_val(page, slot);
2937 	if (op != NULL) *op = page->op[slot];
2938 	return 0;
2939 }
2940 
2941 static size_t
_jump_count(asl_object_private_t * obj)2942 _jump_count(asl_object_private_t *obj)
2943 {
2944 	size_t count = asl_msg_count((asl_msg_t *)obj);
2945 	return count;
2946 }
2947 
2948 static void
_jump_append(asl_object_private_t * obj,asl_object_private_t * newobj)2949 _jump_append(asl_object_private_t *obj, asl_object_private_t *newobj)
2950 {
2951 	int type = asl_get_type((asl_object_t)newobj);
2952 	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
2953 
2954 	asl_msg_merge((asl_msg_t *)obj, (asl_msg_t *)newobj);
2955 }
2956 
2957 static void
_jump_prepend(asl_object_private_t * obj,asl_object_private_t * newobj)2958 _jump_prepend(asl_object_private_t *obj, asl_object_private_t *newobj)
2959 {
2960 	if (obj == NULL) return;
2961 
2962 	int type = asl_get_type((asl_object_t)newobj);
2963 	if ((type != ASL_TYPE_QUERY) && (type != ASL_TYPE_MSG)) return;
2964 
2965 	asl_msg_replace((asl_msg_t *)obj, (asl_msg_t *)newobj);
2966 }
2967 
2968 static asl_object_private_t *
_jump_search(asl_object_private_t * obj,asl_object_private_t * query)2969 _jump_search(asl_object_private_t *obj, asl_object_private_t *query)
2970 {
2971 	if (obj == NULL) return NULL;
2972 
2973 	if (query == NULL)
2974 	{
2975 		/* NULL matches any message */
2976 		asl_msg_list_t *out = asl_msg_list_new();
2977 		asl_msg_list_append(out, obj);
2978 		return (asl_object_private_t *)out;
2979 	}
2980 
2981 	if ((query->asl_type != ASL_TYPE_MSG) && (query->asl_type != ASL_TYPE_QUERY)) return NULL;
2982 
2983 	if (asl_msg_cmp((asl_msg_t *)obj, (asl_msg_t *)query) == 1)
2984 	{
2985 		asl_msg_list_t *out = asl_msg_list_new();
2986 		asl_msg_list_append(out, obj);
2987 		return (asl_object_private_t *)out;
2988 	}
2989 
2990 	return NULL;
2991 }
2992 
2993 static asl_object_private_t *
_jump_match(asl_object_private_t * obj,asl_object_private_t * qlist,size_t * last,size_t start,size_t count,uint32_t duration,int32_t dir)2994 _jump_match(asl_object_private_t *obj, asl_object_private_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t dir)
2995 {
2996 
2997 	(void)last;
2998 	(void)start;
2999 	(void)count;
3000 	(void)duration;
3001 	(void)dir;
3002 
3003 	if (obj == NULL) return NULL;
3004 
3005 	if (qlist == NULL)
3006 	{
3007 		/* NULL matches any message */
3008 		asl_msg_list_t *out = asl_msg_list_new();
3009 		asl_msg_list_append(out, obj);
3010 		return (asl_object_private_t *)out;
3011 	}
3012 
3013 	if (asl_msg_cmp_list((asl_msg_t *)obj, (asl_msg_list_t *)qlist) == 0) return NULL;
3014 
3015 	asl_msg_list_t *out = asl_msg_list_new();
3016 	asl_msg_list_append(out, obj);
3017 	return (asl_object_private_t *)out;
3018 }
3019 
3020 
3021 __private_extern__ const asl_jump_table_t *
asl_msg_jump_table()3022 asl_msg_jump_table()
3023 {
3024 	static const asl_jump_table_t jump =
3025 	{
3026 		.alloc = &_jump_alloc,
3027 		.dealloc = &_jump_dealloc,
3028 		.set_key_val_op = &_jump_set_key_val_op,
3029 		.unset_key = &_jump_unset_key,
3030 		.get_val_op_for_key = &_jump_get_val_op_for_key,
3031 		.get_key_val_op_at_index = &_jump_get_key_val_op_at_index,
3032 		.count = &_jump_count,
3033 		.next = NULL,
3034 		.prev = NULL,
3035 		.get_object_at_index = NULL,
3036 		.set_iteration_index = NULL,
3037 		.remove_object_at_index = NULL,
3038 		.append = &_jump_append,
3039 		.prepend = &_jump_prepend,
3040 		.search = &_jump_search,
3041 		.match = &_jump_match
3042 	};
3043 
3044 	return &jump;
3045 }
3046