OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/sync/notifier/gaia_auth/gaiahelper.h" |
| 6 #include "talk/base/common.h" |
| 7 #include "talk/base/cryptstring.h" |
| 8 #include "talk/base/httpclient.h" |
| 9 #include "talk/base/httpcommon-inl.h" |
| 10 #include "talk/base/stringutils.h" |
| 11 #include "talk/base/urlencode.h" |
| 12 #include "talk/xmpp/constants.h" |
| 13 #include "talk/xmpp/jid.h" |
| 14 |
| 15 /////////////////////////////////////////////////////////////////////////////// |
| 16 |
| 17 namespace { |
| 18 |
| 19 std::string GetValueForKey(const std::string & key, const std::string & nvp) { |
| 20 size_t start_of_line = 0; |
| 21 size_t end_of_line = 0; |
| 22 for (;;) { // for each line |
| 23 start_of_line = nvp.find_first_not_of("\r\n", end_of_line); |
| 24 if (start_of_line == std::string::npos) |
| 25 break; |
| 26 end_of_line = nvp.find_first_of("\r\n", start_of_line); |
| 27 if (end_of_line == std::string::npos) { |
| 28 end_of_line = nvp.length(); |
| 29 } |
| 30 size_t equals = nvp.find('=', start_of_line); |
| 31 if (equals >= end_of_line || |
| 32 equals == std::string::npos || |
| 33 equals - start_of_line != key.length()) { |
| 34 continue; |
| 35 } |
| 36 |
| 37 if (nvp.find(key, start_of_line) == start_of_line) { |
| 38 return std::string(nvp, equals + 1, end_of_line - equals - 1); |
| 39 } |
| 40 } |
| 41 return ""; |
| 42 } |
| 43 |
| 44 } // anonymous namespace |
| 45 |
| 46 /////////////////////////////////////////////////////////////////////////////// |
| 47 |
| 48 namespace buzz { |
| 49 |
| 50 GaiaServer::GaiaServer() |
| 51 : hostname_("www.google.com"), |
| 52 port_(443), |
| 53 use_ssl_(true) { |
| 54 } |
| 55 |
| 56 bool GaiaServer::SetServer(const char* url) { |
| 57 talk_base::Url<char> parsed(url); |
| 58 hostname_ = parsed.server(); |
| 59 port_ = parsed.port(); |
| 60 use_ssl_ = parsed.secure(); |
| 61 return true; // parsed.valid(); |
| 62 } |
| 63 |
| 64 bool GaiaServer::SetDebugServer(const char* server) { |
| 65 const char* colon = strchr(server, ':'); |
| 66 if (colon) { |
| 67 hostname_ = std::string(server, colon - server); |
| 68 port_ = atoi(colon+1); |
| 69 use_ssl_ = false; |
| 70 return true; |
| 71 } |
| 72 return false; |
| 73 } |
| 74 |
| 75 std::string GaiaServer::http_prefix() const { |
| 76 talk_base::Url<char> parsed("", hostname_, port_); |
| 77 parsed.set_secure(use_ssl_); |
| 78 return parsed.url(); |
| 79 } |
| 80 |
| 81 /////////////////////////////////////////////////////////////////////////////// |
| 82 |
| 83 bool GaiaRequestSid(talk_base::HttpClient* client, |
| 84 const std::string& username, |
| 85 const talk_base::CryptString& password, |
| 86 const std::string& signature, |
| 87 const std::string& service, |
| 88 const CaptchaAnswer& captcha_answer, |
| 89 const GaiaServer& gaia_server) { |
| 90 buzz::Jid jid(username); |
| 91 std::string usable_name = username; |
| 92 if (jid.domain() == buzz::STR_DEFAULT_DOMAIN) { |
| 93 // The default domain (default.talk.google.com) is not usable |
| 94 // for Gaia auth. But both gmail.com and googlemain.com will |
| 95 // work, because the gaia server doesn't check to make sure the |
| 96 // appropriate one is being used. So we just slam on gmail.com |
| 97 usable_name = jid.node() + "@" + buzz::STR_GMAIL_COM; |
| 98 } |
| 99 |
| 100 std::string post_data; |
| 101 post_data += "Email=" + UrlEncodeString(usable_name); |
| 102 post_data += "&Passwd=" + password.UrlEncode(); |
| 103 post_data += "&PersistentCookie=false"; |
| 104 post_data += "&source=" + signature; |
| 105 // TODO(chron): This behavior is not the same as in the other gaia auth |
| 106 // loader. We should make it the same. Probably GOOGLE is enough, we don't |
| 107 // want to auth against hosted accounts. |
| 108 post_data += "&accountType=HOSTED_OR_GOOGLE"; |
| 109 post_data += "&skipvpage=true"; |
| 110 if (!service.empty()) { |
| 111 post_data += "&service=" + service; |
| 112 } |
| 113 |
| 114 if (!captcha_answer.captcha_token().empty()) { |
| 115 post_data += "&logintoken=" + captcha_answer.captcha_token(); |
| 116 post_data += "&logincaptcha=" |
| 117 + UrlEncodeString(captcha_answer.captcha_answer()); |
| 118 } |
| 119 |
| 120 client->reset(); |
| 121 client->set_server(talk_base::SocketAddress(gaia_server.hostname(), |
| 122 gaia_server.port(), false)); |
| 123 client->request().verb = talk_base::HV_POST; |
| 124 client->request().path = "/accounts/ClientAuth"; |
| 125 client->request().setContent("application/x-www-form-urlencoded", |
| 126 new talk_base::MemoryStream(post_data.data(), post_data.size())); |
| 127 client->response().document.reset(new talk_base::MemoryStream); |
| 128 client->start(); |
| 129 return true; |
| 130 } |
| 131 |
| 132 GaiaResponse GaiaParseSidResponse(const talk_base::HttpClient& client, |
| 133 const GaiaServer& gaia_server, |
| 134 std::string* captcha_token, |
| 135 std::string* captcha_url, |
| 136 std::string* sid, |
| 137 std::string* lsid, |
| 138 std::string* auth) { |
| 139 uint32 status_code = client.response().scode; |
| 140 const talk_base::MemoryStream* stream = |
| 141 static_cast<const talk_base::MemoryStream*>( |
| 142 client.response().document.get()); |
| 143 size_t length; |
| 144 stream->GetPosition(&length); |
| 145 std::string response; |
| 146 if (length > 0) { |
| 147 response.assign(stream->GetBuffer(), length); |
| 148 } |
| 149 |
| 150 LOG(LS_INFO) << "GaiaAuth request to " << client.request().path; |
| 151 LOG(LS_INFO) << "GaiaAuth Status Code: " << status_code; |
| 152 LOG(LS_INFO) << response; |
| 153 |
| 154 if (status_code == talk_base::HC_FORBIDDEN) { |
| 155 // The error URL may be the relative path to the captcha jpg. |
| 156 std::string image_url = GetValueForKey("CaptchaUrl", response); |
| 157 if (!image_url.empty()) { |
| 158 // We should activate this "full url code" once we have a better ways |
| 159 // to crack the URL for later download. Right now we are too |
| 160 // dependent on what Gaia returns. |
| 161 #if 0 |
| 162 if (image_url.find("http://") != 0 && |
| 163 image_url.find("https://") != 0) { |
| 164 if (image_url.find("/") == 0) { |
| 165 *captcha_url = gaia_server.http_prefix() + image_url; |
| 166 } else { |
| 167 *captcha_url = Utf8(gaia_server.http_prefix()).AsString() |
| 168 + "/accounts/" + image_url; |
| 169 } |
| 170 } |
| 171 #else |
| 172 *captcha_url = "/accounts/" + image_url; |
| 173 #endif |
| 174 |
| 175 *captcha_token = GetValueForKey("CaptchaToken", response); |
| 176 } |
| 177 return GR_UNAUTHORIZED; |
| 178 } |
| 179 |
| 180 if (status_code != talk_base::HC_OK) { |
| 181 return GR_ERROR; |
| 182 } |
| 183 |
| 184 *sid = GetValueForKey("SID", response); |
| 185 *lsid = GetValueForKey("LSID", response); |
| 186 if (auth) { |
| 187 *auth = GetValueForKey("Auth", response); |
| 188 } |
| 189 if (sid->empty() || lsid->empty()) { |
| 190 return GR_ERROR; |
| 191 } |
| 192 |
| 193 return GR_SUCCESS; |
| 194 } |
| 195 |
| 196 bool GaiaRequestAuthToken(talk_base::HttpClient* client, |
| 197 const std::string& sid, |
| 198 const std::string& lsid, |
| 199 const std::string& service, |
| 200 const GaiaServer& gaia_server) { |
| 201 std::string post_data; |
| 202 post_data += "SID=" + UrlEncodeString(sid); |
| 203 post_data += "&LSID=" + UrlEncodeString(lsid); |
| 204 post_data += "&service=" + service; |
| 205 post_data += "&Session=true"; // creates two week cookie |
| 206 |
| 207 client->reset(); |
| 208 client->set_server(talk_base::SocketAddress(gaia_server.hostname(), |
| 209 gaia_server.port(), false)); |
| 210 client->request().verb = talk_base::HV_POST; |
| 211 client->request().path = "/accounts/IssueAuthToken"; |
| 212 client->request().setContent("application/x-www-form-urlencoded", |
| 213 new talk_base::MemoryStream(post_data.data(), post_data.size())); |
| 214 client->response().document.reset(new talk_base::MemoryStream); |
| 215 client->start(); |
| 216 return true; |
| 217 } |
| 218 |
| 219 GaiaResponse GaiaParseAuthTokenResponse(const talk_base::HttpClient& client, |
| 220 std::string* auth_token) { |
| 221 if (client.response().scode != talk_base::HC_OK) { |
| 222 return GR_ERROR; |
| 223 } |
| 224 |
| 225 const talk_base::MemoryStream* stream = |
| 226 static_cast<const talk_base::MemoryStream*>( |
| 227 client.response().document.get()); |
| 228 size_t length; |
| 229 stream->GetPosition(&length); |
| 230 while ((length > 0) && isspace(stream->GetBuffer()[length-1])) |
| 231 --length; |
| 232 auth_token->assign(stream->GetBuffer(), length); |
| 233 return auth_token->empty() ? GR_ERROR : GR_SUCCESS; |
| 234 } |
| 235 |
| 236 } // namespace buzz |
OLD | NEW |