How the plugin stuff works

1. What kind of plugin do I want to make?
2. Introducing URL open plugins
3. URL_proto_add: The plugin of a GNU generation
4. URL_open_add: The power plugin
5. Life is great: My plugin found the URL and it exists!
6. But I'm not using a file descriptor!
7. Life sucks: My plugin got the right URL, but the URL doesn't exist
8. URL redirection
9. Stuffing your data into the cache
10. I love my users: How can I let them know what is going on?
11. Okay, how to create a MIME viewer plugin?


1. What kind of plugin do I want to make?

If you are creating a plugin to do some sort of transport protocol
implementation, you may wish to use URL_proto_add.

If you are creating a plugin to catch URLs that may have odd patterns, or
redirect to other plugins, you probably want a pure URL_open_add plugin.

If you're just wrapping around a more complex external program, you may wish
to consider using XXX

If you are creating a /viewer/ plugin, you want MIME_add.

2. Introducing URL open plugins

All URL open plugins have the same prototype.
    int myOpenPlugin(const char* URL, void* Ptr)

    URL is the name of the resource to be opened.
    Ptr is a thingy suitable to pass to MIME_view; more on that later.
    the return value is -1, or a file descriptor.

Your plugin can open a file, socket, etc.  Or it can just redirect the browser
to something else. 

3. URL_proto_add: The plugin of a GNU generation

The most straight forward way to install a plugin is to name the "protocol".
For instance, all URLs that look like file:/myfile are routed to the
gzilla_file_get function.  gzilla_file_get was hooked in by a line that looks
like:
	URL_proto_add("file",gzilla_file_get);

You would install your plugin like so:
	URL_proto_add("foobatz",myOpenPlugin);


4. URL_open_add: The power plugin

If URL_proto_add doesn't make sense -- you're trying to trap conveniental URLs
sneakily, or you're looking for URLs that don't look like well-formed URLs,
then URL_open_add is for you.  You would install your plugin like so:

     URL_open_add(myOpenPlugin);

The open plugins are called in order from the most recently installed to the
oldest.  The first plugin to return a non-negative number is considered to 
succeeded (and no more plugins will be called).

5. Life is great: My plugin found the URL and it exists!

All you need to do is dispatch the proper MIME viewer to read it.  (This may
change in the future).  First you need to figure out what the MIME type of your
resource is; lets call it content_type for now.  If you have a file descriptor
(fd) that the resource is being read from, you can use a line like so to hook
in the viewer:
   gzilla_web_dispatch_by_Type(Ptr, content_type, &_FD2Ptr(fd).Call,
			       &_FD2Ptr(fd).Data);

This looks a little strange, but it works just fine.  Then you can return from
the subroutine.

If you want your data to be /cached/ you need to include a line like this
before you call gzilla_web_dispatch_by_Type:

   gzilla_web_FD(Ptr,fd);

6. But I'm not using a file descriptor!

If you're not using a file descriptor, you will need to use a call like this:
      void* Data=NULL;
      __IOCallback_t Call=NULL;
      gzilla_web_dispatch_by_Type(Ptr, content_type, &Call, &Data);

Call and Data contain the information on how to pass the data back to the
viewer.  If you have all of the data available and do not wish to place it
into the cache, you would do something like:

     if (Call)
     {
        __CacheFile_t FedCFile;
	FedCFile.Data=MyData;
	FedCFile.Size=MyDataSize;
        Call(0,Data,&FedCFile);
	Call(1,Data, &FedCFile);
     }

The first Call lets the viewer know about the data.  The second Call tells
the viewer that no more data will be available.  Lets consider a more complex
example.  Lets say you get chunks of data, but they haven't been stored in a single
place.  Lets also say that your are using a library that employs callbacks (like
libftp or libwww).  You could use a call snippet sorta like:
   typedef struct
   {
      __CacheFile_t CFile;
      __IOCallback_t Call;
      void* MIME_Viewer_Data;
   } MyPersonalData_t;

   ...
	/* Set up the callback */
	MyPersonalData_t* MyPtr = malloc(sizeof(MyPersonalData_t));
	MyPtr->CFile.URL=strdup(MyURL);
	MyPtr->CFile.Size=0; /* No data yet*/
	MyPtr->CFile.Data=NULL; /* No data yet*/
        gzilla_web_dispatch_by_Type(Ptr, content_type, &MyPtr->Call,
		&MyPtr->MIME_Viewer_Data);
  	/* Set up MyCallback to be called whenever a chunk of data arrives */
	/* Note: We are not sticking the data into the data cache.*/
   ....

   void MyCallback(MyPersonalData_t* MyPointer, void* Ptr2Data, size_t SizeOfData)
   {
       /* First, place the new data in our psuedo cache structure */
       MyPtr->CFile.Data = g_realloc(MyPtr->CFile.Data,MyPtr->CFile.Size+SizeOfData);
       memcpy((char*)MyPtr->CFile.Data+MyPtr->CFile.Size, Ptr2Data,SizeOfData);
       MyPtr->CFile.Size+= SizeOfData;

       /* Next, call the destination */
       MyPtr->Call(0, MPtr->MIME_Viewer_Data, &MPtr->CFile);
   }

   ... Later when you have received all of your data:
   MyPtr->Call(1,MPtr->MIME_Viewer_Data, &MPtr->CFile);


If you wanted to store the data in the cache (and you probably do), you would want
to use code more like this:
   typedef struct
   {
      __CacheFile_t* CPtr;
      __IOCallback_t Call;
      void* MIME_Viewer_Data;
   } MyPersonalData_t;

   ...
	/* Set up the callback */
	MyPersonalData_t* MyPtr = malloc(sizeof(MyPersonalData_t));
	MyPtr->CPtr = malloc(sizeof(__CacheFile_t));
	MyPtr->CPtr->URL =strdup(MyURL);
	MyPtr->CPtr->Type=strdup(content_type);
	MyPtr->CPtr->Size=0; /* No data yet*/
	MyPtr->CPtr->Data=NULL; /* No data yet*/
        gzilla_web_dispatch_by_Type(Ptr, content_type, &MyPtr->Call,
		&MyPtr->MIME_Viewer_Data);
  	/* Set up MyCallback to be called whenever a chunk of data arrives */
	/* Stick the data into the data cache.*/
	GzCache_add(MyPtr->CPtr);
   ....

   void MyCallback(MyPersonalData_t* MyPointer, void* Ptr2Data, size_t SizeOfData)
   {
       /* First, place the new data in our psuedo cache structure */
       MyPtr->CPtr->Data = g_realloc(MyPtr->CPtr->Data,MyPtr->CPtr->Size+SizeOfData);
       memcpy((char*)MyPtr->CPtr->Data+MyPtr->CPtr->Size, Ptr2Data,SizeOfData);
       MyPtr->CPtr->Size+= SizeOfData;

       /* Next, call the destination */
       MyPtr->Call(0, MPtr->MIME_Viewer_Data, MPtr->CPtr);
   }

   ... Later when you have received all of your data:
   MyPtr->Call(1,MPtr->MIME_Viewer_Data, &MPtr->CPtr);


7. Life sucks: My plugin got the right URL, but the URL doesn't exist

When you're plugin is the right one for the URL, but the URL doesn't exist..
you need to let the user know.  The /best/ way (that is: fastest for Gzilla)
is to use
	 Gz_File_Not_Found(URL, Ptr);

However, you can make your own if you want; all you need do is include a routine
like:

   static char Buffy[2048];
   static __CacheFile_t FedCFile = {NULL,NULL,NULL,"text/html",Buffy,sizeof(Buffy)};
   static int gzilla_file_not_found (const char *URL, void* Ptr)
   {  
      void* Data=NULL;
      __IOCallback_t Call=NULL;
      gzilla_web_dispatch_by_Type(Ptr, FedCFile.Type, &Call, &Data);
      if (!Call) return -1;


      sprintf(Buffy,"<html><head><title>404 Not Found</title></head>\n"
	   "<body><h1>404 Not Found</h1><p>The requested file %s"
	   " was not found in the filesystem.</p>\n</body></html>\n",URL);
      Call(0, Data, &FedCFile);
      Call(1, Data, &FedCFile);
      return 0;
   }

This looks really very complex, but it integrates the last few previous
sections.

8. URL redirection

To redirect the browser from your plugin, just issue a command like so:
   gzilla_web_redirect(Ptr, "http://PlaceToRedirectTo/");

To add a URL redirection to the cache, you would use a command like so:
   GzCache_redirect("http://Original/", "http://PlaceToRedirectTo/");


9. Stuffing your data into the cache
Occassionally you would like to place your data directly into the cache,
without relying on gzilla_dispatch_by_Type.  (Say, if you are using shared
memory, or such).  You need to create a cache structure to point to your data:
	  __CacheFile_t* CPtr=malloc(sizeof(*CPtr));
	  CPtr->Flags=0;
	  CPtr->Size = size of your data;
	  CPtr->Data = pointer to your data;
	  CPtr->Type = the content type of your data.
	  CPtr->URL  = A string describing the URL; this is used as the key
		         (eg "http://myurl/")

Then you call GzCache_add to insert it:
     GzCache_add(CPtr);

Note: why not just memory map all of the files in, instead of using the IO
method?  Most MIME viewers access the data /sequentially/.  The Gzilla IO
handler reads in the data sequentially, but in a manner that does not block
the execution of Gzilla.  Memory mapped access does /block/ the execution of
Gzilla, making it seem sluggish, dead, or really unresponsive.

10. I love my users: How can I let them know what is going on?

    gzilla_web_status(Ptr, "text");

Ptr is that mysterious little pointer to stuff you shouldn't worry about.  This
call will add the text to the status line at the bottom of the window.

You would want to inform the user in events like:
    * Initiating the socket
    * The remote machine couldn't be found
    * The connection was rejected
    * The connection was accepted, and now lots of data shall be coming down
      the mountain
    
    * Mysterious aborts

11. Okay, how to create a MIME viewer plugin?

MIME viewers are nontrivial.  But here is how to add them.  For a viewer for
a specific MIME content type, you would use a line like so:
  MIME_type_add("text/html",  gzHTML);

But what about content types of "text/curly"?  You can also add a default
viewer for MIME major types:
       MIME_mtype_add("text", gzPlain);
All text-type data streams will be redirected to gzPlain.




NOTES: yes I know the naming style is all over the place.. it'll get cleaned
up soon.
