]> git.street.me.uk Git - andy/viking.git/blob - src/download.c
Allow reuse of curl connection objects
[andy/viking.git] / src / download.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <utime.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
35
36
37 #include "download.h"
38
39 #include "curl_download.h"
40
41 static gboolean check_file_first_line(FILE* f, gchar *patterns[])
42 {
43   gchar **s;
44   gchar *bp;
45   fpos_t pos;
46   gchar buf[33];
47   size_t nr;
48
49   memset(buf, 0, sizeof(buf));
50   fgetpos(f, &pos);
51   rewind(f);
52   nr = fread(buf, 1, sizeof(buf) - 1, f);
53   fsetpos(f, &pos);
54   for (bp = buf; (bp < (buf + sizeof(buf) - 1)) && (nr > (bp - buf)); bp++) {
55     if (!(isspace(*bp)))
56       break;
57   }
58   if ((bp >= (buf + sizeof(buf) -1)) || ((bp - buf) >= nr))
59     return FALSE;
60   for (s = patterns; *s; s++) {
61     if (strncasecmp(*s, bp, strlen(*s)) == 0)
62       return TRUE;
63   }
64   return FALSE;
65 }
66
67 gboolean a_check_html_file(FILE* f)
68 {
69   gchar * html_str[] = {
70     "<html",
71     "<!DOCTYPE html",
72     "<head",
73     "<title",
74     NULL
75   };
76
77   return check_file_first_line(f, html_str);
78 }
79
80 gboolean a_check_map_file(FILE* f)
81 {
82   /* FIXME no more true since a_check_kml_file */
83   return !a_check_html_file(f);
84 }
85
86 gboolean a_check_kml_file(FILE* f)
87 {
88   gchar * kml_str[] = {
89     "<?xml",
90     NULL
91   };
92
93   return check_file_first_line(f, kml_str);
94 }
95
96 static GList *file_list = NULL;
97 static GMutex *file_list_mutex = NULL;
98
99 void a_download_init (void)
100 {
101         file_list_mutex = g_mutex_new();
102 }
103
104 static gboolean lock_file(const char *fn)
105 {
106         gboolean locked = FALSE;
107         g_mutex_lock(file_list_mutex);
108         if (g_list_find(file_list, fn) == NULL)
109         {
110                 // The filename is not yet locked
111                 file_list = g_list_append(file_list, (gpointer)fn),
112                 locked = TRUE;
113         }
114         g_mutex_unlock(file_list_mutex);
115         return locked;
116 }
117
118 static void unlock_file(const char *fn)
119 {
120         g_mutex_lock(file_list_mutex);
121         file_list = g_list_remove(file_list, (gconstpointer)fn);
122         g_mutex_unlock(file_list_mutex);
123 }
124
125 static int download( const char *hostname, const char *uri, const char *fn, DownloadOptions *options, gboolean ftp, void *handle)
126 {
127   FILE *f;
128   int ret;
129   gchar *tmpfilename;
130   gboolean failure = FALSE;
131   time_t time_condition = 0;
132
133   /* Check file */
134   if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == TRUE )
135   {
136     if (options != NULL && options->check_file_server_time) {
137       /* Get the modified time of this file */
138       struct stat buf;
139       g_stat ( fn, &buf );
140       time_condition = buf.st_mtime;
141       if ( (time(NULL) - time_condition) < options->check_file_server_time )
142                                 /* File cache is too recent, so return */
143                                 return -3;
144     } else {
145       /* Nothing to do as file already exists, so return */
146       return -3;
147     }
148   } else {
149     gchar *dir = g_path_get_dirname ( fn );
150     g_mkdir_with_parents ( dir , 0777 );
151     g_free ( dir );
152   }
153
154   tmpfilename = g_strdup_printf("%s.tmp", fn);
155   if (!lock_file ( tmpfilename ) )
156   {
157     g_debug("%s: Couldn't take lock on temporary file \"%s\"\n", __FUNCTION__, tmpfilename);
158     g_free ( tmpfilename );
159     return -4;
160   }
161   f = g_fopen ( tmpfilename, "w+b" );  /* truncate file and open it */
162   if ( ! f ) {
163     g_warning("Couldn't open temporary file \"%s\": %s", tmpfilename, g_strerror(errno));
164     g_free ( tmpfilename );
165     return -4;
166   }
167
168   /* Call the backend function */
169   ret = curl_download_get_url ( hostname, uri, f, options, ftp, time_condition, handle );
170
171   if (ret != DOWNLOAD_NO_ERROR && ret != DOWNLOAD_NO_NEWER_FILE) {
172     g_debug("%s: download failed: curl_download_get_url=%d", __FUNCTION__, ret);
173     failure = TRUE;
174   }
175
176   if (!failure && options != NULL && options->check_file != NULL && ! options->check_file(f)) {
177     g_debug("%s: file content checking failed", __FUNCTION__);
178     failure = TRUE;
179   }
180
181   if (failure)
182   {
183     g_warning(_("Download error: %s"), fn);
184     g_remove ( tmpfilename );
185     unlock_file ( tmpfilename );
186     g_free ( tmpfilename );
187     fclose ( f );
188     f = NULL;
189     g_remove ( fn ); /* couldn't create temporary. delete 0-byte file. */
190     return -1;
191   }
192
193   if (ret == DOWNLOAD_NO_NEWER_FILE)
194     g_remove ( tmpfilename );
195   else
196     g_rename ( tmpfilename, fn ); /* move completely-downloaded file to permanent location */
197   unlock_file ( tmpfilename );
198   g_free ( tmpfilename );
199   fclose ( f );
200   f = NULL;
201   return 0;
202 }
203
204 /* success = 0, -1 = couldn't connect, -2 HTTP error, -3 file exists, -4 couldn't write to file... */
205 /* uri: like "/uri.html?whatever" */
206 /* only reason for the "wrapper" is so we can do redirects. */
207 int a_http_download_get_url ( const char *hostname, const char *uri, const char *fn, DownloadOptions *opt, void *handle )
208 {
209   return download ( hostname, uri, fn, opt, FALSE, handle );
210 }
211
212 int a_ftp_download_get_url ( const char *hostname, const char *uri, const char *fn, DownloadOptions *opt, void *handle )
213 {
214   return download ( hostname, uri, fn, opt, TRUE, handle );
215 }
216
217 void * a_download_handle_init ()
218 {
219   return curl_download_handle_init ();
220 }
221
222 void a_download_handle_cleanup ( void *handle )
223 {
224   curl_download_handle_cleanup ( handle );
225 }