]> git.street.me.uk Git - andy/viking.git/blob - src/http.c
(no commit message)
[andy/viking.git] / src / http.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 #include <stdio.h>
23 #include <gtk/gtk.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29
30 #ifdef WINDOWS
31
32 #include <io.h>
33 #include <winsock.h>
34 #define access(a,b) _access(a,b)
35 #define close(a) closesocket(a)
36
37 char *dirname ( char * dir )
38 {
39   char *tmp = dir + strlen(dir) - 1;
40   while ( tmp != dir && *tmp != '\\' )
41     tmp--;
42   *tmp = '\0';
43   return dir;
44 }
45
46 #else
47
48 #include <unistd.h>
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netdb.h>
53
54 /* dirname */
55 #include <libgen.h>
56
57 #endif 
58
59 #include "http.h"
60
61 int http_connect(const char *hostname, int port)
62 {
63   int sock; 
64   struct sockaddr_in server;
65   struct hostent *host_addr;
66
67   /* create a socket of type AF_INET, and SOCK_STREAM (TCP) */
68   sock = socket(AF_INET, SOCK_STREAM, 0);
69
70   /* get an IP from a domain name -- essential */
71   host_addr = gethostbyname(hostname);
72   if (host_addr == NULL)
73     return(-1);
74
75   server.sin_family = AF_INET;
76   /* 110 is the standard POP port. Host TO Network order. */
77   server.sin_port = htons(port);
78   /* get the IP address.                                  */
79   server.sin_addr = *((struct in_addr *) host_addr->h_addr);
80   /* padding unused in sockaddr_in                        */
81 #ifndef WINDOWS
82   bzero(&(server.sin_zero), 8);
83 #endif
84
85   if ((connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr))) == -1)
86     return(-2);
87
88   return(sock);
89 }
90
91 int http_get_line(int sock, char *buf, int len)
92 {
93   static char lilbuf;
94   int size, count;
95
96   count = 1;
97   size = 1;
98   lilbuf = 'a';
99   while (size != 0 && lilbuf != '\n' && count < len)
100   {
101     size = recv(sock, &lilbuf, 1, 0);
102     if (size == 0 && count == 1 )
103       return 0;
104
105     if (size > 0)
106       *buf++ = lilbuf;
107     count++;
108   }
109   *buf = '\0';
110
111   return 1;
112 }
113
114 /* makes directory if neccessary */
115 int http_download_get_url ( const char *hostname, const char *uri, const char *fn, int already_redirected, int sendhostname )
116 {
117   static char input_buffer[1024];
118   int sock;
119   int len;
120   FILE *f, *tmp_f;
121   /* int hnlen = strlen ( hostname ); */
122
123   if ( access ( fn, F_OK ) == 0 )
124   {
125     return -3;
126   } else {
127     if ( errno == ENOENT)
128     {
129       char *tmp = g_strdup ( fn );
130 #ifdef WINDOWS
131       mkdir( dirname ( dirname ( tmp ) ) );
132       g_free ( tmp ); tmp = g_strdup ( fn );
133       mkdir( dirname ( tmp ) );
134 #else
135       mkdir( dirname ( dirname ( tmp ) ), 0777 );
136       g_free ( tmp ); tmp = g_strdup ( fn );
137       mkdir( dirname ( tmp ), 0777 );
138 #endif
139       g_free ( tmp );
140     }
141     if ( ! (f = fopen ( fn, "w+b" )) ) /* immediately open file so other threads won't -- prevents race condition */
142       return -4;
143   }
144 #ifdef WINDOWS
145   WSADATA usadata;
146   WSAStartup ( MAKEWORD(2,2), &usadata );
147 #endif
148
149   sock = http_connect ( hostname, 80 );
150   if (sock < 0)
151   {
152     fclose ( f );
153     remove ( fn );
154     return -1;
155   }
156
157
158   if ( sendhostname ) {
159     send ( sock, "GET http://", 11, 0);
160     send ( sock, hostname, strlen(hostname), 0 );
161     send ( sock, uri, strlen ( uri ), 0 );
162     send ( sock, " HTTP/1.0\r\n\r\n", 13, 0 );
163   } else {
164     send ( sock, "GET ", 4, 0 );
165     send ( sock, uri, strlen ( uri ), 0 );
166     send ( sock, "\r\n\r\n", 4, 0 );
167   }
168
169   /* next, skip through all headers EXCEPT content length.,
170      that is, if it begins with "Content-Length: " (strncasecmp),
171      atoi that line from +16 (+17 ?), read that many bytes directly 
172      into file (IF we can open it, else return error) and we're done.
173   */ 
174
175   /* "HTTP/1.x 200 OK" check */
176   if ( recv ( sock, input_buffer, 12, 0 ) < 12 || input_buffer[9] != '2' || input_buffer[10] != '0' || input_buffer[11] != '0' )
177   {
178     /* maybe it's a redirect */
179     if ( ! already_redirected )
180     do
181     {
182       if ( http_get_line ( sock, input_buffer, 1024 ) == 0 )
183         break;
184
185       /* Location: http://abc.def/bla */
186       if ( strncmp(input_buffer, "Location: ", 10) == 0 && strlen(input_buffer) > 17 )
187       {
188         char *uri_start;
189
190         int rv;
191         uri_start = strchr(input_buffer+17,'/');
192
193         if ( uri_start )
194         {
195           char *newhost = g_strndup ( input_buffer + 17, uri_start - input_buffer - 17 );
196           char *newuri = strdup ( uri_start );
197           fclose ( f );
198           remove ( fn );
199           close ( sock );
200
201           rv = http_download_get_url ( newhost, newuri, fn, 1, sendhostname );
202
203           free ( newhost );
204           free ( newuri );
205           return rv;
206         }
207       }
208     } while (input_buffer[0] != '\r' );
209
210     fclose ( f );
211     remove ( fn );
212     return 1;
213   }
214
215   do
216   {
217     if ( http_get_line ( sock, input_buffer, 1024 ) == 0 )
218     {
219       fclose ( f );
220       remove ( fn );
221       close ( sock );
222       return -2;
223     }
224   } while (input_buffer[0] != '\r' );
225
226   tmp_f = tmpfile();
227
228   do {
229     len = recv ( sock, input_buffer, 1024, 0 );
230     if ( len > 0 )
231       fwrite ( input_buffer, 1, len, tmp_f );
232   } while ( len > 0 );
233
234   rewind(tmp_f);
235
236   while ( ! feof(tmp_f) )
237   {
238     len = fread ( input_buffer, 1, 1024, tmp_f );
239     fwrite ( input_buffer, 1, len, f);
240   }
241   fclose ( tmp_f );
242   fclose ( f );
243
244   close ( sock );
245 #ifdef WINDOWS
246     WSACleanup(); /* they sure make winsock programming easy. */
247 #endif
248   return 0;
249 }
250
251 /* success = 0, -1 = couldn't connect, -2 HTTP error, -3 file exists, -4 couldn't write to file... */
252 /* uri: like "/uri.html?whatever" */
253 /* only reason for the "wrapper" is so we can do redirects. */
254 int a_http_download_get_url ( const char *hostname, const char *uri, const char *fn )
255 {
256   return http_download_get_url ( hostname, uri, fn, 0, 1 );
257 }
258
259 int a_http_download_get_url_nohostname ( const char *hostname, const char *uri, const char *fn )
260 {
261   return http_download_get_url ( hostname, uri, fn, 0, 0 );
262 }
263
264
265 int usgs_hack ( const char *scale_factor, const char *uri, const char *fn )
266 {
267   static char input_buffer[1024];
268   int sock;
269   /* int hnlen = strlen ( scale_factor ); */
270
271 #ifdef WINDOWS
272   WSADATA usadata;
273   WSAStartup ( MAKEWORD(2,2), &usadata );
274 #endif
275
276   sock = http_connect ( scale_factor, 80 );
277   if (sock < 0)
278     return -1;
279
280   send ( sock, "GET /", 5, 0);
281   send ( sock, uri, strlen ( uri ), 0 );
282   send ( sock, " HTTP/1.0\r\n\r\n", 13, 0 );
283
284   /* next, skip through all headers EXCEPT content length.,
285      that is, if it begins with "Content-Length: " (strncasecmp),
286      atoi that line from +16 (+17 ?), read that many bytes directly
287      into file (IF we can open it, else return error) and we're done.
288   */
289
290   /* "HTTP/1.x 200 OK" check */
291   for (;;) {
292     if ( http_get_line ( sock, input_buffer, 1024 ) == 0 )
293       break;
294
295     if ( strncmp(input_buffer, "\t\t\t<img src=\"/g", 15) == 0 && strlen(input_buffer) > 28 )
296     {
297       char *uri = input_buffer + 13;
298       close(sock);
299       uri[strlen(uri)-4] = '\0';
300       return a_http_download_get_url_nohostname ( scale_factor, uri, fn );
301     }
302   }
303   close ( sock );
304 #ifdef WINDOWS
305     WSACleanup(); /* they sure make winsock programming easy. */
306 #endif
307   return -10;
308 }