aboutsummaryrefslogtreecommitdiffstats
path: root/libcoopgamma_check_error__.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcoopgamma_check_error__.c')
-rw-r--r--libcoopgamma_check_error__.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/libcoopgamma_check_error__.c b/libcoopgamma_check_error__.c
new file mode 100644
index 0000000..44fb8b7
--- /dev/null
+++ b/libcoopgamma_check_error__.c
@@ -0,0 +1,96 @@
+/* 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;
+}