/*****************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * $Id: lib500.c,v 1.9 2008-09-20 04:26:56 yangtse Exp $
 */

#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <curl/curl.h>

#define numChannels 2

#define activeFtp 1

#define DEFAULT_TIMEOUT_MILLISECS 250
#define MAX_TIMEOUT_MILLISECS 1000

#define URL "ftp://localhost/tmp"
#define USERPWD "user:secret"

struct upload {
  const char *path;
  const char *data;
  int count;
};

static size_t CurlReadCallback( void *ptr, size_t size, size_t nmemb,
                                void *stream )
{
  struct upload *up = (struct upload *)stream;
  (void)size;
  (void)nmemb;

  /* we only return data once */
  if(up->count++)
    return 0;

  strcpy(ptr, up->data);
  return strlen(up->data);
}

int main(void)
{
  const char *userPassword = USERPWD;
  CURLM* curlMulti = curl_multi_init();
  CURL *curlEasy[numChannels];
  int i;
  int running_handles;
  CURLMcode multiResult;
  bool result = true;
  int indx=0;
  char urlbuffer[256];
  long timeout;
  int fdMax;
  fd_set fdRead, fdWrite, fdException;

  struct upload updata[]= {
    {"file1", "mooo", 0},
    {"file2", "more than the 1234456789", 0},
    {"file3", "mooo123", 0},
    {"file4", "more than the 1234456789 ABC", 0},
    {NULL, NULL, 0} /* end of list */
  };

  for(i = 0; i < numChannels; i++ ) {
    CURL* easy = curl_easy_init();

    snprintf(urlbuffer, sizeof(urlbuffer), "%s/%s", URL, updata[indx].path);

    printf("WE USE URL(%d) %s\n", indx, urlbuffer);

    curl_easy_setopt( easy, CURLOPT_UPLOAD, 1 );
    curl_easy_setopt( easy, CURLOPT_NOSIGNAL, 1 );
    curl_easy_setopt( easy, CURLOPT_NOPROGRESS, 1);
    curl_easy_setopt( easy, CURLOPT_VERBOSE, 1 );
    curl_easy_setopt( easy, CURLOPT_FTP_USE_EPRT, 0  /* use PORT instead */ );
    curl_easy_setopt( easy, CURLOPT_FTP_USE_EPSV, 0  /* use PASV instead */ );
    curl_easy_setopt( easy, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_MULTICWD );
    curl_easy_setopt( easy, CURLOPT_FTP_CREATE_MISSING_DIRS , 1 );
    curl_easy_setopt( easy, CURLOPT_SSH_AUTH_TYPES , CURLSSH_AUTH_PASSWORD );
    curl_easy_setopt( easy, CURLOPT_USERPWD , userPassword);
    curl_easy_setopt( easy, CURLOPT_FTPPORT , activeFtp ? "-" : 0 );
    curl_easy_setopt( easy, CURLOPT_URL , urlbuffer );
    curl_easy_setopt( easy, CURLOPT_READFUNCTION , CurlReadCallback );
    curl_easy_setopt( easy, CURLOPT_READDATA , &updata[indx] );
    curl_multi_add_handle( curlMulti, easy );

    indx++;

    curlEasy[ i ] = easy;
  }

  while( result ) {
    int num_messages;
    CURLMsg* curlMsg;

    /* call curl_multi_perform, as long as it wants to be called: */
    while( ( multiResult = curl_multi_perform( curlMulti, &running_handles ) )
           == CURLM_CALL_MULTI_PERFORM );

    /* now process waiting easy handle messages: */
    curlMsg = curl_multi_info_read( curlMulti, &num_messages );
    while( curlMsg ) {
      if( curlMsg->msg == CURLMSG_DONE )
      {
        CURL* easy = curlMsg->easy_handle;
        CURLcode easyResult = curlMsg->data.result;
        curl_multi_remove_handle( curlMulti, easy );
        if( ( easyResult != CURLE_URL_MALFORMAT ) && ( easyResult != CURLE_OK ) )
        {
          /* serious error occured. */
          result = false;
          break;
        }

        /* prepare and resurrect easy handle for next transfer: */
        if( updata[indx].path )
        {
          snprintf(urlbuffer, sizeof(urlbuffer), "%s/%s", URL,
                   updata[indx].path);

          printf("WE USE URL(%d) %s\n", indx, urlbuffer);

          curl_easy_setopt( easy, CURLOPT_URL , urlbuffer );
          curl_easy_setopt( easy, CURLOPT_READFUNCTION, CurlReadCallback);
          curl_easy_setopt( easy, CURLOPT_READDATA, &updata[indx]);
          curl_multi_add_handle( curlMulti, easy );
          indx++;
          running_handles++;
        }
      }
      curlMsg = curl_multi_info_read( curlMulti, &num_messages );
    }

    if( !running_handles )
      break;

    /* get curl timeout setting: */
    curl_multi_timeout( curlMulti, &timeout );
    if( timeout < 0 )
      timeout = DEFAULT_TIMEOUT_MILLISECS;
    if( timeout > MAX_TIMEOUT_MILLISECS )
      timeout = MAX_TIMEOUT_MILLISECS;

    /* check socket action: */
    FD_ZERO( &fdRead );
    FD_ZERO( &fdWrite );
    FD_ZERO( &fdException );
    curl_multi_fdset( curlMulti, &fdRead, &fdWrite, &fdException, &fdMax );
    if( fdMax != -1 ) {
      /* socket stuff to do: */
      struct timeval tv;
      int select_result;

      tv.tv_sec = timeout/1000;
      tv.tv_usec = (timeout%1000) * 1000;
      select_result = select( fdMax + 1, &fdRead, &fdWrite, &fdException, &tv );
      if( select_result < 0 )
      {
        result = false;
        break;
      }
    }
    else
    {
      usleep( DEFAULT_TIMEOUT_MILLISECS * 1000 );
    }


  }

  /* clean-up libcurl easy handles: */
  for(i = 0; i < numChannels; i++ )
  {
    curl_multi_remove_handle( curlMulti, curlEasy[ i ] );
    curl_easy_cleanup( curlEasy[ i ] );
  }

  /* clean-up libcurl multi handle: */
  curl_multi_cleanup( curlMulti );

  return( result );
}

