HIDDEN int ws_start_channel(struct transaction_t *txn, const char *protocol, int (*data_cb)(struct buf *inbuf, struct buf *outbuf, struct buf *logbuf, void **rock)) { int r; const char **hdr, *accept = NULL; wslay_event_context_ptr ev; struct ws_context *ctx; struct wslay_event_callbacks callbacks = { recv_cb, send_cb, NULL, NULL, NULL, NULL, on_msg_recv_cb }; /* Check for supported WebSocket version */ hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Version"); if (!hdr) { txn->error.desc = "Missing WebSocket version"; return HTTP_BAD_REQUEST; } else if (hdr[1]) { txn->error.desc = "Multiple WebSocket versions"; return HTTP_BAD_REQUEST; } else if (strcmp(hdr[0], WS_VERSION)) { txn->error.desc = "Unsupported WebSocket version"; return HTTP_UPGRADE; } if (protocol) { /* Check for supported WebSocket subprotocol */ int i, found = 0; hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Protocol"); if (!hdr) { txn->error.desc = "Missing WebSocket protocol"; return HTTP_BAD_REQUEST; } for (i = 0; !found && hdr[i]; i++) { tok_t tok = TOK_INITIALIZER(hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT); char *token; while ((token = tok_next(&tok))) { if (!strcmp(token, protocol)) { found = 1; break; } } tok_fini(&tok); } if (!found) { txn->error.desc = "Unsupported WebSocket protocol"; return HTTP_BAD_REQUEST; } } if (txn->flags.ver == VER_1_1) { unsigned char sha1buf[SHA1_DIGEST_LENGTH]; /* Check for WebSocket client key */ hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Key"); if (!hdr) { txn->error.desc = "Missing WebSocket client key"; return HTTP_BAD_REQUEST; } else if (hdr[1]) { txn->error.desc = "Multiple WebSocket client keys"; return HTTP_BAD_REQUEST; } else if (strlen(hdr[0]) != WS_CKEY_LEN) { txn->error.desc = "Invalid WebSocket client key"; return HTTP_BAD_REQUEST; } /* Create WebSocket accept key */ buf_setcstr(&txn->buf, hdr[0]); buf_appendcstr(&txn->buf, WS_GUID); xsha1((u_char *) buf_base(&txn->buf), buf_len(&txn->buf), sha1buf); buf_ensure(&txn->buf, WS_AKEY_LEN+1); accept = buf_base(&txn->buf); r = sasl_encode64((char *) sha1buf, SHA1_DIGEST_LENGTH, (char *) accept, WS_AKEY_LEN+1, NULL); if (r != SASL_OK) syslog(LOG_WARNING, "sasl_encode64: %d", r); } /* Create server context */ r = wslay_event_context_server_init(&ev, &callbacks, txn); if (r) { syslog(LOG_WARNING, "wslay_event_context_init: %s", wslay_strerror(r)); return HTTP_SERVER_ERROR; } /* Create channel context */ ctx = xzmalloc(sizeof(struct ws_context)); ctx->event = ev; ctx->accept = accept; ctx->protocol = protocol; ctx->data_cb = data_cb; txn->ws_ctx = ctx; /* Check for supported WebSocket extensions */ parse_extensions(txn); /* Prepare log buffer */ /* Add client data */ buf_printf(&ctx->log, "%s", txn->conn->clienthost); if (httpd_userid) buf_printf(&ctx->log, " as \"%s\"", httpd_userid); if ((hdr = spool_getheader(txn->req_hdrs, "User-Agent"))) { buf_printf(&ctx->log, " with \"%s\"", hdr[0]); if ((hdr = spool_getheader(txn->req_hdrs, "X-Client"))) buf_printf(&ctx->log, " by \"%s\"", hdr[0]); else if ((hdr = spool_getheader(txn->req_hdrs, "X-Requested-With"))) buf_printf(&ctx->log, " by \"%s\"", hdr[0]); } /* Add request-line */ buf_printf(&ctx->log, "; \"WebSocket/%s via %s\"", protocol ? protocol : "echo" , txn->req_line.ver); ctx->log_tail = buf_len(&ctx->log); /* Tell client that WebSocket negotiation has succeeded */ if (txn->conn->sess_ctx) { /* Treat WS data as chunked response */ txn->flags.te = TE_CHUNKED; response_header(HTTP_OK, txn); /* Force the response to the client immediately */ prot_flush(httpd_out); } else response_header(HTTP_SWITCH_PROT, txn); /* Set connection as non-blocking */ prot_NONBLOCK(txn->conn->pin); /* Don't do telemetry logging in prot layer */ prot_setlog(txn->conn->pin, PROT_NO_FD); prot_setlog(txn->conn->pout, PROT_NO_FD); return 0; }
static int squatter_build_query(search_builder_t *bx, const char *query) { tok_t tok = TOK_INITIALIZER(query, NULL, 0); char *p; char *q; int r = 0; int part; int utf8 = charset_lookupname("utf-8"); while ((p = tok_next(&tok))) { if (!strncasecmp(p, "__begin:", 8)) { q = p + 8; if (!strcasecmp(q, "and")) bx->begin_boolean(bx, SEARCH_OP_AND); else if (!strcasecmp(q, "or")) bx->begin_boolean(bx, SEARCH_OP_OR); else if (!strcasecmp(q, "not")) bx->begin_boolean(bx, SEARCH_OP_NOT); else goto error; continue; } if (!strncasecmp(p, "__end:", 6)) { q = p + 6; if (!strcasecmp(q, "and")) bx->end_boolean(bx, SEARCH_OP_AND); else if (!strcasecmp(q, "or")) bx->end_boolean(bx, SEARCH_OP_OR); else if (!strcasecmp(q, "not")) bx->end_boolean(bx, SEARCH_OP_NOT); else goto error; continue; } /* everything else is a ->match() of some kind */ q = strchr(p, ':'); if (q) q++; if (!q) { part = SEARCH_PART_ANY; q = p; } else if (!strncasecmp(p, "to:", 3)) part = SEARCH_PART_TO; else if (!strncasecmp(p, "from:", 5)) part = SEARCH_PART_FROM; else if (!strncasecmp(p, "cc:", 3)) part = SEARCH_PART_CC; else if (!strncasecmp(p, "bcc:", 4)) part = SEARCH_PART_BCC; else if (!strncasecmp(p, "subject:", 8)) part = SEARCH_PART_SUBJECT; else if (!strncasecmp(p, "listid:", 7)) part = SEARCH_PART_LISTID; else if (!strncasecmp(p, "contenttype:", 12)) part = SEARCH_PART_TYPE; else if (!strncasecmp(p, "header:", 7)) part = SEARCH_PART_HEADERS; else if (!strncasecmp(p, "body:", 5)) part = SEARCH_PART_BODY; else goto error; q = charset_convert(q, utf8, charset_flags); bx->match(bx, part, q); free(q); } r = 0; out: tok_fini(&tok); return r; error: syslog(LOG_ERR, "bad query expression at \"%s\"", p); r = IMAP_PROTOCOL_ERROR; goto out; }
/* Parse Sec-WebSocket-Extensions header(s) for interesting extensions */ static void parse_extensions(struct transaction_t *txn) { struct ws_context *ctx = (struct ws_context *) txn->ws_ctx; const char **ext_hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Extensions"); int i; /* Look for interesting extensions. Unknown == ignore */ for (i = 0; ext_hdr && ext_hdr[i]; i++) { tok_t ext = TOK_INITIALIZER(ext_hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT); char *token; while ((token = tok_next(&ext))) { struct ws_extension *extp = extensions; tok_t param; tok_initm(¶m, token, ";", TOK_TRIMLEFT|TOK_TRIMRIGHT); token = tok_next(¶m); /* Locate a matching extension */ while (extp->name && strcmp(token, extp->name)) extp++; /* Check if client wants per-message compression */ if (extp->flag == EXT_PMCE_DEFLATE) { unsigned client_max_wbits = MAX_WBITS; ctx->pmce.deflate.max_wbits = MAX_WBITS; /* Process parameters */ while ((token = tok_next(¶m))) { char *value = strchr(token, '='); if (value) *value++ = '\0'; if (!strcmp(token, "server_no_context_takeover")) { ctx->pmce.deflate.no_context = 1; } else if (!strcmp(token, "client_no_context_takeover")) { /* Don't HAVE to do anything here */ } else if (!strcmp(token, "server_max_window_bits")) { if (value) { if (*value == '"') value++; ctx->pmce.deflate.max_wbits = atoi(value); } else ctx->pmce.deflate.max_wbits = 0; /* force error */ } else if (!strcmp(token, "client_max_window_bits")) { if (value) { if (*value == '"') value++; client_max_wbits = atoi(value); } } } #ifdef HAVE_ZLIB /* Reconfigure compression context for raw deflate */ if (txn->zstrm) deflateEnd(txn->zstrm); else txn->zstrm = xmalloc(sizeof(z_stream)); if (deflateInit2(txn->zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -ctx->pmce.deflate.max_wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) { free(txn->zstrm); txn->zstrm = NULL; } if (txn->zstrm) { /* Configure decompression context for raw deflate */ ctx->pmce.deflate.zstrm = xmalloc(sizeof(z_stream)); if (inflateInit2(ctx->pmce.deflate.zstrm, -client_max_wbits) != Z_OK) { free(ctx->pmce.deflate.zstrm); ctx->pmce.deflate.zstrm = NULL; } } #endif /* HAVE_ZLIB */ if (ctx->pmce.deflate.zstrm) { /* Compression has been enabled */ wslay_event_config_set_allowed_rsv_bits(ctx->event, WSLAY_RSV1_BIT); ctx->ext = extp->flag; } } tok_fini(¶m); } tok_fini(&ext); } }