The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
curlhttpt.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * curlhttpt.cpp - CURLHTTPTransport
4  *
5  * $Id: curlhttpt.cpp 3822 2020-11-03 18:54:47Z scribe $
6  *
7  * Copyright 2004-2013 CrossWire Bible Society (http://www.crosswire.org)
8  * CrossWire Bible Society
9  * P. O. Box 2528
10  * Tempe, AZ 85280-2528
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  */
22 
23 #include <vector>
24 #include <cctype>
25 
26 #include <curl/curl.h>
27 #include <curl/easy.h>
28 
29 #include <swlog.h>
30 #include <filemgr.h>
31 #include <curlhttpt.h>
32 
33 using std::vector;
34 
35 
37 
38 namespace {
39 
40  struct FtpFile {
41  const char *filename;
42  int fd;
43  SWBuf *destBuf;
44  };
45 
46 
47  static int my_httpfwrite(void *buffer, size_t size, size_t nmemb, void *stream) {
48  struct FtpFile *out = (struct FtpFile *)stream;
49  if (out && !out->fd && !out->destBuf) {
50  /* open file for writing */
51  out->fd = FileMgr::createPathAndFile(out->filename);
52  if (out->fd < 0)
53  return -1; /* failure, can't open file to write */
54  }
55  if (out->destBuf) {
56  int s = (int)out->destBuf->size();
57  out->destBuf->size(s+(size*nmemb));
58  memcpy(out->destBuf->getRawData()+s, buffer, size*nmemb);
59  return (int)nmemb;
60  }
61  return (int)FileMgr::write(out->fd, buffer, size * nmemb);
62  }
63 
64 
65  static int my_httpfprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
66  if (clientp) {
67  if (dltotal < 0) dltotal = 0;
68  if (dlnow < 0) dlnow = 0;
69  if (dlnow > dltotal) dlnow = dltotal;
70  ((StatusReporter *)clientp)->update(dltotal, dlnow);
71  }
72  return 0;
73  }
74 
75 
76  static int myhttp_trace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, void *userp) {
77  SWBuf header;
78  (void)userp; /* prevent compiler warning */
79  (void)handle; /* prevent compiler warning */
80 
81  switch (type) {
82  case CURLINFO_TEXT: header = "TEXT"; break;
83  case CURLINFO_HEADER_OUT: header = "=> Send header"; break;
84  case CURLINFO_HEADER_IN: header = "<= Recv header"; break;
85 
86  // these we don't want to log (HUGE)
87  case CURLINFO_DATA_OUT: header = "=> Send data";
88  case CURLINFO_SSL_DATA_OUT: header = "=> Send SSL data";
89  case CURLINFO_DATA_IN: header = "<= Recv data";
90  case CURLINFO_SSL_DATA_IN: header = "<= Recv SSL data";
91  default: /* in case a new one is introduced to shock us */
92  return 0;
93  }
94 
95  if (size > 120) size = 120;
96  SWBuf text;
97  text.size(size);
98  memcpy(text.getRawData(), data, size);
99 SWLOGD("CURLHTTPTransport: %s: %s", header.c_str(), text.c_str());
100  return 0;
101  }
102 }
103 
104 
106  session = (CURL *)curl_easy_init();
107 }
108 
109 
111  curl_easy_cleanup(session);
112 }
113 
114 
115 char CURLHTTPTransport::getURL(const char *destPath, const char *sourceURL, SWBuf *destBuf) {
116  signed char retVal = 0;
117  struct FtpFile ftpfile = {destPath, 0, destBuf};
118 
119  CURLcode res;
120 
121  if (session) {
122  curl_easy_setopt(session, CURLOPT_URL, sourceURL);
123 
124  SWBuf credentials = u + ":" + p;
125  curl_easy_setopt(session, CURLOPT_USERPWD, credentials.c_str());
126  curl_easy_setopt(session, CURLOPT_WRITEFUNCTION, my_httpfwrite);
127  if (!passive)
128  curl_easy_setopt(session, CURLOPT_FTPPORT, "-");
129  curl_easy_setopt(session, CURLOPT_NOPROGRESS, 0);
130  curl_easy_setopt(session, CURLOPT_FAILONERROR, 1);
131  curl_easy_setopt(session, CURLOPT_PROGRESSDATA, statusReporter);
132  curl_easy_setopt(session, CURLOPT_PROGRESSFUNCTION, my_httpfprogress);
133  curl_easy_setopt(session, CURLOPT_DEBUGFUNCTION, myhttp_trace);
134  /* Set a pointer to our struct to pass to the callback */
135  curl_easy_setopt(session, CURLOPT_FILE, &ftpfile);
136 
137  /* Switch on full protocol/debug output */
138  curl_easy_setopt(session, CURLOPT_VERBOSE, true);
139 #ifndef OLDCURL
140  curl_easy_setopt(session, CURLOPT_CONNECTTIMEOUT_MS, timeoutMillis);
141  curl_easy_setopt(session, CURLOPT_TIMEOUT_MS, timeoutMillis);
142 #else
143  curl_easy_setopt(session, CURLOPT_CONNECTTIMEOUT, timeoutMillis/1000);
144  curl_easy_setopt(session, CURLOPT_TIMEOUT, timeoutMillis/1000);
145 #endif
146 
147  /* Disable checking host certificate */
148  if (isUnverifiedPeerAllowed()) {
149  curl_easy_setopt(session, CURLOPT_SSL_VERIFYPEER, false);
150  }
151 
152  /* FTP connection settings */
153 
154 #if (LIBCURL_VERSION_MAJOR > 7) || \
155  ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR > 10)) || \
156  ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 10) && (LIBCURL_VERSION_PATCH >= 5))
157 # define EPRT_AVAILABLE 1
158 #endif
159 
160 #ifdef EPRT_AVAILABLE
161  curl_easy_setopt(session, CURLOPT_FTP_USE_EPRT, 0);
162 SWLOGD("***** using CURLOPT_FTP_USE_EPRT\n");
163 #endif
164 
165 
166 SWLOGD("***** About to perform curl easy action. \n");
167 SWLOGD("***** destPath: %s \n", destPath);
168 SWLOGD("***** sourceURL: %s \n", sourceURL);
169  res = curl_easy_perform(session);
170 SWLOGD("***** Finished performing curl easy action. \n");
171 
172  if(CURLE_OK != res) {
173  if (CURLE_OPERATION_TIMEDOUT == res
174 #ifdef CURLE_FTP_ACCEPT_TIMEOUT
175  || CURLE_FTP_ACCEPT_TIMEOUT == res
176 #endif
177  ) {
178  retVal = -2;
179  }
180  else {
181  retVal = -1;
182  }
183  }
184  }
185 
186  if (ftpfile.fd > 0)
187  FileMgr::closeFile(ftpfile.fd); /* close the local file */
188 
189  return retVal;
190 }
191 
192 
193 // we need to find the 2nd "<td" & then find the ">" after that. The size starts with the next non-space char
194 const char *findSizeStart(const char *buffer) {
195  const char *listing = buffer;
196  const char *pEnd;
197 
198  pEnd = strstr(listing, "<td");
199  if(pEnd == NULL) {
200  return NULL;
201  }
202  listing = pEnd+2;
203  pEnd = strstr(listing, "<td");
204  if(pEnd == NULL)
205  return NULL;
206  listing = pEnd+2;
207  pEnd = strchr(listing, '>');
208  if(pEnd == NULL)
209  return NULL;
210 
211  return pEnd+1;
212 }
213 
214 
215 vector<struct DirEntry> CURLHTTPTransport::getDirList(const char *dirURL) {
216 
217  vector<struct DirEntry> dirList;
218 
219  SWBuf dirBuf;
220  const char *pBuf;
221  char *pBufRes;
222  SWBuf possibleName;
223  double fSize;
224  int possibleNameLength = 0;
225 
226  if (!getURL("", dirURL, &dirBuf)) {
227  pBuf = strstr(dirBuf, "<a href=\"");//Find the next link to a possible file name.
228  while (pBuf != NULL) {
229  pBuf += 9;//move to the start of the actual name.
230  pBufRes = (char *)strchr(pBuf, '\"');//Find the end of the possible file name
231  if (!pBufRes)
232  break;
233  possibleNameLength = (int)(pBufRes - pBuf);
234  possibleName.setFormatted("%.*s", possibleNameLength, pBuf);
235  if (isalnum(possibleName[0])) {
236 SWLOGD("getDirListHTTP: Found a file: %s", possibleName.c_str());
237  pBuf = pBufRes;
238  pBufRes = (char *)findSizeStart(pBuf);
239  fSize = 0;
240  if(pBufRes != NULL) {
241  pBuf = pBufRes;
242  fSize = strtod(pBuf, &pBufRes);
243  if (pBufRes[0] == 'K')
244  fSize *= 1024;
245  else if (pBufRes[0] == 'M')
246  fSize *= 1048576;
247  pBuf = pBufRes;
248  }
249  struct DirEntry i;
250  i.name = possibleName;
251  i.size = (long unsigned int)fSize;
252  i.isDirectory = possibleName.endsWith("/");
253  dirList.push_back(i);
254  } else {
255  pBuf += possibleNameLength;
256  }
257  pBuf++;
258  pBuf = strstr(pBuf, "<a href=\"");//Find the next link to a possible file name.
259  }
260  }
261  else
262  {
263  SWLog::getSystemLog()->logWarning("FTPURLGetDir: failed to get dir %s\n", dirURL);
264  }
265  return dirList;
266 }
267 
269 
bool isDirectory
Definition: filemgr.h:43
#define SWORD_NAMESPACE_START
Definition: defs.h:39
Definition: swbuf.h:47
unsigned long size
Definition: filemgr.h:42
static SWLog * getSystemLog()
Definition: swlog.cpp:53
preg buffer
Definition: regex.c:8089
static void closeFile(int fd)
Definition: filemgr.cpp:491
CURLHTTPTransport(const char *host, StatusReporter *statusReporter=0)
Definition: curlhttpt.cpp:105
static int my_httpfprogress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Definition: curlhttpt.cpp:65
bool endsWith(const SWBuf &postfix) const
Definition: swbuf.h:501
const char * findSizeStart(const char *buffer)
Definition: curlhttpt.cpp:194
return NULL
Definition: regex.c:7953
char * getRawData()
Definition: swbuf.h:379
const char * c_str() const
Definition: swbuf.h:158
SWBuf name
Definition: filemgr.h:41
virtual std::vector< struct DirEntry > getDirList(const char *dirURL)
Definition: curlhttpt.cpp:215
unsigned long size() const
Definition: swbuf.h:185
static long write(int fd, const void *buf, long count)
Definition: filemgr.cpp:115
int size
Definition: regex.c:5043
void logWarning(const char *fmt,...) const
Definition: swlog.cpp:74
bool isUnverifiedPeerAllowed()
Definition: remotetrans.h:99
static int createPathAndFile(const char *fName)
Definition: filemgr.cpp:479
static int my_httpfwrite(void *buffer, size_t size, size_t nmemb, void *stream)
Definition: curlhttpt.cpp:47
static int myhttp_trace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, void *userp)
Definition: curlhttpt.cpp:76
#define SWORD_NAMESPACE_END
Definition: defs.h:40
#define SWLOGD(...)
Definition: defs.h:187
virtual char getURL(const char *destPath, const char *sourceURL, SWBuf *destBuf=0)
Definition: curlhttpt.cpp:115
SWBuf & setFormatted(const char *format,...)
Definition: swbuf.cpp:50
StatusReporter * statusReporter
Definition: remotetrans.h:59