4 * Copyright 2003-2004 Oliver Feiler <kiza@kcore.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /* OS X needs this, otherwise socklen_t is not defined. */
25 # define _BSD_SOCKLEN_T_
28 /* BeOS does not define socklen_t. Using uint as suggested by port creator. */
30 # define socklen_t unsigned int
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
50 #include "conversions.h"
51 #include "net-support.h"
52 #include "io-internal.h"
53 #include "zlib_interface.h"
55 static int const MAX_HTTP_REDIRECTS = 10; /* Maximum number of redirects we will follow. */
56 static int const NET_TIMEOUT = 20; /* Global network timeout in sec */
57 static int const NET_READ = 1;
58 static int const NET_WRITE = 2;
60 extern char *proxyname; /* Hostname of proxyserver. */
61 extern unsigned short proxyport; /* Port on proxyserver to use. */
63 /* Masquerade as Firefox on Linux to increase the share of both in web server statistics. */
64 char *useragent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0";
66 /* Waits NET_TIMEOUT seconds for the socket to return data.
71 * -1 Error occured (netio_error is set)
73 int NetPoll (struct feed * cur_ptr, int * my_socket, int rw) {
77 int retval; /* FD_ISSET + assert == Heisenbug? */
79 /* Set global network timeout */
80 tv.tv_sec = NET_TIMEOUT;
87 FD_SET(*my_socket, &rfdsr);
88 if (select (*my_socket+1, &rfdsr, NULL, NULL, &tv) == 0) {
90 cur_ptr->netio_error = NET_ERR_TIMEOUT;
93 retval = FD_ISSET (*my_socket, &rfdsr);
97 cur_ptr->netio_error = NET_ERR_UNKNOWN;
100 } else if (rw == NET_WRITE) {
101 FD_SET(*my_socket, &rfdsw);
102 if (select (*my_socket+1, NULL, &rfdsw, NULL, &tv) == 0) {
104 cur_ptr->netio_error = NET_ERR_TIMEOUT;
107 retval = FD_ISSET (*my_socket, &rfdsw);
111 cur_ptr->netio_error = NET_ERR_UNKNOWN;
115 cur_ptr->netio_error = NET_ERR_UNKNOWN;
123 /* Connect network sockets.
128 * -1 Error occured (netio_error is set)
130 int NetConnect (int * my_socket, char * host, struct feed * cur_ptr, int httpproto, int suppressoutput) {
132 struct sockaddr_in address;
133 struct hostent *remotehost;
138 realhost = strdup(host);
139 if (sscanf (host, "%[^:]:%hd", realhost, &port) != 2) {
143 /* Create a inet stream TCP socket. */
144 *my_socket = socket (AF_INET, SOCK_STREAM, 0);
145 if (*my_socket == -1) {
146 cur_ptr->netio_error = NET_ERR_SOCK_ERR;
150 /* If proxyport is 0 we didn't execute the if http_proxy statement in main
151 so there is no proxy. On any other value of proxyport do proxyrequests instead. */
152 if (proxyport == 0) {
153 /* Lookup remote IP. */
154 remotehost = gethostbyname (realhost);
155 if (remotehost == NULL) {
158 cur_ptr->netio_error = NET_ERR_HOST_NOT_FOUND;
162 /* Set the remote address. */
163 address.sin_family = AF_INET;
164 address.sin_port = htons(port);
165 memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
167 /* Connect socket. */
168 cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof(address));
170 /* Check if we're already connected.
171 BSDs will return 0 on connect even in nonblock if connect was fast enough. */
172 if (cur_ptr->connectresult != 0) {
173 /* If errno is not EINPROGRESS, the connect went wrong. */
174 if (errno != EINPROGRESS) {
177 cur_ptr->netio_error = NET_ERR_CONN_REFUSED;
181 if ((NetPoll (cur_ptr, my_socket, NET_WRITE)) == -1) {
187 /* We get errno of connect back via getsockopt SO_ERROR (into connectresult). */
188 len = sizeof(cur_ptr->connectresult);
189 getsockopt(*my_socket, SOL_SOCKET, SO_ERROR, &cur_ptr->connectresult, &len);
191 if (cur_ptr->connectresult != 0) {
194 cur_ptr->netio_error = NET_ERR_CONN_FAILED; /* ->strerror(cur_ptr->connectresult) */
199 /* Lookup proxyserver IP. */
200 remotehost = gethostbyname (proxyname);
201 if (remotehost == NULL) {
204 cur_ptr->netio_error = NET_ERR_HOST_NOT_FOUND;
208 /* Set the remote address. */
209 address.sin_family = AF_INET;
210 address.sin_port = htons(proxyport);
211 memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
213 /* Connect socket. */
214 cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof(address));
216 /* Check if we're already connected.
217 BSDs will return 0 on connect even in nonblock if connect was fast enough. */
218 if (cur_ptr->connectresult != 0) {
219 if (errno != EINPROGRESS) {
222 cur_ptr->netio_error = NET_ERR_CONN_REFUSED;
226 if ((NetPoll (cur_ptr, my_socket, NET_WRITE)) == -1) {
232 len = sizeof(cur_ptr->connectresult);
233 getsockopt(*my_socket, SOL_SOCKET, SO_ERROR, &cur_ptr->connectresult, &len);
235 if (cur_ptr->connectresult != 0) {
238 cur_ptr->netio_error = NET_ERR_CONN_FAILED; /* ->strerror(cur_ptr->connectresult) */
250 * Main network function.
251 * (Now with a useful function description *g*)
253 * This function returns the HTTP request's body (deflating gzip encoded data
255 * Updates passed feed struct with values gathered from webserver.
256 * Handles all redirection and HTTP status decoding.
257 * Returns NULL pointer if no data was received and sets netio_error.
259 char * NetIO (int * my_socket, char * host, char * url, struct feed * cur_ptr, char * authdata, int httpproto, int suppressoutput) {
260 char netbuf[4096]; /* Network read buffer. */
261 char *body; /* XML body. */
263 FILE *stream; /* Stream socket. */
264 int chunked = 0; /* Content-Encoding: chunked received? */
265 int redirectcount; /* Number of HTTP redirects followed. */
266 char httpstatus[4]; /* HTTP status sent by server. */
267 char servreply[128]; /* First line of server reply */
269 char *savestart; /* Save start position of pointers. */
270 char *tmphost; /* Pointers needed to strsep operation. */
271 char *newhost; /* New hostname if we need to redirect. */
272 char *newurl; /* New document name ". */
274 char *tmpstring; /* Temp pointers. */
275 char *freeme, *freeme2;
276 char *redirecttarget;
280 int inflate = 0; /* Whether feed data needs decompressed with zlib. */
283 int quirksmode = 0; /* IIS operation mode. */
284 int authfailed = 0; /* Avoid repeating failed auth requests endlessly. */
287 if (!suppressoutput) {
288 if (cur_ptr->title == NULL)
289 fprintf(stderr, "Downloading http://%s%s\n", host, url);
291 fprintf(stderr, "Downloading %s\n", cur_ptr->title);
297 /* Goto label to redirect reconnect. */
300 /* Reconstruct digest authinfo for every request so we don't reuse
301 the same nonce value for more than one request.
302 This happens one superflous time on 303 redirects. */
303 if ((cur_ptr->authinfo != NULL) && (cur_ptr->servauth != NULL)) {
304 if (strstr (cur_ptr->authinfo, " Digest ") != NULL) {
305 NetSupportAuth(cur_ptr, authdata, url, cur_ptr->servauth);
310 stream = fdopen (*my_socket, "r+");
311 if (stream == NULL) {
312 /* This is a serious non-continueable OS error as it will probably not
315 BeOS will stupidly return SUCCESS here making this code silently fail on BeOS. */
316 cur_ptr->netio_error = NET_ERR_SOCK_ERR;
320 /* Again is proxyport == 0, non proxy mode, otherwise make proxy requests. */
321 if (proxyport == 0) {
322 /* Request URL from HTTP server. */
323 if (cur_ptr->lastmodified != NULL) {
325 "GET %s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\nIf-Modified-Since: %s\r\n%s%s\r\n",
329 cur_ptr->lastmodified,
330 (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
331 (cur_ptr->cookies ? cur_ptr->cookies : ""));
334 "GET %s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\n%s%s\r\n",
338 (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
339 (cur_ptr->cookies ? cur_ptr->cookies : ""));
341 fflush(stream); /* We love Solaris, don't we? */
343 /* Request URL from HTTP server. */
344 if (cur_ptr->lastmodified != NULL) {
346 "GET http://%s%s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\nIf-Modified-Since: %s\r\n%s%s\r\n",
351 cur_ptr->lastmodified,
352 (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
353 (cur_ptr->cookies ? cur_ptr->cookies : ""));
356 "GET http://%s%s HTTP/1.0\r\nAccept-Encoding: gzip\r\nUser-Agent: %s\r\nConnection: close\r\nHost: %s\r\n%s%s\r\n",
361 (cur_ptr->authinfo ? cur_ptr->authinfo : ""),
362 (cur_ptr->cookies ? cur_ptr->cookies : ""));
364 fflush(stream); /* We love Solaris, don't we? */
367 if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
372 if ((fgets (servreply, sizeof(servreply), stream)) == NULL) {
376 if (checkValidHTTPHeader(servreply, sizeof(servreply)) != 0) {
377 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
382 tmpstatus = strdup(servreply);
383 savestart = tmpstatus;
385 memset (httpstatus, 0, 4); /* Nullify string so valgrind shuts up. */
386 /* Set pointer to char after first space.
389 Copy three bytes into httpstatus. */
390 strsep (&tmpstatus, " ");
391 if (tmpstatus == NULL) {
392 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
394 free (savestart); /* Probably more leaks when doing auth and abort here. */
397 strncpy (httpstatus, tmpstatus, 3);
400 cur_ptr->lasthttpstatus = atoi (httpstatus);
402 /* If the redirectloop was run newhost and newurl were allocated.
403 We need to free them here. */
404 if ((redirectcount > 0) && (authdata == NULL)) {
409 tmphttpstatus = cur_ptr->lasthttpstatus;
411 /* Check HTTP server response and handle redirects. */
413 switch (tmphttpstatus) {
415 /* Received good status from server, clear problem field. */
416 cur_ptr->netio_error = NET_ERR_OK;
417 cur_ptr->problem = 0;
419 case 300: /* Multiple choice and everything 300 not handled is fatal. */
420 cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
424 /* Permanent redirect. Change feed->feedurl to new location.
425 Done some way down when we have extracted the new url. */
426 case 302: /* Found */
427 case 303: /* See Other */
428 case 307: /* Temp redirect. This is HTTP/1.1 */
431 /* Give up if we reach MAX_HTTP_REDIRECTS to avoid loops. */
432 if (redirectcount > MAX_HTTP_REDIRECTS) {
433 cur_ptr->netio_error = NET_ERR_REDIRECT_COUNT_ERR;
438 while (!feof(stream)) {
439 if ((fgets (netbuf, sizeof(netbuf), stream)) == NULL) {
440 /* Something bad happened. Server sent stupid stuff. */
441 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
446 if (checkValidHTTPHeader(netbuf, sizeof(netbuf)) != 0) {
447 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
452 /* Split netbuf into hostname and trailing url.
453 Place hostname in *newhost and tail into *newurl.
454 Close old connection and reconnect to server.
456 Do not touch any of the following code! :P */
457 if (strncasecmp (netbuf, "Location", 8) == 0) {
458 redirecttarget = strdup (netbuf);
459 freeme = redirecttarget;
461 /* Remove trailing \r\n from line. */
462 redirecttarget[strlen(redirecttarget)-2] = 0;
464 /* In theory pointer should now be after the space char
465 after the word "Location:" */
466 strsep (&redirecttarget, " ");
468 if (redirecttarget == NULL) {
469 cur_ptr->problem = 1;
470 cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
476 /* Location must start with "http", otherwise switch on quirksmode. */
477 if (strncmp(redirecttarget, "http", 4) != 0)
480 /* If the Location header is invalid we need to construct
481 a correct one here before proceeding with the program.
482 This makes headers like
483 "Location: fuck-the-protocol.rdf" work.
484 In violalation of RFC1945, RFC2616. */
486 len = 7 + strlen(host) + strlen(redirecttarget) + 3;
487 newlocation = malloc(len);
488 memset (newlocation, 0, len);
489 strcat (newlocation, "http://");
490 strcat (newlocation, host);
491 if (redirecttarget[0] != '/')
492 strcat (newlocation, "/");
493 strcat (newlocation, redirecttarget);
495 newlocation = strdup (redirecttarget);
497 /* This also frees redirecttarget. */
500 /* Change cur_ptr->feedurl on 301. */
501 if (cur_ptr->lasthttpstatus == 301) {
502 /* Check for valid redirection URL */
503 if (checkValidHTTPURL(newlocation) != 0) {
504 cur_ptr->problem = 1;
505 cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
509 if (!suppressoutput) {
510 fprintf(stderr, "URL points to permanent redirect, updating with new location...\n");
512 free (cur_ptr->feedurl);
513 if (authdata == NULL)
514 cur_ptr->feedurl = strdup (newlocation);
516 /* Include authdata in newly constructed URL. */
517 len = strlen(authdata) + strlen(newlocation) + 2;
518 cur_ptr->feedurl = malloc (len);
519 newurl = strdup(newlocation);
521 strsep (&newurl, "/");
522 strsep (&newurl, "/");
523 snprintf (cur_ptr->feedurl, len, "http://%s@%s", authdata, newurl);
528 freeme = newlocation;
529 strsep (&newlocation, "/");
530 strsep (&newlocation, "/");
531 tmphost = newlocation;
532 /* The following line \0-terminates tmphost in overwriting the first
533 / after the hostname. */
534 strsep (&newlocation, "/");
536 /* newlocation must now be the absolute path on newhost.
537 If not we've been redirected to somewhere totally stupid
538 (oh yeah, no offsite linking, go to our fucking front page).
539 Say goodbye to the webserver in this case. In fact, we don't
540 even say goodbye, but just drop the connection. */
541 if (newlocation == NULL) {
542 cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
547 newhost = strdup (tmphost);
549 newlocation[0] = '/';
550 newurl = strdup (newlocation);
554 /* Close connection. */
557 /* Reconnect to server. */
558 if ((NetConnect (my_socket, newhost, cur_ptr, httpproto, suppressoutput)) != 0) {
570 /* Not modified received. We can close stream and return from here.
571 Not very friendly though. :) */
573 /* Received good status from server, clear problem field. */
574 cur_ptr->netio_error = NET_ERR_OK;
575 cur_ptr->problem = 0;
577 /* This should be freed everywhere where we return
578 and current feed uses auth. */
579 if ((redirectcount > 0) && (authdata != NULL)) {
586 Parse rest of header and rerequest URL from server using auth mechanism
587 requested in WWW-Authenticate header field. (Basic or Digest) */
590 cur_ptr->netio_error = NET_ERR_HTTP_404;
593 case 410: /* The feed is gone. Politely remind the user to unsubscribe. */
594 cur_ptr->netio_error = NET_ERR_HTTP_410;
598 cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
602 /* unknown error codes have to be treated like the base class */
604 /* first pass, modify error code to base class */
606 tmphttpstatus -= tmphttpstatus % 100;
608 /* second pass, give up on unknown error base class */
609 cur_ptr->netio_error = NET_ERR_HTTP_NON_200;
616 /* Read rest of HTTP header and parse what we need. */
617 while (!feof(stream)) {
618 if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
623 if ((fgets (netbuf, sizeof(netbuf), stream)) == NULL)
626 if (checkValidHTTPHeader(netbuf, sizeof(netbuf)) != 0) {
627 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
632 if (strncasecmp (netbuf, "Transfer-Encoding", 17) == 0) {
633 /* Chunked transfer encoding. HTTP/1.1 extension.
634 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 */
635 if (strstr (netbuf, "chunked") != NULL)
638 /* Get last modified date. This is only relevant on HTTP 200. */
639 if ((strncasecmp (netbuf, "Last-Modified", 13) == 0) &&
640 (cur_ptr->lasthttpstatus == 200)) {
641 tmpstring = strdup(netbuf);
643 strsep (&tmpstring, " ");
644 if (tmpstring == NULL)
647 free(cur_ptr->lastmodified);
648 cur_ptr->lastmodified = strdup(tmpstring);
649 if (cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] == '\n')
650 cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] = '\0';
651 if (cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] == '\r')
652 cur_ptr->lastmodified[strlen(cur_ptr->lastmodified)-1] = '\0';
656 if (strncasecmp (netbuf, "Content-Encoding", 16) == 0) {
657 if (strstr (netbuf, "gzip") != NULL)
660 if (strncasecmp (netbuf, "Content-Type", 12) == 0) {
661 tmpstring = strdup(netbuf);
663 strsep(&tmpstring, " ");
664 if (tmpstring == NULL)
668 freeme2 = strstr(tmpstring, ";");
671 free(cur_ptr->content_type);
672 cur_ptr->content_type = strdup(tmpstring);
673 if (cur_ptr->content_type[strlen(cur_ptr->content_type)-1] == '\n')
674 cur_ptr->content_type[strlen(cur_ptr->content_type)-1] = '\0';
675 if (cur_ptr->content_type[strlen(cur_ptr->content_type)-1] == '\r')
676 cur_ptr->content_type[strlen(cur_ptr->content_type)-1] = '\0';
680 /* HTTP authentication
683 if ((strncasecmp (netbuf, "WWW-Authenticate", 16) == 0) &&
684 (cur_ptr->lasthttpstatus == 401)) {
686 /* Don't repeat authrequest if it already failed before! */
687 cur_ptr->netio_error = NET_ERR_AUTH_FAILED;
692 /* Remove trailing \r\n from line. */
693 if (netbuf[strlen(netbuf)-1] == '\n')
694 netbuf[strlen(netbuf)-1] = '\0';
695 if (netbuf[strlen(netbuf)-1] == '\r')
696 netbuf[strlen(netbuf)-1] = '\0';
700 /* Make a copy of the WWW-Authenticate header. We use it to
701 reconstruct a new auth reply on every loop. */
702 free (cur_ptr->servauth);
704 cur_ptr->servauth = strdup (netbuf);
706 /* Load authinfo into cur_ptr->authinfo. */
707 retval = NetSupportAuth(cur_ptr, authdata, url, netbuf);
711 cur_ptr->netio_error = NET_ERR_AUTH_NO_AUTHINFO;
716 cur_ptr->netio_error = NET_ERR_AUTH_GEN_AUTH_ERR;
721 cur_ptr->netio_error = NET_ERR_AUTH_UNSUPPORTED;
729 /* Close current connection and reconnect to server. */
731 if ((NetConnect (my_socket, host, cur_ptr, httpproto, suppressoutput)) != 0) {
735 /* Now that we have an authinfo, repeat the current request. */
738 /* This seems to be optional and probably not worth the effort since we
739 don't issue a lot of consecutive requests. */
740 /*if ((strncasecmp (netbuf, "Authentication-Info", 19) == 0) ||
741 (cur_ptr->lasthttpstatus == 200)) {
745 /* HTTP RFC 2616, Section 19.3 Tolerant Applications.
746 Accept CRLF and LF line ends in the header field. */
747 if ((strcmp(netbuf, "\r\n") == 0) || (strcmp(netbuf, "\n") == 0))
751 /* If the redirectloop was run newhost and newurl were allocated.
752 We need to free them here.
753 But _after_ the authentication code since it needs these values! */
754 if ((redirectcount > 0) && (authdata != NULL)) {
759 /**********************
760 * End of HTTP header *
761 **********************/
763 /* Init pointer so strncat works.
764 Workaround class hack. */
770 /* Read stream until EOF and return it to parent. */
771 while (!feof(stream)) {
772 if ((NetPoll (cur_ptr, my_socket, NET_READ)) == -1) {
777 /* Since we handle binary data if we read compressed input we
778 need to use fread instead of fgets after reading the header. */
779 retval = fread (netbuf, 1, sizeof(netbuf), stream);
782 body = realloc (body, length+retval);
783 memcpy (body+length, netbuf, retval);
788 body = realloc(body, length+1);
791 cur_ptr->content_length = length;
793 /* Close connection. */
797 if (decodechunked(body, &length) == NULL) {
799 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
804 /* If inflate==1 we need to decompress the content.. */
807 /*inflatedbody = gzip_uncompress (body, length, &cur_ptr->content_length);
808 if (inflatedbody == NULL) {
810 cur_ptr->netio_error = NET_ERR_GZIP_ERR;
813 if (jg_gzip_uncompress (body, length, (void **)&inflatedbody, &cur_ptr->content_length) != 0) {
815 cur_ptr->netio_error = NET_ERR_GZIP_ERR;
819 /* Copy uncompressed data back to body. */
827 /* Returns allocated string with body of webserver reply.
828 Various status info put into struct feed *cur_ptr.
829 Set suppressoutput=1 to disable diagnostic output. */
830 char *DownloadFeed(char *url, struct feed *cur_ptr, int suppressoutput) {
833 char *host; /* Needs to freed. */
837 char *authdata = NULL;
839 int httpproto = 0; /* 0: http; 1: https */
841 if (checkValidHTTPURL(url) != 0) {
842 cur_ptr->problem = 1;
843 cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
846 /* strstr will match _any_ substring. Not good, use strncasecmp with length 5! */
847 if (strncasecmp (url, "https", 5) == 0)
857 /* Assume "/" is input is exhausted. */
862 /* If tmphost contains an '@', extract username and pwd. */
863 if (strchr (tmphost, '@') != NULL) {
865 strsep (&tmphost, "@");
866 authdata = strdup (tmpstr);
869 host = strdup (tmphost);
871 /* netio() might change pointer of host to something else if redirect
872 loop is executed. Make a copy so we can correctly free everything. */
874 /* Only run if url was != NULL above. */
878 if (url[strlen(url)-1] == '\n') {
879 url[strlen(url)-1] = '\0';
883 if ((NetConnect (&my_socket, host, cur_ptr, httpproto, suppressoutput)) != 0) {
888 cur_ptr->problem = 1;
891 returndata = NetIO (&my_socket, host, url, cur_ptr, authdata, httpproto, suppressoutput);
892 if ((returndata == NULL) && (cur_ptr->netio_error != NET_ERR_OK)) {
893 cur_ptr->problem = 1;
896 /* url will be freed in the calling function. */
897 free (freeme); /* This is *host. */