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

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