1 /*
2 * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "stdinc.h"
20
21 #include "HttpConnection.h"
22
23 #include "format.h"
24 #include "SettingsManager.h"
25 #include "version.h"
26
27 namespace dcpp {
28
29 static const std::string CORAL_SUFFIX = ".nyud.net";
30
31 /**
32 * Downloads a file and returns it as a string
33 * @todo Report exceptions
34 * @todo Abort download
35 * @param aUrl Full URL of file
36 * @return A string with the content, or empty if download failed
37 */
downloadFile(const string & aUrl)38 void HttpConnection::downloadFile(const string& aUrl) {
39 dcassert(Util::findSubString(aUrl, "http://") == 0);
40 currentUrl = aUrl;
41 // Trim spaces
42 while(currentUrl[0] == ' ')
43 currentUrl.erase(0, 1);
44 while(currentUrl[currentUrl.length() - 1] == ' ') {
45 currentUrl.erase(currentUrl.length()-1);
46 }
47 // reset all settings (as in constructor), moved here from onLine(302) because ok was not reset properly
48 moved302 = false;
49 ok = false;
50 size = -1;
51 // set download type
52 if(Util::stricmp(currentUrl.substr(currentUrl.size() - 4), ".bz2") == 0) {
53 fire(HttpConnectionListener::TypeBZ2(), this);
54 } else {
55 fire(HttpConnectionListener::TypeNormal(), this);
56 }
57
58 string proto, query, fragment;
59 if(SETTING(HTTP_PROXY).empty()) {
60 Util::decodeUrl(currentUrl, proto, server, port, file, query, fragment);
61 if(file.empty())
62 file = "/";
63 } else {
64 Util::decodeUrl(SETTING(HTTP_PROXY), proto, server, port, file, query, fragment);
65 file = currentUrl;
66 }
67
68 if(BOOLSETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
69 if(server.length() > CORAL_SUFFIX.length() && server.compare(server.length() - CORAL_SUFFIX.length(), CORAL_SUFFIX.length(), CORAL_SUFFIX) !=0) {
70 server += CORAL_SUFFIX;
71 } else {
72 coralizeState = CST_NOCORALIZE;
73 }
74
75 }
76
77 if(port == 0)
78 port = 80;
79
80 if(!socket) {
81 socket = BufferedSocket::getSocket(0x0a);
82 }
83 socket->addListener(this);
84 try {
85 socket->connect(server, port, false, false, false);
86 } catch(const Exception& e) {
87 fire(HttpConnectionListener::Failed(), this, e.getError() + " (" + currentUrl + ")");
88 }
89 }
90
on(BufferedSocketListener::Connected)91 void HttpConnection::on(BufferedSocketListener::Connected) noexcept {
92 dcassert(socket);
93 socket->write("GET " + file + " HTTP/1.1\r\n");
94
95 string sRemoteServer = server;
96 if(!SETTING(HTTP_PROXY).empty())
97 {
98 string tfile, proto, query, fragment;
99 uint16_t tport;
100 Util::decodeUrl(file, proto, sRemoteServer, tport, tfile, query, fragment);
101 }
102
103 #ifdef WITH_DHT
104 if (sRemoteServer == "strongdc.sourceforge.net")
105 socket->write("User-Agent: StrongDC++ v2.42\r\n");
106 else
107 socket->write("User-Agent: " APPNAME " v" VERSIONSTRING "\r\n");
108 #else
109 socket->write("User-Agent: " APPNAME " v" VERSIONSTRING "\r\n");
110 #endif
111
112 socket->write("Host: " + sRemoteServer + "\r\n");
113 socket->write("Connection: close\r\n"); // we'll only be doing one request
114 socket->write("Cache-Control: no-cache\r\n\r\n");
115 if (coralizeState == CST_DEFAULT) coralizeState = CST_CONNECTED;
116 }
117
on(BufferedSocketListener::Line,const string & aLine)118 void HttpConnection::on(BufferedSocketListener::Line, const string& aLine) noexcept {
119 if(!ok) {
120 dcdebug("%s\n",aLine.c_str());
121 if(aLine.find("200") == string::npos) {
122 if(aLine.find("301") != string::npos || aLine.find("302") != string::npos){
123 moved302 = true;
124 } else {
125 socket->disconnect();
126 socket->removeListener(this);
127 BufferedSocket::putSocket(socket);
128 socket = NULL;
129 if(SETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
130 fire(HttpConnectionListener::Retried(), this, coralizeState == CST_CONNECTED);
131 coralizeState = CST_NOCORALIZE;
132 dcdebug("HTTP error with Coral, retrying : %s\n",currentUrl.c_str());
133 downloadFile(currentUrl);
134 return;
135 }
136 fire(HttpConnectionListener::Failed(), this, aLine + " (" + currentUrl + ")");
137 coralizeState = CST_DEFAULT;
138 return;
139 }
140 }
141 ok = true;
142 } else if(moved302 && Util::findSubString(aLine, "Location") != string::npos){
143 dcassert(socket);
144 socket->removeListener(this);
145 socket->disconnect();
146 BufferedSocket::putSocket(socket);
147 socket = NULL;
148
149 string location302 = aLine.substr(10, aLine.length() - 11);
150 // make sure we can also handle redirects with relative paths
151 if(Util::strnicmp(location302.c_str(), "http://", 7) != 0) {
152 if(location302[0] == '/') {
153 string proto, query, fragment;
154 Util::decodeUrl(currentUrl, proto, server, port, file, query, fragment);
155 string tmp = "http://" + server;
156 if(port != 80)
157 tmp += ':' + Util::toString(port);
158 location302 = tmp + location302;
159 } else {
160 string::size_type i = currentUrl.rfind('/');
161 dcassert(i != string::npos);
162 location302 = currentUrl.substr(0, i + 1) + location302;
163 }
164 }
165 if(location302 == currentUrl) {
166 fire(HttpConnectionListener::Failed(), this, str(F_("Endless redirection loop (%1%)") % currentUrl));
167 return;
168 }
169 fire(HttpConnectionListener::Redirected(), this, location302);
170
171 coralizeState = CST_DEFAULT;
172 downloadFile(location302);
173
174 } else if(aLine == "\x0d") {
175 socket->setDataMode(size);
176 } else if(Util::findSubString(aLine, "Content-Length") != string::npos) {
177 size = Util::toInt(aLine.substr(16, aLine.length() - 17));
178 } else if(Util::findSubString(aLine, "Content-Encoding") != string::npos) {
179 if(aLine.substr(18, aLine.length() - 19) == "x-bzip2")
180 fire(HttpConnectionListener::TypeBZ2(), this);
181 }
182 }
183
on(BufferedSocketListener::Failed,const string & aLine)184 void HttpConnection::on(BufferedSocketListener::Failed, const string& aLine) noexcept {
185 socket->removeListener(this);
186 BufferedSocket::putSocket(socket);
187 socket = NULL;
188 if(SETTING(CORAL) && coralizeState != CST_NOCORALIZE) {
189 fire(HttpConnectionListener::Retried(), this, coralizeState == CST_CONNECTED);
190 coralizeState = CST_NOCORALIZE;
191 dcdebug("Coralized address failed, retrying : %s\n",currentUrl.c_str());
192 downloadFile(currentUrl);
193 return;
194 }
195 coralizeState = CST_DEFAULT;
196 fire(HttpConnectionListener::Failed(), this, aLine + " (" + currentUrl + ")");
197 }
198
on(BufferedSocketListener::ModeChange)199 void HttpConnection::on(BufferedSocketListener::ModeChange) noexcept {
200 socket->removeListener(this);
201 socket->disconnect();
202 BufferedSocket::putSocket(socket);
203 socket = NULL;
204 fire(HttpConnectionListener::Complete(), this, currentUrl, BOOLSETTING(CORAL) && coralizeState != CST_NOCORALIZE);
205 coralizeState = CST_DEFAULT;
206 }
on(BufferedSocketListener::Data,uint8_t * aBuf,size_t aLen)207 void HttpConnection::on(BufferedSocketListener::Data, uint8_t* aBuf, size_t aLen) noexcept {
208 fire(HttpConnectionListener::Data(), this, aBuf, aLen);
209 }
210
211 } // namespace dcpp
212