|
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).
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.
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.
The first thing you need to do is create yourself a new Visual C++ project. The easiest way to do this is to:
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:
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;
}
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.
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.
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);
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 .
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
S_OK if it is able to proceed E_ABORT if not.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;
^
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.
^
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
S_OK if a description has been returned or E_ABORT (if for instance the buffer was too short!).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:
S_OK if the file extensions were returnedE_ABORT if not (if for instance the buffer was too short!).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.
^
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
S_OKIf 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.:
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
S_OK if it wants Newsbin to proceedE_ABORT if notIf 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).
^
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
S_OK if it has filled in the structure and everything is OKS_DATA_DONE if there is no more information
availableE_ABORT if an error occurred.
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)?
^
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
S_OKHRESULT 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
S_OK to indicate that Newsbin should continue
with the downloadE_ABORT if not.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.
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
S_OK to indicate ???.
E_ABORT to indicate ???.
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 |
| pszData | A pointer to the (decoded) data |
| nLength | The length of the decoded data. |
The plugin should return
S_OK to indicate that the chunk is
OK.E_ABORT to indicate that there is a problem and the rest of the download
should be abortedPBClements 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).
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:
S_OK The post is added to the Post ListE_ABORT The Post is not added to the Post list and this post is
not loaded into memoryHRESULT 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:
S_OK the post is added to the Download ListE_ABORT The Post is not added to the Download listIf 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:
S_OK the post is shown in the Post listE_ABORT The post is not shown in the Post ListHow 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:
S_OK if the plugin thinks it'll work with this version of NewsbinE_ABORT if notThey 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)
| Capability | Call Sequence | Description |
|---|---|---|
| *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_Extension | This will be called to see if the plugin supports the extension for the file. If not, then no further calls are made. |
| PG_Open | This is called to specify the file that the Newsbin user selected. | |
| PG_Filefilter | This 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_Close | Called at the end to terminate the sequence of events | |
| PG_HEADER_SOURCE | PG_Extension (twice) | |
| PG_Init | ||
|
PG_HEADER_SOURCE | PG_FILE_HANDLER | PG_Extension (twice) | |
| PG_Init | ||
| PG_Open | This is called to specify the file that the Newsbin user selected. | |
| PG_Read | Called repeatedly until a S_DATA_DONE return is made | |
| PG_Close | Called at the end to terminate the sequence of events | |
| PG_POST_FILTER | PG_Postfilter | Called 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_FILTER | PG_Filefilter | Called 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_Filefilter | All calls to PG_Postfilter occur before any calls to PG_Filefilter | |
| PG_PRE_DOWNLOAD | PG_PreDownload | Once before any file starts downloading. |
| PG_POST_DOWNLOAD | PG_PostDownload | Once after any file completes downloading. CB_Filename() will give you the full path to the downloaded file |
| PG_CHUNK_DOWNLOAD | PG_ChunkDownload | Called 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_FILTER | PG_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_OPTIONS | PG_Options | when plugin selected and Properties button pressed on Preferences->Plugins dialog |
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.
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;
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;
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 |
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
|
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.
|
No Plugin to Process this File type MF_PT - Plugin Failed to open File: R:\filename.part0.sfvPG_ChunkDownload method is called if
you have defined one even if you have not specified
PG_CHUNK_DOWNLOAD as a capability of the
pluginPG_PostDownload method has no way of
locating the file that has just been downloaded (except perhaps by searching
the disk) as the CB_Filename callback function
does not provide the path to the file.
| Date | Version | Changes |
|---|---|---|
| June/July 2004 | 1.0 | First Version - Limited circulation Documentation by 'itimpi' with input from P.B.Clements |
| September 2004 | 1.1 | Minor 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' |
|