static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag_arguments_t *hrefs) { int fd_tmp, tag_length, tag_arg_length, binary; int retval=FALSE, escape, value, hex, tag_val_length, table_pos, in_script=FALSE; FILE *stream_in; html_state state=HTML_NORM, next_state=HTML_BAD_STATE; char filename[1024], tag[HTML_STR_LENGTH+1], tag_arg[HTML_STR_LENGTH+1]; char tag_val[HTML_STR_LENGTH+1], *tmp_file; unsigned char *line, *ptr; char *arg_value; tag_arguments_t tag_args; quoted_state quoted; unsigned long length; file_buff_t *file_buff_o1, *file_buff_o2, *file_buff_script; file_buff_t *file_tmp_o1; if (!m_area) { if (fd < 0) { cli_dbgmsg("Invalid HTML fd\n"); return FALSE; } lseek(fd, 0, SEEK_SET); fd_tmp = dup(fd); if (fd_tmp < 0) { return FALSE; } stream_in = fdopen(fd_tmp, "r"); if (!stream_in) { close(fd_tmp); return FALSE; } } tag_args.count = 0; tag_args.tag = NULL; tag_args.value = NULL; if (dirname) { snprintf(filename, 1024, "%s/rfc2397", dirname); if (mkdir(filename, 0700)) { file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } file_buff_o1 = (file_buff_t *) cli_malloc(sizeof(file_buff_t)); if (!file_buff_o1) { file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } file_buff_o2 = (file_buff_t *) cli_malloc(sizeof(file_buff_t)); if (!file_buff_o2) { free(file_buff_o1); file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } file_buff_script = (file_buff_t *) cli_malloc(sizeof(file_buff_t)); if (!file_buff_script) { free(file_buff_o1); free(file_buff_o2); file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } snprintf(filename, 1024, "%s/comment.html", dirname); file_buff_o1->fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); if (!file_buff_o1->fd) { cli_dbgmsg("open failed: %s\n", filename); free(file_buff_o1); free(file_buff_o2); free(file_buff_script); file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } snprintf(filename, 1024, "%s/nocomment.html", dirname); file_buff_o2->fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); if (!file_buff_o2->fd) { cli_dbgmsg("open failed: %s\n", filename); close(file_buff_o1->fd); free(file_buff_o1); free(file_buff_o2); free(file_buff_script); file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } snprintf(filename, 1024, "%s/script.html", dirname); file_buff_script->fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); if (!file_buff_script->fd) { cli_dbgmsg("open failed: %s\n", filename); close(file_buff_o1->fd); close(file_buff_o2->fd); free(file_buff_o1); free(file_buff_o2); free(file_buff_script); file_buff_o1 = file_buff_o2 = file_buff_script = NULL; goto abort; } file_buff_o1->length = 0; file_buff_o2->length = 0; file_buff_script->length = 0; } else { file_buff_o1 = NULL; file_buff_o2 = NULL; file_buff_script = NULL; } binary = FALSE; ptr = line = cli_readline(stream_in, m_area, 8192); while (line) { while (*ptr && isspace(*ptr)) { ptr++; } while (*ptr) { if (!binary && *ptr == '\n') { /* Convert it to a space and re-process */ *ptr = ' '; continue; } if (!binary && *ptr == '\r') { ptr++; continue; } switch (state) { case HTML_BAD_STATE: /* An engine error has occurred */ cli_dbgmsg("HTML Engine Error\n"); goto abort; case HTML_SKIP_LENGTH: length--; ptr++; if (!length) { state = next_state; } break; case HTML_SKIP_WS: if (isspace(*ptr)) { ptr++; } else { state = next_state; next_state = HTML_BAD_STATE; } break; case HTML_TRIM_WS: if (isspace(*ptr)) { ptr++; } else { html_output_c(file_buff_o1, file_buff_o2, ' '); state = next_state; next_state = HTML_BAD_STATE; } break; case HTML_NORM: if (*ptr == '<') { html_output_c(file_buff_o1, file_buff_o2, '<'); if (in_script) { html_output_c(file_buff_script, NULL, '<'); } ptr++; state = HTML_SKIP_WS; tag_length=0; next_state = HTML_TAG; } else if (isspace(*ptr)) { state = HTML_TRIM_WS; next_state = HTML_NORM; } else if (*ptr == '&') { state = HTML_CHAR_REF; next_state = HTML_NORM; ptr++; } else { html_output_c(file_buff_o1, file_buff_o2, tolower(*ptr)); if (in_script) { html_output_c(file_buff_script, NULL, tolower(*ptr)); } ptr++; } break; case HTML_TAG: if ((tag_length == 0) && (*ptr == '!')) { /* Comment */ html_output_c(file_buff_o1, NULL, '!'); if (in_script) { html_output_c(file_buff_script, NULL, '!'); } /* Need to rewind in the no-comment output stream */ if (file_buff_o2 && (file_buff_o2->length > 0)) { file_buff_o2->length--; } state = HTML_COMMENT; next_state = HTML_BAD_STATE; ptr++; } else if (*ptr == '>') { html_output_c(file_buff_o1, file_buff_o2, '>'); if (in_script) { html_output_c(file_buff_script, NULL, '>'); } ptr++; tag[tag_length] = '\0'; state = HTML_SKIP_WS; next_state = HTML_PROCESS_TAG; } else if (!isspace(*ptr)) { html_output_c(file_buff_o1, file_buff_o2, tolower(*ptr)); if (in_script) { html_output_c(file_buff_script, NULL, tolower(*ptr)); } if (tag_length < HTML_STR_LENGTH) { tag[tag_length++] = tolower(*ptr); } ptr++; } else { tag[tag_length] = '\0'; state = HTML_SKIP_WS; tag_arg_length = 0; next_state = HTML_TAG_ARG; } break; case HTML_TAG_ARG: if (*ptr == '=') { html_output_c(file_buff_o1, file_buff_o2, '='); tag_arg[tag_arg_length] = '\0'; ptr++; state = HTML_SKIP_WS; escape = FALSE; quoted = NOT_QUOTED; tag_val_length = 0; next_state = HTML_TAG_ARG_VAL; } else if (isspace(*ptr)) { ptr++; tag_arg[tag_arg_length] = '\0'; state = HTML_SKIP_WS; next_state = HTML_TAG_ARG_EQUAL; } else if (*ptr == '>') { html_output_c(file_buff_o1, file_buff_o2, '>'); if (tag_arg_length > 0) { tag_arg[tag_arg_length] = '\0'; html_tag_arg_add(&tag_args, tag_arg, NULL); } ptr++; state = HTML_PROCESS_TAG; next_state = HTML_BAD_STATE; } else { if (tag_arg_length == 0) { /* Start of new tag - add space */ html_output_c(file_buff_o1, file_buff_o2,' '); } html_output_c(file_buff_o1, file_buff_o2, tolower(*ptr)); if (tag_arg_length < HTML_STR_LENGTH) { tag_arg[tag_arg_length++] = tolower(*ptr); } ptr++; } break; case HTML_TAG_ARG_EQUAL: if (*ptr == '=') { html_output_c(file_buff_o1, file_buff_o2, '='); ptr++; state = HTML_SKIP_WS; escape = FALSE; quoted = NOT_QUOTED; tag_val_length = 0; next_state = HTML_TAG_ARG_VAL; } else { if (tag_arg_length > 0) { tag_arg[tag_arg_length] = '\0'; html_tag_arg_add(&tag_args, tag_arg, NULL); } tag_arg_length=0; state = HTML_TAG_ARG; next_state = HTML_BAD_STATE; } break; case HTML_TAG_ARG_VAL: if ((tag_val_length == 5) && (strncmp(tag_val, "data:", 5) == 0)) { /* RFC2397 inline data */ /* Rewind one byte so we don't recursuive */ if (file_buff_o1 && (file_buff_o1->length > 0)) { file_buff_o1->length--; } if (file_buff_o2 && (file_buff_o2->length > 0)) { file_buff_o2->length--; } if (quoted != NOT_QUOTED) { html_output_c(file_buff_o1, file_buff_o2, '"'); } tag_val_length = 0; state = HTML_RFC2397_TYPE; next_state = HTML_TAG_ARG; } else if ((tag_val_length == 6) && (strncmp(tag_val, "\"data:", 6) == 0)) { /* RFC2397 inline data */ /* Rewind one byte so we don't recursuive */ if (file_buff_o1 && (file_buff_o1->length > 0)) { file_buff_o1->length--; } if (file_buff_o2 && (file_buff_o2->length > 0)) { file_buff_o2->length--; } if (quoted != NOT_QUOTED) { html_output_c(file_buff_o1, file_buff_o2, '"'); } tag_val_length = 0; state = HTML_RFC2397_TYPE; next_state = HTML_TAG_ARG; } else if (*ptr == '&') { state = HTML_CHAR_REF; next_state = HTML_TAG_ARG_VAL; ptr++; } else if (*ptr == '\'') { if (tag_val_length == 0) { quoted = SINGLE_QUOTED; html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } else { if (!escape && (quoted==SINGLE_QUOTED)) { html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } tag_val[tag_val_length] = '\0'; html_tag_arg_add(&tag_args, tag_arg, tag_val); ptr++; state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } } } else if (*ptr == '"') { if (tag_val_length == 0) { quoted = DOUBLE_QUOTED; html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } else { if (!escape && (quoted==DOUBLE_QUOTED)) { html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } tag_val[tag_val_length] = '\0'; html_tag_arg_add(&tag_args, tag_arg, tag_val); ptr++; state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { html_output_c(file_buff_o1, file_buff_o2, '"'); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } } } else if (isspace(*ptr) || (*ptr == '>')) { if (quoted == NOT_QUOTED) { tag_val[tag_val_length] = '\0'; html_tag_arg_add(&tag_args, tag_arg, tag_val); state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { html_output_c(file_buff_o1, file_buff_o2, *ptr); if (tag_val_length < HTML_STR_LENGTH) { if (isspace(*ptr)) { tag_val[tag_val_length++] = ' '; } else { tag_val[tag_val_length++] = '>'; } } state = HTML_SKIP_WS; escape = FALSE; quoted = NOT_QUOTED; next_state = HTML_TAG_ARG_VAL; ptr++; } } else { html_output_c(file_buff_o1, file_buff_o2, tolower(*ptr)); if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = *ptr; } ptr++; } if (*ptr == '\\') { escape = TRUE; } else { escape = FALSE; } break; case HTML_COMMENT: html_output_c(file_buff_o1, NULL, tolower(*ptr)); if (in_script) { html_output_c(file_buff_script, NULL, tolower(*ptr)); } if (*ptr == '>') { state = HTML_SKIP_WS; next_state = HTML_NORM; } ptr++; break; case HTML_PROCESS_TAG: /* Default to no action for this tag */ state = HTML_SKIP_WS; next_state = HTML_NORM; if (tag[0] == '/') { /* End tag */ state = HTML_SKIP_WS; next_state = HTML_NORM; if (strcmp(tag, "/script") == 0) { in_script=FALSE; html_output_c(file_buff_script, NULL, '\n'); } } else if (strcmp(tag, "script") == 0) { arg_value = html_tag_arg_value(&tag_args, "language"); if (arg_value && (strcasecmp(arg_value, "jscript.encode") == 0)) { html_tag_arg_set(&tag_args, "language", "javascript"); state = HTML_SKIP_WS; next_state = HTML_JSDECODE; } else if (arg_value && (strcasecmp(arg_value, "vbscript.encode") == 0)) { html_tag_arg_set(&tag_args, "language", "vbscript"); state = HTML_SKIP_WS; next_state = HTML_JSDECODE; } else { in_script = TRUE; } html_output_tag(file_buff_script, tag, &tag_args); } else if (hrefs) { if (strcmp(tag, "a") == 0) { arg_value = html_tag_arg_value(&tag_args, "href"); if (arg_value && strlen(arg_value) > 0) { html_tag_arg_add(hrefs, "href", arg_value); } } else if (strcmp(tag, "img") == 0) { arg_value = html_tag_arg_value(&tag_args, "src"); if (arg_value && strlen(arg_value) > 0) { html_tag_arg_add(hrefs, "src", arg_value); } arg_value = html_tag_arg_value(&tag_args, "dynsrc"); if (arg_value && strlen(arg_value) > 0) { html_tag_arg_add(hrefs, "dynsrc", arg_value); } } else if (strcmp(tag, "iframe") == 0) { arg_value = html_tag_arg_value(&tag_args, "src"); if (arg_value && strlen(arg_value) > 0) { html_tag_arg_add(hrefs, "iframe", arg_value); } } } html_tag_arg_free(&tag_args); break; case HTML_CHAR_REF: if (*ptr == '#') { value = 0; hex = FALSE; state = HTML_CHAR_REF_DECODE; ptr++; } else { html_output_c(file_buff_o1, file_buff_o2, '&'); state = next_state; next_state = HTML_BAD_STATE; } break; case HTML_CHAR_REF_DECODE: if ((value==0) && ((*ptr == 'x') || (*ptr == 'X'))) { hex=TRUE; ptr++; } else if (*ptr == ';') { html_output_c(file_buff_o1, file_buff_o2, value); state = next_state; next_state = HTML_BAD_STATE; ptr++; } else if (isdigit(*ptr) || (hex && isxdigit(*ptr))) { if (hex) { value *= 16; } else { value *= 10; } if (isdigit(*ptr)) { value += (*ptr - '0'); } else { value += (tolower(*ptr) - 'a' + 10); } ptr++; } else { html_output_c(file_buff_o1, file_buff_o2, value); state = next_state; next_state = HTML_BAD_STATE; } break; case HTML_JSDECODE: /* Check for start marker */ if (strncmp((char *) ptr, "#@~^", 4) == 0) { ptr += 4; state = HTML_JSDECODE_LENGTH; next_state = HTML_BAD_STATE; } else { html_output_c(file_buff_o1, file_buff_o2, tolower(*ptr)); html_output_c(file_buff_script, NULL, tolower(*ptr)); ptr++; } break; case HTML_JSDECODE_LENGTH: if (strlen((char *) ptr) < 8) { state = HTML_NORM; next_state = HTML_BAD_STATE; break; } length = base64_chars[ptr[0]] << 2; length += base64_chars[ptr[1]] >> 4; length += (base64_chars[ptr[1]] & 0x0f) << 12; length += (base64_chars[ptr[2]] >> 2) << 8; length += (base64_chars[ptr[2]] & 0x03) << 22; length += base64_chars[ptr[3]] << 16; length += (base64_chars[ptr[4]] << 2) << 24; length += (base64_chars[ptr[5]] >> 4) << 24; table_pos = 0; state = HTML_JSDECODE_DECRYPT; next_state = HTML_BAD_STATE; ptr += 8; break; case HTML_JSDECODE_DECRYPT: if (length == 0) { html_output_str(file_buff_script, "\n", 10); length = 12; state = HTML_SKIP_LENGTH; next_state = HTML_NORM; break; } if (*ptr < 0x80) { value = decrypt_tables[table_order[table_pos]][*ptr]; if (value == 0xFF) { /* special character */ ptr++; length--; switch (*ptr) { case '\0': /* Fixup for end of line */ ptr--; break; case 0x21: html_output_c(file_buff_o1, file_buff_o2, 0x3c); html_output_c(file_buff_script, NULL, 0x3c); break; case 0x23: html_output_c(file_buff_o1, file_buff_o2, 0x0d); html_output_c(file_buff_script, NULL, 0x0d); break; case 0x24: html_output_c(file_buff_o1, file_buff_o2, 0x40); html_output_c(file_buff_script, NULL, 0x40); break; case 0x26: html_output_c(file_buff_o1, file_buff_o2, 0x0a); html_output_c(file_buff_script, NULL, 0x0a); break; case 0x2a: html_output_c(file_buff_o1, file_buff_o2, 0x3e); html_output_c(file_buff_script, NULL, 0x3e); break; } } else { html_output_c(file_buff_o1, file_buff_o2, value); html_output_c(file_buff_script, NULL, tolower(value)); } } table_pos = (table_pos + 1) % 64; ptr++; length--; break; case HTML_RFC2397_TYPE: if (*ptr == '\'') { if (!escape && (quoted==SINGLE_QUOTED)) { /* Early end of data detected. Error */ ptr++; state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } } else if (*ptr == '"') { if (!escape && (quoted==DOUBLE_QUOTED)) { /* Early end of data detected. Error */ ptr++; state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = '"'; } ptr++; } } else if (isspace(*ptr) || (*ptr == '>')) { if (quoted == NOT_QUOTED) { /* Early end of data detected. Error */ state = HTML_SKIP_WS; tag_arg_length=0; next_state = HTML_TAG_ARG; } else { if (tag_val_length < HTML_STR_LENGTH) { if (isspace(*ptr)) { tag_val[tag_val_length++] = ' '; } else { tag_val[tag_val_length++] = '>'; } } state = HTML_SKIP_WS; escape = FALSE; quoted = NOT_QUOTED; next_state = HTML_RFC2397_TYPE; ptr++; } } else if (*ptr == ',') { /* Beginning of data */ tag_val[tag_val_length] = '\0'; state = HTML_RFC2397_INIT; escape = FALSE; next_state = HTML_BAD_STATE; ptr++; } else { if (tag_val_length < HTML_STR_LENGTH) { tag_val[tag_val_length++] = tolower(*ptr); } ptr++; } if (*ptr == '\\') { escape = TRUE; } else { escape = FALSE; } break; case HTML_RFC2397_INIT: file_tmp_o1 = (file_buff_t *) cli_malloc(sizeof(file_buff_t)); if (!file_tmp_o1) { goto abort; } snprintf(filename, 1024, "%s/rfc2397", dirname); tmp_file = cli_gentemp(filename); cli_dbgmsg("RFC2397 data file: %s\n", tmp_file); file_tmp_o1->fd = open(tmp_file, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); free(tmp_file); if (!file_tmp_o1->fd) { cli_dbgmsg("open failed: %s\n", filename); free(file_tmp_o1); goto abort; } file_tmp_o1->length = 0; html_output_str(file_tmp_o1, "From html-normalise\n", 20); html_output_str(file_tmp_o1, "Content-type: ", 14); if ((tag_val_length == 0) && (*tag_val == ';')) { html_output_str(file_tmp_o1, "text/plain\n", 11); } html_output_str(file_tmp_o1, tag_val, tag_val_length); html_output_c(file_tmp_o1, NULL, '\n'); if (strstr(tag_val, ";base64") != NULL) { html_output_str(file_tmp_o1, "Content-transfer-encoding: base64\n", 34); } html_output_c(file_tmp_o1, NULL, '\n'); state = HTML_RFC2397_DATA; binary = TRUE; break; case HTML_RFC2397_DATA: if (*ptr == '&') { state = HTML_CHAR_REF; next_state = HTML_RFC2397_DATA; ptr++; } else if (*ptr == '%') { length = 0; value = 0; state = HTML_ESCAPE_CHAR; next_state = HTML_RFC2397_ESC; ptr++; } else if (*ptr == '\'') { if (!escape && (quoted==SINGLE_QUOTED)) { state = HTML_RFC2397_FINISH; ptr++; } else { html_output_c(file_tmp_o1, NULL, *ptr); ptr++; } } else if (*ptr == '\"') { if (!escape && (quoted==DOUBLE_QUOTED)) { state = HTML_RFC2397_FINISH; ptr++; } else { html_output_c(file_tmp_o1, NULL, *ptr); ptr++; } } else if (isspace(*ptr) || (*ptr == '>')) { if (quoted == NOT_QUOTED) { state = HTML_RFC2397_FINISH; ptr++; } else { html_output_c(file_tmp_o1, NULL, *ptr); ptr++; } } else { html_output_c(file_tmp_o1, NULL, *ptr); ptr++; } if (*ptr == '\\') { escape = TRUE; } else { escape = FALSE; } break; case HTML_RFC2397_FINISH: html_output_flush(file_tmp_o1); close(file_tmp_o1->fd); free(file_tmp_o1); state = HTML_SKIP_WS; escape = FALSE; quoted = NOT_QUOTED; next_state = HTML_TAG_ARG; binary = FALSE; break; case HTML_RFC2397_ESC: if (length == 2) { html_output_c(file_tmp_o1, NULL, value); } else if (length == 1) { html_output_c(file_tmp_o1, NULL, '%'); html_output_c(file_tmp_o1, NULL, value+'0'); } else { html_output_c(file_tmp_o1, NULL, '%'); } state = HTML_RFC2397_DATA; break; case HTML_ESCAPE_CHAR: value *= 16; length++; if (isxdigit(*ptr)) { if (isdigit(*ptr)) { value += (*ptr - '0'); } else { value += (tolower(*ptr) - 'a' + 10); } } else { state = next_state; } if (length == 2) { state = next_state; } ptr++; break; } } free(line); ptr = line = cli_readline(stream_in, m_area, 8192); } retval = TRUE; abort: html_tag_arg_free(&tag_args); if (!m_area) { fclose(stream_in); } if (file_buff_o1) { html_output_flush(file_buff_o1); close(file_buff_o1->fd); free(file_buff_o1); } if (file_buff_o2) { html_output_flush(file_buff_o2); close(file_buff_o2->fd); free(file_buff_o2); } if (file_buff_script) { html_output_flush(file_buff_script); close(file_buff_script->fd); free(file_buff_script); } return retval; }