DSL
download_nocurl.cpp
1 //@AUTOHEADER@BEGIN@
2 /***********************************************************************\
3 | Drift Standard Libraries v1.01 |
4 | Copyright 2010-2023 Drift Solutions / Indy Sams |
5 | Docs and more information available at https://www.driftsolutions.dev |
6 | This file released under the 3-clause BSD license, |
7 | see included DSL.LICENSE.TXT file for details. |
8 \***********************************************************************/
9 //@AUTOHEADER@END@
10 
11 #include <drift/dslcore.h>
12 #include <drift/download.h>
13 #include <drift/base64.h>
14 #include <drift/GenLib.h>
15 #include <drift/Threading.h>
16 
17 DSL_Download_NoCurl::DSL_Download_NoCurl(const string& url, DSL_Download_Callback callback, const string& user, const string& pass, void * user_ptr) {
18  user_agent = "DSL HTTP Downloader Class (Mozilla)";
19  socks = new DSL_Sockets3_Base();
20  SetCallback(callback, user_ptr);
21  SetURL(url);
22  SetUserPass(user, pass);
23 }
24 
25 DSL_Download_NoCurl::~DSL_Download_NoCurl() {
26  if (socks != NULL) {
27  delete socks;
28  socks = NULL;
29  }
30 }
31 
32 void DSL_Download_NoCurl::SetCallback(DSL_Download_Callback pcallback, void * puser_ptr) {
33  callback = pcallback;
34  u_ptr = puser_ptr;
35 }
36 
37 bool DSL_Download_NoCurl::SetURL(const string& purl) {
38  if (purl.length() < 8 || strncmp(purl.c_str(), "http://", 7)) {
39  this->error = TD_INVALID_PROTOCOL;
40  return false;
41  }
42 
43  host.clear();
44  path.clear();
45  port = 80;
46  error = TD_NO_ERROR;
47 
48  char * murl = dsl_strdup(purl.c_str());
49  char * begin = murl + 7;
50 
51  char * host = begin;
52  char *p = strchr(begin, '@');
53  if (p != NULL) {
54  host = p + 1;
55  *p = NULL;
56  char * q = strchr(begin, ':');
57  if (q != NULL) {
58  *q = 0;
59  q++;
60  SetUserPass(begin, q);
61  } else {
62  SetUserPass(begin, "");
63  }
64  }
65 
66  p = strchr(host, '/');
67  if (p != NULL) {
68  path = p;
69  *p = 0;
70  } else {
71  path = "/";
72  }
73 
74  p = strchr(host, ':');
75  if (p != NULL) {
76  *p = 0;
77  p++;
78  port = atoi(p);
79  }
80  this->host = host;
81  if (this->host.empty() || this->path.empty() || port == 0) {
82  this->error = TD_INVALID_URL;
83  dsl_free(murl);
84  return false;
85  }
86 
87  printf("host: %s, port: %u, path: %s\n", this->host.c_str(), port, this->path.c_str());
88 
89  dsl_free(murl);
90  return true;
91 }
92 
93 bool DSL_Download_NoCurl::GetURL(string& str) {
94  if (host.length() && path.length() && port) {
95  stringstream url;
96  url << "http://";
97  if (user.length() || pass.length()) {
98  url << user << ":" << pass << "@";
99  }
100  url << host << ":" << port << path;
101  str = std::move(url.str());
102  return true;
103  }
104  return false;
105 }
106 
107 void DSL_Download_NoCurl::SetUserPass(const string& puser, const string& ppass) {
108  user = puser;
109  pass = ppass;
110 }
111 
112 void DSL_Download_NoCurl::SetTimeout(uint32 millisec) {
113  timeo = millisec;
114 }
115 
116 void DSL_Download_NoCurl::FollowRedirects(bool follow) {
117  followRedirects = follow;
118 }
119 
120 void DSL_Download_NoCurl::SetUserAgent(const string& ua) {
121  if (ua.length()) {
122  user_agent = ua;
123  }
124 }
125 
127  if (this->error != TD_NO_ERROR) { return false; }
128 
129  D_SOCKET * sock = socks->Create(AF_INET, SOCK_STREAM, IPPROTO_TCP);
130  if (sock == NULL) {
131  this->error = TD_ERROR_CREATING_SOCKET;
132  return false;
133  }
134 
135  uint32 ctimeo = timeo;
136  if (ctimeo == 0) { ctimeo = 60000; }
137  if (!socks->ConnectWithTimeout(sock, host.c_str(), port, ctimeo)) {
138  this->error = TD_ERROR_CONNECTING;
139  socks->Close(sock);
140  return false;
141  }
142 
143  stringstream req;
144  req << "GET " << path << " HTTP/1.0\r\n";
145  req << "Host: " << host << " : " << port << "\r\n";
146  req << "User-Agent: " << user_agent << "\r\n";
147  req << "Connection: close\r\n";
148 
149  if (user.length() || pass.length()) {
150  string auth = mprintf("%s:%s", user.c_str(), pass.c_str());
151  size_t len = base64_encode_buffer_size(auth.length());
152  char * tmp = (char *)dsl_zmalloc(len + 1);
153  base64_encode(auth.c_str(), auth.length(), tmp);
154  req << "Authorization: Basic " << tmp << "\r\n";
155  dsl_free(tmp);
156  }
157 
158  req << "\r\n";
159 // printf("RAW GET: %s", buf);
160  if (timeo) {
161  socks->SetRecvTimeout(sock, timeo);
162  socks->SetSendTimeout(sock, timeo);
163  }
164  if (socks->Send(sock, req.str().c_str(), (int)req.str().length()) < (int)req.str().length()) {
165  this->error = TD_TIMEOUT;
166  socks->Close(sock);
167  return false;
168  }
169 
170  uint64 got = 0, fullsize = 0;
171 
172  int n=0,ln=0,tries=0;
173  char buf[16384] = { 0 };
174  while ((n = socks->RecvLine(sock,buf,16384)) >= RL3_NOLINE) {
175  if (n == RL3_NOLINE) {
176  if (tries > 300) {
177  this->error = TD_INVALID_RESPONSE;
178  socks->Close(sock);
179  return false;
180  } else {
181  tries++;
182  safe_sleep(100,true);
183  continue;
184  }
185  }
186  buf[n]=0;
187  while (buf[strlen(buf) - 1] == '\n') { buf[strlen(buf) - 1] = 0; }
188  while (buf[strlen(buf) - 1] == '\r') { buf[strlen(buf) - 1] = 0; }
189  while (buf[strlen(buf) - 1] == '\n') { buf[strlen(buf) - 1] = 0; }
190  if (strlen(buf) == 0) { break; }
191 
192  ln++;
193  if (ln == 1) {
194  if (strstr(buf,"401")) {
195  this->error = TD_BAD_USER_PASS;
196  socks->Close(sock);
197  return false;
198  }
199  if (strstr(buf,"404")) {
200  this->error = TD_FILE_NOT_FOUND;
201  socks->Close(sock);
202  return false;
203  }
204  if (!strstr(buf,"200") && !strstr(buf,"302")) {
205  this->error = TD_INVALID_RESPONSE;
206  socks->Close(sock);
207  return false;
208  }
209  }
210 
211  if (!strnicmp(buf,"Content-Length:",strlen("Content-Length:"))) {
212  char * p = buf + strlen("Content-Length:");
213  if (p[0] == ' ') { p++; }
214  fullsize = atoi64(p);
215  if (callback != NULL && !callback(0, fullsize, u_ptr)) {
216  this->error = TD_CALLBACK_ABORT;
217  socks->Close(sock);
218  return false;
219  }
220  }
221 
222  if (!strnicmp(buf,"Location:",strlen("Location:"))) {
223  char * p = buf + strlen("Location:");
224  if (p[0] == ' ') { p++; }
225 
226  if (followRedirects) {
227  socks->Close(sock);
228 
229  string url = mprintf("http://%s:%u%s", host.c_str(), this->port, path.c_str());
230  DSL_Download_NoCurl * dl = new DSL_Download_NoCurl(url.c_str(), callback, user, pass, u_ptr);
231  dl->SetTimeout(timeo);
232  dl->SetUserAgent(user_agent);
233  if (dl->GetError() == TD_NO_ERROR) {
234  bool ret = dl->Download(fWriteTo);
235  this->error = dl->GetError();
236  delete dl;
237  return ret;
238  } else {
239  this->error = dl->GetError();
240  delete dl;
241  return false;
242  }
243  } else {
244  socks->Close(sock);
245  this->error = TD_REDIRECT;
246  return false;
247  }
248  }
249  }
250 
251  while ((n = socks->Recv(sock, buf, 16384)) > 0) {
252  //buf[n]=0;
253  if (fWriteTo->write(buf, n, fWriteTo) < n) {
254  this->error = TD_FILE_WRITE_ERROR;
255  socks->Close(sock);
256  return false;
257  }
258  got += n;
259  if (callback != NULL && !callback(got, fullsize, u_ptr)) {
260  this->error = TD_CALLBACK_ABORT;
261  socks->Close(sock);
262  return false;
263  }
264  }
265 
266  socks->Close(sock);
267  return true;
268 }
269 
270 //#endif // !defined(NO_CURL)
virtual bool Download(DSL_FILE *fWriteTo)
virtual void SetTimeout(uint32 millisec)
virtual bool ConnectWithTimeout(DSL_SOCKET *sock, const char *host, int port, uint32 timeout)
Connect with timeout in milliseconds.
Definition: sockets3.cpp:475
DSL_API int DSL_CC base64_encode(const void *inBuffer, size_t count, char *outBuffer)
Definition: base64.cpp:74
bool(* DSL_Download_Callback)(uint64 got, uint64 fullsize, void *user_ptr)
Definition: download.h:31
DSL_API_CLASS string mprintf(const string fmt,...)
Definition: dsl.cpp:294
DSL_API void DSL_CC dsl_free(void *ptr)
Definition: dsl.cpp:345
DSL_API void *DSL_CC dsl_zmalloc(size_t lSize)
Definition: dsl.cpp:341
Definition: rwops.h:24
int64(* write)(void *buf, int64 size, DSL_FILE *fp)
Definition: rwops.h:41