#include "headers.h" enum { /* display a status bar */ /* currently not implemented */ B, /* put the file somewhere other than the current directory */ O, /* silence all output -- the default */ /* note that if -q is passed explicitly, it will irrevocably override -v and -b */ Q, /* increase the verbosity level. higher levels imply you want the content lower levels would provide */ /* all verbose output is sent to stderr for ease of capture */ /* 0: no informational, statistical or debug output */ /* 1: informational output (Sending request FOO to host BAR, Received response header BAZ, etc) */ /* 2: statistical. informational+size of header and body in bytes, round-trip time, etc */ /* 3+: debug. statistical+all internal state changes */ /* currently we only implement 1 */ V }; char param[4] = {0}; /* below is to accompany param[O] */ char *outpath; int main(int argc, char **argv) { int i, translen, gotheader; int sockd; char *recvbuf; char *sendbufp, *offsetp, *errstr; FILE *filed; struct tls *tlsc; uri uristruct; i = translen = sockd = 0; sendbufp = offsetp = 0; if( (recvbuf = malloc(BUFSIZ+1)) == NULL) { errstr = "failed to init"; goto err; } filed = 0; tlsc = NULL; for(i = 0; (i = getopt(argc, argv, "bo:qv")) != -1; i = 0) { switch(i) { case 'b': param[B] = 1; break; case 'o': param[O] = 1; outpath = optarg; break; case 'q': param[Q] = 1; param[B] = 0; param[V] = 0; break; case 'v': /* we handle v differently because we want to support different levels of verbosity */ if(!param[Q]) { param[V]++; } break; default: errstr = "[-vb] [-q] [-o ] uri"; goto usage; } } /* we should probably modify uri_parse to return a zero value on failure... */ if(uri_parse(argv[optind], &uristruct)) { errstr = "[-q|[-v -b]] [-o ] uri"; goto usage; } /* if outpath isn't set because we haven't received a -o param, */ /* then we should set the outpath to the final component of the */ /* URI. */ /* If we can't do that, we should just set it to 'default'. */ if(NULL == outpath && !strlen((outpath = (1 + strrchr(uristruct.path, '/'))))) { outpath = "default"; } /* generate our request */ if( !(sendbufp = reqgen(&uristruct))) { errstr = "Unknown protocol."; goto err; } if(param[V]) { fprintf(stderr, "request: %s\n", sendbufp); } /* having a routine or a global variable to track whether we need TLS would be */ /* nice to add in the future */ /* could probably store whether tls is necessary in a char within uristruct... */ /* Note: at the moment, if the TLS pointer passed is non-NULL, */ /* dial's return code can only be treated as an indicator of success. */ if(!strcmp("https", uristruct.proto)) { sockd = dial(uristruct.fqdn, uristruct.proto, &tlsc); } else { sockd = dial(uristruct.fqdn, uristruct.proto, NULL); } if(!sockd) { errstr = "Couldn't connect to the host using the specified protocol."; goto err; } if(tlsc) { translen = tls_write(tlsc, sendbufp, strlen(sendbufp)); } else { translen = send(sockd, sendbufp, strlen(sendbufp), 0); } if(translen) { if(param[V]) { printf("send: %d bytes\n", translen); } } else { errstr = "Couldn't transmit data."; goto err; } /* now for a slightly more complex version of the same routine: until we've encountered the */ /* delimiter, "\r\n\r\n", don't write to disk. Once we have, calculate the size of the body, */ /* then write that number of bytes to disk starting from the offset. Then, write everything */ /* until end of transmission. */ for(gotheader = 0, offsetp = NULL, translen = 1; translen > 0; memset(recvbuf, 0, BUFSIZ+1)) { if( NULL == tlsc) { printf("recv: %d bytes\n", translen = recv(sockd, recvbuf, BUFSIZ, 0)); } else { printf("recv: %d bytes\n", translen = tls_read(tlsc, recvbuf, BUFSIZ)); } /* if we haven't gotten the header and our delimiter isn't in the */ /* received string, we're getting a multipart header and need to */ /* skip over it. */ /* right now we assume HTTP/S -- this is undesirable... */ if( strcmp("gopher", uristruct.proto) && !gotheader) { if( NULL == (offsetp = strstr(recvbuf, "\r\n\r\n"))) { printf("continuing...\n"); continue; } else { gotheader = 1; offsetp += 4; printf("got the header\n"); } } if( !filed && !(filed = fopen(outpath, "w"))) { errstr = "couldn't open file"; goto err; } fwrite(offsetp, sizeof(char), (translen - (offsetp - recvbuf)), filed); offsetp = recvbuf; } free(recvbuf); close(sockd); fclose(filed); exit(EXIT_SUCCESS); err: fprintf(stderr, "%s: %s\n", argv[0], errstr); exit(EXIT_FAILURE); usage: fprintf(stderr, "usage: %s: %s\n", argv[0], errstr); exit(EXIT_FAILURE); }