69c9ab71e7028e9dd5f71fb8aba58535e5178c8c
[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                 rv = getaddrinfo(uri->Host, "http", NULL, &addrinfo);
88                 if( rv != 0 ) {
89                         fprintf(stderr, "Unable to resolve %s: %s\n", uri->Host, gai_strerror(rv));
90                         continue ;
91                 }
92
93                 for( struct addrinfo *addr = addrinfo; addr != NULL; addr = addr->ai_next )
94                 {
95                          int    bSkipLine = 0;
96                         // TODO: Convert to POSIX/BSD
97                         // NOTE: using addr->ai_addr will break for IPv6, as there is more info before the address
98                          int    sock;
99                         
100                         printf("Attempting [%s]:80\n", Net_PrintAddress(addr->ai_family, addr->ai_addr->sa_data));
101                         
102                         sock = Net_OpenSocket_TCPC(addr->ai_family, addr->ai_addr->sa_data, 80);
103                         if( sock == -1 ) {
104                                 continue ;
105                         }
106
107                         _SysDebug("Connected as %i", sock);
108                         
109                         writef(sock, "GET /%s HTTP/1.1\r\n", uri->Path);
110                         writef(sock, "Host: %s\r\n", uri->Host);
111 //                      writef(sock, "Accept-Encodings: */*\r\n");
112                         writef(sock, "User-Agent: awget/0.1 (Acess2)\r\n");
113                         writef(sock, "\r\n");
114                         
115                         // Parse headers
116                         char    inbuf[BUFSIZ+1];
117                         size_t  offset = 0, len = 0;
118                          int    state = 0;
119                         size_t  bytes_seen = 0;
120                         size_t  bytes_wanted = -1;      // invalid
121                         
122
123                         inbuf[0] = '\0';
124                         while( state == 0 || state == 1 )
125                         {
126                                 if( offset == BUFSIZ ) {
127                                         bSkipLine = 1;
128                                         offset = 0;
129                                 }
130                                 inbuf[len] = '\0';
131                         
132                                 char    *eol = strchr(inbuf, '\n');
133                                 // No end of line char? read some more
134                                 if( eol == NULL ) {
135                                         // TODO: Handle -1 return
136                                         len += read(sock, inbuf + offset, BUFSIZ - 1 - offset);
137                                         continue ;
138                                 }
139
140                                 // abuse offset as the end of the string        
141                                 offset = (eol - inbuf) + 1;
142
143                                 // Clear EOL bytes
144                                 *eol = '\0';
145                                 // Nuke the \r
146                                 if( eol - 1 >= inbuf )
147                                         eol[-1] = '\0';
148                                 
149                                 if( !bSkipLine )
150                                         state = _ParseHeaderLine(inbuf, state, &bytes_wanted);
151                 
152                                 // Move unused data down in memory      
153                                 len -= offset;
154                                 memmove( inbuf, inbuf + offset, BUFSIZ - offset );
155                                 offset = len;
156                         }
157
158                         if( state == 2 )
159                         {
160                                 _SysDebug("RXing %i bytes to '%s'", bytes_wanted, outfile);
161                                  int    outfd = open(outfile, O_WR|O_CREAT, 0666);
162                                 if( outfd == -1 ) {
163                                         fprintf(stderr, "Unable to open '%s' for writing\n", outfile);
164                                 }
165                                 else
166                                 {
167                                         // Write the remainder of the buffer
168                                         do
169                                         {
170                                                 write(outfd, inbuf, len);
171                                                 bytes_seen += len;
172                                                 _SysDebug("%i/%i bytes done", bytes_seen, bytes_wanted);
173                                         } while( bytes_seen < bytes_wanted && (len = read(sock, inbuf, sizeof(inbuf))) > 0 );
174                                         close(outfd);
175                                 }
176                         }
177                         
178                         _SysDebug("Closing socket");
179                         close(sock);
180                         break ;
181                 }
182
183                 free(uri);
184         }
185         
186         return 0;
187 }
188
189 int _ParseHeaderLine(char *Line, int State, size_t *Size)
190 {
191         _SysDebug("Header - %s", Line);
192         // First line (Status and version)
193         if( State == 0 )
194         {
195                 // HACK - assumes HTTP/1.1 (well, 9 chars before status)
196                 switch( atoi(Line + 9) )
197                 {
198                 case 200:
199                         // All good!
200                         return 1;
201                 default:
202                         fprintf(stderr, "Unknown HTTP status - %s\n", Line + 9);
203                         return -1;
204                 }
205         }
206         // Last line?
207         else if( Line[0] == '\0' )
208         {
209                 return 2;
210         }
211         // Body lines
212         else
213         {
214                 char    *value;
215                 char    *colon = strchr(Line, ':');
216                 if(colon == NULL)       return 1;
217
218                 *colon = '\0';
219                 value = colon + 2;
220                 if( strcmp(Line, "Content-Length") == 0 ) {
221                         *Size = atoi(value);
222                 }
223                 else {
224                         printf("Ignorning header '%s' = '%s'\n", Line, value);
225                 }
226
227                 return 1;
228         }
229 }
230
231 void writef(int fd, const char *format, ...)
232 {
233         va_list args;
234         size_t  len;
235         
236         va_start(args, format);
237         len = vsnprintf(NULL, 0, format, args);
238         va_end(args);
239         
240         char data[len + 1];
241         va_start(args, format);
242         vsnprintf(data, len+1, format, args);
243         va_end(args);
244         
245         write(fd, data, len);
246 }
247

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