PageRenderTime 34ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/newview/llwebprofile.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 305 lines | 190 code | 47 blank | 68 comment | 12 complexity | 5bd233ff2749fecd69b746aa347b32ea MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llwebprofile.cpp
  3. * @brief Web profile access.
  4. *
  5. * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2011, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llwebprofile.h"
  28. // libs
  29. #include "llbufferstream.h"
  30. #include "llhttpclient.h"
  31. #include "llimagepng.h"
  32. #include "llplugincookiestore.h"
  33. // newview
  34. #include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions
  35. #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals
  36. // third-party
  37. #include "reader.h" // JSON
  38. /*
  39. * Workflow:
  40. * 1. LLViewerMedia::setOpenIDCookie()
  41. * -> GET https://my-demo.secondlife.com/ via LLViewerMediaWebProfileResponder
  42. * -> LLWebProfile::setAuthCookie()
  43. * 2. LLWebProfile::uploadImage()
  44. * -> GET "https://my-demo.secondlife.com/snapshots/s3_upload_config" via ConfigResponder
  45. * 3. LLWebProfile::post()
  46. * -> POST <config_url> via PostImageResponder
  47. * -> redirect
  48. * -> GET <redirect_url> via PostImageRedirectResponder
  49. */
  50. ///////////////////////////////////////////////////////////////////////////////
  51. // LLWebProfileResponders::ConfigResponder
  52. class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder
  53. {
  54. LOG_CLASS(LLWebProfileResponders::ConfigResponder);
  55. public:
  56. ConfigResponder(LLPointer<LLImageFormatted> imagep)
  57. : mImagep(imagep)
  58. {
  59. }
  60. /*virtual*/ void completedRaw(
  61. U32 status,
  62. const std::string& reason,
  63. const LLChannelDescriptors& channels,
  64. const LLIOPipe::buffer_ptr_t& buffer)
  65. {
  66. LLBufferStream istr(channels, buffer.get());
  67. std::stringstream strstrm;
  68. strstrm << istr.rdbuf();
  69. const std::string body = strstrm.str();
  70. if (status != 200)
  71. {
  72. llwarns << "Failed to get upload config (" << status << ")" << llendl;
  73. LLWebProfile::reportImageUploadStatus(false);
  74. return;
  75. }
  76. Json::Value root;
  77. Json::Reader reader;
  78. if (!reader.parse(body, root))
  79. {
  80. llwarns << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << llendl;
  81. LLWebProfile::reportImageUploadStatus(false);
  82. return;
  83. }
  84. // *TODO: 404 = not supported by the grid
  85. // *TODO: increase timeout or handle 499 Expired
  86. // Convert config to LLSD.
  87. const Json::Value data = root["data"];
  88. const std::string upload_url = root["url"].asString();
  89. LLSD config;
  90. config["acl"] = data["acl"].asString();
  91. config["AWSAccessKeyId"] = data["AWSAccessKeyId"].asString();
  92. config["Content-Type"] = data["Content-Type"].asString();
  93. config["key"] = data["key"].asString();
  94. config["policy"] = data["policy"].asString();
  95. config["success_action_redirect"] = data["success_action_redirect"].asString();
  96. config["signature"] = data["signature"].asString();
  97. config["add_loc"] = data.get("add_loc", "0").asString();
  98. config["caption"] = data.get("caption", "").asString();
  99. // Do the actual image upload using the configuration.
  100. LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << llendl;
  101. LLWebProfile::post(mImagep, config, upload_url);
  102. }
  103. private:
  104. LLPointer<LLImageFormatted> mImagep;
  105. };
  106. ///////////////////////////////////////////////////////////////////////////////
  107. // LLWebProfilePostImageRedirectResponder
  108. class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder
  109. {
  110. LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder);
  111. public:
  112. /*virtual*/ void completedRaw(
  113. U32 status,
  114. const std::string& reason,
  115. const LLChannelDescriptors& channels,
  116. const LLIOPipe::buffer_ptr_t& buffer)
  117. {
  118. if (status != 200)
  119. {
  120. llwarns << "Failed to upload image: " << status << " " << reason << llendl;
  121. LLWebProfile::reportImageUploadStatus(false);
  122. return;
  123. }
  124. LLBufferStream istr(channels, buffer.get());
  125. std::stringstream strstrm;
  126. strstrm << istr.rdbuf();
  127. const std::string body = strstrm.str();
  128. llinfos << "Image uploaded." << llendl;
  129. LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl;
  130. LLWebProfile::reportImageUploadStatus(true);
  131. }
  132. private:
  133. LLPointer<LLImageFormatted> mImagep;
  134. };
  135. ///////////////////////////////////////////////////////////////////////////////
  136. // LLWebProfileResponders::PostImageResponder
  137. class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder
  138. {
  139. LOG_CLASS(LLWebProfileResponders::PostImageResponder);
  140. public:
  141. /*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
  142. {
  143. // Viewer seems to fail to follow a 303 redirect on POST request
  144. // (URLRequest Error: 65, Send failed since rewinding of the data stream failed).
  145. // Handle it manually.
  146. if (status == 303)
  147. {
  148. LLSD headers = LLViewerMedia::getHeaders();
  149. headers["Cookie"] = LLWebProfile::getAuthCookie();
  150. const std::string& redir_url = content["location"];
  151. LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl;
  152. LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers);
  153. }
  154. else
  155. {
  156. llwarns << "Unexpected POST status: " << status << " " << reason << llendl;
  157. LL_DEBUGS("Snapshots") << "headers: [" << content << "]" << llendl;
  158. LLWebProfile::reportImageUploadStatus(false);
  159. }
  160. }
  161. // Override just to suppress warnings.
  162. /*virtual*/ void completedRaw(U32 status, const std::string& reason,
  163. const LLChannelDescriptors& channels,
  164. const LLIOPipe::buffer_ptr_t& buffer)
  165. {
  166. }
  167. };
  168. ///////////////////////////////////////////////////////////////////////////////
  169. // LLWebProfile
  170. std::string LLWebProfile::sAuthCookie;
  171. LLWebProfile::status_callback_t LLWebProfile::mStatusCallback;
  172. // static
  173. void LLWebProfile::uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location)
  174. {
  175. // Get upload configuration data.
  176. std::string config_url(getProfileURL(LLStringUtil::null) + "snapshots/s3_upload_config");
  177. config_url += "?caption=" + LLURI::escape(caption);
  178. config_url += "&add_loc=" + std::string(add_location ? "1" : "0");
  179. LL_DEBUGS("Snapshots") << "Requesting " << config_url << llendl;
  180. LLSD headers = LLViewerMedia::getHeaders();
  181. headers["Cookie"] = getAuthCookie();
  182. LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers);
  183. }
  184. // static
  185. void LLWebProfile::setAuthCookie(const std::string& cookie)
  186. {
  187. LL_DEBUGS("Snapshots") << "Setting auth cookie: " << cookie << llendl;
  188. sAuthCookie = cookie;
  189. }
  190. // static
  191. void LLWebProfile::post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url)
  192. {
  193. if (dynamic_cast<LLImagePNG*>(image.get()) == 0)
  194. {
  195. llwarns << "Image to upload is not a PNG" << llendl;
  196. llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0);
  197. return;
  198. }
  199. const std::string boundary = "----------------------------0123abcdefab";
  200. LLSD headers = LLViewerMedia::getHeaders();
  201. headers["Cookie"] = getAuthCookie();
  202. headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
  203. std::ostringstream body;
  204. // *NOTE: The order seems to matter.
  205. body << "--" << boundary << "\r\n"
  206. << "Content-Disposition: form-data; name=\"key\"\r\n\r\n"
  207. << config["key"].asString() << "\r\n";
  208. body << "--" << boundary << "\r\n"
  209. << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
  210. << config["AWSAccessKeyId"].asString() << "\r\n";
  211. body << "--" << boundary << "\r\n"
  212. << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
  213. << config["acl"].asString() << "\r\n";
  214. body << "--" << boundary << "\r\n"
  215. << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n"
  216. << config["Content-Type"].asString() << "\r\n";
  217. body << "--" << boundary << "\r\n"
  218. << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n"
  219. << config["policy"].asString() << "\r\n";
  220. body << "--" << boundary << "\r\n"
  221. << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n"
  222. << config["signature"].asString() << "\r\n";
  223. body << "--" << boundary << "\r\n"
  224. << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n"
  225. << config["success_action_redirect"].asString() << "\r\n";
  226. body << "--" << boundary << "\r\n"
  227. << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n"
  228. << "Content-Type: image/png\r\n\r\n";
  229. // Insert the image data.
  230. // *FIX: Treating this as a string will probably screw it up ...
  231. U8* image_data = image->getData();
  232. for (S32 i = 0; i < image->getDataSize(); ++i)
  233. {
  234. body << image_data[i];
  235. }
  236. body << "\r\n--" << boundary << "--\r\n";
  237. // postRaw() takes ownership of the buffer and releases it later.
  238. size_t size = body.str().size();
  239. U8 *data = new U8[size];
  240. memcpy(data, body.str().data(), size);
  241. // Send request, successful upload will trigger posting metadata.
  242. LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers);
  243. }
  244. // static
  245. void LLWebProfile::reportImageUploadStatus(bool ok)
  246. {
  247. if (mStatusCallback)
  248. {
  249. mStatusCallback(ok);
  250. }
  251. }
  252. // static
  253. std::string LLWebProfile::getAuthCookie()
  254. {
  255. // This is needed to test image uploads on Linux viewer built with OpenSSL 1.0.0 (0.9.8 works fine).
  256. const char* debug_cookie = getenv("LL_SNAPSHOT_COOKIE");
  257. return debug_cookie ? debug_cookie : sAuthCookie;
  258. }