1 /* $Id: proto_push_md_command.c 20800 2012-01-19 05:13:45Z m-oki $ */
2
3 /*
4 * Copyright (c) 2012, Internet Initiative Japan, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <inttypes.h>
35 #include <string.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39
40 #include <libarms.h>
41 #include <libarms_resource.h>
42 #include <axp_extern.h>
43
44 #include <libarms_log.h>
45 #include <arms_xml_tag.h>
46 #include <libarms/base64.h>
47 #include <libarms/malloc.h>
48 #include <transaction/transaction.h>
49 #include <protocol/arms_methods.h>
50 #include <module_db_mi.h>
51
52 #include "compat.h"
53
54 /*
55 * Callback Functions
56 */
57 static void *md_command_context(tr_ctx_t *);
58 static int
59 md_command_cparg(AXP *, uint32_t, int, const char *, size_t, tr_ctx_t *);
60 static int md_command_response(transaction *, char *, int, int *);
61 static void md_command_release(tr_ctx_t *);
62
63
64 static char *arms_md_command_attr[] = {
65 "id", NULL,
66 "encoding", NULL,
67 NULL
68 };
69
70 /*
71 * XML Schema: md-command-request
72 */
73 static struct axp_schema arms_md_command_req_body[] = {
74 {ARMS_TAG_MDCOMMAND, "md-command", AXP_TYPE_TEXT,
75 arms_md_command_attr, push_default_hook, NULL},
76 {0, NULL, 0, NULL, NULL, NULL}
77 };
78 static struct axp_schema md_command_request = {
79 ARMS_TAG_MDCOMMAND_REQ, "md-command-request", AXP_TYPE_CHILD,
80 NULL, push_default_hook, arms_md_command_req_body
81 };
82
83 /*
84 * Method definition
85 */
86 arms_method_t md_command_methods = {
87 ARMS_TR_MD_COMMAND, /* pm_type */
88 "md-command", /* type string */
89 &md_command_request, /* schema */
90 0, /* pm_flags */
91 md_command_response, /* pm_response */
92 NULL, /* pm_done */
93 NULL, /* pm_exec */
94 md_command_cparg, /* pm_copyarg */
95 NULL, /* pm_rollback */
96 md_command_context, /* pm_context */
97 md_command_release, /* pm_release */
98 };
99
100 /*
101 * Method implementations
102 */
103
104 #define BEGIN 1
105 #define FIRST_RESULT 2
106 #define NEXT_RESULT 3
107 #define DONE 4
108 #define END 5
109 #define ERROR_RESULT 6
110 struct md_command_args {
111 int mod_id;
112 int mod_count;
113 int already_running;
114 int state;
115 int req_len;
116 int encoding;
117 char request[8192];
118 char result[1024 + 1];
119 int resultlen;
120 int next;
121 arms_base64_stream_t base64;
122 };
123
124 static int already_running = 0;
125
126
127 /*
128 * Context Alloc
129 */
130 static void *
md_command_context(tr_ctx_t * ctx)131 md_command_context(tr_ctx_t *ctx)
132 {
133 struct md_command_args *arg;
134
135 arg = CALLOC(1, sizeof(*arg));
136 if (arg != NULL) {
137 if (already_running) {
138 arg->already_running = already_running;
139 } else {
140 /* only one md-command */
141 already_running = 1;
142 }
143 arg->state = BEGIN;
144 }
145 return arg;
146 }
147
148 /*
149 * Context Free
150 */
151 static void
md_command_release(tr_ctx_t * tr_ctx)152 md_command_release(tr_ctx_t *tr_ctx)
153 {
154 struct md_command_args *arg;
155
156 if (tr_ctx->arg) {
157 arg = tr_ctx->arg;
158 if (arg->already_running == 0)
159 already_running = 0;
160 FREE(arg);
161 }
162 }
163
164 /*
165 * Copy argument
166 */
167 static int
md_command_cparg(AXP * axp,uint32_t pm_type,int tag,const char * buf,size_t len,tr_ctx_t * tr_ctx)168 md_command_cparg(AXP *axp, uint32_t pm_type, int tag,
169 const char *buf, size_t len, tr_ctx_t *tr_ctx)
170 {
171 struct md_command_args *arg = tr_ctx->arg;
172 uint32_t mod_id;
173
174 if (arg->already_running) {
175 tr_ctx->result = 302;
176 return 0;
177 }
178
179 switch (tag) {
180 case ARMS_TAG_START_CPARG:
181 break;
182 case ARMS_TAG_END_CPARG:
183 if (arg->mod_count < 1) {
184 tr_ctx->result = 203;/*Parameter Problem*/
185 }
186 break;
187 case ARMS_TAG_MDCOMMAND:
188 if (tr_ctx->read_done)
189 break;
190 arg->mod_count++;
191 if (arg->mod_count > 1) {
192 tr_ctx->result = 422;/*Multiple Request*/
193 return -1;
194 }
195 mod_id = get_module_id(axp, tag);
196 arg->mod_id = mod_id;
197 if (sizeof(arg->request) < len) {
198 tr_ctx->result = 402;/*SA Failure*/
199 return -1;
200 }
201 if (arms_get_encoding(axp, tag) == ARMS_DATA_BINARY) {
202 /* decode base64 */
203 len = arms_base64_decode_stream(&arg->base64,
204 arg->request,
205 sizeof(arg->request),
206 buf, len);
207 } else {
208 memcpy(arg->request, buf, len);
209 }
210 arg->req_len = len;
211 break;
212 default:
213 break;
214 }
215
216 return 0;
217 }
218
219 /*
220 * Generate md-command-response mesage.
221 */
222 static int
md_command_response(transaction * tr,char * buf,int len,int * wrote)223 md_command_response(transaction *tr, char *buf, int len, int *wrote)
224 {
225 tr_ctx_t *tr_ctx = &tr->tr_ctx;
226 struct md_command_args *arg = tr_ctx->arg;
227 arms_context_t *res = arms_get_context();
228 int size, total, rv;
229
230 switch (arg->state) {
231 case BEGIN:
232 libarms_log(ARMS_LOG_DEBUG, "Generate response to RS");
233 arg->result[0] = '\0';
234 rv = res->callbacks.command_cb(
235 arg->mod_id,
236 ARMS_PUSH_MD_COMMAND,
237 arg->request, arg->req_len,
238 arg->result, sizeof(arg->result) - 1,
239 &arg->next,
240 res->udata);
241 arg->encoding = ARMS_DATA_TEXT;
242 if (ARMS_RESULT_IS_ERROR(rv)) {
243 tr_ctx->result = 102;/*exec error*/
244 arg->state = ERROR_RESULT;
245 } else {
246 arg->state = FIRST_RESULT;
247 }
248 if (ARMS_RESULT_IS_BYTES(rv)) {
249 if (ARMS_RV_DATA_MASK(rv) < sizeof(arg->result)) {
250 arg->encoding = ARMS_DATA_BINARY;
251 arg->resultlen = ARMS_RV_DATA_MASK(rv);
252 } else {
253 /* too big bytes. no md-result */
254 tr_ctx->result = 102;/*exec error*/
255 arg->state = ERROR_RESULT;
256 snprintf(arg->result, sizeof(arg->result),
257 "data length too big (%d bytes)",
258 ARMS_RV_DATA_MASK(rv));
259 }
260 }
261 size = arms_write_begin_message(tr, buf, len);
262 buf += size;
263 len -= size;
264 if (arg->encoding == ARMS_DATA_BINARY) {
265 size += snprintf(buf, len,
266 "<md-result id=\"%d\" encoding=\"base64\">",
267 arg->mod_id);
268 } else {
269 size += snprintf(buf, len,
270 "<md-result id=\"%d\">", arg->mod_id);
271 }
272 *wrote = size;
273 return TR_WANT_WRITE;
274 case ERROR_RESULT:
275 *wrote = strlcpy(buf, arms_escape(arg->result), len);
276 arg->state = DONE;
277 return TR_WANT_WRITE;
278 case FIRST_RESULT:
279 if (arg->encoding == ARMS_DATA_BINARY) {
280 int blen;
281
282 blen = ROUND_BASE64_BINARY(arg->resultlen);
283 arg->resultlen -= blen;
284 *wrote = arms_base64_encode(buf, len,
285 arg->result,
286 blen);
287 memcpy(arg->result,
288 arg->result + blen, arg->resultlen);
289 } else {
290 *wrote = strlcpy(buf, arms_escape(arg->result), len);
291 arg->resultlen = 0;
292 }
293 if ((arg->next & ARMS_FRAG_FINISHED) != 0)
294 arg->state = DONE;
295 else
296 arg->state = NEXT_RESULT;
297 return TR_WANT_WRITE;
298 case NEXT_RESULT:
299 arg->result[arg->resultlen] = '\0';
300 arg->next = 0;
301 rv = res->callbacks.command_cb(
302 arg->mod_id,
303 ARMS_PUSH_MD_COMMAND,
304 NULL, 0,
305 arg->result + arg->resultlen,
306 sizeof(arg->result) - 1 - arg->resultlen,
307 &arg->next,
308 res->udata);
309 if (ARMS_RESULT_IS_BYTES(rv) &&
310 ARMS_RV_DATA_MASK(rv) < sizeof(arg->result)) {
311 int blen;
312
313 /* binary */
314 arg->resultlen += ARMS_RV_DATA_MASK(rv);
315 blen = ROUND_BASE64_BINARY(arg->resultlen);
316 arg->resultlen -= blen;
317 *wrote = arms_base64_encode(buf, len,
318 arg->result,
319 blen);
320 memcpy(arg->result,
321 arg->result + blen, arg->resultlen);
322 } else {
323 /* text */
324 *wrote = strlcpy(buf, arms_escape(arg->result), len);
325 arg->resultlen = 0;
326 }
327 if ((arg->next & ARMS_FRAG_FINISHED) != 0)
328 arg->state = DONE;
329 else
330 arg->state = NEXT_RESULT;
331 return TR_WANT_WRITE;
332 case DONE:
333 if (arg->resultlen > 0) {
334 total = size = arms_base64_encode(buf, len,
335 arg->result, arg->resultlen);
336 buf += size;
337 len -= size;
338 } else {
339 total = 0;
340 }
341
342 size = snprintf(buf, len, "</md-result>");
343 buf += size;
344 len -= size;
345 total += size;
346
347 total += arms_write_end_message(tr, buf, len);
348 *wrote = total;
349 arg->state = END;
350 return TR_WRITE_DONE;
351 case END:
352 return TR_WRITE_DONE;
353 default:
354 break;
355 }
356 return TR_FATAL_ERROR;
357 }
358