Newsbin Plugin SDK
Developers Guide

CONTENTS

Clicking on the * at the start of any heading will return you to the Contents list.

Text in blue is a hyperlink that can be clicked on to jump to another part of the document

Comments in red are questions that are awaiting feedback or suggestions/thoughts for future development of the interface.

This documentation currently refers to the plugin interface as it stands at the Newsbin 4.3.3B2 release (build 5011).

 


* INTRODUCTION

The Newsbin Plugin SDK is designed to allow users to extend the functionality of Newsbin. Examples of the areas in which it can extend functionality are:

This interface was first introduced in Newsbin 4.3.0, although the definitions of the interface was not made available publicly at that time.
The first release of the SDK was in conjunction with Newsbin 4.3.2.  

Note that the Plugin Interface is still under development and is subject to change.
Where possible changes will be made in a backwards compatible manner, but this cannot be guaranteed to always be the case.


* REQUIREMENTS

A Newsbin plugin is supplied as a Windows DLL that supports a specified interface. It is placed into the Newsbin PLUGIN folder, and then it will automatically become available next time that Newsbin is loaded.   Newsbin provides an element of protection against random files or DLL's being placed at this location as to be used by Newsbin the DLL has to pass all the following checks:

You can see what plugin DLLs Newsbin has detected that passed the above checks by looking at the Preferences->Plugins dialog from within Newsbin.

The plugin SDK is designed to be used with any tool that can produce Windows DLL's, and can support the type of interface semantics that are defined in this document.   Examples of such tools are:

although it should be usable with any compatible tool.

The DLL should be written to be thread-safe as you may get multiple Newsbin threads calling into the plugin in parallel for some of the capabilities.

 


* GETTING STARTED

Visual C++ (v6)

The first thing you need to do is create yourself a new Visual C++ project.   The easiest way to do this is to:

Delphi 6

The first thing you need to do is create a Library project. The easiest way to do this to: 


* DLL Initialisation

When Newsbin starts up, it will examine the “plugin” sub-folder of the Newsbin install directory, and enumerate the DLL’s that are found in that location.

When Windows loads up the DLL, the DllMain routine is called. This needs to initialise a variable called Entry that is of type ENTRY_POINTS_V2 to contain the entry points into the DLL. This variable must have external visibility and have "C" style linkage. More detail is given on the methods defined in this structure and their under the Entry Points section of this document.

This code will therefore look something like the following:

Visual C++

ENTRYPOINTS_V2 __declspec(dllexport) Entry =
{
  sizeof(ENTRYPOINTS_V2),
  &PG_Init,
  &PG_Description,
  &PG_Extension,
  &PG_Options,
  &PG_Filename,
  &PG_Read,
  &PG_Close,
  &PG_PreDownload,
  &PG_PostDownload,
  NULL,			// &PG_ChunkDownload, 
  &PG_PostFilter,
  &PG_FileFilter,
  &PG_Version,
};


BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		// You could insert here anything that needs to be done during
		// DLL initialisation
		break;

	case DLL_PROCESS_DETACH:
		// You could insert here anything that needs to be done during
		// DLL terminatiom
		break;
		
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		// Unlikely to want to do anything during thread attach/detach.
		break;	
	}
	return TRUE;
}

Delphi

var 
  Entry: ENTRYPOINTS_V2; 
exports
  Entry;

begin
Entry.PG_nSize := SizeOf(ENTRYPOINTS_V2);
Entry.PG_Init := @PG_Init;
Entry.PG_Description := @PG_Description;
Entry.PG_Version := @PG_Version;
end.

Exactly which entry points need to be filled in will depend on the functionality that this DLL is to support. Note that any entry points that are not needed can be set to NULL.


* Entry Points

The following structure is used to define the Entry Points into the DLL. Functions that are not required to support the capabilities specified can be set to NULL.

Does Newsbin protect itself against any Entry Points being set to NULL even though they really should not be NULL according to the capabilities the DLL claims to support?

C/C++:  

typedef struct	_entrypoints
{
  size_t                     PG_nSize;
  HRESULT (WINAPI *PG_Init)(PLUGIN_TYPE* pType, FILECALLBACKS_V1* p1, POSTCALLBACKS_V1* p2);
  HRESULT (WINAPI *PG_Description)(char* pszDescription, size_t nLength);
  HRESULT (WINAPI *PG_Extension)(char* pszExtension, size_t nLength);
  HRESULT (WINAPI *PG_Options)(HWND hWnd);
  HRESULT (WINAPI *PG_Open)(const char* pszFilename);
  HRESULT (WINAPI *PG_Read)(RECORD_T* pRecord);
  HRESULT (WINAPI *PG_Close)(void);
  HRESULT (WINAPI *PG_PreDownload)(NBFILEDATA hFileData);
  HRESULT (WINAPI *PG_PostDownload)(NBFILEDATA hFileData);
  HRESULT (WINAPI *PG_ChunkDownload)(NBFILEDATA hFileData, size_t nIndex, const unsigned char* pszData, size_t nLength);
  HRESULT (WINAPI *PG_PostFilter)(NBPOSTDATA hPostData);
  HRESULT (WINAPI *PG_FileFilter)(NBFILEDATA hFileData);
  HRESULT (WINAPI *PG_Version)(VERSION_TYPE* PluginVersion,VERSION_TYPE nNewsbinVersion);
}ENTRY_POINTS_V2;

Delphi:  

_entrypoints = record
      PG_nSize:       size_t;

      PG_Init:        function(pType: PPLUGIN_TYPE; p1: PFILECALLBACKS_V1; p2: PPOSTCALLBACKS_V1): HRESULT; stdcall;
      PG_Description: function(pszDescription: PChar; nLength: size_t): HRESULT; stdcall;
      PG_Extension:   function(pszExtension: PChar; nLength: size_t): HRESULT; stdcall;
      PG_Options:     function(hWnd: HWND): HRESULT; stdcall;
      
      PG_Open:        function(const pszFilename: PChar): HRESULT; stdcall;
      PG_Read:        function(pRecord: PRECORD_T): HRESULT; stdcall;
      PG_Close:       function: HRESULT; stdcall;

      PG_PreDownload:   function(hFileData: NBFILEDATA): HRESULT; stdcall;
      PG_PostDownload:  function(hFileData: NBFILEDATA): HRESULT; stdcall;
      PG_ChunkDownload: function(hFileData: NBFILEDATA; nIndex: size_t; const pszData: PChar; nLength: size_t): HRESULT; stdcall;

      PG_PostFilter:    function(hPostData: NBPOSTDATA): HRESULT; stdcall;
      PG_FileFilter:    function(hFileData: NBFILEDATA): HRESULT; stdcall;

      PG_Version:       function(PluginVersion: PVERSION_TYPE; nNewsbinVersion: VERSION_TYPE): HRESULT; stdcall;
    end;

Note that the names used here for the functions/fields do not have to be the same in your own plugin if you would prefer to use different ones.  However most people will probably prefer to use the same names as the structure member names above.

These fields/functions are now described in more detail below

At the moment the descriptions below are in the same order as the structure members above.  I would welcome any feedback for/against re-ordering the descriptions so that they are in alphabetical order.


C/C++:   size_t PG_nSize;

Delphi:  

This field is used as part of the versioning mechanism that Newsbin uses to work out if this plugin is compatible with the version of Newsbin that is trying to use it.

The Plugin must initialise this to the size of the structure so that Newsbin can check that it is the correct version.  It will therefore be initialised using code of the form:

C/C++:

  PG_nSize = sizeof (ENRYPOINTS_V2);

Delphi:  

  Entry.PG_nSize := SizeOf(ENTRYPOINTS_V2); 

^


C/C++:   HRESULT WINAPI PG_Init (PLUGIN_TYPE*pType, FILECALLBACKS_V1* p1, POSTCALLBACKS_V1* p2);

Delphi:   function PG_Init(pType: PPLUGIN_TYPE; p1: PFILECALLBACKS_V1; p2: PPOSTCALLBACKS_V1): HRESULT; stdcall;

This is the first function that Newsbin calls.  It can be called multiple times, so if this matters the DLL must protect itself against this.

The *ptype parameter must be will describe what are the capabilities of this plugin by using a OR'ed combination of the bitflags shown in the table below (and defined in the plugin header file nbPluginAPI.h. This will also implicitly specify what other entry points have been defined as the ones needed to support the defined capabilities .

Constant Meaning
PG_HEADER_SOURCE Can create new download records for Newsbin.  This is expected to be used in conjunction with the PG_FILE_HANDLER capability.
PG_POST_FILTER Wants to be called as each post is loaded into the Post list.
PG_FILE_FILTER Wants to be called for each post in the Post List after processing a file.  It allows the filter to decide which post entries (if any) should be added to the Download List.
PG_PRE_DOWNLOAD Wants to be called before every download starts
PG_POST_DOWNLOAD Wants to be called after every download completes
PG_CHUNK_DOWNLOAD Wants to be called after every chunk is decoded.
PG_FILE_HANDLER Interested in specific files with specific extensions
PG_DOWNLOAD_FILTER When passed the list of loaded posts, designate which ones to download
PG_HAS_OPTIONS Tells Newsbin that the PG_Options function can be called if it's defined.  In the plugin manager dialog available via Preferences->Plugin,   clicking the Properties button calls PG_Options if this flag and the function are defined.

The addresses of the Callback routines passed in the p1 and p2 parameters should be stored for use later when getting information about a post or file.

You could also do other initialisation her such as create worker threads here for instance to, process web requests. 

The plugin should return 

A simple example of the code one might expect to find in this routine is:

C/C++:  

     g_pEntry = p1;                               // Save Entry points in local variable
     g_pPosts = p2;                               // Save Entry points in local variable
     *pType =  PG_FILE_HANDLER | PG_POST_FILTER;  // Set capabilities
     return(S_OK);                                // Return OK
	
Delphi:  
      g_pEntry := p1;
      g_pPosts := p2;
      pType^ := PG_FILE_HANDLER or PG_POST_FILTER;
      Result := S_OK;
      
^


C/C++:  HRESULT WINAPI PG_Shutdown (void);

Delphi:  

Not implemented yet.

It has been proposed as a function that will be called by Newsbin when it is about to close down so that the DLL can tidily release any resources that it has allocated.

 

^


C/C++:   HRESULT WINAPI PG_Description (char* pszDescription, size_t nLength);

Delphi:  function PG_Description (pszDescription: PChar; nLength: size_t): HRESULT; stdcall;

This function is called by Newsbin to find out how the plugin wants describe itself  (e.g "This plugin filters posts")

The pszDescription parameter points to a buffer that can be used to return the text of the description (e.g "This plugin filters posts") as a zero terminated string. The nLength parameter specifies the maximum size of the available buffer space.

The plugin should return 

 ^



C/C++:   HRESULT WINAPI PG_Extension (char* pszExtension, size_t nLength);

Delphi:   function PG_Extension (pszExtension: PChar; nLength: size_t): HRESULT; stdcall;

Routine to report the file extension(s) that this plugin handles.  This entry point is only called if the plugin specified PG_FILE_HANDLER as a capability when PG_Init was called.

The pszExtension parameter points to a buffer that can be used to return the file extension(s) that this plugin can handle, and the nLength parameter specifies the maximum size of the available buffer space.    Set it to a zero length string if extensions are not relevant.  The extension is specified as a zero terminated string, and must include the leading period (e.g. ".nzb").  To specify multiple extensions, provide a list of zero terminated strings with an additional zero byte to terminate the list.  The code might therefore look something like this:

C/C++:

      size_t nOffset = 0;                       // Initialise offset variable
      memcpy(pszExtension + nOffset,".sfv",5);  // Set an extension
      nOffset += 5;                             // Update offset variable
      memcpy(pszExtension + nOffset,".csv",5);  // Set next extension
      nOffset += 5;                             // Update offset variable
      memset(pszExtension + nOffset,0,2);       // Set 0 bytes to follow

or perhaps more efficiently:

      memcpy(pszExtension,".sfv\0.csv\0\0",12);

Delphi:

    *** To Be Supplied ***

The plugin should return:

What happens if more than one plugin sets a zero length string or say that they can handle the same extension - do they both get called?  Initial testing suggest not, but need to confirm this as it might depend on code returned at the end.  Certainly returning E_ABORT seems to stop any further plugin being called.

 

^


C/C++:   HRESULT WINAPI PG_Options (HWND hWnd);

Delphi:   function PG_Options (hWnd: HWND): HRESULT; stdcall;

This function is called by Newsbin to allow the plugin to display a dialogue when the Properties button is pressed in the Plugin Manager dialog (available in Newsbin via Preferences->Plugins) .  The plugin should create the dialogue using the supplied hWnd as the parent window..

Can be used for purposes such as capturing configuration parameters.  In such a case, saving the configuration is up to the plugin.

The plugin should return

If the plugin wants to know where on disk to store information, it can get the location at which the plugin DLL was located (and thus by implication the Newsbin install location by using the GetModuleFilename()Windows function in the DllMain routine to get and save the path from which the DLL was loaded.:

 ^



C/C++:   HRESULT WINAPI PG_Open (const char* pszFilename);

Delphi:   function PG_Open (const pszFilename: PChar): HRESULT; stdcall;

Set the filename of interest.

This routine will be called by Newsbin prior to initiating a series of PG_Read calls to the plugin. The filename can have been specified by the Newsbin user in any of the following ways:

The Plugin should return 

If the plugin has defined PG_HEADER_SOURCE as one of its capabilities then this call will be followed by a sequence of PG_Read calls to allow the header to specify the details of the new headers (posts).

^


C/C++:  HRESULT WINAPI PG_Read (RECORD_T* pRecord);

Delphi:  function PG_Read (pRecord: PRECORD_T): HRESULT; stdcall;

Pointer to the routine that is called repeatedly (until it returns S_DATA_DONE) for a plugin that has specified PG_FILE_HANDLER as one of its capabilities.

The pRecord parameter is a pointer to a structure of the following type (defined in the Newsbin plugin header file) that the plugin will fill in. Newsbin will then take it and convert it onto its own internal internal data structure

C/C++:  

	typedef struct	_record_t
	{
		const char*	pszSubject;
		const char*	pszGroup;
		const char*	pszServer;
		const char*	pszFrom;
		const char*	pszMessageId;
		time_t		nRecordDate;
		size_t		nRecordSize;
		size_t		nRecordNumber;
		size_t		nRecordIndex;
	}RECORD_T;

Delphi:  
    _record = record 
      pszSubject: PChar; 
      pszGroup: PChar; 
      pszServer: PChar; 
      pszFrom: PChar; 
      pszMessageId: PChar; 

      nRecordDate: size_t; 
      nRecordSize: size_t; 
      nRecordNumber: size_t; 
      nRecordIndex: size_t; 
    end; 

    RECORD_T = _record; 
    PRECORD_T = ^RECORD_T; 

Field Description
pszSubject Zero terminated string that specifies the post subject.
pszGroup Zero terminated string that specifies the post group.
What if the post is cross-posted?
pszServer Zero terminated string that specifies the post server.
What if the post is cross-posted?
pszFrom Zero terminated string that specifies the poster of the post
pszMessageId MessageId of the post.
nRecordDate
nRecordSize
nRecordNumber
nRecordIndex

The Plugin should return 

I assume that any unused char * fields should be set to NULL, and the size_t ones to 0? 
Does Newsbin release the memory associated with the string returns or is the responsibility of the plugin? 
I would assume that Newsbin does not release the memory but is this correct? 
Can they be static areas in the plugin (although I guess this could cause errors with thread-safety)?

^


C/C++:   HRESULT WINAPI PG_Close (void);

Delphi:   function: PG_Close HRESULT; stdcall;

Called for File Processor plugins when the S_DATA_DONE or error is returned to the PG_Read calls.

The plugin should clean up and resources it may have allocated and return

^



C/C++:   HRESULT WINAPI PG_PreDownload (NBFILEDATA hFileData);

Delphi:   function PG_PreDownload (hFileData: NBFILEDATA): HRESULT; stdcall;

Routine called before a file download starts if the plugin has specified PG_PRE_DOWNLOAD as one of its capabilities. The plugin may obtain details about the file by calling the CB_ family of file callback functions.  Note, however, that the CB_Filename callback returns an empty string at this point so if the filename is wanted it has to be parsed out of the subject.

The plugin should return 

It would be nice if Newsbin parsed out the filename for you and passed it back in the CB_Filename callback so that everyone does not need to write their own filename parsing code.

^



C/C++:  HRESULT WINAPI PG_PostDownload (NBFILEDATA hFileData);

Delphi:  function PG_PostDownload (hFileData: NBFILEDATA): HRESULT; stdcall;

Routine called after a file download completes if the plugin has specified PG_POST_DOWNLOAD as one of its capabilities.  The plugin may obtain details about the post by calling the CP_ family of file callback functions.

The plugin should return

^



C/C++:  HRESULT WINAPI PG_ChunkDownload (NBFILEDATA hFileData, size_t nIndex, const unsigned char* pszData, size_t nLength);

Delphi:  function PG_ChunkDownload (hFileData: NBFILEDATA; nIndex: size_t; const pszData: PChar; nLength: size_t): HRESULT; stdcall;

This function is called by Newsbin after a chunk has been decoded if the plugin has specified PG_CHUNK_DOWNLOAD as one of its capabilities. The plugin may obtain details about the post by calling the CB_ family of File callback functions.

Parameter

Description

nIndex The part number of the post within the file
pszDataA pointer to the (decoded) data
nLengthThe length of the decoded data.

The plugin should return

PBClements comments copied from forum:

To handle incomplete files, the PAR2 plugin needs to handle the PG_ChunkDownload calls to look for good data in the chunks that have been downloaded, but if the file is complete, those chunk files then get deleted before PG_PostDownload is called with the complete version of the file.

I note that the Newsbin Pro Settings dialogue includes a "Save Chunks" option under "Advanced / Spooler Settings", but I think we need an option to allow a plugin to tell Newsbin not to merge chunks when a download becomes complete.

If the PAR2 plugin has already scanned the chunks as they were decoded, it really does not want to waste any time rescanning the same data when the download completes. e.g. with a 50MB rar file from a DVD.

I suggest that PG_ChunkDownload should return S_OK to let Newsbin decide what to do with the chunk, or S_SAVECHUNK to tell Newsbin to save a copy of the chunk once the download has completed, or S_DONTMERGE to tell Newsbin not to merge the chunk once the download has completed.

I see that when the download of an incomplete file finished and PG_PostDownload is called, CB_Filename is blank and CB_Complete returns 0 and that when the download of a complete file finishes, CB_Filename gives the filename and CB_Complete is 1.

I suggest that if the plugin has returned S_DONTMERGE for all chunks , that when PG_PostDownload is called, CB_Filename should give the path+filename but the the file won't actually exist. CB_Complete should be 1 or 0 as appropriate (i.e. whether or not the post was complete). It is then up to the plugin to merge the chunks.

If the plugin returned S_DONTMERGE for some but not all of the chunks, then Newsbin should merge all of the other chunks and when PG_PostDownload is called, the file referred to by CB_Filename will exist (but only contain the chunks for which S_DONTMERGE was specified).

^



C/C++:  HRESULT WINAPI PG_PostFilter (NBPOSTDATA hPostData);

Delphi:  function PG_PostFilter (hPostData: NBPOSTDATA): HRESULT; stdcall;

As each post is read from disk or the server a notification is pasted here if the plugin has specified PG_POST_FILTER as one of its capabilities .  The pData parameter passed in can be used in conjunction with the CP_ family of Post callback routines to find out more information about the post. 

Whether the post is added to the Post List is determined by the return code as follows:

^



C/C++:  HRESULT WINAPI PG_FileFilter (NBFILEDATA hFileData);

Delphi:  function PG_FileFilter (hFileData: NBFILEDATA): HRESULT; stdcall;

After processing a file, if the plugin has specified PG_FILE_FILTER as one of its capabilities then a call is made to this routine for each post in the Post List.. The pData parameter passed in can be used in conjunction with the CB_ family of File callback routines to find out more information about the post.

If you have also specified PG_DOWNLOAD_FILTER as one of the capabilities then whether the post is added to the Download List is determined by the return code as follows:

If you have specified PG_FILE_FILTER as a capability without PG_DOWNLOAD_FILTER this is called for every entry in the Post List to determine whether it should be visible or not. You can do size/subject/from filtering here.  In this case the return codes function as follows:

How are these two different uses distinguished if a plugin had both capabilities?  Is the first one perhaps delimited by a PG_Open.PG_Close sequence before the second use is invoked?

^


C/C++:    HRESULT WINAPI PG_Version (VERSION_TYPE* PluginVersion, VERSION_TYPE nNewsbinVersion);

Delphi:   function PG_Version(PluginVersion: PVERSION_TYPE; nNewsbinVersion: VERSION_TYPE): HRESULT; stdcall;

Versioning mechanism. Newsbin informs the plugin of the Newsbin version in the nNewsbinVersion parameter, and the plugin reports it's version back to Newsbin.  The Newsbin version is reported as a value of 0x432 representing the Newsbin 4.3.2 release.

Return values:

^


* Call Sequences

This section tries to help clarify the way that the plugin interface works by giving an indication of the sequence of calls that will be made on the plugin interface according to the capabilities that have been defined when PG_Init was called.   Note that these sequences are the results of experimentation (thanks to P.B. Clements) and:

They are documented here purely to act as help in understanding the current behaviour or the interface.

Please feel free to provide feedback on other sequences that have been discovered that might be of interest, and also any cases where you believe the information here is wrong (or has changed in a new release)

CapabilityCall SequenceDescription
*pType=0
(i.e. no capabilities defined)
PG_Init  
PG_Description  
PG_Version  
PG_Extension (twice)This seems a bit surprising!
PG_Init
PG_FILE_HANDLER PG_Extension (twice)This will be called to see if the plugin supports the extension for the file. If not, then no further calls are made.
PG_Init
PG_FILE_HANDLER
| PG_DOWNLOAD_FILTER
PG_ExtensionThis will be called to see if the plugin supports the extension for the file. If not, then no further calls are made.
PG_OpenThis is called to specify the file that the Newsbin user selected.
PG_FilefilterThis is called for each file in the Post List to allow the plugin to specify whether it should be added to the Download List or not
  PG_CloseCalled at the end to terminate the sequence of events
     
PG_HEADER_SOURCEPG_Extension (twice) 
PG_Init 
     
PG_HEADER_SOURCE
| PG_FILE_HANDLER
PG_Extension (twice) 
PG_Init 
PG_OpenThis is called to specify the file that the Newsbin user selected.
PG_ReadCalled repeatedly until a S_DATA_DONE return is made
PG_CloseCalled at the end to terminate the sequence of events
     
PG_POST_FILTERPG_PostfilterCalled once for every post when you use "Load Spool", "Download Latest" or "Download All Headers" and you must return S_OK to permit the post to be displayed

This facility appears to be broken in the 4.33 betas as the PG_PostFilter method does not get called even when the capability is specified.

     
PG_FILE_FILTERPG_FilefilterCalled once for every file when you use "Load Spool", "Download Latest" or "Download All Headers" and you must return S_OK to permit the post to be displayed
     
PG_POST_FILTER
| PG_FILE_FILTER
PG_Postfilter 
PG_FilefilterAll calls to PG_Postfilter occur before any calls to PG_Filefilter
     
PG_PRE_DOWNLOADPG_PreDownloadOnce before any file starts downloading. 
     
PG_POST_DOWNLOADPG_PostDownloadOnce after any file completes downloading. CB_Filename() will give you the full path to the downloaded file
     
PG_CHUNK_DOWNLOADPG_ChunkDownloadCalled after each chunk is downloaded

It appears that if you define the PG_ChunkDownload method then it gets invoked even if the PG_CHUNK_DOWNLOAD capability has not been defined.

     
PG_DOWNLOAD_FILTERPG_Extension (3 times) 
  PG_Init 
     
PG_FILE_HANDLER
| PG_FILE_FILTER
| PG_DOWNLOAD_FILTER
PG_Extension (see text)
PG_FileFilter (see text)
 
PG_FileFilter() gets called once for every file and you must return S_OK to permit Newbin to display the entry for that file. Additionally, when you use Process with Plugin, Newsbin calls PG_Extension twice, then PG_Init once, and PG_Open once, followed by PG_FileFilter once for every file (but this time you must return E_ABORT to prevent the file from being downloaded).
     
PG_HAS_OPTIONSPG_Optionswhen plugin selected and Properties button pressed on Preferences->Plugins dialog

* Callbacks

There are two different families of calls back into Newsbin.     The two different types are the "file" type and the "post" type. A "File" is a combined (or in the process of being combined) entry for a single file. A "post" is a single instance of a posts from the XOVER data or spool load.

CB "file" Callbacks

These are used from within the following entry points:

This structure defines functions supplied to the callback to break information out about the post when working in file mode.

Does Newsbin ever set any of these to NULL so that the plugin needs to protect itself against this scenario?  I assume not?

C/C++:  

typedef struct	_callbacks
{
	size_t               PG_nSize;
	const char* (WINAPI *CB_Subject)   (NBFILEDATA hFileData);
	const char* (WINAPI *CB_From)      (NBFILEDATA hFileData);
	const char* (WINAPI *CB_Filename)  (NBFILEDATA hFileData);
	size_t      (WINAPI *CB_Size)      (NBFILEDATA hFileData);
	size_t      (WINAPI *CB_MaxRecords)(NBFILEDATA hFileData);
	size_t      (WINAPI *CB_Complete)  (NBFILEDATA hFileData);
	time_t      (WINAPI *CB_Date)      (NBFILEDATA hFileData);

}FILECALLBACKS_V1;

Delphi:  

_file_callbacks = record 
      PG_nSize:      size_t; 

      CB_Subject:    function (hFileData: NBFILEDATA): PChar; stdcall; 
      CB_From:       function (hFileData: NBFILEDATA): PChar; stdcall; 
      CB_Filename:   function (hFileData: NBFILEDATA): PChar; stdcall; 
      CB_Size:       function (hFileData: NBFILEDATA): size_t; stdcall; 
      CB_MaxRecords: function (hFileData: NBFILEDATA): size_t; stdcall; 
      CB_Complete:   function (hFileData: NBFILEDATA): size_t; stdcall; 
      CB_Date:       function (hFileData: NBFILEDATA): time_t; stdcall; 
    end;
    FILECALLBACKS_V1 = _file_callbacks; 
    PFILECALLBACKS_V1 = ^FILECALLBACKS_V1;  
Function Purpose
PG_nSize Specifies the size of the structure. It is used for versioning purposes.

Does this really add any value?  Is the plugin meant to check it before using any of the callback functions?  If so is this the value set in the PG_Init call or is it something different?

CB_Subject Subject
CB_From From field
CB_Filename Filename - if we know it
CB_Size Size of this file
CB_MaxRecords Number of parts in the file
CB_Complete Is it complete? 1=yes, 0=no

Should the % complete be indicated in some way?  (with 0 = 100% so it is easy to test for)

CB_Date Time stamp of record #1.

CP "Post" Callbacks

This structure defines callback functions supplied to break information out about the post when working in post mode.

These are used from within the following entry points:

Does Newsbin ever set any of these to NULL so that the plugin needs to protect itself against this scenario?  I assume not?

C/C++:  

typedef struct	_callbacks_posts
{
	size_t               PG_nSize;
	const char* (WINAPI *CP_Subject)        (NBPOSTDATA hPostData);
	const char* (WINAPI *CP_From)           (NBPOSTDATA hPostData);
	size_t      (WINAPI *CP_Size)           (NBPOSTDATA hPostData);
	size_t      (WINAPI *CP_MaxRecords)     (NBPOSTDATA hPostData);
	size_t      (WINAPI *CP_CurrentRecords) (NBPOSTDATA hPostData);
	time_t      (WINAPI *CP_Date)           (NBPOSTDATA hPostData);

}POSTCALLBACKS_V1;

Delphi:  

    _post_callbacks = record 
      PG_nSize:          size_t; 

      CP_Subject:        function(hPostData: NBPOSTDATA): PChar; stdcall; 
      CP_From:           function(hPostData: NBPOSTDATA): PChar; stdcall; 
      CP_Size:           function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_MaxRecords:     function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_CurrentRecords: function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_Date:           function(hPostData: NBPOSTDATA): time_t; stdcall; 
    end; 

 

Function Purpose
PG_nSize Specifies the size of the the structure. It is used for versioning purposes.

Does this really add any value?  Is the plugin meant to check it before using any of the callback functions?  If so is this the value set in the PG_Init call or is it something different?

CP_Subject Subject.
CP_From From field.
CP_Size Size of this post
CP_MaxRecords Records needed to complete this file.
CP_CurrentRecords Records needed to complete this file.
CP_Date Time stamp of record #1.

* DATA STRUCTURES

What is the purpose of this structure?  When is it used?

C/C++:  

typedef struct	_xoverinfo
{
	size_t	m_nRecordNumber;
	size_t	m_nSize;
	char*	m_pszSubject;
	size_t	m_nSubject;
	char*	m_pszFrom;
	size_t	m_nFrom;
	char*	m_pszMessageId;
	size_t	m_nMessageId;
	char*	m_pszDate;
	size_t	m_nDate;
	time_t	m_nUnixDate;
}XOVERINFO,*PXOVERINFO;

Delphi:  

Field Description
nRecordNumber
nSize
pszSubject The subject as a zero terminated string
nSubject Size of the subject buffer?
m_pszFrom The From field (the posters name) as a zero terminated string
m_nFrom Size of the from buffer?
m_pszMessageId The Message-Id as a zero terminated string
m_nMessageId Size of the MessageId buffer?
m_pszDate The date as a zero terminated string (in the form MM/DD/YYYY HH:MM
The above assumes US date format - should it really be local data format (e.g. DD/MM/YYYY HH:MM in the UK?
m_nDate Size of the Date buffer?
m_nUnixDate

* C/C++ HEADER FILE

The following is the contents of the Newsbin plugin header file for use with C/C++ at the time this documentation was produced. 

// nbPluginAPI.h

#ifndef __NB_PLUGIN_API_H__
#define __NB_PLUGIN_API_H__

//*************************************************************************
// Header that define Plugin interface for use with newsbin.
//
//	On Startup, Newsbin will enumerate the DLL's on the "plugin" folder,
//	and use any that support this interface.
//
// NOTE:  Compatible with Newsbin 4.32
//*************************************************************************

#ifdef __cplusplus
extern "C" {
#endif

// Various special data types used within the interface
typedef struct _entrypoints                     ENTRYPOINTS_V2;
typedef const void*                             NBFILEDATA;
typedef struct _file_callbacks                  FILECALLBACKS_V1;
typedef const void*                             NBPOSTDATA;
typedef struct _post_callbacks                  POSTCALLBACKS_V1;
typedef   unsigned long				PLUGIN_TYPE; // 32 bit value
typedef   unsigned long				VERSION_TYPE; // 32 bit value
typedef struct _record RECORD_T;

// Bit values that the plugin should return in pType when Newsbin calls
// PG_Init() to let Newsbin known which entrypoints the plugin wants
// Newsbin to call.

#define      PG_HEADER_SOURCE     0x00000001
#define      PG_POST_FILTER       0x00000010
#define      PG_FILE_FILTER       0x00000020
#define      PG_PRE_DOWNLOAD      0x00000100
#define      PG_POST_DOWNLOAD     0x00001000
#define      PG_CHUNK_DOWNLOAD    0x00010000
#define      PG_FILE_HANDLER      0x00100000
#define      PG_DOWNLOAD_FILTER   0x00200000
#define      PG_HAS_OPTIONS       0x00400000

//  Special return values used by plugin interface

#define      S_DATADONE            _HRESULT_TYPEDEF_(0x00001000L)

//  Structure that defines the public entry points exposed by the plugin.

struct _entrypoints
{
  size_t    PG_nSize;

  HRESULT   (WINAPI *PG_Init)(PLUGIN_TYPE* pType,
                            FILECALLBACKS_V1* p1,
                            POSTCALLBACKS_V1* p2);

  HRESULT   (WINAPI *PG_Description)(char* pszDescription,
                                   size_t nLength);

  HRESULT   (WINAPI *PG_Extension)(char* pszExtension,
                                 size_t nLength);

  HRESULT   (WINAPI *PG_Options)(HWND hWnd);

  HRESULT   (WINAPI *PG_Open)(const char* pszFilename);

  HRESULT   (WINAPI *PG_Read)(RECORD_T* pRecord);

  HRESULT   (WINAPI *PG_Close)(void);

  HRESULT   (WINAPI *PG_PreDownload)(NBFILEDATA hFileData);

  HRESULT   (WINAPI *PG_PostDownload)(NBFILEDATA hFileData);

  HRESULT   (WINAPI *PG_ChunkDownload)(NBFILEDATA hFileData,size_t nIndex,
                                     const unsigned char* pszData,
                                     size_t nLength);

  HRESULT   (WINAPI *PG_PostFilter)(NBPOSTDATA hPostData);

  HRESULT   (WINAPI *PG_FileFilter)(NBFILEDATA hFileData);

  HRESULT   (WINAPI *PG_Version)(VERSION_TYPE* PluginVersion,VERSION_TYPE nNewsbinVersion);

};

// These functions may be called from PG_PostFilter to obtain information
// about a post.
struct _post_callbacks
{
  size_t         PG_nSize;

  const char* (WINAPI *CP_Subject)       (NBPOSTDATA hPostData);
  const char* (WINAPI *CP_From)          (NBPOSTDATA hPostData);
  size_t      (WINAPI *CP_Size)          (NBPOSTDATA hPostData);
  size_t      (WINAPI *CP_MaxRecords)    (NBPOSTDATA hPostData);
  size_t      (WINAPI *CP_CurrentRecords)(NBPOSTDATA hPostData);
  time_t      (WINAPI *CP_Date)          (NBPOSTDATA hPostData);
};

// These functions may be called from PG_FileFilter, PG_PreDownload, and
// PG_PostDownload to obtain information about a file.
struct _file_callbacks
{
  size_t         PG_nSize;
  const char* (WINAPI *CB_Subject)   (NBFILEDATA hFileData);
  const char* (WINAPI *CB_From)      (NBFILEDATA hFileData);
  const char* (WINAPI *CB_Filename)  (NBFILEDATA hFileData);
  size_t      (WINAPI *CB_Size)      (NBFILEDATA hFileData);
  size_t      (WINAPI *CB_MaxRecords)(NBFILEDATA hFileData);
  size_t      (WINAPI *CB_Complete)  (NBFILEDATA hFileData);
  time_t      (WINAPI *CB_Date)      (NBFILEDATA hFileData);
};

// The plugin fills in the structure with information when Newsbin calls
// PG_Read().
struct _record
{
  const char*   pszSubject;
  const char*   pszGroup;
  const char*   pszServer;
  const char*   pszFrom;
  const char*   pszMessageId;
  time_t      nRecordDate;
  size_t      nRecordSize;
  size_t      nRecordNumber;
  size_t      nRecordIndex;
};

// This structure appears to be completely unused.
#ifndef _XOVERDEF
#define _XOVERDEF
typedef struct   _xoverinfo
{
  size_t   m_nRecordNumber;
  size_t   m_nSize;
  char*    m_pszSubject;
  size_t   m_nSubject;
  char*    m_pszFrom;
  size_t   m_nFrom;
  char*    m_pszMessageId;
  size_t   m_nMessageId;
  char*    m_pszDate;
  size_t   m_nDate;
  time_t   m_nUnixDate;
}XOVERINFO,*PXOVERINFO;
#endif

// The DLL must declare this variable and initialise it with function
// pointers to all of the entry points that Newsbin will be calling.
extern ENTRYPOINTS_V2 __declspec(dllexport) Entry;

//
//	Function Prototypes
//	These are provided as a convenience to the developer on the
//	assumption that he uses the suggested names for these routines.
//	Developers are at liberty to use their own names, but then they
//	need to define their own function prototypes.
//

HRESULT   WINAPI PG_Init(PLUGIN_TYPE* pType,
                       FILECALLBACKS_V1* p1,
                       POSTCALLBACKS_V1* p2);

HRESULT   WINAPI PG_Description(char* pszDescription,
                              size_t nLength);

HRESULT   WINAPI PG_Extension(char* pszExtension,
                            size_t nLength);

HRESULT   WINAPI PG_Options(HWND hWnd);

HRESULT   WINAPI PG_Open(const char* pszFilename);

HRESULT   WINAPI PG_Read(RECORD_T* pRecord);
HRESULT   WINAPI PG_Close(void);

HRESULT   WINAPI PG_PreDownload(NBFILEDATA hFileData);
HRESULT   WINAPI PG_PostDownload(NBFILEDATA hFileData);
HRESULT   WINAPI PG_ChunkDownload(NBFILEDATA hFileData,
                                size_t nIndex,
                                const unsigned char* pszData,
                                size_t nLength);
HRESULT   WINAPI PG_PostFilter(NBPOSTDATA hPostData);
HRESULT   WINAPI PG_FileFilter(NBFILEDATA hFileData);

//
// New versioning mechanism. Plugin reports it's version and Newsbin reports it's version
// if the plugin thinks it'll work with this version it returns S_OK;
//
HRESULT   WINAPI PG_Version(VERSION_TYPE* PluginVersion,VERSION_TYPE nNewsbinVersion);

#ifdef __cplusplus
}
#endif

#endif

* DELPHI HEADER FILE

The following is the contents of the Newsbin plugin header file in a format suitable for use with Delphi

nbPluginAPI.pas 

Code: 
unit nbPluginAPI; 

interface 
  uses 
    Windows; 

  type 
    size_t             = LongWord; 
    time_t             = LongWord; 

    NBPOSTDATA         = Pointer; 
    NBFILEDATA         = Pointer; 

    PLUGIN_TYPE        = LongWord; 
    PPLUGIN_TYPE       = ^PLUGIN_TYPE; 

    VERSION_TYPE       = LongWord; 
    PVERSION_TYPE      = ^VERSION_TYPE; 

  const 
    PG_NONE            = $00000000; 
    PG_HEADER_SOURCE   = $00000001; 
    PG_POST_FILTER     = $00000010; 
    PG_FILE_FILTER     = $00000020; 
    PG_PRE_DOWNLOAD    = $00000100; 
    PG_POST_DOWNLOAD   = $00001000; 
    PG_CHUNK_DOWNLOAD  = $00010000; 
    PG_FILE_HANDLER    = $00100000; 
    PG_DOWNLOAD_FILTER = $00200000; 
    PG_HAS_OPTIONS     = $00400000; 
    PG_ALL             = PG_HEADER_SOURCE or PG_POST_FILTER or PG_FILE_FILTER or 
                         PG_PRE_DOWNLOAD or PG_POST_DOWNLOAD or PG_CHUNK_DOWNLOAD or 
                         PG_FILE_HANDLER or PG_DOWNLOAD_FILTER or PG_HAS_OPTIONS; 

  type 
    _post_callbacks = record 
      PG_nSize: size_t; 

      CP_Subject: function(hPostData: NBPOSTDATA): PChar; stdcall; 
      CP_From: function(hPostData: NBPOSTDATA): PChar; stdcall; 
      CP_Size: function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_MaxRecords: function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_CurrentRecords: function(hPostData: NBPOSTDATA): size_t; stdcall; 
      CP_Date: function(hPostData: NBPOSTDATA): time_t; stdcall; 
    end; 

    POSTCALLBACKS_V1 = _post_callbacks; 
    PPOSTCALLBACKS_V1 = ^POSTCALLBACKS_V1; 

    _file_callbacks = record 
      PG_nSize: size_t; 

      CB_Subject: function(hFileData: NBFILEDATA): PChar; stdcall; 
      CB_From: function(hFileData: NBFILEDATA): PChar; stdcall; 
      CB_Filename: function(hFileData: NBFILEDATA): PChar; stdcall; 
      CB_Size: function(hFileData: NBFILEDATA): size_t; stdcall; 
      CB_MaxRecords: function(hFileData: NBFILEDATA): size_t; stdcall; 
      CB_Complete: function(hFileData: NBFILEDATA): size_t; stdcall; 
      CB_Date: function(hFileData: NBFILEDATA): time_t; stdcall; 
    end; 

    FILECALLBACKS_V1 = _file_callbacks; 
    PFILECALLBACKS_V1 = ^FILECALLBACKS_V1; 

    _record = record 
      pszSubject: PChar; 
      pszGroup: PChar; 
      pszServer: PChar; 
      pszFrom: PChar; 
      pszMessageId: PChar; 

      nRecordDate: size_t; 
      nRecordSize: size_t; 
      nRecordNumber: size_t; 
      nRecordIndex: size_t; 
    end; 

    RECORD_T = _record; 
    PRECORD_T = ^RECORD_T; 

    _entrypoints = record 
      PG_nSize: size_t; 

      PG_Init: function(pType: PPLUGIN_TYPE; p1: PFILECALLBACKS_V1; p2: PPOSTCALLBACKS_V1): HRESULT; stdcall; 
      PG_Description: function(pszDescription: PChar; nLength: size_t): HRESULT; stdcall; 
      PG_Extension: function(pszExtension: PChar; nLength: size_t): HRESULT; stdcall; 
      PG_Options: function(hWnd: HWND): HRESULT; stdcall; 

      PG_Open: function(const pszFilename: PChar): HRESULT; stdcall; 
      PG_Read: function(pRecord: PRECORD_T): HRESULT; stdcall; 
      PG_Close: function: HRESULT; stdcall; 

      PG_PreDownload: function(hFileData: NBFILEDATA): HRESULT; stdcall; 
      PG_PostDownload: function(hFileData: NBFILEDATA): HRESULT; stdcall; 
      PG_ChunkDownload: function(hFileData: NBFILEDATA; nIndex: size_t; const pszData: PChar; nLength: size_t): HRESULT; stdcall; 

      PG_PostFilter: function(hPostData: NBPOSTDATA): HRESULT; stdcall; 
      PG_FileFilter: function(hFileData: NBFILEDATA): HRESULT; stdcall; 

      PG_Version: function(PluginVersion: PVERSION_TYPE; nNewsbinVersion: VERSION_TYPE): HRESULT; stdcall; 
    end; 

    ENTRYPOINTS_V2 = _entrypoints; 
    PENTRYPOINTS_V2 = ^ENTRYPOINTS_V2; 

    { 
      Unused at the moment, so why bother porting? 

      // This structure appears to be completely unused. 
      typedef struct   _xoverinfo 
        size_t   m_nRecordNumber; 
        size_t   m_nSize; 
        char*   m_pszSubject; 
        size_t   m_nSubject; 
        char*   m_pszFrom; 
        size_t   m_nFrom; 
        char*   m_pszMessageId; 
        size_t   m_nMessageId; 
        char*   m_pszDate; 
        size_t   m_nDate; 
        time_t   m_nUnixDate; 
      XOVERINFO,*PXOVERINFO; 
    } 
implementation 
end. 
 

* KNOWN BUGS


* COPYRIGHT and CHANGE HISTORY

Copyright

Copyright © 2004 DJI Interprises, LLC. All rights reserved

Change History

The following is the history of changes:

Date Version Changes
June/July 2004 1.0 First Version - Limited circulation
Documentation by 'itimpi' with input from P.B.Clements
September 20041.1Minor changes to reflect small differences introduced in the 4.33 series of Newsbin
Addition of details on how to use Delphi to develop plugins based on input from 'Rick'