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