2017-03-27 Fred Gleason <fredg@paravelsystems.com>

* Added a 'WEBAPI_AUTHS' table to the database.
	* Added a 'USERS.WEBAPI_AUTH_TIMEOUT' field to the database.
	* Incremented the database version to 260.
	* Added 'RDUser::webapiAuthTimeout()' and
	'RDUser::setWebapiAuthTimeout()' methods in 'lib/rduser.cpp' and
	'lib/rduser.h'.
	* Added a 'WebAPI Timeout' control to the Edit User dialog in
	'rdadmin/edit_user.cpp' and 'rdadmin/edit_user.h'.
	* Implemented a 'CreateTicket' Web API call.
This commit is contained in:
Fred Gleason 2017-03-27 13:43:42 -04:00
parent 60a9deb349
commit bc2c441680
61 changed files with 602 additions and 639 deletions

View File

@ -15654,3 +15654,13 @@
2017-03-24 Fred Gleason <fredg@paravelsystems.com>
* Updated 'NEWS'.
* Incremented the package version to 2.15.3.
2017-03-27 Fred Gleason <fredg@paravelsystems.com>
* Added a 'WEBAPI_AUTHS' table to the database.
* Added a 'USERS.WEBAPI_AUTH_TIMEOUT' field to the database.
* Incremented the database version to 260.
* Added 'RDUser::webapiAuthTimeout()' and
'RDUser::setWebapiAuthTimeout()' methods in 'lib/rduser.cpp' and
'lib/rduser.h'.
* Added a 'WebAPI Timeout' control to the Edit User dialog in
'rdadmin/edit_user.cpp' and 'rdadmin/edit_user.h'.
* Implemented a 'CreateTicket' Web API call.

View File

@ -142,6 +142,11 @@ else
fi
fi
#
# Check for OpenSSL
#
AC_CHECK_HEADER(openssl/sha.h,[],[AC_MSG_ERROR([*** OpenSSL not found ***])])
#
# Check for OggVorbis
#
@ -236,7 +241,7 @@ AM_CONDITIONAL([DOCBOOK_AM], [test "$USING_DOCBOOK" = yes])
#
# Set Hard Library Dependencies
#
AC_SUBST(LIB_RDLIBS,"-lm -lpthread -lqui -lrd -lcurl -lid3 $FLAC_LIBS -lsndfile -lsamplerate -lcdda_interface -lcdda_paranoia -lcrypt -ldl -lpam -lSoundTouch")
AC_SUBST(LIB_RDLIBS,"-lm -lpthread -lqui -lrd -lcurl -lid3 $FLAC_LIBS -lsndfile -lsamplerate -lcdda_interface -lcdda_paranoia -lcrypt -ldl -lpam -lSoundTouch -lcrypto")
#
# Setup MPEG Dependencies

View File

@ -87,6 +87,112 @@
</table>
</sect1>
<sect1>
<title>Authentication</title>
<para>
With the exception of the <command>CreateTicket</command> command, all
commands in this API must be authenticated by one of the following two
methods:
</para>
<sect2>
<title>
Username / Password
</title>
<para>
<table xml:id="ex.authentication.userpassword" frame="all">
<title>Username/Password Authentication Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</sect2>
<sect2>
<title>
Ticket
</title>
<para>
<table xml:id="ex.authentication.ticket" frame="all">
<title>Ticket Authentication Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
TICKET
</entry>
<entry>
40 character ticket code, obtained from a previous
<command>CreateTicket</command> call.
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</sect2>
</sect1>
<sect1>
<title>Result Reporting</title>
<para>
@ -184,28 +290,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
GROUP_NAME
@ -290,28 +374,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -368,28 +430,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
LOG_NAME
@ -462,28 +502,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -551,28 +569,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -640,6 +636,51 @@
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>CreateTicket</title>
<subtitle>Create an authorization ticket for a given user/client IP address</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_CREATETICKET</code>
</para>
<para>
Required User Permissions: none
</para>
<table xml:id="ex.createticket" frame="all">
<title>CreateTicket Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
COMMAND
</entry>
<entry>
31
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
@ -707,28 +748,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -796,28 +815,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
LOG_NAME
@ -892,28 +889,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -1179,28 +1154,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -1565,28 +1518,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -1759,28 +1690,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -1864,28 +1773,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -2035,28 +1922,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -2128,28 +1993,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
GROUP_NAME
@ -2245,28 +2088,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -2328,28 +2149,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -2422,28 +2221,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -2505,28 +2282,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
GROUP_NAME
@ -2588,28 +2343,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
@ -2655,28 +2388,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
NAME
@ -2733,28 +2444,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
SERVICE_NAME
@ -2833,28 +2522,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
@ -2900,28 +2567,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
TRACKABLE
@ -2980,28 +2625,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -3058,28 +2681,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -3148,28 +2749,6 @@
Mandatory.
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory. String.
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory. String.
</entry>
</row>
<row>
<entry>
LOG_NAME
@ -3693,28 +3272,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER
@ -3798,28 +3355,6 @@
Mandatory
</entry>
</row>
<row>
<entry>
LOGIN_NAME
</entry>
<entry>
Rivendell User Name
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
PASSWORD
</entry>
<entry>
Login Password
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
CART_NUMBER

View File

@ -76,6 +76,7 @@ EXTRA_DIST = audio_perms.txt\
users.txt\
version.txt\
vguest_resources.txt\
webapi_auths.txt\
web_connections.txt
CLEANFILES = *~

View File

@ -11,6 +11,7 @@ FULL_NAME char(255) Indexed
PHONE_NUMBER char(20)
DESCRIPTION char(255)
PASSWORD char(32) Not-NULL, Hashed
WEBAPI_AUTH_TIMEOUT int(11) signed Seconds
ENABLE_WEB enum('N','Y')
ADMIN_USERS_PRIV enum('N','Y') Retired
ADMIN_CONFIG_PRIV enum('N','Y')

View File

@ -0,0 +1,12 @@
WEBAPI_AUTH Table Layout for Rivendell
The WEB_CONNECTIONS table holds data concerning each authenticated Web API
user.
FIELD NAME TYPE REMARKS
--------------------------------------------------------------------------
TICKET char(41) Primary key
LOGIN_NAME char(255) From USERS.LOGIN_NAME
IP_ADDRESS char(16)
EXPIRATION_DATETIME datetime

View File

@ -24,7 +24,7 @@
/*
* Current Database Version
*/
#define RD_VERSION_DATABASE 259
#define RD_VERSION_DATABASE 260
#endif // DBVERSION_H

View File

@ -41,6 +41,13 @@ RDFormPost::RDFormPost(RDFormPost::Encoding encoding,unsigned maxsize,
post_error=RDFormPost::ErrorNotInitialized;
post_auto_delete=auto_delete;
//
// Client Info
//
if(getenv("REMOTE_ADDR")!=NULL) {
post_client_address.setAddress(getenv("REMOTE_ADDR"));
}
//
// Verify Transfer Type
//
@ -128,6 +135,12 @@ RDFormPost::Error RDFormPost::error() const
}
QHostAddress RDFormPost::clientAddress() const
{
return post_client_address;
}
QStringList RDFormPost::names() const
{
QStringList list;

View File

@ -38,6 +38,7 @@ class RDFormPost
bool auto_delete=true);
~RDFormPost();
RDFormPost::Error error() const;
QHostAddress clientAddress() const;
QStringList names() const;
QVariant value(const QString &name,bool *ok=NULL);
bool getValue(const QString &name,QHostAddress *addr,bool *ok=NULL);
@ -58,6 +59,7 @@ class RDFormPost
private:
void LoadUrlEncoding(char first);
void LoadMultipartEncoding(char first);
QHostAddress post_client_address;
RDFormPost::Encoding post_encoding;
RDFormPost::Error post_error;
std::map<QString,QVariant> post_values;

View File

@ -137,6 +137,19 @@ void RDUser::setPhone(const QString &phone) const
}
int RDUser::webapiAuthTimeout() const
{
return RDGetSqlValue("USERS","LOGIN_NAME",user_name,"WEBAPI_AUTH_TIMEOUT").
toInt();
}
void RDUser::setWebapiAuthTimeout(int sec) const
{
SetRow("WEBAPI_AUTH_TIMEOUT",sec);
}
bool RDUser::adminConfig() const
{
return RDBool(RDGetSqlValue("USERS","LOGIN_NAME",user_name,
@ -498,6 +511,20 @@ void RDUser::SetRow(const QString &param,const QString &value) const
}
void RDUser::SetRow(const QString &param,int value) const
{
RDSqlQuery *q;
QString sql;
sql=QString().sprintf("UPDATE USERS SET %s=%d WHERE LOGIN_NAME=\"%s\"",
(const char *)param,
value,
(const char *)user_name);
q=new RDSqlQuery(sql);
delete q;
}
void RDUser::SetRow(const QString &param,bool value) const
{
SetRow(param,RDYesNo(value));

View File

@ -41,6 +41,8 @@ class RDUser
void setDescription(const QString &desc) const;
QString phone() const;
void setPhone(const QString &phone) const;
int webapiAuthTimeout() const;
void setWebapiAuthTimeout(int sec) const;
bool adminConfig() const;
void setAdminConfig(bool priv) const;
bool createCarts() const;
@ -102,6 +104,7 @@ class RDUser
private:
void SetRow(const QString &param,const QString &value) const;
void SetRow(const QString &param,int value) const;
void SetRow(const QString &param,bool value) const;
QString user_name;
QString user_password;

View File

@ -51,6 +51,7 @@
#define RDXPORT_COMMAND_SAVELOG 28
#define RDXPORT_COMMAND_ADDLOG 29
#define RDXPORT_COMMAND_DELETELOG 30
#define RDXPORT_COMMAND_CREATETICKET 31
#endif // RDXPORT_INTERFACE_H

View File

@ -576,6 +576,7 @@ bool CreateDb(QString name,QString pwd)
PHONE_NUMBER CHAR(20),\
DESCRIPTION CHAR(255),\
PASSWORD CHAR(32),\
WEBAPI_AUTH_TIMEOUT int not null default 3600,\
ENABLE_WEB enum('N','Y') default 'N',\
ADMIN_USERS_PRIV ENUM('N','Y') NOT NULL DEFAULT 'N',\
ADMIN_CONFIG_PRIV ENUM('N','Y') NOT NULL DEFAULT 'N',\
@ -2412,6 +2413,19 @@ bool CreateDb(QString name,QString pwd)
return false;
}
//
// Create WEBAPI_AUTH table
//
sql=QString("create table if not exists WEBAPI_AUTHS(")+
"TICKET char(41) not null primary key,"+
"LOGIN_NAME char(255) not null,"+
"IPV4_ADDRESS char(16) not null,"+
"EXPIRATION_DATETIME datetime not null,"+
"index TICKET_IDX(TICKET,IPV4_ADDRESS,EXPIRATION_DATETIME))";
if(!RunQuery(sql)) {
return false;
}
return true;
}
@ -8313,6 +8327,25 @@ int UpdateDb(int ver)
}
delete q;
}
if(ver<260) {
sql=QString("create table if not exists WEBAPI_AUTHS(")+
"TICKET char(41) not null primary key,"+
"LOGIN_NAME char(255) not null,"+
"IPV4_ADDRESS char(16) not null,"+
"EXPIRATION_DATETIME datetime not null,"+
"index TICKET_IDX(TICKET,IPV4_ADDRESS,EXPIRATION_DATETIME))";
if(!RunQuery(sql)) {
return false;
}
sql=QString("alter table USERS add column ")+
"WEBAPI_AUTH_TIMEOUT int not null default 3600 after PASSWORD";
if(!RunQuery(sql)) {
return false;
}
}
//
// Update Version Field

View File

@ -2,7 +2,7 @@
//
// Edit a Rivendell User
//
// (C) Copyright 2002-2003,2016 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2003,2016-2017 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -71,11 +71,11 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// User Name
//
user_name_edit=new QLineEdit(this);
user_name_edit->setGeometry(100,11,sizeHint().width()-110,19);
user_name_edit->setGeometry(120,11,sizeHint().width()-130,19);
user_name_edit->setMaxLength(255);
user_name_edit->setValidator(validator);
QLabel *user_name_label=new QLabel(user_name_edit,tr("&User Name:"),this);
user_name_label->setGeometry(5,11,90,19);
user_name_label->setGeometry(5,11,110,19);
user_name_label->setFont(font);
user_name_label->setAlignment(AlignRight|AlignVCenter|ShowPrefix);
@ -83,12 +83,12 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Full Name
//
user_full_name_edit=new QLineEdit(this);
user_full_name_edit->setGeometry(100,32,sizeHint().width()-110,19);
user_full_name_edit->setGeometry(120,32,sizeHint().width()-130,19);
user_full_name_edit->setMaxLength(255);
user_full_name_edit->setValidator(validator);
QLabel *user_full_name_label=
new QLabel(user_full_name_edit,tr("&Full Name:"),this);
user_full_name_label->setGeometry(10,32,85,19);
user_full_name_label->setGeometry(10,32,105,19);
user_full_name_label->setFont(font);
user_full_name_label->setAlignment(AlignRight|AlignVCenter|ShowPrefix);
@ -96,12 +96,12 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// User Description
//
user_description_edit=new QLineEdit(this);
user_description_edit->setGeometry(100,53,sizeHint().width()-110,19);
user_description_edit->setGeometry(120,53,sizeHint().width()-130,19);
user_description_edit->setMaxLength(255);
user_description_edit->setValidator(validator);
QLabel *user_description_label=
new QLabel(user_description_edit,tr("&Description:"),this);
user_description_label->setGeometry(5,53,90,19);
user_description_label->setGeometry(5,53,110,19);
user_description_label->setFont(font);
user_description_label->setAlignment(AlignRight|AlignVCenter|ShowPrefix);
@ -109,21 +109,34 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// User Phone
//
user_phone_edit=new QLineEdit(this);
user_phone_edit->setGeometry(100,75,120,19);
user_phone_edit->setGeometry(120,75,120,19);
user_phone_edit->setMaxLength(20);
user_phone_edit->setValidator(validator);
QLabel *user_phone_label=new QLabel(user_phone_edit,tr("&Phone:"),this);
user_phone_label->setGeometry(10,75,85,19);
user_phone_label->setGeometry(10,75,105,19);
user_phone_label->setFont(font);
user_phone_label->setAlignment(AlignRight|AlignVCenter|ShowPrefix);
//
// WebAPI Authorization Timeout
//
user_webapi_auth_spin=new QSpinBox(this);
user_webapi_auth_spin->setGeometry(120,97,80,19);
user_webapi_auth_spin->setRange(0,86400);
user_webapi_auth_spin->setSpecialValueText(tr("Disabled"));
QLabel *user_webapi_auth_label=
new QLabel(user_webapi_auth_spin,tr("WebAPI Timeout:"),this);
user_webapi_auth_label->setGeometry(10,97,105,19);
user_webapi_auth_label->setFont(font);
user_webapi_auth_label->setAlignment(AlignRight|AlignVCenter|ShowPrefix);
//
// Enable Web Login
//
user_web_box=new QCheckBox(this);
user_web_box->setGeometry(20,96,15,15);
user_web_box->setGeometry(20,118,15,15);
user_web_label=new QLabel(user_web_box,tr("Allow Web Logins"),this);
user_web_label->setGeometry(40,96,180,19);
user_web_label->setGeometry(40,118,180,19);
user_web_label->setFont(font);
user_web_label->setAlignment(AlignLeft|AlignVCenter|ShowPrefix);
@ -131,7 +144,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Change Password Button
//
QPushButton *password_button=new QPushButton(this);
password_button->setGeometry(230,75,80,50);
password_button->setGeometry(sizeHint().width()-90,75,80,50);
password_button->setFont(font);
password_button->setText(tr("Change\n&Password"));
connect(password_button,SIGNAL(clicked()),this,SLOT(passwordData()));
@ -140,7 +153,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Administrative Group Priviledges
//
user_admin_group=new QButtonGroup(tr("Administrative Rights"),this);
user_admin_group->setGeometry(10,125,355,45);
user_admin_group->setGeometry(10,147,355,45);
user_admin_group->setFont(font);
user_admin_config_button=new QCheckBox(user_admin_group);
@ -159,7 +172,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Production Group Priviledges
//
user_prod_group=new QButtonGroup(tr("Production Rights"),this);
user_prod_group->setGeometry(10,180,355,85);
user_prod_group->setGeometry(10,202,355,85);
user_prod_group->setFont(font);
user_create_carts_button=new QCheckBox(user_prod_group);
@ -216,7 +229,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Traffic Group Priviledges
//
user_traffic_group=new QButtonGroup(tr("Traffic Rights"),this);
user_traffic_group->setGeometry(10,275,355,66);
user_traffic_group->setGeometry(10,297,355,66);
user_traffic_group->setFont(font);
user_create_log_button=new QCheckBox(user_traffic_group);
@ -257,7 +270,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// OnAir Group Priviledges
//
user_onair_group=new QButtonGroup(tr("OnAir Rights"),this);
user_onair_group->setGeometry(10,351,355,85);
user_onair_group->setGeometry(10,373,355,85);
user_onair_group->setFont(font);
user_playout_log_button=new QCheckBox(user_onair_group);
@ -307,7 +320,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Podcast Group Priviledges
//
user_podcast_group=new QButtonGroup(tr("Podcasting Rights"),this);
user_podcast_group->setGeometry(10,446,355,66);
user_podcast_group->setGeometry(10,468,355,66);
user_podcast_group->setFont(font);
user_add_podcast_button=new QCheckBox(user_podcast_group);
@ -340,7 +353,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
// Group Permissions Button
//
user_assign_perms_button=new QPushButton(this);
user_assign_perms_button->setGeometry(10,516,sizeHint().width()/2-20,50);
user_assign_perms_button->setGeometry(10,538,sizeHint().width()/2-20,50);
user_assign_perms_button->setFont(font);
user_assign_perms_button->setText(tr("Assign Group\nPermissions"));
connect(user_assign_perms_button,SIGNAL(clicked()),this,SLOT(groupsData()));
@ -350,7 +363,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
//
user_assign_feeds_button=new QPushButton(this);
user_assign_feeds_button->
setGeometry(sizeHint().width()/2+10,516,sizeHint().width()/2-20,50);
setGeometry(sizeHint().width()/2+10,538,sizeHint().width()/2-20,50);
user_assign_feeds_button->setFont(font);
user_assign_feeds_button->setText(tr("Assign Podcast Feed\nPermissions"));
connect(user_assign_feeds_button,SIGNAL(clicked()),this,SLOT(feedsData()));
@ -383,6 +396,7 @@ EditUser::EditUser(const QString &user,QWidget *parent)
user_full_name_edit->setText(user_user->fullName());
user_description_edit->setText(user_user->description());
user_phone_edit->setText(user_user->phone());
user_webapi_auth_spin->setValue(user_user->webapiAuthTimeout());
user_web_box->setChecked(user_user->enableWeb());
if(user_user->adminConfig()) {
user_admin_config_button->setChecked(true);
@ -437,7 +451,7 @@ EditUser::~EditUser()
QSize EditUser::sizeHint() const
{
return QSize(375,636);
return QSize(375,658);
}
@ -525,6 +539,7 @@ void EditUser::okData()
user_user->setFullName(user_full_name_edit->text());
user_user->setDescription(user_description_edit->text());
user_user->setPhone(user_phone_edit->text());
user_user->setWebapiAuthTimeout(user_webapi_auth_spin->value());
user_user->setEnableWeb(user_web_box->isChecked());
user_user->setAdminConfig(user_admin_config_button->isChecked());
user_user->setCreateCarts(user_create_carts_button->isChecked());

View File

@ -30,6 +30,7 @@
#include <qlineedit.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <qspinbox.h>
#include <rduser.h>
@ -55,6 +56,7 @@ class EditUser : public QDialog
QLineEdit *user_full_name_edit;
QLineEdit *user_description_edit;
QLineEdit *user_phone_edit;
QSpinBox *user_webapi_auth_spin;
QCheckBox *user_web_box;
QLabel *user_web_label;
QButtonGroup *user_admin_group;

View File

@ -4127,6 +4127,14 @@ pro přívod pro podcast</translation>
<source>&amp;Cancel</source>
<translation>Z&amp;rušit</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -4084,6 +4084,14 @@ zuweisen</translation>
<source>&amp;Cancel</source>
<translation>Abbre&amp;chen</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -4072,6 +4072,14 @@ Feeds para Podcasts</translation>
<source>Allow Web Logins</source>
<translation>Permitir ingreso vía Web</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -3702,6 +3702,14 @@ Permissions</source>
<source>&amp;Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -4074,6 +4074,14 @@ tilgangsrettar</translation>
<source>&amp;Cancel</source>
<translation>&amp;Avbryt</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -4074,6 +4074,14 @@ tilgangsrettar</translation>
<source>&amp;Cancel</source>
<translation>&amp;Avbryt</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -4077,6 +4077,14 @@ Feeds de Podcasts </translation>
<source>&amp;Cancel</source>
<translation>&amp;Cancelar</translation>
</message>
<message>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WebAPI Timeout:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditUserPerms</name>

View File

@ -131,6 +131,7 @@ void MainObject::RunSystemMaintenance()
PurgeLogs();
PurgeElr();
PurgeGpioEvents();
PurgeWebapiAuths();
sql="update VERSION set LAST_MAINT_DATETIME=now()";
q=new RDSqlQuery(sql);
delete q;
@ -280,6 +281,17 @@ void MainObject::PurgeGpioEvents()
delete q;
}
void MainObject::PurgeWebapiAuths()
{
QString sql;
RDSqlQuery *q;
sql=QString("delete from WEBAPI_AUTHS where EXPIRATION_DATETIME<now()");
q=new RDSqlQuery(sql);
delete q;
}
int main(int argc,char *argv[])
{

View File

@ -44,6 +44,7 @@ class MainObject : public QObject
void PurgeElr();
void PurgeDropboxes();
void PurgeGpioEvents();
void PurgeWebapiAuths();
RDConfig *maint_config;
bool maint_verbose;
bool maint_system;

View File

@ -194,6 +194,10 @@ void MainObject::Revert(int schema) const
case 259:
Revert259();
break;
case 260:
Revert260();
break;
}
}
@ -529,6 +533,23 @@ void MainObject::Revert259() const
}
void MainObject::Revert260() const
{
QString sql;
QSqlQuery *q;
sql=QString("alter table USERS drop column WEBAPI_AUTH_TIMEOUT");
q=new QSqlQuery(sql);
delete q;
sql=QString("drop table WEBAPI_AUTHS");
q=new QSqlQuery(sql);
delete q;
SetVersion(259);
}
int MainObject::GetVersion() const
{
QString sql;
@ -571,6 +592,7 @@ int MainObject::MapSchema(const QString &ver)
version_map["2.13"]=255;
version_map["2.14"]=258;
version_map["2.15"]=259;
version_map["2.16"]=260;
//
// Normalize String

View File

@ -55,6 +55,7 @@ class MainObject : public QObject
void Revert257() const;
void Revert258() const;
void Revert259() const;
void Revert260() const;
int GetVersion() const;
void SetVersion(int schema) const;
int MapSchema(const QString &ver);

View File

@ -24,6 +24,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <map>
@ -32,6 +33,7 @@
#include <qstringlist.h>
#include <rddb.h>
#include <rdescape_string.h>
#include <rdweb.h>
#include <rdformpost.h>
#include <rdxport_interface.h>
@ -278,6 +280,26 @@ bool Xport::Authenticate()
{
QString name;
QString passwd;
QString ticket;
int command;
QString sql;
RDSqlQuery *q;
unsigned char rawstr[1024];
unsigned char sha1[SHA_DIGEST_LENGTH];
if(xport_post->getValue("TICKET",&ticket)) {
sql=QString("select LOGIN_NAME from WEBAPI_AUTHS where ")+
"(TICKET=\""+RDEscapeString(ticket)+"\")&&"+
"(IPV4_ADDRESS=\""+xport_post->clientAddress().toString()+"\")&&"+
"(EXPIRATION_DATETIME>now())";
q=new RDSqlQuery(sql);
if(q->first()) {
xport_user=new RDUser(q->value(0).toString());
delete q;
return true;
}
delete q;
}
if(!xport_post->getValue("LOGIN_NAME",&name)) {
return false;
@ -286,8 +308,43 @@ bool Xport::Authenticate()
return false;
}
xport_user=new RDUser(name);
if(!xport_user->checkPassword(passwd,false)) {
return false;
}
return xport_user->checkPassword(passwd,false);
if(xport_post->getValue("COMMAND",&command)) {
if(command==RDXPORT_COMMAND_CREATETICKET) {
QDateTime now=QDateTime::currentDateTime();
snprintf((char *)rawstr,1024,"%s %s %s",
(const char *)now.toString("yyyy-MM-dd hh:mm:ss.zzz"),
(const char *)name,
(const char *)xport_post->clientAddress().toString());
SHA1(rawstr,strlen((char *)rawstr),sha1);
ticket="";
for(int i=0;i<SHA_DIGEST_LENGTH;i++) {
ticket+=QString().sprintf("%02x",0xFF&rawstr[i]);
}
sql=QString("insert into WEBAPI_AUTHS set ")+
"TICKET=\""+RDEscapeString(ticket)+"\","+
"LOGIN_NAME=\""+RDEscapeString(name)+"\","+
"IPV4_ADDRESS=\""+xport_post->clientAddress().toString()+"\","+
"EXPIRATION_DATETIME=\""+
now.addSecs(xport_user->webapiAuthTimeout()).
toString("yyyy-MM-dd hh:mm:ss")+"\"";
q=new RDSqlQuery(sql);
delete q;
printf("Content-type: application/xml\n\n");
printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
printf("<ticketInfo>\n");
printf(" %s\n",(const char *)RDXmlField("ticket",ticket));
printf(" %s\n",(const char *)
RDXmlField("expires",now.addSecs(xport_user->webapiAuthTimeout())));
printf("</ticketInfo>\n");
exit(0);
}
}
return true;
}

View File

@ -28,6 +28,7 @@ install-exec-am:
cp audioinfo.html $(DESTDIR)@libexecdir@
cp audiostore.html $(DESTDIR)@libexecdir@
cp copyaudio.html $(DESTDIR)@libexecdir@
cp createticket.html $(DESTDIR)@libexecdir@
cp delete_audio.html $(DESTDIR)@libexecdir@
cp deletelog.html $(DESTDIR)@libexecdir@
cp editcart.html $(DESTDIR)@libexecdir@
@ -64,6 +65,7 @@ uninstall-local:
rm -f $(DESTDIR)@libexecdir@/audioinfo.html
rm -f $(DESTDIR)@libexecdir@/audiostore.html
rm -f $(DESTDIR)@libexecdir@/copyaudio.html
rm -f $(DESTDIR)@libexecdir@/createticket.html
rm -f $(DESTDIR)@libexecdir@/delete_audio.html
rm -f $(DESTDIR)@libexecdir@/deletelog.html
rm -f $(DESTDIR)@libexecdir@/editcart.html
@ -98,6 +100,7 @@ EXTRA_DIST = addcart.html\
audioinfo.html\
audiostore.html\
copyaudio.html\
createticket.html\
delete_audio.html\
deletelog.html\
editcart.html\

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">GROUP NAME:</td>
<td><input type="text" name="GROUP_NAME" size="12" maxlength="10"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">LOG NAME:</td>
<td><input type="text" name="LOG_NAME" size="20" maxlength="64"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">SOURCE_CART NUMBER:</td>
<td><input type="text" name="SOURCE_CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -0,0 +1,25 @@
<html>
<head>
<title>Rivendell CREATETICKET Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="31">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">LOG NAME:</td>
<td><input type="text" name="LOG_NAME" size="20" maxlength="64"></td>
</tr>

View File

@ -21,6 +21,12 @@
<td>&nbsp;</td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" id="TICKET" size="40" maxlength="40"></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" id="CART_NUMBER" size="64" maxlength="255"></td>
<td>&nbsp;</td>

View File

@ -32,6 +32,7 @@ function EditCart_MakePost()
var form='COMMAND=14';
form+='&LOGIN_NAME='+document.getElementById("LOGIN_NAME").value;
form+='&PASSWORD='+document.getElementById("PASSWORD").value;
form+='&TICKET='+document.getElementById("TICKET").value;
form+='&CART_NUMBER='+document.getElementById("CART_NUMBER").value;
if(document.getElementById("INCLUDE_CUTS").value.length==0) {
form+="&INCLUDE_CUTS=0";

View File

@ -21,6 +21,12 @@
<td>&nbsp;</td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" id="TICKET" size="40" maxlength="40"></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" id="CART_NUMBER" size="8" maxlength="6"></td>
<td>&nbsp;</td>

View File

@ -32,6 +32,7 @@ function EditCut_MakePost()
var form='COMMAND=15';
form+='&LOGIN_NAME='+document.getElementById("LOGIN_NAME").value;
form+='&PASSWORD='+document.getElementById("PASSWORD").value;
form+='&TICKET='+document.getElementById("TICKET").value;
form+='&CART_NUMBER='+document.getElementById("CART_NUMBER").value;
form+='&CUT_NUMBER='+document.getElementById("CUT_NUMBER").value;

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="255"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">GROUP NAME:</td>
<td><input type="text" name="GROUP_NAME" size="20" maxlength="255"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">GROUP NAME:</td>
<td><input type="text" name="GROUP_NAME" size="20" maxlength="255"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">NAME:</td>
<td><input type="text" name="NAME" size="20" maxlength="64"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">SERVICE NAME:</td>
<td><input type="text" name="SERVICE_NAME" size="20" maxlength="10"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">TRACKABLE:</td>
<td><input type="text" name="TRACKABLE" size="2" maxlength="1"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">LOG_NAME:</td>
<td><input type="text" name="LOG_NAME" size="20" maxlength="64"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="20" maxlength="6"></td>
</tr>

View File

@ -14,6 +14,10 @@
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">CART NUMBER:</td>
<td><input type="text" name="CART_NUMBER" size="8" maxlength="6"></td>
</tr>