aboutsummaryrefslogblamecommitdiffstats
path: root/libcoopgamma_check_error__.c
blob: 44fb8b791a50c23ebe0db1ee6253632b4e2e3be3 (plain) (tree)































































































                                                                                                              
/* See LICENSE file for copyright and license details. */
#include "common.h"


/**
 * Check whether the server sent an error, if so copy it to `ctx`
 * 
 * This function will also reports EBADMSG if the message ID
 * that the message is a response to does not match the request
 * information, or if it is missing
 * 
 * @param   ctx    The state of the library, must be connected
 * @param   async  Information about the request
 * @return         1 if the server sent an error (even indicating success),
 *                 0 on success, -1 on failure. Information about failure
 *                 is copied `ctx`.
 */
int
libcoopgamma_check_error__(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async)
{
	char temp[3 * sizeof(uint64_t) + 1];
	size_t old_tail = ctx->inbound_tail;
	char *line;
	char *value;
	int command_ok = 0;
	int have_in_response_to = 0;
	int have_error = 0;
	int bad = 0;
	char *payload;
	size_t n;

	for (;;) {
		line = libcoopgamma_next_header__(ctx);
		value = &strchr(line, ':')[2U];
		if (!*line) {
			break;
		} else if (!strcmp(line, "Command: error")) {
			command_ok = 1;
		} else if (strstr(line, "In response to: ") == line) {
			uint32_t id = (uint32_t)atol(value);
			have_in_response_to = 1 + !!have_in_response_to;
			if (id != async->message_id) {
				bad = 1;
			} else {
				sprintf(temp, "%" PRIu32, id);
				if (strcmp(value, temp))
					bad = 1;
			}
		} else if (strstr(line, "Error: ") == line) {
			have_error = 1 + !!have_error;
			ctx->error.server_side = 1;
			ctx->error.custom = (strstr(value, "custom") == value);
			if (ctx->error.custom) {
				if (value[6] == '\0') {
					ctx->error.number = 0;
					continue;
				} else if (value[6] != ' ') {
					bad = 1;
					continue;
				}
				value += 7;
			}
			ctx->error.number = (uint64_t)atoll(value);
			sprintf(temp, "%" PRIu64, ctx->error.number);
			if (strcmp(value, temp))
				bad = 1;
		}
	}

	if (!command_ok) {
		ctx->inbound_tail = old_tail;
		return 0;
	}

	payload = libcoopgamma_next_payload__(ctx, &n);
	if (payload) {
		if (memchr(payload, '\0', n) || payload[n - 1U] != '\n')
			goto badmsg;
		ctx->error.description = malloc(n);
		if (ctx->error.description == NULL)
			goto fail;
		memcpy(ctx->error.description, payload, n - 1U);
		ctx->error.description[n - 1U] = '\0';
	}

	if (bad || have_in_response_to != 1 || have_error != 1)
		goto badmsg;

	return 1;

badmsg:
	errno = EBADMSG;
fail:
	copy_errno(ctx);
	return -1;
}