6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek MODULE_CONTEXT(obj, fts_parser_script_user_module)
7465d6a1ef6e83825dba3a4dc4dda7271671aba0Jakub Hrozek#define SCRIPT_HANDSHAKE "VERSION\tscript\t4\t0\nalarm=10\nnoreply\n"
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher union mail_user_module_context module_ctx;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic MODULE_CONTEXT_DEFINE_INIT(fts_parser_script_user_module,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic int script_connect(struct mail_user *user, const char **path_r)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher path = mail_user_plugin_getenv(user, "fts_decoder");
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek path = t_strconcat(user->set->base_dir, "/", path, NULL);
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek fd = net_connect_unix_with_retries(path, 1000);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("net_connect_unix(%s) failed: %m", path);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic int script_contents_read(struct mail_user *user)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct fts_parser_script_user *suser = SCRIPT_USER_CONTEXT(user);
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek input = i_stream_create_fd_autoclose(&fd, 1024);
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek while ((line = i_stream_read_next_line(input)) != NULL) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* <content-type> <extension> [<extension> ...] */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher args = p_strsplit_spaces(user->pool, line, " ");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (args[0][0] == '\0' || args[1] == NULL) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("parser script sent invalid input: %s", line);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher content = array_append_space(&suser->content);
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek i_error("parser script read(%s) failed: %s", path,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher i_error("parser script didn't send any data");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("parser script didn't send empty EOF line");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic bool script_support_content(struct mail_user *user,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher struct fts_parser_script_user *suser = SCRIPT_USER_CONTEXT(user);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher suser = p_new(user->pool, struct fts_parser_script_user, 1);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher p_array_init(&suser->content, user->pool, 32);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher MODULE_CONTEXT_SET(user, fts_parser_script_user_module, suser);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher if (strcmp(*content_type, "application/octet-stream") == 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher str_array_icase_find(content->extensions, extension)) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (strcmp(content->content_type, *content_type) == 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void parse_content_disposition(const char *content_disposition,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek rfc822_parser_init(&parser, (const unsigned char *)content_disposition,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* type; param; param; .. */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (rfc822_parse_mime_token(&parser, str) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (strcasecmp(results[0], "filename*") == 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* RFC 2231 style non-ascii filename. we don't really care
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher much about the filename actually, just about its extension */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherfts_parser_script_try_init(struct fts_parser_context *parser_context)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher parse_content_disposition(parser_context->content_disposition, &filename);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (!script_support_content(parser_context->user, &parser_context->content_type, filename))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher fd = script_connect(parser_context->user, &path);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher cmd = t_strdup_printf(SCRIPT_HANDSHAKE"%s\n\n", parser_context->content_type);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (write_full(fd, cmd, strlen(cmd)) < 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher parser = i_new(struct script_fts_parser, 1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void fts_parser_script_more(struct fts_parser *_parser,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct script_fts_parser *parser = (struct script_fts_parser *)_parser;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* first we'll send everything to the script */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher write_full(parser->fd, block->data, block->size) < 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("write(%s) failed: %m", parser->path);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("shutdown(%s) failed: %m", parser->path);
d25fa6f2608d5fe0617ada47f9d426f45deb96ffJakub Hrozek /* read the result from the script */
d25fa6f2608d5fe0617ada47f9d426f45deb96ffJakub Hrozek ret = read(parser->fd, parser->outbuf, sizeof(parser->outbuf));
d25fa6f2608d5fe0617ada47f9d426f45deb96ffJakub Hrozekstatic int fts_parser_script_deinit(struct fts_parser *_parser,
d25fa6f2608d5fe0617ada47f9d426f45deb96ffJakub Hrozek struct script_fts_parser *parser = (struct script_fts_parser *)_parser;