wmpager: EWMH support
[dockapps.git] / wmweather+ / download.c
blob0a90a008025d2255ca76ac8869924c51c6e2e722
1 #include "config.h"
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
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <curl/curl.h>
26 #include "die.h"
27 #include "download.h"
29 static CURLM *multi_handle=NULL;
30 static int still_running=0;
31 struct download_info {
32 CURL *handle;
33 FILE *fp;
34 void (*callback)(char *filename, void *data);
35 char *filename;
36 void *data;
37 int flags;
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){
44 d->next=active_list;
45 d->prev=NULL;
46 if(active_list!=NULL) active_list->prev=d;
47 active_list=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;
54 d->next=NULL;
55 d->prev=NULL;
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;
63 return NULL;
66 static void handle_done(CURLMsg *msg){
67 struct download_info *info;
68 long status;
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);
73 if(info==NULL){
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);
77 return;
81 remove_active(info);
82 curl_multi_remove_handle(multi_handle, info->handle);
83 fclose(info->fp);
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);
90 } else {
91 warn("Download of %s failed: %s", info->filename, curl_easy_strerror(msg->data.result));
93 unlink(info->filename);
94 } else {
95 (*info->callback)(info->filename, info->data);
98 curl_easy_cleanup(info->handle);
99 free(info->filename);
100 free(info);
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){
114 fd_set rd, wr, er;
115 struct timeval tv;
116 int maxfd, n, x;
117 CURLMsg *msg;
119 if(sleeptime>0){
120 FD_ZERO(&rd);
121 FD_ZERO(&wr);
122 FD_ZERO(&er);
123 curl_multi_fdset(multi_handle, &rd, &wr, &er, &maxfd);
125 tv.tv_sec=0;
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);
133 if(n==0) return;
134 if(n<0){
135 switch(errno){
136 case EINTR:
137 case ENOMEM:
138 /* transient errors, hope it's good next time */
139 break;
141 default:
142 warn("WTF? select errno=%d", errno);
143 break;
145 usleep(sleeptime);
146 return;
149 while(curl_multi_perform(multi_handle, &x)==CURLM_CALL_MULTI_PERFORM);
150 while((msg=curl_multi_info_read(multi_handle, &x))){
151 switch(msg->msg){
152 case CURLMSG_DONE:
153 handle_done(msg);
154 break;
155 default:
156 warn("Unknown CURL message type %d", msg->msg);
157 break;
162 int download_kill(char *filename){
163 struct download_info *info;
164 info=find_active_file(filename);
165 if(info==NULL) return ENOENT;
166 remove_active(info);
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);
172 fclose(info->fp);
173 free(info);
174 return 0;
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;
179 FILE *fp;
181 if(callback==NULL || filename==NULL || from_addr==NULL) return 1;
183 if(flags&DOWNLOAD_KILL_OTHER_REQUESTS){
184 download_kill(filename);
185 } else {
186 info=find_active_file(filename);
187 if(info!=NULL){
188 errno=0;
189 warn("Cannot download %s: download already in progress", filename);
190 return 1;
194 if((info=malloc(sizeof(*info)))==NULL){
195 warn("Malloc error in download_file");
196 goto fail;
198 info->handle=NULL;
199 info->fp=NULL;
200 info->callback=callback;
201 info->filename=NULL;
202 info->data=data;
203 info->flags=flags;
204 info->next=NULL;
205 info->prev=NULL;
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);
210 goto fail;
213 info->handle=curl_easy_init();
214 if(info->handle==NULL){
215 warn("Error creating a CURL handle");
216 goto fail;
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");
229 goto fail;
231 if(postdata!=NULL){
232 if(curl_easy_setopt(info->handle, CURLOPT_COPYPOSTFIELDS, postdata)!=CURLE_OK
234 warn("Error setting CURL post options");
235 goto fail;
238 if(curl_multi_add_handle(multi_handle, info->handle)!=CURLM_OK){
239 warn("Could not add handle for %s to multihandle", info->filename);
240 goto fail;
242 add_active(info);
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...
249 download_process(0);
251 return 0;
253 fail:
254 if(info){
255 if(info->handle) curl_easy_cleanup(info->handle);
256 info->handle=NULL;
257 if(info->fp) fclose(info->fp);
258 info->fp=NULL;
259 if(info->filename){
260 unlink(info->filename);
261 free(info->filename);
263 info->filename=NULL;
264 free(info);
266 return 1;