The technique described in the previous section, using the LSV_send_command function to send commands over TCP/IP to LISTSERV, will work for most one-line LISTSERV commands, such as ADD, DELETE, SET, SHOW, etc. However, some commands require a different or modified approach:

    • Creating or replacing a list header
    • Adding or replacing a password
    • Bulk operations
    • Commands that respond over e-mail
    • Application-friendly commands

There may also be some special error handling involved.

10.4.1 Creating or Replacing a List Header


Multi-line commands cannot be sent through the TCPGUI interface.  Therefore, the usual approach to send a list header to LISTSERV (the PUT command followed by the multiple lines of the header) cannot be used. Instead, TCPGUI has a special command for sending a list header: X-STL.

The syntax of the X-STL command is:

X-STL listname header-data


where header-data is a single text string that contains every header line (including the leading asterisk), one after the other, formatted in a particular way:

      • A count of the number of characters in the header line, to include the asterisk in column 1
      • An underscore character (ASCII 0x5F; don't use Unicode or Microsoft "smart characters")
      • The header line itself

Finally, the last header line in the string should not have any trailing spaces.

Thus, if the command you would use to put your list through e-mail was:

PUT TEST LIST PW=ABCDE
* test

*
* Owner=joan@example.com
* Notebook=No
* Confidential=Yes


For Windows, the command you send through the TCPGUI interface would be:

X-STL TEST 6_* test1_*25_* Owner= joan@example.com13_* Notebook=No18_* Confidential=Yes


For unix, you will have to escape the asterisks, as described in 10.2 Running LCMDX :

X-STL TEST 6_\* test1_\*25_* Owner= joan@example.com13_\* Notebook=No18_\* Confidential=Yes


(Remember that this is a single-line command -- there should be no line-break characters other than at the very end of the line.)  For clarity, ach of the five lines in the header are represented as follows:

Standard header line (emailed)

Number of characters

X-STL formatted header line

(For unix, add the backslash for the asterisks as noted above)

* test

6

6_* test

*

1

1_*

* Owner=joan@example.com

25

25_* Owner= joan@example.com

* Notebook=No

13

13_* Notebook=No

* Confidential=Yes

18

18_* Confidential=Yes


(The second line is, of course, a blank header line containing a single asterisk, so it is only one character long.)

Note:   If double quotes are required (for instance, for the Prime= or Sender= keyword settings), you MUST escape them with a backslash character, thus:


lcmdx listserv.example.com xxxxxxxxx@example.com XXXXX X-STL XXXXXXXXX 6_* test1_*30_* Owner= xxxxxxxxx@example.com13_* Notebook=No18_* Confidential=Yes49_* Sender= \”test <xxxxxxxxx@listserv.example.com>\”


(remember that the entire command must be on one line, not wrapped as is unavoidable in this document). Additionally, when counting characters for the line counts, the backslash-double quote combination counts as a single character. If the line count is wrong, you will get an “Error in header data stream” message.

10.4.2 Adding or Replacing a Password


You can’t send a “PW ADD” or “PW REP” command through the TCPGUI interface. There is a special command for adding a password:

X-PWADD address password


The LSV_send_command function generally requires a password, which you don’t necessarily have when you’re in the process of adding one. In this case, you can send the command anonymously through TCPGUI by using the anonymous e-mail address “@” as the “originator” in the call to LSV_send_command (but not in the X-PWADD command, obviously).

When LISTSERV receives the X-PWADD command, it sends e-mail to the address provided, requesting confirmation. E-mail confirmation is the only way for LISTSERV to determine that the e-mail address provided is truly the correct address. Therefore, an application should never count on the password being immediately available. A message to the user, letting them know that they can continue after they have successfully confirmed the password registration, may be advisable.

10.4.3 Bulk Operations


As noted above, the TCPGUI interface can only handle single-line commands. Therefore bulk operations, such as the bulk add and delete commands cannot be sent through TCPGUI, and can only be sent through the mail. The only ways to send bulk adds and deletes from an application are:

    • Turn them into individual add or delete commands, and send each of these through the TCPGUI interface. If there are many subscribers to add or delete, this can extremely slow, and is therefore not recommended.
    • Write a mail file containing the bulk add or delete job and use a local mail application to send it to LISTSERV.
    • Open up a connection with the SMTP port on the LISTSERV host and use SMTP commands (documented in RFC821) to send a mail file (documented in RFC822) containing the bulk add or delete job. This is essentially the same thing as the previous bullet, except that in the previous case, you used a third-party application to send the file, whereas in this case you will either type the file manually or use cut and paste into your telnet client to perform the operation.

10.4.4 Commands that Respond Over Email


LISTSERV will accept almost any one-line command through the TCPGUI interface. The answer you receive through the TCPGUI interface, however, may not always be what you expected. Commands whose responses tend to be long will generally be sent through e-mail. LISTSERV will always send the response to the following commands back through e-mail:

    • INFO
    • INDEX
    • LISTS DETAILED
    • LISTS GLOBAL
    • LISTS SUMMARY
    • GETPOST
    • GET for anything other than a list header

For these other commands, LISTSERV will send the response back through e-mail unless you use an option to make it come back through the TCPGUI interface:

    • GET listname – requires the “(MSG” option
    • REVIEW – requires the “MSG” option

10.4.5 Application-Friendly Commands


Some commands that can be sent through the TCPGUI interface and to which LISTSERV will send the response back through the TCPGUI interface nevertheless have responses that are human-friendly but not application-friendly. One such command is the QUERY command, which sends a response that looks like:

Subscription options for Joan Smith, list MYLIST:

MAIL           You are sent individual postings as they are received 
MIME           You prefer to receive messages in MIME format
SUBJECTHDR             Full(normal) mail headers with list name in message subject 
REPRO          You receive a copy of your own postings
NOACK          No acknowledgement of successfully processed postings 

Subscription date: 12 Mar 2020

The topics you subscribe to are: Mytopic, Other


To assist the application developer, the LISTSERV provides alternate commands for the following:

      • QUERY
      • A special option for QUERY: DEFSUB
      • SCAN

There are also advanced options for the GET command which can be used with changelog files:

      • GET with MSG and COLumns(...)

Finally, there is an alternate command to read and parse list headers in an application-friendly way:

      • SHOW X-LISTKWD

10.4.5.1 QUERY

To QUERY subscriber options from an application you should use:

QUERY ***GUI*** listname [FOR address]


The response will look like the following:

***HDR*** e-mail-address
***NAME*** firstname lastname

***OPT*** option1
***OPT*** option2
...
***OPT*** optionN

***SUBDATE*** date
***TOPICS*** subscriber-topics
***TOPLIST*** list-topics
***HDR*** e-mail-address (next subscriber)
etc.

N matching entries found.


Where:

    • The “***HDR***” line denotes the beginning of the subscription settings for a particular subscriber (recall that the QUERY command could yield information for multiple subscriptions) and identifies the e-mail address of the subscription.
    • The “***NAME***” line provides the full name stored for that subscriber.
    • The “***OPT***” lines each show one option set for the subscription. The first option would always be MAIL or NOMAIL. All the other options are only those options that are NOT the default options in LISTSERV (as opposed to the default options set for the particular list -- these do not apply here).
    • The “***SUBDATE***” line contains the subscription date.  (If the user subscribed to the list prior to LISTSERV 1.8c, the subscription date is not stored and this line will not appear.)
    • The “***TOPICS***” line lists the topics which are selected for this subscription.
    • The “***TOPLIST***” line lists all the topics that are available for the list, regardless of whether this particular subscription has them selected, except for “ALL” and “OTHER”.
    • These lines are repeated for each matching entry found.
    • The last line gives a count of matching entries found.

It should be noted that for scripting purposes, it is possible to use a wildcard rather than a single list name with this command:

QUERY ***GUI*** * [FOR address]


TIP:   It should be noted that for reporting or command-line scripting purposes, it is possible to use a wildcard rather than a single list name with this command:


QUERY ***GUI*** * [FOR address]


This makes it possible to write a script that, for instance, can produce a simple list of all lists to which a given subscriber is subscribed.  While this is not sufficient for GDPR-type purposes, such a script using the easily-parsed QUERY ***GUI*** command could be more useful to the system administrator than the only other alternative, which is the overly-verbose 'Query * FOR address' command.


While it is also possible to use the wildcard to produce web output via TCPGUI, one should do so with care, as the amount of data returned may be more than you wish to display on a single web page.

10.4.5.2 QUERY DEFSUB

There is also a special QUERY command for obtaining default subscription options:

QUERY ***GUI*** ***DEFSUB*** listname


If this command is sent using an address that is subscribed to the given list, it works exactly the same as the “QUERY ***GUI***” command described above. If it is sent anonymously (see “Adding or replacing a password” above) or from an address which is not subscribed, then the first line in the response is:

***DEF***


And the remainder of the response is the same as the response to “QUERY ***GUI***” for a subscriber with the list’s default subscription settings.

10.4.5.3 SCAN

To scan a list for a pattern, you should use

SCAN ***GUI*** listname pattern 


The response will look like the following:

***MBX*** user1@host.domain
Firstname Lastname <user1@host.domain>
***MBX*** user2@host2.domain
“Full name w/ special characters” <user2@host2.domain> etc.
***END***
SCAN: N matches.

10.4.5.4 Changelog access

Please note that this feature is not available under LISTSERV Lite.

Changelogs may be accessed via TCPGUI by using the GET command options MSG and COLumns(...). Both are designed primarily for GUI use, and while they have been documented, there was no attempt at making the syntax intuitive to end users, as they are not expected to use this feature. The design goal was a syntax that is easy to generate and parse.

The MSG option (named for compatibility with the REVIEW option by the same name) simply tells LISTSERV to return the contents of the file as “interactive messages,” which for TCPGUI means that the data will be returned as command output. Thus the option is effective only with text files, as this is not a binary channel. 

The COLumns(...) option tells LISTSERV to filter records from the file before sending it. The syntax is as follows: 

COLumns(colspec1 colfilter1 [colspec2 colfilter2[...]]) 


Where:

    • ‘colspec’ defines the column to which the filter is to be applied. It can be either a-b (positions a to b, inclusive), a- (a through end of line), a.b (positions a to a+b-1) or Wa (blank-delimited word number a). 
    • ‘colfilter’ is either a-b, a-, -b or a. The latter is equivalent to a-a, whereas the two incomplete forms only check one of the bounds. There must not be any spaces surrounding the hyphen in a-b, and neither a nor b may contain spaces or parentheses. 

It is possible to specify as many different columns as desired. Each column must match its respective filter in order for the record to be selected. 

It is also possible to specify the same column multiple times (for this purpose, equivalent a-b and a.b specifications are considered the same column). This provides multiple acceptable ranges for the column in question. 

It is important to understand how exactly comparisons take place: 

    • All operands are treated as case-insensitive strings, even if they happen to be valid numbers. Thus, 9.00 is greater than 200. 
    • Comparisons proceed from left to right and stop at the first differing character. There is no concept of column alignment; in particular, leading blanks are significant (this is not applicable if you defined the column as Wa). 
    • If the reference is shorter than the column, the comparison ends successfully when the last character in the reference has been compared. For instance, if you search a changelog for W1 200101-200102, effectively only the first 6 characters will be searched. 
    • If the column is shorter than the reference, it is considered to be padded with blanks. This usually fails the record. 
    • Another way to describe it would be to say that the reference, which is well-defined, takes precedence over the record column, which could vary widely. If the reference is too short, the remainder of the column is ignored. If the column is too short, it will usually not match. 
    • Comparisons succeed for their bounds, i.e. 2000-3000 will select both 2000 and 3000. 
    • Comparisons that are bound to fail will fail as expected. If you look for a string of 4 characters in a column that is defined to be only 3 wide, you will find no match and there will be no error. 

If there are no matches, the file is not sent and the message “No matching records” is sent instead as command response. This message is suppressed when the MSG option is used; you simply get an empty response.

To select ADD or DELETE records in 2018 in a changelog, you could use COL(1-4 2018 W2 ADD W2 DELETE)

Note that this feature works with any file, not just changelogs. In particular, it works with archived changelogs.  

Example:  To show all POST operations for MYLIST-L since 1 June 2017, 

GET MYLIST-L CHANGELOG (MSG COLUMNS(W1 20170601- W2 POST)


The COLUMNS() option was enhanced in LISTSERV version 16.5 to make it easier to search changelog files for specific email addresses (primarily for the purpose of GDPR reporting).  This was accomplished by adding a comparison sub-option (in addition to all of the existing comparison sub-options documented above) which allows searches using an optional data type followed by a pattern, as follows:

[^]=typepattern

Where:

    • ‘^’ is an optional negation
    • type is a mandatory single character indicating the type of comparison
    • pattern is the text to match the requested column to; as usual, a match will select the line for inclusion, and no match will try the next column/pattern pair, if any
    • No spaces are allowed in the operand

Type can be:

    • ‘=’: column must equal pattern (this is not a substring match)
    • ‘W’: pattern must appear in column as a space-delimited word
    • ‘*’: wildcard matching of column to pattern

The ‘=’ option is arguably redundant since COLUMNS(16- ==ADD) does the same thing as COLUMNS(16- ADD), but it can come in handy if you want to match to a pattern containing a hyphen, or starting with an equal sign.

In a GDPR context, you might do:

GET listname.CHANGELOG (MSG COL(16- =Wemailaddr)


to pull all records out of the listname.CHANGELOG file that contain the space-delimited email address (represented by emailaddr in the example) anywhere from column 16 of the record to the end of the record.  For an email address such as joe@example.com, this might include records such as

20170329114508 POST joe@EXAMPLE.COM This is the subject of my email.

10.4.5.5. The SHOW X-LISTKWD command

Info:  SHOW X-LISTKWD cannot be issued by list subscribers without higher privilege.  Execution requires list owner or LISTSERV maintainer privileges and authentication (personal password, OK cookie confirmation).  List owners who are not LISTSERV maintainers may execute the command only against lists they own.

Tip:  SHOW X-LISTKWD can be used to display exactly what LISTSERV thinks is set in the list configuration for any given list.  It can be quite useful as a diagnostic tool to determine why something is not working "as advertised" in your list configuration.

SHOW X-LISTKWD is another LISTSERV internal command, which can be useful for determining via a script what lists exist on the server, and/or what options are set for a specified list.  

The formal syntax is

SHOW X-LISTKWD *


or


SHOW X-LISTKWD keyword[,keyword2,...keywordn] * | listname [listname2,...listnamen]


For instance,

SHOW X-LISTKWD *


sends back a list of the lists on your server in a machine-readable format:

***LIST*** ODBCTEST
***LIST*** TEST-NONOTEBOOK
***LIST*** TEST


This is similar to the human-friendly output of the LISTS command, which would probably provide more information than you need:

ODBCTEST          OBDC test list
TEST              Test list
TEST-NONOTEBOOK   Test list with no notebook


But the command is much more powerful than this.  Let's add "OWNER" as a parameter before the asterisk, and make the command

SHOW X-LISTKWD OWNER *


***LIST*** ODBCTEST
OWNER nathan@EXAMPLE.COM
***LIST*** TEST-NONOTEBOOK
OWNER nathan@EXAMPLE.COM
***LIST*** TEST
OWNER nathan@EXAMPLE.COM jdoe@EXAMPLE.COM


That's more like it; now you have a tool to provide an easily-parsed list of all of your list owners, by list.  Note that if you have defined an owner address that contains characters that need to be escaped, e.g.,

Owner= "john.o'shaugnessy"@example.com


, that address will show up in the output without the double-quotes, e.g., 

***LIST*** TEST
OWNER nathan@EXAMPLE.COM john.o'shaugnessy@EXAMPLE.COM


But what if we want to know who all the owners, editors, and moderators are for all lists on the server?  Simple, comma-separate the three keywords and use a wildcard instead of a list name:

SHOW X-LISTKWD OWNER,EDITOR,MODERATOR *


***LIST*** ODBCTEST
OWNER nathan@EXAMPLE.COM
***LIST*** TEST-NONOTEBOOK
OWNER nathan@EXAMPLE.COM
EDITOR nathan@EXAMPLE.COM
MODERATOR ALL nathan@EXAMPLE.COM
***LIST*** TEST
OWNER nathan@EXAMPLE.COM jdoe@EXAMPLE.COM
EDITOR jdoe@EXAMPLE.COM (TEST)


Important:  When specifying multiple keywords with SHOW X-LISTKWD, there cannot be any spaces between the keywords and the commas.  You MUST specify multiple keywords exactly as shown, i.e.,


keyword1,keyword2,keyword3


       and so forth, for as many keywords as you are specifying.  If any spaces are found in the keywords, the command fails with no output.  This command was designed to be issued by an automated process, so it expects a rather rigid syntax.  If your calls to SHOW X-LISTKWD are returning no data, check to ensure that you have not inadvertently put spaces between the keywords and their commas.


In summary,


SHOW X-LISTKWD OWNER,EDITOR,MODERATOR *


works, but


SHOW X-LISTKWD OWNER, EDITOR, MODERATOR *

SHOW X-LISTKWD OWNER , EDITOR , MODERATOR *


or any other combination of keywords, commas, and spaces, do not work.


Tip:  When issued by a list owner who is not a LISTSERV maintainer, this command will output information only for the lists owned by that list owner

Note:  If you have set the "Quiet:" parameter in the Owner= keyword, it will show up like this:


***LIST*** TEST
OWNER nathan@EXAMPLE.COM QUIET: jdoe@EXAMPLE.COM

If you want to issue the SHOW X-LISTKWD command against a single list, simply specify it instead of the wildcard:

SHOW X-LISTKWD OWNER,EDITOR,MODERATOR TEST


***LIST*** TEST
OWNER nathan@EXAMPLE.COM jdoe@EXAMPLE.COM
EDITOR jdoe@EXAMPLE.COM (TEST)


You may specify multiple lists (space-separated) if you choose:

SHOW X-LISTKWD OWNER,EDITOR,MODERATOR TEST ODBCTEST


***LIST*** TEST
OWNER nathan@EXAMPLE.COM jdoe@EXAMPLE.COM
EDITOR jdoe@EXAMPLE.COM (TEST)
***LIST*** ODBCTEST
OWNER nathan@EXAMPLE.COM


Tip:  When you specify list names explicitly, the results will display in the same order in which you specified the list names.

The SHOW X-LISTKWD command works with any documented list header keyword.  For instance:

SHOW X-LISTKWD NOTEBOOK,DIGEST TEST


***LIST*** TEST
NOTEBOOK YES E:\LISTSERV\LISTS\TEST MONTHLY PRIVATE
DIGEST YES SAME DAILY BOTTOM_BANNER


SHOW X-LISTKWD NOTEBOOK,DIGEST TEST


***LIST*** TEST
DEFAULT-OPTIONS NOACK REPRO


SHOW X-LISTKWD SUBSCRIPTION *


***LIST*** ODBCTEST
SUBSCRIPTION BY_OWNER
***LIST*** TEST-NONOTEBOOK
SUBSCRIPTION CLOSED
***LIST*** TEST
SUBSCRIPTION OPEN


and so forth.  If a particular keyword is not set explicitly for a given list, there is simply no output for that keyword:

SHOW X-LISTKWD SUBSCRIPTION,NOTIFY *


***LIST*** ODBCTEST
SUBSCRIPTION BY_OWNER
***LIST*** TEST-NONOTEBOOK
SUBSCRIPTION CLOSED
NOTIFY YES
***LIST*** TEST
SUBSCRIPTION OPEN


In this case, only the TEST-NONOTEBOOK list has a setting for Notify.  You can assume at that point that the other lists are operating with the default setting (for Notify=, it would be "Yes", so in reality, all three of the lists are set to "Notify= Yes").

10.4.6 Error handling


When the command you send produces an error, the TCPGUI interface sends back the exact error that you would receive through the mail when that error is detected by LISTSERV (for example, if you use the wrong listname, or misspell a command).

However, the TCPGUI interface does have some error messages specific to it, for when the error occurs within TCPGUI rather than LISTSERV. These are:

    • ***NOPW*** -- the e-mail address does not have a password registered with LISTSERV
    • ***BADPW*** -- the password provided in the TCPGUI command does not match the password registered for the given e-mail address.