AmendHub

Download

jcs

/

subtext

/

logger.c

 

(View History)

jcs   logger: Check vnsprintf untruncated length to maybe truncate Latest amendment: 639 on 2026-09-22

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include "focusable.h"
21 #include "logger.h"
22 #include "subtext.h"
23 #include "tcp.h"
24 #include "user.h"
25 #include "util.h"
26
27 struct logger *logger = NULL;
28
29 static UDPiopb syslog_pb;
30 static StreamPtr syslog_stream = 0;
31 static char *syslog_rcv_buf = NULL;
32 #define SYSLOG_RCV_BUF_SIZE 2048
33 static wdsEntry syslog_wds[2];
34 static char *syslog_send_buf = NULL;
35 #define SYSLOG_SEND_BUF_SIZE 1024
36
37 void logger_layout(Rect *init_bounds);
38 void logger_key_down(struct focusable *focusable, EventRecord *event);
39 void logger_mouse_down(struct focusable *focusable, EventRecord *event);
40 bool logger_menu(struct focusable *focusable, short menu, short item);
41 void logger_resize(struct focusable *focusable, EventRecord *event);
42 void logger_update(struct focusable *focusable, EventRecord *event);
43 void logger_resume(struct focusable *focusable, EventRecord *event);
44 void logger_close(struct focusable *focusable, EventRecord *event);
45 bool logger_quit(struct focusable *focusable);
46 void logger_flush_buffer(bool force);
47
48 void syslog_vprintf(const char *format, va_list ap);
49
50 void
51 logger_init(void)
52 {
53 struct focusable *focusable;
54 Rect bounds = { 0 };
55 short padding = 5;
56
57 logger = xmalloczero(sizeof(struct logger));
58 if (!logger)
59 panic("Can't allocate logger");
60 logger->buffered_logs = xmalloc(LOGGER_BUFFERED_LOG_SIZE);
61 if (!logger->buffered_logs)
62 panic("Can't allocate logger buffer");
63 logger->buffered_logs[0] = '\0';
64 logger->autoflush = true;
65
66 bounds.left = padding;
67 bounds.top = ((screenBits.bounds.bottom -
68 screenBits.bounds.top) / 3);
69 bounds.right = screenBits.bounds.right - padding - 1;
70 bounds.bottom = screenBits.bounds.bottom - padding - 1;
71
72 logger->win = NewWindow(0L, &bounds, "\p", false,
73 documentProc, (WindowPtr)-1L, true, 0);
74 if (!logger->win)
75 panic("Can't create logger window");
76 logger_update_title();
77
78 SetPort(logger->win);
79
80 TextFont(LOGGER_FONT);
81 TextSize(LOGGER_FONT_SIZE);
82
83 bounds.right -= bounds.left;
84 bounds.bottom -= bounds.top;
85 bounds.top = bounds.left = 0;
86 logger_layout(&bounds);
87
88 focusable = xmalloczero(sizeof(struct focusable));
89 if (!focusable)
90 panic("Can't create focusable");
91 focusable->win = logger->win;
92 focusable->cookie = logger;
93 focusable->mouse_down = logger_mouse_down;
94 focusable->menu = logger_menu;
95 focusable->resize = logger_resize;
96 focusable->update = logger_update;
97 focusable->close = logger_close;
98 focusable->quit = logger_quit;
99 focusable->resume = logger_resume;
100 if (!add_focusable(focusable))
101 panic("Can't add focusable");
102 logger->focusable = focusable;
103
104 DrawControls(logger->win);
105 DrawGrowIconOnly(logger->win);
106 }
107
108 void
109 logger_layout(Rect *init_bounds)
110 {
111 Rect bounds, inset_bounds, win_bounds;
112 bool init = (init_bounds != NULL);
113
114 if (init)
115 win_bounds = *init_bounds;
116 else
117 win_bounds = logger->win->portRect;
118
119 /* messages scrollbar */
120 bounds.top = -1;
121 bounds.right = win_bounds.right - win_bounds.left + 1;
122 bounds.bottom = win_bounds.bottom - win_bounds.top - 14;
123 bounds.left = bounds.right - SCROLLBAR_WIDTH;
124 if (init) {
125 logger->messages_scroller = NewControl(logger->win, &bounds,
126 "\p", true, 1, 1, 1, scrollBarProc, 0L);
127 if (!logger->messages_scroller)
128 panic("Can't create messages scroller");
129 } else
130 (*(logger->messages_scroller))->contrlRect = bounds;
131
132 /* messages */
133 bounds.right = (*(logger->messages_scroller))->contrlRect.left;
134 bounds.left = 0;
135 bounds.top = 0;
136 bounds.bottom = win_bounds.bottom - win_bounds.top;
137 if (init) {
138 inset_bounds = bounds;
139 InsetRect(&inset_bounds, 4, 4);
140 logger->messages_te = TEStylNew(&inset_bounds, &bounds);
141 if (logger->messages_te == NULL)
142 panic("Can't create logger TE");
143 (*(logger->messages_te))->caretHook = NullCaretHook;
144 TEActivate(logger->messages_te);
145 } else {
146 (*(logger->messages_te))->viewRect = bounds;
147 InsetRect(&bounds, 4, 4);
148 (*(logger->messages_te))->destRect = bounds;
149 TECalText(logger->messages_te);
150 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
151 }
152
153 DrawControls(logger->win);
154 DrawGrowIconOnly(logger->win);
155
156 if (!init)
157 logger_flush_buffer(true);
158 }
159
160 void
161 logger_resume(struct focusable *focusable, EventRecord *event)
162 {
163 show_focusable(focusable);
164 InvalRect(logger->win->visRgn);
165 }
166
167 void
168 logger_close(struct focusable *focusable, EventRecord *event)
169 {
170 destroy_focusable(&focusable);
171 xfree(&logger->buffered_logs);
172 xfree(&logger);
173 }
174
175 bool
176 logger_quit(struct focusable *focusable)
177 {
178 if (logger)
179 logger_close(focusable, NULL);
180
181 return true;
182 }
183
184 void
185 logger_update(struct focusable *focusable, EventRecord *event)
186 {
187 GrafPtr old_port;
188 Rect r;
189 short what = -1;
190
191 GetPort(&old_port);
192 SetPort(logger->win);
193
194 if (event != NULL)
195 what = event->what;
196
197 switch (what) {
198 case -1:
199 case updateEvt:
200 TextFont(applFont);
201 TextSize(10);
202
203 EraseRect(&logger->win->portRect);
204
205 r = (*(logger->messages_te))->viewRect;
206 InsetRect(&r, -1, -1);
207 FrameRect(&r);
208
209 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
210 DrawControls(logger->win);
211 DrawGrowIconOnly(logger->win);
212 break;
213 case activateEvt:
214 if (event->modifiers & activeFlag) {
215 TEActivate(logger->messages_te);
216 } else {
217 TEDeactivate(logger->messages_te);
218 }
219 break;
220 }
221
222 SetPort(old_port);
223 }
224
225 void
226 logger_update_title(void)
227 {
228 static char logger_title[64];
229 short n, uprinted = 0;
230
231 if (!logger)
232 return;
233
234 snprintf(logger_title, sizeof(logger_title),
235 "%s | %luKB Free | %lu Call%s | ", db->config.name,
236 (FreeMem() / 1024),
237 session_today_tally.calls, session_today_tally.calls == 1 ? "" : "s");
238
239 for (n = 0; n < MAX_SESSIONS; n++) {
240 if (sessions[n] == NULL || !sessions[n]->logged_in)
241 continue;
242
243 if (uprinted++)
244 strlcat(logger_title, ", ", sizeof(logger_title));
245 else
246 strlcat(logger_title, "Logged in: ", sizeof(logger_title));
247
248 strlcat(logger_title,
249 sessions[n]->user ? sessions[n]->user->username : GUEST_USERNAME,
250 sizeof(logger_title));
251 }
252
253 if (uprinted == 0)
254 strlcat(logger_title, "0 connected", sizeof(logger_title));
255
256 SetWTitle(logger->win, CtoPstr(logger_title));
257 }
258
259 void
260 logger_mouse_down(struct focusable *focusable, EventRecord *event)
261 {
262 Point p;
263 ControlHandle control;
264 Rect r;
265 int val, adj;
266
267 p = event->where;
268 GlobalToLocal(&p);
269
270 r = (*(logger->messages_te))->viewRect;
271 if (PtInRect(p, &r)) {
272 TEClick(p, ((event->modifiers & shiftKey) != 0),
273 logger->messages_te);
274 return;
275 }
276
277 switch (FindControl(p, logger->win, &control)) {
278 case inUpButton:
279 case inDownButton:
280 case inPageUp:
281 case inPageDown:
282 if (control != logger->messages_scroller)
283 break;
284 SetTrackControlTE(logger->messages_te);
285 TrackControl(control, p, TrackMouseDownInControl);
286 break;
287 case inThumb:
288 val = GetCtlValue(control);
289 if (TrackControl(control, p, 0L) == 0)
290 break;
291 adj = val - GetCtlValue(control);
292 if (adj != 0) {
293 val -= adj;
294 if (control == logger->messages_scroller)
295 TEScroll(0, adj * TEGetHeight(0, 0, logger->messages_te),
296 logger->messages_te);
297 SetCtlValue(control, val);
298 }
299 break;
300 }
301 }
302
303 bool
304 logger_menu(struct focusable *focusable, short menu, short item)
305 {
306 switch (menu) {
307 case EDIT_MENU_ID:
308 switch (item) {
309 case EDIT_MENU_COPY_ID:
310 HLock(logger->messages_te);
311 if ((*(logger->messages_te))->selStart ==
312 (*(logger->messages_te))->selEnd)
313 SysBeep(10);
314 else
315 TECopy(logger->messages_te);
316 HUnlock(logger->messages_te);
317 break;
318 default:
319 SysBeep(10);
320 break;
321 }
322 return true;
323 }
324
325 return false;
326 }
327
328 void
329 logger_resize(struct focusable *focusable, EventRecord *event)
330 {
331 Rect bounds;
332 long newsize, width, height;
333
334 bounds.left = 100;
335 bounds.top = 100;
336 bounds.right = screenBits.bounds.right;
337 bounds.bottom = screenBits.bounds.bottom;
338
339 newsize = GrowWindow(focusable->win, event->where, &bounds);
340
341 height = HiWord(newsize);
342 width = LoWord(newsize);
343 SizeWindow(focusable->win, width, height, true);
344 logger_layout(NULL);
345 }
346
347 size_t
348 logger_printf(const char *format, ...)
349 {
350 va_list va;
351 size_t len;
352
353 if (logger) {
354 va_start(va, format);
355 len = logger_vprintf(format, va);
356 va_end(va);
357 }
358
359 if (db->config.syslog_ip) {
360 va_start(va, format);
361 syslog_vprintf(format, va);
362 va_end(va);
363 }
364
365 return len;
366 }
367
368 size_t
369 logger_vprintf(const char *format, va_list ap)
370 {
371 static char buf[600];
372 ssize_t len;
373 time_t now = Time;
374
375 if (!logger)
376 return 0;
377
378 blanker_unblank();
379
380 len = strftime(buf, sizeof(buf), "\r[%H:%M:%S] ", localtime(&now));
381 len += vsnprintf(buf + len, sizeof(buf) - len, format, ap);
382 if (len >= sizeof(buf)) {
383 buf[sizeof(buf) - 1] = ']';
384 buf[sizeof(buf) - 2] = '…';
385 buf[sizeof(buf) - 3] = '[';
386 len = sizeof(buf);
387 }
388
389 while (len > 0 && buf[len - 1] == '\r') {
390 buf[len - 1] = '\0';
391 len--;
392 }
393
394 if (logger->buffered_logs_len + len > LOGGER_BUFFERED_LOG_SIZE)
395 logger_flush_buffer(false);
396
397 len = strlcat(logger->buffered_logs, buf, LOGGER_BUFFERED_LOG_SIZE);
398 logger->buffered_logs_len = MIN(len, LOGGER_BUFFERED_LOG_SIZE);
399
400 if (logger->autoflush)
401 logger_flush_buffer(false);
402
403 return len;
404 }
405
406 void
407 logger_flush_buffer(bool force)
408 {
409 RgnHandle savergn;
410 Rect zerorect = { 0, 0, 0, 0 };
411 GrafPtr old_port;
412 short line_height = 0, new_lines = 1, n;
413 char *buf;
414
415 if (!logger || (logger->buffered_logs_len == 0 && !force))
416 return;
417
418 for (n = 0; n < logger->buffered_logs_len; n++) {
419 if (logger->buffered_logs[n] == '\r')
420 new_lines++;
421 }
422
423 line_height = LOGGER_FONT_SIZE + 3;
424
425 GetPort(&old_port);
426 SetPort(logger->win);
427
428 /* check for TE overflow */
429
430 HLock(logger->messages_te);
431
432 /* too many lines */
433 if ((unsigned long)(*(logger->messages_te))->nLines >=
434 (nitems((*(logger->messages_te))->lineStarts) - new_lines))
435 goto te_overflow;
436
437 /* too many characters */
438 if ((unsigned long)(*(logger->messages_te))->teLength >=
439 (SHRT_MAX - logger->buffered_logs_len))
440 goto te_overflow;
441
442 /* destRect of all lines is too tall */
443 if ((unsigned long)(*(logger->messages_te))->nLines * line_height >=
444 (SHRT_MAX - new_lines))
445 goto te_overflow;
446
447 goto no_overflow;
448
449 te_overflow:
450 savergn = NewRgn();
451 GetClip(savergn);
452 /* create an empty clip region so all TE updates are hidden */
453 ClipRect(&zerorect);
454
455 /* select some lines at the start, delete them */
456 TESetSelect(0, (*(logger->messages_te))->lineStarts[new_lines * 2],
457 logger->messages_te);
458 TEDelete(logger->messages_te);
459
460 /* scroll up, causing a repaint */
461 TEPinScroll(0, SHRT_MAX, logger->messages_te);
462
463 /* then scroll back down to what it looked like before we did anything */
464 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
465
466 /* resume normal drawing */
467 SetClip(savergn);
468 DisposeRgn(savergn);
469
470 no_overflow:
471 TESetSelect(SHRT_MAX, SHRT_MAX, logger->messages_te);
472
473 buf = logger->buffered_logs;
474 if ((*(logger->messages_te))->teLength == 0) {
475 /* skip leading \r */
476 logger->buffered_logs_len--;
477 buf++;
478 }
479
480 TEInsert(buf, logger->buffered_logs_len, logger->messages_te);
481
482 if ((unsigned long)(*(logger->messages_te))->nLines >=
483 nitems((*(logger->messages_te))->lineStarts))
484 warn("te overflow!");
485
486 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
487 SetCtlValue(logger->messages_scroller,
488 GetCtlMax(logger->messages_scroller));
489 UpdateScrollbarForTE(logger->win, logger->messages_scroller,
490 logger->messages_te, false);
491 HUnlock(logger->messages_te);
492
493 SetPort(old_port);
494
495 logger->buffered_logs_len = 0;
496 logger->buffered_logs[0] = '\0';
497 }
498
499 void
500 syslog_init(void)
501 {
502 short error;
503
504 if (!db->config.syslog_ip)
505 return;
506
507 if (_TCPInit() != noErr)
508 panic("Failed initializing MacTCP");
509
510 syslog_rcv_buf = xmalloc(SYSLOG_RCV_BUF_SIZE);
511 if (syslog_rcv_buf == NULL)
512 panic("Failed allocating syslog buf");
513 error = _UDPCreate(&syslog_pb, &syslog_stream,
514 (Ptr)syslog_rcv_buf, SYSLOG_RCV_BUF_SIZE, NULL, NULL,
515 NULL, false);
516 if (error)
517 panic("UDPCreate failed: %d", error);
518
519 syslog_send_buf = xmalloc(SYSLOG_SEND_BUF_SIZE);
520 if (syslog_send_buf == NULL)
521 panic("Failed allocating syslog send buf");
522 }
523
524 void
525 syslog_deinit(void)
526 {
527 if (syslog_stream) {
528 _UDPRelease(&syslog_pb, syslog_stream, NULL, false);
529 syslog_stream = 0;
530 }
531
532 if (syslog_rcv_buf)
533 xfree(&syslog_rcv_buf);
534 if (syslog_send_buf)
535 xfree(&syslog_send_buf);
536 }
537
538 void
539 syslog_reinit(void)
540 {
541 syslog_deinit();
542 syslog_init();
543 }
544
545 /* RFC3164 */
546 void
547 syslog_vprintf(const char *format, va_list ap)
548 {
549 size_t len;
550 time_t now = Time;
551
552 if (!db->config.syslog_ip || syslog_send_buf == NULL)
553 return;
554
555 /* system (3) priority, notice (5) severity */
556 len = strftime(syslog_send_buf, SYSLOG_SEND_BUF_SIZE,
557 "<29>%b %d %H:%M:%S subtext: ", localtime(&now));
558 len += vsnprintf(syslog_send_buf + len, SYSLOG_SEND_BUF_SIZE - len,
559 format, ap);
560
561 if (len >= SYSLOG_SEND_BUF_SIZE)
562 len = SYSLOG_SEND_BUF_SIZE - 1;
563
564 syslog_wds[0].ptr = (Ptr)syslog_send_buf;
565 syslog_wds[0].length = len;
566 syslog_wds[1].ptr = 0;
567 syslog_wds[1].length = 0;
568
569 _UDPSend(&syslog_pb, syslog_stream, syslog_wds,
570 db->config.syslog_ip, 514, NULL, NULL, false);
571 }