34108407722b97978726c9d3f5913df05603df2a
[tpg/acess2.git] / Usermode / Applications / wget_src / main.c
1 /*
2  * Acess2 Command-line HTTP Client (wget)
3  * - By John Hodge
4  *
5  * main.c
6  */
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <net.h>
10 #include <uri.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <netdb.h>
14
15 enum eProcols
16 {
17         PROTO_NONE,
18         PROTO_HTTP,
19         PROTO_HTTPS
20 };
21
22 const char      **gasURLs;
23  int    giNumURLs;
24
25  int    main(int argc, char *argv[]);
26  int    _ParseHeaderLine(char *Line, int State, size_t *Size);
27 void    writef(int fd, const char *format, ...);
28
29 int main(int argc, char *argv[])
30 {
31          int    proto, rv;
32         gasURLs = malloc( (argc - 1) * sizeof(*gasURLs) );
33         
34         // Parse arguments
35         for(int i = 1; i < argc; i ++ )
36         {
37                 char    *arg = argv[i];
38                 if( arg[0] != '-' ) {
39                         // URL
40                         gasURLs[giNumURLs++] = arg;
41                 }
42                 else if( arg[1] != '-') {
43                         // Short arg
44                 }
45                 else {
46                         // Long arg
47                 }
48         }
49
50         // Do the download
51         for( int i = 0; i < giNumURLs; i ++ )
52         {
53                 char    *outfile = NULL;
54                 tURI    *uri = URI_Parse(gasURLs[i]);
55                 struct addrinfo *addrinfo;
56
57                 if( !uri ) {
58                         fprintf(stderr, "'%s' is not a valid URL", gasURLs[i]);
59                         continue ;
60                 }               
61
62                 printf("Proto: %s, Host: %s, Path: %s\n", uri->Proto, uri->Host, uri->Path);
63
64                 if( uri->Path[0] == '\0' || uri->Path[strlen(uri->Path)-1] == '/' )
65                         outfile = "index.html";
66                 else {
67                         outfile = strrchr(uri->Path, '/');
68                         if( !outfile )
69                                 outfile = uri->Path;
70                         else
71                                 outfile += 1;
72                 }
73
74                 if( strcmp(uri->Proto, "http") == 0 ) {
75                         proto = PROTO_HTTP;
76                 }
77                 else if( strcmp(uri->Proto, "https") == 0 ) {
78                         proto = PROTO_HTTPS;
79                 }
80                 else {
81                         // Unknown
82                         fprintf(stderr, "Unknown protocol '%s'\n", uri->Proto);
83                         free(uri);
84                         continue ;
85                 }
86
87                 if( proto != PROTO_HTTP ) {
88                         fprintf(stderr, "TODO: Support protocols other than HTTP\n");
89                         free(uri);
90                         continue ;
91                 }
92
93                 rv = getaddrinfo(uri->Host, "http", NULL, &addrinfo);
94                 if( rv != 0 ) {
95                         fprintf(stderr, "Unable to resolve %s: %s\n", uri->Host, gai_strerror(rv));
96                         continue ;
97                 }
98
99                 for( struct addrinfo *addr = addrinfo; addr != NULL; addr = addr->ai_next )
100                 {
101                          int    bSkipLine = 0;
102                         // TODO: Convert to POSIX/BSD
103                         // NOTE: using addr->ai_addr will break for IPv6, as there is more info before the address
104                          int    sock;
105                         
106                         printf("Attempting [%s]:80\n", Net_PrintAddress(addr->ai_family, addr->ai_addr->sa_data));
107                         
108                         sock = Net_OpenSocket_TCPC(addr->ai_family, addr->ai_addr->sa_data, 80);
109                         if( sock == -1 ) {
110                                 continue ;
111                         }
112
113                         _SysDebug("Connected as %i", sock);
114                         
115                         writef(sock, "GET /%s HTTP/1.1\r\n", uri->Path);
116                         writef(sock, "Host: %s\r\n", uri->Host);
117 //                      writef(sock, "Accept-Encodings: */*\r\n");
118                         writef(sock, "User-Agent: awget/0.1 (Acess2)\r\n");
119                         writef(sock, "\r\n");
120                         
121                         // Parse headers
122                         char    inbuf[BUFSIZ+1];
123                         size_t  offset = 0, len = 0;
124                          int    state = 0;
125                         size_t  bytes_seen = 0;
126                         size_t  bytes_wanted = -1;      // invalid
127                         
128
129                         inbuf[0] = '\0';
130                         while( state == 0 || state == 1 )
131                         {
132                                 if( offset == BUFSIZ ) {
133                                         bSkipLine = 1;
134                                         offset = 0;
135                                 }
136                                 inbuf[len] = '\0';
137                         
138                                 char    *eol = strchr(inbuf, '\n');
139                                 // No end of line char? read some more
140                                 if( eol == NULL ) {
141                                         // TODO: Handle -1 return
142                                         len += read(sock, inbuf + offset, BUFSIZ - 1 - offset);
143                                         continue ;
144                                 }
145
146                                 // abuse offset as the end of the string        
147                                 offset = (eol - inbuf) + 1;
148
149                                 // Clear EOL bytes
150                                 *eol = '\0';
151                                 // Nuke the \r
152                                 if( eol - 1 >= inbuf )
153                                         eol[-1] = '\0';
154                                 
155                                 if( !bSkipLine )
156                                         state = _ParseHeaderLine(inbuf, state, &bytes_wanted);
157                 
158                                 // Move unused data down in memory      
159                                 len -= offset;
160                                 memmove( inbuf, inbuf + offset, BUFSIZ - offset );
161                                 offset = len;
162                         }
163
164                         if( state == 2 )
165                         {
166                                 _SysDebug("RXing %i bytes to '%s'", bytes_wanted, outfile);
167                                  int    outfd = open(outfile, O_WR|O_CREAT, 0666);
168                                 if( outfd == -1 ) {
169                                         fprintf(stderr, "Unable to open '%s' for writing\n", outfile);
170                                 }
171                                 else
172                                 {
173                                         // Write the remainder of the buffer
174                                         do
175                                         {
176                                                 write(outfd, inbuf, len);
177                                                 bytes_seen += len;
178                                                 _SysDebug("%i/%i bytes done", bytes_seen, bytes_wanted);
179                                         } while( bytes_seen < bytes_wanted && (len = read(sock, inbuf, sizeof(inbuf))) > 0 );
180                                         close(outfd);
181                                 }
182                         }
183                         
184                         _SysDebug("Closing socket");
185                         close(sock);
186                         break ;
187                 }
188
189                 free(uri);
190         }
191         
192         return 0;
193 }
194
195 int _ParseHeaderLine(char *Line, int State, size_t *Size)
196 {
197         _SysDebug("Header - %s", Line);
198         // First line (Status and version)
199         if( State == 0 )
200         {
201                 // HACK - assumes HTTP/1.1 (well, 9 chars before status)
202                 switch( atoi(Line + 9) )
203                 {
204                 case 200:
205                         // All good!
206                         return 1;
207                 default:
208                         fprintf(stderr, "Unknown HTTP status - %s\n", Line + 9);
209                         return -1;
210                 }
211         }
212         // Last line?
213         else if( Line[0] == '\0' )
214         {
215                 return 2;
216         }
217         // Body lines
218         else
219         {
220                 char    *value;
221                 char    *colon = strchr(Line, ':');
222                 if(colon == NULL)       return 1;
223
224                 *colon = '\0';
225                 value = colon + 2;
226                 if( strcmp(Line, "Content-Length") == 0 ) {
227                         *Size = atoi(value);
228                 }
229                 else {
230                         printf("Ignorning header '%s' = '%s'\n", Line, value);
231                 }
232
233                 return 1;
234         }
235 }
236
237 void writef(int fd, const char *format, ...)
238 {
239         va_list args;
240         size_t  len;
241         
242         va_start(args, format);
243         len = vsnprintf(NULL, 0, format, args);
244         va_end(args);
245         
246         char data[len + 1];
247         va_start(args, format);
248         vsnprintf(data, len+1, format, args);
249         va_end(args);
250         
251         write(fd, data, len);
252 }
253

UCC git Repository :: git.ucc.asn.au