summaryrefslogtreecommitdiff
path: root/gopher.c
diff options
context:
space:
mode:
Diffstat (limited to 'gopher.c')
-rw-r--r--gopher.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/gopher.c b/gopher.c
new file mode 100644
index 000000000..a112150ea
--- /dev/null
+++ b/gopher.c
@@ -0,0 +1,179 @@
+/* Gopher protocol routines
+
+ Copyright (C) 2002 James A. Morrison <ja2morri@uwaterloo.ca>
+ Copyright (C) 2000 Igor Khavkine <igor@twu.net>
+ This file is part of Gopherfs.
+
+ Gopherfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ Gopherfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <error.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "gopherfs.h"
+
+#include <hurd/hurd_types.h>
+#include <hurd/netfs.h>
+
+/* do a DNS lookup for NAME and store result in *ENT */
+error_t
+lookup_host (char *name, struct hostent **ent)
+{
+ error_t err;
+ struct hostent hentbuf;
+ int herr = 0;
+ char *tmpbuf;
+ size_t tmpbuflen = 512;
+
+ tmpbuf = (char *) malloc (tmpbuflen);
+ if (!tmpbuf)
+ return ENOMEM;
+
+ /* XXX: use getaddrinfo */
+ while ((err = gethostbyname_r (name, &hentbuf, tmpbuf,
+ tmpbuflen, ent, &herr)) == ERANGE)
+ {
+ tmpbuflen *= 2;
+ tmpbuf = (char *) realloc (tmpbuf, tmpbuflen);
+ if (!tmpbuf)
+ return ENOMEM;
+ }
+ free (tmpbuf);
+
+ if (!herr)
+ return EINVAL;
+
+ return 0;
+}
+
+/* store the remote socket in *FD after writing a gopher selector
+ to it */
+error_t
+open_selector (struct netnode * node, int *fd)
+{
+ error_t err;
+ struct hostent *server_ent;
+ struct sockaddr_in server;
+ ssize_t written;
+ size_t towrite;
+
+ err = lookup_host (node->server, &server_ent);
+ if (!err)
+ return err;
+ if (debug_flag)
+ fprintf (stderr, "trying to open %s:%d/%s\n", node->server,
+ node->port, node->selector);
+
+ server.sin_family = AF_INET;
+ server.sin_port = htons (node->port);
+ server.sin_addr = *(struct in_addr *) server_ent->h_addr;
+
+ *fd = socket (PF_INET, SOCK_STREAM, 0);
+ if (*fd == -1)
+ return errno;
+
+ err = connect (*fd, (struct sockaddr *) &server, sizeof (server));
+ if (err == -1)
+ return errno;
+
+ towrite = strlen (node->selector);
+ /* guard against EINTR failures */
+ written = TEMP_FAILURE_RETRY (write (*fd, node->selector, towrite));
+ written += TEMP_FAILURE_RETRY (write (*fd, "\r\n", 2));
+ if (written == -1 || written < (towrite + 2))
+ return errno;
+
+ return 0;
+}
+
+/* fetch a directory node from the gopher server
+ DIR should already be locked */
+error_t
+fill_dirnode (struct netnode * dir)
+{
+ error_t err = 0;
+ FILE *sel;
+ int sel_fd;
+ char *line;
+ size_t line_len;
+ struct node *nd, **prevp;
+
+ err = open_selector (dir, &sel_fd);
+ if (err)
+ return err;
+ if (debug_flag)
+ fprintf (stderr, "filling out dir %s\n", dir->name);
+ errno = 0;
+ sel = fdopen (sel_fd, "r");
+ if (!sel)
+ {
+ close (sel_fd);
+ return errno;
+ }
+
+ dir->noents = TRUE;
+ dir->num_ents = 0;
+ prevp = &dir->ents;
+ line = NULL;
+ line_len = 0;
+ while (getline (&line, &line_len, sel) >= 0)
+ {
+ char type, *name, *selector, *server;
+ unsigned short port;
+ char *tok, *endtok;
+
+ if (debug_flag)
+ fprintf (stderr, "%s\n", line);
+ if (*line == '.' || err)
+ break;
+
+ /* parse the gopher node description */
+ type = *line;
+ endtok = line + 1;
+ name = strsep (&endtok, "\t");
+ selector = strsep (&endtok, "\t");
+ server = strsep (&endtok, "\t");
+ port = (unsigned short) atoi (strsep (&endtok, "\t"));
+
+ nd = gopherfs_make_node (type, name, selector, server, port);
+ if (!nd)
+ {
+ err = ENOMEM;
+ break;
+ }
+ *prevp = nd;
+ nd->prevp = prevp;
+ prevp = &nd->next;
+
+ dir->num_ents++;
+ if (dir->noents)
+ dir->noents = FALSE;
+ }
+ free (line);
+
+ if (err)
+ {
+ fclose (sel);
+ return err;
+ }
+
+ return 0;
+}