From:	CSBVAX::CSBVAX::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 21-MAR-1989 11:43
To:	MRGATE::"ARISIA::EVERHART"
Subj:	HOW TO CALL MAIL


Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Tue, 21 MAR 89 08:01:04 PDT
Received: from nrl3.arpa (CCF.NRL.NAVY.MIL) by KL.SRI.COM with TCP; Tue, 21 Mar 89 07:46:50 PST
Date: 20 Mar 89 19:31:00 EST
From: "V6550C::YOUNGDALE" <youngdale%v6550c.decnet@nrl3.arpa>
Subject: HOW TO CALL MAIL
To: "info-vax" <info-vax@kl.sri.com>

	There has been a lot of traffic recently having to do with the
callable mail interface and the lack of information of how to use it.  Last
October, Bill Palter (sunybcs!victor!palter@rutgers.edu) posted a list of the
routines, which gave a vague description of how one goes about using them.
Taking this as a starting point, I have written a program that will send
and read mail messages, and print out a list of the folder names in the default
file.  I have included this program at the end of this message.
     	The description that was posted last fall is large (176 blocks) so
I will not repost it unless there is an avalanche of requests for it.
Although it is vague, it is still possible to glean informaion from it
as to the calling procedures that are used for the various routines.  What
makes it nice is that nearly all of the calls have 3 arguments - a context
variable, an input item list, and an output item list.  The structure of
the item lists are given, and thus the only problem involved in calling
mail is to figure out which item codes should be used by which subroutines,
and the order of the subroutine calls.  This can be done by trial and error,
since MAIL will give error messages if you make a mistake.  Most of the
item codes are divided up by their context and should be used only in
subroutines for that same context.  In addition, sometimes you need to guess
about the argument passing conventions and data types of some of the arguments.
	It did not take me very long to write this program.  Actually,
once I had written the first subroutine(send), and figured out the general
procedure for doing things, the others went much more quickly. Further
extensions the capabilities should prove even easier.  (If you want a list of
the item codes, it is given in module $MAILDEF in SYS$LIBRARY:FORSYSDEF.TLB).
	Finally, I would like to say that I cannot claim that 100% of the
information in the program is correct.  Since I do not have a real manual,
I have had to make guesses about how things work, and the statements are
based upon what would seem to be correct based upon the behavior of test
programs.
------------------------------------------------------------------
Eric Youngdale          INTERNET:  YOUNGDALE%V6550C.DECNET@CCF3.NRL.NAVY.MIL
Code 6551		      or:  YOUNGDALE%V6550C.DECNET@NRL.ARPA
Naval Research Lab	    SPAN:  11.14 (or 11277::)
Washington, DC  20375	 ATT-NET:  (202) 767-3276	  
                       FLAME-NET:  NLA0:
	Disclaimer:  The opinions expressed herein are mine and not necessarily
those of my employer.
------------------------------------------------------------------

$! ...................... Cut between dotted lines and save. .....................
$!.............................................................................
$! VAX/VMS archive file created by VMS_SHARE V06.03 20-Oct-1988.
$!
$! VMS_SHARE was written by James Gray (Gray:OSBUSouth@Xerox.COM) from
$! VMS_SHAR by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au).
$!
$! To unpack, simply save, concatinate all parts into one file and
$! execute (@) that file.
$!
$! This archive was created by user YOUNGDALE
$! on 20-MAR-1989 19:05:44.01.
$!
$! It contains the following 1 file:
$!        TMAIL.FOR
$!
$!==============================================================================
$ SET SYMBOL/SCOPE=( NOLOCAL, NOGLOBAL )
$ VERSION = F$GETSYI( "VERSION" )
$ IF VERSION .GES "V4.4" THEN GOTO VERSION_OK
$ WRITE SYS$OUTPUT "You are running VMS ''VERSION'; ", -
    "VMS_SHARE V06.03 20-Oct-1988 requires VMS V4.4 or higher."
$ EXIT 44 
$VERSION_OK:
$ GOTO START
$
$UNPACK_FILE:
$ WRITE SYS$OUTPUT "Creating ''FILE_IS'"
$ DEFINE/USER_MODE SYS$OUTPUT NL:
$ EDIT/TPU/COMMAND=SYS$INPUT/NODISPLAY/OUTPUT='FILE_IS'/NOSECTION -
    VMS_SHARE_DUMMY.DUMMY

b_part := CREATE_BUFFER( "{Part}", GET_INFO( COMMAND_LINE, "file_name" ) );
s_file_spec := GET_INFO( COMMAND_LINE, "output_file" );SET( OUTPUT_FILE
, b_part, s_file_spec ); 
b_errors := CREATE_BUFFER( "{Errors}" );i_errors := 0; 
pat_beg_1 := ANCHOR & "-+-+-+ Beginning";
pat_beg_2 := LINE_BEGIN & "+-+-+-+ Beginning";
pat_end := ANCHOR & "+-+-+-+-+ End";
pat_trail := " " & LINE_END;POSITION( BEGINNING_OF( b_part ) ); LOOP b 
:= SEARCH( pat_trail, FORWARD); EXITIF b=0; POSITION( END_OF( b ) )
; LOOP MOVE_HORIZONTAL( -1 ); EXITIF CURRENT_CHARACTER <> ' '; ERASE_CHARACTER
( 1 ); EXITIF CURRENT_OFFSET=0; ENDLOOP; ENDLOOP; POSITION( BEGINNING_OF
; s_x := ERASE_CHARACTER( 1 ); 
  IF s_x = "+" THEN r_skip := SEARCH( pat_beg_1, FORWARD, EXACT ); IF r_skip <
> 0 THEN 
      s_x := ""; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ERASE_LINE; ENDIF; ENDIF; 
  IF s_x = "-" THEN r_skip := SEARCH( pat_end, FORWARD, EXACT ); IF r_skip <
> 0 THEN 
      s_x := ""; MOVE_HORIZONTAL( -CURRENT_OFFSET ); m_skip := MARK( NONE )
; r_skip := SEARCH( pat_beg_2, FORWARD, EXACT ); IF r_skip <> 0 THEN POSITION
( END_OF( r_skip ) ); MOVE_HORIZONTAL( -CURRENT_OFFSET ); MOVE_VERTICAL( 1 )
; MOVE_HORIZONTAL( -1 ); ELSE POSITION( END_OF( b_part ) ); ENDIF; ERASE
( CREATE_RANGE( m_skip, MARK( NONE ), NONE ) ); ENDIF; ENDIF; 
  IF s_x = "V" THEN 
    s_x := ""; IF i_append_line <> 0 THEN APPEND_LINE; MOVE_HORIZONTAL
( -CURRENT_OFFSET ); ENDIF; i_append_line := 1; MOVE_VERTICAL( 1 ); ENDIF; 
  IF s_x = "X" THEN 
    s_x := ""; IF i_append_line <> 0 THEN APPEND_LINE; MOVE_HORIZONTAL
( -CURRENT_OFFSET ); ENDIF; i_append_line := 0; MOVE_VERTICAL( 1 ); ENDIF; 
  IF s_x <> "" THEN i_errors := i_errors + 1; s_text := CURRENT_LINE; POSITION
( b_errors ); 
    COPY_TEXT( "The following line could not be unpacked properly:" )
; SPLIT_LINE; COPY_TEXT( s_x ); COPY_TEXT( s_text ); POSITION( b_part )
; MOVE_VERTICAL( 1 ); ENDIF; ENDLOOP; POSITION( BEGINNING_OF( b_part ) ); LOOP 
  r_x := SEARCH( "`", FORWARD, EXACT ); EXITIF r_x = 0; POSITION( r_x )
; ERASE_CHARACTER( 1 ); 
  IF CURRENT_CHARACTER = "`" THEN MOVE_HORIZONTAL( 1 ); ELSE COPY_TEXT( ASCII
( INT( ERASE_CHARACTER( 3 ) ) ) ); ENDIF; ENDLOOP; IF i_errors = 0 THEN SET
( NO_WRITE, b_errors, ON ); ELSE POSITION( BEGINNING_OF( b_errors ) ); 
  COPY_TEXT( FAO( "The following !UL errors were detected while unpacking !AS"
, i_errors, s_file_spec ) ); SPLIT_LINE; 
  SET( OUTPUT_FILE, b_errors, "SYS$COMMAND" );ENDIF; EXIT; 
$ DELETE VMS_SHARE_DUMMY.DUMMY;*
$ CHECKSUM 'FILE_IS
$ WRITE SYS$OUTPUT " CHECKSUM ", -
  F$ELEMENT( CHECKSUM_IS .EQ. CHECKSUM$CHECKSUM, ",", "failed!,passed." )
$ RETURN
$
$START:
$ FILE_IS = "TMAIL.FOR"
$ CHECKSUM_IS = 1618418156
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X`009program callable_mail
Xc This program demonstrates how to use callable mail.  There are
Xc 3 different subroutines (plus support subroutines).
Xc`009The first one sends a mail message.  The message can either be in the
Xc form of strings or a filename which is read by MAIL.  In this example
Xc program, the mail message is sent to yourself.
Xc`009The second subroutine reads mail from the default mail file.
Xc It is set up to read all of the messages in the MAIL folder
Xc and print them on the screen, but there are lines commented out
Xc that demonstrate how to select messages based on sender, date, etc.
Xc`009The third subroutine prints out a list of the names of the
Xc folders in the default mail file.
Xc
Xc`009`009`009`009`009-ERY
Xc`009`009`009`009`0093/17/89
Xc
X      `009include '($jpidef)/nolist'
X`009character*1 c
X`009character*12 username
X`009character*30 folder
X`009character*30 filenm
Xc we want the username for the process that is running this program.
Xc when we send mail in this example program, can send it to that same
Xc username
X`009i=lib$getjpi(jpi$_username,,,,username,ilen)
X`009if(.not.i) call lib$signal(%val(i))
Xc trim spaces from the end of the username
X`009ijk=index(username(1:ilen),' ')
X`009if(ijk.ne.0) ilen=ijk-1
X`009type 100
X`009accept 101,c
X`009if((c.eq.'F').or.(c.eq.'f')) then
X`009`009type 110
X`009`009accept 111,iflen,filenm
X`009`009call send_mail(1,username(1:ilen),filenm(1:iflen))
X`009`009endif
X`009if((c.eq.'S').or.(c.eq.'s'))`032
X     1 call send_mail(0,username(1:ilen),' ')
X`009if((c.eq.'R').or.(c.eq.'r')) then`032
X`009`009type 112
X`009`009accept 111,ifold_len,folder
X`009`009call read_mail(username(1:ilen),folder(1:ifold_len))
X `009`009endif
X`009if((c.eq.'D').or.(c.eq.'d')) call dir_folder
X100`009format(' Enter option(F,S,R,D):',$)
X101`009format(a1)
X110`009format(' Enter name of file to send:',$)
X111`009format(q,a30)
X112`009format(' Enter name of the folder to read:',$)
X`009end
X
X
X`009subroutine send_mail(file_send,username,filenm)
Xc this program sends a mail message using callable mail
Xc
X`009include '($maildef)/nolist'
X`009structure /mail_item_array/
X`009integer*2 len
X`009integer*2 item_code
X`009integer*4 address
X`009integer*4 ret_len_addr
X`009end structure
X
X`009character*(*) username,filenm
Xc we make the array 1 larger than the actual number of items in the list.
Xc the last record will have all fields zero, and signal the end of the list
X`009record /mail_item_array/ mess_info(4),null,body(2),address(2)
X`009integer file_send
Xc changing the "From:" field requires SYSPRV
Xc If you do not have this priv, the item is ignored
X  `009call setup(address,MAIL$_SEND_USERNAME,username,)
X`009call setup(mess_info(1),MAIL$_SEND_FROM_LINE,'Elvis',)
X`009call setup(mess_info(2),MAIL$_SEND_SUBJECT,'this is a test',)
X`009call setup(mess_info(3),mail$_send_cc_line,'Circular file',)
X`009if(file_send.eq.0) then
X`009  call setup(body,MAIL$_SEND_RECORD,
X     1'This is the text of the mail message',)
X`009else
Xc the file is sent as the text of the message if file_send=1
X`009  call setup(body,MAIL$_SEND_FILENAME,filenm,)
X`009endif
Xc
Xc I am not checking the status codes, since the MAIL$ routines already
Xc print out error messages.  This can be turned off of course, but for
Xc my purposes this is easier.
Xc
X`009CALL mail$send_begin(CONTEXT, null, NULL)
X `009CALL mail$send_add_attribute(CONTEXT, mess_info, NULL)
X        CALL MAIL$SEND_ADD_bodypart(CONTEXT, body, NULL)
Xc if we are not sending a file, then we can add additional lines
Xc to the file by continuing to call the add bodypart subroutine.
Xc
Xc Somehow this reminds me of Frankenstein - we  keep adding these bodyparts
X`009if(file_send.eq.0) then
Xc send the same line three times - for lack of variety.
X          CALL MAIL$SEND_ADD_bodypart(CONTEXT, body, NULL)
X          CALL MAIL$SEND_ADD_bodypart(CONTEXT, body, NULL)
X`009endif
X        CALL MAIL$SEND_ADD_ADDRESS(CONTEXT, address, NULL)
X `009CALL mail$send_message(CONTEXT, NULL, NULL)
X `009CALL mail$send_end(CONTEXT, NULL, NULL)
X`009return
X `009end
X
X
X`009subroutine read_mail(username,folder_name)
Xc this program reads a mail message using callable mail
Xc
Xc We are using the AUTO_NEWMAIL, which means that if we are reading a
Xc new message, then it is automatically moved to the MAIL folder
Xc when we are done reading it.  This does not, however, decrement the
Xc new mail counter.  Thus we need to do this ourselves
X`009include '($maildef)/nolist'      `032
X`009structure /mail_item_array/
X`009integer*2 len
X`009integer*2 item_code
X`009integer*4 address
X`009integer*4 ret_len_addr
X`009end structure
Xc
X`009character*(*) username,folder_name
X`009character*80 buffr,sender,recip,cc_to,subject
X`009character*25 ascdate
X`009parameter mail$_notexist='007e80da'x
X `009integer context,context1,ucontext,messid
X`009integer message_selected,message_size,datlen
X`009integer*2 nmess,mess_flag
Xc these variables must be declared volatile - FORTRAN has no way of knowing
Xc that the values are changing since the variables are not explicitly
Xc changed.
X`009volatile message_selected,message_size,datlen,messid,len_buffr
X`009volatile nmess,mess_flag
Xc
X `009record /mail_item_array/ nselect(2),text(2),messfile(2)
X`009record /mail_item_array/ newmess(2),snewmess(2)
X`009record /mail_item_array/ mess_inf(9),select(5),next(2),cont(3)
X`009record /mail_item_array/ null
Xc the following items are for input
X`009call setup(newmess,MAIL$_USER_NEW_MESSAGES,%descr(nmess),)
X`009call setup(snewmess,MAIL$_USER_SET_NEW_MESSAGES,%descr(nmess),)
X`009call setup(next,MAIL$_MESSAGE_NEXT,' ',)
Xc the array setup has elements that define the characteristics of the
Xc messages that we want to select.  Just keep adding any specifications until
Xc you have completely specified the characteristics of all of the mail
Xc messages that you wish to select.
X`009call setup(select(1),MAIL$_MESSAGE_FOLDER,folder_name,)
X`009call setup(select(2),MAIL$_NOSIGNAL,' ',)
Xc`009call setup(select(3),MAIL$_MESSAGE_SINCE,'11-JAN-1989',)
Xc `009call setup(select(4),MAIL$_MESSAGE_FROM_SUBSTRING,username,)
X `009call setup(cont(1),MAIL$_MESSAGE_CONTINUE,' ',)
X`009call setup(cont(2),MAIL$_MESSAGE_AUTO_NEWMAIL,' ',)
Xc the following items are for output
X`009call setup(nselect,
X     1 MAIL$_MESSAGE_SELECTED,%descr(message_selected),)
X       `009call setup(text,MAIL$_MESSAGE_RECORD,buffr,len_buffr)
Xc
Xc we will ask for all of these things at the same time in one subroutine
Xc call.
X`009call setup(mess_inf(1),MAIL$_MESSAGE_CURRENT_ID,%descr(messid),)
X`009call setup(mess_inf(2),MAIL$_MESSAGE_DATE,ascdate,datlen)
X`009call setup(mess_inf(3),MAIL$_MESSAGE_SIZE,%descr(message_size),)
X `009call setup(mess_inf(4),MAIL$_MESSAGE_SENDER,sender,len_send)
X`009call setup(mess_inf(5),MAIL$_MESSAGE_TO,recip,len_to)
X`009call setup(mess_inf(6),MAIL$_MESSAGE_CC,cc_to,len_cc)
X`009call setup(mess_inf(7),MAIL$_MESSAGE_SUBJECT,subject,len_subj)
X`009call setup(mess_inf(8),
X     1MAIL$_MESSAGE_RETURN_FLAGS,%descr(mess_flag),)
Xc
Xc first find out the number of new messages
X`009call mail$user_begin(ucontext,null,null)
X`009call mail$user_get_info(ucontext,null,newmess)
X`009type *,' There are ',nmess,' new messages.'
Xc first we open the mailfile
X`009call mail$mailfile_begin(context,null,null)
X`009call mail$mailfile_open(context,null,null)
Xc now we select the context to read messages
X`009call setup(messfile,MAIL$_MESSAGE_FILE_CTX,%descr(context),)
X`009call mail$message_begin(context1,messfile,null)
Xc select the messages that we want to read, and find out how many we got
X`009istat=mail$message_select(context1,select,nselect)
Xc the definitions of the error codes for mail are not in FORSYSDEF.TLB(I think)
Xc They are in STARLET.MLB, but I cannot include that module here.
X`009if(.not.istat) then`032
X `009`009if(istat.eq.mail$_notexist) then
X`009`009`009type 1234
X`009`009else
X`009`009  `009call lib$signal(%val(istat))
X`009`009endif
X`009`009goto 152
X`009`009endif
X1234`009format(' No such folder.')
X   `009type *,'Number of messages=',message_selected
X`009do messcnt=1,message_selected
X`009call mail$message_get(context1,next,mess_inf(1))
Xc this information is also in the message itself.  If we print it
Xc it would appear twice.
Xc`009type *,'--------------------------------------------------'
Xc `009type *,' Header is'
Xc `009type *,'From: ',sender(1:len_send)
Xc `009type *,'To:   ',recip(1:len_to)
Xc `009type *,'CC:   ',cc_to(1:len_cc)
Xc `009type *,'Subject:',subject(1:len_subj)
X `009type *,'***************************************************'
Xc we have to keep track of the number of new messages ourself.
X`009if(iand(mess_flag,mail$m_newmsg).ne.0) nmess=nmess-1
X`009type 1000,messid,ascdate(1:datlen)
X1000`009format('   #',i3,'               ',a<datlen>)
X`009do line_count=1,message_size+5
X`009call mail$message_get(context1,cont,text)
Xc***********************************************************************
Xc         IMPORTANT - THERE MAY BE ESCAPE SEQUENCES IN THE MAIL MESSAGES
X13`009ijk=index(buffr(1:len_buffr),char(27))
X  `009if(ijk.ne.0) then
X`009`009buffr(ijk:ijk)=char(64)
X`009`009goto 13
X`009`009endif
X  `009type *,buffr(1:len_buffr)
X`009end do
X`009end do
X152 `009call mail$message_end(context1,null,null)
X`009call mail$user_set_info(ucontext,snewmess,null)
X`009call mail$user_end(ucontext,null,null)
X`009call mail$mailfile_close(context,null,null)
X`009call mail$mailfile_end(context,null,null)
X`009return
X `009end
X
X`009subroutine dir_folder
Xc this program displays a list of the folders in the default mail file
Xc
X`009include '($maildef)/nolist'
Xc
X`009structure /mail_item_array/
X`009integer*2 len
X`009integer*2 item_code
X`009integer*4 address
X`009integer*4 ret_len_addr
X`009end structure
Xc
X`009external f_rout
X`009integer idata
X`009volatile idata
X `009integer context
X`009record /mail_item_array/ null,folderr(3)
X`009call setup(folderr(1),
X     1 MAIL$_MAILFILE_FOLDER_ROUTINE,%descr(f_rout),)
X       `009call setup(folderr(2),MAIL$_MAILFILE_USER_DATA,%descr(idata),)
Xc
X`009call mail$mailfile_begin(context,null,null)
X`009call mail$mailfile_open(context,null,null)
X`009call mail$mailfile_info_file(context,folderr,null)
X`009call mail$mailfile_close(context,null,null)
X`009call mail$mailfile_end(context,null,null)
X`009type *,' There were ',idata,' folders in the default mail file'
X`009return
X  `009end
X
X`009function f_rout(idata,string)
Xc a descriptor for a zero length string is passed at the end of the list
X`009character*(*) string
X`009integer f_rout
X`009include '($ssdef)'
X`009if(len(string).ne.0) then
X`009`009type *,'Mail folder name:',string
X    `009`009idata=idata+1
X`009`009endif
X`009f_rout=SS$_NORMAL
X`009return
X`009end
X
X`009subroutine setup(item,code,string,ret_len)
X`009character*(*) string
X`009integer ret_len
X`009integer code
X`009structure /mail_item/
X`009integer*2 len
X`009integer*2 item_code
X`009integer*4 address
X`009integer*4 ret_len_addr
X`009end structure
X
X`009record /mail_item/ item
X`009item.len=len(string)
X`009item.address=%loc(string)
X`009item.item_code=code
X`009item.ret_len_addr=%loc(ret_len)
X`009return
X`009end
$ GOSUB UNPACK_FILE
$ EXIT


