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

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