Exemple #1
0
/*
 * Add iCalendar recur-rule-parts to a structured element.
 */
void icalrecurrencetype_add_as_xxx(struct icalrecurrencetype *recur, void *obj,
                                   void (*add_int)(void *, const char *, int),
                                   void (*add_str)(void *, const char *,
                                           const char *))
{
    char *rrule, *rpart;
    tok_t rparts;

    /* generate an iCal RRULE string */
    rrule = icalrecurrencetype_as_string_r(recur);

    /* split string into rparts & values */
    tok_initm(&rparts, rrule, "=;", TOK_TRIMLEFT|TOK_TRIMRIGHT);
    while ((rpart = tok_next(&rparts))) {
        if (!strcmp(rpart, "UNTIL")) {
            /* need to translate date format to ISO */
            struct icaltimetype until = icaltime_from_string(tok_next(&rparts));

            add_str(obj, "until", icaltime_as_iso_string(until));
        }
        else {
            /* assume the rpart has multiple values - split them */
            tok_t vlist;
            char *val, *p;

            tok_init(&vlist, tok_next(&rparts), ",",
                     TOK_TRIMLEFT|TOK_TRIMRIGHT);
            while ((val = tok_next(&vlist))) {
                if (add_int) {
                    /* try converting value to integer */
                    int n = strtol(val, &p, 10);

                    if (n && !*p) {
                        add_int(obj, lcase(rpart), n);
                        continue;
                    }
                }

                add_str(obj, lcase(rpart), val);
            }
            tok_fini(&vlist);
        }
    }
    tok_fini(&rparts);

    free(rrule);
}
Exemple #2
0
EXPORTED char *tok_next(tok_t *t)
{
    const char *sep;
    char *token;

    /* initialising us with a NULL buffer is harmless */
    if (!t->buf)
	return NULL;

    /* use the given separator or the default separator string */
    sep = (t->sep ? t->sep : " \t\n\r");

    if ((t->flags & TOK_EMPTY)) {
	if ((t->flags & _TOK_FIRST)) {
	    t->flags &= ~_TOK_FIRST;
	    t->state = t->buf;
	}
	token = strsep(&t->state, sep);
    }
    else {
	char *buf = NULL;

	if ((t->flags & _TOK_FIRST)) {
	    /* strtok_r() wants the buffer only the first time */
	    t->flags &= ~_TOK_FIRST;
	    buf = t->buf;
	}

	token = strtok_r(buf, sep, &t->state);
    }

    if (!token) {
	/* end of tokens; clean up the tok_t to ensure we don't
	 * leak any memory even if the caller doesn't call tok_fini() */
	tok_fini(t);
	return NULL;
    }

    /* we have a token, perform any additional munging */

    if ((t->flags & TOK_TRIMLEFT)) {
	while (*token && isspace(*token))
	    token++;
    }

    if ((t->flags & TOK_TRIMRIGHT)) {
	char *p = token + strlen(token) - 1;
	while (p >= token && isspace(*p))
	    *p-- = '\0';
    }

    t->curr = token;
    return token;
}
Exemple #3
0
/*
 * userdeny() checks to see if 'user' is denied access to 'service'
 * Returns 1 if a matching deny entry exists in DB, otherwise returns 0.
 */
EXPORTED int userdeny(const char *user, const char *service, char *msgbuf, size_t bufsiz)
{
    int r, ret = 0; /* allow access by default */
    const char *data = NULL;
    size_t datalen;
    struct buf buf = BUF_INITIALIZER;
    char *wild = NULL;
    const char *msg = NULL;
    tok_t tok;
    char *pat;
    int not;

    if (!denydb) denydb_open(/*create*/0);
    if (!denydb) return 0;

    memset(&tok, 0, sizeof(tok));

    /* fetch entry for user */
    syslog(LOG_DEBUG, "fetching user_deny.db entry for '%s'", user);
    do {
	r = cyrusdb_fetch(denydb, user, strlen(user), &data, &datalen, NULL);
    } while (r == CYRUSDB_AGAIN);

    /* XXX  Should we try to reopen the DB if we get IOERROR?
	    This might be necessary when using SQL backend
	    and we lose the connection.
    */

    if (r || !data || !datalen) {
	/* ignore non-existent/empty entry, report all other errors */
	if (r != CYRUSDB_NOTFOUND) {
	    syslog(LOG_WARNING,
		   "DENYDB_ERROR: error reading entry '%s': %s",
		   user, cyrusdb_strerror(r));
	}
	goto out;
    }
    buf_init_ro(&buf, data, datalen);

	/* parse the data */
    r = parse_record(&buf, &wild, &msg);
    if (r) {
	syslog(LOG_WARNING,
	       "DENYDB_ERROR: invalid entry for '%s'", user);
	goto out;
    }

    /* scan wildmat right to left for a match against our service */
    syslog(LOG_DEBUG, "wild: '%s'   service: '%s'", wild, service);
    tok_initm(&tok, wild, ",", 0);
    while ((pat = tok_next(&tok))) {
	/* XXX  trim leading & trailing whitespace? */

	/* is it a negated pattern? */
	not = (*pat == '!');
	if (not) ++pat;

	syslog(LOG_DEBUG, "pat %d:'%s'", not, pat);

	/* see if pattern matches our service */
	if (wildmat(service, pat)) {
	    /* match ==> we're done */
	    ret = !not;
	    if (msgbuf) strlcpy(msgbuf, msg, bufsiz);
	    break;
	}
    }

out:
    tok_fini(&tok);
    buf_free(&buf);
    return ret;
}
Exemple #4
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;
}
Exemple #5
0
/*
 * Add the proper XML element for an iCalendar value.
 */
static void icalproperty_add_value_as_xml_element(xmlNodePtr xprop,
        icalproperty *prop)

{
    const char *type, *str = NULL;
    xmlNodePtr xtype;
    const icalvalue *value;
    char buf[40];

    /* Add type */
    type = lcase(icalmemory_tmp_copy(
                     icalproperty_value_kind_as_string(prop)));
    xtype = xmlNewChild(xprop, NULL, BAD_CAST type, NULL);


    /* Add value */
    value = icalproperty_get_value(prop);

    switch (icalvalue_isa(value)) {
    case ICAL_DATE_VALUE:
        str = icaltime_as_iso_string(icalvalue_get_date(value));
        break;

    case ICAL_DATETIME_VALUE:
        str = icaltime_as_iso_string(icalvalue_get_datetime(value));
        break;

    case ICAL_DATETIMEPERIOD_VALUE: {
        struct icaldatetimeperiodtype dtp =
            icalvalue_get_datetimeperiod(value);

        if (!icaltime_is_null_time(dtp.time)) {
            str = icaltime_as_iso_string(dtp.time);
            break;
        }
        else {
            icalperiodtype_add_as_xml_element(xtype, dtp.period);
            return;
        }
    }

    case ICAL_GEO_VALUE: {
        struct icalgeotype geo = icalvalue_get_geo(value);

        snprintf(buf, sizeof(buf), "%f", geo.lat);
        xmlNewTextChild(xtype, NULL, BAD_CAST "latitude", BAD_CAST buf);
        snprintf(buf, sizeof(buf), "%f", geo.lon);
        xmlNewTextChild(xtype, NULL, BAD_CAST "longitude", BAD_CAST buf);
        return;
    }

    case ICAL_PERIOD_VALUE:
        icalperiodtype_add_as_xml_element(xtype, icalvalue_get_period(value));
        return;

    case ICAL_RECUR_VALUE: {
        struct icalrecurrencetype recur = icalvalue_get_recur(value);

        icalrecurrencetype_add_as_xxx(&recur, xtype, NULL,
                                      &icalrecur_add_string_as_xml_element);
        return;
    }

    case ICAL_REQUESTSTATUS_VALUE: {
        struct icalreqstattype stat = icalvalue_get_requeststatus(value);

        if (!stat.desc) stat.desc = icalenum_reqstat_desc(stat.code);

        snprintf(buf, sizeof(buf), "%u.%u",
                 icalenum_reqstat_major(stat.code),
                 icalenum_reqstat_minor(stat.code));
        xmlNewTextChild(xtype, NULL, BAD_CAST "code", BAD_CAST buf);
        xmlNewTextChild(xtype, NULL, BAD_CAST "description", BAD_CAST stat.desc);
        if (stat.debug)
            xmlNewTextChild(xtype, NULL, BAD_CAST "data", BAD_CAST stat.debug);

        return;
    }

    case ICAL_TRIGGER_VALUE: {
        struct icaltriggertype trig = icalvalue_get_trigger(value);

        if (!icaltime_is_null_time(trig.time))
            str = icaltime_as_iso_string(trig.time);
        else
            str = icaldurationtype_as_ical_string(trig.duration);
        break;
    }

    case ICAL_UTCOFFSET_VALUE:
        str = icalvalue_utcoffset_as_iso_string(value);
        break;

    default:
        str = icalvalue_as_ical_string(value);

        switch (icalproperty_isa(prop)) {
        case ICAL_CATEGORIES_PROPERTY:
        case ICAL_RESOURCES_PROPERTY:
        case ICAL_POLLPROPERTIES_PROPERTY:
            if (strchr(str, ',')) {
                /* Handle multi-valued properties */
                tok_t tok;

                tok_init(&tok, str, ",", TOK_TRIMLEFT|TOK_TRIMRIGHT|TOK_EMPTY);
                str = tok_next(&tok);
                xmlAddChild(xtype, xmlNewText(BAD_CAST str));

                while ((str = tok_next(&tok))) {
                    if (*str) {
                        xtype = xmlNewChild(xprop, NULL, BAD_CAST type, NULL);
                        xmlAddChild(xtype, xmlNewText(BAD_CAST str));
                    }
                }
                tok_fini(&tok);
                return;
            }

        default:
            break;
        }

        break;
    }

    if (str) xmlAddChild(xtype, xmlNewText(BAD_CAST str));
}
Exemple #6
0
/*
 * Construct a JSON array for an iCalendar property.
 */
static json_t *icalproperty_as_json_array(icalproperty *prop)
{
    icalproperty_kind prop_kind;
    const char *x_name, *property_name = NULL;
    icalparameter *param;
    const char *type = NULL;
    const icalvalue *value;
    json_t *jprop, *jparams;

    if (!prop) return NULL;

    prop_kind = icalproperty_isa(prop);
    x_name = icalproperty_get_x_name(prop);

    if (prop_kind == ICAL_X_PROPERTY && x_name)
        property_name = x_name;
    else
        property_name = icalproperty_kind_to_string(prop_kind);

    if (!property_name) {
        icalerror_warn("Got a property of an unknown kind.");
        return NULL;
    }

    /* Create property array */
    jprop = json_array();


    /* Add property name */
    json_array_append_new(jprop,
                          json_string(lcase(icalmemory_tmp_copy(property_name))));


    /* Add parameters */
    jparams = json_object();
    for (param = icalproperty_get_first_parameter(prop, ICAL_ANY_PARAMETER);
         param != 0;
         param = icalproperty_get_next_parameter(prop, ICAL_ANY_PARAMETER)) {

        if (icalparameter_isa(param) == ICAL_VALUE_PARAMETER) continue;

        icalparameter_as_json_object_member(param, jparams);
    }
    json_array_append_new(jprop, jparams);


    /* Add type */
    type = icalproperty_value_kind_as_string(prop);
    json_array_append_new(jprop, json_string(lcase(icalmemory_tmp_copy(type))));


    /* Add value */
    value = icalproperty_get_value(prop);
    if (value) {
        switch (icalproperty_isa(prop)) {
        case ICAL_CATEGORIES_PROPERTY:
        case ICAL_RESOURCES_PROPERTY:
        case ICAL_POLLPROPERTIES_PROPERTY:
            if (icalvalue_isa(value) == ICAL_TEXT_VALUE) {
                /* Handle multi-valued properties */
                const char *str = icalvalue_as_ical_string(value);
                tok_t tok;

                tok_init(&tok, str, ",", TOK_TRIMLEFT|TOK_TRIMRIGHT|TOK_EMPTY);
                while ((str = tok_next(&tok))) {
                    if (*str) json_array_append_new(jprop, json_string(str));
                }
                tok_fini(&tok);
                break;
            }

        default:
            json_array_append_new(jprop, icalvalue_as_json_object(value));
            break;
        }
    }

    return jprop;
}
Exemple #7
0
EXPORTED struct backend *backend_connect(struct backend *ret_backend, const char *server,
				struct protocol_t *prot, const char *userid,
				sasl_callback_t *cb, const char **auth_status,
				int logfd)

{
    /* need to (re)establish connection to server or create one */
    int sock = -1;
    int r;
    int err = -1;
    int do_tls = 0;
    int noauth = 0;
    struct addrinfo hints, *res0 = NULL, *res;
    struct sockaddr_un sunsock;
    struct backend *ret;

    if (!ret_backend) {
	ret = xzmalloc(sizeof(struct backend));
	strlcpy(ret->hostname, server, sizeof(ret->hostname));
	ret->timeout = NULL;
    }
    else
	ret = ret_backend;

    if (server[0] == '/') { /* unix socket */
	res0 = &hints;
	memset(res0, 0, sizeof(struct addrinfo));
	res0->ai_family = PF_UNIX;
	res0->ai_socktype = SOCK_STREAM;

	res0->ai_addr = (struct sockaddr *) &sunsock;
	res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1;
#ifdef SIN6_LEN
	res0->ai_addrlen += sizeof(sunsock.sun_len);
	sunsock.sun_len = res0->ai_addrlen;
#endif
	sunsock.sun_family = AF_UNIX;
	strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path));

	if (!strcmp(prot->sasl_service, "lmtp") ||
	    !strcmp(prot->sasl_service, "csync")) {
	    noauth = 1;
	}
    }
    else { /* inet socket */
	char host[1024], *p;
	const char *service = prot->service;

	/* Parse server string for possible port and options */
	strlcpy(host, server, sizeof(host));
	if ((p = strchr(host, ':'))) {
	    *p++ = '\0';
	    service = p;

	    if ((p = strchr(service, '/'))) {
		tok_t tok;
		char *opt;

		*p++ = '\0';
		tok_initm(&tok, p, "/", 0);
		while ((opt = tok_next(&tok))) {
		    if (!strcmp(opt, "tls")) do_tls = 1;
		    else if (!strcmp(opt, "noauth")) noauth = 1;
		}
		tok_fini(&tok);
	    }
	}

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	err = getaddrinfo(host, service, &hints, &res0);
	if (err) {
	    syslog(LOG_ERR, "getaddrinfo(%s) failed: %s",
		   server, gai_strerror(err));
	    goto error;
	}
    }

    for (res = res0; res; res = res->ai_next) {
	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (sock < 0)
	    continue;

	/* Do a non-blocking connect() */
	nonblock(sock, 1);
	if (!connect(sock, res->ai_addr, res->ai_addrlen)) {
	    /* connect() succeeded immediately */
	    break;
	}
	else if (errno == EINPROGRESS) {
	    /* connect() in progress */
	    int n;
	    fd_set wfds, rfds;
	    time_t now = time(NULL);
	    time_t timeout = now + config_getint(IMAPOPT_CLIENT_TIMEOUT);
	    struct timeval waitfor;

	    /* select() socket for writing until we succeed, fail, or timeout */
	    do {
		FD_ZERO(&wfds);
		FD_SET(sock, &wfds);
		rfds = wfds;
		waitfor.tv_sec = timeout - now;
		waitfor.tv_usec = 0;

		n = select(sock + 1, &rfds, &wfds, NULL, &waitfor);
		now = time(NULL);

		/* Retry select() if interrupted */
	    } while (n < 0 && errno == EINTR && now < timeout);

	    if (!n) {
		/* select() timed out */
		errno = ETIMEDOUT;
	    }
	    else if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &wfds)) {
		/* Socket is ready for I/O - get SO_ERROR to determine status */
		socklen_t errlen = sizeof(err);

		if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen) &&
		    !(errno = err)) {
		    /* connect() succeeded */
		    break;
		}
	    }
	}

	close(sock);
	sock = -1;
    }

    if (sock < 0) {
	if (res0 != &hints) freeaddrinfo(res0);
	syslog(LOG_ERR, "connect(%s) failed: %m", server);
	goto error;
    }

    /* Reset socket to blocking */
    nonblock(sock, 0);

    memcpy(&ret->addr, res->ai_addr, res->ai_addrlen);
    if (res0 != &hints)
	freeaddrinfo(res0);

    ret->in = prot_new(sock, 0);
    ret->out = prot_new(sock, 1);
    ret->sock = sock;
    prot_settimeout(ret->in, config_getint(IMAPOPT_CLIENT_TIMEOUT));
    prot_setflushonread(ret->in, ret->out);
    ret->prot = prot;

    /* use literal+ to send literals */
    prot_setisclient(ret->in, 1);
    prot_setisclient(ret->out, 1);

    /* Start TLS if required */
    if (do_tls) r = backend_starttls(ret, NULL, NULL, NULL);

    /* Login to the server */
    if (prot->type == TYPE_SPEC)
	r = prot->u.spec.login(ret, userid, cb, auth_status, noauth);
    else
	r = backend_login(ret, userid, cb, auth_status, noauth);

    if (r) goto error;

    if (logfd >= 0) {
	prot_setlog(ret->in, logfd);
	prot_setlog(ret->out, logfd);
    }
    else prot_settimeout(ret->in, 0);

    return ret;

error:
    forget_capabilities(ret);
    if (ret->in) {
	prot_free(ret->in);
	ret->in = NULL;
    }
    if (ret->out) {
	prot_free(ret->out);
	ret->out = NULL;
    }
    if (sock >= 0)
	close(sock);
    if (ret->saslconn) {
	sasl_dispose(&ret->saslconn);
	ret->saslconn = NULL;
    }
    if (!ret_backend)
	free(ret);
    return NULL;
}
Exemple #8
0
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;
}
Exemple #9
0
/* 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(&param, token, ";", TOK_TRIMLEFT|TOK_TRIMRIGHT);
            token = tok_next(&param);

            /* 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(&param))) {
                    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(&param);
        }

        tok_fini(&ext);
    }
}