Lasso Soft Inc. > Home

[email_pop]

Linkemail_pop
AuthorJustin Dennis
CategoryEmail
Version8.5.x
LicensePublic Domain
Posted28 May 2010
Updated28 May 2010
More by this author...

Description

This version of [email_pop] changes the way Lasso sets up a POP connection using SSL on port 995, specifically for Gmail. It also modifies the internal "authentication" tag to issue an extra CAPA command as a workaround for a plaintext authentication issue I don't fully understand. Many thanks to Bil Corry for figuring out the SSL connection issue and providing a work-around.

Sample Usage

[var: 'pop' = (email_pop: -host='pop.gmail.com', -port=995, -username='username', -password='', -apop=false)]

Source Code

Click the "Download" button below to retrieve a copy of this tag, including the complete documentation and sample usage shown on this page. Place the downloaded ".inc" file in your LassoStartup folder, restart Lasso, and you can begin using this tag immediately.

(delete)]
// [/iterate]
// [output: $pop->(close)]
//
// High-Level Commands - These are the recommended tags for most users
//
// [email_pop:
//   -host='mail.example.com',
//   -port=110, (optional)
//   -username='username', (if a username and password are specifed then
//   -password='password', the type will auto-authorize the POP server)
//   -apop=true, (optional, APOP will be tried before USER/PASS)
//   -log=false, (optional, log all actions to the console)
//   -timeout=15 (optional, timeout for al reads and writes 
// ]
// [email_pop->authorize:
//   -username='username',
//   -password='password',
//   -apop=true, (optional, APOP will be tried before USER/PASS)
// ]
// [email_pop->size] returns the number of messages availble for download
// [email_pop->(get: x)] returns the specified message from the server
// [email_pop->(delete: x)] deletes the specified message from the server
// [email_pop->(close)] closes the POP connection, performing any specified deletes
// [email_pop->(cancel)] closes the POP connection, but does not perform any deletes
//
// The size and get tags allow the iterate code above to be used.  The delete tag
// will delete the last message which was specified for get if no parameter is passed.
//
// Low-Level Commands - These tags allow access to the raw POP commands
//
// [email_pop->(USER: 'username')] sends a username to the server
// [email_pop->(PASS: 'username')] sends a password to the server
// [email_pop->(APOP: 'username','digest')] sends APOP authentication to the server
// [email_pop->(QUIT)] closes the connection to the server
// [email_pop->(RSET)] resets the server, cancelling any deletes
// [email_pop->(STAT] returns the status of the server
// [email_pop->(LIST)] returns a list of index and sizes for all messages on server
// [email_pop->(LIST: x)] returns the index and size for message x.
// [email_pop->(RETR: x)] retrieves message x from the server
// [email_pop->(TOP: x, y)] fetches message x from the server, getting the headers and y lines
// [email_pop->(DELE: x)] deletes message x from the server
// [email_pop->(NOOP)] a null operation which can be used to keep a connection open.
//
// Implementation - These tags are used in the implementation and shouldn't be accessed directly
//
// [email_pop->(pop_cmd:
//   -command='POP command',
//   -timeout=15, (optional timeout for this command)
//   -multi=false (if true a long response to the command (ended with .) is collected
// ]
// [email_pop->(pop_err:
//   -msg='error message',
//   -code=-1, (optional, result code)
//   -cmd='POP command' (optional)
// ]
// [email_pop->(pop_log:
//   -msg='log message',
//   -code=0, (optional, result code)
//   -cmd='POP command' (optional)
// ]
//

define_type: 'pop',
	-namespace = 'email_';
	
	local: 'pop_net' = null;
	local: 'pop_mode' = '';	// init,auth,live,done
	local: 'pop_timeout' = 15;
	local: 'pop_token' = '';
	local: 'pop_index' = 0;
	local: 'pop_err' = (array);
	local: 'pop_res' = (array);
	local: 'pop_log' = false;
	local: 'pop_debug' = false;
	local: 'pop_get' = 'retr';
	local: 'pop_ids' = map;
	local: 'pop_capa' = null;
	local: 'pop_server' = '';
	
	// Call Back Tags
	
	define_tag: 'onCreate',
			-optional='server',
			-optional='port',
			-optional='username',
			-optional='password',
			-optional='apop',
			-optional='timeout',
			-optional='log',
			-optional='debug',
			-optional='get',
			-optional='host',
			-optional='ssl';

		self->'pop_net' = (net);
		
		if: (local_defined: 'get');
			self->'pop_get' = (string: #get);
		else;
			self->'pop_get' = 'uidl';
		/if;
		
		if: (local_defined: 'log');
			self->'pop_log' = (#log === null) || (boolean: #log);
		/if;

		if: (local_defined: 'debug');
			self->'pop_debug' = (#debug === null) || (boolean: #debug);
		/if;

		if: (local_defined: 'apop');
			local: '_apop' = (#apop === null) || (boolean: #apop);
		else;
			local: '_apop' = true;
		/if;

		if: (local_defined: 'ssl');
			local: '_ssl' = (#ssl === null) || (boolean: #ssl);
		else;
			local: '_ssl' = false;
		/if;

		if: (local_defined: 'server');
			local: '_server' = (string: #server);
			#_server->trim;
		else: (local_defined: 'host');
			local: '_server' = (string: #host);
			#_server->trim;
		/if;
		
		self->'pop_server' = #_server;
		
		if: (local_defined: 'port');
			local: '_port' = (integer: #port);
		else;
			local: '_port' = 110;
		/if;

		local: '_timeout' = (integer: self->'pop_timeout');
		if: (local_defined: 'timeout');
			#_timeout = (integer: #timeout);
			if: (#_timeout < 5);
				local: '_timeout' = 5;
			/if;
			self->'pop_timeout' = (integer: #_timeout);
		/if;

		if( #_port == 995 );
			self->'pop_net'->(SetType: Net_TypeSSL);
			self->'pop_net'->(connect: #_server, #_port);
		else;
			self->'pop_net'->(connect: #_server, #_port);

			if: (self->'pop_net'->(wait: #_timeout, net_waitread) == net_waitread);
				local: 'result' = self->'pop_net'->(read: 32768);
				protect;
					self->'pop_token' = (string_findregexp: #result, -find='<[^>]+>')->(get:1);
				/protect;
				self->(pop_log: -msg=#result->(split: '\r\n')->(get:1));
			else;
				self->(pop_err: -msg='The remote server timed out on connect.');
			/if;
			
			local: 'capa' = self->capabilities;
			if: (#_ssl == true);
				if: (#capa >> 'STLS');
					self->stls;
					local: 'done' = (self->'pop_res'->last->second >> '+OK');
					self->'pop_net'->(SetType: Net_TypeSSL);
				else;
					self->(pop_err: -msg='Could not open SSL communications',-cmd='STLS');
				/if;
			/if;
		/if;

		if: (local_defined: 'username') && (local_defined: 'password');
			self->(authorize: -username=(local: 'username'), -password=(local: 'password'), -apop=(local: '_apop'));
		/if;
			
	/define_tag;
	
	define_tag: 'onDestroy';

		protect;
			if: (self->'pop_mode' != 'done');
				self->quit;
			/if;
			
			handle: true;
				if: (self->'pop_net'->(isa: 'net'));
					self->'pop_net'->close;
					self->'pop_net' = null;
				/if;
			/handle;
		/protect;
		
	/define_tag;


	define_tag: 'onConvert',
			-required='type';
	
	/define_tag;
	

	// Communication Commands

	define_tag: 'pop_cmd', 
			-required='command',
			-optional='timeout',
			-optional='multi',
			-optional='until';

		local: 'result' = '';
		if: (self->'pop_net'->(isa: 'net'));
			
			local: '_command' = (string: #command);
			if: !(#_command->(endswith: '\r\n'));
				#_command += '\r\n';
			/if;
			
			local: '_timeout' = (integer: self->'pop_timeout');
			if: (local_defined: 'timeout');
				#_timeout = (integer: #timeout);
			/if;
			
			local: '_multi' = (boolean: (local: 'multi'));
			local: '_until' = (string: (local: 'until'));
			
			if: (self->'pop_net'->(wait: #_timeout, net_waitwrite) == net_waitwrite);
				local: 'write' = (integer: self->'pop_net'->(write: #_command));
				if: (self->'pop_net'->(wait: #_timeout, net_waitread) == net_waitread);
					if: (#_until != '');
						local: 'result' = '';
						while: #result !>> #_until;
							#result += self->'pop_net'->(read: 1);
							if: (loop_count > 32768);
								loop_abort;
							/if;
						/while;
						self->(pop_log: -msg=#result->(split: '\r\n')->(get:1), -cmd=#command);
					else;
						local: 'result' = '';
						while: #result !>> '\r\n';
							#result += self->'pop_net'->(read: 1);
							if: (loop_count > 32768);
								loop_abort;
							/if;
						/while;
						self->(pop_log: -msg=#result->(split: '\r\n')->(get:1), -cmd=#command);
						if: (#_multi == true) && (#result !>> '\r\n.\r\n') && (#result !>> '-ERR');
							while: (#result !>> '\r\n.\r\n');
								if: (loop_count > 10000);
									self->(pop_err: -msg='Error while reading from remote server.', -cmd=#command);
									loop_abort;
								/if;
								local: 'temp' = self->'pop_net'->(read: 32768);
								self->(pop_log: -msg='... reading (' + #temp->size + ' bytes)', -cmd=#command);
								if: (#temp->size == 0);
									loop_abort;
								/if;
								#result += #temp;
							/while;
						/if;
					/if;
				else;
					self->(pop_err: -msg='The remote server timed out on read.', -cmd=#command);
				/if;
			else;
				self->(pop_err: -msg='The remote server timed out on write.', -cmd=#command);
			/if;
		else;
			self->(pop_err: -msg='Lost network connection unexpectedly.', -cmd=#command);
		/if;
		return: #result;
	
	/define_tag;

	define_tag: 'pop_err',
			-required='msg',
			-optional='code',
			-optional='cmd';
		
		if: (local_defined: 'cmd');
			local: '_cmd' = (string: #cmd);
		else;
			local: '_cmd' = '(none)';
		/if;
		
		if: (local_defined: 'code');
			local: '_code' = (integer: #code);
		else;
			local: '_code' = -1;
		/if;
		
		if: (self->'pop_err'->size > 0) && (self->'pop_err'->last == null);
			self->'pop_err'->remove;
		/if;
			
		if: (self->'pop_debug');
			self->'pop_err'->(insert: #_cmd=#msg);
			self->'pop_res'->(insert: #_cmd=#msg);
		else;
			self->'pop_err' = (array: #_cmd=#msg);
			self->'pop_res' = (array: #_cmd=#msg);
		/if;
		
		if: (self->'pop_log');
			log_detail: 'POP: ' + #_cmd + ' - ' + #msg;
		/if;
		
		fail: #_code, #msg;
		
	/define_tag;
	
	define_tag: 'pop_log',
			-required='msg',
			-optional='code',
			-optional='cmd';

		if: (local_defined: 'cmd');
			local: '_cmd' = (string: #cmd);
		else;
			local: '_cmd' = '(none)';
		/if;
		
		if: (local_defined: 'code');
			local: '_code' = (integer: #code);
		else;
			local: '_code' = -1;
		/if;
		
		if: (self->'pop_err'->size > 0) && (self->'pop_err'->last != null);
			if: (self->'pop_debug');
				self->'pop_err'->(insert: null);
			else;
				self->'pop_err' = (array: null);
			/if;
		/if;
			
		if: (self->'pop_debug');
			self->'pop_res'->(insert: #_cmd=#msg);
		else;
			self->'pop_res' = (array: #_cmd=#msg);
		/if;
		
		if: (self->'pop_log');
			log_detail: 'POP: ' + #_cmd + ' - ' + #msg;
		/if;
		
		error_seterrormessage: (error_noerror);
		error_seterrorcode: (error_noerror: -errorcode);
		
	/define_tag;
	
	
	// Low Level Commands

	define_tag: 'user',
			-optional='username';
		if: (local_defined: 'username');
			return: self->(pop_cmd: 'USER ' + #username);
		else;
			self->(pop_err: -msg='No username specified.',-cmd='USER');
		/if;
	/define_tag;

	define_tag: 'pass',
			-optional='password';
		if: (local_defined: 'password');
			return: self->(pop_cmd: 'PASS ' + #password);
		else;
			self->(pop_err: -msg='No password specified.',-cmd='PASS');
		/if;
	/define_tag;

	define_tag: 'apop',
			-required='username',
			-required='password';
		if: (self->'pop_token' >> '<');
			local: 'digest' = (encrypt_md5: self->'pop_token' + #password);
			return: self->(pop_cmd: 'APOP ' + #username + ' ' + #digest);
		else;
			self->(pop_err: -msg='No token found.',-cmd='APOP');
		/if;
	/define_tag;

	define_tag: 'auth',
			-required='username',
			-required='password',
			-required='method';
		if: #method == 'CRAM-MD5';
			local: 'result' = self->(pop_cmd: 'AUTH CRAM-MD5');
			if: #result->(beginswith: '+ ');
				#result = (_substring: #result, 3);
				local: 'token' = (string: (decode_base64: #result));
				local: 'digest' = (encode_base64: #username + ' ' + (encrypt_crammd5: #password, #token));
				return: self->(pop_cmd: #digest);
			else;	
				self->(pop_err: -msg='Invalid response for ' + #method + ' method.',-cmd='AUTH');
			/if;
		else: #method == 'DIGEST-MD5';
			local: 'result' = self->(pop_cmd: 'AUTH DIGEST-MD5');
			if: #result->(beginswith: '+ ');
				#result = (_substring: #result, 3);
				local: 'digest' = (email_digestchallenge: (decode_base64: #result));
				#digest->(insert: 'username' = #username);
				#digest->(insert: 'password' = #password);
				#digest->(insert: 'digest-uri' = 'pop/' + self->'pop_server');
				#digest->(insert: 'cnonce' = (encrypt_md5: lasso_uniqueid));
				#digest->(insert: 'nc' = '00000001');
				local: 'response' = (encode_base64: (email_digestresponse: #digest));
				return: self->(pop_cmd: #response);
			else;	
				self->(pop_err: -msg='Invalid response for ' + #method + ' method.',-cmd='AUTH');
			/if;
		else;
			self->(pop_err: -msg='Method ' + #method + ' not found.',-cmd='AUTH');
		/if;

			local: 'digest' = (encrypt_md5: self->'pop_token' + #password);
			return: self->(pop_cmd: 'APOP ' + #username + ' ' + #digest);
	/define_tag;

	define_tag: 'quit';
		self->'pop_mode' = 'done';
		return: self->(pop_cmd: 'QUIT')->trim &;
	/define_tag;

	define_tag: 'rset';
		return: self->(pop_cmd: 'RSET')->trim &;
	/define_tag;

	define_tag: 'stat';
		return: self->(pop_cmd: 'STAT')->trim &;
	/define_tag;

	define_tag: 'list',
			-optional='msg';
		if: (local_defined: 'msg');
			return: self->(pop_cmd: 'LIST ' + (integer: #msg))->trim &;
		else;
			return: self->(pop_cmd: 'LIST', -multi=true)->trim &;
		/if;
	/define_tag;

	define_tag: 'uidl',
			-optional='msg';
		if: (local_defined: 'msg');
			local: 'result' = 
			return: self->(pop_cmd: 'UIDL ' + (integer: #msg))->trim &;
		else;
			return: self->(pop_cmd: 'UIDL', -multi=true)->trim &;
		/if;
	/define_tag;

	define_tag: 'retr',
			-required='msg';
		return: self->(pop_cmd: 'RETR ' + (integer: #msg), -multi=true)->trim &;
	/define_tag;

	define_tag: 'top',
			-required='msg',
			-optional='size';
		return: self->(pop_cmd: 'TOP ' + (integer: #msg) + ' ' + (integer: (local: 'size')), -multi=true)->trim &;
	/define_tag;

	define_tag: 'dele',
			-required='msg';
		return: self->(pop_cmd: 'DELE ' + (integer: #msg))->trim &;
	/define_tag;

	define_tag: 'noop';
		return: self->(pop_cmd: 'NOOP')->trim &;
	/define_tag;

	define_tag: 'capa';
		return: self->(pop_cmd: 'CAPA', -multi=true)->trim &;
	/define_tag;

	define_tag: 'stls';
		return: self->(pop_cmd: 'STLS', -until='\r\n')->trim &;
	/define_tag;


	// High Level Commands
	
	define_tag: 'authorize',
			-required='username',
			-required='password',
			-optional='apop';

		local: 'done' = false;
		if: (local_defined: 'apop');
			local: '_apop' = (#apop === null) || (boolean: #apop);
		else;
			local: '_apop' = true;
		/if;
		if: self->'pop_capa' >> 'sasl';
			local: 'methods' = self->'pop_capa'->(find: 'sasl');
		else;
			local: 'methods' = (array: 'LOGIN','PLAIN');
		/if;
		if: (#done == false) && (#methods >> 'DIGEST-MD5');
			self->(auth: -username=#username, -password=#password, -method='DIGEST-MD5');
			local: 'done' = (self->'pop_res'->last->second >> '+ ');
		/if;
		if: (#done == false) && (#methods >> 'CRAM-MD5');
			self->(auth: -username=#username, -password=#password, -method='CRAM-MD5');
			local: 'done' = (self->'pop_res'->last->second >> '+OK');
		/if;
		if: (#done == false) && (#_apop && (self->'pop_token' >> '<'));
			self->(apop: -username=#username, -password=#password);
			local: 'done' = (self->'pop_res'->last->second >> '+OK');
		/if;
		if: (#done == false);
			self->capa;
			self->(user: #username);
			self->(pass: #password);
			local: 'done' = (self->'pop_res'->last->second >> '+OK');
		/if;
		if: #done == false;
			self->(pop_err: -msg='Could not authenticate user.',-cmd='PASS');
		else;
			self->'pop_mode' = 'ready';
		/if;
		self->'pop_ids' = map;
		self->'pop_index' = 0;
	/define_tag;

	define_tag: 'size';
		protect;
			return: (integer: (string_findregexp: self->(pop_cmd: 'STAT'), -find='[0-9]+')->(get:1));
		/protect;
		return: 0;
	/define_tag;
	
	define_tag: 'get',
			-optional='msg';
		if: (local_defined: 'msg') && !(string_isdigit: #msg);
			if: (self->'pop_ids' >> #msg);
				local: 'dex' = self->'pop_ids'->(find: #msg);
			else;
  				fail_if: (local_defined: 'msg'), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer position.';
			/if;
		else;
			local: 'dex' = (integer: (local: 'msg'));
		/if;
		if: (#dex == 0);
			local: 'dex' = (integer: self->'pop_index');
		/if;
		self->'pop_index' = #dex;
		if: #dex > 0;
			if: (self->'pop_get' >> 'retr');
				return: self->(retrieve: #dex);
			else: (self->'pop_get' >> 'dele');
				return: self->(delete: #dex);
			else: (self->'pop_get' >> 'head');
				return: self->(headers: #dex);
			else: (self->'pop_get' >> 'top');
				local: 'size' = (integer: (string_replaceregexp: self->'pop_get', -find='.*(\\d+).*', -replace='\\1'));
				return: self->(retrieve: #dex, #size);
			else;
				return: self->(uniqueid: #dex);
			/if;
		/if;
		return: '';
	/define_tag;
	
	define_tag: 'retrieve',
			-optional='msg',
			-optional='size';
  		fail_if: (local_defined: 'size') && !(string_isdigit: #size), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer size.';
		if: (local_defined: 'msg') && !(string_isdigit: #msg);
			if: (self->'pop_ids' >> #msg);
				local: 'dex' = self->'pop_ids'->(find: #msg);
			else;
  				fail_if: (local_defined: 'msg'), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer position.';
			/if;
		else;
			local: 'dex' = (integer: (local: 'msg'));
		/if;
		if: (#dex == 0);
			local: 'dex' = (integer: self->'pop_index');
		/if;
		self->'pop_index' = #dex;
		if: #dex > 0;
			if: (local_defined: 'size');
				local: 'result' = self->(top: #dex, (integer: #size));
			else;
				local: 'result' = self->(retr: #dex);
			/if;
			fail_if: (#result->(beginswith: '-ERR')), -9956, 'The tag [Email_POP->' + tag_name + '] did not find a message at provided position (' + #dex ').';
			local: 'index' = #result->(find: '\r\n');
			if: (#index > 0);
				local: 'result' = (_substring: #result, #index + 2);
			/if;
			#result->trim;
			#result->(removetrailing: '\r\n.');
			return: #result;
		/if;
		return: '';
	/define_tag;
	
	define_tag: 'headers',
			-optional='msg';
		if: (local_defined: 'msg') && !(string_isdigit: #msg);
			if: (self->'pop_ids' >> #msg);
				local: 'dex' = self->'pop_ids'->(find: #msg);
			else;
  				fail_if: (local_defined: 'msg'), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer position.';
			/if;
		else;
			local: 'dex' = (integer: (local: 'msg'));
		/if;
		if: (#dex == 0);
			local: 'dex' = (integer: self->'pop_index');
		/if;
		self->'pop_index' = #dex;
		if: #dex > 0;
			local: 'result' = self->(top: #dex, 0);
			fail_if: (#result->(beginswith: '-ERR')), -9956, 'The tag [Email_POP->' + tag_name + '] did not find a message at provided position (' + #dex ').';
			local: 'index' = #result->(find: '\r\n');
			if: (#index > 0);
				local: 'result' = (_substring: #result, #index + 2);
			/if;
			#result->trim;
			#result->(removetrailing: '\r\n.');
			return: #result;
		/if;
		return: '';
	/define_tag;
	
	define_tag: 'delete',
			-optional='msg';
		if: (local_defined: 'msg') && !(string_isdigit: #msg);
			if: (self->'pop_ids' >> #msg);
				local: 'dex' = self->'pop_ids'->(find: #msg);
			else;
  				fail_if: (local_defined: 'msg'), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer position.';
			/if;
		else;
			local: 'dex' = (integer: (local: 'msg'));
		/if;
		if: (#dex == 0);
			local: 'dex' = (integer: self->'pop_index');
		/if;
		self->'pop_index' = 0;
		if: #dex > 0;
			local: 'result' = self->(dele: #dex);
			fail_if: (#result->(beginswith: '-ERR')), -9956, 'The tag [Email_POP->' + tag_name + '] did not find a message at provided position (' + #dex ').';
		/if;
	/define_tag;
	
	define_tag: 'uniqueid',
			-optional='msg';
  		fail_if: (local_defined: 'msg') && !(string_isdigit: #msg), -9956, 'The tag [Email_POP->' + tag_name + '] expected an integer position.';
		local: 'dex' = (integer: (local: 'msg'));
		if: (#dex == 0);
			local: 'dex' = (integer: self->'pop_index');
		/if;
		self->'pop_index' = #dex;
		if: #dex > 0;
			local: 'result' = self->(uidl: #dex);
			fail_if: (#result->(beginswith: '-ERR')), -9956, 'The tag [Email_POP->' + tag_name + '] did not find a message at provided position (' + #dex ').';
			local: 'array' = #result->(split: ' ');
			if: (#array->size > 2);
				local: 'id' = #array->(get: 3);
				if: !(string_isdigit: #id);
					self->'pop_ids'->(insert: #id = #dex);
				/if;
				return: @#id;
			/if;
		/if;
		return: '';
	/define_tag;
	
	define_tag: 'capabilities';
		if: self->'pop_capa' === null;
			self->'pop_capa' = map;
			local: 'results' = self->capa;
			if: #results >> '\r\n';
				local: 'array' = #results->(split: '\r\n');
			else: #results >> '\r';
				local: 'array' = #results->(split: '\r');
			else: #results >> '\n';
				local: 'array' = #results->(split: '\n');
			else;
				local: 'array' = array;
			/if;
			iterate: #array, (local: 'temp');
				if: (#temp->size > 0) && (#temp->(beginswith:'+') == false) && (#temp->(beginswith:'.') == false);
					if: #temp >> ' ';
						local: 'split' = #temp->(split: ' ');
						local: 'name' = #split->first;
						#split->(remove: 1);
						self->'pop_capa'->(insert: #name = #split);
					else;
						self->'pop_capa'->(insert: #temp = '');
					/if;
				/if;
			/iterate;
		/if;
		return: self->'pop_capa';
	/define_tag;

	define_tag: 'close';
		self->'pop_ids' = map;
		self->'pop_index' = 0;
		self->(quit);
	/define_tag;
	
	define_tag: 'cancel';
		self->'pop_ids' = map;
		self->'pop_index' = 0;
		self->(rset);
		self->(quit);
	/define_tag;
	
	define_tag: 'results';
		return: self->'pop_res';
	/define_tag;
	
	define_tag: 'errors';
		return: self->'pop_err';
	/define_tag;
	
	define_tag: 'lasterror';
		return: self->'pop_err'->last;
	/define_tag;
			
	define_tag: 'data';
		return: self->'pop_token';
	/define_tag;
	
/define_type;

?>

Comments

No comments

Please log in to comment

Subscribe to the LassoTalk mail list

LassoSoft Inc. > Home

 

 

©LassoSoft Inc 2015 | Web Development by Treefrog Inc | PrivacyLegal terms and Shipping | Contact LassoSoft