/* This file is part of Taler. Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V. Copyright (C) 2018 Taler Systems SA Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Taler; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file twister_api.c * @brief api to control twister proxy * @author Christian Grothoff * @author Marcello Stanisci */ #include "platform.h" #include #include "taler_twister_service.h" #include "twister.h" #define LOG(kind,...) \ GNUNET_log_from (kind, "twister-api",__VA_ARGS__) /** * Opaque handle for asynchronous operation. */ struct TALER_TWISTER_Operation { /** * Pointer to next operation. */ struct TALER_TWISTER_Operation *next; /** * Pointer to previous operation. */ struct TALER_TWISTER_Operation *prev; /** * Pointer to main handle (= connection to the twister). */ struct TALER_TWISTER_Handle *h; /** * Callback for this operation. */ GNUNET_SCHEDULER_TaskCallback cb; /** * Closure to pass the callback above. */ void *cb_cls; }; /** * Handle for talking with the Twister service. */ struct TALER_TWISTER_Handle { /** * Configuration to use. */ const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Message queue (if available). */ struct GNUNET_MQ_Handle *mq; /** * First pending operation. */ struct TALER_TWISTER_Operation *op_head; /** * Last pending operation. */ struct TALER_TWISTER_Operation *op_tail; }; /** * Generic error handler, called with the appropriate * error code and the same closure specified at the creation of * the message queue. * Not every message queue implementation supports an error handler. * * @param cls closure with the `struct TALER_TWISTER_Handle *` * @param error error code */ static void mq_error_handler (void *cls, enum GNUNET_MQ_Error error) { struct TALER_TWISTER_Handle *h = cls; GNUNET_MQ_destroy (h->mq); h->mq = NULL; /* FIXME: maybe give test case nicer way to shut down... */ GNUNET_assert (0); } /** * Type of a function to call when we receive a message * from the service. * * @param cls closure * @param client_msg message received */ static void handle_acknowledgement (void *cls, const struct GNUNET_MessageHeader *ack) { struct TALER_TWISTER_Handle *h = cls; struct TALER_TWISTER_Operation *op; op = h->op_head; GNUNET_assert (NULL != op); /* twister very wrong, fail test */ GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op); if (NULL != op->cb) op->cb (op->cb_cls); GNUNET_free (op); } /** * Connect to the twister service. * * @param cfg the configuration to use * @return handle to use in #TALER_TWISTER_disconnect to disconnect */ struct TALER_TWISTER_Handle * TALER_TWISTER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct TALER_TWISTER_Handle *h; h = GNUNET_new (struct TALER_TWISTER_Handle); h->cfg = cfg; { struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_fixed_size (acknowledgement, TWISTER_MESSAGE_TYPE_ACKNOWLEDGEMENT, struct GNUNET_MessageHeader, h), GNUNET_MQ_handler_end () }; LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to twister service.\n"); h->mq = GNUNET_CLIENT_connect (h->cfg, "twister", handlers, &mq_error_handler, h); } if (NULL == h->mq) { LOG (GNUNET_ERROR_TYPE_ERROR, "Could not connect to twister service\n"); GNUNET_free (h); return NULL; } return h; } /** * Disconnect from twister service. * * @param h handle to destroy */ void TALER_TWISTER_disconnect (struct TALER_TWISTER_Handle *h) { struct TALER_TWISTER_Operation *op; while (NULL != (op = h->op_head)) { GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op); GNUNET_free (op); } if (NULL != h->mq) { GNUNET_MQ_destroy (h->mq); h->mq = NULL; } GNUNET_free (h); } /** * Abort operation. Twister behavior may then include the * changes requested by the operation, or not! Must be called * before the operation callback was invoked. * * @param op operation to cancel, * operation's callback will not be called */ void TALER_TWISTER_cancel (struct TALER_TWISTER_Operation *op) { /* Just don't call the callback anymore */ op->cb = NULL; } /** * Randomly truncate the request. * * @param h twister instance to control * @param cb function to call once twister is ready; tipically * a acknowledge function. * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_malform_upload (struct TALER_TWISTER_Handle *h, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_Malform *src; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD); /* Send message. */ GNUNET_MQ_send (h->mq, env); LOG (GNUNET_ERROR_TYPE_DEBUG, "Batching a (upload) body malformation\n"); return op; } /** * Randomly truncate the response. * * @param h twister instance to control * @param cb function to call once twister has processed this * request. * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_malform (struct TALER_TWISTER_Handle *h, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_Malform *src; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_MALFORM); /* Send message. */ GNUNET_MQ_send (h->mq, env); LOG (GNUNET_ERROR_TYPE_DEBUG, "Batching a body malformation\n"); return op; } /** * Instruct the twister to flip a character into * the string JSON field that belongs to the object * being returned to the HTTP client. * * @param h twister instance to control * @param path object-like notation to point the string * object where we seek a character to flip. * @param cb function to call once twister has processed this * request * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_flip_download (struct TALER_TWISTER_Handle *h, const char *path, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_FlipPath *src; //FIXME 'src' right name? uint16_t stralloc; uint16_t size; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); stralloc = strlen (path) + 1; size = sizeof (*src) + stralloc; GNUNET_assert (size < UINT16_MAX); env = GNUNET_MQ_msg_extra (src, size, TWISTER_MESSAGE_TYPE_FLIP_PATH_DL); /* Put data into the envelope. */ GNUNET_assert (stralloc == GNUNET_STRINGS_buffer_fill ((char *) &src[1], stralloc, 1, path)); GNUNET_MQ_send (h->mq, env); return op; } /** * Instruct the twister to flip a character into * the string JSON field that belongs to the object * being uploaded to the proxied service. * * @param h twister instance to control * @param path object-like notation to point the string * object where we seek a character to flip. * @param cb function to call once twister has batched this * request * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_flip_upload (struct TALER_TWISTER_Handle *h, const char *path, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_FlipPath *src; //FIXME 'src' right name? uint16_t size; uint16_t stralloc = strlen (path) + 1; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will UL-flip: %s\n", path); op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); size = sizeof (*src) + stralloc; GNUNET_assert (size < UINT16_MAX); env = GNUNET_MQ_msg_extra (src, size, TWISTER_MESSAGE_TYPE_FLIP_PATH_UL); /* Put data into the envelope. */ GNUNET_assert (stralloc == GNUNET_STRINGS_buffer_fill ((char *) &src[1], stralloc, 1, path)); /* Send message. */ GNUNET_MQ_send (h->mq, env); return op; } /** * Delete the object pointed to by @a path. Note, this * object belongs to the JSON response object. * * @param h twister instance to control * @param path object-like notation to point the object to be deleted. E.g., the path "f1.f2.0" will delete the object {"f1": {"f2": [{"to be": "deleted"}]}}. * @param cb function to call once twister is ready * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_delete_path (struct TALER_TWISTER_Handle *h, const char *path, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_DeletePath *src; //FIXME 'src' right name? op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_DELETE_PATH); /* Put data into the envelope. */ strcpy (src->path, path); /* Send message. */ GNUNET_MQ_send (h->mq, env); return op; } /** * Change the response field pointed by @a modify_path with * @a modify_value. * * @param h twister instance to control * @param path object-like notation path to the object to modify * @param value value to use for @a modify_path * @param cb callback to call once twister gets this instruction. * @param cb_cls closure for @a cb_callback * * @return operation handle. */ struct TALER_TWISTER_Operation * TALER_TWISTER_modify_path_dl (struct TALER_TWISTER_Handle *h, const char *path, const char *value, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_ModifyPath *src; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_MODIFY_PATH_DL); /* Put data into the envelope. */ strcpy (src->path, path); strcpy (src->value, value); /* Send message. */ GNUNET_MQ_send (h->mq, env); return op; } /** * Change the JSON field pointed by @a path to the new @a value. * It only applies to upload objects. * * @param h twister instance to control * @param path object-like notation path to the object to modify * @param value value to use for @a modify_path * @param cb callback to call once twister gets this instruction. * @param cb_cls closure for @a cb_callback * * @return operation handle. */ struct TALER_TWISTER_Operation * TALER_TWISTER_modify_path_ul (struct TALER_TWISTER_Handle *h, const char *path, const char *value, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_ModifyPath *src; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_MODIFY_PATH_UL); /* Put data into the envelope. */ strcpy (src->path, path); strcpy (src->value, value); /* Send message. */ GNUNET_MQ_send (h->mq, env); return op; } /** * Change the next response code to @a new_rc. * * @param h twister instance to control * @param new_rc response code to return from the next response * @param cb function to call once twister is ready * @param cb_cls closure for @a cb * @return operation handle (to possibly abort) */ struct TALER_TWISTER_Operation * TALER_TWISTER_change_response_code (struct TALER_TWISTER_Handle *h, unsigned int new_rc, GNUNET_SCHEDULER_TaskCallback cb, void *cb_cls) { struct TALER_TWISTER_Operation *op; struct GNUNET_MQ_Envelope *env; struct TWISTER_SetResponseCode *src; op = GNUNET_new (struct TALER_TWISTER_Operation); op->h = h; op->cb = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); /* Prepare *env*elope. */ env = GNUNET_MQ_msg (src, TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE); /* Put data into the envelope. */ src->response_code = htonl ((uint32_t) new_rc); /* Send message. */ GNUNET_MQ_send (h->mq, env); return op; } /* end of twister_api.c */