function [retcode] = sendmail(from, to, subj, mesg, varargin) % SENDMAIL Send Internet e-mail % Using SENDMAIL (which needs the TCP/UDP/IP toolbox, freely available from % http://petrydpc.ite.mh.se/tools/) it is possible to send e-mail messages % from the Matlab prompt. This can be useful when you want to be notified % when large jobs terminate on remote machines. % % Usage % [RETCODE] = SENDMAIL(FROM, TO, SUBJ, MESG [, FILENAME]) send an e-mail with % subject SUBJ and contents MESG to the email address in TO, with % the From: field set to FROM. % % FROM, TO and SUBJ are Matlab strings. MESG can be either a % string, or a cell array of strings. % % RETCODE is 0 if the message was sent succesfully, otherwise it % has the value -1. % % The optional argument FILENAME is a string containing the % filename of the file to be attached to the message. It may % contain path information. % % Examples % sendmail('me@some.where.com','me@else.where.org','Job Finished!','It took 4 days, 12 hours,6 minutes.') % % sendmail('me@some.where.com','me@else.where.org','Job Finished!','Finaly...', 'results.mat') % % Installation % 1. Make sure the TCP/UDP/IP toolbox is somewhere in your PATH % 2. Adjust the SMTPSERVER, SMTPSERVERPORT and CLIENTIP variables to reflect your situation. % Author: Joris Portegies Zwart % http://www.science.uva.nl/~portegie/ % Version 3.1 % Date: December 24, 2002 % Contributors: Simon Bridger % Revisions : % 3.1 Removed bug in filename extraction % 3.0 Added attachment capabilities % 2.1 SJB % - Add "Content-Transfer-Encoding" to work w/outlook97/exchange server % - Made server info optional % - Added possibility of mesg in cell format % 2.0 Update to use the new TCP/UDP/IP Toolbox 2.0.2 % 1.3 Added server response checking % 1.2 Changed to , as in RFC821 % 1.1 Corrected minor syntax errors % 1.0 First version % Copyright (C) 2002 Joris Portegies Zwart % % This program is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License % as published by the Free Software Foundation; either version 2 % of the License, or (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details: % % http://www.gnu.org/copyleft/gpl.html ncmesg = nargchk(4, 5, nargin); if ~isempty(ncmesg) error(ncmesg) end SMTPSERVER = 'mail.server.com'; % IP adress of SMTP server SMTPSERVERPORT = 25; % Port number, ususally 25 CLIENTIP = 'my.computerip.com'; % IP adress of client machine, usually not important CRLF = [char(13) char(10)]; S = rand('state'); rand('state',sum(100*clock)); MIMEBREAK = char(floor(97 + 25 * rand(1, 25))); rand('state', S); if iscellstr(mesg) % Message is a cellarray of strings msg_string=''; for i=1:length(mesg) msg_string=[msg_string, mesg{i}, CRLF]; end mesg=msg_string; end if (nargin==5) fullname = varargin{1}; fsid = findstr(fullname, filesep); if ~isempty(fsid) filename = fullname( (fsid(end)+1):end); else filename = fullname; end disp('Encoding attachment.') b64data = base64encode(fullname); hasattachment = 1; else hasattachment = 0; end try smtp_connection = pnet('tcpconnect', SMTPSERVER, SMTPSERVERPORT); str = ''; while (isempty(str)) str=pnet(smtp_connection,'readline'); end disp(['Connection with ' SMTPSERVER ':' num2str(SMTPSERVERPORT) ' established.']); catch disp('Network Connection Failed, Aborting sendmail') retcode = -1; return end Header =... ['From: ' from CRLF ... 'To: ' to CRLF ... 'Subject: ' subj CRLF ... 'MIME-Version: 1.0' CRLF ... 'Content-type: multipart/mixed; boundary="' MIMEBREAK '"' CRLF]; Message =... ['This is a multi-part message in MIME format.' CRLF CRLF ... '--' MIMEBREAK CRLF ... 'Content-type: text/plain; charset=us-ascii' CRLF ... 'Content-transfer-encoding: 7bit' CRLF CRLF ... mesg CRLF CRLF]; if hasattachment Attachm =... ['--' MIMEBREAK CRLF ... 'Content-type: application/octet-stream; name="' filename '"' CRLF ... 'Content-transfer-encoding: base64' CRLF CRLF]; for n = 1:length(b64data) Attachm = [Attachm b64data{n} CRLF]; end else Attachm = []; end Closing =[CRLF '--' MIMEBREAK '--' CRLF CRLF '.']; try sendcommand(smtp_connection, ['HELO ' CLIENTIP]); sendcommand(smtp_connection, ['MAIL FROM:' from ]); sendcommand(smtp_connection, ['RCPT TO:' to ]); sendcommand(smtp_connection, ['DATA']); sendcommand(smtp_connection, [Header Message Attachm Closing]); sendcommand(smtp_connection, ['QUIT']); catch disp('Aborting sendmail') pnet(smtp_connection,'close'); retcode = -1; return end pnet(smtp_connection,'close'); disp('Email sent.') retcode = 0; return % SENDCOMMAND % function [str] = sendcommand(smtp_connection, command) CRLF = [char(13) char(10)]; str = ''; % This function call should return the number of character written, % but doesn't. The proper syntax would be % % numc_written = pnet(smtp_connection,'printf', '%s', [command CRLF]); % % but we have to use pnet(smtp_connection,'printf', '%s', [command CRLF]); while (isempty(str)) str = pnet(smtp_connection, 'readline'); end % Uncomment the next line to echo all server responses % disp(str) resp_top = str2num(str(1)); switch resp_top case {2, 3} % 2xx means succesful completion, 3xx means awaiting input return case{4} % 4xx means transient failure, could try again? disp(['Transient failure issuing command ' command]) disp(['The server said ' str]) error('ERROR!') case{5} % 5xx means permanent failure disp(['Permanent failure issuing command ' command]) disp(['The server said ' str]) error('ERROR!') otherwise % Unspecified server behaviour disp('Unknown server response, trying to continue ...'); return end return % BASE64ENCODE % function [out] = base64encode(filename) % This function reads a file as a bitstream, 76 groups of 6 bits at % a time. Each group gets encoded as an 8-bit character from the % base64 dictionary. Each line of 76 characters is stored in a cell % of the cell array out. BASE64 = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ' ... % The Base64 dicitionary. 'abcdefghijklmnopqrstuvwxyz' ... '0123456789+/']; BIT2DEC = 2.^[5 4 3 2 1 0]; NBITS = 6 * 76; % Number of bits per line fid = fopen(filename, 'r', 'b'); % Open file as big-endian status = fseek(fid, 0, 'eof'); % Set pointer to end of file fsize = ftell(fid); % to determine size. frewind(fid) % Reset pointer numlines = ceil((4 * fsize/3)/76); out = cell(1,numlines); for n = 1:(numlines-1) % Encoding the first numlines-1 lines is easy. [fc, count] = fread(fid, NBITS, 'ubit1'); out{n} = BASE64(1 + BIT2DEC * reshape(fc, 6, 76)); end % The last line needs a little more work. First, read remaining bits: [fc, count] = fread(fid, NBITS, 'ubit1'); %Pad last part of stream so it's length is multiple of 6 bits: fc = [fc ; zeros(6*ceil(count/6) - count, 1)]; % Encode final group of 6 bits out{numlines} = BASE64(1 + BIT2DEC * reshape(fc, 6, length(fc)/6)); % Pad encoded stream such with '=' characters number of characters % in encoding is integral multiple of 4 numpad = rem(fsize * 8, 24)/8; out{numlines} = [out{end} repmat('=', 1, numpad)]; fclose(fid); return