====== Webclient ====== {{xkalab00.tar.gz}} Vytvoření klientské síťové aplikace v prostředí UNIXu s využitím komunikačního rozhraní BSD sockets. Vytvořte program (klient) s využitím rozhraní schránek (BSD sockets), který implementuje stahování zadaného objektu a případných vnořených objektů pomocí URL z WWW serveru s využitím HTTP protokolu do souboru uloženého v lokálním souborovém systému. Vytvořte program v jazyce C/C++, který je přeložitelný na studentském unixovém serveru eva včetně funkčního Makefile souboru (program přeložitelný po zadání příkazu make). Program využívá spojovanou službu (protokol TCP). Jméno přeloženého programu klienta bude webclient. Program předpokládá jeden povinný parametr a to URL identifikující objekt, který bude uložen do lokáního souborového systému do aktuálního adresáře. Pokud v dotazu URL není uvedeno jméno souboru, obsah bude uložen do souboru index.html. Program musí podporovat stavové kódy HTTP protokolu pro přesměrování požadavku 3xx. Oznámení o chybách, které mohou nastat, bude vytištěno na standardní chybový výstup (stderr). Za vnořený objekt považujte obrázek (nestahujte definici CSS, Javascript, Flash objekty atd.). Obrázky uložte do adresáře podle cesty, jaká je uvedena v HTML dokumentu. #include #include #include #include #include #include #include #include #include #include #include int webclient(char* url, unsigned short jumps) { if (jumps > 5) { fprintf(stderr, "D'OH! Too many jumps, my legs hurts!\n"); return 1; } if (strstr(url, "://") != NULL) { url = strstr(url, "://") + strlen("://"); } char* parsedhost = url; char* parsedport = "80"; char* parsedurl = ""; char* parsedfile = "index.htm"; unsigned int i = 0; while (url[i] != '\0') { if (url[i] == ':') { url[i] = '\0'; parsedport = url + i + 1; } if (url[i] == '/') { url[i] = '\0'; parsedurl = url + i + 1; i++; while (url[i] != '\0') { if (url[i] == '/') { parsedfile = url + i + 1; } i++; } break; } i++; } if (strlen(parsedurl) < 1) { parsedurl = ""; } //printf("Host: %s\nPort: %s\nURL: %s\nFile: %s\n", parsedhost, parsedport, parsedurl, parsedfile); //DEBUG int s = socket(PF_INET, SOCK_STREAM, 0); if (s < 0) { fprintf(stderr, "D'OH! Broken socket, call electrician.\n"); return 1; } struct sockaddr_in sadr; bzero(&sadr, sizeof(sadr)); //radsi to vynulujem sadr.sin_family = AF_INET; //rodina struct hostent *host; if ((host = gethostbyname(parsedhost)) == NULL) { fprintf(stderr, "D'OH! Unknown host.\n"); return 1; } memcpy(&sadr.sin_addr, host->h_addr, host->h_length); //adresa sadr.sin_port = htons(atoi(parsedport)); if (connect(s, (struct sockaddr*) &sadr, sizeof(sadr)) < 0) { fprintf(stderr, "D'OH! Server is dead.\n"); return 1; } printf("Connected!\n"); printf("Negotiating...\n"); char* get = NULL; if (parsedurl == NULL) { parsedurl = ""; } //printf("Processing HTTP GET...\n"); get = malloc((strlen("GET /") + strlen(parsedurl) + strlen(" HTTP/1.0\r\nHost: ") + strlen(parsedhost) + strlen("\r\n\r\n")) * sizeof(char)); if (get == NULL) { fprintf(stderr, "D'OH! Malloc fail, buy more RAM!\n"); return 1; } get[0] = '\0'; strcat(get, "GET /"); strcat(get, parsedurl); strcat(get, " HTTP/1.0\r\nHost: "); strcat(get, parsedhost); strcat(get, "\r\n\r\n"); //printf("%s\n", get); if ((write(s, get, strlen(get))) <= 0) { fprintf(stderr, "D'OH! Can't write.\n"); if (parsedurl != NULL) { free(get); } return 1; } if (parsedurl != NULL) { free(get); } printf("Reading...\n"); char* header; unsigned int allocated = 1; unsigned int dumped = 0; header = malloc(allocated * sizeof(char)); if (header == NULL) { fprintf(stderr, "D'OH! Malloc fail, buy me more memory!\n"); return 1; } while (strstr(header, "\r\n\r\n") == NULL) { read(s, header + dumped, 1); dumped++; if (dumped >= allocated) { allocated *= 2; header = realloc(header, allocated); if (header == NULL) { fprintf(stderr, "D'OH! Realloc fail, buy me more memory!\n"); return 1; } } } //printf("Header:\n%s", header); unsigned int response = atoi(header + 9); printf("Response: %d\n", response); char* location = NULL; if (response >= 300 && response < 400) { location = strstr(header, "Location: ") + strlen("Location: "); i = 0; while (location[i] != '\r') { i++; } location[i] = '\0'; //printf("\nLocation: %s\n", location); } if (response == 200) { //printf("Socket -> %s...\n", parsedfile); printf("Downloading...\n"); FILE* fp = fopen(parsedfile, "w"); if (fp == NULL) { fprintf(stderr, "D'OH! I don't know where to write, buy me some paper!\n"); free(header); return 1; } char socketchar[1]; while (read(s, socketchar, 1) > 0) { fwrite(socketchar, sizeof(char), 1, fp); } fclose(fp); } if (response >= 300 && response < 400) { printf("Redirected...\n"); webclient(location, ++jumps); } free(header); shutdown(s, 2); close(s); return 0; } int main(int argc, char* argv[]) { if (argc > 2) { fprintf(stderr, "D'OH! Too many parameters.\n"); return 1; } if (argc == 1) { printf("USAGE: webclinet URL\n"); return 0; } return webclient(argv[1], 0); } CC = gcc CFLAGS = -O2 -std=c99 -pedantic -Wall -W -pipe PROG = webclient all: $(PROG) webclient: webclient.c clean: -rm -f $(PROG) *.htm *.html *.png *.gif *.jpg *.jpeg *.bmp #!/bin/bash echo "Cleaning..." make clean echo echo "Compiling..." make echo echo "Downloading simple web page with images..." ./webclient http://www.fit.vutbr.cz echo echo "Downloading more complex URL (image)..." ./webclient http://www.fit.vutbr.cz:80/common/img/fit_logo_cz.gif