#include <curl/curl.h>
#include <iostream>

size_t write_callback(char *buffer, size_t size, size_t nitems, void *outstream) {
   return nitems;
}

int main(int argc, char** argv) {
   curl_global_init(CURL_GLOBAL_ALL);

   CURL *curl = curl_easy_init();
   CURLcode result;
   if (curl) {
      // The FTP server in this case is vsftpd v
      curl_easy_setopt(curl, CURLOPT_URL, "ftp://user:pass@10.0.0.8/test-file-50M.bin");
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 0);
      curl_easy_setopt(curl, CURLOPT_INTERFACE, "eth1");
      curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_callback);
      curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1);
      curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

      std::cout << "Finished setting curl options!\n";
      while (true) {
         std::cout << "Top of while loop!\n";
         
         // After what appears to be a non-deterministic number of iterations,
         // curl_easy_perform hangs.
         //
         // The stack trace available with gdb -p shows this:
         // #0  0x00007f9b88b25e84 in poll () from /lib64/libc.so.6
         // #1  0x000000000047b1fd in Curl_poll (ufds=0x7ffdd0fcd6d0, nfds=2, timeout_ms=1000) at select.c:313
         // #2  0x000000000046fbc9 in multi_wait (multi=0x319033c8, extra_fds=0x0, extra_nfds=0, timeout_ms=1000, ret=0x0, extrawait=true, use_wakeup=true) at multi.c:1366
         // #3  0x000000000046fe65 in curl_multi_poll (multi=0x319033c8, extra_fds=0x0, extra_nfds=0, timeout_ms=1000, ret=0x0) at multi.c:1509
         // #4  0x0000000000461bfb in easy_transfer (multi=0x319033c8) at easy.c:698
         // #5  0x0000000000461e14 in easy_perform (data=0x31900e48, events=false) at easy.c:796
         // #6  0x0000000000461e4c in curl_easy_perform (data=0x31900e48) at easy.c:815
         // #7  0x0000000000460d19 in main (argc=1, argv=0x7ffdd0fcd9a8) at curl_test.c:24
         //
         // strace -p shows this:
         // strace: Process 110425 attached
         // restart_syscall(<... resuming interrupted read ...>) = 1
         // recvfrom(6, "g}\10\341C=\7\260\336\256I&\1\362\225CBw\37\200\17\272~W\261\273/E/hGr"..., 16384, 0, NULL, NULL) = 1448
         // recvfrom(6, 0x1783298, 16384, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
         // poll([{fd=6, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
         // poll([{fd=6, events=POLLIN}, {fd=3, events=POLLIN}], 2, 1000) = 0 (Timeout)
         // recvfrom(6, 0x1783298, 16384, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
         // poll([{fd=6, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
         // poll([{fd=6, events=POLLIN}, {fd=3, events=POLLIN}], 2, 1000) = 1 ([{fd=6, revents=POLLIN}])
         // recvfrom(6, "\211\277\231@\240\373\367\330\212\214\317\233\37\212\201\246[\276{\375\214?\222K\317|\247\n~>b\230"..., 16384, 0, NULL, NULL) = 1448
         // recvfrom(6, 0x1783298, 16384, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
         // poll([{fd=6, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
         // poll([{fd=6, events=POLLIN}, {fd=3, events=POLLIN}], 2, 1000) = 0 (Timeout)
         // recvfrom(6, 0x1783298, 16384, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
         // poll([{fd=6, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
         // poll([{fd=6, events=POLLIN}, {fd=3, events=POLLIN}], 2, 1000) = 1 ([{fd=6, revents=POLLIN}])
         // recvfrom(6, "ub\v\37\321\1\6\317\217\305c\221K\306\177\243\315\261\4\337\276|\354*\315\201\355\316\257\306'D"..., 16384, 0, NULL, NULL) = 1448
         // recvfrom(6, 0x1783298, 16384, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
         // poll([{fd=6, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
         // poll([{fd=6, events=POLLIN}, {fd=3, events=POLLIN}], 2, 1000) = 0 (Timeout)      

         result = curl_easy_perform(curl);
         if (result == CURLE_OK) {
            std::cout << "done with curl_easy_perform!\n";
         }
         else std::cout << "got some error with curl: " << result << '\n';
      }
      return 0;
   }
   else return 1;
}

