/* Licensed to Stichting The Commons Conservancy (TCC) under one or more * contributor license agreements. See the AUTHORS file distributed with * this work for additional information regarding copyright ownership. * TCC licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "signtext.h" #include "crypto.h" #include "message.h" #include #include #include #include #include #include "cJSON.h" #include void message_cancel(SignTextData *signtext) { // const char *uuid = adw_view_stack_get_visible_child_name(signtext->stack); // SignTextInstance *instance = g_object_get_data(G_OBJECT (signtext->stack), uuid); // if (instance) { /* * Send an error to show we cancelled. */ // message_send_error(instance, "error:userCancel"); // signtext_instance_free(instance); // } } void message_receive(SignTextData* signtext) { int rv; /* read single message */ uint32_t length = 0; int actual = 0; // Set "stdin" to have binary mode: rv = _setmode(_fileno(stdin), _O_BINARY); if (rv == -1) { perror("_setmode failed: "); return; } while (1) { if (fread(&length, sizeof(length), 1, stdin) == 1) { char* buffer = malloc(length); if (fread(buffer, length, 1, stdin) != 1) { // error assert(0); } fputs("Request: ", stderr); fwrite(buffer, length, 1, stderr); fputs("\r\n", stderr); PostMessage(signtext->hwnd, WM_MESSAGE_RECEIVED, length, (LPARAM)buffer); } else if (feof(stdin)) { /* * Browser went away, our work here is done. */ PostMessage(signtext->hwnd, WM_DESTROY, 0, 0); break; } else { // error assert(0); } } } void message_receive_do(SignTextData* signtext, char* buffer, uint32_t length) { cJSON* json = cJSON_ParseWithLength(buffer, length); if (!json) { // error assert(0); } const cJSON* url = cJSON_GetObjectItemCaseSensitive(json, "url"); if (!cJSON_IsString(url) || (url->valuestring == NULL)) { // error assert(0); } const cJSON* uuid = cJSON_GetObjectItemCaseSensitive(json, "uuid"); if (!cJSON_IsString(uuid) || (uuid->valuestring == NULL)) { // error assert(0); } const cJSON* id = cJSON_GetObjectItemCaseSensitive(json, "id"); if (!cJSON_IsNumber(id)) { // error assert(0); } const cJSON* request = cJSON_GetObjectItemCaseSensitive(json, "request"); if (!request) { // error assert(0); } LPWSTR wUuid = signtext_utf8ToUtf16(uuid->valuestring, strlen(uuid->valuestring)); SignTextInstance* instance; instance = (SignTextInstance*)GetProp(signtext->hwnd, wUuid); if (!instance) { fprintf(stderr, "Creating instance - should happen just once"); instance = signtext_instance_new(signtext, id->valueint, uuid->valuestring, url->valuestring); instance->wUuid = wUuid; SetProp(signtext->hwnd, wUuid, instance); } else { fprintf(stderr, "Using existing instance - second and subsequent message"); free(wUuid); } /* * Did we get a message on a cancelled connection? */ if (instance->is_cancelled) { /* do nothing, someone will be around to clean this up */ } /* * Process request. * * A string request is a data payload, append it to existing text buffer and * send back an ACK. */ else if (cJSON_IsString(request)) { size_t length = strlen(request->valuestring); instance->in_buffer = realloc(instance->in_buffer, instance->in_length + length + 1); memcpy(instance->in_buffer + instance->in_length, request->valuestring, length); instance->in_length += length; instance->in_buffer[instance->in_length] = 0; /* * A response with a value of TRUE means ACK, send us the next bit of data. */ message_send_boolean_response(instance, TRUE); } /* * An object request means the data is done and we're ready to let the end user * see the UI. * * Make the UI visible. */ else if (cJSON_IsObject(request)) { const cJSON* digestAlgorithm = cJSON_GetObjectItemCaseSensitive(json, "digestAlgorithm"); if (!cJSON_IsString(digestAlgorithm) || (digestAlgorithm->valuestring == NULL)) { /* log a warning and ignore */ } else { instance->digestAlgorithm = _strdup(digestAlgorithm->valuestring); } PostMessage(signtext->hwnd, WM_MESSAGE_SHOW, 0, (LPARAM)instance); } /* * Process ACK response. * * A boolean request is an ACK/NAK recognising a chunk of data that has been * sent back. Send the next bit of the response. */ else if (cJSON_IsTrue(request)) { message_send_response(instance); } else { // error assert(0); } free(buffer); } void message_send_response(SignTextInstance* instance) { size_t length = instance->out_length - instance->out_offset; if (length > MESSAGE_MAX) { length = MESSAGE_MAX; } /* * Any data to send, send it, otherwise it's EOF, which is FALSE. */ if (length) { char* buffer = malloc(length + 1); StringCbCopyNA(buffer, length + 1, instance->out_buffer + instance->out_offset, length); instance->out_offset += length; message_send_string_response(instance, buffer); free(buffer); } else { instance->response_done = TRUE; message_send_boolean_response(instance, FALSE); } } void message_send(SignTextData* signtext, char *buffer, uint32_t length) { int rv; // todo: mutex needed here, or iovec? rv = _setmode(_fileno(stdout), _O_BINARY); if (rv == -1) { perror("_setmode failed: "); return; } if (fwrite(&length, sizeof(length), 1, stdout) != 1) { // error assert(0); } if (fwrite(buffer, length, 1, stdout) != 1) { // error assert(0); } fflush(stdout); fputs("Response: ", stderr); fwrite(buffer, length, 1, stderr); fputs("\r\n", stderr); free(buffer); PostMessage(signtext->hwnd, WM_MESSAGE_SENT, 0, 0); } void message_send_boolean_response(SignTextInstance* instance, int response) { cJSON* root = cJSON_CreateObject(); if (root == NULL) { // error assert(0); } if (!cJSON_AddStringToObject(root, "uuid", instance->uuid)) { // error assert(0); } if (!cJSON_AddNumberToObject(root, "id", instance->id)) { // error assert(0); } if (!cJSON_AddBoolToObject(root, "response", response)) { // error assert(0); } const char *buffer = cJSON_PrintUnformatted(root); if (!buffer) { // error assert(0); } PostMessage(instance->signtext->hwnd, WM_MESSAGE_SEND, strlen(buffer), (LPARAM)buffer); cJSON_Delete(root); } void message_send_string_response(SignTextInstance* instance, const char *response) { cJSON* root = cJSON_CreateObject(); if (root == NULL) { // error assert(0); } if (!cJSON_AddStringToObject(root, "uuid", instance->uuid)) { // error assert(0); } if (!cJSON_AddNumberToObject(root, "id", instance->id)) { // error assert(0); } if (!cJSON_AddStringToObject(root, "response", response)) { // error assert(0); } char* buffer = cJSON_PrintUnformatted(root); if (!buffer) { // error assert(0); } PostMessage(instance->signtext->hwnd, WM_MESSAGE_SEND, strlen(buffer), (LPARAM)buffer); cJSON_Delete(root); } void message_send_cancel_response(SignTextInstance* instance) { instance->is_cancelled = TRUE; cJSON* root = cJSON_CreateObject(); if (root == NULL) { // error assert(0); } if (!cJSON_AddStringToObject(root, "uuid", instance->uuid)) { // error assert(0); } if (!cJSON_AddNumberToObject(root, "id", instance->id)) { // error assert(0); } if (!cJSON_AddStringToObject(root, "error", "error:userCancel")) { // error assert(0); } char* buffer = cJSON_PrintUnformatted(root); if (!buffer) { // error assert(0); } // todo check me PostMessage(instance->signtext->hwnd, WM_MESSAGE_SEND, strlen(buffer), (LPARAM)buffer); cJSON_Delete(root); } void message_close(SignTextData* signtext) { /* to shut down, close stdin and stdout */ fclose(stdin); fclose(stdout); PostMessage(signtext->hwnd, WM_DESTROY, 0, 0); }