1 /* This program creates the token and certificate files for Amazon Devpay's
2 * Simple Token Service (STS). Right now you can then use those files with
17 # error activate_devpay only works if devpay is enabled.
20 #define MAX_RESPONSE_SIZE (1024*1024)
28 static void usage(void) {
30 "USAGE: activate-devpay KEY [ >> amanda.conf ]\n"
31 " This tool uses an Amazon Devpay activation key to retrieve an\n"
32 " user token, access key, and secret key for use with Amazon S3. Output\n"
33 " is in a form suitable for placement in an Amanda configuration file\n");
38 /* This function is **not** thread-safe. Sorry. */
39 static const char * parse_commandline(int argc, char ** argv) {
48 static char * activation_url(const char *key) {
52 encoded_key = curl_escape(key, 0);
53 url = g_strdup_printf(STS_BASE_URL "?Action=ActivateDesktopProduct&ActivationKey=%s&ProductToken=" STS_PRODUCT_TOKEN "&Version=2007-06-05", encoded_key);
54 curl_free(encoded_key);
59 /* This function is a CURLOPT_WRITEFUNCTION and a wrapper for
60 g_markup_parse_context_parse(). It's not very smart about errors. */
61 static size_t libcurl_gmarkup_glue(void *ptr, size_t size1, size_t size2,
63 GMarkupParseContext *context = stream;
64 /* If this overflows, we have real problems, because we are expected to
65 * return the result of this multiplication in a size_t. */
66 size_t read_size = size1 * size2;
67 GError * error = NULL;
69 read_size = size1 * size2;
71 if (g_markup_parse_context_parse(context, ptr, read_size, &error)) {
75 g_fprintf(stderr, "Internal error parsing XML.\n");
77 g_fprintf(stderr, "Error parsing XML: %s\n",
85 static void do_server_stuff(const char * key, GMarkupParseContext * parser) {
88 char curl_error_buffer[CURL_ERROR_SIZE];
90 handle = curl_easy_init();
92 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, TRUE);
93 curl_easy_setopt(handle, CURLOPT_NOSIGNAL, TRUE);
94 curl_easy_setopt(handle, CURLOPT_AUTOREFERER, TRUE);
95 curl_easy_setopt(handle, CURLOPT_ENCODING, ""); /* Support everything. */
96 #ifdef CURLOPT_MAXFILESIZE
97 curl_easy_setopt(handle, CURLOPT_MAXFILESIZE, MAX_RESPONSE_SIZE);
99 curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buffer);
101 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, libcurl_gmarkup_glue);
102 curl_easy_setopt(handle, CURLOPT_WRITEDATA, parser);
103 url = activation_url(key);
104 curl_easy_setopt(handle, CURLOPT_URL, url);
106 if (curl_easy_perform(handle) != 0) {
107 g_error("Problem fetching data from server:\n%s",
115 static void parser_got_text(GMarkupParseContext * context,
120 Credentials * rval = user_data;
122 const char * current_tag = g_markup_parse_context_get_element(context);
124 g_assert(rval != NULL);
125 g_assert(*error == NULL);
127 /* We use strrstr instead of strcmp because Amazon uses namespaces
128 * that I don't want to deal with. */
129 if (g_strrstr(current_tag, "UserToken")) {
130 g_string_append_len(rval->user_token, text, text_len);
132 } else if (g_strrstr(current_tag, "AWSAccessKeyId")) {
133 g_string_append_len(rval->access_key, text, text_len);
135 } else if (g_strrstr(current_tag, "SecretAccessKey")) {
136 g_string_append_len(rval->secret_key, text, text_len);
138 } else if (g_strrstr(current_tag, "Code")) {
139 /* Is it a code we know? */
140 if (strncmp(text, "ExpiredActivationKey", text_len) == 0) {
141 g_set_error(error, G_MARKUP_ERROR, -1,
142 "Activation key has expired; get a new one.");
143 } else if (strncmp(text, "InvalidActivationKey", text_len) == 0) {
144 g_set_error(error, G_MARKUP_ERROR, -1,
145 "Activation key is not valid; double-check.");
147 /* Do nothing; wait for the message. */
149 } else if (g_strrstr(current_tag, "Message")) {
150 g_set_error(error, G_MARKUP_ERROR, -1, "%.*s", text_len, text);
154 static void parser_got_error(GMarkupParseContext * context G_GNUC_UNUSED,
156 gpointer user_data G_GNUC_UNUSED) {
157 g_fprintf (stderr, "Problem with Amazon response: %s\n", error->message);
161 static GMarkupParseContext * parser_init(Credentials * credentials) {
162 static const GMarkupParser parser_settings = {
163 NULL, /* start_element */
164 NULL, /* end_element */
165 parser_got_text, /* text */
166 NULL, /* passthrough */
167 parser_got_error /* error */
169 bzero(credentials, sizeof(*credentials));
171 credentials->user_token = g_string_new("");
172 credentials->access_key = g_string_new("");
173 credentials->secret_key = g_string_new("");
175 return g_markup_parse_context_new(&parser_settings, 0, credentials, NULL);
178 static void parser_cleanup(GMarkupParseContext * context) {
179 GError * error = NULL;
180 g_markup_parse_context_end_parse(context, &error);
183 g_fprintf (stderr, "Unexpected end of Amazon response: %s\n",
188 g_markup_parse_context_free(context);
191 /* This function is responsible for the whole output thing. */
192 static void do_output(Credentials * rare) {
194 rare->user_token == NULL || !rare->user_token->len ||
195 rare->access_key == NULL || !rare->access_key->len ||
196 rare->secret_key == NULL || !rare->secret_key->len) {
197 g_fprintf(stderr, "Missing authentication data in response!\n");
201 g_printf("device_property \"S3_USER_TOKEN\" \"%s\"\n"
202 "device_property \"S3_ACCESS_KEY\" \"%s\"\n"
203 "device_property \"S3_SECRET_KEY\" \"%s\"\n",
204 rare->user_token->str, rare->access_key->str,
205 rare->secret_key->str);
208 int main(int argc, char ** argv) {
210 GMarkupParseContext * parser;
211 Credentials credentials;
213 key = parse_commandline(argc, argv);
215 curl_global_init(CURL_GLOBAL_ALL);
216 parser = parser_init(&credentials);
218 do_server_stuff(key, parser);
220 curl_global_cleanup();
221 parser_cleanup(parser);
223 do_output(&credentials);