This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/mbcico/session.c

603 lines
13 KiB
C
Raw Normal View History

2001-08-17 05:46:24 +00:00
/*****************************************************************************
*
2002-01-07 19:16:03 +00:00
* $Id$
2001-08-17 05:46:24 +00:00
* Purpose ...............: Fidonet mailer
*
*****************************************************************************
2003-08-23 14:57:51 +00:00
* Copyright (C) 1997-2003
2001-08-17 05:46:24 +00:00
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS 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, or (at your option) any
* later version.
*
* MBSE BBS 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.
*
* You should have received a copy of the GNU General Public License
* along with MBSE BBS; see the file COPYING. If not, write to the Free
2003-08-15 20:05:34 +00:00
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
2001-08-17 05:46:24 +00:00
*****************************************************************************/
2002-06-30 12:48:44 +00:00
#include "../config.h"
2001-08-17 05:46:24 +00:00
#include "../lib/libs.h"
#include "../lib/structs.h"
2002-01-07 19:16:03 +00:00
#include "../lib/users.h"
2001-08-17 05:46:24 +00:00
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/nodelist.h"
2002-10-20 20:58:55 +00:00
#include "../lib/mberrors.h"
2001-08-17 05:46:24 +00:00
#include "ttyio.h"
#include "statetbl.h"
#include "emsi.h"
#include "ftsc.h"
#include "session.h"
#include "yoohoo.h"
#include "mbcico.h"
2003-12-27 16:51:42 +00:00
#ifdef USE_NEWBINKP
#include "binkpnew.h"
#else
2001-08-17 05:46:24 +00:00
#include "binkp.h"
2003-12-27 16:51:42 +00:00
#endif
2001-08-17 05:46:24 +00:00
#include "callstat.h"
2003-08-23 14:57:51 +00:00
#include "inbound.h"
2003-11-22 21:42:44 +00:00
#include "opentcp.h"
2001-08-17 05:46:24 +00:00
2003-11-30 14:36:53 +00:00
extern int tcp_mode;
extern pid_t mypid;
2002-04-20 20:23:22 +00:00
2001-08-17 05:46:24 +00:00
2003-12-08 18:01:25 +00:00
node *nlent = NULL;
fa_list *remote = NULL;
2001-08-17 05:46:24 +00:00
int session_flags;
int remote_flags;
int tx_define_type(void);
int rx_define_type(void);
static int type;
static char *data=NULL;
2003-07-06 20:37:58 +00:00
struct sockaddr_in peeraddr;
2001-08-17 05:46:24 +00:00
char *typestr(int);
char *typestr(int tp)
{
2003-08-23 15:55:45 +00:00
switch (tp) {
case SESSION_FTSC: return (char *)"FTS-0001";
case SESSION_YOOHOO: return (char *)"YooHoo/2U2";
case SESSION_EMSI: return (char *)"EMSI";
case SESSION_BINKP: return (char *)"Binkp";
default: return (char *)"Unknown";
}
2001-08-17 05:46:24 +00:00
}
int session(faddr *a, node *nl, int role, int tp, char *dt)
{
2003-08-23 15:55:45 +00:00
int rc = MBERR_OK, addrlen = sizeof(struct sockaddr_in);
fa_list *tmpl;
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
session_flags = 0;
type = tp;
nlent = nl;
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
if (getpeername(0,(struct sockaddr*)&peeraddr,&addrlen) == 0) {
Syslog('s', "TCP connection: len=%d, family=%hd, port=%hu, addr=%s",
2001-08-17 05:46:24 +00:00
addrlen,peeraddr.sin_family, peeraddr.sin_port, inet_ntoa(peeraddr.sin_addr));
2003-08-23 15:55:45 +00:00
if (role == 0) {
if (tcp_mode == TCPMODE_IBN) {
Syslog('+', "Incoming IBN/TCP connection from %s", inet_ntoa(peeraddr.sin_addr));
IsDoing("Incoming IBN/TCP");
} else if (tcp_mode == TCPMODE_IFC) {
Syslog('+', "Incoming IFC/TCP connection from %s", inet_ntoa(peeraddr.sin_addr));
IsDoing("Incoming IFC/TCP");
2003-11-22 21:42:44 +00:00
} else if (tcp_mode == TCPMODE_ITN) {
Syslog('+', "Incoming ITN/TCP connection from %s", inet_ntoa(peeraddr.sin_addr));
IsDoing("Incoming ITN/TCP");
2003-08-23 15:55:45 +00:00
} else if (tcp_mode == TCPMODE_NONE) {
WriteError("Unknown TCP connection, parameter missing");
die(MBERR_COMMANDLINE);
}
2001-08-17 05:46:24 +00:00
}
2003-08-23 15:55:45 +00:00
session_flags |= SESSION_TCP;
}
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
if (data)
free(data);
data=NULL;
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
if (dt)
data=xstrcpy(dt);
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
emsi_local_protos=0;
emsi_local_opts=0;
emsi_local_lcodes=0;
2001-08-17 05:46:24 +00:00
2003-08-23 15:55:45 +00:00
tidy_falist(&remote);
remote=NULL;
if (a) {
remote=(fa_list*)malloc(sizeof(fa_list));
remote->next=NULL;
remote->addr=(faddr*)malloc(sizeof(faddr));
remote->addr->zone=a->zone;
remote->addr->net=a->net;
remote->addr->node=a->node;
remote->addr->point=a->point;
remote->addr->domain=xstrcpy(a->domain);
remote->addr->name=NULL;
} else {
2001-08-17 05:46:24 +00:00
remote=NULL;
2003-08-23 15:55:45 +00:00
}
remote_flags=SESSION_FNC;
if (role) {
if (type == SESSION_UNKNOWN)
(void)tx_define_type();
2003-08-23 18:27:02 +00:00
Syslog('+', "Start outbound %s session with %s", typestr(type), ascfnode(a,0x1f));
2003-08-23 15:55:45 +00:00
switch(type) {
case SESSION_UNKNOWN: rc = MBERR_UNKNOWN_SESSION; break;
case SESSION_FTSC: rc = tx_ftsc(); break;
case SESSION_YOOHOO: rc = tx_yoohoo(); break;
case SESSION_EMSI: rc = tx_emsi(data); break;
case SESSION_BINKP: rc = binkp(role); break;
2001-08-17 05:46:24 +00:00
}
2003-08-23 15:55:45 +00:00
} else {
if (type == SESSION_FTSC)
session_flags |= FTSC_XMODEM_CRC;
if (type == SESSION_UNKNOWN)
(void)rx_define_type();
2003-08-23 18:27:02 +00:00
Syslog('+', "Start inbound %s session", typestr(type));
2003-08-23 15:55:45 +00:00
switch(type) {
case SESSION_UNKNOWN: rc = MBERR_UNKNOWN_SESSION; break;
case SESSION_FTSC: rc = rx_ftsc(); break;
case SESSION_YOOHOO: rc = rx_yoohoo(); break;
case SESSION_EMSI: rc = rx_emsi(data); break;
case SESSION_BINKP: rc = binkp(role); break;
2001-08-17 05:46:24 +00:00
}
2003-08-23 15:55:45 +00:00
}
sleep(2);
for (tmpl = remote; tmpl; tmpl = tmpl->next) {
/*
* Unlock all nodes, locks not owned by us are untouched.
*/
2003-11-30 14:36:53 +00:00
(void)nodeulock(tmpl->addr, mypid);
2003-08-23 15:55:45 +00:00
/*
* If successfull session, reset all status records.
*/
if (rc == 0)
putstatus(tmpl->addr, 0, 0);
}
2003-12-28 13:57:27 +00:00
2003-08-23 15:55:45 +00:00
tidy_falist(&remote);
if (data)
free(data);
data = NULL;
if (emsi_local_password)
free(emsi_local_password);
if (emsi_remote_password)
free(emsi_remote_password);
inbound_close(rc == 0);
return rc;
2001-08-17 05:46:24 +00:00
}
SM_DECL(tx_define_type,(char *)"tx_define_type")
SM_STATES
2003-08-23 18:27:02 +00:00
skipjunk,
wake,
waitchar,
nextchar,
checkintro,
sendinq
2001-08-17 05:46:24 +00:00
SM_NAMES
2003-08-23 18:27:02 +00:00
(char *)"skipjunk",
(char *)"wake",
(char *)"waitchar",
(char *)"nextchar",
(char *)"checkintro",
(char *)"sendinq"
2001-08-17 05:46:24 +00:00
SM_EDECL
2003-08-23 18:27:02 +00:00
int c = 0;
char buf[256], *p;
char ebuf[13], *ep;
int standby = 0;
int maybeftsc=0;
int maybeyoohoo=0;
type = SESSION_UNKNOWN;
ebuf[0] = '\0';
ep = ebuf;
buf[0] = '\0';
p = buf;
2001-08-17 05:46:24 +00:00
SM_START(skipjunk)
SM_STATE(skipjunk)
2003-08-23 18:27:02 +00:00
Syslog('s', "tx_define_type SKIPJUNK");
while ((c = GETCHAR(1)) >= 0) /*nothing*/ ;
if (c == TIMEOUT) {
2003-09-17 19:33:34 +00:00
gpt_resettimers();
gpt_settimer(0, 60); /* 60 second master timer */
2003-08-23 18:27:02 +00:00
SM_PROCEED(wake);
} else {
SM_ERROR;
}
2001-08-17 05:46:24 +00:00
SM_STATE(wake)
2003-08-23 18:27:02 +00:00
Syslog('s', "tx_define_type WAKE");
2003-09-17 19:33:34 +00:00
if (gpt_expired(0)) {
2003-08-23 18:27:02 +00:00
Syslog('+', "Remote did not respond");
SM_ERROR;
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
p = buf;
PUTCHAR('\r');
if ((c = GETCHAR(2)) == TIMEOUT) {
SM_PROCEED(wake);
} else if (c < 0) {
WriteError("Error while waking remote");
SM_ERROR;
} else {
2003-09-17 19:33:34 +00:00
gpt_settimer(0, 60);
2003-08-23 18:27:02 +00:00
Syslog('S', "Got %c wakeup", c);
SM_PROCEED(nextchar);
}
2001-08-17 05:46:24 +00:00
SM_STATE(waitchar)
2003-08-23 18:27:02 +00:00
if ((c = GETCHAR(2)) == TIMEOUT) {
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
2003-09-17 19:33:34 +00:00
if (gpt_expired(0)) {
2003-08-23 18:27:02 +00:00
Syslog('+', "Too many tries waking remote");
SM_ERROR;
2001-08-17 05:46:24 +00:00
}
2003-08-23 18:27:02 +00:00
SM_PROCEED(sendinq);
} else if (c < 0) {
Syslog('+', "Error while getting intro from remote");
SM_ERROR;
} else {
SM_PROCEED(nextchar);
}
2001-08-17 05:46:24 +00:00
SM_STATE(nextchar)
2003-08-23 18:27:02 +00:00
if (c == 'C') {
session_flags |= FTSC_XMODEM_CRC;
maybeftsc++;
}
if (c == NAK) {
session_flags &= ~FTSC_XMODEM_CRC;
maybeftsc++;
}
if (c == ENQ)
maybeyoohoo++;
if (((localoptions & NOWAZOO) == 0) && (maybeyoohoo > 1)) {
type = SESSION_YOOHOO;
SM_SUCCESS;
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if (maybeftsc > 1) {
type = SESSION_FTSC;
SM_SUCCESS;
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if ((c >= ' ') && (c <= '~')) {
if (c != 'C')
maybeftsc = 0;
maybeyoohoo = 0;
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if ((p-buf) < (sizeof(buf)-1)) {
*p++ = c;
*p = '\0';
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if (c == '*') {
standby = 1;
ep = ebuf;
buf[0] = '\0';
} else if (standby) {
if ((ep - ebuf) < (sizeof(ebuf) - 1)) {
*ep++ = c;
*ep = '\0';
}
if ((ep - ebuf) >= (sizeof(ebuf) - 1)) {
standby = 0;
SM_PROCEED(checkintro);
}
}
} else {
switch (c) {
case DC1: break;
case '\r':
case '\n': standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
if (buf[0])
2003-08-23 18:27:02 +00:00
Syslog('+', "Intro: \"%s\"", printable(buf, 0));
2001-08-17 05:46:24 +00:00
p = buf;
buf[0] = '\0';
break;
2003-08-23 18:27:02 +00:00
default: standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
Syslog('i', "Got '%s' reading intro", printablec(c));
break;
}
2003-08-23 18:27:02 +00:00
}
SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
SM_STATE(checkintro)
2003-08-23 18:27:02 +00:00
Syslog('i', "Check \"%s\" for being EMSI request",ebuf);
if (((localoptions & NOEMSI) == 0) && (strncasecmp(ebuf,"EMSI_REQA77E",12) == 0)) {
type = SESSION_EMSI;
data = xstrcpy((char *)"**EMSI_REQA77E");
Syslog('i', "Sending **EMSI_INQC816 (2 times)");
PUTSTR((char *)"\r**EMSI_INQC816\r**EMSI_INQC816\r\021");
SM_SUCCESS;
} else {
p = buf;
buf[0] = '\0';
SM_PROCEED(waitchar);
}
2001-08-17 05:46:24 +00:00
SM_STATE(sendinq)
2003-08-23 18:27:02 +00:00
Syslog('s', "tx_define_type SENDINQ");
PUTCHAR(DC1);
if ((localoptions & NOEMSI) == 0) {
Syslog('S', "send **EMSI_INQC816 (2 times)");
PUTSTR((char *)"\r**EMSI_INQC816\r**EMSI_INQC816");
}
if ((localoptions & NOWAZOO) == 0) {
Syslog('S', "send YOOHOO char");
PUTCHAR(YOOHOO);
}
Syslog('S', "send TSYNC char");
PUTCHAR(TSYNC);
if ((localoptions & NOEMSI) == 0)
PUTSTR((char *)"\r\021");
SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
SM_END
SM_RETURN
SM_DECL(rx_define_type,(char *)"rx_define_type")
SM_STATES
2003-08-23 18:27:02 +00:00
sendintro,
2003-08-23 21:15:16 +00:00
settimer,
2003-08-23 18:27:02 +00:00
waitchar,
nextchar,
checkemsi,
getdat
2001-08-17 05:46:24 +00:00
SM_NAMES
2003-08-23 18:27:02 +00:00
(char *)"sendintro",
2003-08-23 21:15:16 +00:00
(char *)"settimer",
2003-08-23 18:27:02 +00:00
(char *)"waitchar",
(char *)"nextchar",
(char *)"checkemsi",
(char *)"getdat"
2001-08-17 05:46:24 +00:00
SM_EDECL
2003-08-23 18:27:02 +00:00
int count=0;
int c=0;
int maybeftsc=0,maybeyoohoo=0;
char ebuf[13],*ep;
char *p;
int standby=0;
int datasize;
type=SESSION_UNKNOWN;
session_flags|=FTSC_XMODEM_CRC;
ebuf[0]='\0';
ep=ebuf;
2003-09-17 19:33:34 +00:00
gpt_resettimers();
gpt_settimer(0, 60);
gpt_settimer(1, 20);
2001-08-17 05:46:24 +00:00
SM_START(sendintro)
SM_STATE(sendintro)
2003-08-23 18:27:02 +00:00
if (count++ > 6) {
Syslog('+', "Too many tries to get anything from the caller");
SM_ERROR;
}
2001-08-17 05:46:24 +00:00
2003-08-23 21:15:16 +00:00
Syslog('s', "rxdefine_type SENDINTRO count=%d", count);
2003-08-23 18:27:02 +00:00
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if ((localoptions & NOEMSI) == 0) {
PUTSTR((char *)"**EMSI_REQA77E\r\021");
}
PUTCHAR('\r');
if (STATUS) {
SM_ERROR;
} else {
2003-08-23 21:15:16 +00:00
SM_PROCEED(settimer);
2003-08-23 18:27:02 +00:00
}
2001-08-17 05:46:24 +00:00
2003-08-23 21:15:16 +00:00
SM_STATE(settimer)
Syslog('s', "Set 20 secs timer");
2003-09-17 19:33:34 +00:00
gpt_settimer(1, 20);
2003-08-23 21:15:16 +00:00
SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
SM_STATE(waitchar)
2003-09-17 19:33:34 +00:00
if (gpt_expired(0)) {
2003-08-23 19:02:25 +00:00
Syslog('+', "Session setup timeout");
2003-08-23 18:27:02 +00:00
SM_ERROR;
}
2003-09-17 19:33:34 +00:00
if (gpt_expired(1)) {
2003-08-23 21:15:16 +00:00
Syslog('s', "20 sec timer timeout");
2003-08-23 18:27:02 +00:00
SM_PROCEED(sendintro);
2003-08-23 21:15:16 +00:00
}
if ((c = GETCHAR(1)) == TIMEOUT) {
SM_PROCEED(waitchar);
2003-08-23 18:27:02 +00:00
} else if (c < 0) {
2003-08-23 19:02:25 +00:00
Syslog('+', "Session setup error");
2003-08-23 18:27:02 +00:00
SM_ERROR;
} else {
SM_PROCEED(nextchar);
}
2001-08-17 05:46:24 +00:00
SM_STATE(nextchar)
2003-08-23 18:27:02 +00:00
if ((c >= ' ') && (c <= 'z')) {
if (c == '*') {
standby = 1;
ep = ebuf;
ebuf[0] = '\0';
} else if (standby) {
if ((ep - ebuf) < (sizeof(ebuf) - 1)) {
*ep++ = c;
*ep = '\0';
}
if ((ep - ebuf) >= (sizeof(ebuf) - 1)) {
standby = 0;
SM_PROCEED(checkemsi);
}
}
SM_PROCEED(waitchar);
} else {
switch (c) {
case DC1: SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
break;
2003-08-23 18:27:02 +00:00
case TSYNC: standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
if (++maybeftsc > 1) {
2003-08-23 18:27:02 +00:00
type = SESSION_FTSC;
SM_SUCCESS;
2001-08-17 05:46:24 +00:00
} else {
2003-08-23 18:27:02 +00:00
SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
}
break;
2003-08-23 18:27:02 +00:00
case YOOHOO:standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
if (++maybeyoohoo > 1) {
2003-08-23 18:27:02 +00:00
type = SESSION_YOOHOO;
SM_SUCCESS;
2001-08-17 05:46:24 +00:00
} else {
2003-08-23 18:27:02 +00:00
SM_PROCEED(waitchar);
2001-08-17 05:46:24 +00:00
}
break;
2003-08-23 18:27:02 +00:00
case '\r':
case '\n': standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
if (ebuf[0]) {
2003-08-23 18:27:02 +00:00
SM_PROCEED(checkemsi);
2001-08-17 05:46:24 +00:00
} else {
2003-08-23 21:15:16 +00:00
/*
* If the 20 second timer is expired or after the
* first sendintro, send the intro again. After
* that take it easy.
*/
2003-09-17 19:33:34 +00:00
if (gpt_expired(1) || (count == 1)) {
2003-08-23 21:15:16 +00:00
Syslog('s', "sendintro after eol char");
SM_PROCEED(sendintro);
} else {
Syslog('s', "waitchar after eol char");
SM_PROCEED(waitchar);
}
2001-08-17 05:46:24 +00:00
}
break;
2003-08-23 18:27:02 +00:00
default: standby = 0;
2001-08-17 05:46:24 +00:00
ep = ebuf;
ebuf[0] = '\0';
Syslog('i', "Got '%s' from remote", printablec(c));
SM_PROCEED(waitchar);
break;
}
2003-08-23 18:27:02 +00:00
}
2001-08-17 05:46:24 +00:00
SM_STATE(checkemsi)
2003-08-23 18:27:02 +00:00
Syslog('i', "check \"%s\" for being EMSI inquery or data",ebuf);
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if (localoptions & NOEMSI) {
2003-08-23 21:15:16 +00:00
Syslog('s', "Force sendintro");
2003-08-23 18:27:02 +00:00
SM_PROCEED(sendintro);
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if (strncasecmp(ebuf, "EMSI_INQC816", 12) == 0) {
type = SESSION_EMSI;
data = xstrcpy((char *)"**EMSI_INQC816");
SM_SUCCESS;
} else if (strncasecmp(ebuf, "EMSI_HBT", 8) == 0) {
standby = 0;
ep = ebuf;
ebuf[0] = '\0';
2003-08-23 21:15:16 +00:00
SM_PROCEED(settimer);
2003-08-23 18:27:02 +00:00
} else if (strncasecmp(ebuf, "EMSI_DAT", 8) == 0) {
SM_PROCEED(getdat);
} else {
2003-08-23 21:15:16 +00:00
SM_PROCEED(settimer);
2003-08-23 18:27:02 +00:00
}
2001-08-17 05:46:24 +00:00
SM_STATE(getdat)
2003-08-23 18:27:02 +00:00
Syslog('i', "Try get emsi_dat packet starting with \"%s\"",ebuf);
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
if (sscanf(ebuf+8, "%04x", &datasize) != 1) {
SM_PROCEED(sendintro);
}
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
datasize += 18; /* strlen("**EMSI_DATxxxxyyyy"), include CRC */
data=malloc(datasize+1);
strcpy(data,"**");
strcat(data, ebuf);
p = data + strlen(data);
2001-08-17 05:46:24 +00:00
2003-08-23 18:27:02 +00:00
while (((p - data) < datasize) && ((c = GETCHAR(8)) >= 0)) {
*p++ = c;
*p= '\0';
}
if (c == TIMEOUT) {
2003-08-23 21:15:16 +00:00
Syslog('s', "c = TIMEOUT -> sendintro");
2003-08-23 18:27:02 +00:00
SM_PROCEED(sendintro);
} else if (c < 0) {
Syslog('+', "Error while reading EMSI_DAT from the caller");
SM_ERROR;
}
type = SESSION_EMSI;
SM_SUCCESS;
2001-08-17 05:46:24 +00:00
SM_END
SM_RETURN