Milán Major

netscape-revival

1 branch
Code

lib/libnet/mkstream.c

/*  -*- Mode: C; tab-width: 4 -*-
 *  Do stream related stuff like push data down the
 *  stream and determine which converter to use to
 *  set up the stream.
 */
#include "mkutils.h"
#include "mkstream.h"
#include "mkgeturl.h"
#include "mktcp.h"

#include "cvview.h"

#ifdef XP_UNIX
#include "cvextcon.h"
#endif


/* for XP_GetString() */
#include "xpgetstr.h"
extern int XP_ALERT_UNRECOGNIZED_ENCODING;
extern int XP_ALERT_CANTFIND_CONVERTER;


#ifdef PROFILE
#pragma profile on
#endif

typedef struct _net_ConverterStruct {
     NET_Converter * converter;        /* the converter function */
     char          * format_in;        /* the input (mime) type that the 
										* function accepts 
										*/
#ifdef NEW_DECODERS
     char          * encoding_in;      /* the input content-encoding that the
										* function accepts, or 0 for `any'.
										*/
#endif /* NEW_DECODERS */
     FO_Present_Types format_out;      /* the output type of the function */
     void          * data_obj;         /* a converter specific data object 
										* passed in at the
                                        * time of registration 
										*/
} net_ConverterStruct;

PRIVATE XP_List * net_converter_list=0;  /* a list of converters */
PRIVATE XP_List * net_decoder_list=0;    /* a list of decoders */

#define NET_MIME_EXACT_MATCH    1
#define NET_MIME_PARTIAL_MATCH  2
#define NET_WILDCARD_MATCH      3

/* will compare two mime times and return
 * NET_MIME_EXACT_MATCH if they match exactly
 * or NET_MIME_PARTIAL_MATCH if they match due
 * to a wildcard entry (image.*, or movie.*).
 *
 * if 'partial' equals '*' then
 * NET_WILDCARD_MATCH is returned
 * 
 * If there is no match then 0(zero) is returned
 */
PRIVATE int
net_compare_mime_types(char * absolute, char * partial)
{
   char *star;

   TRACEMSG(("StreamBuilder: Comparing %s and %s\n",absolute, partial));

   if(!strcasecomp(absolute, partial))
	  return(NET_MIME_EXACT_MATCH);

   if(!XP_STRCMP(partial, "*"))
	  return(NET_WILDCARD_MATCH);

   if((star = XP_STRCHR(partial, '*')) == 0)
	  return(0);  /* not a wildcard mime type */

   /* compare just the part before the slash star
    *
    */
   if(!strncasecomp(absolute, partial, (star-1)-partial))
	  return(NET_MIME_PARTIAL_MATCH);

   return(0); /* no match */
}

/* Find a converter routine to create a stream and return the stream struct
*/
PUBLIC NET_StreamClass * 
NET_StreamBuilder  (FO_Present_Types format_out,
                    URL_Struct  *URL_s,
                    MWContext   *context)
{
    net_ConverterStruct * cs_ptr;
    XP_List * ct_list_ptr = net_converter_list;
    XP_List * ce_list_ptr = net_decoder_list;

	assert (URL_s->content_type);
	
    TRACEMSG(("Entering StreamBuilder:\n\
     F-in: %s\n\
    F-out: %d\n\
      Enc: %s\n",URL_s->content_type,format_out, 
    (URL_s->content_encoding ? URL_s->content_encoding : "None Specified")));

#if 0
	/* First, determine whether there is a content-type converter for this
	   document; if there isn't one, then the only way to handle it is to
	   send it straight to disk, so try that.
	 */
	if (format_out != FO_SAVE_TO_DISK)
	  {
		while ((cs_ptr=(net_ConverterStruct *) XP_ListNextObject(ct_list_ptr))
			   != 0)
		  {
			if (format_out == cs_ptr->format_out &&
				net_compare_mime_types (URL_s->content_type,
										cs_ptr->format_in))
			  break;
		  }

		if (! cs_ptr)  /* There is no content-type converter; save it. */
		  format_out = FO_SAVE_TO_DISK;
		ct_list_ptr = net_converter_list;  /* Reset for next traversal. */
	  }
#endif


    /* if there are content encodings then
     * execute decoders first
     */
    /* go through list of converters and choose
     * the first exact or partial match we
     * find.  The order of the list
     * is guarenteed by the registration
     * routines
	 *
#ifdef NEW_DECODERS
	 * the format-out is compared as well.
#else
	 * Note that format-out is not consulting/compared when looking up
	 * encoding converters.
#endif
     */
	if (URL_s->content_encoding && *URL_s->content_encoding)
	  {
		while ((cs_ptr=(net_ConverterStruct *) XP_ListNextObject(ce_list_ptr))
			   != 0)
		  {
#ifdef NEW_DECODERS
			if (format_out == cs_ptr->format_out &&
				net_compare_mime_types (URL_s->content_type,
										cs_ptr->format_in) &&
				net_compare_mime_types (URL_s->content_encoding,
										cs_ptr->encoding_in))
#else /* !NEW_DECODERS */
			  /* the old way */
			if (net_compare_mime_types (URL_s->content_encoding,
										cs_ptr->format_in))
#endif /* !NEW_DECODERS */
			  {
				return ((NET_StreamClass *)
						((*cs_ptr->converter)
						 (format_out, cs_ptr->data_obj, URL_s, context)));
			  }
		  }

#ifndef NEW_DECODERS
		/* #### This clause is getting tripped by running FO_PRESENT
		   on foo.tar.Z - in that case, the URL has an encoding, but we
		   shoudn't match it.  Really the test should be, "is this encoding
		   known", not "did this encoding match."  Fix this! */


		/* If we get here, then this document has a content-encoding that
		   we don't have a decoder for.  If it's not one of the encodings
		   that we *know* we don't need a decoder for, then this is likely
		   to be a problem, so warn the user.  (But only in FO_PRESENT -
		   it's safe to save unknown encodings to disk, etc.)
		 */
		if (format_out == FO_PRESENT &&
			strcasecomp (URL_s->content_encoding, ENCODING_7BIT) &&
			strcasecomp (URL_s->content_encoding, ENCODING_8BIT) &&
			strcasecomp (URL_s->content_encoding, ENCODING_BINARY))
		  {
			char *msg = (char *) XP_ALLOC (strlen (URL_s->content_encoding)
										   + 100);
			if (msg)
			  {
				XP_STRCPY (msg, XP_GetString(XP_ALERT_UNRECOGNIZED_ENCODING));
				XP_STRCAT (msg, URL_s->content_encoding);
				XP_STRCAT (msg, "'.");
				FE_Alert (context, msg);
				FREE (msg);
			  }

			/* Now throw away the encoding so that we don't warn twice. */
			FREE (URL_s->content_encoding);
			URL_s->content_encoding = 0;
		  }
#endif /* !NEW_DECODERS */
	  }

	/* now search for content-type converters
     */
    /* go through list of converters and choose
     * the first exact or partial match we
     * find.  The order of the list
     * is guarenteed by the registration
     * routines
     */
    while((cs_ptr=(net_ConverterStruct *) XP_ListNextObject(ct_list_ptr)) != 0)
      {
        if(format_out == cs_ptr->format_out)
            if(net_compare_mime_types(URL_s->content_type, cs_ptr->format_in))
              {
				return( (NET_StreamClass *) (*cs_ptr->converter) (format_out, 
                                             	cs_ptr->data_obj, URL_s, context));
	          }
      }

    TRACEMSG(("Alert! did not find a converter or decoder\n"));
    FE_Alert(context, XP_GetString(XP_ALERT_CANTFIND_CONVERTER));

    return(0);  /* Uh-oh. didn't find a converter.  VERY VERY BAD! */
}

/* prints out all presentation mime types during successive calls
 * call with first equal true to reset to the beginning
 * and with first equal false to print out the next
 * converter in the list.  Returns zero when all converters
 * are printed out.
 */
MODULE_PRIVATE char *
XP_ListNextPresentationType(Bool first)
{
    static XP_List * converter_list_ptr;
    net_ConverterStruct * converter;

    /* reset list pointers 
     */
    if(first)
        converter_list_ptr = net_converter_list;

    /* print out next converter if there are any more
     */
    converter = (net_ConverterStruct *) XP_ListNextObject(converter_list_ptr);

    if(converter && converter->format_out == FO_PRESENT)
	return(converter->format_in);

    /* else */
    return(NULL);
}
	
/* prints out all encoding mime types during successive calls
 * call with first equal true to reset to the beginning
 * and with first equal false to print out the next
 * converter in the list.  Returns zero when all converters
 * are printed out.
 */
MODULE_PRIVATE char *
XP_ListNextEncodingType(Bool first)
{
    static XP_List * decoder_list_ptr;
    net_ConverterStruct * converter;

    /* reset list pointers
     */
    if(first)
      {
        decoder_list_ptr = net_decoder_list;
      }

    converter = (net_ConverterStruct *) XP_ListNextObject(decoder_list_ptr);

    if(converter)
        return(converter->format_in);

    /* else */
    return(NULL);
}
      
PRIVATE void
net_RegisterGenericConverterOrDecoder(XP_List        ** conv_list,
							  		  char            * format_in,
#ifdef NEW_DECODERS
							  		  char            * encoding_in,
#endif /* NEW_DECODERS */
                              		  FO_Present_Types  format_out,
                              		  void            * data_obj,
                              		  NET_Converter   * converter_func)
{
    XP_List * list_ptr = *conv_list;
    net_ConverterStruct * converter_struct_ptr;
    net_ConverterStruct * new_converter_struct;
    int f_in_type;

    /* check for an existing converter with the same format_in and format_out 
     * and replace it if found.
     *
     * if the list is NULL XP_ListNextObject will return 0
     */
    while((converter_struct_ptr = (net_ConverterStruct *) XP_ListNextObject(list_ptr)) != 0)
      {
	    if(format_out == converter_struct_ptr->format_out)
	        if(!strcasecomp(format_in, converter_struct_ptr->format_in)
#ifdef NEW_DECODERS
			   && ((!encoding_in && !converter_struct_ptr->encoding_in) ||
				   (encoding_in && converter_struct_ptr->encoding_in &&
					!XP_STRCMP (encoding_in,
								converter_struct_ptr->encoding_in)))
#endif /* NEW_DECODERS */
			   )
	          {
		        /* replace this converter entry with
		         * the one passed in
		         */
		        converter_struct_ptr->data_obj = data_obj;
		        converter_struct_ptr->converter = converter_func;
		        return;
	          }
       }

    /* find out the type of entry format_in is
     */
    f_in_type = NET_MIME_EXACT_MATCH;
        if(XP_STRCHR(format_in, '*'))
      {
	    if(!XP_STRCMP(format_in, "*"))
	        f_in_type = NET_WILDCARD_MATCH;
	    else
	        f_in_type = NET_MIME_PARTIAL_MATCH;
      }

    /* if existing converter not found/replaced
     * add this converter to the list in
     * its order of importance so that we can
     * take the first exact or partial fit
     */ 
    new_converter_struct = XP_NEW(net_ConverterStruct);
    if(!new_converter_struct)
	    return;

    new_converter_struct->format_in = 0;
    StrAllocCopy(new_converter_struct->format_in, format_in);
#ifdef NEW_DECODERS
    new_converter_struct->encoding_in = (encoding_in
										 ? XP_STRDUP (encoding_in) : 0);
#endif /* NEW_DECODERS */
    new_converter_struct->format_out = format_out;
    new_converter_struct->converter = converter_func;
    new_converter_struct->data_obj = data_obj;

    if(!*conv_list)
      {
	    /* create the list and add this object
	     * don't worry about order since there
	     * can't be any 
  	     */
	    *conv_list = XP_ListNew();
	    if(!*conv_list)
	        return; /* big error */

 	    XP_ListAddObject(*conv_list, new_converter_struct);
	    return;
      }

    /* if it is an exact match type 
     * then we can just add it to the beginning
     * of the list
     */
    if(f_in_type == NET_MIME_EXACT_MATCH)
      {
	    XP_ListAddObject(*conv_list, new_converter_struct);
	    TRACEMSG(("Adding Converter to Beginning of list"));
      }
    else if(f_in_type == NET_MIME_PARTIAL_MATCH)
      {
	    /* this is a partial wildcard of the form (image/(star))
         * store it at the end of all the exact match pairs
         */
        list_ptr = *conv_list;
        while((converter_struct_ptr = (net_ConverterStruct *) XP_ListNextObject(list_ptr)) != 0)
          {
            if(XP_STRCHR(converter_struct_ptr->format_in, '*'))
	          {
		        XP_ListInsertObject (*conv_list, 
									 converter_struct_ptr, 
									 new_converter_struct); 
		        return;
              }
          }
		/* else no * objects in list */
	    XP_ListAddObjectToEnd(*conv_list, new_converter_struct);
	    TRACEMSG(("Adding Converter to Middle of list"));
      }
    else
      {
	    /* this is a very wild wildcard match of the form "*" only.
         * It has the least precedence so store it at the end
	     * of the list.  (There must always be at least one
	     * item in the list (see above))
	     */
	    XP_ListAddObjectToEnd(*conv_list, new_converter_struct);
	    TRACEMSG(("Adding Converter to end of list"));
      }

    return;  /* end */
}

#ifdef NEW_DECODERS
struct net_encoding_converter
{
  char *encoding_in;
  void *data_obj;
  NET_Converter * converter_func;
};

static struct net_encoding_converter *net_all_encoding_converters;
static int net_encoding_converter_size = 0;
static int net_encoding_converter_fp = 0;

#endif /* NEW_DECODERS */

/* register a routine to decode a stream
 */
PUBLIC void
NET_RegisterEncodingConverter(char *encoding_in,
                              void          * data_obj,
                              NET_Converter * converter_func)
{

#ifdef NEW_DECODERS
	/* Don't register anything right now, but remember that we have a
	   decoder for this so that we can register it hither and yon at
	   a later date.
	 */
  if (net_encoding_converter_size <= net_encoding_converter_fp)
	{
	  net_encoding_converter_size += 10;
	  if (net_all_encoding_converters)
		net_all_encoding_converters = (struct net_encoding_converter *)
		  XP_REALLOC (net_all_encoding_converters,
					  net_encoding_converter_size
					  * sizeof (*net_all_encoding_converters));
	  else
		net_all_encoding_converters = (struct net_encoding_converter *)
		  XP_CALLOC (net_encoding_converter_size,
					 sizeof (*net_all_encoding_converters));
	}

  net_all_encoding_converters [net_encoding_converter_fp].encoding_in
	= XP_STRDUP (encoding_in);
  net_all_encoding_converters [net_encoding_converter_fp].data_obj
	= data_obj;
  net_all_encoding_converters [net_encoding_converter_fp].converter_func
	= converter_func;
  net_encoding_converter_fp++;

#else /* ! NEW_DECODERS */

  /* the old way */
    net_RegisterGenericConverterOrDecoder(&net_decoder_list,
                                          encoding_in,
                                          0, /* format_out is unused */
                                          data_obj,
                                          converter_func);

#endif /* !NEW_DECODERS */
}


#ifdef MOZILLA_CLIENT

#ifdef NEW_DECODERS
void
NET_RegisterAllEncodingConverters (char *format_in,
								   FO_Present_Types format_out)
{
  int i;

#ifdef XP_UNIX
  /* On Unix at least, there should be *some* registered... */
  assert (net_encoding_converter_fp > 0);
#endif

  for (i = 0; i < net_encoding_converter_fp; i++)
	{
	  char *encoding_in = net_all_encoding_converters [i].encoding_in;
	  void *data_obj = net_all_encoding_converters [i].data_obj;
	  NET_Converter *func = net_all_encoding_converters [i].converter_func;

	  net_RegisterGenericConverterOrDecoder (&net_decoder_list,
											 format_in,
											 encoding_in,
											 format_out,
											 data_obj,
											 func);

	  net_RegisterGenericConverterOrDecoder (&net_decoder_list,
											 format_in,
											 encoding_in,
											 format_out | FO_CACHE_ONLY,
											 data_obj,
											 func);

	  net_RegisterGenericConverterOrDecoder (&net_decoder_list,
											 format_in,
											 encoding_in,
											 format_out | FO_ONLY_FROM_CACHE,
											 data_obj,
											 func);
	}
}

#endif /* NEW_DECODERS */
#endif /* MOZILLA_CLIENT */




/*  Register a routine to convert between format_in and format_out
 *
 */
#if defined(XP_WIN) && defined(MOZILLA_CLIENT)
/*  Reroute XP handling of streams for windows.
 */
PUBLIC void
NET_RegisterContentTypeConverter (char          * format_in,
                                  FO_Present_Types format_out,
                                  void          * data_obj,
                                  NET_Converter * converter_func)
{
    PMS_RegisterContentTypeConverter(format_in, format_out,
        data_obj, converter_func, FALSE);
}

PUBLIC void
RealNET_RegisterContentTypeConverter (char          * format_in,
                                  FO_Present_Types format_out,
                                  void          * data_obj,
                                  NET_Converter * converter_func)
#else
PUBLIC void
NET_RegisterContentTypeConverter (char          * format_in,
                                  FO_Present_Types format_out,
                                  void          * data_obj,
                                  NET_Converter * converter_func)
#endif /* XP_WIN */
{
    net_RegisterGenericConverterOrDecoder(&net_converter_list,
                                          format_in,
#ifdef NEW_DECODERS
										  0,
#endif /* NEW_DECODERS */
                                          format_out,
                                          data_obj,
                                          converter_func);

}

#ifdef MOZILLA_CLIENT
#ifdef XP_UNIX

/* register a mime type and a command to be executed
 *
 * if stream_block_size is zero then the data will be completely
 * pooled and the external viewer called with the filename.
 * if stream_block_size is greater than zero the viewer will be
 * called with a popen and the stream_block_size will be used as
 * the maximum size of each buffer to pass to the viewer
 */
MODULE_PRIVATE void
NET_RegisterExternalViewerCommand(char * format_in, 
								  char * system_command, 
								  unsigned int stream_block_size) 
{
    CV_ExtViewStruct * new_obj = XP_NEW(CV_ExtViewStruct);

    if(!new_obj)
	   return;

    XP_MEMSET(new_obj, 0, sizeof(CV_ExtViewStruct));

    /* make a new copy of the command so it can be passed
     * as the data object
     */
    StrAllocCopy(new_obj->system_command, system_command);
    new_obj->stream_block_size = stream_block_size;

    /* register the routine
     */
    NET_RegisterContentTypeConverter(format_in, FO_PRESENT, new_obj, NET_ExtViewerConverter);

#ifdef NEW_DECODERS
	/* Also register the content-encoding converters on this type.
	   Those get registered only on types we know how to display,
	   either internally or externally - and don't get registered
	   on types we know nothing about.
	 */
	NET_RegisterAllEncodingConverters (format_in, FO_PRESENT);
#endif
}

/* removes all external viewer commands
 */
PUBLIC void
NET_ClearExternalViewerConverters(void)
{
    XP_List * list_ptr = net_converter_list;
    net_ConverterStruct * converter_struct_ptr;


	while((converter_struct_ptr = 
						(net_ConverterStruct *) XP_ListNextObject(list_ptr)))
	  {
		if(converter_struct_ptr->converter == NET_ExtViewerConverter)
		  {
			list_ptr = net_converter_list; /* reset to beginning */
			XP_ListRemoveObject(list_ptr, converter_struct_ptr);
			
			FREE(converter_struct_ptr->data_obj);
			FREE(converter_struct_ptr->format_in);
#ifdef NEW_DECODERS
			FREE(converter_struct_ptr->encoding_in);
#endif /* NEW_DECODERS */
			FREE(converter_struct_ptr);
		  }
	  }
}

#ifdef NEW_DECODERS
/* removes all registered converters of any kind
 */
PUBLIC void
NET_ClearAllConverters(void)
{
    net_ConverterStruct * converter_struct_ptr;
    XP_List * list_ptr;

	list_ptr = net_converter_list;
	while((converter_struct_ptr = 
						(net_ConverterStruct *) XP_ListNextObject(list_ptr)))
	  {
		list_ptr = net_converter_list; /* reset to beginning */
		XP_ListRemoveObject(list_ptr, converter_struct_ptr);
		FREEIF(converter_struct_ptr->format_in);
		FREEIF(converter_struct_ptr->encoding_in);
# ifdef XP_UNIX
		/* total kludge!! */
		if(converter_struct_ptr->converter == NET_ExtViewerConverter)
		  FREEIF(converter_struct_ptr->data_obj);
# endif /* XP_UNIX */
		FREE(converter_struct_ptr);
	  }

	list_ptr = net_decoder_list;
	while((converter_struct_ptr = 
						(net_ConverterStruct *) XP_ListNextObject(list_ptr)))
	  {
		list_ptr = net_decoder_list; /* reset to beginning */
		XP_ListRemoveObject(list_ptr, converter_struct_ptr);
		FREEIF(converter_struct_ptr->format_in);
		FREEIF(converter_struct_ptr->encoding_in);
# ifdef XP_UNIX
		/* total kludge!! */
		if(converter_struct_ptr->converter == NET_ExtViewerConverter)
		  FREEIF(converter_struct_ptr->data_obj);
# endif /* XP_UNIX */
		FREE(converter_struct_ptr);
	  }
}
#endif /* NEW_DECODERS */


/* register an external program to act as a content-type
 * converter
 */
MODULE_PRIVATE void
NET_RegisterExternalConverterCommand(char * format_in,
									 int    format_out,
                                     char * system_command,
                                     char * new_format)
{
    CV_ExtConverterStruct * new_obj = XP_NEW(CV_ExtConverterStruct);

    if(!new_obj)
       return;

    memset(new_obj, 0, sizeof(CV_ExtConverterStruct));

    /* make a new copy of the command so it can be passed
     * as the data object
     */
    StrAllocCopy(new_obj->command,    system_command);
    StrAllocCopy(new_obj->new_format, new_format);
    new_obj->is_encoding_converter = FALSE;

    /* register the routine
     */
    NET_RegisterContentTypeConverter(format_in, format_out, new_obj, NET_ExtConverterConverter);
}

/* register an external program to act as a content-encoding
 * converter
 */
PUBLIC void
NET_RegisterExternalDecoderCommand (char * format_in,
									char * format_out,
									char * system_command)
{
    CV_ExtConverterStruct * new_obj = XP_NEW(CV_ExtConverterStruct);

    if(!new_obj)
       return;

    memset(new_obj, 0, sizeof(CV_ExtConverterStruct));

    /* make a new copy of the command so it can be passed
     * as the data object
     */
    StrAllocCopy(new_obj->command,    system_command);
    StrAllocCopy(new_obj->new_format, format_out);
    new_obj->is_encoding_converter = TRUE;

    /* register the routine
     */
    NET_RegisterEncodingConverter (format_in, new_obj,
								   NET_ExtConverterConverter);
}


#endif /* XP_UNIX */
#endif /* MOZILLA_CLIENT */

/*  NewMKStream
 *  Utility to create a new initialized NET_StreamClass object
 */
NET_StreamClass *
NET_NewStream          (char                 *name,
                        MKStreamWriteFunc     process,
                        MKStreamCompleteFunc  complete,
                        MKStreamAbortFunc     abort,
                        MKStreamWriteReadyFunc ready,
                        void                 *streamData,
                        MWContext            *windowID)
{
    NET_StreamClass *stream = XP_NEW (NET_StreamClass);
    if (!stream)
        return nil;
        
    stream->name        = name;
    stream->window_id   = windowID;
    stream->data_object = streamData;
    stream->put_block   = process;
    stream->complete    = complete;
    stream->abort       = abort;
    stream->is_write_ready = ready;
    
    return stream;
}

#ifdef PROFILE
#pragma profile off
#endif