VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/nsprpub/tools/httpget.c@ 2426

Last change on this file since 2426 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is the Netscape Portable Runtime (NSPR).
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38
39/*
40 * Author: Wan-Teh Chang
41 *
42 * Given an HTTP URL, httpget uses the GET method to fetch the file.
43 * The fetched file is written to stdout by default, or can be
44 * saved in an output file.
45 *
46 * This is a single-threaded program.
47 */
48
49#include "prio.h"
50#include "prnetdb.h"
51#include "prlog.h"
52#include "prerror.h"
53#include "prprf.h"
54#include "prinit.h"
55
56#include <stdio.h>
57#include <string.h>
58#include <stdlib.h> /* for atoi */
59
60#define FCOPY_BUFFER_SIZE (16 * 1024)
61#define INPUT_BUFFER_SIZE 1024
62#define LINE_SIZE 512
63#define HOST_SIZE 256
64#define PORT_SIZE 32
65#define PATH_SIZE 512
66
67/*
68 * A buffer for storing the excess input data for ReadLine.
69 * The data in the buffer starts from (including) the element pointed to
70 * by inputHead, and ends just before (not including) the element pointed
71 * to by inputTail. The buffer is empty if inputHead == inputTail.
72 */
73
74static char inputBuf[INPUT_BUFFER_SIZE];
75/*
76 * inputBufEnd points just past the end of inputBuf
77 */
78static char *inputBufEnd = inputBuf + sizeof(inputBuf);
79static char *inputHead = inputBuf;
80static char *inputTail = inputBuf;
81
82static PRBool endOfStream = PR_FALSE;
83
84/*
85 * ReadLine --
86 *
87 * Read in a line of text, terminated by CRLF or LF, from fd into buf.
88 * The terminating CRLF or LF is included (always as '\n'). The text
89 * in buf is terminated by a null byte. The excess bytes are stored in
90 * inputBuf for use in the next ReadLine call or FetchFile call.
91 * Returns the number of bytes in buf. 0 means end of stream. Returns
92 * -1 if read fails.
93 */
94
95PRInt32 ReadLine(PRFileDesc *fd, char *buf, PRUint32 bufSize)
96{
97 char *dst = buf;
98 char *bufEnd = buf + bufSize; /* just past the end of buf */
99 PRBool lineFound = PR_FALSE;
100 char *crPtr = NULL; /* points to the CR ('\r') character */
101 PRInt32 nRead;
102
103loop:
104 PR_ASSERT(inputBuf <= inputHead && inputHead <= inputTail
105 && inputTail <= inputBufEnd);
106 while (lineFound == PR_FALSE && inputHead != inputTail
107 && dst < bufEnd - 1) {
108 if (*inputHead == '\r') {
109 crPtr = dst;
110 } else if (*inputHead == '\n') {
111 lineFound = PR_TRUE;
112 if (crPtr == dst - 1) {
113 dst--;
114 }
115 }
116 *(dst++) = *(inputHead++);
117 }
118 if (lineFound == PR_TRUE || dst == bufEnd - 1 || endOfStream == PR_TRUE) {
119 *dst = '\0';
120 return dst - buf;
121 }
122
123 /*
124 * The input buffer should be empty now
125 */
126 PR_ASSERT(inputHead == inputTail);
127
128 nRead = PR_Read(fd, inputBuf, sizeof(inputBuf));
129 if (nRead == -1) {
130 *dst = '\0';
131 return -1;
132 } else if (nRead == 0) {
133 endOfStream = PR_TRUE;
134 *dst = '\0';
135 return dst - buf;
136 }
137 inputHead = inputBuf;
138 inputTail = inputBuf + nRead;
139 goto loop;
140}
141
142PRInt32 DrainInputBuffer(char *buf, PRUint32 bufSize)
143{
144 PRInt32 nBytes = inputTail - inputHead;
145
146 if (nBytes == 0) {
147 if (endOfStream) {
148 return -1;
149 } else {
150 return 0;
151 }
152 }
153 if ((PRInt32) bufSize < nBytes) {
154 nBytes = bufSize;
155 }
156 memcpy(buf, inputHead, nBytes);
157 inputHead += nBytes;
158 return nBytes;
159}
160
161PRStatus FetchFile(PRFileDesc *in, PRFileDesc *out)
162{
163 char buf[FCOPY_BUFFER_SIZE];
164 PRInt32 nBytes;
165
166 while ((nBytes = DrainInputBuffer(buf, sizeof(buf))) > 0) {
167 if (PR_Write(out, buf, nBytes) != nBytes) {
168 fprintf(stderr, "httpget: cannot write to file\n");
169 return PR_FAILURE;
170 }
171 }
172 if (nBytes < 0) {
173 /* Input buffer is empty and end of stream */
174 return PR_SUCCESS;
175 }
176 while ((nBytes = PR_Read(in, buf, sizeof(buf))) > 0) {
177 if (PR_Write(out, buf, nBytes) != nBytes) {
178 fprintf(stderr, "httpget: cannot write to file\n");
179 return PR_FAILURE;
180 }
181 }
182 if (nBytes < 0) {
183 fprintf(stderr, "httpget: cannot read from socket\n");
184 return PR_FAILURE;
185 }
186 return PR_SUCCESS;
187}
188
189PRStatus FastFetchFile(PRFileDesc *in, PRFileDesc *out, PRUint32 size)
190{
191 PRInt32 nBytes;
192 PRFileMap *outfMap;
193 void *addr;
194 char *start;
195 PRUint32 rem;
196 PRUint32 bytesToRead;
197 PRStatus rv;
198 PRInt64 sz64;
199
200 LL_UI2L(sz64, size);
201 outfMap = PR_CreateFileMap(out, sz64, PR_PROT_READWRITE);
202 PR_ASSERT(outfMap);
203 addr = PR_MemMap(outfMap, LL_ZERO, size);
204 if (addr == (void *) -1) {
205 fprintf(stderr, "cannot memory-map file: (%d, %d)\n", PR_GetError(),
206 PR_GetOSError());
207
208 PR_CloseFileMap(outfMap);
209 return PR_FAILURE;
210 }
211 PR_ASSERT(addr != (void *) -1);
212 start = (char *) addr;
213 rem = size;
214 while ((nBytes = DrainInputBuffer(start, rem)) > 0) {
215 start += nBytes;
216 rem -= nBytes;
217 }
218 if (nBytes < 0) {
219 /* Input buffer is empty and end of stream */
220 return PR_SUCCESS;
221 }
222 bytesToRead = (rem < FCOPY_BUFFER_SIZE) ? rem : FCOPY_BUFFER_SIZE;
223 while (rem > 0 && (nBytes = PR_Read(in, start, bytesToRead)) > 0) {
224 start += nBytes;
225 rem -= nBytes;
226 bytesToRead = (rem < FCOPY_BUFFER_SIZE) ? rem : FCOPY_BUFFER_SIZE;
227 }
228 if (nBytes < 0) {
229 fprintf(stderr, "httpget: cannot read from socket\n");
230 return PR_FAILURE;
231 }
232 rv = PR_MemUnmap(addr, size);
233 PR_ASSERT(rv == PR_SUCCESS);
234 rv = PR_CloseFileMap(outfMap);
235 PR_ASSERT(rv == PR_SUCCESS);
236 return PR_SUCCESS;
237}
238
239PRStatus ParseURL(char *url, char *host, PRUint32 hostSize,
240 char *port, PRUint32 portSize, char *path, PRUint32 pathSize)
241{
242 char *start, *end;
243 char *dst;
244 char *hostEnd;
245 char *portEnd;
246 char *pathEnd;
247
248 if (strncmp(url, "http", 4)) {
249 fprintf(stderr, "httpget: the protocol must be http\n");
250 return PR_FAILURE;
251 }
252 if (strncmp(url + 4, "://", 3) || url[7] == '\0') {
253 fprintf(stderr, "httpget: malformed URL: %s\n", url);
254 return PR_FAILURE;
255 }
256
257 start = end = url + 7;
258 dst = host;
259 hostEnd = host + hostSize;
260 while (*end && *end != ':' && *end != '/') {
261 if (dst == hostEnd - 1) {
262 fprintf(stderr, "httpget: host name too long\n");
263 return PR_FAILURE;
264 }
265 *(dst++) = *(end++);
266 }
267 *dst = '\0';
268
269 if (*end == '\0') {
270 PR_snprintf(port, portSize, "%d", 80);
271 PR_snprintf(path, pathSize, "%s", "/");
272 return PR_SUCCESS;
273 }
274
275 if (*end == ':') {
276 end++;
277 dst = port;
278 portEnd = port + portSize;
279 while (*end && *end != '/') {
280 if (dst == portEnd - 1) {
281 fprintf(stderr, "httpget: port number too long\n");
282 return PR_FAILURE;
283 }
284 *(dst++) = *(end++);
285 }
286 *dst = '\0';
287 if (*end == '\0') {
288 PR_snprintf(path, pathSize, "%s", "/");
289 return PR_SUCCESS;
290 }
291 } else {
292 PR_snprintf(port, portSize, "%d", 80);
293 }
294
295 dst = path;
296 pathEnd = path + pathSize;
297 while (*end) {
298 if (dst == pathEnd - 1) {
299 fprintf(stderr, "httpget: file pathname too long\n");
300 return PR_FAILURE;
301 }
302 *(dst++) = *(end++);
303 }
304 *dst = '\0';
305 return PR_SUCCESS;
306}
307
308void PrintUsage(void) {
309 fprintf(stderr, "usage: httpget url\n"
310 " httpget -o outputfile url\n"
311 " httpget url -o outputfile\n");
312}
313
314int main(int argc, char **argv)
315{
316 PRHostEnt hostentry;
317 char buf[PR_NETDB_BUF_SIZE];
318 PRNetAddr addr;
319 PRFileDesc *socket = NULL, *file = NULL;
320 PRIntn cmdSize;
321 char host[HOST_SIZE];
322 char port[PORT_SIZE];
323 char path[PATH_SIZE];
324 char line[LINE_SIZE];
325 int exitStatus = 0;
326 PRBool endOfHeader = PR_FALSE;
327 char *url;
328 char *fileName = NULL;
329 PRUint32 fileSize;
330
331 if (argc != 2 && argc != 4) {
332 PrintUsage();
333 exit(1);
334 }
335
336 if (argc == 2) {
337 /*
338 * case 1: httpget url
339 */
340 url = argv[1];
341 } else {
342 if (strcmp(argv[1], "-o") == 0) {
343 /*
344 * case 2: httpget -o outputfile url
345 */
346 fileName = argv[2];
347 url = argv[3];
348 } else {
349 /*
350 * case 3: httpget url -o outputfile
351 */
352 url = argv[1];
353 if (strcmp(argv[2], "-o") != 0) {
354 PrintUsage();
355 exit(1);
356 }
357 fileName = argv[3];
358 }
359 }
360
361 if (ParseURL(url, host, sizeof(host), port, sizeof(port),
362 path, sizeof(path)) == PR_FAILURE) {
363 exit(1);
364 }
365
366 if (PR_GetHostByName(host, buf, sizeof(buf), &hostentry)
367 == PR_FAILURE) {
368 fprintf(stderr, "httpget: unknown host name: %s\n", host);
369 exit(1);
370 }
371
372 addr.inet.family = PR_AF_INET;
373 addr.inet.port = PR_htons((short) atoi(port));
374 addr.inet.ip = *((PRUint32 *) hostentry.h_addr_list[0]);
375
376 socket = PR_NewTCPSocket();
377 if (socket == NULL) {
378 fprintf(stderr, "httpget: cannot create new tcp socket\n");
379 exit(1);
380 }
381
382 if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
383 fprintf(stderr, "httpget: cannot connect to http server\n");
384 exitStatus = 1;
385 goto done;
386 }
387
388 if (fileName == NULL) {
389 file = PR_STDOUT;
390 } else {
391 file = PR_Open(fileName, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
392 00777);
393 if (file == NULL) {
394 fprintf(stderr, "httpget: cannot open file %s: (%d, %d)\n",
395 fileName, PR_GetError(), PR_GetOSError());
396 exitStatus = 1;
397 goto done;
398 }
399 }
400
401 cmdSize = PR_snprintf(buf, sizeof(buf), "GET %s HTTP/1.0\r\n\r\n", path);
402 PR_ASSERT(cmdSize == (PRIntn) strlen("GET HTTP/1.0\r\n\r\n")
403 + (PRIntn) strlen(path));
404 if (PR_Write(socket, buf, cmdSize) != cmdSize) {
405 fprintf(stderr, "httpget: cannot write to http server\n");
406 exitStatus = 1;
407 goto done;
408 }
409
410 if (ReadLine(socket, line, sizeof(line)) <= 0) {
411 fprintf(stderr, "httpget: cannot read line from http server\n");
412 exitStatus = 1;
413 goto done;
414 }
415
416 /* HTTP response: 200 == OK */
417 if (strstr(line, "200") == NULL) {
418 fprintf(stderr, "httpget: %s\n", line);
419 exitStatus = 1;
420 goto done;
421 }
422
423 while (ReadLine(socket, line, sizeof(line)) > 0) {
424 if (line[0] == '\n') {
425 endOfHeader = PR_TRUE;
426 break;
427 }
428 if (strncmp(line, "Content-Length", 14) == 0
429 || strncmp(line, "Content-length", 14) == 0) {
430 char *p = line + 14;
431
432 while (*p == ' ' || *p == '\t') {
433 p++;
434 }
435 if (*p != ':') {
436 continue;
437 }
438 p++;
439 while (*p == ' ' || *p == '\t') {
440 p++;
441 }
442 fileSize = 0;
443 while ('0' <= *p && *p <= '9') {
444 fileSize = 10 * fileSize + (*p - '0');
445 p++;
446 }
447 }
448 }
449 if (endOfHeader == PR_FALSE) {
450 fprintf(stderr, "httpget: cannot read line from http server\n");
451 exitStatus = 1;
452 goto done;
453 }
454
455 if (fileName == NULL || fileSize == 0) {
456 FetchFile(socket, file);
457 } else {
458 FastFetchFile(socket, file, fileSize);
459 }
460
461done:
462 if (socket) PR_Close(socket);
463 if (file) PR_Close(file);
464 PR_Cleanup();
465 return exitStatus;
466}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette