3 /* Copyright (C) 2002 Brad Jorsch <anomie@users.sourceforge.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include <curl/curl.h>
29 static CURLM
*multi_handle
=NULL
;
30 static int still_running
=0;
31 struct download_info
{
34 void (*callback
)(char *filename
, void *data
);
38 struct download_info
*next
;
39 struct download_info
*prev
;
41 static struct download_info
*active_list
=NULL
;
43 static void add_active(struct download_info
*d
){
46 if(active_list
!=NULL
) active_list
->prev
=d
;
50 static void remove_active(struct download_info
*d
){
51 if(active_list
==d
) active_list
=d
->next
;
52 if(d
->prev
!=NULL
) d
->prev
->next
=d
->next
;
53 if(d
->next
!=NULL
) d
->next
->prev
=d
->prev
;
58 static struct download_info
*find_active_file(char *f
){
59 struct download_info
*d
;
60 for(d
=active_list
; d
!=NULL
; d
=d
->next
){
61 if(!strcmp(d
->filename
,f
)) return d
;
66 static void handle_done(CURLMsg
*msg
){
67 struct download_info
*info
;
70 if(curl_easy_getinfo(msg
->easy_handle
, CURLINFO_PRIVATE
, &info
)!=CURLE_OK
|| info
==NULL
){
71 warn("Could not retrieve info handle from CURL handle. WTF?");
72 for(info
=active_list
; info
&& info
->handle
!=msg
->easy_handle
; info
=info
->next
);
74 warn("Could not find it in the active list either. WTF?");
75 curl_multi_remove_handle(multi_handle
, msg
->easy_handle
);
76 curl_easy_cleanup(msg
->easy_handle
);
82 curl_multi_remove_handle(multi_handle
, info
->handle
);
85 if(msg
->data
.result
!=CURLE_OK
){
86 if(msg
->data
.result
==CURLE_HTTP_RETURNED_ERROR
){
87 if(curl_easy_getinfo(info
->handle
, CURLINFO_RESPONSE_CODE
, &status
)!=CURLE_OK
) status
=600;
88 if(status
!=404 || !(info
->flags
&DOWNLOAD_NO_404
))
89 warn("HTTP download of %s returned %d", info
->filename
, status
);
91 warn("Download of %s failed: %s", info
->filename
, curl_easy_strerror(msg
->data
.result
));
93 unlink(info
->filename
);
95 (*info
->callback
)(info
->filename
, info
->data
);
98 curl_easy_cleanup(info
->handle
);
104 void download_init(char *email
){
105 if(multi_handle
==NULL
){
106 if(curl_global_init(CURL_GLOBAL_ALL
))
107 die("Could not initialize CURL");
108 multi_handle
=curl_multi_init();
109 if(multi_handle
==NULL
) die("Could not create a CURL multihandle");
113 void download_process(unsigned long sleeptime
){
123 curl_multi_fdset(multi_handle
, &rd
, &wr
, &er
, &maxfd
);
126 tv
.tv_usec
=sleeptime
;
127 if(sleeptime
>=1000000){
128 tv
.tv_sec
=sleeptime
/1000000;
129 tv
.tv_usec
=sleeptime
%1000000;
132 n
=select(maxfd
+1, &rd
, &wr
, &er
, &tv
);
138 /* transient errors, hope it's good next time */
142 warn("WTF? select errno=%d", errno
);
149 while(curl_multi_perform(multi_handle
, &x
)==CURLM_CALL_MULTI_PERFORM
);
150 while((msg
=curl_multi_info_read(multi_handle
, &x
))){
156 warn("Unknown CURL message type %d", msg
->msg
);
162 int download_kill(char *filename
){
163 struct download_info
*info
;
164 info
=find_active_file(filename
);
165 if(info
==NULL
) return ENOENT
;
167 curl_multi_remove_handle(multi_handle
, info
->handle
);
168 warn("Download of %s interrupted", info
->filename
);
169 unlink(info
->filename
);
170 curl_easy_cleanup(info
->handle
);
171 free(info
->filename
);
177 int download_file(char *filename
, char *from_addr
, char *postdata
, int flags
, void (*callback
)(char *filename
, void *data
), void *data
){
178 struct download_info
*info
=NULL
;
181 if(callback
==NULL
|| filename
==NULL
|| from_addr
==NULL
) return 1;
183 if(flags
&DOWNLOAD_KILL_OTHER_REQUESTS
){
184 download_kill(filename
);
186 info
=find_active_file(filename
);
189 warn("Cannot download %s: download already in progress", filename
);
194 if((info
=malloc(sizeof(*info
)))==NULL
){
195 warn("Malloc error in download_file");
200 info
->callback
=callback
;
207 if((info
->filename
=strdup(filename
))==NULL
) goto fail
;
208 if((info
->fp
=fopen(info
->filename
, "wb"))==NULL
){
209 warn("Error opening %s for output", info
->filename
);
213 info
->handle
=curl_easy_init();
214 if(info
->handle
==NULL
){
215 warn("Error creating a CURL handle");
218 if(curl_easy_setopt(info
->handle
, CURLOPT_URL
, from_addr
)!=CURLE_OK
||
219 curl_easy_setopt(info
->handle
, CURLOPT_NOPROGRESS
, 1)!=CURLE_OK
||
220 curl_easy_setopt(info
->handle
, CURLOPT_NOSIGNAL
, 1)!=CURLE_OK
||
221 curl_easy_setopt(info
->handle
, CURLOPT_WRITEDATA
, info
->fp
)!=CURLE_OK
||
222 curl_easy_setopt(info
->handle
, CURLOPT_FAILONERROR
, 1)!=CURLE_OK
||
223 curl_easy_setopt(info
->handle
, CURLOPT_AUTOREFERER
, 1)!=CURLE_OK
||
224 curl_easy_setopt(info
->handle
, CURLOPT_FOLLOWLOCATION
, 1)!=CURLE_OK
||
225 curl_easy_setopt(info
->handle
, CURLOPT_TIMEOUT
, 10*60)!=CURLE_OK
||
226 curl_easy_setopt(info
->handle
, CURLOPT_PRIVATE
, info
)!=CURLE_OK
228 warn("Error setting CURL options");
232 if(curl_easy_setopt(info
->handle
, CURLOPT_COPYPOSTFIELDS
, postdata
)!=CURLE_OK
234 warn("Error setting CURL post options");
238 if(curl_multi_add_handle(multi_handle
, info
->handle
)!=CURLM_OK
){
239 warn("Could not add handle for %s to multihandle", info
->filename
);
244 /* Call download_process with 0 to force at least one call to
245 * curl_multi_process, because curl won't actually create a socket until
246 * that function is called and download_process won't otherwise call
247 * curl_multi_process until the socket is created...
255 if(info
->handle
) curl_easy_cleanup(info
->handle
);
257 if(info
->fp
) fclose(info
->fp
);
260 unlink(info
->filename
);
261 free(info
->filename
);