00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llxmlrpctransaction.h"
00035
00036 #include "llviewercontrol.h"
00037
00038
00039 #include <curl/curl.h>
00040 #include <xmlrpc-epi/xmlrpc.h>
00041
00042 #include "viewer.h"
00043
00044 LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
00045 {
00046 return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
00047 }
00048
00049 std::string LLXMLRPCValue::asString() const
00050 {
00051 const char* s = XMLRPC_GetValueString(mV);
00052 return s ? s : "";
00053 }
00054
00055 int LLXMLRPCValue::asInt() const { return XMLRPC_GetValueInt(mV); }
00056 bool LLXMLRPCValue::asBool() const { return XMLRPC_GetValueBoolean(mV) != 0; }
00057 double LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
00058
00059 LLXMLRPCValue LLXMLRPCValue::rewind()
00060 {
00061 return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
00062 }
00063
00064 LLXMLRPCValue LLXMLRPCValue::next()
00065 {
00066 return LLXMLRPCValue(XMLRPC_VectorNext(mV));
00067 }
00068
00069 bool LLXMLRPCValue::isValid() const
00070 {
00071 return mV != NULL;
00072 }
00073
00074 LLXMLRPCValue LLXMLRPCValue::createArray()
00075 {
00076 return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
00077 }
00078
00079 LLXMLRPCValue LLXMLRPCValue::createStruct()
00080 {
00081 return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
00082 }
00083
00084
00085 void LLXMLRPCValue::append(LLXMLRPCValue& v)
00086 {
00087 XMLRPC_AddValueToVector(mV, v.mV);
00088 }
00089
00090 void LLXMLRPCValue::appendString(const std::string& v)
00091 {
00092 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
00093 }
00094
00095 void LLXMLRPCValue::appendInt(int v)
00096 {
00097 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
00098 }
00099
00100 void LLXMLRPCValue::appendBool(bool v)
00101 {
00102 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
00103 }
00104
00105 void LLXMLRPCValue::appendDouble(double v)
00106 {
00107 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
00108 }
00109
00110
00111 void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
00112 {
00113 XMLRPC_SetValueID(v.mV, id, 0);
00114 XMLRPC_AddValueToVector(mV, v.mV);
00115 }
00116
00117 void LLXMLRPCValue::appendString(const char* id, const std::string& v)
00118 {
00119 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
00120 }
00121
00122 void LLXMLRPCValue::appendInt(const char* id, int v)
00123 {
00124 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
00125 }
00126
00127 void LLXMLRPCValue::appendBool(const char* id, bool v)
00128 {
00129 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
00130 }
00131
00132 void LLXMLRPCValue::appendDouble(const char* id, double v)
00133 {
00134 XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
00135 }
00136
00137 void LLXMLRPCValue::free()
00138 {
00139 XMLRPC_CleanupValue(mV);
00140 mV = NULL;
00141 }
00142
00143 XMLRPC_VALUE LLXMLRPCValue::getValue() const
00144 {
00145 return mV;
00146 }
00147
00148
00149 class LLXMLRPCTransaction::Impl
00150 {
00151 public:
00152 typedef LLXMLRPCTransaction::Status Status;
00153
00154 CURL* mCurl;
00155 CURLM* mCurlMulti;
00156
00157 Status mStatus;
00158 CURLcode mCurlCode;
00159 std::string mStatusMessage;
00160 std::string mStatusURI;
00161
00162 char mCurlErrorBuffer[CURL_ERROR_SIZE];
00163
00164 std::string mURI;
00165 char* mRequestText;
00166 int mRequestTextSize;
00167
00168 std::string mProxyAddress;
00169 struct curl_slist* mHeaders;
00170
00171 std::string mResponseText;
00172 XMLRPC_REQUEST mResponse;
00173
00174 Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);
00175 Impl(const std::string& uri,
00176 const std::string& method, LLXMLRPCValue params, bool useGzip);
00177 ~Impl();
00178
00179 bool process();
00180
00181 void setStatus(Status code,
00182 const std::string& message = "", const std::string& uri = "");
00183 void setCurlStatus(CURLcode);
00184
00185 private:
00186 void init(XMLRPC_REQUEST request, bool useGzip);
00187
00188 static size_t curlDownloadCallback(
00189 void* data, size_t size, size_t nmemb, void* user_data);
00190 };
00191
00192 LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
00193 XMLRPC_REQUEST request, bool useGzip)
00194 : mCurl(0), mCurlMulti(0),
00195 mStatus(LLXMLRPCTransaction::StatusNotStarted),
00196 mURI(uri),
00197 mRequestText(0), mHeaders(0),
00198 mResponse(0)
00199 {
00200 init(request, useGzip);
00201 }
00202
00203
00204 LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
00205 const std::string& method, LLXMLRPCValue params, bool useGzip)
00206 : mCurl(0), mCurlMulti(0),
00207 mStatus(LLXMLRPCTransaction::StatusNotStarted),
00208 mURI(uri),
00209 mRequestText(0), mHeaders(0),
00210 mResponse(0)
00211 {
00212 XMLRPC_REQUEST request = XMLRPC_RequestNew();
00213 XMLRPC_RequestSetMethodName(request, method.c_str());
00214 XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
00215 XMLRPC_RequestSetData(request, params.getValue());
00216
00217 init(request, useGzip);
00218 }
00219
00220
00221
00222
00223 void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
00224 {
00225 mCurl = curl_easy_init();
00226
00227 if (gSavedSettings.getBOOL("BrowserProxyEnabled"))
00228 {
00229 mProxyAddress = gSavedSettings.getString("BrowserProxyAddress");
00230 S32 port = gSavedSettings.getS32 ( "BrowserProxyPort" );
00231
00232
00233 curl_easy_setopt(mCurl, CURLOPT_PROXY, mProxyAddress.c_str());
00234 curl_easy_setopt(mCurl, CURLOPT_PROXYPORT, (long) port);
00235 curl_easy_setopt(mCurl, CURLOPT_PROXYTYPE, (long) CURLPROXY_HTTP);
00236 };
00237
00238
00239 curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, 1L);
00240 curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &curlDownloadCallback);
00241 curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this);
00242 curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, &mCurlErrorBuffer);
00243 curl_easy_setopt(mCurl, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
00244 curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, (long) gVerifySSLCert);
00245 curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, gVerifySSLCert? 2L : 0L);
00246
00247 curl_easy_setopt(mCurl, CURLOPT_CONNECTTIMEOUT, 40L);
00248
00249
00250
00251 curl_easy_setopt(mCurl, CURLOPT_DNS_CACHE_TIMEOUT, -1L);
00252
00253 mHeaders = curl_slist_append(mHeaders, "Content-Type: text/xml");
00254 curl_easy_setopt(mCurl, CURLOPT_URL, mURI.c_str());
00255 curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaders);
00256 if (useGzip)
00257 {
00258 curl_easy_setopt(mCurl, CURLOPT_ENCODING, "");
00259 }
00260
00261 mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize);
00262 if (mRequestText)
00263 {
00264 curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, mRequestText);
00265 curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, (long) mRequestTextSize);
00266 }
00267 else
00268 {
00269 setStatus(StatusOtherError);
00270 }
00271
00272 mCurlMulti = curl_multi_init();
00273 curl_multi_add_handle(mCurlMulti, mCurl);
00274 }
00275
00276
00277 LLXMLRPCTransaction::Impl::~Impl()
00278 {
00279 if (mResponse)
00280 {
00281 XMLRPC_RequestFree(mResponse, 1);
00282 }
00283
00284 if (mHeaders)
00285 {
00286 curl_slist_free_all(mHeaders);
00287 }
00288
00289 if (mRequestText)
00290 {
00291 XMLRPC_Free(mRequestText);
00292 }
00293
00294 if (mCurl)
00295 {
00296 if (mCurlMulti)
00297 {
00298 curl_multi_remove_handle(mCurlMulti, mCurl);
00299 }
00300 curl_easy_cleanup(mCurl);
00301 }
00302
00303 if (mCurlMulti)
00304 {
00305 curl_multi_cleanup(mCurlMulti);
00306 }
00307
00308 }
00309
00310 bool LLXMLRPCTransaction::Impl::process()
00311 {
00312 switch(mStatus)
00313 {
00314 case LLXMLRPCTransaction::StatusComplete:
00315 case LLXMLRPCTransaction::StatusCURLError:
00316 case LLXMLRPCTransaction::StatusXMLRPCError:
00317 case LLXMLRPCTransaction::StatusOtherError:
00318 {
00319 return true;
00320 }
00321
00322 case LLXMLRPCTransaction::StatusNotStarted:
00323 {
00324 setStatus(LLXMLRPCTransaction::StatusStarted);
00325 break;
00326 }
00327
00328 default:
00329 {
00330
00331 }
00332 }
00333
00334 const F32 MAX_PROCESSING_TIME = 0.05f;
00335 LLTimer timer;
00336 int count;
00337
00338 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(mCurlMulti, &count))
00339 {
00340 if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME)
00341 {
00342 return false;
00343 }
00344 }
00345
00346 while(CURLMsg* curl_msg = curl_multi_info_read(mCurlMulti, &count))
00347 {
00348 if (CURLMSG_DONE == curl_msg->msg)
00349 {
00350 if (curl_msg->data.result != CURLE_OK)
00351 {
00352 setCurlStatus(curl_msg->data.result);
00353 llwarns << "LLXMLRPCTransaction CURL error "
00354 << mCurlCode << ": " << mCurlErrorBuffer << llendl;
00355 llwarns << "LLXMLRPCTransaction request URI: "
00356 << mURI << llendl;
00357
00358 return true;
00359 }
00360
00361 setStatus(LLXMLRPCTransaction::StatusComplete);
00362
00363 mResponse = XMLRPC_REQUEST_FromXML(
00364 mResponseText.data(), mResponseText.size(), NULL);
00365
00366 bool hasError = false;
00367 bool hasFault = false;
00368 int faultCode = 0;
00369 std::string faultString;
00370
00371 LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
00372 if (error.isValid())
00373 {
00374 hasError = true;
00375 faultCode = error["faultCode"].asInt();
00376 faultString = error["faultString"].asString();
00377 }
00378 else if (XMLRPC_ResponseIsFault(mResponse))
00379 {
00380 hasFault = true;
00381 faultCode = XMLRPC_GetResponseFaultCode(mResponse);
00382 faultString = XMLRPC_GetResponseFaultString(mResponse);
00383 }
00384
00385 if (hasError || hasFault)
00386 {
00387 setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
00388
00389 llwarns << "LLXMLRPCTransaction XMLRPC "
00390 << (hasError ? "error " : "fault ")
00391 << faultCode << ": "
00392 << faultString << llendl;
00393 llwarns << "LLXMLRPCTransaction request URI: "
00394 << mURI << llendl;
00395 }
00396
00397 return true;
00398 }
00399 }
00400
00401 return false;
00402 }
00403
00404 void LLXMLRPCTransaction::Impl::setStatus(Status status,
00405 const std::string& message, const std::string& uri)
00406 {
00407 mStatus = status;
00408 mStatusMessage = message;
00409 mStatusURI = uri;
00410
00411 if (mStatusMessage.empty())
00412 {
00413 switch (mStatus)
00414 {
00415 case StatusNotStarted:
00416 mStatusMessage = "(not started)";
00417 break;
00418
00419 case StatusStarted:
00420 mStatusMessage = "(waiting for server response)";
00421 break;
00422
00423 case StatusDownloading:
00424 mStatusMessage = "(reading server response)";
00425 break;
00426
00427 case StatusComplete:
00428 mStatusMessage = "(done)";
00429 break;
00430
00431 default:
00432
00433
00434 mStatusMessage =
00435 "Despite our best efforts, something unexpected has gone wrong. \n"
00436 " \n"
00437 "Please check www.secondlife.com/status \n"
00438 "to see if there is a known problem with the service.";
00439
00440 mStatusURI = "http://secondlife.com/status/";
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459 }
00460 }
00461 }
00462
00463 void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code)
00464 {
00465 std::string message;
00466 std::string uri = "http://secondlife.com/community/support.php";
00467
00468 switch (code)
00469 {
00470 case CURLE_COULDNT_RESOLVE_HOST:
00471 message =
00472 "DNS could not resolve the host name.\n"
00473 "Please verify that you can connect to the www.secondlife.com\n"
00474 "web site. If you can, but continue to receive this error,\n"
00475 "please go to the support section and report this problem.";
00476 break;
00477
00478 case CURLE_SSL_PEER_CERTIFICATE:
00479 message =
00480 "The login server couldn't verify itself via SSL.\n"
00481 "If you continue to receive this error, please go\n"
00482 "to the Support section of the SecondLife.com web site\n"
00483 "and report the problem.";
00484 break;
00485
00486 case CURLE_SSL_CACERT:
00487 case CURLE_SSL_CONNECT_ERROR:
00488 message =
00489 "Often this means that your computer\'s clock is set incorrectly.\n"
00490 "Please go to Control Panels and make sure the time and date\n"
00491 "are set correctly.\n"
00492 "\n"
00493 "If you continue to receive this error, please go\n"
00494 "to the Support section of the SecondLife.com web site\n"
00495 "and report the problem.";
00496 break;
00497
00498 default:
00499 break;
00500 }
00501
00502 mCurlCode = code;
00503 setStatus(StatusCURLError, message, uri);
00504 }
00505
00506 size_t LLXMLRPCTransaction::Impl::curlDownloadCallback(
00507 void* data, size_t size, size_t nmemb, void* user_data)
00508 {
00509 Impl& impl(*(Impl*)user_data);
00510
00511 size_t n = size * nmemb;
00512
00513 impl.mResponseText.append((const char*)data, n);
00514
00515 if (impl.mStatus == LLXMLRPCTransaction::StatusStarted)
00516 {
00517 impl.setStatus(LLXMLRPCTransaction::StatusDownloading);
00518 }
00519
00520 return n;
00521 }
00522
00523
00524 LLXMLRPCTransaction::LLXMLRPCTransaction(
00525 const std::string& uri, XMLRPC_REQUEST request, bool useGzip)
00526 : impl(* new Impl(uri, request, useGzip))
00527 { }
00528
00529
00530 LLXMLRPCTransaction::LLXMLRPCTransaction(
00531 const std::string& uri,
00532 const std::string& method, LLXMLRPCValue params, bool useGzip)
00533 : impl(* new Impl(uri, method, params, useGzip))
00534 { }
00535
00536 LLXMLRPCTransaction::~LLXMLRPCTransaction()
00537 {
00538 delete &impl;
00539 }
00540
00541 bool LLXMLRPCTransaction::process()
00542 {
00543 return impl.process();
00544 }
00545
00546 LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode)
00547 {
00548 if (curlCode)
00549 {
00550 *curlCode =
00551 (impl.mStatus == StatusCURLError)
00552 ? impl.mCurlCode
00553 : CURLE_OK;
00554 }
00555
00556 return impl.mStatus;
00557 }
00558
00559 std::string LLXMLRPCTransaction::statusMessage()
00560 {
00561 return impl.mStatusMessage;
00562 }
00563
00564 std::string LLXMLRPCTransaction::statusURI()
00565 {
00566 return impl.mStatusURI;
00567 }
00568
00569 XMLRPC_REQUEST LLXMLRPCTransaction::response()
00570 {
00571 return impl.mResponse;
00572 }
00573
00574 LLXMLRPCValue LLXMLRPCTransaction::responseValue()
00575 {
00576 return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
00577 }
00578
00579
00580 F64 LLXMLRPCTransaction::transferRate()
00581 {
00582 if (!impl.mCurl || impl.mStatus != StatusComplete)
00583 {
00584 return 0.0L;
00585 }
00586
00587 double size_bytes = 0.0;
00588 double time_seconds = 0.0;
00589 double rate_bytes_per_sec = 0.0;
00590
00591 curl_easy_getinfo(impl.mCurl, CURLINFO_SIZE_DOWNLOAD, &size_bytes);
00592 curl_easy_getinfo(impl.mCurl, CURLINFO_TOTAL_TIME, &time_seconds);
00593 curl_easy_getinfo(impl.mCurl, CURLINFO_SPEED_DOWNLOAD, &rate_bytes_per_sec);
00594
00595 double rate_bits_per_sec = rate_bytes_per_sec * 8.0;
00596
00597 llinfos << "Buffer size: " << impl.mResponseText.size() << " B" << llendl;
00598 llinfos << "Transfer size: " << size_bytes << " B" << llendl;
00599 llinfos << "Transfer time: " << time_seconds << " s" << llendl;
00600 llinfos << "Transfer rate: " << rate_bits_per_sec/1000.0 << " Kb/s" << llendl;
00601
00602 return rate_bits_per_sec;
00603 }