diff options
Diffstat (limited to 'src/backend/legacy.c')
-rw-r--r-- | src/backend/legacy.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/backend/legacy.c b/src/backend/legacy.c new file mode 100644 index 00000000..c105ecb3 --- /dev/null +++ b/src/backend/legacy.c @@ -0,0 +1,509 @@ +/** + * Create a taler://pay/ URI for the given @a con and @a order_id + * and @a session_id and @a instance_id. + * + * @param con HTTP connection + * @param order_id the order id + * @param session_id session, may be NULL + * @param instance_id instance, may be "default" + * @return corresponding taler://pay/ URI, or NULL on missing "host" + */ +char * +TMH_make_taler_pay_uri (struct MHD_Connection *con, + const char *order_id, + const char *session_id, + const char *instance_id) +{ + const char *host; + const char *forwarded_host; + const char *uri_path; + const char *uri_instance_id; + const char *query; + char *result; + + host = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "Host"); + forwarded_host = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "X-Forwarded-Host"); + + uri_path = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "X-Forwarded-Prefix"); + if (NULL == uri_path) + uri_path = "-"; + if (NULL != forwarded_host) + host = forwarded_host; + if (0 == strcmp (instance_id, + "default")) + uri_instance_id = "-"; + else + uri_instance_id = instance_id; + if (NULL == host) + { + /* Should never happen, at least the host header should be defined */ + GNUNET_break (0); + return NULL; + } + + if (GNUNET_YES == TALER_mhd_is_https (con)) + query = ""; + else + query = "?insecure=1"; + GNUNET_assert (NULL != order_id); + GNUNET_assert (0 < GNUNET_asprintf (&result, + "taler://pay/%s/%s/%s/%s%s%s%s", + host, + uri_path, + uri_instance_id, + order_id, + (NULL == session_id) ? "" : "/", + (NULL == session_id) ? "" : session_id, + query)); + return result; +} + + +/** + * Closure for the #wireformat_iterator_cb(). + */ +struct WireFormatIteratorContext +{ + /** + * The global iteration context. + */ + struct IterateInstancesCls *iic; + + /** + * The merchant instance we are currently building. + */ + struct MerchantInstance *mi; + + /** + * Set to #GNUNET_YES if the default instance was found. + */ + int default_instance; +}; + + +/** + * Callback that looks for 'merchant-account-*' sections, + * and populates our wire method according to the data + * + * @param cls closure with a `struct WireFormatIteratorContext *` + * @section section name this callback gets + */ +static void +wireformat_iterator_cb (void *cls, + const char *section) +{ + struct WireFormatIteratorContext *wfic = cls; + struct MerchantInstance *mi = wfic->mi; + struct IterateInstancesCls *iic = wfic->iic; + char *instance_option; + struct WireMethod *wm; + char *payto; + char *fn; + json_t *j; + struct GNUNET_HashCode jh_wire; + char *wire_file_mode; + + if (0 != strncasecmp (section, + "merchant-account-", + strlen ("merchant-account-"))) + return; + GNUNET_asprintf (&instance_option, + "HONOR_%s", + mi->id); + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + instance_option)) + { + GNUNET_free (instance_option); + return; + } + GNUNET_free (instance_option); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "PAYTO_URI", + &payto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "PAYTO_URI"); + iic->ret = GNUNET_SYSERR; + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "WIRE_RESPONSE", + &fn)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "WIRE_RESPONSE"); + GNUNET_free (payto); + iic->ret = GNUNET_SYSERR; + return; + } + + /* Try loading existing JSON from file */ + if (GNUNET_YES == + GNUNET_DISK_file_test (fn)) + { + json_error_t err; + char *url; + + if (NULL == + (j = json_load_file (fn, + JSON_REJECT_DUPLICATES, + &err))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load JSON from `%s': %s at %d:%d\n", + fn, + err.text, + err.line, + err.column); + GNUNET_free (fn); + GNUNET_free (payto); + iic->ret = GNUNET_SYSERR; + return; + } + url = TALER_JSON_wire_to_payto (j); + if (NULL == url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "URL missing in `%s', disabling account `%s'\n", + fn, + section); + GNUNET_free (fn); + GNUNET_free (payto); + iic->ret = GNUNET_SYSERR; + return; + } + if (0 != strcasecmp (url, + payto)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "URL `%s' does not match configuration `%s', disabling account `%s'\n", + url, + payto, + section); + GNUNET_free (fn); + GNUNET_free (payto); + GNUNET_free (url); + iic->ret = GNUNET_SYSERR; + return; + } + GNUNET_free (url); + } + else /* need to generate JSON */ + { + struct GNUNET_HashCode salt; + char *salt_str; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &salt, + sizeof (salt)); + salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt, + sizeof (salt)); + j = json_pack ("{s:s, s:s}", + "payto_uri", payto, + "salt", salt_str); + GNUNET_free (salt_str); + + /* Make sure every path component exists. */ + if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "mkdir", + fn); + GNUNET_free (fn); + GNUNET_free (payto); + json_decref (j); + iic->ret = GNUNET_SYSERR; + return; + } + + if (0 != json_dump_file (j, + fn, + JSON_COMPACT | JSON_SORT_KEYS)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to write hashed wire details to `%s'\n", + fn); + GNUNET_free (fn); + GNUNET_free (payto); + json_decref (j); + iic->ret = GNUNET_SYSERR; + return; + } + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "WIRE_FILE_MODE", + &wire_file_mode)) + { + errno = 0; + mode_t mode = (mode_t) strtoul (wire_file_mode, + NULL, + 8); + if (0 != errno) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "WIRE_FILE_MODE", + "Must be octal number\n"); + iic->ret = GNUNET_SYSERR; + GNUNET_free (fn); + return; + } + if (0 != chmod (fn, mode)) + { + TALER_LOG_ERROR ("chmod failed on %s\n", fn); + iic->ret = GNUNET_SYSERR; + GNUNET_free (fn); + return; + } + } + } + + GNUNET_free (fn); + + if (GNUNET_OK != + TALER_JSON_merchant_wire_signature_hash (j, + &jh_wire)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to hash wire input\n"); + GNUNET_free (fn); + GNUNET_free (payto); + json_decref (j); + iic->ret = GNUNET_SYSERR; + return; + } + + wm = GNUNET_new (struct WireMethod); + wm->wire_method = TALER_payto_get_method (payto); + GNUNET_free (payto); + GNUNET_asprintf (&instance_option, + "ACTIVE_%s", + mi->id); + wm->active = GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + instance_option); + GNUNET_free (instance_option); + if (GNUNET_YES == wm->active) + GNUNET_CONTAINER_DLL_insert (mi->wm_head, + mi->wm_tail, + wm); + else + GNUNET_CONTAINER_DLL_insert_tail (mi->wm_head, + mi->wm_tail, + wm); + wm->j_wire = j; + wm->h_wire = jh_wire; +} + + +/** + * Callback that looks for 'instance-*' sections, + * and populates accordingly each instance's data + * + * @param cls closure of type `struct IterateInstancesCls` + * @section section name this callback gets + */ +static void +instances_iterator_cb (void *cls, + const char *section) +{ + struct IterateInstancesCls *iic = cls; + char *token; + struct MerchantInstance *mi; + /* used as hashmap keys */ + struct GNUNET_HashCode h_pk; + struct GNUNET_HashCode h_id; + + if (0 != strncasecmp (section, + "instance-", + strlen ("instance-"))) + return; + /** Get id **/ + token = strrchr (section, '-'); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Extracted token: %s\n", + token + 1); + mi = GNUNET_new (struct MerchantInstance); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "NAME", + &mi->name)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "NAME"); + GNUNET_free (mi); + iic->ret = GNUNET_SYSERR; + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "KEYFILE", + &mi->keyfile)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "KEYFILE"); + GNUNET_free (mi->name); + GNUNET_free (mi); + iic->ret = GNUNET_SYSERR; + return; + } + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "TIP_EXCHANGE", + &mi->tip_exchange)) + { + char *tip_reserves; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "TIP_RESERVE_PRIV_FILENAME", + &tip_reserves)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "TIP_RESERVE_PRIV_FILENAME"); + GNUNET_free (mi->keyfile); + GNUNET_free (mi->name); + GNUNET_free (mi); + iic->ret = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_key_from_file (tip_reserves, + GNUNET_NO, + &mi->tip_reserve.eddsa_priv)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "TIP_RESERVE_PRIV_FILENAME", + "Failed to read private key"); + GNUNET_free (tip_reserves); + GNUNET_free (mi->keyfile); + GNUNET_free (mi->name); + GNUNET_free (mi); + iic->ret = GNUNET_SYSERR; + return; + } + GNUNET_free (tip_reserves); + } + + if (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_key_from_file (mi->keyfile, + GNUNET_YES, + &mi->privkey.eddsa_priv)) + { + GNUNET_break (0); + GNUNET_free (mi->keyfile); + GNUNET_free (mi->name); + GNUNET_free (mi); + iic->ret = GNUNET_SYSERR; + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&mi->privkey.eddsa_priv, + &mi->pubkey.eddsa_pub); + + mi->id = GNUNET_strdup (token + 1); + if (0 == strcasecmp ("default", + mi->id)) + iic->default_instance = GNUNET_YES; + + GNUNET_CRYPTO_hash (mi->id, + strlen (mi->id), + &h_id); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (by_id_map, + &h_id, + mi, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to put an entry into the 'by_id' hashmap\n"); + iic->ret = GNUNET_SYSERR; + GNUNET_free (mi->keyfile); + GNUNET_free (mi->name); + GNUNET_free (mi); + return; + } + GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + &h_pk); + + + /* Initialize wireformats */ + { + struct WireFormatIteratorContext wfic = { + .iic = iic, + .mi = mi + }; + + GNUNET_CONFIGURATION_iterate_sections (cfg, + &wireformat_iterator_cb, + &wfic); + } + if (NULL == mi->wm_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load wire formats for instance `%s'\n", + mi->id); + iic->ret = GNUNET_SYSERR; + } + +} + + +/** + * Iterate over each merchant instance, in order to populate + * each instance's own data + * + * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors + * (for example, if no "default" instance is defined) + */ +static int +iterate_instances (void) +{ + struct IterateInstancesCls iic; + + iic.default_instance = GNUNET_NO; + iic.ret = GNUNET_OK; + GNUNET_CONFIGURATION_iterate_sections (cfg, + &instances_iterator_cb, + &iic); + + if (GNUNET_NO == iic.default_instance) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No default merchant instance found\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != iic.ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "At least one instance was not successfully parsed\n"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} |