Bazaar/Protect : more features and bugfixes

git-svn-id: svn://ultimatepp.org/upp/trunk@2777 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
micio 2010-10-13 12:15:14 +00:00
parent b7133c97bc
commit ae32efdfbb
8 changed files with 264 additions and 44 deletions

View file

@ -2,9 +2,9 @@ description "Software copy protection module\377";
uses
Core,
ScgiServer,
Cypher,
MySql;
MySql,
Web;
file
ProtectStatus.h,

View file

@ -91,13 +91,14 @@ bool ProtectClient::Connect(void)
{
lastError = 0;
// if already connected, disconnect first
// disconnect first, im case we're already connected
Disconnect();
// send a connect packet to server
VectorMap<String, Value>v;
v.Add("REASON", PROTECT_CONNECT);
v.Add("EMAIL", userEMail);
v.Add("CLIENTID", (int)clientID);
VectorMap<String, Value> res = SendMap(v);
// check for errors
@ -204,9 +205,8 @@ bool ProtectClient::GetLicenseInfo(void)
lastError = res.Get("ERROR");
return false;
}
if(res.Find("EMAIL") >= 0) userEMail = res.Get("EMAIL");
if(res.Find("USERNAME") >= 0) userName = res.Get("NAME");
if(res.Find("NAME") >= 0) userName = res.Get("NAME");
if(res.Find("ADDRESS") >= 0) userAddress = res.Get("ADDRESS");
if(res.Find("COUNTRY") >= 0) userCountry = res.Get("COUNTRY");
if(res.Find("ZIP") >= 0) userZIP = res.Get("ZIP");
@ -219,6 +219,34 @@ bool ProtectClient::GetLicenseInfo(void)
return true;
}
// updates user data on server
bool ProtectClient::UpdateUserData(void)
{
lastError = 0;
// sends a register packet to server
VectorMap<String, Value>v;
v.Add("REASON", PROTECT_UPDATEUSERDATA);
v.Add("CLIENTID", (int)clientID);
v.Add("EMAIL", userEMail);
v.Add("NAME", userName);
v.Add("ADDRESS", userAddress);
v.Add("COUNTRY", userCountry);
v.Add("ZIP", userZIP);
v.Add("PHONE", userPhone);
v.Add("FAX", userFax);
v.Add("CELL", userCell);
VectorMap<String, Value> res = SendMap(v);
// check for errors
if(res.Find("ERROR") >= 0)
{
lastError = res.Get("ERROR");
return false;
}
return true;
}
// register app
bool ProtectClient::Register(void)
{

View file

@ -87,6 +87,9 @@ class ProtectClient
// gets license info
bool GetLicenseInfo(void);
// updates user data on server
bool UpdateUserData(void);
// register app
bool Register(void);
@ -100,6 +103,15 @@ class ProtectClient
ProtectClient &SetUserPhone(String const &phone) { userPhone = phone; return *this; }
ProtectClient &SetUserFax(String const &fax) { userFax = fax; return *this; }
ProtectClient &SetUserCell(String const &cell) { userCell = cell; return *this; }
String GetUserEMail(void) { return userEMail; }
String GetUserName(void) { return userName; }
String GetUserAddress(void) { return userAddress; }
String GetUserCountry(void) { return userCountry; }
String GetUserZip(void) { return userZIP; }
String GetUserPhone(void) { return userPhone; }
String GetUserFax(void) { return userFax; }
String GetUserCell(void) { return userCell; }
};
END_UPP_NAMESPACE

View file

@ -109,7 +109,7 @@ dword ProtectServer::ConnectClient(String const &eMail)
{
id = Random();
}
while(clientLinks.Find(id) >= 0);
while(id == 0 || id == -1 || clientLinks.Find(id) >= 0);
// adds to clientLinks and to registered emails count
clientLinks.Add(id, TimeMail(GetSysTime() + clientTimeout, eMail));
@ -151,34 +151,50 @@ void ProtectServer::OnRequest()
int err = 0;
VectorMap<String, Value> data;
VectorMap<String, Value> results;
String IV;
clientSock.Write("Content-Type: text/plain\r\n\r\n");
// ignore requests without post data
if(!HasPostData())
err = PROTECT_BAD_REQUEST;
// post data should contain at least an IV field and a DATA field
else if(post.Find("IV") < 0 || post.Find("DATA") < 0)
err = PROTECT_MISSING_IV;
else
// POST rwquest ?
if(HasPostData())
{
// gets IV and setup the cypher to decrypt DATA field
String IV = ScanHexString(post.GetString("IV"));
cypher->SetKey(key, IV);
// decrypts DATA field and build its vectormap
String decoded;
try
// all requests besides PROTECT_ACTIVATE go through post
if(post.Find("IV") < 0)
err = PROTECT_MISSING_IV;
else
{
decoded = (*cypher)(ScanHexString(post.GetString("DATA")));
LoadFromXML(data, decoded);
}
catch(...)
{
err = PROTECT_BAD_DATA;
IV = ScanHexString(post.GetString("IV"));
if(post.Find("DATA") < 0)
err = PROTECT_MISSING_DATA;
else
{
cypher->SetKey(key, IV);
String decoded = (*cypher)(ScanHexString(post.GetString("DATA")));
LoadFromXML(data, decoded);
}
}
}
// GET rwquest ?
else if(query.GetCount())
{
// here should come just PROTECT_ACTIVATE requests
if(query.Find("IV") < 0)
err = PROTECT_MISSING_IV;
else
{
IV = ScanHexString(query.GetString("IV"));
if(query.Find("DATA") < 0)
err = PROTECT_MISSING_DATA;
else
{
cypher->SetKey(key, IV);
String decoded = (*cypher)(ScanHexString(query.GetString("DATA")));
LoadFromXML(data, decoded);
}
}
}
else
err = PROTECT_BAD_REQUEST;
// Get request reason and process it
// supports following reasons :
// CONNECT establish connection to server
@ -201,11 +217,28 @@ void ProtectServer::OnRequest()
else if(results.Find("ERROR") >= 0)
results.Add("ERRORMSG", ProtectMessage(results.Get("ERROR")));
// encodes results and send back to client
cypher->SetKey(key);
clientSock.Write("IV=" + HexString(cypher->GetNonce()) + "\r\n");
String encoded = HexString((*cypher)(StoreAsXML(results, "ProtectServer")));
clientSock.Write("DATA=" + encoded + "\r\n");
// if not ACTIVATION REQUEST encodes results and send back to client
if(data.Get("REASON") != PROTECT_ACTIVATE)
{
clientSock.Write("Content-Type: text/plain\r\n\r\n");
cypher->SetKey(key);
clientSock.Write("IV=" + HexString(cypher->GetNonce()) + "\r\n");
String encoded = HexString((*cypher)(StoreAsXML(results, "ProtectServer")));
clientSock.Write("DATA=" + encoded + "\r\n");
}
// otherwise send a fancy welcome html -- replacing %NAME% with user name
else
{
String w;
clientSock.Write("Content-Type: text/html\r\n\r\n");
if(results.Find("ERROR") >= 0)
w = welcome;
else
w = activationFailed;
data = db.Get(data.Get("EMAIL"));
w.Replace("%USER%", data.Get("NAME"));
clientSock.Write(w);
}
}
void ProtectServer::OnClosed()
@ -222,6 +255,7 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
dword clientID;
VectorMap<String, Value> userRec;
String eMail;
String activationKey;
int numConnections;
switch(reason)
@ -258,6 +292,7 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
{
// new registration
db.Set(vs);
userRec = db.Get(eMail);
if(SendActivationMail(userRec))
res.Add("STATUS", "ACTIVATION RESENT");
else
@ -286,6 +321,39 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
}
}
break;
case PROTECT_ACTIVATE:
// we need email to activate
if(v.Find("EMAIL") < 0)
{
res.Add("ERROR", PROTECT_MISSING_EMAIL);
return res;
}
eMail = v.Get("EMAIL");
// we need ACTIVATIONKEY too
if(v.Find("ACTIVATIONKEY") < 0)
{
res.Add("ERROR", PROTECT_MISSING_ACTIVATIONKEY);
return res;
}
activationKey = v.Get("ACTIVATIONKEY");
// get user record by email
userRec = db.Get(eMail);
// if no mail data, product is unregistered
if(!userRec.GetCount())
{
res.Add("ERROR", PROTECT_UNREGISTERED);
return res;
}
// sets registered flag and update record
userRec.Get("ACTIVATED") = true;
db.Set(userRec);
res.Add("STATUS", "ACTIVATION SUCCESSFUL");
break;
case PROTECT_CONNECT:
// we need email to connect
if(v.Find("EMAIL") < 0)
@ -349,6 +417,21 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
DisconnectClient(clientID);
break;
case PROTECT_REFRESH :
// we need the conneciton ID
if(v.Find("CLIENTID") < 0)
{
res.Add("ERROR", PROTECT_MISSING_CLIENTID);
return res;
}
clientID = (int)v.Get("CLIENTID");
if(!IsClientConnected(clientID))
{
res.Add("ERROR", PROTECT_NOT_CONNECTED);
return res;
}
break;
case PROTECT_GETKEY:
// we need the conneciton ID
if(v.Find("CLIENTID") < 0)
@ -367,7 +450,73 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
res.Add("KEY", appKey);
break;
case PROTECT_GETLICENSEINFO :
// we need the conneciton ID
if(v.Find("CLIENTID") < 0)
{
res.Add("ERROR", PROTECT_MISSING_CLIENTID);
return res;
}
// we shall be connected
clientID = (int)v.Get("CLIENTID");
if(!IsClientConnected(clientID))
{
res.Add("ERROR", PROTECT_NOT_CONNECTED);
return res;
}
// get user data from database
res = db.Get(clientLinks.Get(clientID).eMail);
// remove unnecessary data
res.RemoveKey("ACTIVATIONKEY");
res.RemoveKey("ACTIVATIONSENT");
break;
case PROTECT_UPDATEUSERDATA :
{
// we need the conneciton ID
if(v.Find("CLIENTID") < 0)
{
res.Add("ERROR", PROTECT_MISSING_CLIENTID);
return res;
}
// we shall be connected
clientID = (int)v.Get("CLIENTID");
if(!IsClientConnected(clientID))
{
res.Add("ERROR", PROTECT_NOT_CONNECTED);
return res;
}
// get user data from database
userRec = db.Get(clientLinks.Get(clientID).eMail);
// avoid hacking of source packet stripping
// data non-updateable by user
VectorMap<String, Value>vs(v, 1);
vs.RemoveKey("LICENSES");
vs.RemoveKey("EXPIRATION");
vs.RemoveKey("ACTIVATIONKEY");
vs.RemoveKey("ACTIVATIONSENT");
vs.RemoveKey("ACTIVATED");
vs.RemoveKey("EMAIL");
// update user record
for(int i = 0; i < vs.GetCount(); i++)
{
int j = userRec.Find(vs.GetKey(i));
if(j >= 0)
userRec[j] = vs[i];
}
db.Set(userRec);
}
break;
default:
// disconnect me anyways
if(v.Find("CLIENTID") >= 0)
{
clientID = (int)v.Get("CLIENTID");
DisconnectClient(clientID);
}
res.Add("ERROR", PROTECT_UNKNOWN_REASON);
break;
}
@ -377,9 +526,25 @@ VectorMap<String, Value> ProtectServer::ProcessRequest(int reason, VectorMap<Str
// sends activation mail to user
bool ProtectServer::SendActivationMail(VectorMap<String, Value> const &userData)
{
// build request map
VectorMap<String, Value>req;
req.Add("REASON", PROTECT_ACTIVATE);
req.Add("EMAIL", userData.Get("EMAIL"));
req.Add("ACTIVATIONKEY", userData.Get("ACTIVATIONKEY"));
// builds activation link
String link = "http://" + serverVars.Get("SERVER_NAME") + serverVars.Get("REQUEST_URI") + "?";
// encodes results and send back to client
cypher->SetKey(key);
link += "IV=" + HexString(cypher->GetNonce()) + "&";
String encoded = HexString((*cypher)(StoreAsXML(req, "ProtectServer")));
link += "DATA=" + encoded;
smtp.To(userData.Get("EMAIL"));
smtp.Subject("APPLICATION ACTIVATION");
smtp.Text("http://www.timberstruct.it?DATA=" + (String)userData.Get("ACTIVATIONKEY"));
smtp.From(serverVars.Get("SERVER_NAME"));
smtp.Text(link);
return smtp.Send();
}

View file

@ -2,7 +2,7 @@
#define _ProtectServer_h_
#include <Core/Core.h>
#include <ScgiServer/ScgiServer.h>
#include <Web/Web.h>
#include <Cypher/Cypher.h>
#include "ProtectStatus.h"
@ -42,6 +42,10 @@ class ProtectServer : public ScgiServer
// key used to en/decrypt http data
String key;
// welcome and activation failed messages sent on key activation
String welcome;
String activationFailed;
void OnAccepted();
void OnRequest();
@ -83,6 +87,10 @@ public:
// sets encryption key
ProtectServer &SetKey(String const &_key) { key = _key; return *this; }
// sets welcome and activation failed (HTML) messages
ProtectServer &SetWelcome(String const &w) { welcome = w; return *this; }
ProtectServer &SetActivationFailed(String const &a) { activationFailed = a; return *this; }
// gets database
ProtectDB &GetDB(void) { return db; }

View file

@ -9,11 +9,13 @@ const char *__ProtectMessages[] =
tt_("HTTP SERVER ERROR"),
tt_("BAD REQUEST"),
tt_("MISSING INITIALIZATION VECTOR ON DATA"),
tt_("MISSING DATA FIELD"),
tt_("MISSING EMAIL ON DATA"),
tt_("ILL-FORMED EMAIL ADDRESS"),
tt_("MISSING CONNECTION REASON ON DATA"),
tt_("UNKNOWN CONNECTION REASON"),
tt_("MISSING CLIENT ID ON DATA"),
tt_("MISSING ACTIVATION KEY ON DATA"),
tt_("BAD REQUEST DATA"),
tt_("NOT CONNECTED TO SERVER"),
tt_("SERVER CONNECTION EXPIRED"),
@ -43,6 +45,7 @@ const char *__ProtectReasons[] =
"REFRESH", // refreshes server connection (to restart timeout)
"GETKEY", // gets application key
"REGISTER", // registers app for timed demo
"ACTIVATE", // activate registration by click on email link
"GETLICENSEINFO" // gets info about license (name, expiration date, app version....)
};

View file

@ -11,11 +11,13 @@ typedef enum {
PROTECT_HTTP_ERROR, // error on HTTP communication with server
PROTECT_BAD_REQUEST, // missing POST data on request
PROTECT_MISSING_IV, // missing Initialization Vector on POST data
PROTECT_MISSING_DATA, // missing DATA field on POST data
PROTECT_MISSING_EMAIL, // missing mandatory EMAIL field in POST DATA
PROTECT_INVALID_EMAIL, // ill-formed email address
PROTECT_MISSING_REASON, // missing connection's reason
PROTECT_UNKNOWN_REASON, // unknown connection's reason
PROTECT_MISSING_CLIENTID, // missing client connection ID in POST DATA
PROTECT_MISSING_ACTIVATIONKEY, // missing activation key on activation request
PROTECT_BAD_DATA, // missing mandatory fields in POST DATA
PROTECT_NOT_CONNECTED, // not connected to server -- must connect first
PROTECT_CONNECTION_EXPIRED, // server connection timeout -- should refresh more often
@ -33,13 +35,15 @@ extern String ProtectMessage(int m);
// server request reasons
typedef enum {
PROTECT_BAD_REASON, // internal error
PROTECT_CONNECT, // establish connection to server
PROTECT_DISCONNECT, // frees server connection
PROTECT_REFRESH, // refreshes server connection (to restart timeout)
PROTECT_GETKEY, // gets application key
PROTECT_REGISTER, // registers app for timed demo
PROTECT_GETLICENSEINFO // gets info about license (name, expiration date, app version....)
PROTECT_BAD_REASON, // internal error
PROTECT_CONNECT, // establish connection to server
PROTECT_DISCONNECT, // frees server connection
PROTECT_REFRESH, // refreshes server connection (to restart timeout)
PROTECT_GETKEY, // gets application key
PROTECT_REGISTER, // registers app for timed demo
PROTECT_ACTIVATE, // activate registration by click on email link
PROTECT_GETLICENSEINFO, // gets info about license (name, expiration date, app version....)
PROTECT_UPDATEUSERDATA // update user-modifiable user data (name, address....)
} ProtectReasons;
// get reason in string format

View file

@ -1,7 +1,7 @@
#ifndef _Protect_icpp_init_stub
#define _Protect_icpp_init_stub
#include "Core/init"
#include "ScgiServer/init"
#include "Cypher/init"
#include "MySql/init"
#include "Web/init"
#endif