home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkgopher.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  35.7 KB  |  1,171 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* A state machine to implement the Gopher protocol
  19.  * Designed and Implemented by Lou Montulli circa '94
  20.  */
  21.  
  22. #include "mkutils.h"
  23. #include "ssl.h"
  24.  
  25. /* Gopher types:
  26.  */
  27. #define GTYPE_TEXT            '0'        /* text/plain */
  28. #define GTYPE_MENU            '1'        /* menu - becomes text/html */
  29. #define GTYPE_CSO             '2'        /* cso search - becomes text/html */
  30. #define GTYPE_ERROR        '3'     /* some sort of error */
  31. #define GTYPE_MACBINHEX    '4'     /* application/binhex */
  32. #define GTYPE_PCBINARY     '5'     /* application/octet-stream */
  33. #define GTYPE_UUENCODED    '6'     /* application/uuencoded */
  34. #define GTYPE_INDEX        '7'     /* search - becomes text/html */
  35. #define GTYPE_TELNET       '8'     /* maps to telnet URL */
  36. #define GTYPE_BINARY       '9'     /* application/octet-stream */
  37. #define GTYPE_GIF          'g'     /* image/gif */
  38. #define GTYPE_HTML         'h'     /* HTML URL */
  39. #define GTYPE_HTMLCAPS     'H'     /* HTML URL Capitalized */
  40. #define GTYPE_INFO         'i'     /* unselectable info */
  41. #define GTYPE_IMAGE        'I'     /* image/gif */
  42. #define GTYPE_MIME         'm'     /* not used */
  43. #define GTYPE_SOUND        's'     /* audio/(wildcard) */
  44. #define GTYPE_TN3270       'T'     /* maps to tn3270 program (url) */
  45. #define GTYPE_WWW          'w'     /* W3 address */
  46. #define GTYPE_MPEG         ';'     /* video/mpeg */
  47.  
  48. #include "mkgeturl.h"       /* URL Struct */
  49. #include "mkstream.h"
  50. #include "mkformat.h"       /* for File Format stuff (cinfo) */
  51. #include "mkgopher.h"       /* function prototypes */
  52. #include "mktcp.h"          /* connect, read, etc. */
  53. #include "mkparse.h"        /* NET_ParseURL() */
  54. #include "mkremote.h"       /* NET_RemoteHostLoad */
  55.  
  56.  
  57. #include "xp_error.h"
  58.  
  59.  
  60. /* for XP_GetString() */
  61. #include "xpgetstr.h"
  62. extern int XP_HTML_GOPHER_INDEX;
  63. extern int XP_HTML_CSO_SEARCH;
  64. extern int XP_PROGRESS_WAITREPLY_GOTHER;
  65. extern int MK_OUT_OF_MEMORY;
  66. extern int MK_SERVER_DISCONNECTED;
  67. extern int MK_TCP_READ_ERROR;
  68. extern int MK_TCP_WRITE_ERROR;
  69. extern int XP_ERRNO_EWOULDBLOCK;
  70. extern int XP_SERVER_RETURNED_NO_DATA;
  71. extern int MK_MALFORMED_URL_ERROR;
  72.  
  73. #include "merrors.h"
  74.  
  75. #define CD_OUTPUT_BUFFER_SIZE 4*1024
  76.  
  77. typedef struct _GopherConData {
  78.     int              next_state;        /* the next state or action to be taken */
  79.     char            *data_buf;          /* temporary string storage */
  80.     int32            data_buf_size;     /* current size of the line buffer */
  81.     char             gopher_type;             /* the gopher type */
  82.     char            *command;           /* the request command */
  83.     char            *output_buf;        /* temporary output buffer */
  84.     NET_StreamClass *stream;            /* The output stream */
  85.     Bool          pause_for_read;    /* Pause now for next read? */
  86.     Bool destroy_graph_progress;  /* do we need to destroy graph progress? */
  87.     int32   original_content_length; /* the content length at the time of
  88.                                       * calling graph progress
  89.                                       */
  90.     char             cso_last_char;
  91.     TCP_ConData      *tcp_con_data;
  92. } GopherConData;
  93.  
  94. #define PUTSTRING(s)     (*connection_data->stream->put_block) \
  95.                                    (connection_data->stream, s, XP_STRLEN(s))
  96. #define PUTBLOCK(b, l)   (*connection_data->stream->put_block) \
  97.                                    (connection_data->stream, b, l)
  98. #define COMPLETE_STREAM  (*connection_data->stream->complete) \
  99.                                    (connection_data->stream)
  100. #define ABORT_STREAM(s)  (*connection_data->stream->abort) \
  101.                                    (connection_data->stream, s)
  102.  
  103. /* helpful shortcut names
  104.  */
  105. #define CD_NEXT_STATE           connection_data->next_state
  106. #define CD_DATA_BUF             connection_data->data_buf
  107. #define CD_DATA_BUF_SIZE        connection_data->data_buf_size
  108. #define CD_STREAM               connection_data->stream
  109. #define CD_GOPHER_TYPE            connection_data->gopher_type
  110. #define CD_COMMAND              connection_data->command
  111. #define CD_OUTPUT_BUF              connection_data->output_buf
  112. #define CD_PAUSE_FOR_READ       connection_data->pause_for_read
  113. #define CD_DESTROY_GRAPH_PROGRESS   connection_data->destroy_graph_progress
  114. #define CD_ORIGINAL_CONTENT_LENGTH  connection_data->original_content_length
  115. #define CD_CSO_LAST_CHAR            connection_data->cso_last_char
  116. #define CD_TCP_CON_DATA            connection_data->tcp_con_data
  117.  
  118.  
  119. #define CE_WINDOW_ID            cur_entry->window_id
  120. #define CE_URL_S                cur_entry->URL_s
  121. #define CE_STATUS               cur_entry->status
  122. #define CE_SOCK                 cur_entry->socket
  123. #define CE_CON_SOCK             cur_entry->con_sock
  124. #define CE_BYTES_RECEIVED       cur_entry->bytes_received
  125.  
  126. /* states for the state machine
  127.  */
  128. #define GOPHER_BEGIN_CONNECT    1
  129. #define GOPHER_FINISH_CONNECT   2
  130. #define GOPHER_SEND_REQUEST     3
  131. #define GOPHER_BEGIN_TRANSFER   4
  132. #define GOPHER_TRANSFER_MENU    5
  133. #define GOPHER_TRANSFER_CSO     6
  134. #define GOPHER_TRANSFER_BINARY  7
  135. #define GOPHER_DONE             8
  136. #define GOPHER_ERROR_DONE       9
  137. #define GOPHER_FREE             10
  138.  
  139.  
  140. /* forward decl */
  141. PRIVATE int32 net_ProcessGopher(ActiveEntry * cur_entry);
  142.  
  143. /* net_begin_gopher_menu
  144.  *
  145.  * adds little bits to the begining of the document so that is looks nice
  146.  */
  147.  
  148. PRIVATE int 
  149. net_begin_gopher_menu(GopherConData * connection_data)
  150. {
  151.  
  152.     CD_NEXT_STATE = GOPHER_TRANSFER_MENU;
  153.  
  154.     XP_STRCPY(CD_OUTPUT_BUF, "<H1>Gopher Menu</H1>\n<PRE>");
  155.        return(PUTSTRING(CD_OUTPUT_BUF));
  156.  
  157. }
  158.  
  159. /* parse the gopher menu type
  160.  */
  161. PRIVATE int 
  162. net_parse_menu (ActiveEntry * cur_entry)
  163. {
  164.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  165.     char gopher_type;
  166.     char *name;
  167.     char *gopher_path=0; 
  168.     char *port;         
  169.     char *ptr;
  170.     char *line;
  171.     char *host;        
  172.  
  173.     CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUF,
  174.                                                 &CD_DATA_BUF_SIZE, &CD_PAUSE_FOR_READ);
  175.  
  176.     if(CE_STATUS == 0)
  177.       {
  178.         CD_NEXT_STATE = GOPHER_DONE;
  179.         CD_PAUSE_FOR_READ = FALSE;
  180.         if(CE_BYTES_RECEIVED < 4)
  181.           {
  182.             XP_STRCPY(CD_OUTPUT_BUF, XP_GetString( XP_SERVER_RETURNED_NO_DATA ) );
  183.             PUTSTRING(CD_OUTPUT_BUF);
  184.           }
  185.  
  186.            return(MK_DATA_LOADED);
  187.       }
  188.  
  189.     /* if TCP error of if there is not a full line yet return
  190.      */
  191.     if(!line)
  192.       {
  193.          return CE_STATUS;
  194.       }
  195.     else if(CE_STATUS < 0)
  196.       {
  197.         NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  198.  
  199.         /* return TCP error
  200.          */
  201.         return MK_TCP_READ_ERROR;
  202.       }
  203.  
  204.     if(CE_STATUS > 1)
  205.       {
  206.         CE_BYTES_RECEIVED += CE_STATUS;
  207.         FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED, CE_STATUS, CE_URL_S->content_length);
  208.       }
  209.  
  210.     gopher_type = *line;
  211.  
  212.     if(gopher_type != '\0')
  213.         ptr = line+1;
  214.     else
  215.         return(0);
  216.  
  217.     /* remove trailing spaces */
  218.     XP_StripLine(line);
  219.  
  220.     TRACEMSG(("gopher_parse_menu: parsing line: %s\n",line));
  221.  
  222.     /* quit when just a dot is found on a line by itself
  223.      */
  224.     if(!XP_STRCMP(line,"."))
  225.       {
  226.         CD_NEXT_STATE = GOPHER_DONE;
  227.         CD_PAUSE_FOR_READ = FALSE;
  228.  
  229.         if(CE_BYTES_RECEIVED < 4)
  230.           {
  231.             XP_STRCPY(CD_OUTPUT_BUF, XP_GetString( XP_SERVER_RETURNED_NO_DATA ) );
  232.             PUTSTRING(CD_OUTPUT_BUF);
  233.           }
  234.             
  235.         return(MK_DATA_LOADED);
  236.       }
  237.  
  238.     if (gopher_type && *ptr) 
  239.       {
  240.         name = ptr;
  241.         gopher_path = XP_STRCHR(name, '\t');
  242.         if (gopher_path) 
  243.           {
  244.             *gopher_path++ = 0;
  245.             host = XP_STRCHR(gopher_path, '\t');
  246.             if (host) 
  247.               {
  248.                 *host++ = 0;    
  249.                 port = XP_STRCHR(host, '\t');
  250.                 if (port) 
  251.                   {
  252.                     char *tab;
  253.  
  254.                     port[0] = ':';    /* fake the port no */
  255.                     tab = XP_STRCHR(port, '\t');
  256.                     if (tab) 
  257.                         *tab++ = '\0';  
  258.  
  259.                     if (port[1]=='0' && port[2]=='\0')
  260.                         port[0] = 0;    
  261.                 } 
  262.              } 
  263.         } 
  264.     } 
  265.  
  266.     if(!gopher_path)
  267.       {
  268.         return(PUTSTRING(ptr)); /* keep going bad data */
  269.       }
  270.         
  271.     /* Nameless files are a separator line */
  272.     if (gopher_type == GTYPE_TEXT) 
  273.       {
  274.         int i=0;
  275.         while(XP_IS_SPACE(name[i])) i++;
  276.         if(!XP_STRLEN(name))
  277.         gopher_type = GTYPE_INFO;
  278.       }
  279.  
  280.     /* these first few are grouped together with an
  281.      * if else because they each have special needs 
  282.      * the rest of them fit nicely in the switch
  283.      */
  284.     if(gopher_type == GTYPE_ERROR)
  285.       {
  286.         /* ignore it */
  287.       }
  288.     else if(gopher_type == GTYPE_WWW)
  289.       {
  290.         /* points to URL */
  291.         XP_STRCPY(CD_OUTPUT_BUF, "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-text\">");
  292.         CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  293.  
  294.         if(CE_STATUS < 0)
  295.             return(CE_STATUS);
  296.  
  297.         PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"%s\">%s</A>\n", gopher_path, name);
  298.         return(PUTSTRING(CD_OUTPUT_BUF));
  299.  
  300.       }
  301.     else if(gopher_type == GTYPE_INFO)
  302.       {
  303.             /* Information or separator line */
  304.         PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "       %s\n", name);
  305.         CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  306.        }
  307.     else if(gopher_type == GTYPE_TELNET)
  308.       {
  309.         if (*gopher_path) 
  310.           {
  311.             char * temp = NET_Escape(gopher_path, URL_XALPHAS);
  312.             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);
  313.             FREE(temp);
  314.           }
  315.         else 
  316.           {
  317.             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);
  318.           }
  319.         CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  320.       }
  321.     else if(gopher_type == GTYPE_TN3270)
  322.       {
  323.         if (gopher_path && *gopher_path) 
  324.             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);
  325.         else 
  326.             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);
  327.         CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  328.       }
  329.     else
  330.       {
  331.         if(host && gopher_path) 
  332.            {
  333.             if(!XP_STRCMP(host, "error.host:70"))
  334.               {
  335.                 
  336.                 PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "Error: %s", name);
  337.                 CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  338.                 
  339.               }
  340.             else
  341.               {
  342.                 char * newpath = NET_Escape(gopher_path, URL_PATH);
  343.     
  344.                 PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<A HREF=\"gopher://%s/%c%s\">", host, gopher_type, newpath);
  345.     
  346.                 switch(gopher_type) {
  347.                     case GTYPE_TEXT:
  348.                     case GTYPE_HTML:
  349.                     case GTYPE_HTMLCAPS:
  350.                       PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  351.                                         CD_OUTPUT_BUFFER_SIZE 
  352.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  353.                                         "<IMG ALIGN=absbottom BORDER=0 "
  354.                                         "SRC=\"internal-gopher-text\">");
  355.                             break;
  356.                     case GTYPE_MENU:
  357.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  358.                                         CD_OUTPUT_BUFFER_SIZE 
  359.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  360.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-menu\">");
  361.                             break;
  362.                     case GTYPE_CSO:
  363.                     case GTYPE_INDEX:
  364.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  365.                                         CD_OUTPUT_BUFFER_SIZE 
  366.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  367.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-index\">");
  368.                             break;
  369.                     case GTYPE_PCBINARY:
  370.                     case GTYPE_UUENCODED:
  371.                     case GTYPE_BINARY:
  372.                     case GTYPE_MACBINHEX:
  373.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  374.                                         CD_OUTPUT_BUFFER_SIZE 
  375.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  376.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-binary\">");
  377.                             break;
  378.                     case GTYPE_IMAGE:
  379.                     case GTYPE_GIF:
  380.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  381.                                         CD_OUTPUT_BUFFER_SIZE 
  382.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  383.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-image\">");
  384.                             break;
  385.                     case GTYPE_SOUND:
  386.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  387.                                         CD_OUTPUT_BUFFER_SIZE 
  388.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  389.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-sound\">");
  390.                             break;
  391.                     case GTYPE_MPEG:
  392.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  393.                                         CD_OUTPUT_BUFFER_SIZE 
  394.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  395.                                                 "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-movie\">");
  396.                             break;
  397.     
  398.                     case GTYPE_MIME:
  399.                     default:
  400.                             PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  401.                                         CD_OUTPUT_BUFFER_SIZE 
  402.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  403.                                             "<IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-unknown\">");
  404.                             break;
  405.                 }
  406.  
  407.                 PR_snprintf(&CD_OUTPUT_BUF[XP_STRLEN(CD_OUTPUT_BUF)], 
  408.                                         CD_OUTPUT_BUFFER_SIZE 
  409.                                                 - XP_STRLEN(CD_OUTPUT_BUF),
  410.                                         " %s</A>\n", name);
  411.                 FREE(newpath);
  412.  
  413.                 CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  414.               }    
  415.           }
  416.       }
  417.  
  418.     return(CE_STATUS); /* keep going */
  419.  
  420. }
  421.  
  422. /* net_begin_gopher_cso
  423.  *
  424.  * adds formatting to the front of the document to make
  425.  * it look pretty
  426.  */
  427. PRIVATE int
  428. net_begin_gopher_cso(GopherConData * connection_data)
  429. {
  430.     XP_STRCPY(CD_OUTPUT_BUF, "<H1>CSO Search Results</H1>\n<PRE>");
  431.  
  432.     CD_NEXT_STATE = GOPHER_TRANSFER_CSO;
  433.  
  434.     return(PUTSTRING(CD_OUTPUT_BUF));
  435. }
  436.  
  437. /* 
  438.  * parse cso output
  439.  *
  440.  * receives date from the cso server and turns it into HTML
  441.  */
  442. PRIVATE int 
  443. net_parse_cso (ActiveEntry * cur_entry)
  444. {
  445.     char *line;
  446.     char *colon1, *colon2; 
  447.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  448.     
  449.     CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUF,
  450.                                                 &CD_DATA_BUF_SIZE, &CD_PAUSE_FOR_READ);
  451.  
  452.     if(CE_STATUS == 0)
  453.     CE_STATUS = MK_SERVER_DISCONNECTED;
  454.     CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_SERVER_DISCONNECTED);
  455.     
  456.     /* if TCP error of if there is not a full line yet return
  457.      */
  458.     if(CE_STATUS < 0)
  459.       {
  460.         NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  461.  
  462.         /* return TCP error
  463.          */
  464.         return MK_TCP_READ_ERROR;
  465.       }
  466.      else if(!line)
  467.       {
  468.          return CE_STATUS;
  469.       }
  470.  
  471.     if(CE_STATUS > 1)
  472.       { 
  473.         CE_BYTES_RECEIVED += CE_STATUS;
  474.         FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED, CE_STATUS, CE_URL_S->content_length);
  475.       }
  476.  
  477.     /* a line beginning with a 2 means the end of data
  478.      */
  479.     if (*line == '2')
  480.       {
  481.         CD_NEXT_STATE = GOPHER_DONE;
  482.         CD_PAUSE_FOR_READ = FALSE;
  483.         return(MK_DATA_LOADED);
  484.       }
  485.             
  486.     /* if it starts with a 5 something went wrong, print
  487.      * out the error message 
  488.      */
  489.     if (*line == '5') 
  490.       {
  491.         PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, "<H2>%s</H2>", line+4);
  492.         PUTSTRING(CD_OUTPUT_BUF);
  493.         CD_NEXT_STATE = GOPHER_DONE;
  494.         CD_PAUSE_FOR_READ = FALSE;
  495.         return(MK_DATA_LOADED);
  496.       }
  497.             
  498.     if(*line == '-') 
  499.       {
  500.         colon1 = XP_STRCHR(line,':');
  501.         if(colon1)
  502.             colon2 = XP_STRCHR(colon1+1, ':'); 
  503.         else
  504.             colon2 = NULL;
  505.     
  506.         if(colon2 != NULL)
  507.           {
  508.             if (*(colon2-1) != CD_CSO_LAST_CHAR)   
  509.               { /* print seperator */
  510.                 XP_STRCPY(CD_OUTPUT_BUF, "</PRE><H2>");
  511.                 CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  512.               }
  513.                     
  514.             if(CE_STATUS > -1)
  515.                 CE_STATUS = PUTSTRING(colon2+1);
  516.             XP_STRCPY(CD_OUTPUT_BUF, "\n");
  517.             if(CE_STATUS > -1)
  518.                 CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  519.                 
  520.             if (*(colon2-1) != CD_CSO_LAST_CHAR)   
  521.               {
  522.                 /* end seperator */
  523.                 XP_STRCPY(CD_OUTPUT_BUF, "</H2><PRE>");
  524.                 if(CE_STATUS > -1)
  525.                     CE_STATUS = PUTSTRING(CD_OUTPUT_BUF);
  526.               }
  527.                                     
  528.             /* remember the last char so that we can
  529.              * tell when the sequence number changes
  530.              */
  531.             CD_CSO_LAST_CHAR =  *(colon2-1);
  532.                 
  533.           } /* end if colon2 */
  534.       } /* end if *line == '-' */
  535.     
  536.     return(1); /* keep going */
  537.  
  538. } /* end of procedure */
  539.  
  540. /*  display the page that allows searching of a gopher index 
  541.  */
  542. PRIVATE int 
  543. net_display_index_splash_screen (ActiveEntry * cur_entry)
  544. {
  545.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  546.     char *address_copy = 0;
  547.  
  548.     StrAllocCopy(address_copy, CE_URL_S->address);
  549.     NET_UnEscape(address_copy);
  550.  
  551.     PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, XP_GetString(XP_HTML_GOPHER_INDEX), address_copy, address_copy);
  552.     PUTSTRING(CD_OUTPUT_BUF);
  553.  
  554.     COMPLETE_STREAM;
  555.  
  556.     FREE(address_copy);
  557.  
  558.     return(0);
  559. }
  560.  
  561.  
  562. /* parse CSO server output
  563. */
  564. PRIVATE int 
  565. net_display_cso_splash_screen (ActiveEntry * cur_entry)
  566. {
  567.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  568.     char *address_copy = 0;
  569.  
  570.     StrAllocCopy(address_copy, CE_URL_S->address);
  571.     NET_UnEscape(address_copy);
  572.  
  573.     PR_snprintf(CD_OUTPUT_BUF, CD_OUTPUT_BUFFER_SIZE, XP_GetString(XP_HTML_CSO_SEARCH), address_copy, address_copy);
  574.  
  575.        PUTSTRING(CD_OUTPUT_BUF);
  576.  
  577.     COMPLETE_STREAM;
  578.  
  579.     FREE(address_copy);
  580.  
  581.     return(0);
  582. }
  583.  
  584. /* send the request to get the data
  585.  */
  586. PRIVATE int
  587. net_send_gopher_request (ActiveEntry * cur_entry)
  588. {
  589.  
  590.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  591.  
  592.     TRACEMSG(("MKGopher: Connected, writing command `%s' to socket %d\n", 
  593.                             CD_COMMAND, CE_SOCK));
  594.  
  595.     CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, CD_COMMAND, XP_STRLEN(CD_COMMAND));
  596.  
  597.     NET_Progress (CE_WINDOW_ID, XP_GetString(XP_PROGRESS_WAITREPLY_GOTHER));
  598.  
  599.     /* start the graph progress indicator
  600.      */
  601.     FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
  602.     CD_DESTROY_GRAPH_PROGRESS = TRUE;  /* we will need to destroy it */
  603.     CD_ORIGINAL_CONTENT_LENGTH = CE_URL_S->content_length;
  604.  
  605.     CD_NEXT_STATE = GOPHER_BEGIN_TRANSFER;
  606.     CD_PAUSE_FOR_READ = TRUE;
  607.  
  608.     if(CE_STATUS < 0)
  609.         CE_STATUS = MK_TCP_WRITE_ERROR;
  610.     
  611.     return(CE_STATUS); /* everything OK */
  612.  
  613. } /* end GopherLoad */
  614.  
  615.  
  616. /* pull down data in binary mode
  617.  */
  618. PRIVATE int
  619. net_pull_gopher_data(ActiveEntry * cur_entry)
  620. {
  621.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  622.     unsigned int write_ready, read_size;
  623.  
  624.     /* check to see if the stream is ready for writing
  625.      */
  626.     write_ready = (*CD_STREAM->is_write_ready)(CD_STREAM);
  627.  
  628.     if(write_ready < 1)
  629.       {
  630.         CD_PAUSE_FOR_READ = TRUE;
  631.         return(0);  /* wait until we are ready to write */
  632.       }
  633.     else if(write_ready < (unsigned int) NET_Socket_Buffer_Size)
  634.       {
  635.         read_size = write_ready;
  636.       }
  637.     else
  638.       {
  639.         read_size = NET_Socket_Buffer_Size;
  640.       }
  641.  
  642.     CE_STATUS = PR_Read(CE_SOCK, NET_Socket_Buffer, read_size);
  643.  
  644.     if(CE_STATUS == 0)
  645.       {  /* all done */
  646.         CD_NEXT_STATE = GOPHER_DONE;
  647.         CE_STATUS = MK_DATA_LOADED;
  648.       }
  649.     else if(CE_STATUS > 0)
  650.       {
  651.         CE_BYTES_RECEIVED += CE_STATUS;
  652.         FE_GraphProgress(CE_WINDOW_ID, 
  653.                          CE_URL_S, 
  654.                          CE_BYTES_RECEIVED, 
  655.                          CE_STATUS, 
  656.                          CE_URL_S->content_length);
  657.         CE_STATUS = PUTBLOCK(NET_Socket_Buffer, CE_STATUS);
  658.         CD_PAUSE_FOR_READ = TRUE;
  659.       }
  660.     else
  661.       {
  662.         /* status less than zero
  663.          */
  664.         int rv = PR_GetError();
  665.         if(rv == PR_WOULD_BLOCK_ERROR)
  666.           {
  667.             CD_PAUSE_FOR_READ = TRUE;
  668.             return 0;
  669.           }
  670.         CE_STATUS = (rv < 0) ? rv : (-rv);
  671.       }
  672.    
  673.     return(CE_STATUS);
  674.  
  675. }
  676.  
  677. /* a list of dis-allowed ports for gopher
  678.  * connections
  679.  */
  680. PRIVATE int bad_ports_table[] = { 
  681. 1, 7, 9, 11 , 13, 15, 17, 19, 20,
  682. 21, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
  683. 103, 104, 109, 110, 111, 113, 115, 117, 119,
  684. 135, 143, 512, 513, 514, 515, 526, 530, 531, 532, 
  685. 540, 556, 601, 6000, 0 };
  686.  
  687. /* begin the load by initializing data structures and calling Process Gopher
  688.  */
  689. PRIVATE int32
  690. net_GopherLoad (ActiveEntry * cur_entry)
  691. {
  692.     char gopher_type;       /* type */
  693.     char * path;            /* the URL path */
  694.     char * gopher_path;        /* the gopher path */
  695.     char * gopher_host;
  696.     char * query;           /* holds the '?' query string */
  697.     char *ptr;
  698.     GopherConData * connection_data;    /* state data for this connection */
  699.  
  700.     /* check for illegal gopher port numbers and
  701.      * return invalid URL for those
  702.      */
  703.     gopher_host = NET_ParseURL(CE_URL_S->address, GET_HOST_PART);
  704.     if(gopher_host)
  705.       {
  706.         int port_number;
  707.         int i;
  708.         char *colon = XP_STRCHR(gopher_host, ':');
  709.  
  710.         if(colon)
  711.           {
  712.             colon++;  /* now it points to a port number */
  713.             
  714.             port_number = XP_ATOI(colon);
  715.  
  716.             /* disallow well known ports */
  717.             for(i=0; bad_ports_table[i]; i++)
  718.                 if(port_number == bad_ports_table[i])
  719.                     {
  720.                     char *error_msg = NET_ExplainErrorDetails(
  721.                                                         MK_MALFORMED_URL_ERROR, 
  722.                                                         CE_URL_S->address);
  723.                     if(error_msg)
  724.                         FE_Alert(CE_WINDOW_ID, error_msg);
  725.                     FREE(gopher_host);
  726.                     return(MK_MALFORMED_URL_ERROR);
  727.                     }
  728.           }
  729.         FREE(gopher_host);
  730.       }
  731.         
  732.  
  733.     /* malloc space for connection data 
  734.      */
  735.     connection_data = XP_NEW(GopherConData);
  736.     if(!connection_data)
  737.       {
  738.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  739.         return(MK_OUT_OF_MEMORY);
  740.       }
  741.     XP_MEMSET(connection_data, 0, sizeof(GopherConData));  /* zero it */
  742.  
  743.     CE_SOCK = NULL;
  744.  
  745.     cur_entry->con_data = connection_data;
  746.  
  747.     CD_OUTPUT_BUF = (char*) XP_ALLOC(CD_OUTPUT_BUFFER_SIZE);
  748.     if(!CD_OUTPUT_BUF)
  749.       {
  750.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  751.         return(MK_OUT_OF_MEMORY);
  752.       }
  753.    
  754.     /* Get entity type, and gopher_path string.
  755.      */        
  756.     path = NET_ParseURL(CE_URL_S->address, GET_PATH_PART);
  757.  
  758.     TRACEMSG(("GopherLoad: Got path: %s\n",path));
  759.  
  760.     if(*path && *path != '/') 
  761.       {
  762.         gopher_type = *path;   
  763.         gopher_path = path+1; 
  764.       }
  765.     else if ((*path=='/') && (*(path+1)))   /* past first slash slash */
  766.       {
  767.         gopher_type = *(path+1);  /* get gopher_type */
  768.         gopher_path = path+2;  /* go past slash and gopher_type */
  769.       }
  770.     else /* no path or just slash */
  771.       {
  772.         /* special case!!!
  773.          * if the URL looks like gopher://host/?search term
  774.          * treat it as a text file and append the search term
  775.          */
  776.         if(XP_STRCHR(CE_URL_S->address, '?'))
  777.           {
  778.             gopher_type = '0';
  779.           }
  780.         else
  781.           {
  782.             gopher_type = '1';      /* menus are the default */
  783.           }
  784.         if(*path == '\0')
  785.             gopher_path = path;    
  786.         else
  787.             gopher_path = path+1; 
  788.       }
  789.  
  790.     CD_GOPHER_TYPE = gopher_type; /* set for later use */
  791.  
  792.     TRACEMSG(("URL: %s\ngopher_type: %c\nGopher_Path: %s\n", 
  793.                     CE_URL_S->address, gopher_type, gopher_path));
  794.  
  795.     /* We now have the gopher type so we can safely set up
  796.      * the Stream stack since we know what type will be
  797.      * returned ahead of time
  798.      */
  799.     switch(gopher_type) {
  800.         case GTYPE_HTML:    /* all HTML types */
  801.         case GTYPE_HTMLCAPS:
  802.         case GTYPE_MENU:
  803.         case GTYPE_CSO:
  804.         case GTYPE_INDEX:
  805.             StrAllocCopy(CE_URL_S->content_type, TEXT_HTML);
  806.             break;
  807.         case GTYPE_TEXT:
  808.             StrAllocCopy(CE_URL_S->content_type, TEXT_PLAIN);
  809.             break;
  810.         case GTYPE_MACBINHEX:
  811.             StrAllocCopy(CE_URL_S->content_type, APPLICATION_BINHEX);
  812.             break;
  813.         case GTYPE_PCBINARY:
  814.         case GTYPE_BINARY:
  815.         case GTYPE_MIME:
  816.             StrAllocCopy(CE_URL_S->content_type, APPLICATION_OCTET_STREAM);
  817.             break;
  818.         case GTYPE_UUENCODED:
  819.             StrAllocCopy(CE_URL_S->content_type, APPLICATION_UUENCODE);
  820.             break;
  821.         case GTYPE_TELNET:
  822.         case GTYPE_TN3270:
  823. #ifdef MOZILLA_CLIENT
  824.             /* do the telnet and return */
  825.             return(NET_RemoteHostLoad(cur_entry));
  826. #else
  827.             XP_ASSERT(0);
  828.             break;
  829. #endif /* MOZILLA_CLIENT */
  830.         case GTYPE_GIF:
  831.         case GTYPE_IMAGE:
  832.             StrAllocCopy(CE_URL_S->content_type, IMAGE_GIF);
  833.             break;
  834.         case GTYPE_SOUND:
  835.             StrAllocCopy(CE_URL_S->content_type, AUDIO_BASIC);
  836.             break;
  837.         case GTYPE_MPEG:
  838.             StrAllocCopy(CE_URL_S->content_type, VIDEO_MPEG);
  839.             break;
  840.         default:
  841.             StrAllocCopy(CE_URL_S->content_type, TEXT_PLAIN);
  842.             break;
  843.     }
  844.  
  845.     CD_STREAM = NET_StreamBuilder(cur_entry->format_out, CE_URL_S, CE_WINDOW_ID);
  846.  
  847.     if (!CD_STREAM) 
  848.       {
  849.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
  850.         return(MK_UNABLE_TO_CONVERT);
  851.       }
  852.  
  853.     TRACEMSG(("MKGopher: Stream now set up \n"));
  854.  
  855.     /* now do all the special handling of each type that
  856.      * doesn't involve just a direct get 
  857.      */
  858.     switch(gopher_type) {
  859.         case GTYPE_INDEX:
  860.             /* Search is allowed */
  861.             query = XP_STRCHR(CE_URL_S->address, '?');  /* Look for search string */
  862.  
  863.             if (!query || !query[1]) {      /* No search defined */
  864.                 /* Display "cover page" */
  865.                 net_display_index_splash_screen(cur_entry);
  866.                 CD_NEXT_STATE = GOPHER_DONE;
  867.                 return -1;      /* Local function only */
  868.             }
  869.  
  870.             *query++ = 0;     /* Go past '?' */
  871.           
  872.             StrAllocCopy(CD_COMMAND, NET_UnEscape(gopher_path));
  873.  
  874.             StrAllocCat(CD_COMMAND, "\t");
  875.       
  876.             /* Remove plus signs */
  877.             for (ptr=query; *ptr; ptr++) 
  878.                 if (*ptr == '+') 
  879.                     *ptr = ' ';
  880.  
  881.             StrAllocCat(CD_COMMAND, NET_UnEscape(query));
  882.  
  883.             *(query-1) = '?';  /* set it back to the way it was */
  884.             break;
  885.  
  886.         case GTYPE_CSO:
  887.             /* Search is allowed */
  888.             query = XP_STRCHR(CE_URL_S->address, '?');      /* Look for search string */
  889.  
  890.             if (!query || !query[1])           /* No search required */
  891.               {
  892.                 /* Display "cover page" */
  893.                 net_display_cso_splash_screen(cur_entry);     
  894.                 CE_STATUS = MK_DATA_LOADED;
  895.                 return -1;      /* Local function only */
  896.               }
  897.             *query++ = 0;       /* Go past '?' */
  898.  
  899.             StrAllocCopy(CD_COMMAND, "query ");
  900.  
  901.             /* Remove plus signs */
  902.             for (ptr=query; *ptr; ptr++) 
  903.                 if (*ptr == '+') 
  904.                     *ptr = ' ';
  905.  
  906.             StrAllocCat(CD_COMMAND, (char *)NET_UnEscape(query));
  907.  
  908.             *(query-1) = '?';  /* set it back to the way it was */
  909.            
  910.             break;
  911.  
  912.         case GTYPE_TEXT:
  913.            /* Look for search string */
  914.             query = XP_STRCHR(CE_URL_S->address, '?');     
  915.  
  916.             /* special case!!!
  917.              * if a query exist treat it special, send 
  918.              * the query instead
  919.              */
  920.             if(query)
  921.               {
  922.                 *query++ = 0;       /* Go past '?' */
  923.  
  924.                 /* Remove plus signs */
  925.                 for (ptr=query; *ptr; ptr++) 
  926.                   if (*ptr == '+') 
  927.                     *ptr = ' ';
  928.  
  929.                 StrAllocCopy(CD_COMMAND, (char *)NET_UnEscape(query));
  930.                 
  931.                 *(query-1) = '?';  /* set it back to the way it was */
  932.               }    
  933.             else if(*path != '\0')
  934.               {
  935.                 StrAllocCopy(CD_COMMAND, (char*)NET_UnEscape(gopher_path));
  936.               }
  937.             else
  938.               {
  939.                 StrAllocCopy(CD_COMMAND, "/");
  940.               }
  941.             break;
  942.  
  943.         default:   /* all other types besides index and CSO */
  944.             if(*path == '\0')
  945.                 StrAllocCopy(CD_COMMAND, "/");
  946.             else
  947.                 StrAllocCopy(CD_COMMAND, (char*)NET_UnEscape(gopher_path));
  948.     }
  949.  
  950.     FREE_AND_CLEAR(path);  /* NET_Parse malloc'd the path string */
  951.  
  952.     /* protect against other protocol attacks by limiting
  953.      * the command to a single line.  Terminate the command
  954.      * at any \n or \r
  955.      */
  956.     if(XP_STRCHR(CD_COMMAND, '\n') || XP_STRCHR(CD_COMMAND, '\r'))
  957.       {
  958.            char *error_msg = NET_ExplainErrorDetails(MK_MALFORMED_URL_ERROR, 
  959.                                                   CE_URL_S->address);
  960.         if(error_msg)
  961.             FE_Alert(CE_WINDOW_ID, error_msg);
  962.         return(MK_MALFORMED_URL_ERROR);
  963.       }
  964.     
  965.     StrAllocCat(CD_COMMAND, CRLF); /* finish off the command */
  966.  
  967.     CD_NEXT_STATE = GOPHER_BEGIN_CONNECT;
  968.  
  969.     return(net_ProcessGopher(cur_entry));
  970. }
  971.  
  972. /* NET_ProcessGopher
  973.  *    completes the data transfer; is called from NET_ProcessNet()
  974.  *
  975.  * returns negative when complete
  976.  */
  977. PRIVATE int32
  978. net_ProcessGopher(ActiveEntry * cur_entry)
  979. {
  980.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  981.  
  982.     TRACEMSG(("Entered ProcessGopher: gopher_type=%c\n",CD_GOPHER_TYPE));
  983.  
  984.     CD_PAUSE_FOR_READ = FALSE;
  985.  
  986.     while(!CD_PAUSE_FOR_READ)
  987.       {
  988.         TRACEMSG(("ProcessGopher: in switch with state: #%d\n",CD_NEXT_STATE));
  989.  
  990.         switch(CD_NEXT_STATE) {
  991.  
  992.         case GOPHER_BEGIN_CONNECT:
  993.             /*  Set up a socket to the server for the data:
  994.             */
  995.             TRACEMSG(("MKGopher: Setting up net connection\n"));
  996.  
  997.             CE_STATUS = NET_BeginConnect(CE_URL_S->address, 
  998.                                          CE_URL_S->IPAddressString,
  999.                                          "Gopher", 
  1000.                                          70, 
  1001.                                          &CE_SOCK, 
  1002.                                          FALSE, 
  1003.                                          &CD_TCP_CON_DATA, 
  1004.                                          CE_WINDOW_ID,
  1005.                                          &CE_URL_S->error_msg,
  1006.                                          cur_entry->socks_host,
  1007.                                          cur_entry->socks_port);
  1008.  
  1009.             if(CE_SOCK != NULL)
  1010.                 NET_TotalNumberOfOpenConnections++;
  1011.  
  1012.             if(CE_STATUS == MK_CONNECTED)
  1013.                 {
  1014.                 CD_NEXT_STATE = GOPHER_SEND_REQUEST;
  1015.                 NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
  1016.               }
  1017.             else if(CE_STATUS > -1)
  1018.               {
  1019.                 CD_NEXT_STATE = GOPHER_FINISH_CONNECT;
  1020.                 CD_PAUSE_FOR_READ = TRUE;
  1021.                 CE_CON_SOCK = CE_SOCK; /* set so we select on it */
  1022.                 NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
  1023.               }
  1024.             break;
  1025.  
  1026.         case GOPHER_FINISH_CONNECT:
  1027.             CE_STATUS = NET_FinishConnect(CE_URL_S->address,
  1028.                                           "Gopher",
  1029.                                           70, 
  1030.                                           &CE_SOCK, 
  1031.                                           &CD_TCP_CON_DATA, 
  1032.                                           CE_WINDOW_ID,
  1033.                                           &CE_URL_S->error_msg);
  1034.             if(CE_STATUS == MK_CONNECTED)
  1035.               {
  1036.                 CD_NEXT_STATE = GOPHER_SEND_REQUEST;
  1037.                 NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
  1038.                 CE_CON_SOCK = NULL;  /* reset so we don't select on it */
  1039.                 NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
  1040.               }
  1041.             else
  1042.               {
  1043.  
  1044.                 /* unregister the old CE_SOCK from the select list
  1045.                  * and register the new value in the case that it changes
  1046.                  */
  1047.                 if(CE_CON_SOCK != CE_SOCK)
  1048.                   {
  1049.                     NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
  1050.                     CE_CON_SOCK = CE_SOCK;
  1051.                     NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
  1052.                   }
  1053.  
  1054.                 CD_PAUSE_FOR_READ = TRUE;
  1055.               }
  1056.             break;
  1057.  
  1058.             case GOPHER_SEND_REQUEST:
  1059.             CE_STATUS = net_send_gopher_request(cur_entry);
  1060.             break;
  1061.        
  1062.         case GOPHER_BEGIN_TRANSFER:
  1063.             if(CD_GOPHER_TYPE == GTYPE_MENU || CD_GOPHER_TYPE == GTYPE_INDEX)
  1064.                 CE_STATUS = net_begin_gopher_menu(connection_data);
  1065.             else if(CD_GOPHER_TYPE == GTYPE_CSO)
  1066.                 CE_STATUS = net_begin_gopher_cso(connection_data);
  1067.             else
  1068.                 CD_NEXT_STATE = GOPHER_TRANSFER_BINARY;
  1069.             break;
  1070.  
  1071.         case GOPHER_TRANSFER_MENU:
  1072.             CE_STATUS = net_parse_menu(cur_entry);
  1073.             break;
  1074.  
  1075.         case GOPHER_TRANSFER_CSO:
  1076.             CE_STATUS = net_parse_cso(cur_entry);
  1077.             break;
  1078.     
  1079.         case GOPHER_TRANSFER_BINARY:
  1080.             CE_STATUS = net_pull_gopher_data(cur_entry);
  1081.             break;
  1082.     
  1083.         case GOPHER_DONE:
  1084.             NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
  1085.             PR_Close(CE_SOCK);
  1086.             NET_TotalNumberOfOpenConnections--;
  1087.             COMPLETE_STREAM;
  1088.             CD_NEXT_STATE = GOPHER_FREE;
  1089.             break;
  1090.  
  1091.         case GOPHER_ERROR_DONE:
  1092.             if(CE_SOCK != NULL)
  1093.               {
  1094.                 NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
  1095.                 NET_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
  1096.                 NET_ClearDNSSelect(CE_WINDOW_ID, CE_SOCK);
  1097.                 PR_Close(CE_SOCK);
  1098.                 NET_TotalNumberOfOpenConnections--;
  1099.               }
  1100.             if(CD_STREAM)
  1101.                 ABORT_STREAM(CE_STATUS);
  1102.             CD_NEXT_STATE = GOPHER_FREE;
  1103.             break;
  1104.     
  1105.         case GOPHER_FREE:
  1106.             if(CD_DESTROY_GRAPH_PROGRESS)
  1107.                 FE_GraphProgressDestroy(CE_WINDOW_ID,
  1108.                                         CE_URL_S,
  1109.                                         CD_ORIGINAL_CONTENT_LENGTH,
  1110.                                         CE_BYTES_RECEIVED);
  1111.             FREEIF(CD_COMMAND);
  1112.             FREEIF(CD_STREAM);      /* don't forget the stream */
  1113.             FREEIF(CD_OUTPUT_BUF);
  1114.             FREEIF(CD_DATA_BUF);
  1115.             if(CD_TCP_CON_DATA)
  1116.                 NET_FreeTCPConData(CD_TCP_CON_DATA);
  1117.  
  1118.             FREE(connection_data);
  1119.             return(-1);  /* all done */
  1120.  
  1121.     } /* end switch(NEXT_STATE) */
  1122.  
  1123.  
  1124.     if(CE_STATUS < 0 && CD_NEXT_STATE != GOPHER_DONE && 
  1125.                 CD_NEXT_STATE != GOPHER_ERROR_DONE && 
  1126.                 CD_NEXT_STATE != GOPHER_FREE)
  1127.       {
  1128.         CD_NEXT_STATE = GOPHER_ERROR_DONE;
  1129.         CD_PAUSE_FOR_READ = FALSE; 
  1130.       }
  1131.     
  1132.     } /* end while */
  1133.  
  1134.     return(CE_STATUS);
  1135. }
  1136.  
  1137. /* abort the connection in progress
  1138.  */
  1139. PRIVATE int32
  1140. net_InterruptGopher(ActiveEntry * cur_entry)
  1141. {
  1142.     GopherConData * connection_data = (GopherConData *)cur_entry->con_data;
  1143.  
  1144.     CD_NEXT_STATE = GOPHER_ERROR_DONE;
  1145.     CE_STATUS = MK_INTERRUPTED;
  1146.  
  1147.     return(net_ProcessGopher(cur_entry));
  1148. }
  1149.  
  1150. /* Free any memory 
  1151.  */
  1152. PRIVATE void
  1153. net_CleanupGopher(void)
  1154. {
  1155.     /* nothing to free */
  1156.     return;
  1157. }
  1158.  
  1159. MODULE_PRIVATE void
  1160. NET_InitGopherProtocol(void)
  1161. {
  1162.     static NET_ProtoImpl gopher_proto_impl;
  1163.  
  1164.     gopher_proto_impl.init = net_GopherLoad;
  1165.     gopher_proto_impl.process = net_ProcessGopher;
  1166.     gopher_proto_impl.interrupt = net_InterruptGopher;
  1167.     gopher_proto_impl.cleanup = net_CleanupGopher;
  1168.  
  1169.     NET_RegisterProtocolImplementation(&gopher_proto_impl, GOPHER_TYPE_URL);
  1170. }
  1171.