Tizen 2.0 Release
[profile/ivi/ecore.git] / src / lib / ecore_file / ecore_file_download.c
index e0472f0..03e593b 100644 (file)
-/*
- * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
- */
-
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
 
-#include "Ecore_Con.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef BUILD_ECORE_CON
+# include "Ecore_Con.h"
+#endif
+
 #include "ecore_file_private.h"
 
-#define ECORE_MAGIC_FILE_DOWNLOAD_JOB  0xf7427cb8
+#ifdef BUILD_ECORE_CON
+
+#define ECORE_MAGIC_FILE_DOWNLOAD_JOB 0xf7427cb8
+#define ECORE_FILE_DOWNLOAD_TIMEOUT 30
 
-typedef struct _Ecore_File_Download_Job                Ecore_File_Download_Job;
 struct _Ecore_File_Download_Job
 {
    ECORE_MAGIC;
 
-   Ecore_Con_Url       *url_con;
-   FILE                        *file;
+   Ecore_Con_Url        *url_con;
+   FILE                 *file;
 
-   char                        *dst;
+   char                 *dst;
 
-   void        (*completion_cb)(void *data, const char *file, int status);
-
-   int (*progress_cb)  (void *data, const char *file,
-                        long int dltotal, long int dlnow,
-                        long int ultotal, long int ulnow);
+   Ecore_File_Download_Completion_Cb completion_cb;
+   Ecore_File_Download_Progress_Cb progress_cb;
 };
 
 #ifdef HAVE_CURL
 Ecore_File_Download_Job *_ecore_file_download_curl(const char *url, const char *dst,
-                                                  void (*completion_cb)(void *data, const char *file, int status),
-                                                  int (*progress_cb)(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow),
-                                                  void *data);
+                                                   Ecore_File_Download_Completion_Cb completion_cb,
+                                                   Ecore_File_Download_Progress_Cb progress_cb,
+                                                   void *data,
+                                                   Eina_Hash *headers);
 
-static int _ecore_file_download_url_complete_cb(void *data, int type, void *event);
-static int _ecore_file_download_url_progress_cb(void *data, int type, void *event);
+static Eina_Bool _ecore_file_download_url_complete_cb(void *data, int type, void *event);
+static Eina_Bool _ecore_file_download_url_progress_cb(void *data, int type, void *event);
 #endif
-static void _ecore_file_download_abort(Ecore_File_Download_Job *job);
 
-static int                      init = 0;
-static Ecore_Event_Handler     *_url_complete_handler = NULL;
-static Ecore_Event_Handler     *_url_progress_download = NULL;
-static Ecore_List              *_job_list;
+static Ecore_Event_Handler *_url_complete_handler = NULL;
+static Ecore_Event_Handler *_url_progress_download = NULL;
+static Eina_List           *_job_list;
+
+static int download_init = 0;
+
+#endif /* BUILD_ECORE_CON */
 
-EAPI int
+int
 ecore_file_download_init(void)
 {
-#ifndef _WIN32
-   ecore_con_url_init();
-
-   if (init++ == 0)
-     {
-#ifdef HAVE_CURL
-       _url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _ecore_file_download_url_complete_cb, NULL);
-       _url_progress_download = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _ecore_file_download_url_progress_cb, NULL);
-#endif
-     }
-   if (!_job_list)
+#ifdef BUILD_ECORE_CON
+   download_init++;
+   if (download_init > 1) return 1;
+   if (!ecore_con_init()) return 0;
+   if (!ecore_con_url_init())
      {
-       _job_list = ecore_list_new();
-       if (!_job_list) return 0;
+        ecore_con_shutdown();
+        return 0;
      }
-
+# ifdef HAVE_CURL
+   _url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _ecore_file_download_url_complete_cb, NULL);
+   _url_progress_download = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _ecore_file_download_url_progress_cb, NULL);
+# endif
+#endif /* BUILD_ECORE_CON */
    return 1;
-#else
-   return 0;
-#endif
 }
 
-EAPI int
+void
 ecore_file_download_shutdown(void)
 {
-#ifndef _WIN32
-   if (--init == 0)
-     {
-       if (_url_complete_handler)
-         ecore_event_handler_del(_url_complete_handler);
-       if (_url_progress_download)
-         ecore_event_handler_del(_url_progress_download);
-       _url_complete_handler = NULL;
-       _url_progress_download = NULL;
-       if (_job_list)
-         ecore_list_destroy(_job_list);
-       _job_list = NULL;
-     }
-
-   return ecore_con_url_shutdown();
-#else
-   return 0;
-#endif
+#ifdef BUILD_ECORE_CON
+   download_init--;
+   if (download_init > 0) return;
+   if (_url_complete_handler)
+     ecore_event_handler_del(_url_complete_handler);
+   if (_url_progress_download)
+     ecore_event_handler_del(_url_progress_download);
+   _url_complete_handler = NULL;
+   _url_progress_download = NULL;
+   ecore_file_download_abort_all();
+   ecore_con_url_shutdown();
+   ecore_con_shutdown();
+#endif /* BUILD_ECORE_CON */
 }
 
-EAPI void
-ecore_file_download_abort_all(void)
+#ifdef BUILD_ECORE_CON
+# ifdef HAVE_CURL
+static Eina_Bool
+_ecore_file_download_headers_foreach_cb(const Eina_Hash *hash __UNUSED__, const void *key, void *data, void *fdata)
 {
-   if (!ecore_list_empty_is(_job_list))
-     {
-       Ecore_File_Download_Job *job;
+   Ecore_File_Download_Job *job = fdata;
+   ecore_con_url_additional_header_add(job->url_con, key, data);
 
-       while ((job = ecore_list_first_remove(_job_list)))
-         {
-            _ecore_file_download_abort(job);
-         }
-     }
-   ecore_list_clear(_job_list);
+   return EINA_TRUE;
 }
+# endif
+#endif
 
-/**
- * Download @p url to the given @p dst
- * @param  url The complete url to download
- * @param  dst The local file to save the downloaded to
- * @param  completion_cb A callback called on download complete
- * @param  progress_cb A callback called during the download operation
- * @return 1 if the download start or 0 on failure
- *
- * You must provide the full url, including 'http://', 'ftp://' or 'file://'.\n
- * If @p dst already exist it will not be overwritten and the function will fail.\n
- * Ecore must be compiled with CURL to download using http and ftp protocols.
- */
-EAPI int
-ecore_file_download(const char *url, const char *dst,
-                   void (*completion_cb)(void *data, const char *file, int status),
-                   int (*progress_cb)(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow),
-                   void *data)
+static Eina_Bool
+_ecore_file_download(const char *url,
+                     const char *dst,
+                     Ecore_File_Download_Completion_Cb completion_cb,
+                     Ecore_File_Download_Progress_Cb progress_cb,
+                     void *data,
+                     Ecore_File_Download_Job **job_ret,
+                     Eina_Hash *headers)
 {
+#ifdef BUILD_ECORE_CON
+   if (!url)
+     {
+        CRIT("Download URL is null");
+        return EINA_FALSE;
+     }
+
    char *dir = ecore_file_dir_get(dst);
 
    if (!ecore_file_is_dir(dir))
      {
-       free(dir);
-       return 0;
+        ERR("%s is not a directory", dir);
+        free(dir);
+        return EINA_FALSE;
      }
    free(dir);
-   if (ecore_file_exists(dst)) return 0;
+   if (ecore_file_exists(dst))
+     {
+        WRN("%s already exists", dst);
+        return EINA_FALSE;
+     }
 
-   /* FIXME: Add handlers for http and ftp! */
    if (!strncmp(url, "file://", 7))
      {
-       /* FIXME: Maybe fork? Might take a while to copy.
-        * Check filesize? */
-       /* Just copy it */
-
-       url += 7;
-       /* skip hostname */
-       url = strchr(url, '/');
-       return ecore_file_cp(url, dst);
+        /* FIXME: Maybe fork? Might take a while to copy.
+         * Check filesize? */
+        /* Just copy it */
+
+        url += 7;
+        /* skip hostname */
+        url = strchr(url, '/');
+        return ecore_file_cp(url, dst);
      }
-#ifdef HAVE_CURL
-   else if ((!strncmp(url, "http://", 7)) ||
-           (!strncmp(url, "ftp://", 6)))
+# ifdef HAVE_CURL
+   else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
+            (!strncmp(url, "ftp://", 6)))
      {
-       /* download */
-       Ecore_File_Download_Job *job;
-
-       job = _ecore_file_download_curl(url, dst, completion_cb, progress_cb, data);
-       if (job)
-         return 1;
-       else
-         return 0;
+        /* download */
+        Ecore_File_Download_Job *job;
+
+       job = _ecore_file_download_curl(url, dst, completion_cb, progress_cb, data, headers);
+        if(job_ret) *job_ret = job;
+        if(job)
+          return EINA_TRUE;
+        else
+          {
+             ERR("no job returned\n");
+             return EINA_FALSE;
+          }
+        return job ? EINA_TRUE : EINA_FALSE;
      }
-#endif
+# else
+   else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
+            (!strncmp(url, "ftp://", 6)))
+     {
+        (void)completion_cb;
+        (void)progress_cb;
+        (void)data;
+        (void)job_ret;
+        (void)headers;
+        return EINA_FALSE;
+     }
+# endif
    else
      {
-       return 0;
+        return EINA_FALSE;
      }
-#ifndef HAVE_CURL
-   completion_cb = NULL;
-   progress_cb = NULL;
-   data = NULL;
-#endif
+#else
+   (void)url;
+   (void)dst;
+   (void)completion_cb;
+   (void)progress_cb;
+   (void)data;
+   (void)job_ret;
+   (void)headers;
+   return EINA_FALSE;
+#endif /* BUILD_ECORE_CON */
+}
+
+/**
+ * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
+ *
+ * @{
+ */
+
+/**
+ * @brief Download the given url to the given destination.
+ *
+ * @param  url The complete url to download.
+ * @param  dst The local file to save the downloaded to.
+ * @param  completion_cb A callback called on download complete.
+ * @param  progress_cb A callback called during the download operation.
+ * @param  data User data passed to both callbacks.
+ * @param  job_ret Job used to abort the download.
+ * @return @c EINA_TRUE if the download start or @c EINA_FALSE on failure.
+ *
+ * This function starts the download of the URL @p url and saves it to
+ * @p dst. @p url must provide the protocol, including 'http://',
+ * 'ftp://' or 'file://'. Ecore_File must be compiled with CURL to
+ * download using http and ftp protocols. If @p dst is ill-formed, or
+ * if it already exists, the function returns @c EINA_FALSE. When the
+ * download is complete, the callback @p completion_cb is called and
+ * @p data is passed to it. The @p status parameter of @p completion_cb
+ * will be filled with the status of the download (200, 404,...). The
+ * @p progress_cb is called during the download operation, each time a
+ * packet is received or when CURL wants. It can be used to display the
+ * percentage of the downloaded file. Return 0 from this callback, if provided,
+ * to continue the operation or anything else to abort the download. The only
+ * operations that can be aborted are those with protocol 'http' or 'ftp'. In
+ * that case @p job_ret can be filled. It can be used with
+ * ecore_file_download_abort() or ecore_file_download_abort_all() to
+ * respectively abort one or all download operations. This function returns
+ * @c EINA_TRUE if the download starts, @c EINA_FALSE otherwise.
+ */
+EAPI Eina_Bool
+ecore_file_download(const char *url,
+                    const char *dst,
+                    Ecore_File_Download_Completion_Cb completion_cb,
+                    Ecore_File_Download_Progress_Cb progress_cb,
+                    void *data,
+                    Ecore_File_Download_Job **job_ret)
+{
+   return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, NULL);
+}
+
+/**
+ * @brief Download the given url to the given destination with additional headers.
+ *
+ * @param  url The complete url to download.
+ * @param  dst The local file to save the downloaded to.
+ * @param  completion_cb A callback called on download complete.
+ * @param  progress_cb A callback called during the download operation.
+ * @param  data User data passed to both callbacks.
+ * @param  job_ret Job used to abort the download.
+ * @param  headers pointer of header lists.
+ * @return @c EINA_TRUE if the download start or @c EINA_FALSE on failure.
+ */
+EAPI Eina_Bool
+ecore_file_download_full(const char *url,
+                         const char *dst,
+                         Ecore_File_Download_Completion_Cb completion_cb,
+                         Ecore_File_Download_Progress_Cb progress_cb,
+                         void *data,
+                         Ecore_File_Download_Job **job_ret,
+                         Eina_Hash *headers)
+{
+   return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, headers);
 }
 
 /**
- * Check if the given protocol is available
- * @param  protocol The protocol to check
- * @return 1 if protocol is handled or 0 if not
+ * @brief Check if the given protocol is available.
+ *
+ * @param  protocol The protocol to check.
+ * @return @c EINA_TRUE if protocol is handled, @c EINA_FALSE otherwise.
  *
- * @p protocol can be 'http://', 'ftp://' or 'file://'.\n
- * Ecore must be compiled with CURL to handle http and ftp protocols.
+ * This function returns @c EINA_TRUE if @p protocol is supported,
+ * @c EINA_FALSE otherwise. @p protocol can be 'http://', 'ftp://' or
+ * 'file://'. Ecore_FILE must be compiled with CURL to handle http and
+ * ftp protocols.
  */
-EAPI int
+EAPI Eina_Bool
 ecore_file_download_protocol_available(const char *protocol)
 {
-   if (!strncmp(protocol, "file://", 7)) return 1;
-#ifdef HAVE_CURL
-   else if (!strncmp(protocol, "http://", 7)) return 1;
-   else if (!strncmp(protocol, "ftp://", 6)) return 1;
-#endif
+#ifdef BUILD_ECORE_CON
+   if (!strncmp(protocol, "file://", 7)) return EINA_TRUE;
+# ifdef HAVE_CURL
+   else if (!strncmp(protocol, "http://", 7)) return EINA_TRUE;
+   else if (!strncmp(protocol, "ftp://", 6)) return EINA_TRUE;
+# endif
+#else
+   (void)protocol;
+#endif /* BUILD_ECORE_CON */
 
-   return 0;
+   return EINA_FALSE;
 }
 
-#ifdef HAVE_CURL
+#ifdef BUILD_ECORE_CON
+
+# ifdef HAVE_CURL
 static int
 _ecore_file_download_url_compare_job(const void *data1, const void *data2)
 {
-   const Ecore_File_Download_Job       *job = data1;
-   const Ecore_Con_Url                 *url = data2;
+   const Ecore_File_Download_Job *job = data1;
+   const Ecore_Con_Url           *url = data2;
 
    if (job->url_con == url) return 0;
    return -1;
 }
 
-static int
-_ecore_file_download_url_complete_cb(void *data, int type, void *event)
+static Eina_Bool
+_ecore_file_download_url_complete_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
 {
-   Ecore_Con_Event_Url_Complete        *ev = event;
-   Ecore_File_Download_Job     *job;
+   Ecore_Con_Event_Url_Complete *ev = event;
+   Ecore_File_Download_Job      *job;
 
-   job = ecore_list_find(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
-   if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return 1;
-
-   ecore_list_remove(_job_list);
+   job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
+   if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
 
+   fclose(job->file);
    if (job->completion_cb)
-     job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, !ev->status);
+     job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, ev->status);
 
-   _ecore_file_download_abort(job);
+   _job_list = eina_list_remove(_job_list, job);
+   free(job->dst);
+   ecore_con_url_free(job->url_con);
+   free(job);
 
-   return 0;
+   return ECORE_CALLBACK_DONE;
 }
 
-static int
-_ecore_file_download_url_progress_cb(void *data, int type, void *event)
+static Eina_Bool
+_ecore_file_download_url_progress_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
 {
 /* this reports the downloads progress. if we return 0, then download
  * continues, if we return anything else, then the download stops */
-   Ecore_Con_Event_Url_Progress        *ev = event;
-   Ecore_File_Download_Job     *job;
+   Ecore_Con_Event_Url_Progress *ev = event;
+   Ecore_File_Download_Job      *job;
 
-   job = ecore_list_find(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
-   if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return 1;
+   job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
+   if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
 
    if (job->progress_cb)
      if (job->progress_cb(ecore_con_url_data_get(job->url_con), job->dst,
-                         (long int) ev->down.total, (long int) ev->down.now,
-                         (long int) ev->up.total, (long int) ev->up.now) != 0)
+                          (long int) ev->down.total, (long int) ev->down.now,
+                          (long int) ev->up.total, (long int) ev->up.now) != 0)
        {
-         ecore_list_remove(_job_list);
-         _ecore_file_download_abort(job);
+          _job_list = eina_list_remove(_job_list, job);
+          fclose(job->file);
+          free(job->dst);
+          free(job);
+
+          return ECORE_CALLBACK_PASS_ON;
        }
 
-   return 0;
+   return ECORE_CALLBACK_DONE;
 }
 
 Ecore_File_Download_Job *
 _ecore_file_download_curl(const char *url, const char *dst,
-                         void (*completion_cb)(void *data, const char *file,
-                                               int status),
-                         int (*progress_cb)(void *data, const char *file,
-                                            long int dltotal, long int dlnow,
-                                            long int ultotal, long int ulnow),
-                         void *data)
+                          Ecore_File_Download_Completion_Cb completion_cb,
+                          Ecore_File_Download_Progress_Cb progress_cb,
+                          void *data,
+                          Eina_Hash *headers)
 {
    Ecore_File_Download_Job *job;
 
@@ -264,17 +363,18 @@ _ecore_file_download_curl(const char *url, const char *dst,
    job->file = fopen(dst, "wb");
    if (!job->file)
      {
-       free(job);
-       return NULL;
+        free(job);
+        return NULL;
      }
    job->url_con = ecore_con_url_new(url);
    if (!job->url_con)
      {
-       fclose(job->file);
-       free(job);
-       return NULL;
+        fclose(job->file);
+        free(job);
+        return NULL;
      }
 
+   if (headers) eina_hash_foreach(headers, _ecore_file_download_headers_foreach_cb, job);
    ecore_con_url_fd_set(job->url_con, fileno(job->file));
    ecore_con_url_data_set(job->url_con, data);
 
@@ -282,21 +382,74 @@ _ecore_file_download_curl(const char *url, const char *dst,
 
    job->completion_cb = completion_cb;
    job->progress_cb = progress_cb;
-   ecore_list_append(_job_list, job);
+   _job_list = eina_list_append(_job_list, job);
 
-   ecore_con_url_send(job->url_con, NULL, 0, NULL);
+   if (!ecore_con_url_get(job->url_con))
+     {
+        ecore_con_url_free(job->url_con);
+        _job_list = eina_list_remove(_job_list, job);
+        fclose(job->file);
+        ecore_file_remove(job->dst);
+        free(job->dst);
+        free(job);
+        return NULL;
+     }
 
    return job;
 }
+# endif
 #endif
 
-static void
-_ecore_file_download_abort(Ecore_File_Download_Job *job)
+/**
+ * @brief Abort the given download job and call the completion_cb
+ * callbck with a status of 1 (error).
+ *
+ * @param job The download job to abort.
+ *
+ * This function aborts a download operation started by
+ * ecore_file_download(). @p job is the #Ecore_File_Download_Job
+ * structure filled by ecore_file_download(). If it is @c NULL, this
+ * function does nothing. To abort all the currently downloading
+ * operations, call ecore_file_download_abort_all().
+ */
+EAPI void
+ecore_file_download_abort(Ecore_File_Download_Job *job)
 {
-#ifdef HAVE_CURL
-   ecore_con_url_destroy(job->url_con);
-#endif  
+   if (!job)
+     return;
+
+#ifdef BUILD_ECORE_CON
+   if (job->completion_cb)
+     job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, 1);
+# ifdef HAVE_CURL
+   ecore_con_url_free(job->url_con);
+# endif
+   _job_list = eina_list_remove(_job_list, job);
    fclose(job->file);
    free(job->dst);
    free(job);
+#endif /* BUILD_ECORE_CON */
 }
+
+/**
+ * @brief Abort all downloads.
+ *
+ * This function aborts all the downloads that have been started by
+ * ecore_file_download(). It loops over the started downloads and call
+ * ecore_file_download_abort() for each of them. To abort only one
+ * specific download operation, call ecore_file_download_abort().
+ */
+EAPI void
+ecore_file_download_abort_all(void)
+{
+#ifdef BUILD_ECORE_CON
+   Ecore_File_Download_Job *job;
+
+   EINA_LIST_FREE(_job_list, job)
+             ecore_file_download_abort(job);
+#endif /* BUILD_ECORE_CON */
+}
+
+/**
+ * @}
+ */