[remote] Add generic command and pack system to simplify user code.
[vsc-common.git] / remote / remote.c
1 /*
2  * remote.c: XML-RPC based remote extension for libvsccli
3  *
4  * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  */
20
21 #include <termios.h>
22
23 #include <xmlrpc-c/client.h>
24
25 #include <libvscmisc/assert.h>
26 #include <libvscmisc/error.h>
27 #include <libvscmisc/memory.h>
28 #include <libvscmisc/string.h>
29
30 #include <libvsccli/option.h>
31
32 #include "../include/libvscremote/libvscremote.h"
33
34 static int _initialized = FALSE;
35 static VscRemoteMethodLookupFunction _method_lookup_function = NULL;
36 static VscRemoteParameterNameLookupFunction _parameter_name_lookup_function = NULL;
37 static char *_remote_url = NULL;
38 static char *_username = NULL;
39 static char *_cookie = NULL;
40
41 static void
42 _error_from_xmlrpc (struct VscError *error, const xmlrpc_env *env,
43                     const char *file, const char *function, int line)
44 {
45         vsc_error_report (error, VSC__ERROR_CODE__XMLRPC_ERROR, file, line,
46                           function, "%s (%d)", env->fault_string,
47                           env->fault_code);
48 }
49
50 #define _ERROR_FROM_XMLRPC(error, env) \
51         _error_from_xmlrpc (error, env, __FILE__, __FUNCTION__, __LINE__)
52
53 static char *
54 _request_password (struct VscError *error, const char *username)
55 {
56         struct termios old, new;
57         char *password = NULL;
58         size_t size = 256;
59
60         VSC__ASSERT (error != NULL);
61         VSC__ASSERT (! error->occured);
62
63         password = vsc_alloc (error, size);
64
65         if (error->occured) {
66                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
67                 return NULL;
68         }
69
70         if (tcgetattr (fileno (stdin), &old) != 0) {
71                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
72                              "Internal error");
73                 return NULL;
74         }
75
76         new = old;
77         new.c_lflag &= ~ECHO;
78
79         if (tcsetattr (fileno (stdin), TCSAFLUSH, &new) != 0) {
80                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
81                              "Internal error");
82                 return NULL;
83         }
84
85         printf ("Enter password for %s: ", username);
86
87         if (fgets (password, size, stdin) == NULL) {
88                 vsc_free (&password);
89         }
90
91         if (password != NULL) {
92                 size = strlen (password);
93
94                 if (size > 0) {
95                         password[size - 1] = '\0';
96                 }
97         }
98
99         (void) tcsetattr (fileno (stdin), TCSAFLUSH, &old);
100
101         printf ("\n");
102
103         return password;
104 }
105
106 static const char * /* method_name */
107 _lookup_method (struct VscError *error, const char *command_name,
108                 VscRemotePackFunction *pack_function,
109                 VscRemoteUnpackFunction *unpack_function, void *user_data)
110 {
111         const char *method_name = NULL;
112
113         VSC__ASSERT (error != NULL);
114         VSC__ASSERT (! error->occured);
115         VSC__ASSERT (_method_lookup_function != NULL);
116         VSC__ASSERT (pack_function != NULL);
117         VSC__ASSERT (unpack_function != NULL);
118
119         method_name = _method_lookup_function (error, command_name, pack_function,
120                                            unpack_function, user_data);
121
122         if (error->occured) {
123                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
124                 return NULL;
125         }
126
127         if (method_name == NULL) {
128                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
129                              "Internal error");
130                 return NULL;
131         }
132
133         if (strcmp (command_name, "login") != 0 &&
134             strcmp (command_name, "logout") != 0) {
135                 if (*pack_function == NULL) {
136                         VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
137                                      "Internal error");
138                         return NULL;
139                 }
140
141                 if (*unpack_function == NULL) {
142                         VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
143                                      "Internal error");
144                         return NULL;
145                 }
146         }
147
148         return method_name;
149 }
150
151 static const char *
152 _lookup_parameter_name (struct VscError *error, const char *command_name,
153                        const char *option_long_name, void *user_data)
154 {
155         const char *argument_name = NULL;
156
157         VSC__ASSERT (error != NULL);
158         VSC__ASSERT (! error->occured);
159         VSC__ASSERT (_parameter_name_lookup_function != NULL);
160
161         argument_name = _parameter_name_lookup_function
162                            (error, command_name, option_long_name, user_data);
163
164         if (error->occured) {
165                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
166                 return NULL;
167         }
168
169         if (argument_name == NULL) {
170                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
171                              "Internal error");
172                 return NULL;
173         }
174
175         return argument_name;
176 }
177
178 static void
179 _decompose_response (struct VscError *error, xmlrpc_value *response, int *code,
180                      char **message, xmlrpc_value **value)
181 {
182         xmlrpc_env env;
183         xmlrpc_value *struct_value = NULL;
184         xmlrpc_value *temp_value = NULL;
185
186         VSC__ASSERT (error != NULL);
187         VSC__ASSERT (! error->occured);
188         VSC__ASSERT (response != NULL);
189         VSC__ASSERT (code != NULL);
190         VSC__ASSERT (message != NULL);
191
192         xmlrpc_env_init (&env);
193
194         xmlrpc_decompose_value (&env, response, "S", &struct_value);
195
196         if (env.fault_occurred) {
197                 _ERROR_FROM_XMLRPC (error, &env);
198                 goto cleanup;
199         }
200
201         xmlrpc_struct_find_value (&env, struct_value, "code", &temp_value);
202
203         if (env.fault_occurred) {
204                 _ERROR_FROM_XMLRPC (error, &env);
205                 goto cleanup;
206         }
207
208         if (temp_value == NULL) {
209                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
210                              "Expecting XML-RPC response to contain a 'code' element");
211                 goto cleanup;
212         }
213
214         xmlrpc_read_int (&env, temp_value, code);
215         xmlrpc_DECREF (temp_value);
216
217         if (env.fault_occurred) {
218                 _ERROR_FROM_XMLRPC (error, &env);
219                 goto cleanup;
220         }
221
222         if (code != 0) {
223                 xmlrpc_struct_find_value (&env, struct_value, "message", &temp_value);
224
225                 if (env.fault_occurred) {
226                         _ERROR_FROM_XMLRPC (error, &env);
227                         goto cleanup;
228                 }
229
230                 if (temp_value == NULL) {
231                         VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
232                                      "Expecting XML-RPC response to contain a 'message' "
233                                      "element, because 'code' is non-zero");
234                         goto cleanup;
235                 }
236
237                 xmlrpc_read_string (&env, temp_value, (const char **) message);
238                 xmlrpc_DECREF (temp_value);
239
240                 if (env.fault_occurred) {
241                         _ERROR_FROM_XMLRPC (error, &env);
242                         goto cleanup;
243                 }
244         }
245
246         if (value != NULL) {
247                 xmlrpc_struct_find_value (&env, struct_value, "value", value);
248
249                 if (env.fault_occurred) {
250                         _ERROR_FROM_XMLRPC (error, &env);
251                         goto cleanup;
252                 }
253         }
254
255 cleanup:
256         if (struct_value != NULL) {
257                 xmlrpc_DECREF (struct_value);
258         }
259
260         xmlrpc_env_clean (&env);
261 }
262
263 /*
264  * Login
265  */
266
267 static void
268 _login (struct VscError *error, const struct VscCliCommandInfo *command_info,
269         const struct VscCliOption *option_list, void *user_data);
270
271 static const struct VscCliOptionInfo _login_option_infos[] = {
272         { -1, "username", "Username for the remote system",
273           VSC_CLI__OPTION_TYPE__STRING, FALSE, TRUE },
274         { -1, "password", "Password for the remote system",
275           VSC_CLI__OPTION_TYPE__STRING, FALSE, FALSE },
276         VSC_CLI__NULL__OPTION_INFO,
277 };
278
279 const struct VscCliCommandInfo vsc_remote_login_command_info = {
280         "login",
281         "Login to the remote system",
282         "",
283         _login,
284         _login_option_infos,
285 };
286
287 static void
288 _login (struct VscError *error,
289         const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
290         const struct VscCliOption *option_list, void *user_data)
291 {
292         const char *method_name;
293         VscRemotePackFunction pack_function = NULL;
294         VscRemoteUnpackFunction unpack_function = NULL;
295         const char *username_name;
296         const char *password_name;
297         const char *username;
298         const char *password;
299         char *password_;
300         xmlrpc_env env;
301         xmlrpc_value *response = NULL;
302         int code;
303         char *message = NULL;
304         xmlrpc_value *cookie_value = NULL;
305
306         VSC__ASSERT (error != NULL);
307         VSC__ASSERT (! error->occured);
308
309         vsc_remote_check (error, FALSE);
310
311         if (error->occured) {
312                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
313                 return;
314         }
315
316         method_name = _lookup_method (error, command_info->name, &pack_function,
317                                       &unpack_function, user_data);
318
319         if (error->occured) {
320                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
321                 return;
322         }
323
324         username_name = _lookup_parameter_name (error, command_info->name,
325                                                 _login_option_infos[0].long_name,
326                                                 user_data);
327
328         if (error->occured) {
329                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
330                 return;
331         }
332
333         password_name = _lookup_parameter_name (error, command_info->name,
334                                                 _login_option_infos[1].long_name,
335                                                 user_data);
336
337         if (error->occured) {
338                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
339                 return;
340         }
341
342         VSC__ASSERT (_username == NULL);
343
344         username = vsc_cli_option_list_lookup_string
345                       (option_list, _login_option_infos[0].long_name);
346         password = vsc_cli_option_list_lookup_string
347                       (option_list, _login_option_infos[1].long_name);
348
349         VSC__ASSERT (username != NULL);
350
351         if (password == NULL) {
352                 password_ = _request_password (error, username);
353
354                 if (password_ == NULL) {
355                         VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
356                                      "Could not read passowrd");
357                         return;
358                 }
359         } else {
360                 password_ = vsc_strdup (error, password);
361
362                 if (error->occured) {
363                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
364                         return;
365                 }
366         }
367
368         xmlrpc_env_init (&env);
369
370         response = xmlrpc_client_call (&env, _remote_url, method_name,
371                                        "({s:s,s:s})",
372                                        username_name, username,
373                                        password_name, password_);
374
375         if (env.fault_occurred) {
376                 _ERROR_FROM_XMLRPC (error, &env);
377                 response = NULL;
378                 goto cleanup;
379         }
380
381         _decompose_response (error, response, &code, &message, &cookie_value);
382
383         if (error->occured) {
384                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
385                 goto cleanup;
386         }
387
388         if (code != 0) {
389                 VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
390                              "%s (%d)", message, code);
391                 vsc_free (&_cookie);
392                 goto cleanup;
393         }
394
395         xmlrpc_read_string (&env, cookie_value, (const char **) &_cookie);
396         xmlrpc_DECREF (cookie_value);
397
398         if (env.fault_occurred) {
399                 _ERROR_FROM_XMLRPC (error, &env);
400                 _cookie = NULL;
401                 goto cleanup;
402         }
403
404         _username = vsc_strdup (error, username);
405
406         if (error->occured) {
407                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
408                 goto cleanup;
409         }
410
411 cleanup:
412         if (response != NULL) {
413                 xmlrpc_DECREF (response);
414         }
415
416         vsc_free (&password_);
417         vsc_free (&message);
418
419         xmlrpc_env_clean (&env);
420 }
421
422 /*
423  * Logout
424  */
425
426 static void
427 _logout (struct VscError *error, const struct VscCliCommandInfo *command_info,
428          const struct VscCliOption *option_list, void *user_data);
429
430 static const struct VscCliOptionInfo _logout_option_infos[] = {
431         VSC_CLI__NULL__OPTION_INFO,
432 };
433
434 const struct VscCliCommandInfo vsc_remote_logout_command_info = {
435         "logout",
436         "Logout from the remote system",
437         "",
438         _logout,
439         _logout_option_infos,
440 };
441
442 static void
443 _logout (struct VscError *error,
444          const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
445          const struct VscCliOption *option_list VSC__ATTR__UNUSED,
446          void *user_data VSC__ATTR__UNUSED)
447 {
448         const char *method_name = NULL;
449         VscRemotePackFunction pack_function = NULL;
450         VscRemoteUnpackFunction unpack_function = NULL;
451         const char *cookie_name = NULL;
452         xmlrpc_env env;
453         xmlrpc_value *response = NULL;
454         int code;
455         char *message = NULL;
456
457         VSC__ASSERT (error != NULL);
458         VSC__ASSERT (! error->occured);
459
460         vsc_remote_check (error, TRUE);
461
462         if (error->occured) {
463                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
464                 return;
465         }
466
467         method_name = _lookup_method (error, command_info->name, &pack_function,
468                                       &unpack_function, user_data);
469
470         if (error->occured) {
471                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
472                 return;
473         }
474
475         cookie_name = _lookup_parameter_name (error, command_info->name, "cookie",
476                                               user_data);
477
478         if (error->occured) {
479                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
480                 return;
481         }
482
483         xmlrpc_env_init (&env);
484
485         response = xmlrpc_client_call (&env, _remote_url, method_name,
486                                        "({s:s})",
487                                        cookie_name, _cookie);
488
489         if (env.fault_occurred) {
490                 _ERROR_FROM_XMLRPC (error, &env);
491                 response = NULL;
492                 goto cleanup;
493         }
494
495         _decompose_response (error, response, &code, &message, NULL);
496
497         if (error->occured) {
498                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
499                 goto cleanup;
500         }
501
502         if (code != 0) {
503                 VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
504                              "%s (%d)", message, code);
505                 goto cleanup;
506         }
507
508         vsc_free (&_cookie);
509         vsc_free (&_username);
510
511 cleanup:
512         if (response != NULL) {
513                 xmlrpc_DECREF (response);
514         }
515
516         vsc_free (&message);
517
518         xmlrpc_env_clean (&env);
519 }
520
521 /*
522  * Core
523  */
524
525 void
526 vsc_remote_init (struct VscError *error, const char *name, const char *version,
527                  const char *remote_url,
528                  VscRemoteMethodLookupFunction method_lookup_function,
529                  VscRemoteParameterNameLookupFunction parameter_name_lookup_function)
530 {
531         xmlrpc_env env;
532         char user_agent[256];
533         struct xmlrpc_clientparms clientParms;
534         struct xmlrpc_curl_xportparms curlParms;
535
536         VSC__ASSERT (error != NULL);
537         VSC__ASSERT (! error->occured);
538         VSC__ASSERT (method_lookup_function != NULL);
539         VSC__ASSERT (parameter_name_lookup_function != NULL);
540
541         if (_initialized) {
542                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
543                              "Already initialized");
544                 return;
545         }
546
547         _method_lookup_function = method_lookup_function;
548         _parameter_name_lookup_function = parameter_name_lookup_function;
549
550         if (remote_url != NULL) {
551                 _remote_url = vsc_strdup (error, remote_url);
552
553                 if (error->occured) {
554                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
555                         return;
556                 }
557         } else {
558                 _remote_url = NULL;
559         }
560
561         /*
562          * Configure the XML-RPC-C Curl backend to send a User-Agent.
563          */
564         xmlrpc_env_init (&env);
565
566         memset (&clientParms, 0, sizeof (struct xmlrpc_clientparms));
567         memset (&curlParms, 0, sizeof (struct xmlrpc_curl_xportparms));
568
569         snprintf (user_agent, 256, "%s/%s", name, version);
570
571         curlParms.user_agent = user_agent;
572
573         clientParms.transport = "curl";
574         clientParms.transportparmsP = &curlParms;
575         clientParms.transportparm_size = XMLRPC_CXPSIZE (user_agent);
576
577         xmlrpc_client_init2 (&env, XMLRPC_CLIENT_NO_FLAGS, name, version,
578                              &clientParms, XMLRPC_CPSIZE (transportparm_size));
579
580         if (env.fault_occurred) {
581                 _ERROR_FROM_XMLRPC (error, &env);
582                 goto cleanup;
583         }
584
585         _initialized = TRUE;
586
587 cleanup:
588         xmlrpc_env_clean (&env);
589 }
590
591 void
592 vsc_remote_cleanup (void)
593 {
594         struct VscError error;
595
596         VSC__ASSERT (_initialized);
597
598         if (! _initialized) {
599                 return;
600         }
601
602         /*
603          * FIXME: If an RPC error occured this call segfaults. This only happens if
604          *        the XML-RPC-C library is compiled with -O3. If compiled with -O0
605          *        everything is fine.
606          */
607         xmlrpc_client_cleanup();
608
609         if (_cookie != NULL) {
610                 vsc_error_init (&error);
611
612                 _logout (&error, &vsc_remote_logout_command_info, NULL, NULL);
613
614                 vsc_error_cleanup (&error);
615         }
616
617         vsc_free (&_remote_url);
618         vsc_free (&_cookie);
619         vsc_free (&_username);
620
621         _method_lookup_function = NULL;
622         _parameter_name_lookup_function = NULL;
623
624         _initialized = FALSE;
625 }
626
627 void
628 vsc_remote_check (struct VscError *error, int expected_login_state)
629 {
630         VSC__ASSERT (error != NULL);
631         VSC__ASSERT (! error->occured);
632
633         if (! _initialized) {
634                 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
635                              "Not initialized");
636                 return;
637         }
638
639         if (_remote_url == NULL) {
640                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
641                              "No remote URL specified");
642                 return;
643         }
644
645         if (expected_login_state == TRUE && _cookie == NULL) {
646                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL, "Not logged in");
647                 return;
648         }
649
650         if (expected_login_state == FALSE && _cookie != NULL) {
651                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
652                              "Already logged in");
653                 return;
654         }
655 }
656
657 const char *
658 vsc_remote_get_cookie (struct VscError *error)
659 {
660         VSC__ASSERT (error != NULL);
661         VSC__ASSERT (! error->occured);
662
663         if (! _initialized) {
664                 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
665                              "Not initialized");
666                 return NULL;
667         }
668
669         if (_cookie == NULL) {
670                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
671                              "No cookie avaiable");
672                 return NULL;
673         }
674
675         return _cookie;
676 }
677
678 const char *
679 vsc_remote_get_username (struct VscError *error)
680 {
681         VSC__ASSERT (error != NULL);
682         VSC__ASSERT (! error->occured);
683
684         if (! _initialized) {
685                 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
686                              "Not initialized");
687                 return NULL;
688         }
689
690         return _username;
691 }
692
693 /*
694  * Command
695  */
696
697 void
698 vsc_remote_default_pack (struct VscError *error,
699                          const struct VscCliCommandInfo *command_info,
700                          const struct VscCliOption *option_list,
701                          const char *cookie, xmlrpc_value *struct_value,
702                          void *user_data)
703 {
704         int i;
705         xmlrpc_env env;
706         const struct VscCliOption *option;
707         const struct VscCliOptionInfo *option_info;
708         const char *name;
709         xmlrpc_value *value = NULL;
710
711         VSC__ASSERT (error != NULL);
712         VSC__ASSERT (! error->occured);
713
714         name = _lookup_parameter_name (error, command_info->name, "cookie",
715                                        user_data);
716
717         if (error->occured) {
718                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
719                 return;
720         }
721
722         xmlrpc_env_init (&env);
723
724         value = xmlrpc_string_new (&env, cookie);
725
726         if (env.fault_occurred) {
727                 _ERROR_FROM_XMLRPC (error, &env);
728                 goto cleanup;
729         }
730
731         xmlrpc_struct_set_value (&env, struct_value, name, value);
732
733         xmlrpc_DECREF (value);
734
735         if (env.fault_occurred) {
736                 _ERROR_FROM_XMLRPC (error, &env);
737                 goto cleanup;
738         }
739
740         for (i = 0; command_info->option_infos[i].long_name != NULL; i++) {
741                 option_info = &command_info->option_infos[i];
742                 option = vsc_cli_option_list_lookup (option_list,
743                                                      option_info->long_name);
744                 name = _lookup_parameter_name (error, command_info->name,
745                                                option_info->long_name, user_data);
746
747                 if (error->occured) {
748                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
749                         goto cleanup;
750                 }
751
752                 if (option != NULL) {
753                         switch (option_info->type) {
754                         case VSC_CLI__OPTION_TYPE__BOOLEAN:
755                                 value = xmlrpc_bool_new (&env, TRUE);
756                                 break;
757
758                         case VSC_CLI__OPTION_TYPE__NUMBER:
759                                 value = xmlrpc_int_new (&env, option->number);
760                                 break;
761
762                         case VSC_CLI__OPTION_TYPE__STRING:
763                                 value = xmlrpc_string_new (&env, option->string);
764                                 break;
765
766                         default:
767                                 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
768                                              "Internal error");
769                                 goto cleanup;
770                         }
771                 } else if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN &&
772                            option_info->required) {
773                         value = xmlrpc_bool_new (&env, FALSE);
774                 } else {
775                         continue;
776                 }
777
778                 if (env.fault_occurred) {
779                         _ERROR_FROM_XMLRPC (error, &env);
780                         goto cleanup;
781                 }
782
783                 xmlrpc_struct_set_value (&env, struct_value, name, value);
784
785                 xmlrpc_DECREF (value);
786
787                 if (env.fault_occurred) {
788                         _ERROR_FROM_XMLRPC (error, &env);
789                         goto cleanup;
790                 }
791         }
792
793 cleanup:
794         xmlrpc_env_clean (&env);
795 }
796
797 void
798 vsc_remote_default_command (struct VscError *error,
799                             const struct VscCliCommandInfo *command_info,
800                             const struct VscCliOption *option_list,
801                             void *user_data)
802 {
803         xmlrpc_env env;
804         const char *method_name = NULL;
805         VscRemotePackFunction pack_function = NULL;
806         VscRemoteUnpackFunction unpack_function = NULL;
807         xmlrpc_value *struct_value = NULL;
808         xmlrpc_value *value = NULL;
809         xmlrpc_value *response = NULL;
810         int code = 0;
811         char *message = NULL;
812
813         VSC__ASSERT (error != NULL);
814         VSC__ASSERT (! error->occured);
815
816         vsc_remote_check (error, TRUE);
817
818         if (error->occured) {
819                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
820                 return;
821         }
822
823         method_name = _lookup_method (error, command_info->name, &pack_function,
824                                       &unpack_function, user_data);
825
826         if (error->occured) {
827                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
828                 return;
829         }
830
831         xmlrpc_env_init (&env);
832
833         struct_value = xmlrpc_struct_new (&env);
834
835         if (env.fault_occurred) {
836                 _ERROR_FROM_XMLRPC (error, &env);
837                 struct_value = NULL;
838                 goto cleanup;
839         }
840
841         /*
842          * Pack
843          */
844         pack_function (error, command_info, option_list, _cookie, struct_value,
845                        user_data);
846
847         if (error->occured) {
848                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
849                 goto cleanup;
850         }
851
852         /*
853          * Call
854          */
855         response = xmlrpc_client_call (&env, _remote_url, method_name,
856                                        "(S)", struct_value);
857
858         xmlrpc_DECREF (struct_value);
859         struct_value = NULL;
860
861         if (env.fault_occurred) {
862                 _ERROR_FROM_XMLRPC (error, &env);
863                 response = NULL;
864                 goto cleanup;
865         }
866
867         /*
868          * Decompose
869          */
870         _decompose_response (error, response, &code, &message, &value);
871
872         if (error->occured) {
873                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
874                 goto cleanup;
875         }
876
877         /*
878          * Unpack
879          */
880         unpack_function (error, command_info, option_list, code, message, value,
881                          user_data);
882         xmlrpc_DECREF (value);
883
884         if (error->occured) {
885                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
886                 goto cleanup;
887         }
888
889 cleanup:
890         if (struct_value != NULL) {
891                 xmlrpc_DECREF (struct_value);
892         }
893
894         if (response != NULL) {
895                 xmlrpc_DECREF (response);
896         }
897
898         vsc_free (&message);
899
900         xmlrpc_env_clean (&env);
901 }