llxmlrpclistener.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:22k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file   llxmlrpclistener.cpp
  3.  * @author Nat Goodspeed
  4.  * @date   2009-03-18
  5.  * @brief  Implementation for llxmlrpclistener.
  6.  * 
  7.  * $LicenseInfo:firstyear=2009&license=viewergpl$
  8.  * 
  9.  * Copyright (c) 2009-2010, Linden Research, Inc.
  10.  * 
  11.  * Second Life Viewer Source Code
  12.  * The source code in this file ("Source Code") is provided by Linden Lab
  13.  * to you under the terms of the GNU General Public License, version 2.0
  14.  * ("GPL"), unless you have obtained a separate licensing agreement
  15.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  16.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18.  * 
  19.  * There are special exceptions to the terms and conditions of the GPL as
  20.  * it is applied to this Source Code. View the full text of the exception
  21.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  22.  * online at
  23.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24.  * 
  25.  * By copying, modifying or distributing this software, you acknowledge
  26.  * that you have read and understood your obligations described above,
  27.  * and agree to abide by those obligations.
  28.  * 
  29.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31.  * COMPLETENESS OR PERFORMANCE.
  32.  * $/LicenseInfo$
  33.  */
  34. // Precompiled header
  35. #include "llviewerprecompiledheaders.h"
  36. // associated header
  37. #include "llxmlrpclistener.h"
  38. // STL headers
  39. #include <map>
  40. #include <set>
  41. // std headers
  42. // external library headers
  43. #include <boost/scoped_ptr.hpp>
  44. #include <boost/range.hpp>          // boost::begin(), boost::end()
  45. #include <xmlrpc-epi/xmlrpc.h>
  46. #include "curl/curl.h"
  47. // other Linden headers
  48. #include "llerror.h"
  49. #include "stringize.h"
  50. #include "llxmlrpctransaction.h"
  51. #if LL_WINDOWS
  52. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  53. #endif
  54. template <typename STATUS>
  55. class StatusMapperBase
  56. {
  57.     typedef std::map<STATUS, std::string> MapType;
  58. public:
  59.     StatusMapperBase(const std::string& desc):
  60.         mDesc(desc)
  61.     {}
  62.     std::string lookup(STATUS status) const
  63.     {
  64.         typename MapType::const_iterator found = mMap.find(status);
  65.         if (found != mMap.end())
  66.         {
  67.             return found->second;
  68.         }
  69.         return STRINGIZE("<unknown " << mDesc << " " << status << ">");
  70.     }
  71. protected:
  72.     std::string mDesc;
  73.     MapType mMap;
  74. };
  75. class StatusMapper: public StatusMapperBase<LLXMLRPCTransaction::EStatus>
  76. {
  77. public:
  78.     StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::EStatus>("Status")
  79.     {
  80. mMap[LLXMLRPCTransaction::StatusNotStarted]  = "NotStarted";
  81. mMap[LLXMLRPCTransaction::StatusStarted]     = "Started";
  82. mMap[LLXMLRPCTransaction::StatusDownloading] = "Downloading";
  83. mMap[LLXMLRPCTransaction::StatusComplete]    = "Complete";
  84. mMap[LLXMLRPCTransaction::StatusCURLError]   = "CURLError";
  85. mMap[LLXMLRPCTransaction::StatusXMLRPCError] = "XMLRPCError";
  86. mMap[LLXMLRPCTransaction::StatusOtherError]  = "OtherError";
  87.     }
  88. };
  89. static const StatusMapper sStatusMapper;
  90. class CURLcodeMapper: public StatusMapperBase<CURLcode>
  91. {
  92. public:
  93.     CURLcodeMapper(): StatusMapperBase<CURLcode>("CURLcode")
  94.     {
  95.         // from curl.h
  96. // skip the "CURLE_" prefix for each of these strings
  97. #define def(sym) (mMap[sym] = #sym + 6)
  98.         def(CURLE_OK);
  99.         def(CURLE_UNSUPPORTED_PROTOCOL);    /* 1 */
  100.         def(CURLE_FAILED_INIT);             /* 2 */
  101.         def(CURLE_URL_MALFORMAT);           /* 3 */
  102.         def(CURLE_URL_MALFORMAT_USER);      /* 4 - NOT USED */
  103.         def(CURLE_COULDNT_RESOLVE_PROXY);   /* 5 */
  104.         def(CURLE_COULDNT_RESOLVE_HOST);    /* 6 */
  105.         def(CURLE_COULDNT_CONNECT);         /* 7 */
  106.         def(CURLE_FTP_WEIRD_SERVER_REPLY);  /* 8 */
  107.         def(CURLE_FTP_ACCESS_DENIED);       /* 9 a service was denied by the FTP server
  108.                                           due to lack of access - when login fails
  109.                                           this is not returned. */
  110.         def(CURLE_FTP_USER_PASSWORD_INCORRECT); /* 10 - NOT USED */
  111.         def(CURLE_FTP_WEIRD_PASS_REPLY);    /* 11 */
  112.         def(CURLE_FTP_WEIRD_USER_REPLY);    /* 12 */
  113.         def(CURLE_FTP_WEIRD_PASV_REPLY);    /* 13 */
  114.         def(CURLE_FTP_WEIRD_227_FORMAT);    /* 14 */
  115.         def(CURLE_FTP_CANT_GET_HOST);       /* 15 */
  116.         def(CURLE_FTP_CANT_RECONNECT);      /* 16 */
  117.         def(CURLE_FTP_COULDNT_SET_BINARY);  /* 17 */
  118.         def(CURLE_PARTIAL_FILE);            /* 18 */
  119.         def(CURLE_FTP_COULDNT_RETR_FILE);   /* 19 */
  120.         def(CURLE_FTP_WRITE_ERROR);         /* 20 */
  121.         def(CURLE_FTP_QUOTE_ERROR);         /* 21 */
  122.         def(CURLE_HTTP_RETURNED_ERROR);     /* 22 */
  123.         def(CURLE_WRITE_ERROR);             /* 23 */
  124.         def(CURLE_MALFORMAT_USER);          /* 24 - NOT USED */
  125.         def(CURLE_UPLOAD_FAILED);           /* 25 - failed upload "command" */
  126.         def(CURLE_READ_ERROR);              /* 26 - could open/read from file */
  127.         def(CURLE_OUT_OF_MEMORY);           /* 27 */
  128.         /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
  129.                  instead of a memory allocation error if CURL_DOES_CONVERSIONS
  130.                  is defined
  131.         */
  132.         def(CURLE_OPERATION_TIMEOUTED);     /* 28 - the timeout time was reached */
  133.         def(CURLE_FTP_COULDNT_SET_ASCII);   /* 29 - TYPE A failed */
  134.         def(CURLE_FTP_PORT_FAILED);         /* 30 - FTP PORT operation failed */
  135.         def(CURLE_FTP_COULDNT_USE_REST);    /* 31 - the REST command failed */
  136.         def(CURLE_FTP_COULDNT_GET_SIZE);    /* 32 - the SIZE command failed */
  137.         def(CURLE_HTTP_RANGE_ERROR);        /* 33 - RANGE "command" didn't work */
  138.         def(CURLE_HTTP_POST_ERROR);         /* 34 */
  139.         def(CURLE_SSL_CONNECT_ERROR);       /* 35 - wrong when connecting with SSL */
  140.         def(CURLE_BAD_DOWNLOAD_RESUME);     /* 36 - couldn't resume download */
  141.         def(CURLE_FILE_COULDNT_READ_FILE);  /* 37 */
  142.         def(CURLE_LDAP_CANNOT_BIND);        /* 38 */
  143.         def(CURLE_LDAP_SEARCH_FAILED);      /* 39 */
  144.         def(CURLE_LIBRARY_NOT_FOUND);       /* 40 */
  145.         def(CURLE_FUNCTION_NOT_FOUND);      /* 41 */
  146.         def(CURLE_ABORTED_BY_CALLBACK);     /* 42 */
  147.         def(CURLE_BAD_FUNCTION_ARGUMENT);   /* 43 */
  148.         def(CURLE_BAD_CALLING_ORDER);       /* 44 - NOT USED */
  149.         def(CURLE_INTERFACE_FAILED);        /* 45 - CURLOPT_INTERFACE failed */
  150.         def(CURLE_BAD_PASSWORD_ENTERED);    /* 46 - NOT USED */
  151.         def(CURLE_TOO_MANY_REDIRECTS );     /* 47 - catch endless re-direct loops */
  152.         def(CURLE_UNKNOWN_TELNET_OPTION);   /* 48 - User specified an unknown option */
  153.         def(CURLE_TELNET_OPTION_SYNTAX );   /* 49 - Malformed telnet option */
  154.         def(CURLE_OBSOLETE);                /* 50 - NOT USED */
  155.         def(CURLE_SSL_PEER_CERTIFICATE);    /* 51 - peer's certificate wasn't ok */
  156.         def(CURLE_GOT_NOTHING);             /* 52 - when this is a specific error */
  157.         def(CURLE_SSL_ENGINE_NOTFOUND);     /* 53 - SSL crypto engine not found */
  158.         def(CURLE_SSL_ENGINE_SETFAILED);    /* 54 - can not set SSL crypto engine as
  159.                                           default */
  160.         def(CURLE_SEND_ERROR);              /* 55 - failed sending network data */
  161.         def(CURLE_RECV_ERROR);              /* 56 - failure in receiving network data */
  162.         def(CURLE_SHARE_IN_USE);            /* 57 - share is in use */
  163.         def(CURLE_SSL_CERTPROBLEM);         /* 58 - problem with the local certificate */
  164.         def(CURLE_SSL_CIPHER);              /* 59 - couldn't use specified cipher */
  165.         def(CURLE_SSL_CACERT);              /* 60 - problem with the CA cert (path?) */
  166.         def(CURLE_BAD_CONTENT_ENCODING);    /* 61 - Unrecognized transfer encoding */
  167.         def(CURLE_LDAP_INVALID_URL);        /* 62 - Invalid LDAP URL */
  168.         def(CURLE_FILESIZE_EXCEEDED);       /* 63 - Maximum file size exceeded */
  169.         def(CURLE_FTP_SSL_FAILED);          /* 64 - Requested FTP SSL level failed */
  170.         def(CURLE_SEND_FAIL_REWIND);        /* 65 - Sending the data requires a rewind
  171.                                           that failed */
  172.         def(CURLE_SSL_ENGINE_INITFAILED);   /* 66 - failed to initialise ENGINE */
  173.         def(CURLE_LOGIN_DENIED);            /* 67 - user); password or similar was not
  174.                                           accepted and we failed to login */
  175.         def(CURLE_TFTP_NOTFOUND);           /* 68 - file not found on server */
  176.         def(CURLE_TFTP_PERM);               /* 69 - permission problem on server */
  177.         def(CURLE_TFTP_DISKFULL);           /* 70 - out of disk space on server */
  178.         def(CURLE_TFTP_ILLEGAL);            /* 71 - Illegal TFTP operation */
  179.         def(CURLE_TFTP_UNKNOWNID);          /* 72 - Unknown transfer ID */
  180.         def(CURLE_TFTP_EXISTS);             /* 73 - File already exists */
  181.         def(CURLE_TFTP_NOSUCHUSER);         /* 74 - No such user */
  182.         def(CURLE_CONV_FAILED);             /* 75 - conversion failed */
  183.         def(CURLE_CONV_REQD);               /* 76 - caller must register conversion
  184.                                           callbacks using curl_easy_setopt options
  185.                                           CURLOPT_CONV_FROM_NETWORK_FUNCTION);
  186.                                           CURLOPT_CONV_TO_NETWORK_FUNCTION); and
  187.                                           CURLOPT_CONV_FROM_UTF8_FUNCTION */
  188.         def(CURLE_SSL_CACERT_BADFILE);      /* 77 - could not load CACERT file); missing
  189.                                           or wrong format */
  190.         def(CURLE_REMOTE_FILE_NOT_FOUND);   /* 78 - remote file not found */
  191.         def(CURLE_SSH);                     /* 79 - error from the SSH layer); somewhat
  192.                                           generic so the error message will be of
  193.                                           interest when this has happened */
  194.         def(CURLE_SSL_SHUTDOWN_FAILED);     /* 80 - Failed to shut down the SSL
  195.                                           connection */
  196. #undef  def
  197.     }
  198. };
  199. static const CURLcodeMapper sCURLcodeMapper;
  200. LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname):
  201.     mBoundListener(LLEventPumps::instance().
  202.                    obtain(pumpname).
  203.                    listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1)))
  204. {
  205. }
  206. /**
  207.  * Capture an outstanding LLXMLRPCTransaction and poll it periodically until
  208.  * done.
  209.  *
  210.  * The sequence is:
  211.  * # Instantiate Poller, which instantiates, populates and initiates an
  212.  *   LLXMLRPCTransaction. Poller self-registers on the LLEventPump named
  213.  *   "mainloop".
  214.  * # "mainloop" is conventionally pumped once per frame. On each such call,
  215.  *   Poller checks its LLXMLRPCTransaction for completion.
  216.  * # When the LLXMLRPCTransaction completes, Poller collects results (if any)
  217.  *   and sends notification.
  218.  * # The tricky part: Poller frees itself (and thus its LLXMLRPCTransaction)
  219.  *   when done. The only external reference to it is the connection to the
  220.  *   "mainloop" LLEventPump.
  221.  */
  222. class Poller
  223. {
  224. public:
  225.     /// Validate the passed request for required fields, then use it to
  226.     /// populate an XMLRPC_REQUEST and an associated LLXMLRPCTransaction. Send
  227.     /// the request.
  228.     Poller(const LLSD& command):
  229.         mReqID(command),
  230.         mUri(command["uri"]),
  231.         mMethod(command["method"]),
  232.         mReplyPump(command["reply"])
  233.     {
  234.         // LL_ERRS if any of these are missing
  235.         const char* required[] = { "uri", "method", "reply" };
  236.         // optional: "options" (array of string)
  237.         // Validate the request
  238.         std::set<std::string> missing;
  239.         for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri)
  240.         {
  241.             // If the command does not contain this required entry, add it to 'missing'.
  242.             if (! command.has(*ri))
  243.             {
  244.                 missing.insert(*ri);
  245.             }
  246.         }
  247.         if (! missing.empty())
  248.         {
  249.             LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: ";
  250.             const char* separator = "";
  251.             for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end());
  252.                  mi != mend; ++mi)
  253.             {
  254.                 LL_CONT << separator << *mi;
  255.                 separator = ", ";
  256.             }
  257.             LL_CONT << LL_ENDL;
  258.         }
  259.         // Build the XMLRPC request.
  260.         XMLRPC_REQUEST request = XMLRPC_RequestNew();
  261.         XMLRPC_RequestSetMethodName(request, mMethod.c_str());
  262.         XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
  263.         XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
  264.         LLSD params(command["params"]);
  265.         if (params.isMap())
  266.         {
  267.             for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap());
  268.                  pi != pend; ++pi)
  269.             {
  270.                 std::string name(pi->first);
  271.                 LLSD param(pi->second);
  272.                 if (param.isString())
  273.                 {
  274.                     XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0);
  275.                 }
  276.                 else if (param.isInteger() || param.isBoolean())
  277.                 {
  278.                     XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger());
  279.                 }
  280.                 else if (param.isReal())
  281.                 {
  282.                     XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal());
  283.                 }
  284.                 else
  285.                 {
  286.                     LL_ERRS("LLXMLRPCListener") << mMethod << " request param "
  287.                                                 << name << " has unknown type: " << param << LL_ENDL;
  288.                 }
  289.             }
  290.         }
  291.         LLSD options(command["options"]);
  292.         if (options.isArray())
  293.         {
  294.             XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array);
  295.             for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray());
  296.                  oi != oend; ++oi)
  297.             {
  298.                 XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0);
  299.             }
  300.             XMLRPC_AddValueToVector(xparams, xoptions);
  301.         }
  302.         XMLRPC_RequestSetData(request, xparams);
  303.         mTransaction.reset(new LLXMLRPCTransaction(mUri, request));
  304. mPreviousStatus = mTransaction->status(NULL);
  305.         // Free the XMLRPC_REQUEST object and the attached data values.
  306.         XMLRPC_RequestFree(request, 1);
  307.         // Now ensure that we get regular callbacks to poll for completion.
  308.         mBoundListener =
  309.             LLEventPumps::instance().
  310.             obtain("mainloop").
  311.             listen(LLEventPump::inventName(), boost::bind(&Poller::poll, this, _1));
  312.         LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL;
  313.     }
  314.     /// called by "mainloop" LLEventPump
  315.     bool poll(const LLSD&)
  316.     {
  317.         bool done = mTransaction->process();
  318.         CURLcode curlcode;
  319.         LLXMLRPCTransaction::EStatus status;
  320.         {
  321.             // LLXMLRPCTransaction::status() is defined to accept int* rather
  322.             // than CURLcode*. I don't feel the urge to fix the signature, but
  323.             // we want a CURLcode rather than an int. So fetch it as a local
  324.             // int, but then assign to a CURLcode for the remainder of this
  325.             // method.
  326.             int curlint;
  327.             status = mTransaction->status(&curlint);
  328.             curlcode = CURLcode(curlint);
  329.         }
  330.         LLSD data(mReqID.makeResponse());
  331.         data["status"] = sStatusMapper.lookup(status);
  332.         data["errorcode"] = sCURLcodeMapper.lookup(curlcode);
  333.         data["error"] = "";
  334.         data["transfer_rate"] = 0.0;
  335.         LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump));
  336. if (! done)
  337.         {
  338.             // Not done yet, carry on.
  339. if (status == LLXMLRPCTransaction::StatusDownloading
  340. && status != mPreviousStatus)
  341. {
  342. // If a response has been received, send the 
  343. // 'downloading' status if it hasn't been sent.
  344. replyPump.post(data);
  345. }
  346. mPreviousStatus = status;
  347.             return false;
  348.         }
  349.         // Here the transaction is complete. Check status.
  350.         data["error"] = mTransaction->statusMessage();
  351. data["transfer_rate"] = mTransaction->transferRate();
  352.         LL_INFOS("LLXMLRPCListener") << mMethod << " result from " << mUri << ": status "
  353.                                      << data["status"].asString() << ", errorcode "
  354.                                      << data["errorcode"].asString()
  355.                                      << " (" << data["error"].asString() << ")"
  356.                                      << LL_ENDL;
  357.         // In addition to CURLE_OK, LLUserAuth distinguishes different error
  358.         // values of 'curlcode':
  359.         // CURLE_COULDNT_RESOLVE_HOST,
  360.         // CURLE_SSL_PEER_CERTIFICATE,
  361.         // CURLE_SSL_CACERT,
  362.         // CURLE_SSL_CONNECT_ERROR.
  363.         // Given 'message', need we care?
  364.         if (status == LLXMLRPCTransaction::StatusComplete)
  365.         {
  366.             // Success! Parse data.
  367.             std::string status_string(data["status"]);
  368.             data["responses"] = parseResponse(status_string);
  369.             data["status"] = status_string;
  370.         }
  371.         // whether successful or not, send reply on requested LLEventPump
  372.         replyPump.post(data);
  373.         // Because mTransaction is a boost::scoped_ptr, deleting this object
  374.         // frees our LLXMLRPCTransaction object.
  375.         // Because mBoundListener is an LLTempBoundListener, deleting this
  376.         // object disconnects it from "mainloop".
  377.         // *** MUST BE LAST ***
  378.         delete this;
  379.         return false;
  380.     }
  381. private:
  382.     /// Derived from LLUserAuth::parseResponse() and parseOptionInto()
  383.     LLSD parseResponse(std::string& status_string)
  384.     {
  385.         // Extract every member into data["responses"] (a map of string
  386.         // values).
  387.         XMLRPC_REQUEST response = mTransaction->response();
  388.         if (! response)
  389.         {
  390.             LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL;
  391.             return LLSD();
  392.         }
  393.         XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
  394.         if (! param)
  395.         {
  396.             LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL;
  397.             return LLSD();
  398.         }
  399.         // Now, parse everything
  400.         return parseValues(status_string, "", param);
  401.     }
  402.     /**
  403.      * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
  404.      * @param key_pfx Used to describe a given key in log messages. At top
  405.      * level, pass "". When parsing an options array, pass the top-level key
  406.      * name of the array plus the index of the array entry; to this we'll
  407.      * append the subkey of interest.
  408.      * @param param XMLRPC_VALUE iterator. At top level, pass
  409.      * XMLRPC_RequestGetData(XMLRPC_REQUEST).
  410.      */
  411.     LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
  412.     {
  413.         LLSD responses;
  414.         for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
  415.              current = XMLRPC_VectorNext(param))
  416.         {
  417.             std::string key(XMLRPC_GetValueID(current));
  418.             LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
  419.             XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current);
  420.             if (xmlrpc_type_string == type)
  421.             {
  422.                 LLSD::String val(XMLRPC_GetValueString(current));
  423.                 LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  424.                 responses.insert(key, val);
  425.             }
  426.             else if (xmlrpc_type_int == type)
  427.             {
  428.                 LLSD::Integer val(XMLRPC_GetValueInt(current));
  429.                 LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  430.                 responses.insert(key, val);
  431.             }
  432.             else if (xmlrpc_type_double == type)
  433.             {
  434.                 LLSD::Real val(XMLRPC_GetValueDouble(current));
  435.                 LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  436.                 responses.insert(key, val);
  437.             }
  438.             else if (xmlrpc_type_array == type)
  439.             {
  440.                 // We expect this to be an array of submaps. Walk the array,
  441.                 // recursively parsing each submap and collecting them.
  442.                 LLSD array;
  443.                 int i = 0;          // for descriptive purposes
  444.                 for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row;
  445.                      row = XMLRPC_VectorNext(current), ++i)
  446.                 {
  447.                     // Recursive call. For the lower-level key_pfx, if 'key'
  448.                     // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
  449.                     // nested call, a subkey "bar" will then be logged as
  450.                     // "foo[0]:bar", and so forth.
  451.                     // Parse the scalar subkey/value pairs from this array
  452.                     // entry into a temp submap. Collect such submaps in 'array'.
  453.                     array.append(parseValues(status_string,
  454.                                              STRINGIZE(key_pfx << key << '[' << i << "]:"),
  455.                                              row));
  456.                 }
  457.                 // Having collected an 'array' of 'submap's, insert that whole
  458.                 // 'array' as the value of this 'key'.
  459.                 responses.insert(key, array);
  460.             }
  461.             else
  462.             {
  463.                 // whoops - unrecognized type
  464.                 LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
  465.                                              << key_pfx << key << LL_ENDL;
  466.                 responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>'));
  467.                 status_string = "BadType";
  468.             }
  469.         }
  470.         return responses;
  471.     }
  472.     const LLReqID mReqID;
  473.     const std::string mUri;
  474.     const std::string mMethod;
  475.     const std::string mReplyPump;
  476.     LLTempBoundListener mBoundListener;
  477.     boost::scoped_ptr<LLXMLRPCTransaction> mTransaction;
  478. LLXMLRPCTransaction::EStatus mPreviousStatus; // To detect state changes.
  479. };
  480. bool LLXMLRPCListener::process(const LLSD& command)
  481. {
  482.     // Allocate a new heap Poller, but do not save a pointer to it. Poller
  483.     // will check its own status and free itself on completion of the request.
  484.     (new Poller(command));
  485.     // conventional event listener return
  486.     return false;
  487. }