home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 1999 November
/
PCWorld_1999-11_cd.bin
/
Komunik
/
Sambar
/
_setup.1
/
login.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-05-16
|
42KB
|
1,627 lines
/*
** LOGIN
**
** HTTP Wrapper for the Login/Logout/Profile Management Library
**
** Confidential Property of Tod Sambar
** (c) Copyright Tod Sambar 1996-1997
** All rights reserved.
**
**
** Public Functions:
**
** login_init
** user_login
** user_logout
** user_profile
** user_add
** user_delete
** user_update
** user_passwd
** user_list
** user_select
** group_add
** group_delete
** group_list
** group_select
** ftp_connect
** mail_connect
** telnet_connect
**
**
** History:
** Chg# Date Description Resp
** ---- ------- ------------------------------------------------------- ----
** 27JAN96 Created sambar
** 29MAR97 Added user list and update functions sambar
** 30MAR97 Added FTP connection security sambar
** 10OCT97 Added user password interface sambar
** 14NOV97 Added group management sambar
** 28OCT98 Added MAIL connection security sambar
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <login.h>
/*
** Login RPC Commands
*/
typedef struct login__rpcs
{
SA_CHAR *name;
SA_RPCPARAM *params;
SA_INT numparams;
SA_INT auth;
SA_VOID *func;
SA_CHAR *descr;
} LOGIN__RPCS;
static SA_RPCPARAM adduserp [] =
{
{ "username", 1, "User login." },
{ "password", 0, "User password." },
{ "group", 0, "User group." },
{ "name", 0, "User name." },
{ "dir", 0, "Ftp directory of the user." },
{ "ftpprivs", 1, "FTP priviledges of the user." },
{ "ftpmax", 1, "FTP maximum upload (MB)." },
{ "mbox", 0, "Create a mailbox for the user." }
};
static SA_RPCPARAM upduserp [] =
{
{ "username", 1, "User login." },
{ "password", 0, "User password." },
{ "group", 0, "User group." },
{ "name", 0, "User name." },
{ "dir", 0, "Ftp directory of the user." },
{ "ftpprivs", 1, "FTP priviledges of the user." },
{ "ftpmax", 1, "FTP maximum upload (MB)." },
{ "mbox", 0, "Create a mailbox for the user." }
};
static SA_RPCPARAM deluserp [] =
{
{ "username", 1, "User login." }
};
static SA_RPCPARAM userpropp [] =
{
{ "username", 1, "User login." },
{ "prop", 1, "User property: group,password,name,dir,ftpprivs,ftpmax" }
};
static SA_RPCPARAM selectuserp [] =
{
{ "username", 0, "User login." },
};
static SA_RPCPARAM addgroupp [] =
{
{ "groupname", 1, "Symbolic name of the group." }
};
static SA_RPCPARAM delgroupp [] =
{
{ "groupname", 1, "Symbolic name of the group." }
};
static SA_RPCPARAM selectgroupp [] =
{
{ "username", 0, "User to test against group list." }
};
static SA_RPCPARAM userpasswdp [] =
{
{ "username", 1, "User login." },
{ "password", 0, "Existing password for the user." },
{ "newpasswd", 0, "New password for the user." },
{ "confpasswd", 0, "Confirm new password." }
};
static LOGIN__RPCS login_rpcs [] =
{
{ "adduser", adduserp, sizeof(adduserp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_add,
"Add a user to the system." },
{ "upduser", upduserp, sizeof(upduserp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_update,
"Update a user." },
{ "deluser", deluserp, sizeof(deluserp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_delete,
"Delete a user from the system." },
{ "userprop", userpropp, sizeof(userpropp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_prop,
"Get a single user attribute for display." },
{ "listusers", NULL, 0,
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_list,
"List users for update/delete." },
{ "selectuser", selectuserp, sizeof(selectuserp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)user_select,
"List users for selection in a pick list." },
{ "addgroup", addgroupp, sizeof(addgroupp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)group_add,
"Add a group to the system." },
{ "delgroup", delgroupp, sizeof(delgroupp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)group_delete,
"Delete a group from the system." },
{ "listgroups", NULL, 0,
SA_AUTHORIZATION_ADMIN, (SA_VOID *)group_list,
"List groups for delete." },
{ "selectgroup",selectgroupp, sizeof(selectgroupp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ADMIN, (SA_VOID *)group_select,
"List groups for selection in a pick list." },
{ "userpasswd", userpasswdp, sizeof(userpasswdp)/sizeof(SA_RPCPARAM),
SA_AUTHORIZATION_ALL, (SA_VOID *)user_passwd,
"Interface to allow users to change their password." }
};
/*
** LOGIN_INIT
**
** Initialize the Login/Logout/Profile Interfaces for use by the
** Sambar Server plugins.
**
**
** Parameters:
** sactx Sambar Server context
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
login_init(sactx)
SA_CTX *sactx;
{
int i;
/* Register the User RPCs with the server */
for (i = 0; i < sizeof(login_rpcs) / sizeof(LOGIN__RPCS); i++)
{
if (sa_cmd_init(sactx, login_rpcs[i].name, login_rpcs[i].params,
login_rpcs[i].numparams, login_rpcs[i].auth, login_rpcs[i].descr,
(SA_RPCFUNC)login_rpcs[i].func) != SA_SUCCEED)
{
sa_log(sactx, "Unable to initialize User Management RPCs");
return (SA_FAIL);
}
}
sa_log(sactx, "Login/Logout/Profile Management Initialized");
return (SA_SUCCEED);
}
/*
** USER_LOGIN
**
** Login a user. Verify the username/password against the Sambar Server
** password interfaces. Store some profile information for use.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
** username Name of the user logging in.
** usernamelen Length of the user name
** password Password of the user logging in.
** passwordlen Length of the password.
** infop Error return code
**
** Return Values:
** SA_SUCCESS | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_login(sactx, saconn, username, usernamelen, password, passwordlen, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_CHAR *username;
SA_INT usernamelen;
SA_CHAR *password;
SA_INT passwordlen;
SA_INT *infop;
{
SA_PASSWD passwd;
LOGIN_PROFILE *profile;
SA_CHAR buffer[512];
if (sa_passwd_lookup(sactx, username, usernamelen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "Login for user '%s' failed.", username);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDLOGIN;
return (SA_FAIL);
}
/* Verify the passwords are the same */
if ((passwd.passwordlen != passwordlen) ||
(strncmp(passwd.password, password, passwordlen) != 0))
{
sprintf(buffer, "Login for user '%s' failed - bad password (%s)",
username, password);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDLOGIN;
return (SA_FAIL);
}
/* Allocate and populate a user profile structure */
profile = (LOGIN_PROFILE *)malloc(sizeof(LOGIN_PROFILE));
if (profile == (LOGIN_PROFILE *)NULL)
{
sprintf(buffer, "Login for user '%s' failed - no memory", username);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDLOGIN;
return (SA_FAIL);
}
if (passwd.namelen > 0)
memcpy(profile->name, passwd.name, passwd.namelen);
if (passwd.grouplen > 0)
memcpy(profile->group, passwd.group, passwd.grouplen);
memcpy(profile->username, username, usernamelen);
profile->name[passwd.namelen] = '\0';
profile->group[passwd.grouplen] = '\0';
profile->username[usernamelen] = '\0';
/* Save the user's profile handle with the user connection */
if (sa_conn_key(saconn, SA_SET, LOGIN_PROFILE_KEY, (SA_VOID **)profile)
!= SA_SUCCEED)
{
(SA_VOID)free(profile);
sa_log(sactx, "sa_conn_key(SET, LOGIN_PROFILE_KEY) failed!");
return (SA_FAIL);
}
#ifdef DEBUG
sprintf(buffer, "User '%s' logged in.", username);
sa_log(sactx, buffer);
#endif /* DEBUG */
return (SA_SUCCEED);
}
/*
** USER_LOGOUT
**
** Log out a user. Free profile resources.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
**
** Return Values:
** SA_SUCCESS | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_logout(sactx, saconn)
SA_CTX *sactx;
SA_CONN *saconn;
{
LOGIN_PROFILE *profile;
#ifdef DEBUG
SA_CHAR buffer[512];
#endif /* DEBUG */
/* Get the user's pillar handle from the user context */
if (sa_conn_key(saconn, SA_GET, LOGIN_PROFILE_KEY, (SA_VOID **)&profile)
!= SA_SUCCEED)
{
/* User never completed login */
return (SA_SUCCEED);
}
#ifdef DEBUG
sprintf(buffer, "User '%s' logged out.", profile->username);
sa_log(sactx, buffer);
#endif /* DEBUG */
(void)free(profile);
return (SA_SUCCEED);
}
/*
** USER_PROFILE
**
** Respond to a user profile request.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
** buffer Profile attribute being queried.
** buflen Length of the profile attribute.
** data Buffer for the profile result
** Note: A maximum of 512 bytes may be written to the data buffer.
**
** Return Values:
** SA_SUCCESS | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_profile(sactx, saconn, buffer, buflen, data)
SA_CTX *sactx;
SA_CONN *saconn;
SA_CHAR *buffer;
SA_INT buflen;
SA_CHAR *data;
{
LOGIN_PROFILE *profile;
SA_CHAR tmp[512];
/* Get the user's profile handle */
if (sa_conn_key(saconn, SA_GET, LOGIN_PROFILE_KEY, (SA_VOID **)&profile)
!= SA_SUCCEED)
{
/* User never completed login */
return (SA_FAIL);
}
if (profile == (LOGIN_PROFILE *)NULL)
{
sa_log(sactx, "user_profile: LOGIN_PROFILE_KEY returned NULL!");
return (SA_FAIL);
}
/*
** Profile lookup
*/
if ((buflen == 5) && (strncmp(buffer, "group", 5) == 0))
{
strcpy(data, profile->group);
}
else if ((buflen == 4) && (strncmp(buffer, "name", 4) == 0))
{
strcpy(data, profile->name);
}
else
{
sprintf(tmp, "Profile attribute '%s' not supported!", buffer);
sa_log(sactx, tmp);
return (SA_FAIL);
}
return (SA_SUCCEED);
}
/*
** USER_ADD
**
** Add a new user
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_add(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT mbox;
SA_INT datalen;
SA_CHAR *data;
SA_PASSWD passwd;
memset(&passwd, 0, sizeof(SA_PASSWD));
mbox = 0;
if (sa_param(sactx, saparams, "mbox", &data, &datalen) == SA_SUCCEED)
{
if ((datalen == 2) & (stricmp(data, "on") == 0))
mbox = 1;
}
if (sa_param(sactx, saparams, "password", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'password'!");
return (SA_FAIL);
}
passwd.passwordlen = MIN(datalen, SA_MAX_NAME);
if (passwd.passwordlen > 0)
memcpy(passwd.password, data, passwd.passwordlen);
if (sa_param(sactx, saparams, "group", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'group'!");
return (SA_FAIL);
}
passwd.grouplen = MIN(datalen, SA_MAX_NAME);
if (passwd.grouplen > 0)
memcpy(passwd.group, data, passwd.grouplen);
if (sa_param(sactx, saparams, "name", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'name'!");
return (SA_FAIL);
}
passwd.namelen = MIN(datalen, SA_MAX_NAME);
if (passwd.namelen > 0)
memcpy(passwd.name, data, passwd.namelen);
if (sa_param(sactx, saparams, "dir", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'dir'!");
return (SA_FAIL);
}
passwd.dirlen = MIN(datalen, SA_MAX_NAME);
if (passwd.dirlen > 0)
memcpy(passwd.dir, data, passwd.dirlen);
if (sa_param(sactx, saparams, "ftpprivs", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'ftpprivs'!");
return (SA_FAIL);
}
passwd.ftpprivs = atol(data);
if (sa_param(sactx, saparams, "ftpmax", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'ftpmax'!");
return (SA_FAIL);
}
passwd.ftpmax = atol(data);
if (passwd.ftpmax < 5)
passwd.ftpmax = 0;
if (sa_param(sactx, saparams, "username", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): Expected parameter 'username'!");
return (SA_FAIL);
}
if ((datalen == 0) || (datalen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_add(): 'username' field left NULL!");
return (SA_FAIL);
}
/* Create the user account. */
if (sa_passwd_add(sactx, data, datalen, &passwd) != SA_SUCCEED)
{
*infop = SA_E_ALREADYDEFINED;
sa_log(sactx, "user_add(): sa_passwd_add() failed!");
return (SA_FAIL);
}
/* Create the user mailbox. */
if (mbox)
{
if (sa_mbox_create(sactx, data) != SA_SUCCEED)
{
*infop = SA_E_ALREADYDEFINED;
sa_log(sactx, "user_add(): sa_mbox_create() failed!");
return (SA_FAIL);
}
}
return (SA_SUCCEED);
}
/*
** USER_UPDATE
**
** Update an existing user
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_update(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT mbox;
SA_INT datalen;
SA_CHAR *data;
SA_PASSWD passwd;
memset(&passwd, 0, sizeof(SA_PASSWD));
mbox = 0;
if (sa_param(sactx, saparams, "mbox", &data, &datalen) == SA_SUCCEED)
{
if ((datalen == 2) & (stricmp(data, "on") == 0))
mbox = 1;
}
if (sa_param(sactx, saparams, "password", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'password'!");
return (SA_FAIL);
}
passwd.passwordlen = MIN(datalen, SA_MAX_NAME);
if (passwd.passwordlen > 0)
memcpy(passwd.password, data, passwd.passwordlen);
if (sa_param(sactx, saparams, "group", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'group'!");
return (SA_FAIL);
}
passwd.grouplen = MIN(datalen, SA_MAX_NAME);
if (passwd.grouplen > 0)
memcpy(passwd.group, data, passwd.grouplen);
if (sa_param(sactx, saparams, "name", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'name'!");
return (SA_FAIL);
}
passwd.namelen = MIN(datalen, SA_MAX_NAME);
if (passwd.namelen > 0)
memcpy(passwd.name, data, passwd.namelen);
if (sa_param(sactx, saparams, "dir", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'dir'!");
return (SA_FAIL);
}
passwd.dirlen = MIN(datalen, SA_MAX_NAME);
if (passwd.dirlen > 0)
memcpy(passwd.dir, data, passwd.dirlen);
if (sa_param(sactx, saparams, "ftpprivs", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'ftpprivs'!");
return (SA_FAIL);
}
passwd.ftpprivs = atol(data);
if (sa_param(sactx, saparams, "ftpmax", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'ftpmax'!");
return (SA_FAIL);
}
passwd.ftpmax = atol(data);
if (passwd.ftpmax < 5)
passwd.ftpmax = 0;
if (sa_param(sactx, saparams, "username", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): Expected parameter 'username'!");
return (SA_FAIL);
}
if ((datalen == 0) || (datalen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): 'username' field left NULL!");
return (SA_FAIL);
}
if (sa_passwd_update(sactx, data, datalen, &passwd) != SA_SUCCEED)
{
*infop = SA_E_ALREADYDEFINED;
sa_log(sactx, "user_update(): sa_passwd_update() failed!");
return (SA_FAIL);
}
/* Create the user mailbox. */
if (mbox)
{
if (sa_mbox_create(sactx, data) != SA_SUCCEED)
{
*infop = SA_E_ALREADYDEFINED;
sa_log(sactx, "user_update(): sa_mbox_create() failed!");
return (SA_FAIL);
}
}
return (SA_SUCCEED);
}
/*
** USER_DELETE
**
** Delete an existing user
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_delete(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT datalen;
SA_INT sysadminlen;
SA_CHAR *data;
SA_CHAR sysadmin[SA_MAX_NAME + 1];
/* Get the sysadmin user (disallow deletes) */
if (sa_ctx_props(sactx, SA_GET, SA_CTXPROP_SYSADMIN, (SA_BYTE *)sysadmin,
SA_MAX_NAME, &sysadminlen) != SA_SUCCEED)
{
sa_log(sactx, "user_delete(): failure retrieving system admin user!");
return (SA_FAIL);
}
if (sa_param(sactx, saparams, "username", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_delete(): Expected parameter 'username'!");
return (SA_FAIL);
}
if ((datalen == 0) || (datalen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_delete(): 'username' field left NULL!");
return (SA_FAIL);
}
/* Don't allow the deletion of the system administrator */
if ((datalen != sysadminlen) || (memcmp(sysadmin, data, sysadminlen) != 0))
{
(SA_VOID)sa_passwd_delete(sactx, data, datalen);
(SA_VOID)sa_mbox_delete(sactx, data);
}
return (SA_SUCCEED);
}
/*
** USER_SELECT
**
** List all users for selection.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
**
** Notes:
** This list is truncated at 500 names.
*/
SA_RETCODE SA_PUBLIC
user_select(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT i;
SA_BOOL selected;
SA_INT usernamelen;
SA_INT numusers;
SA_CHAR *username;
SA_USER users[500];
SA_CHAR buffer[256];
/* If a username is provided make it SELECTED in the OPTION list */
(SA_VOID)sa_param(sactx, saparams, "username", &username, &usernamelen);
if (sa_passwd_list(sactx, users, 500, &numusers) != SA_SUCCEED)
{
sa_log(sactx, "user_select(): failure retrieving names list!");
return (SA_FAIL);
}
/* We could sort the names alphabetically at this point... */
/* Display the list of users. */
for (i = 0; i < numusers; i++)
{
selected = 0;
if ((users[i].usernamelen == usernamelen) &&
(memcmp(username, users[i].username, usernamelen) == 0))
{
selected = 1;
}
sprintf(buffer, "<OPTION %s>%s\n", (selected ? "SELECTED" : ""),
users[i].username);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
return (SA_SUCCEED);
}
/*
** USER_LIST
**
** List all users for delete or update.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
**
** Notes:
** This list is truncated at 500 names.
** List users by group...
*/
SA_RETCODE SA_PUBLIC
user_list(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT i;
SA_INT j;
SA_INT sysadminlen;
SA_INT numusers;
SA_INT numgroups;
SA_GROUP groups[100];
SA_USER users[500];
SA_CHAR sysadmin[SA_MAX_NAME + 1];
SA_CHAR buffer[256];
/* Get the sysadmin user (disallow deletes) */
if (sa_ctx_props(sactx, SA_GET, SA_CTXPROP_SYSADMIN, (SA_BYTE *)sysadmin,
SA_MAX_NAME, &sysadminlen) != SA_SUCCEED)
{
sa_log(sactx, "user_list(): failure retrieving system admin user!");
return (SA_FAIL);
}
if (sa_passwd_list(sactx, users, 500, &numusers) != SA_SUCCEED)
{
sa_log(sactx, "user_list(): failure retrieving names list!");
return (SA_FAIL);
}
if (sa_group_list(sactx, groups, 100, &numgroups) != SA_SUCCEED)
{
sa_log(sactx, "user_list(): failure retrieving groups list!");
return (SA_FAIL);
}
/* We could sort the list alphabetically at this point. */
/*
** Display the users by "group".
** Zero the usernamelen after displaying so we can put the rest in
** the "other" list.
*/
for (j = 0; j < numgroups; j++)
{
sprintf(buffer, "<FONT SIZE=+1 COLOR=#990033>%s</FONT><BLOCKQUOTE>\n",
groups[j].name);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
/* Display the users in that group. */
for (i = 0; i < numusers; i++)
{
if ((users[i].usernamelen > 0) &&
(users[i].grouplen == groups[j].namelen) &&
(memcmp(users[i].group, groups[j].name, users[i].grouplen)==0))
{
/* Make sure we don't display the user twice. */
users[i].usernamelen = 0;
if ((users[i].usernamelen != sysadminlen) ||
(strncmp(sysadmin, users[i].username, sysadminlen) != 0))
{
sprintf(buffer,
"<A HREF=\"/session/deluser?username=%s&RCpage=/sysadmin/usermgmt/userlist.stm\" onClick=\"return confirmDelete()\"><IMG border=0 HEIGHT=20 WIDTH=20 SRC=\"/sysimage/system/trash.gif\"></A>\n",
users[i].username);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
sprintf(buffer,
"<A HREF=\"/sysadmin/usermgmt/upduser.stm?RCSusername=%s\" TARGET=body><IMG border=0 HEIGHT=20 WIDTH=20 SRC=\"/sysimage/system/info.gif\"> %s</A><BR>\n",
users[i].username, users[i].username);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
}
if (sa_conn_send(saconn, "</BLOCKQUOTE>", SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
/* Display the "other" group */
sprintf(buffer, "<FONT SIZE=+1 COLOR=#990066>%s</FONT><BLOCKQUOTE>\n",
SA_DEFAULT_GROUP);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
/* Display the users in that group. */
for (i = 0; i < numusers; i++)
{
/* Display all users not in any other group. */
if (users[i].usernamelen > 0)
{
if ((users[i].usernamelen != sysadminlen) ||
(strncmp(sysadmin, users[i].username, sysadminlen) != 0))
{
sprintf(buffer,
"<A HREF=\"/session/deluser?username=%s&RCpage=/sysadmin/usermgmt/userlist.stm\" onClick=\"return confirmDelete()\"><IMG border=0 HEIGHT=15 WIDTH=15 SRC=\"/sysimage/system/trash.gif\"></A>\n",
users[i].username);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
sprintf(buffer,
"<A HREF=\"/sysadmin/usermgmt/upduser.stm?RCSusername=%s\" TARGET=body><IMG border=0 HEIGHT=15 WIDTH=15 SRC=\"/sysimage/system/info.gif\"> %s</A><BR>\n",
users[i].username, users[i].username);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
}
if (sa_conn_send(saconn, "</BLOCKQUOTE>\n", SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
return (SA_SUCCEED);
}
/*
** USER_PASSWD
**
** Update a user' password.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_passwd(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT datalen;
SA_INT passwordlen;
SA_INT usernamelen;
SA_CHAR *data;
SA_CHAR *password;
SA_CHAR *username;
SA_PASSWD passwd;
/* What username is being updated? */
if (sa_param(sactx, saparams, "username", &username, &usernamelen)
!= SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Expected parameter 'username'!");
return (SA_FAIL);
}
if ((usernamelen == 0) || (usernamelen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_update(): 'username' field left NULL!");
return (SA_FAIL);
}
/* Lookup the username provided for update. */
if (sa_passwd_lookup(sactx, username, usernamelen, &passwd) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): sa_passwd_lookup() failed!");
return (SA_FAIL);
}
/* Make sure that the password is valid. */
if (sa_param(sactx, saparams, "password", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Expected parameter 'password'!");
return (SA_FAIL);
}
/* Compare with the existing password. */
if (datalen != passwd.passwordlen)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Invalid password provided.");
return (SA_FAIL);
}
if (datalen > 0)
{
if (memcmp(passwd.password, data, datalen) != 0)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Invalid password provided.");
return (SA_FAIL);
}
}
/* Get the new password. */
if (sa_param(sactx, saparams, "newpasswd", &password, &passwordlen)
!= SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Expected parameter 'newpasswd'!");
return (SA_FAIL);
}
/* Confirm the new password. */
if (sa_param(sactx, saparams, "confpasswd", &data, &datalen)
!= SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_passwd(): Expected parameter 'confpasswd'!");
return (SA_FAIL);
}
/* Does the new password and confirm password match? */
if ((passwordlen > 0) && (datalen > 0))
{
if ((passwordlen != datalen) ||
(memcmp(password, data, datalen) != 0))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx,
"user_passwd(): New & confirmation password do not match!");
return (SA_FAIL);
}
}
passwd.passwordlen = MIN(datalen, SA_MAX_NAME);
if (passwd.passwordlen > 0)
memcpy(passwd.password, data, passwd.passwordlen);
if (sa_passwd_update(sactx, username, usernamelen, &passwd) != SA_SUCCEED)
{
sa_log(sactx, "user_passwd(): sa_passwd_update() failed!");
return (SA_FAIL);
}
return (SA_SUCCEED);
}
/*
** USER_PROP
**
** Lookup and return a single user property.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Return Values:
** SA_SUCCESS | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
user_prop(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_PASSWD passwd;
SA_INT userlen;
SA_CHAR *user;
SA_INT proplen;
SA_CHAR *prop;
SA_CHAR buffer[256];
/* Get the page to direct to for user update */
if (sa_param(sactx, saparams, "username", &user, &userlen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_prop(): Expected parameter 'username'!");
return (SA_FAIL);
}
if ((userlen == 0) || (userlen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_prop(): 'username' field left NULL!");
return (SA_FAIL);
}
/* Get the property being looked up. */
if (sa_param(sactx, saparams, "prop", &prop, &proplen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_prop(): Expected parameter 'prop'!");
return (SA_FAIL);
}
if ((proplen == 0) || (proplen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "user_prop(): 'prop' field left NULL!");
return (SA_FAIL);
}
if (sa_passwd_lookup(sactx, user, userlen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "User lookup for '%s' failed.", user);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDDATA;
return (SA_FAIL);
}
/* Lookup and return the appropriate property */
if (strcmp(prop, "group") == 0)
{
if (sa_conn_send(saconn, passwd.group, passwd.grouplen) != SA_SUCCEED)
return (SA_FAIL);
}
else if (strcmp(prop, "password") == 0)
{
if (sa_conn_send(saconn, passwd.password, passwd.passwordlen)
!= SA_SUCCEED)
{
return (SA_FAIL);
}
}
else if (strcmp(prop, "name") == 0)
{
if (sa_conn_send(saconn, passwd.name, passwd.namelen) != SA_SUCCEED)
return (SA_FAIL);
}
else if (strcmp(prop, "dir") == 0)
{
if (sa_conn_send(saconn, passwd.dir, passwd.dirlen) != SA_SUCCEED)
return (SA_FAIL);
}
else if (strcmp(prop, "ftpprivs") == 0)
{
if (sa_param(sactx, saparams, "checked", &prop, &proplen) == SA_SUCCEED)
{
int tmp;
tmp = 0;
if (proplen > 0)
tmp = atoi(prop);
if (tmp == passwd.ftpprivs)
{
if (sa_conn_send(saconn, "CHECKED", SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
}
else
{
sprintf(buffer, "%ld", passwd.ftpprivs);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
}
else if (strcmp(prop, "ftpmax") == 0)
{
if (passwd.ftpmax < 5)
passwd.ftpmax = 0;
sprintf(buffer, "%ld", passwd.ftpmax);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
else if (strcmp(prop, "mbox") == 0)
{
if (sa_mbox_exists(sactx, user))
strcpy(buffer, "CHECKED");
else
strcpy(buffer, "");
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
else
{
sprintf(buffer, "User lookup for property '%s' failed.", prop);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDDATA;
return (SA_FAIL);
}
return (SA_SUCCEED);
}
/*
** FTP_CONNECT
**
** Process an FTP Connect request. Verify the username/password against
** the Sambar Server password interfaces and return the root directory
** and priviledges.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
** username Name of the user logging in.
** usernamelen Length of the user name
** password Password of the user logging in.
** passwordlen Length of the password.
** ftpresp FTP response structure.
**
** Return Values:
** SA_SUCCESS Login Accepted.
** SA_FAIL Login Denied.
*/
SA_RETCODE SA_PUBLIC
ftp_connect(sactx, name, namelen, password, passwordlen, ftpresp)
SA_CTX *sactx;
SA_CHAR *name;
SA_INT namelen;
SA_CHAR *password;
SA_INT passwordlen;
SA_FTP *ftpresp;
{
SA_PASSWD passwd;
SA_CHAR buffer[512];
if (sa_passwd_lookup(sactx, name, namelen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "FTP login for user '%s' failed.", name);
sa_log(sactx, buffer);
return (SA_FAIL);
}
/* Verify the passwords are the same */
if ((passwd.passwordlen != passwordlen) ||
(strncmp(passwd.password, password, passwordlen) != 0))
{
/* Special case for anonymous user - NULL password match OK */
if ((namelen != 9) || (strncmp(name, "anonymous", 9) != 0))
{
sprintf(buffer,
"FTP login for user '%s' failed - bad password (%s)",
name, password);
sa_log(sactx, buffer);
return (SA_FAIL);
}
}
/* Return the directory and access priviledges */
ftpresp->privs = passwd.ftpprivs;
ftpresp->ftpmax = passwd.ftpmax;
ftpresp->dirlen = passwd.dirlen;
if (ftpresp->dirlen > 0)
strncpy(ftpresp->dir, passwd.dir, ftpresp->dirlen);
ftpresp->dir[ftpresp->dirlen] = '\0';
#ifdef DEBUG
sprintf(buffer, "FTP User '%s' logged in.", name);
sa_log(sactx, buffer);
#endif /* DEBUG */
return (SA_SUCCEED);
}
/*
** MAIL_CONNECT
**
** Process an MAIL Connect request. Verify the username/password against
** the Sambar Server password interfaces.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
** username Name of the user logging in.
** usernamelen Length of the user name
** password Password of the user logging in.
** passwordlen Length of the password.
**
** Return Values:
** SA_SUCCESS Login Accepted.
** SA_FAIL Login Denied.
*/
SA_RETCODE SA_PUBLIC
mail_connect(sactx, name, namelen, password, passwordlen)
SA_CTX *sactx;
SA_CHAR *name;
SA_INT namelen;
SA_CHAR *password;
SA_INT passwordlen;
{
SA_PASSWD passwd;
SA_CHAR buffer[512];
if (sa_passwd_lookup(sactx, name, namelen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "MAIL login for user '%s' failed.", name);
sa_log(sactx, buffer);
return (SA_FAIL);
}
/* Verify the passwords are the same */
if ((passwd.passwordlen != passwordlen) ||
(strncmp(passwd.password, password, passwordlen) != 0))
{
/* Special case for anonymous user - NULL password match OK */
if ((namelen != 9) || (strncmp(name, "anonymous", 9) != 0))
{
sprintf(buffer,
"MAIL login for user '%s' failed - bad password (%s)",
name, password);
sa_log(sactx, buffer);
return (SA_FAIL);
}
}
#ifdef DEBUG
sprintf(buffer, "MAIL User '%s' logged in.", name);
sa_log(sactx, buffer);
#endif /* DEBUG */
return (SA_SUCCEED);
}
/*
** TELNET_CONNECT
**
** Process an Telnet Connect request. Verify the username/password against
** the Sambar Server password interfaces.
**
** Parameters:
** sactx Sambar Server Application context to release.
** saconn Sambar Server User Connection handle.
** username Name of the user logging in.
** usernamelen Length of the user name
** password Password of the user logging in.
** passwordlen Length of the password.
**
** Return Values:
** SA_SUCCESS Login Accepted.
** SA_FAIL Login Denied.
*/
SA_RETCODE SA_PUBLIC
telnet_connect(sactx, name, namelen, password, passwordlen)
SA_CTX *sactx;
SA_CHAR *name;
SA_INT namelen;
SA_CHAR *password;
SA_INT passwordlen;
{
SA_PASSWD passwd;
SA_CHAR buffer[512];
if (sa_passwd_lookup(sactx, name, namelen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "Telnet login for user '%s' failed.", name);
sa_log(sactx, buffer);
return (SA_FAIL);
}
/* Verify the passwords are the same */
if ((passwd.passwordlen != passwordlen) ||
(strncmp(passwd.password, password, passwordlen) != 0))
{
/* Special case for anonymous user - NULL password match OK */
if ((namelen != 9) || (strncmp(name, "anonymous", 9) != 0))
{
sprintf(buffer,
"Telnet login for user '%s' failed - bad password (%s)",
name, password);
sa_log(sactx, buffer);
return (SA_FAIL);
}
}
#ifdef DEBUG
sprintf(buffer, "Telnet User '%s' logged in.", name);
sa_log(sactx, buffer);
#endif /* DEBUG */
return (SA_SUCCEED);
}
/*
** GROUP_ADD
**
** Add a new group
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
*/
SA_RETCODE SA_PUBLIC
group_add(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_CHAR *data;
SA_INT datalen;
if (sa_param(sactx, saparams, "groupname", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_add(): Expected parameter 'groupname'!");
return (SA_FAIL);
}
if ((datalen == 0) || (datalen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_add(): 'name' field left NULL!");
return (SA_FAIL);
}
/* Can't create the group "other" */
if ((datalen == 5) &&
(memcmp(data, SA_DEFAULT_GROUP, strlen(SA_DEFAULT_GROUP)) == 0))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_add(): 'other' group already exists!");
return (SA_FAIL);
}
if (sa_group_add(sactx, data, datalen) != SA_SUCCEED)
{
*infop = SA_E_ALREADYDEFINED;
sa_log(sactx, "group_add(): sa_group_add() failed (already defined?)");
return (SA_FAIL);
}
return (SA_SUCCEED);
}
/*
** GROUP_DELETE
**
** Delete an existing group
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
**
** Notes:
** No users can belong to the group.
*/
SA_RETCODE SA_PUBLIC
group_delete(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT datalen;
SA_CHAR *data;
if (sa_param(sactx, saparams, "groupname", &data, &datalen) != SA_SUCCEED)
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_delete(): Expected parameter 'groupname'!");
return (SA_FAIL);
}
if ((datalen == 0) || (datalen > SA_MAX_NAME))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_delete(): 'groupname' field left NULL!");
return (SA_FAIL);
}
/* Can't delete the group "other" */
if ((datalen == 5) &&
(memcmp(data, SA_DEFAULT_GROUP, strlen(SA_DEFAULT_GROUP)) == 0))
{
*infop = SA_E_INVALIDDATA;
sa_log(sactx, "group_delete(): 'other' group cannot be deleted!");
return (SA_FAIL);
}
/* Should only be allowed to do this if the group has no users. */
(SA_VOID)sa_group_delete(sactx, data, datalen);
return (SA_SUCCEED);
}
/*
** GROUP_SELECT
**
** List all groups for selection.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
**
** Notes:
** This list is truncated at 100 names.
** The group other always exists and is used for users with unrecognized
** group names.
*/
SA_RETCODE SA_PUBLIC
group_select(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT i;
SA_BOOL found;
SA_BOOL selected;
SA_INT grouplen;
SA_INT userlen;
SA_INT numgroups;
SA_CHAR *user;
SA_PASSWD passwd;
SA_CHAR group[SA_MAX_NAME + 1];
SA_GROUP groups[100];
SA_CHAR buffer[256];
/* If a name is provided make it SELECTED in the OPTION list */
userlen = 0;
grouplen = 0;
(SA_VOID)sa_param(sactx, saparams, "username", &user, &userlen);
if (userlen > 0)
{
if (sa_passwd_lookup(sactx, user, userlen, &passwd) != SA_SUCCEED)
{
sprintf(buffer, "User lookup for '%s' failed.", user);
sa_log(sactx, buffer);
*infop = SA_E_INVALIDDATA;
return (SA_FAIL);
}
grouplen = passwd.grouplen;
if (grouplen > 0)
memcpy(group, passwd.group, grouplen);
}
if (sa_group_list(sactx, groups, 100, &numgroups) != SA_SUCCEED)
{
sa_log(sactx, "group_select(): failure retrieving groups list!");
return (SA_FAIL);
}
/* We could sort the names alphabetically at this point... */
/* Display the list of groups. */
found = 0;
for (i = 0; i < numgroups; i++)
{
selected = 0;
if ((grouplen > 0) &&
(groups[i].namelen == grouplen) &&
(memcmp(group, groups[i].name, grouplen) == 0))
{
selected = 1;
found = 1;
}
sprintf(buffer, "<OPTION %s>%s\n", (selected ? "SELECTED" : ""),
groups[i].name);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
/* The group "other" always exists. */
sprintf(buffer, "<OPTION %s>%s\n", (found ? "" : "SELECTED"),
SA_DEFAULT_GROUP);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
return (SA_SUCCEED);
}
/*
** GROUP_LIST
**
** List all groups for delete.
**
** Parameters:
** sactx Sambar Server context
** saconn Sambar Server connection
** saparams RPC Parameters
** infop Error parameters
**
** Returns:
** SA_SUCCEED | SA_FAIL
**
** Notes:
** This list is truncated at 100 names.
** The group "other" always exists and is the default group.
*/
SA_RETCODE SA_PUBLIC
group_list(sactx, saconn, saparams, infop)
SA_CTX *sactx;
SA_CONN *saconn;
SA_PARAMS *saparams;
SA_INT *infop;
{
SA_INT i;
SA_INT numgroups;
SA_GROUP groups[100];
SA_CHAR buffer[256];
if (sa_group_list(sactx, groups, 100, &numgroups) != SA_SUCCEED)
{
sa_log(sactx, "group_list(): failure retrieving groups list!");
return (SA_FAIL);
}
/* We could sort the list alphabetically at this point. */
/* Display the list of groups. */
for (i = 0; i < numgroups; i++)
{
sprintf(buffer,
"<A HREF=\"/session/delgroup?groupname=%s&RCpage=/sysadmin/usermgmt/grplist.stm\" onClick=\"return confirmDelete()\"><IMG border=0 HEIGHT=20 WIDTH=20 SRC=\"/sysimage/system/trash.gif\"></A> %s<BR>\n",
groups[i].name, groups[i].name);
if (sa_conn_send(saconn, buffer, SA_NULLTERM) != SA_SUCCEED)
return (SA_FAIL);
}
return (SA_SUCCEED);
}