[comp.os.vms] NASTY VMSmail bug

blambert@lotus.lotus.com (Brian Lambert) (01/01/91)

Hi:

I ran into a bug in VMSmail the other day that I thought I would make the
net aware of.  It cost me a boatload of time - I hope this saves others
from looking for the same bug.

Hold onto your sneaks, it's a weird one...

It seems that if you create a VMS file with the Stream_LF record format
(the default for VAX C), that is >1951 or so bytes long, with an odd number
of bytes, and try to send the file as a message through VMSmail using
callable mail functions or the MAIL utility (MAIL FILE.EXT USER), you get a
nasty error:

    %MAIL-E-SENDERR, error sending to user BLAMBERT
    %MAIL-W-WRITEERR, error writing DISK$DISK:[BLAMBERT.MAIL]MAIL.MAI;1
    -RMS-F-DUP, duplicate key detected (DUP not set)

If you take the same file, add one byte to it, and issue the same send
operation, it works just fine.  I spent a great deal of time trying to come
up with the exact number of bytes, and it seems to be 1951.  1951 works,
1953 doesn't work.  (Who knows, though, this may have something to do with
the length of the records in the file...)

I called this into DEC, and they told me that it was first documented in
V5.0 of VMS and that I was only the second caller to report the problem. 
Since there are going to more and more of us using callable mail, and many
people use C these days, I think it's going to become a fairly common
problem.

Below is a C code example which shows the bug in action.  The solution is to
open the file and feed it to callable mail one line at a time or to use
some other record format.

brian lambert
Lotus Development Corporation
blambert@lotus.com

----
/*
    A stupid program to illustrate the point.

    cc x.c
    link/notrace f.c (cuts out traceback info)
*/

#include <stdio.h>
#include <string.h>
#include <ssdef.h>
#include <maildef.h>

/*
    Item list 3 datatype.
*/
typedef struct {
    short buffer_len;
    short item_code;
    void *buffer_ptr;
    void *return_len_ptr;
} ITEM3;

/*
    Dummy items for MAIL calls
*/
const ITEM3 dummy_in[] = {
    { 0, MAIL$_NOSIGNAL, 0, 0 },
    { 0, 0, 0, 0 }
};

const ITEM3 dummy_out[] = {
    { 0, 0, 0, 0 },
    { 0, 0, 0, 0 }
};

/*
    To address type.
*/
const short to_type = MAIL$_TO;

/*
    Prototype.
*/
void send(char *filename, char *username, char *subject);

/*
    Main
*/
int main(void)
{
    int i;
    char *user = "blambert";         /* your name here */
    char *goodname = "good.fil";
    char *badname = "bad.fil";
    FILE *badfp, goodfp;

    /*
        Open the files.
    */
    goodfp = fopen(goodname, "w");
    badfp = fopen(badname, "w");

    /*
        Write records to each file.
    */
    for (i = 0; i < 48; i++) {
        fprintf(goodfp, "1234567890123456789012345678901234567890\n");
        fprintf(badfp, "1234567890123456789012345678901234567890\n");
    }

    /*
        Make the bad file contain an odd number of bytes and closem.
    */
    fprintf(badfp, "11\n");
    fclose(goodfp);
    fclose(badfp);

    /*
        Send'em.
    */
    send(goodname, user, "This will get there");
    send(badname, user, "This will not get there!");

    return SS$_NORMAL;
}


/*
    Send a file to a user.
*/
void send(char *filename, char *username, char *subject)
{
    int stat;
    unsigned int send_context = 0;

    /*
        Input items for send add attribute
    */
    ITEM3 set_items[] = {                
        { strlen(username), MAIL$_SEND_TO_LINE, username, 0 },
        { strlen(subject), MAIL$_SEND_SUBJECT, subject, 0 },
        { 0, MAIL$_NOSIGNAL, 0, 0 },
        { 0, 0, 0, 0 }
    };

    /*
        Items for add address.
    */
    ITEM3 addr_items[] = {                
        { strlen(username), MAIL$_SEND_USERNAME, username, 0 },
        { 2, MAIL$_SEND_USERNAME_TYPE, &to_type, 0 },
        { 0, MAIL$_NOSIGNAL, 0, 0 },
        { 0, 0, 0, 0 }
    };

    /*
        Items for send add body part (just filename to send).
    */
    ITEM3 file_items[] = {                
        { strlen(filename), MAIL$_SEND_FILENAME, filename, 0 },
        { 0, MAIL$_NOSIGNAL, 0, 0 },
        { 0, 0, 0, 0 }
    };

    /*
        Do the send begin
    */
    stat = mail$send_begin(&send_context, dummy_in, dummy_out);
    if (stat != SS$_NORMAL)
        lib$signal(stat);

    /*
        Set the attributes
    */
    stat = mail$send_add_attribute(&send_context, set_items, dummy_out);
    if (stat != SS$_NORMAL)
        lib$signal(stat);

    /*
        Add the address
    */
    stat = mail$send_add_address(&send_context, addr_items, dummy_out);
    if (stat != SS$_NORMAL)
        lib$signal(stat);

    /*
        Add the bodypart - file name.
    */
    stat = mail$send_add_bodypart(&send_context, file_items, dummy_out);
    if (stat != SS$_NORMAL)
        lib$signal(stat);

    /*
        Send the message out - use dummy out because we don't want to
	supply MAIL$_NOSIGNAL here.  Mail will signal the nasty error.
    */
    stat = mail$send_message(&send_context, dummy_out, dummy_out);
    if (stat != SS$_NORMAL)
        lib$signal(stat);

    return;
}

/*
    END!
*/