function t1server(port, timeout, idletimeout, debug)
%
% simple webserver to allow our HTML-based interactive scripts to access
% standalone-compiled Matlab applications. Naturally, you can run this
% server process from your Matlab workspace, too.
%
% The basic idea is to embed small Java applets into HTML pages.
% Each applet shows one line of Matlab script code. When double-clicked,
% the applet encodes the Matlab code using Base64 encoding and tries to
% access the URL  http://localhost:8888/<base64-encoded-matlab-script>, 
% e.g. 'http://localhost:8888/cmVzdWx0MmZpZyggc2luKHBpLzQpKjAuMyAp'.
% Using this trick we can easily encode any ASCII/Unicode string, we
% avoid to design our own TCP protocol, and we don't have to mess with
% Java applet security (applets are allowed to do http requests).
%
% Naturally, you can access this server from any other web client as well,
% e.g. via wget or any webbrowser. Just make sure to type in a valid 
% base64-encoded URL :-)
%
% This server process listens to http requests on the specified port.
% It listens to all requests but ignores requests from all computers except
% localhost - for obvious security reasons. (This also means that we
% comply with TMW license restrictions regarding web access to Matlab).
%
% When a valid http request arrives from localhost, the server will read
% the URL from the GET request and then base64-decode it into the original
% Matlab command. It will then execute the command via eval().
% WARNING: the t1server process will happily execute any Matlab command 
% you send it - including deletion of files or running external programs.
% 
% 07.01.05 - print license info on startup
% 20.12.04 - timeouts, support plaintext 'exit' and 'status' requests
% 14.12.04 - port,timeout,debug params, return http status 204 (no content)
% 13.12.04 - use t1eval to check for allowed functions 
% 10.12.04 - new function, based on webserver_demo
% 
% (C) 2004, F.N.Hendrich, University of Hamburg, Dept. Computer Science
%           hendrich@informatik.uni-hamburg.de
%
% This code is based on the 'WEBSERVER_DEMO' example from the tcp_udp_ip
% toolbox. See the tcp_udp_ip toolbox documentation for details and 
% licensing information. The following (C)-notice taken from pnet.c:
%
% (C) 2002, Peter Rydesäter, Mario Bergeron, Mike Medeiros
%           See http://www.rydesater.com
%

idle_secs     = 0; % count idle time
max_idle_secs = 0;
uptime_secs   = 0; % total server uptime

if (nargin==0)
  port=8888; 
else 
  if (ischar(port)), port=str2num(port); end
end

if (nargin < 2)
  timeout = 30*60; % 30 mins
else
  if (ischar(timeout)) timeout = str2num(timeout); end
end

if (nargin < 3)
  idletimeout = 5*60; % 5 mins
else
  if (ischar(idletimeout)) idletimeout = str2num(idletimeout); end
end

if (nargin <3)
  debug = 0;
end

sock = pnet('tcpsocket',port);
if (sock==-1)
  msg = sprintf( '-E- Error: could not open TCP server on port %d.\n', port );
  error( msg );
  error( 'Please check your firewall settings and make sure that no other' );
  error( 'server process is already running and using the port.' );
end

pnet(sock,'setreadtimeout',1);
try
  pp = num2str( port );
  disp( 't1server (C) 2004-2005 F.N.Hendrich, University of Hamburg' );
  disp( 'programmed with MATLAB (C) 1984-2005 by The Mathworks, Inc.' );
  disp( 'This program may freely be used and distributed' );
  disp( 'under the license conditions given in the file' );
  disp( '''license.txt'' provided with the Matlab Component Runtime' );
  disp( 't1server configuration:' );
  disp( [ 'port=' pp '' ] );
  disp( [ 'timeout=' num2str(timeout) ' secs.' ] );
  disp( [ 'idletimeout= ' num2str(idletimeout) ' secs.' ] );
  disp( [ 'NOTE: requests are allowed from localhost only.' ] );
  disp( [ 'Example: http://localhost:' pp '/YXNjaWltYXQ=' ] );
  disp( [ 'Use http://localhost:' pp '/exit to stop the server']);
  disp( [ 'Use http://localhost:' pp '/status for server status report' ]);

  while 1,
    con=pnet(sock,'tcplisten');
    if( con~=-1 ),
      try,
	[ip,tmpport]=pnet(con,'gethost');
	%disp(sprintf('Connection from host:%d.%d.%d.%d port:%d\n',ip,port));
        %disp( ip );

        % reset idle time counter: we just got a request...
        if (idle_secs > max_idle_secs) 
          max_idle_secs = idle_secs;
        end
        idle_secs = 0; 

        % avoid locking the server for slow/dead networks/browsers
        %
	pnet(con,'setreadtimeout',2);  
	pnet(con,'setwritetimeout',1);  

        % read the rest of the incoming http request (and throw it away)
        %
        %for ii=2:15
	%  dummy = pnet(con,'readline', 'noblock' );
        %  % disp( [ 'DATA' num2str(ii) ' ' char(dummy) ] ); % debug only
        %end

        % we encode the requested function as a base64-encoded URL.
        % The URL is in the first line of a http-get request, e.g.
        % 'GET /cmVzdWx0MmZpZyggc2luKHBpLzQpKjAuMyAp HTTP/1.1'
        %
        try 
          txt_ip = sprintf( '%d.%d.%d.%d', ip );
          if (strcmp( txt_ip, '127.0.0.1') == 1)  % localhost is ok

            data = pnet( con, 'readline', 'noblock' );
            if (debug) 
              disp( data );
            end

            if (isempty( strfind( data, 'GET')))
              url  = 'error';
            else 
              url  = data( 6 : length(data)-9 );
            end

            if (strcmp( url, 'error'))
              disp( ['Warning: unknown HTTP request ignored.'] );
              disp( 'The full HTTP request is (first 20 lines):' );
              disp( data );
              for ii=1:20
                data = pnet( con, 'readline', 'noblock' );
                if (~isempty(data)) disp( data ); end
              end
              % make the client happy, despite of the wrong request...
	      pnet(con,'printf', 'HTTP/1.1 204 OK\n'); % 204=http no content
	      pnet(con,'printf', 'Content-Type: text/html\n\n');
	      pnet(con,'printf', '<html><body>empty</body></html>\n' );

            elseif (strcmp( url, 'status' ) == 1) % dump status info
              disp( 't1server: received a ''status'' command...' );
              % write OK response with status info to client
              %
	      pnet(con,'printf', 'HTTP/1.1 200 OK\n'); % 204=http no content
	      pnet(con,'printf', 'Content-Type: text/html\n\n');
	      pnet(con,'printf', '<html><head>\n' );
              pnet(con,'printf', '<title>t1server status</title></head>\n' );
              pnet(con,'printf','<body><h1>t1server status</h1>\n' );
              s1 = sprintf( 'Port: %d<p>\n', port );
              s2 = sprintf( 'Uptime: %d [seconds]<p>\n', uptime_secs );
              s3 = sprintf( 'Uptime timeout: %d [seconds]<p>\n', timeout );
              s4 = sprintf( 'Idle time: %d [seconds]<p>\n', max_idle_secs );
              s5 = sprintf( 'Idle time limit: %d [seconds]<p>\n', idletimeout );
              pnet(con,'printf', s1 ); 
              pnet(con,'printf', s2 ); 
              pnet(con,'printf', s3 ); 
              pnet(con,'printf', s4 ); 
              pnet(con,'printf', s5 ); 
              pnet(con,'printf','</body></html>\n');

            elseif (strcmp( url, 'exit' ) == 1) % special case 
	      pnet(con,'printf', 'HTTP/1.1 204 OK\n'); % 204=http no content
              % pnet(sock,'close');
              disp( 't1server: received an ''exit'' command, exiting...' );
              break; % return
            else 
              % make the client happy, first...
	      pnet(con,'printf', 'HTTP/1.1 204 OK\n'); % 204=http no content
	      pnet(con,'printf', 'Content-Type: text/html\n\n');
	      pnet(con,'printf', '<html><body>empty</body></html>\n' );

              % now decode and execute its request
              %
              cmd  = base64decode( url );
              disp( [ 't1server: ''' cmd '''' ]);

              if (strcmp( cmd, 'exit' ) == 1)  % special case 
                % pnet(sock,'close');
                break; % return
              else 
                % now exec the command: we dispatch to t1eval, which checks 
                % that the command is on our list of allowed callable functions.
                % This way, the license restrictions of standalone compiled
                % programs are guaranteed.
                %
                % eval( cmd ); % works, but forbidden
                t1eval( cmd );
              end
            end

          else 
            % don't even try to read the rest of the incoming data
            %
            disp( [ 'Warning: request from host ' txt_ip ' ignored!' ] );
	    pnet(con,'printf','HTTP/1.1 204 OK\n'); % 204=http no content
          end
        catch
          disp( 'Internal error in t1server while decoding the GET request:' );
          disp( lasterr ); 

          try 
            % try to write some response to the client
	    pnet(con,'printf','HTTP/1.1 204 OK\n'); % 204=http no content
          catch
            disp( lasterr );
          end
        end

      end
      pnet(con,'close');
      drawnow;
    end

    % some timeout for easy development: stopping the server via CNTL-C
    % does not work on Linux :-(
    %
    idle_secs   = idle_secs + 1;
    uptime_secs = uptime_secs + 1;

    % disp( [ 'uptime_secs: ' num2str(uptime_secs) ] );

    if (timeout > 0) & (uptime_secs >= timeout) 
      disp( [ 'Warning: uptime reached timeout value of ' ...
              num2str(idle_secs)  ' seconds, exiting...' ] );
      break; 
    end;
    if (idletimeout > 0) & (idle_secs >= idletimeout) 
      disp( [ 'Warning: idle for more than ' num2str(idle_secs)  ...
              ' seconds, exiting...' ] );
      break; 
    end;
  end

  disp( 't1server: main loop exited...' );
  pnet(sock,'close');
  disp( 't1server: server socket closed.' );
end
