/* * Callback function invoked via smtp_start_session() */ static const char *message_cb(void **buf, int *len, void *arg) { message_status_t *ms = (message_status_t*) arg; if (NULL == *buf) { *buf = malloc(8192); } if (NULL == len) { ms->sent_flag = SENT_NONE; return NULL; } /* Free the previous string */ if (NULL != ms->prev_string) { free(ms->prev_string); ms->prev_string = NULL; } switch (ms->sent_flag) { case SENT_NONE: /* Send a blank line to signify the end of the header */ ms->sent_flag = SENT_HEADER; ms->prev_string = NULL; *len = 2; return "\r\n"; case SENT_HEADER: if (NULL != mca_plog_smtp_component.body_prefix) { ms->sent_flag = SENT_BODY_PREFIX; ms->prev_string = crnl(mca_plog_smtp_component.body_prefix); *len = strlen(ms->prev_string); return ms->prev_string; } case SENT_BODY_PREFIX: ms->sent_flag = SENT_BODY; ms->prev_string = crnl(ms->msg); *len = strlen(ms->prev_string); return ms->prev_string; case SENT_BODY: if (NULL != mca_plog_smtp_component.body_suffix) { ms->sent_flag = SENT_BODY_SUFFIX; ms->prev_string = crnl(mca_plog_smtp_component.body_suffix); *len = strlen(ms->prev_string); return ms->prev_string; } case SENT_BODY_SUFFIX: case SENT_ALL: default: ms->sent_flag = SENT_ALL; *len = 0; return NULL; } }
static void write_normal(const char *buf, int size) { if (size == 0) return; while (size > 0) { if (console->at_right_margin && console->wraparound_mode) crnl(); /* Write to line end at most */ int line_remain = min(size, console->width - console->x); if (console->insert_mode && console->x + line_remain < console->width) scroll(console->x, console->width - 1, console->y, console->y, line_remain, 0); DWORD bytes_written; WriteConsoleA(console->out, buf, line_remain, &bytes_written, NULL); console->x += line_remain; if (console->x == console->width) { console->x--; console->at_right_margin = 1; } buf += line_remain; size -= line_remain; } }
static void control_escape(char ch) { switch (ch) { case '[': for (int i = 0; i < CONSOLE_MAX_PARAMS; i++) console->params[i] = 0; console->param_count = 0; console->private_mode = 0; console->processor = control_escape_csi; break; case ']': console->params[0] = 0; console->string_len = -1; console->processor = control_escape_osc; break; case 'D': /* IND */ nl(); console->processor = NULL; break; case 'E': /* NEL */ crnl(); console->processor = NULL; break; case 'M': /* RI */ if (console->y == console->scroll_top) scroll_down(1); else set_pos(console->x, console->y - 1); console->processor = NULL; break; case '(': console->processor = control_escape_set_default_character_set; break; case ')': console->processor = control_escape_set_alternate_character_set; break; case '#': console->processor = control_escape_sharp; break; default: log_error("control_escape(): Unhandled character %c\n", ch); console->processor = NULL; } }
static size_t console_write(struct file *f, const char *buf, size_t count) { struct console_file *console_file = (struct console_file *)f; console_lock(); console_retrieve_state(); #define OUTPUT() \ if (last != -1) \ { \ write_normal(buf + last, i - last); \ last = -1; \ } size_t last = -1; size_t i; for (i = 0; i < count; i++) { char ch = buf[i]; if (ch == 0x1B) /* Escape */ { OUTPUT(); console->processor = control_escape; } else if (ch == '\t') { OUTPUT(); int x = (console->x + 8) & ~7; if (x < console->width) set_pos(x, console->y); else set_pos(console->width - 1, console->y); } else if (ch == '\b') { OUTPUT(); backspace(FALSE); } else if (ch == '\r') { OUTPUT(); if (console->termios.c_oflag & OCRNL) nl(); else cr(); } else if (ch == '\n' || ch == '\v' || ch == '\f') { OUTPUT(); if (console->termios.c_oflag & ONLCR) crnl(); else nl(); } else if (ch == 0x0E || ch == 0x0F) { /* Shift In and Shift Out */ OUTPUT(); } else if (console->processor) console->processor(ch); else if (ch < 0x20) { OUTPUT(); log_error("Unhandled control character '\\x%x'\n", ch); } else if (last == -1) last = i; } OUTPUT(); /* This will make the caret immediately visible */ set_pos(console->x, console->y); console_unlock(); #if 0 char str[4096]; memcpy(str, buf, count); str[count] = '\n'; str[count + 1] = 0; log_debug(str); #endif return count; }
static size_t console_read(struct file *f, char *buf, size_t count) { struct console_file *console_file = (struct console_file *)f; console_lock(); console_retrieve_state(); size_t bytes_read = 0; while (console->input_buffer_head != console->input_buffer_tail && count > 0) { count--; buf[bytes_read++] = console->input_buffer[console->input_buffer_tail]; console->input_buffer_tail = (console->input_buffer_tail + 1) % MAX_INPUT; } if (console->termios.c_lflag & ICANON) { char line[MAX_CANON + 1]; /* One more for storing CR or LF */ size_t len = 0; while (count > 0) { INPUT_RECORD ir; DWORD read; ReadConsoleInputA(console->in, &ir, 1, &read); if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_RETURN: { if (!(console->termios.c_iflag & IGNCR)) line[len++] = console->termios.c_iflag & ICRNL ? '\n' : '\r'; size_t r = min(count, len); memcpy(buf + bytes_read, line, r); bytes_read += r; count -= r; if (r < len) { /* Some bytes not fit, add to input buffer */ console_add_input(line + r, len - r); } if (console->termios.c_lflag & ECHO) crnl(); goto read_done; } case VK_BACK: { if (len > 0) { len--; if (console->termios.c_lflag & ECHO) backspace(TRUE); } } default: { char ch = ir.Event.KeyEvent.uChar.AsciiChar; if (ch >= 0x20) { if (len < MAX_CANON) { line[len++] = ch; if (console->termios.c_lflag & ECHO) write_normal(&ch, 1); } } } } } } } else /* Non canonical mode */ { int vtime = console->termios.c_cc[VTIME]; int vmin = console->termios.c_cc[VMIN]; while (count > 0) { if (bytes_read > 0 && bytes_read >= vmin) break; /* If vmin > 0 and vtime == 0, it is a blocking read, otherwise we need to poll first */ if (vtime > 0 || (vmin == 0 && vtime == 0)) { if (WaitForSingleObject(console->in, vtime * 100) == WAIT_TIMEOUT) break; } INPUT_RECORD ir; DWORD read; ReadConsoleInputA(console->in, &ir, 1, &read); if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_UP: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOA" : "\x1B[A", 3); break; case VK_DOWN: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOB" : "\x1B[B", 3); break; case VK_RIGHT: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOC" : "\x1B[C", 3); break; case VK_LEFT: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOD" : "\x1B[D", 3); break; case VK_HOME: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOH" : "\x1B[H", 3); break; case VK_END: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOF" : "\x1B[F", 3); break; case VK_INSERT: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[2~", 4); break; case VK_DELETE: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[3~", 4); break; case VK_PRIOR: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[5~", 4); break; case VK_NEXT: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[6~", 4); break; case VK_F1: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOP", 3); break; case VK_F2: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOQ", 3); break; case VK_F3: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOR", 3); break; case VK_F4: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOS", 3); break; case VK_F5: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[15~", 5); break; case VK_F6: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[17~", 5); break; case VK_F7: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[18~", 5); break; case VK_F8: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[19~", 5); break; case VK_F9: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[20~", 5); break; case VK_F10: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[21~", 5); break; case VK_F11: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[23~", 5); break; case VK_F12: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[24~", 5); break; case VK_F13: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[25~", 5); break; case VK_F14: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[26~", 5); break; case VK_F15: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[28~", 5); break; case VK_F16: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[29~", 5); break; case VK_F17: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[31~", 5); break; case VK_F18: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[32~", 5); break; case VK_F19: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[33~", 5); break; case VK_F20: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[34~", 5); break; default: { char ch = ir.Event.KeyEvent.uChar.AsciiChar; if (ch == '\r' && console->termios.c_iflag & IGNCR) break; if (ch == '\r' && console->termios.c_iflag & ICRNL) ch = '\n'; else if (ch == '\n' && console->termios.c_iflag & ICRNL) ch = '\r'; if (ch > 0) { count--; buf[bytes_read++] = ch; if (console->termios.c_lflag & ECHO) write_normal(&ch, 1); } } } } else { /* TODO: Other types of input */ } } } read_done: /* This will make the caret immediately visible */ set_pos(console->x, console->y); console_unlock(); return bytes_read; }
static void write_normal(const char *buf, int size) { if (size == 0) return; charset_func charset = console->charset == 0? console->g0_charset: console->g1_charset; WCHAR data[1024]; int len = 0, displen = 0; int i = 0; while (i < size) { if (console->at_right_margin && console->wraparound_mode) crnl(); /* Write to line end at most */ int line_remain = min(size - i, console->width - console->x); len = 0; displen = 0; int seqlen = -1; while (displen < line_remain && i < size) { console->utf8_buf[console->utf8_buf_size++] = buf[i++]; if (console->utf8_buf_size == 1) seqlen = utf8_get_sequence_len(console->utf8_buf[0]); if (seqlen < 0) console->utf8_buf_size = 0; if (seqlen == console->utf8_buf_size) { uint32_t codepoint = utf8_decode(console->utf8_buf); if (codepoint >= 0 && codepoint <= 0x10FFFF) { /* TODO: Handle non BMP characters (not supported by conhost) */ int l = wcwidth(codepoint); if (l > 0) { if (displen + l > line_remain && console->wraparound_mode) { i--; console->utf8_buf_size--; break; } displen += l; data[len++] = charset(codepoint); } } console->utf8_buf_size = 0; } } if (console->insert_mode && console->x + displen < console->width) scroll(console->x, console->width - 1, console->y, console->y, displen, 0); DWORD chars_written; WriteConsoleW(console->out, data, len, &chars_written, NULL); console->x += displen; if (console->x == console->width) { console->x--; console->at_right_margin = 1; } } }