home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
- /* A state machine to implement the Gopher protocol
- * Designed and Implemented by Lou Montulli circa '94
- */
-
- #include "mkutils.h"
- #include "ssl.h"
-
- /* Gopher types:
- */
- #define GTYPE_TEXT '0' /* text/plain */
- #define GTYPE_MENU '1' /* menu - becomes text/html */
- #define GTYPE_CSO '2' /* cso search - becomes text/html */
- #define GTYPE_ERROR '3' /* some sort of error */
- #define GTYPE_MACBINHEX '4' /* application/binhex */
- #define GTYPE_PCBINARY '5' /* application/octet-stream */
- #define GTYPE_UUENCODED '6' /* application/uuencoded */
- #define GTYPE_INDEX '7' /* search - becomes text/html */
- #define GTYPE_TELNET '8' /* maps to telnet URL */
- #define GTYPE_BINARY '9' /* application/octet-stream */
- #define GTYPE_GIF 'g' /* image/gif */
- #define GTYPE_HTML 'h' /* HTML URL */
- #define GTYPE_HTMLCAPS 'H' /* HTML URL Capitalized */
- #define GTYPE_INFO 'i' /* unselectable info */
- #define GTYPE_IMAGE 'I' /* image/gif */
- #define GTYPE_MIME 'm' /* not used */
- #define GTYPE_SOUND 's' /* audio/(wildcard) */
- #define GTYPE_TN3270 'T' /* maps to tn3270 program (url) */
- #define GTYPE_WWW 'w' /* W3 address */
- #define GTYPE_MPEG ';' /* video/mpeg */
-
- #include "mkgeturl.h" /* URL Struct */
- #include "mkstream.h"
- #include "mkformat.h" /* for File Format stuff (cinfo) */
- #include "mkgopher.h" /* function prototypes */
- #include "mktcp.h" /* connect, read, etc. */
- #include "mkparse.h" /* NET_ParseURL() */
- #include "mkremote.h" /* NET_RemoteHostLoad */
-
-
- #include "xp_error.h"
-
-
- /* for XP_GetString() */
- #include "xpgetstr.h"
- extern int XP_HTML_GOPHER_INDEX;
- extern int XP_HTML_CSO_SEARCH;
- extern int XP_PROGRESS_WAITREPLY_GOTHER;
- extern int MK_OUT_OF_MEMORY;
- extern int MK_SERVER_DISCONNECTED;
- extern int MK_TCP_READ_ERROR;
- extern int MK_TCP_WRITE_ERROR;
- extern int XP_ERRNO_EWOULDBLOCK;
- extern int XP_SERVER_RETURNED_NO_DATA;
- extern int MK_MALFORMED_URL_ERROR;
-
- #include "merrors.h"
-
- #define CD_OUTPUT_BUFFER_SIZE 4*1024
-
- typedef struct _GopherConData {
- int next_state; /* the next state or action to be taken */
- char *data_buf; /* temporary string storage */
- int32 data_buf_size; /* current size of the line buffer */
- char gopher_type; /* the gopher type */
- char *command; /* the request command */
- char *output_buf; /* temporary output buffer */
- NET_StreamClass *stream; /* The output stream */
- Bool pause_for_read; /* Pause now for next read? */
- Bool destroy_graph_progress; /* do we need to destroy graph progress? */
- int32 original_content_length; /* the content length at the time of
- * calling graph progress
- */
- char cso_last_char;
- TCP_ConData *tcp_con_data;
- } GopherConData;
-
- #define PUTSTRING(s) (*connection_data->stream->put_block) \
- (connection_data->stream, s, XP_STRLEN(s))
- #define PUTBLOCK(b, l) (*connection_data->stream->put_block) \
- (connection_data->stream, b, l)
- #define COMPLETE_STREAM (*connection_data->stream->complete) \
- (connection_data->stream)
- #define ABORT_STREAM(s) (*connection_data->stream->abort) \
- (connection_data->stream, s)
-
- /* helpful shortcut names
- */
- #define CD_NEXT_STATE connection_data->next_state
- #define CD_DATA_BUF connection_data->data_buf
- #define CD_DATA_BUF_SIZE connection_data->data_buf_size
- #define CD_STREAM connection_data->stream
- #define CD_GOPHER_TYPE connection_data->gopher_type
- #define CD_COMMAND connection_data->command
- #define CD_OUTPUT_BUF connection_data->output_buf
- #define CD_PAUSE_FOR_READ connection_data->pause_for_read
- #define CD_DESTROY_GRAPH_PROGRESS connection_data->destroy_graph_progress
- #define CD_ORIGINAL_CONTENT_LENGTH connection_data->original_content_length
- #define CD_CSO_LAST_CHAR connection_data->cso_last_char
- #define CD_TCP_CON_DATA connection_data->tcp_con_data
-
-
- #define CE_WINDOW_ID cur_entry->window_id
- #define CE_URL_S cur_entry->URL_s
- #define CE_STATUS cur_entry->status
- #define CE_SOCK cur_entry->socket
- #define CE_CON_SOCK cur_entry->con_sock
- #define CE_BYTES_RECEIVED cur_entry->bytes_received
-
- /* states for the state machine
- */
- #define GOPHER_BEGIN_CONNECT 1
- #define GOPHER_FINISH_CONNECT 2
- #define GOPHER_SEND_REQUEST 3
- #define GOPHER_BEGIN_TRANSFER 4
- #define GOPHER_TRANSFER_MENU 5
- #define GOPHER_TRANSFER_CSO 6
- #define GOPHER_TRANSFER_BINARY 7
- #define GOPHER_DONE 8
- #define GOPHER_ERROR_DONE 9
- #define GOPHER_FREE 10
-
-
- /* forward decl */
- PRIVATE int32 net_ProcessGopher(ActiveEntry * cur_entry);
-
- /* net_begin_gopher_menu
- *
- * adds little bits to the begining of the document so that is looks nice
- */
-
- PRIVATE int
- net_begin_gopher_menu(GopherConData * connection_data)
- {
-
- CD_NEXT_STATE = GOPHER_TRANSFER_MENU;
-
- XP_STRCPY(CD_OUTPUT_BUF, "<H1>Gopher Menu</H1>\n<PRE>");
- return(PUTSTRING(CD_OUTPUT_BUF));
-
- }
-
- /* parse the gopher menu type
- */
- PRIVATE int
- net_parse_menu (ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
- char gopher_type;
- char *name;
- char *gopher_path=0;
- char *port;
- char *ptr;
- char *line;
- char *host;
-
- CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUF,
- &CD_DATA_BUF_SIZE, &CD_PAUSE_FOR_READ);
-
- if(CE_STATUS == 0)
- {
- CD_NEXT_STATE = GOPHER_DONE;
- CD_PAUSE_FOR_READ = FALSE;
- if(CE_BYTES_RECEIVED < 4)
- {
- XP_STRCPY(CD_OUTPUT_BUF, XP_GetString( XP_SERVER_RETURNED_NO_DATA ) );
- PUTSTRING(CD_OUTPUT_BUF);
- }
-
- return(MK_DATA_LOADED);
- }
-
- /* if TCP error of if there is not a full line yet return
- */
- if(!line)
- {
- return CE_STATUS;
- }
- else if(CE_STATUS < 0)
- {
- NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
-
- /* return TCP error
- */
- return MK_TCP_READ_ERROR;
- }
-
- if(CE_STATUS > 1)
- {
- CE_BYTES_RECEIVED += CE_STATUS;
- FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED, CE_STATUS, CE_URL_S->content_length);
- }
-
- gopher_type = *line;
-
- if(gopher_type != '\0')
- ptr = line+1;
- else
- return(0);
-
- /* remove trailing spaces */
- XP_StripLine(line);
-
- TRACEMSG(("gopher_parse_menu: parsing line: %s\n",line));
-
- /* quit when just a dot is found on a line by itself
- */
- if(!XP_STRCMP(line,"."))
- {
- CD_NEXT_STATE = GOPHER_DONE;
- CD_PAUSE_FOR_READ = FALSE;
-
- if(CE_BYTES_RECEIVED < 4)
- {
- XP_STRCPY(CD_OUTPUT_BUF, XP_GetString( XP_SERVER_RETURNED_NO_DATA ) );
- PUTSTRING(CD_OUTPUT_BUF);
- }
-
- return(MK_DATA_LOADED);
- }
-
- if (gopher_type && *ptr)
- {
- name = ptr;
- gopher_path = XP_STRCHR(name, '\t');
- if (gopher_path)
- {
- *gopher_path++ = 0;
- host = XP_STRCHR(gopher_path, '\t');
- if (host)
- {
- *host++ = 0;
- port = XP_STRCHR(host, '\t');
- if (port)
- {
- char *tab;
-
- port[0] = ':'; /* fake the port no */
- tab = XP_STRCHR(port, '\t');
- if (tab)
- *tab++ = '\0';
-
- if (port[1]=='0' && port[2]=='\0')
- port[0] = 0;
- }
- }
- }
- }
-
- if(!gopher_path)
- {
- return(PUTSTRING(ptr)); /* keep going bad data */
- }
-
- /* Nameless files are a separator line */
- if (gopher_type == GTYPE_TEXT)
- {
- int i=0;
- while(XP_IS_SPACE(name[i])) i++;
- if(!XP_STRLEN(name))
- gopher_type = GTYPE_INFO;
- }
-
- /* these first few are grouped together with an
- * if else because they each have special needs
- * the rest of them fit nicely in the switch
- */
- if(gopher_type == GTYPE_ERROR)
- {
- /* ignore it */
- }
- else if(gopher_type == GTYPE_WWW)
- {
- /* points to URL */
- XP_STRCPY(CD_OUTPUT_BUF, "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-text\">");
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
-
- if(CE_STATUS < 0)
- return(CE_STATUS);
-
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"%s\">%s</A>\n", gopher_path, name);
- return(PUTSTRING(CD_OUTPUT_BUF));
-
- }
- else if(gopher_type == GTYPE_INFO)
- {
- /* Information or separator line */
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, " %s\n", name);
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
- else if(gopher_type == GTYPE_TELNET)
- {
- if (*gopher_path)
- {
- char * temp = NET_Escape(gopher_path, URL_XALPHAS);
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"telnet://%s@%s/\"><IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-telnet\"> %s</A>\n", temp, host, name);
- FREE(temp);
- }
- else
- {
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"telnet://%s/\"><IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-telnet\"> %s</A>\n", host, name);
- }
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
- else if(gopher_type == GTYPE_TN3270)
- {
- if (gopher_path && *gopher_path)
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"tn3270://%s@%s/\"><IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-telnet\"> %s</A>\n", gopher_path, host, name);
- else
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"tn3270://%s/\"><IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-telnet\"> %s</A>\n", host, name);
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
- else
- {
- if(host && gopher_path)
- {
- if(!XP_STRCMP(host, "error.host:70"))
- {
-
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "Error: %s", name);
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
-
- }
- else
- {
- char * newpath = NET_Escape(gopher_path, URL_PATH);
-
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"gopher://%s/%c%s\">", host, gopher_type, newpath);
-
- switch(gopher_type) {
- case GTYPE_TEXT:
- case GTYPE_HTML:
- case GTYPE_HTMLCAPS:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 "
- "SRC=\"internal-gopher-text\">");
- break;
- case GTYPE_MENU:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-menu\">");
- break;
- case GTYPE_CSO:
- case GTYPE_INDEX:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-index\">");
- break;
- case GTYPE_PCBINARY:
- case GTYPE_UUENCODED:
- case GTYPE_BINARY:
- case GTYPE_MACBINHEX:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-binary\">");
- break;
- case GTYPE_IMAGE:
- case GTYPE_GIF:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-image\">");
- break;
- case GTYPE_SOUND:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-sound\">");
- break;
- case GTYPE_MPEG:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-movie\">");
- break;
-
- case GTYPE_MIME:
- default:
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-unknown\">");
- break;
- }
-
- PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)],
- CD_OUTPUT_BUFFER_SIZE
- - XP_STRLEN(CD_OUTPUT_BUF),
- " %s</A>\n", name);
- FREE(newpath);
-
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
- }
- }
-
- return(CE_STATUS); /* keep going */
-
- }
-
- /* net_begin_gopher_cso
- *
- * adds formatting to the front of the document to make
- * it look pretty
- */
- PRIVATE int
- net_begin_gopher_cso(GopherConData * connection_data)
- {
- XP_STRCPY(CD_OUTPUT_BUF, "<H1>CSO Search Results</H1>\n<PRE>");
-
- CD_NEXT_STATE = GOPHER_TRANSFER_CSO;
-
- return(PUTSTRING(CD_OUTPUT_BUF));
- }
-
- /*
- * parse cso output
- *
- * receives date from the cso server and turns it into HTML
- */
- PRIVATE int
- net_parse_cso (ActiveEntry * cur_entry)
- {
- char *line;
- char *colon1, *colon2;
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
-
- CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUF,
- &CD_DATA_BUF_SIZE, &CD_PAUSE_FOR_READ);
-
- if(CE_STATUS == 0)
- CE_STATUS = MK_SERVER_DISCONNECTED;
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_SERVER_DISCONNECTED);
-
- /* if TCP error of if there is not a full line yet return
- */
- if(CE_STATUS < 0)
- {
- NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
-
- /* return TCP error
- */
- return MK_TCP_READ_ERROR;
- }
- else if(!line)
- {
- return CE_STATUS;
- }
-
- if(CE_STATUS > 1)
- {
- CE_BYTES_RECEIVED += CE_STATUS;
- FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED, CE_STATUS, CE_URL_S->content_length);
- }
-
- /* a line beginning with a 2 means the end of data
- */
- if (*line == '2')
- {
- CD_NEXT_STATE = GOPHER_DONE;
- CD_PAUSE_FOR_READ = FALSE;
- return(MK_DATA_LOADED);
- }
-
- /* if it starts with a 5 something went wrong, print
- * out the error message
- */
- if (*line == '5')
- {
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<H2>%s</H2>", line+4);
- PUTSTRING(CD_OUTPUT_BUF);
- CD_NEXT_STATE = GOPHER_DONE;
- CD_PAUSE_FOR_READ = FALSE;
- return(MK_DATA_LOADED);
- }
-
- if(*line == '-')
- {
- colon1 = XP_STRCHR(line,':');
- if(colon1)
- colon2 = XP_STRCHR(colon1+1, ':');
- else
- colon2 = NULL;
-
- if(colon2 != NULL)
- {
- if (*(colon2-1) != CD_CSO_LAST_CHAR)
- { /* print seperator */
- XP_STRCPY(CD_OUTPUT_BUF, "</PRE><H2>");
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
-
- if(CE_STATUS > -1)
- CE_STATUS = PUTSTRING(colon2+1);
- XP_STRCPY(CD_OUTPUT_BUF, "\n");
- if(CE_STATUS > -1)
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
-
- if (*(colon2-1) != CD_CSO_LAST_CHAR)
- {
- /* end seperator */
- XP_STRCPY(CD_OUTPUT_BUF, "</H2><PRE>");
- if(CE_STATUS > -1)
- CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
- }
-
- /* remember the last char so that we can
- * tell when the sequence number changes
- */
- CD_CSO_LAST_CHAR = *(colon2-1);
-
- } /* end if colon2 */
- } /* end if *line == '-' */
-
- return(1); /* keep going */
-
- } /* end of procedure */
-
- /* display the page that allows searching of a gopher index
- */
- PRIVATE int
- net_display_index_splash_screen (ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
- char *address_copy = 0;
-
- StrAllocCopy(address_copy, CE_URL_S->address);
- NET_UnEscape(address_copy);
-
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, XP_GetString(XP_HTML_GOPHER_INDEX), address_copy, address_copy);
- PUTSTRING(CD_OUTPUT_BUF);
-
- COMPLETE_STREAM;
-
- FREE(address_copy);
-
- return(0);
- }
-
-
- /* parse CSO server output
- */
- PRIVATE int
- net_display_cso_splash_screen (ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
- char *address_copy = 0;
-
- StrAllocCopy(address_copy, CE_URL_S->address);
- NET_UnEscape(address_copy);
-
- PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, XP_GetString(XP_HTML_CSO_SEARCH), address_copy, address_copy);
-
- PUTSTRING(CD_OUTPUT_BUF);
-
- COMPLETE_STREAM;
-
- FREE(address_copy);
-
- return(0);
- }
-
- /* send the request to get the data
- */
- PRIVATE int
- net_send_gopher_request (ActiveEntry * cur_entry)
- {
-
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
-
- TRACEMSG(("MKGopher: Connected, writing command `%s' to socket %d\n",
- CD_COMMAND, CE_SOCK));
-
- CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, CD_COMMAND, XP_STRLEN(CD_COMMAND));
-
- NET_Progress (CE_WINDOW_ID, XP_GetString(XP_PROGRESS_WAITREPLY_GOTHER));
-
- /* start the graph progress indicator
- */
- FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
- CD_DESTROY_GRAPH_PROGRESS = TRUE; /* we will need to destroy it */
- CD_ORIGINAL_CONTENT_LENGTH = CE_URL_S->content_length;
-
- CD_NEXT_STATE = GOPHER_BEGIN_TRANSFER;
- CD_PAUSE_FOR_READ = TRUE;
-
- if(CE_STATUS < 0)
- CE_STATUS = MK_TCP_WRITE_ERROR;
-
- return(CE_STATUS); /* everything OK */
-
- } /* end GopherLoad */
-
-
- /* pull down data in binary mode
- */
- PRIVATE int
- net_pull_gopher_data(ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
- unsigned int write_ready, read_size;
-
- /* check to see if the stream is ready for writing
- */
- write_ready = (*CD_STREAM->is_write_ready)(CD_STREAM);
-
- if(write_ready < 1)
- {
- CD_PAUSE_FOR_READ = TRUE;
- return(0); /* wait until we are ready to write */
- }
- else if(write_ready < (unsigned int) NET_Socket_Buffer_Size)
- {
- read_size = write_ready;
- }
- else
- {
- read_size = NET_Socket_Buffer_Size;
- }
-
- CE_STATUS = PR_Read(CE_SOCK, NET_Socket_Buffer, read_size);
-
- if(CE_STATUS == 0)
- { /* all done */
- CD_NEXT_STATE = GOPHER_DONE;
- CE_STATUS = MK_DATA_LOADED;
- }
- else if(CE_STATUS > 0)
- {
- CE_BYTES_RECEIVED += CE_STATUS;
- FE_GraphProgress(CE_WINDOW_ID,
- CE_URL_S,
- CE_BYTES_RECEIVED,
- CE_STATUS,
- CE_URL_S->content_length);
- CE_STATUS = PUTBLOCK(NET_Socket_Buffer, CE_STATUS);
- CD_PAUSE_FOR_READ = TRUE;
- }
- else
- {
- /* status less than zero
- */
- int rv = PR_GetError();
- if(rv == PR_WOULD_BLOCK_ERROR)
- {
- CD_PAUSE_FOR_READ = TRUE;
- return 0;
- }
- CE_STATUS = (rv < 0) ? rv : (-rv);
- }
-
- return(CE_STATUS);
-
- }
-
- /* a list of dis-allowed ports for gopher
- * connections
- */
- PRIVATE int bad_ports_table[] = {
- 1, 7, 9, 11 , 13, 15, 17, 19, 20,
- 21, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
- 103, 104, 109, 110, 111, 113, 115, 117, 119,
- 135, 143, 512, 513, 514, 515, 526, 530, 531, 532,
- 540, 556, 601, 6000, 0 };
-
- /* begin the load by initializing data structures and calling Process Gopher
- */
- PRIVATE int32
- net_GopherLoad (ActiveEntry * cur_entry)
- {
- char gopher_type; /* type */
- char * path; /* the URL path */
- char * gopher_path; /* the gopher path */
- char * gopher_host;
- char * query; /* holds the '?' query string */
- char *ptr;
- GopherConData * connection_data; /* state data for this connection */
-
- /* check for illegal gopher port numbers and
- * return invalid URL for those
- */
- gopher_host = NET_ParseURL(CE_URL_S->address, GET_HOST_PART);
- if(gopher_host)
- {
- int port_number;
- int i;
- char *colon = XP_STRCHR(gopher_host, ':');
-
- if(colon)
- {
- colon++; /* now it points to a port number */
-
- port_number = XP_ATOI(colon);
-
- /* disallow well known ports */
- for(i=0; bad_ports_table[i]; i++)
- if(port_number == bad_ports_table[i])
- {
- char *error_msg = NET_ExplainErrorDetails(
- MK_MALFORMED_URL_ERROR,
- CE_URL_S->address);
- if(error_msg)
- FE_Alert(CE_WINDOW_ID, error_msg);
- FREE(gopher_host);
- return(MK_MALFORMED_URL_ERROR);
- }
- }
- FREE(gopher_host);
- }
-
-
- /* malloc space for connection data
- */
- connection_data = XP_NEW(GopherConData);
- if(!connection_data)
- {
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
- return(MK_OUT_OF_MEMORY);
- }
- XP_MEMSET(connection_data, 0, sizeof(GopherConData)); /* zero it */
-
- CE_SOCK = NULL;
-
- cur_entry->con_data = connection_data;
-
- CD_OUTPUT_BUF = (char*) XP_ALLOC(CD_OUTPUT_BUFFER_SIZE);
- if(!CD_OUTPUT_BUF)
- {
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
- return(MK_OUT_OF_MEMORY);
- }
-
- /* Get entity type, and gopher_path string.
- */
- path = NET_ParseURL(CE_URL_S->address, GET_PATH_PART);
-
- TRACEMSG(("GopherLoad: Got path: %s\n",path));
-
- if(*path && *path != '/')
- {
- gopher_type = *path;
- gopher_path = path+1;
- }
- else if ((*path=='/') && (*(path+1))) /* past first slash slash */
- {
- gopher_type = *(path+1); /* get gopher_type */
- gopher_path = path+2; /* go past slash and gopher_type */
- }
- else /* no path or just slash */
- {
- /* special case!!!
- * if the URL looks like gopher://host/?search term
- * treat it as a text file and append the search term
- */
- if(XP_STRCHR(CE_URL_S->address, '?'))
- {
- gopher_type = '0';
- }
- else
- {
- gopher_type = '1'; /* menus are the default */
- }
- if(*path == '\0')
- gopher_path = path;
- else
- gopher_path = path+1;
- }
-
- CD_GOPHER_TYPE = gopher_type; /* set for later use */
-
- TRACEMSG(("URL: %s\ngopher_type: %c\nGopher_Path: %s\n",
- CE_URL_S->address, gopher_type, gopher_path));
-
- /* We now have the gopher type so we can safely set up
- * the Stream stack since we know what type will be
- * returned ahead of time
- */
- switch(gopher_type) {
- case GTYPE_HTML: /* all HTML types */
- case GTYPE_HTMLCAPS:
- case GTYPE_MENU:
- case GTYPE_CSO:
- case GTYPE_INDEX:
- StrAllocCopy(CE_URL_S->content_type, TEXT_HTML);
- break;
- case GTYPE_TEXT:
- StrAllocCopy(CE_URL_S->content_type, TEXT_PLAIN);
- break;
- case GTYPE_MACBINHEX:
- StrAllocCopy(CE_URL_S->content_type, APPLICATION_BINHEX);
- break;
- case GTYPE_PCBINARY:
- case GTYPE_BINARY:
- case GTYPE_MIME:
- StrAllocCopy(CE_URL_S->content_type, APPLICATION_OCTET_STREAM);
- break;
- case GTYPE_UUENCODED:
- StrAllocCopy(CE_URL_S->content_type, APPLICATION_UUENCODE);
- break;
- case GTYPE_TELNET:
- case GTYPE_TN3270:
- #ifdef MOZILLA_CLIENT
- /* do the telnet and return */
- return(NET_RemoteHostLoad(cur_entry));
- #else
- XP_ASSERT(0);
- break;
- #endif /* MOZILLA_CLIENT */
- case GTYPE_GIF:
- case GTYPE_IMAGE:
- StrAllocCopy(CE_URL_S->content_type, IMAGE_GIF);
- break;
- case GTYPE_SOUND:
- StrAllocCopy(CE_URL_S->content_type, AUDIO_BASIC);
- break;
- case GTYPE_MPEG:
- StrAllocCopy(CE_URL_S->content_type, VIDEO_MPEG);
- break;
- default:
- StrAllocCopy(CE_URL_S->content_type, TEXT_PLAIN);
- break;
- }
-
- CD_STREAM = NET_StreamBuilder(cur_entry->format_out, CE_URL_S, CE_WINDOW_ID);
-
- if (!CD_STREAM)
- {
- CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
- return(MK_UNABLE_TO_CONVERT);
- }
-
- TRACEMSG(("MKGopher: Stream now set up \n"));
-
- /* now do all the special handling of each type that
- * doesn't involve just a direct get
- */
- switch(gopher_type) {
- case GTYPE_INDEX:
- /* Search is allowed */
- query = XP_STRCHR(CE_URL_S->address, '?'); /* Look for search string */
-
- if (!query || !query[1]) { /* No search defined */
- /* Display "cover page" */
- net_display_index_splash_screen(cur_entry);
- CD_NEXT_STATE = GOPHER_DONE;
- return -1; /* Local function only */
- }
-
- *query++ = 0; /* Go past '?' */
-
- StrAllocCopy(CD_COMMAND, NET_UnEscape(gopher_path));
-
- StrAllocCat(CD_COMMAND, "\t");
-
- /* Remove plus signs */
- for (ptr=query; *ptr; ptr++)
- if (*ptr == '+')
- *ptr = ' ';
-
- StrAllocCat(CD_COMMAND, NET_UnEscape(query));
-
- *(query-1) = '?'; /* set it back to the way it was */
- break;
-
- case GTYPE_CSO:
- /* Search is allowed */
- query = XP_STRCHR(CE_URL_S->address, '?'); /* Look for search string */
-
- if (!query || !query[1]) /* No search required */
- {
- /* Display "cover page" */
- net_display_cso_splash_screen(cur_entry);
- CE_STATUS = MK_DATA_LOADED;
- return -1; /* Local function only */
- }
- *query++ = 0; /* Go past '?' */
-
- StrAllocCopy(CD_COMMAND, "query ");
-
- /* Remove plus signs */
- for (ptr=query; *ptr; ptr++)
- if (*ptr == '+')
- *ptr = ' ';
-
- StrAllocCat(CD_COMMAND, (char *)NET_UnEscape(query));
-
- *(query-1) = '?'; /* set it back to the way it was */
-
- break;
-
- case GTYPE_TEXT:
- /* Look for search string */
- query = XP_STRCHR(CE_URL_S->address, '?');
-
- /* special case!!!
- * if a query exist treat it special, send
- * the query instead
- */
- if(query)
- {
- *query++ = 0; /* Go past '?' */
-
- /* Remove plus signs */
- for (ptr=query; *ptr; ptr++)
- if (*ptr == '+')
- *ptr = ' ';
-
- StrAllocCopy(CD_COMMAND, (char *)NET_UnEscape(query));
-
- *(query-1) = '?'; /* set it back to the way it was */
- }
- else if(*path != '\0')
- {
- StrAllocCopy(CD_COMMAND, (char*)NET_UnEscape(gopher_path));
- }
- else
- {
- StrAllocCopy(CD_COMMAND, "/");
- }
- break;
-
- default: /* all other types besides index and CSO */
- if(*path == '\0')
- StrAllocCopy(CD_COMMAND, "/");
- else
- StrAllocCopy(CD_COMMAND, (char*)NET_UnEscape(gopher_path));
- }
-
- FREE_AND_CLEAR(path); /* NET_Parse malloc'd the path string */
-
- /* protect against other protocol attacks by limiting
- * the command to a single line. Terminate the command
- * at any \n or \r
- */
- if(XP_STRCHR(CD_COMMAND, '\n') || XP_STRCHR(CD_COMMAND, '\r'))
- {
- char *error_msg = NET_ExplainErrorDetails(MK_MALFORMED_URL_ERROR,
- CE_URL_S->address);
- if(error_msg)
- FE_Alert(CE_WINDOW_ID, error_msg);
- return(MK_MALFORMED_URL_ERROR);
- }
-
- StrAllocCat(CD_COMMAND, CRLF); /* finish off the command */
-
- CD_NEXT_STATE = GOPHER_BEGIN_CONNECT;
-
- return(net_ProcessGopher(cur_entry));
- }
-
- /* NET_ProcessGopher
- * completes the data transfer; is called from NET_ProcessNet()
- *
- * returns negative when complete
- */
- PRIVATE int32
- net_ProcessGopher(ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
-
- TRACEMSG(("Entered ProcessGopher: gopher_type=%c\n",CD_GOPHER_TYPE));
-
- CD_PAUSE_FOR_READ = FALSE;
-
- while(!CD_PAUSE_FOR_READ)
- {
- TRACEMSG(("ProcessGopher: in switch with state: #%d\n",CD_NEXT_STATE));
-
- switch(CD_NEXT_STATE) {
-
- case GOPHER_BEGIN_CONNECT:
- /* Set up a socket to the server for the data:
- */
- TRACEMSG(("MKGopher: Setting up net connection\n"));
-
- CE_STATUS = NET_BeginConnect(CE_URL_S->address,
- CE_URL_S->IPAddressString,
- "Gopher",
- 70,
- &CE_SOCK,
- FALSE,
- &CD_TCP_CON_DATA,
- CE_WINDOW_ID,
- &CE_URL_S->error_msg,
- cur_entry->socks_host,
- cur_entry->socks_port);
-
- if(CE_SOCK != NULL)
- NET_TotalNumberOfOpenConnections++;
-
- if(CE_STATUS == MK_CONNECTED)
- {
- CD_NEXT_STATE = GOPHER_SEND_REQUEST;
- NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
- }
- else if(CE_STATUS > -1)
- {
- CD_NEXT_STATE = GOPHER_FINISH_CONNECT;
- CD_PAUSE_FOR_READ = TRUE;
- CE_CON_SOCK = CE_SOCK; /* set so we select on it */
- NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
- }
- break;
-
- case GOPHER_FINISH_CONNECT:
- CE_STATUS = NET_FinishConnect(CE_URL_S->address,
- "Gopher",
- 70,
- &CE_SOCK,
- &CD_TCP_CON_DATA,
- CE_WINDOW_ID,
- &CE_URL_S->error_msg);
- if(CE_STATUS == MK_CONNECTED)
- {
- CD_NEXT_STATE = GOPHER_SEND_REQUEST;
- NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
- CE_CON_SOCK = NULL; /* reset so we don't select on it */
- NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
- }
- else
- {
-
- /* unregister the old CE_SOCK from the select list
- * and register the new value in the case that it changes
- */
- if(CE_CON_SOCK != CE_SOCK)
- {
- NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
- CE_CON_SOCK = CE_SOCK;
- NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
- }
-
- CD_PAUSE_FOR_READ = TRUE;
- }
- break;
-
- case GOPHER_SEND_REQUEST:
- CE_STATUS = net_send_gopher_request(cur_entry);
- break;
-
- case GOPHER_BEGIN_TRANSFER:
- if(CD_GOPHER_TYPE == GTYPE_MENU || CD_GOPHER_TYPE == GTYPE_INDEX)
- CE_STATUS = net_begin_gopher_menu(connection_data);
- else if(CD_GOPHER_TYPE == GTYPE_CSO)
- CE_STATUS = net_begin_gopher_cso(connection_data);
- else
- CD_NEXT_STATE = GOPHER_TRANSFER_BINARY;
- break;
-
- case GOPHER_TRANSFER_MENU:
- CE_STATUS = net_parse_menu(cur_entry);
- break;
-
- case GOPHER_TRANSFER_CSO:
- CE_STATUS = net_parse_cso(cur_entry);
- break;
-
- case GOPHER_TRANSFER_BINARY:
- CE_STATUS = net_pull_gopher_data(cur_entry);
- break;
-
- case GOPHER_DONE:
- NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
- PR_Close(CE_SOCK);
- NET_TotalNumberOfOpenConnections--;
- COMPLETE_STREAM;
- CD_NEXT_STATE = GOPHER_FREE;
- break;
-
- case GOPHER_ERROR_DONE:
- if(CE_SOCK != NULL)
- {
- NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
- NET_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
- NET_ClearDNSSelect(CE_WINDOW_ID, CE_SOCK);
- PR_Close(CE_SOCK);
- NET_TotalNumberOfOpenConnections--;
- }
- if(CD_STREAM)
- ABORT_STREAM(CE_STATUS);
- CD_NEXT_STATE = GOPHER_FREE;
- break;
-
- case GOPHER_FREE:
- if(CD_DESTROY_GRAPH_PROGRESS)
- FE_GraphProgressDestroy(CE_WINDOW_ID,
- CE_URL_S,
- CD_ORIGINAL_CONTENT_LENGTH,
- CE_BYTES_RECEIVED);
- FREEIF(CD_COMMAND);
- FREEIF(CD_STREAM); /* don't forget the stream */
- FREEIF(CD_OUTPUT_BUF);
- FREEIF(CD_DATA_BUF);
- if(CD_TCP_CON_DATA)
- NET_FreeTCPConData(CD_TCP_CON_DATA);
-
- FREE(connection_data);
- return(-1); /* all done */
-
- } /* end switch(NEXT_STATE) */
-
-
- if(CE_STATUS < 0 && CD_NEXT_STATE != GOPHER_DONE &&
- CD_NEXT_STATE != GOPHER_ERROR_DONE &&
- CD_NEXT_STATE != GOPHER_FREE)
- {
- CD_NEXT_STATE = GOPHER_ERROR_DONE;
- CD_PAUSE_FOR_READ = FALSE;
- }
-
- } /* end while */
-
- return(CE_STATUS);
- }
-
- /* abort the connection in progress
- */
- PRIVATE int32
- net_InterruptGopher(ActiveEntry * cur_entry)
- {
- GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
-
- CD_NEXT_STATE = GOPHER_ERROR_DONE;
- CE_STATUS = MK_INTERRUPTED;
-
- return(net_ProcessGopher(cur_entry));
- }
-
- /* Free any memory
- */
- PRIVATE void
- net_CleanupGopher(void)
- {
- /* nothing to free */
- return;
- }
-
- MODULE_PRIVATE void
- NET_InitGopherProtocol(void)
- {
- static NET_ProtoImpl gopher_proto_impl;
-
- gopher_proto_impl.init = net_GopherLoad;
- gopher_proto_impl.process = net_ProcessGopher;
- gopher_proto_impl.interrupt = net_InterruptGopher;
- gopher_proto_impl.cleanup = net_CleanupGopher;
-
- NET_RegisterProtocolImplementation(&gopher_proto_impl, GOPHER_TYPE_URL);
- }
-