#ifndef _CGI_HELPER_2005_06_06
#define _CGI_HELPER_2005_06_06
//////////////////////////////////
//module name: cgi 操作封装
//author: wwjs(华有为)
//create date: 2005-06-06
//modify date: 2005-06-06
//modify log:
//
////////////////////////////////
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <map>
#include <string>
#include <vector>
using namespace std;
typedef struct tagCGIFORMDATA
{
vector<unsigned char> vData;
long lType;
string szName;
string szExtend;
tagCGIFORMDATA()
:lType(0)
{
}
}tagCGIFORMDATA;
typedef map<string, string> CGIPARAMS;
typedef map<string, tagCGIFORMDATA> CGIFROMDATAS ;
/*
函数介绍:去掉字符串2头的空格
输入参数:需要去掉空格的字符串szSource
输出参数:已经去掉空格后字符串szDest
返回值:已经去掉空格后字符串的个数
*/
inline int TrimString(string& szDest, string szSource)
{
szDest = "";
size_t i = 0;
for(; i < szSource.size() && szSource[i] == ' '; i++);//去掉前面空格
size_t j = szSource.size();
if(j > 0)
{
for(; j > 0 && szSource[i] == ' '; j--);//去掉后面空格
}
if(j > i)
szDest = szSource.substr(i, j-i);//取出去掉空格后的字符串
return szDest.size();
}
/*
函数介绍:将URL路径字符串用分割字符分成一段段,在把等号两旁的字符串分别放到两个临时的string,
成对应关系保存到map模版类中
输入参数:需要拆分的字符串,分割字符
输出参数:处理过的CGIPARAMS
返回值:模版类map的大小
*/
inline int GetCGIParams(CGIPARAMS& cgiParam, const string& szData, const char* lpszFind)
{
cgiParam.clear();
string szParam;
string szValue;
int pos1 = 0;
while(true)
{
int pos2 = szData.find('=', pos1);
if(pos2 <= 0)
break;
TrimString(szParam, szData.substr(pos1, pos2-pos1));//把szData字符串中等号前的字符给szParam
int pos3 = szData.find(lpszFind, pos2);
if(pos3 <= 0)
{
TrimString(szValue, szData.substr(pos2+1, szData.size() - pos2-1));
cgiParam[szParam] = szValue;
break;//到字符串末尾,跳出死循环
}
TrimString(szValue, szData.substr(pos2+1, pos3 - pos2-1));//把szData字符串中等号到lpszFind之间的字符给szValue
cgiParam[szParam] = szValue;
pos1=pos3+1;
}
return cgiParam.size();
}
/*
函数介绍:进行URL解码
输入参数:需要拆分的字符串,分割字符
输出参数:处理过的CGIPARAMS
返回值:模版类map的大小
*/
inline int GetCGIParams(CGIPARAMS& cgiParam)
{
cgiParam.clear();
string szQueryString;
#ifdef _DEBUG
szQueryString = "http://wwjs-pc/stat/qbox_stat.htm?ticket=38E363F6963BF58AED42812B1D459F8DE9CBC4BA1773DA4A882F4236BA2254EF49AB7427C62609B3844FE0C1B03D7CC427A391032C97E84F629A0374F5E12255C1943D7CBD9DD77F07C8C7222DBD4BA2D50B39244C58B08C71280BE77F0B42616987B840F6894FD2C55E6956EC359FBF5B5D1B18056A6C125EA28F1C4E9BB2431AC453559E7A293A8CB7CF7E5C37774E2E18EE4997C54C1BB4CE72D95C2FB7CE&loginParam=FBAC30E4C6A582E3FC020D16C032F980AC665607F7D8D3280791BEE53E771864&length=26&sessionKey=38E363F6963BF58AB5EA3D516E985865";
#else
char* pData = getenv("QUERY_STRING");
szQueryString = pData != 0 ? pData : "";//如果环境变量中有字符串则把它赋值给szQueryString,负责把空字符赋szQueryString
#endif
return GetCGIParams(cgiParam, szQueryString, "&");
}
/*
函数介绍:进行Cookie解码
输入参数:需要拆分的字符串,分割字符
输出参数:处理过的CGIPARAMS
返回值:模版类map的大小
*/
inline int GetCookies(CGIPARAMS& cgiParam)
{
#ifdef _DEBUG
string szCookies = "loginParam=FBAC30E4C6A582E3FC020D16C032F9803B92AC1C493167C125713FFF0F5D9087;length=26;sessionKey=38E363F6963BF58AB5EA3D516E985865";
#else
char* lpszCookies = getenv("HTTP_COOKIE");
string szCookies = lpszCookies != 0 ? lpszCookies : "";
#endif
return GetCGIParams(cgiParam, szCookies, ";");
}
////////////////// cgi form 处理部分
class CCGIParamsEx
{
#define CGI_CRLF "\r\n"
#define HTTP_GET "GET"
#define HTTP_POST "POST"
#define X_CONTENT_TYPE "application/x-www-form-urlencoded"
#define M_CONTENT_TYPE "multipart/form-data"
unsigned char *HTTPBuffer;
unsigned char *REQUEST_METHOD;
unsigned char *CONTENT_TYPE;
unsigned char *CONTENT_LENGTH;
unsigned char *REMOTE_ADDR;
unsigned long ulVersion;
/***************queue start***************/
typedef struct _UploadQueue_{
unsigned char type;
unsigned char name[255];
unsigned char *value;
unsigned char extend[255];
unsigned long size;
struct _UploadQueue_ *next;
}UploadQueue, *PUploadQueue;
typedef struct _QUEUEU_{
int IsInit; //是否初始化
unsigned int serial; //队列元素个数
/*
函数介绍:输入队列函数
输入参数:_UploadQueue_结构中的各参数,Q被定义成指针的指针,用来存放指向队列下个元素指针的地址
输出参数:被填充后的_UploadQueue_结构
返回值:
*/
void push(PUploadQueue * Q,unsigned char type,unsigned char * name,unsigned char * value,unsigned char * extend, unsigned long size)
{
PUploadQueue tmp = (PUploadQueue)malloc(sizeof(UploadQueue));
PUploadQueue PQ = *Q;
if (tmp!=NULL)
{
tmp->type = type;
strcpy((char*)tmp->name, (char*)name);
tmp->value = (unsigned char *)malloc(size+1);
memset(tmp->value, 0x0, size+1);
memcpy(tmp->value, value, size);
strcpy((char*)tmp->extend, (char*)extend);
tmp->size = size;
tmp->next = NULL;
if (*Q==NULL)
{
*Q = tmp;
}
else
{
while(PQ!=NULL&&PQ->next!=NULL)//循环把指针指到队列的末尾,再往队列中插值
{
PQ = PQ->next;
}
PQ->next = tmp;
}
}
else
{
// printf("No memory available.\n");
}
}
/*
函数介绍:输出队列函数
输入参数:指向_UploadQueue_结构指针的指针
输出参数:指向队列下个元素
返回值:
*/
void pop(PUploadQueue *Q)
{
PUploadQueue tmp;
if (IsEmpty(*Q)) return;//空队列,直接退出
tmp = *Q;
*Q = (*Q)->next;//首个元素弹出,指针后移
free(tmp->value);
free(tmp);
}
int IsEmpty(PUploadQueue Q)
{
return Q==NULL;
}
/*
函数介绍:保存队列函数
输入参数:待保存的_UploadQueue_结构
输出参数:名称与tagCGIFORMDATA结构对应存入的cgiDatas
返回值:
*/
void SaveQueue(CGIFROMDATAS& cgiDatas, PUploadQueue Q)
{
char filePath[255]={0};
FILE * fout = NULL;
time_t timer = time(NULL);
struct tm *now = localtime(&timer);
char psBuffer[128]={0}, szLog[256]={0};
char * pszTmp = NULL;
for(int n = 0; Q!=NULL; n++)//循环整个队列
{
if(Q->size > 0)
{
tagCGIFORMDATA d;
d.lType = Q->type;//把_UploadQueue_结构中的数据存到tagCGIFORMDATA结构
d.vData.resize(Q->size);
d.szName = (char*)Q->name;
d.szExtend = (char*)Q->extend;
memcpy(&d.vData[0], Q->value, Q->size);
cgiDatas[(char*)Q->name] = d;//名称与tagCGIFORMDATA结构对应存到map模版
}
Q = Q->next;
}
}
}Queue, *PQueue;
/***********http request start**********/
typedef struct _HTTPREQUEST_{
int IsInit;
char *(*Request)(char *key, char *sOut);
void (*FreeHTTPEnv)();
}HTTPRequest, *PHTTPRequest;
/*
保存文件
xPath = 路径
xIn = 数据
xl = 数据长度
*/
Queue Q;
PUploadQueue UQ;
HTTPRequest R;
/***************queue end***************/
/***********http request end**********/
/***********http upload start**********/
typedef struct _CUPLOAD_{
/*
int (*GetBoundary)(char*);
int (*GetFormType)(char *sIn);
void (*GetFormName)(char *sIn, char *sOut);
void (*GetFileExtendA)(char *sIn, char *sOut);
void (*GetFileExtendB)(char *sIn, char *sOut);
void (*DBParse)(unsigned char *sIn, unsigned long sPos, unsigned long sl);
int (*UImport)(char* sIn, unsigned long sl, char* key);
PUploadQueue (*UExport)(PUploadQueue *Q, char *name);
*/
}CUpload, *PCUpload;
CUpload U;
/***********http upload end**********/
/*
函数介绍:字符转十六进制
输入参数:需要转换的字符串sIn,转换个数sl
输出参数:已经转换的字符串sOut
返回值:
*/
void BinToHex(unsigned char *sIn, char *sOut, int sl){
int i;
*sOut = 0x0;
for (i=0;i<sl;i++)
{
sprintf(sOut+strlen(sOut), "%2.2X", sIn[i]);//把字符转换成宽度为2的十六进制存到sOut中
}
}
/*
函数介绍:在一个字符串中指定一段位置与另一个字符串比较
输入参数:被比较的字符串,开始位置,结束位置,比较字符串
输出参数:比较第一个相等字符在sIn中的位置
返回值:比较第一个相等字符在sIn中的位置
*/
unsigned long substring(int sPos, char *sIn, int sl, char *key){
int i = 0;
int kl = (int)strlen(key);
for (i=sPos;i<sl;i++)
{
if (!strncmp(&sIn[i], key, kl))
{//这样来找字符串也是没办法的事,挺浪费资源,strstr只能找无chr(0)的字符串,可能就是这里耽搁了上行时间,一个20MB的文件本地上传测试速度是3秒
return i;
}
}
return (unsigned long)-1;
}
void InitHTTPRequest(PHTTPRequest r)
{
InitHTTPEnv();
r->IsInit = 1;
r->Request = &Request;
r->FreeHTTPEnv = &FreeHTTPEnv;
InitQueue(&Q);
InitCUpload(&U);
}
/*
函数介绍:初始化HTTP环境变量
输入参数:
输出参数:
返回值:
*/
void InitHTTPEnv()
{
unsigned long cl = 0;
REQUEST_METHOD = (unsigned char *)getenv("REQUEST_METHOD");
CONTENT_TYPE = (unsigned char *)getenv("CONTENT_TYPE");
CONTENT_LENGTH = (unsigned char *)getenv("CONTENT_LENGTH");
if (CONTENT_LENGTH != NULL)
{
cl = (unsigned int)atoi((char*)CONTENT_LENGTH);//标准输入流中数据大小
HTTPBuffer = (unsigned char *)malloc(cl + 1);
memset((char *)HTTPBuffer, 0x0, cl + 1);//初始化内存区域,都赋值为零
#ifdef WIN32
_setmode(fileno(stdin), O_BINARY);
#endif
fread(HTTPBuffer, cl, 1, stdin);//把标准输入流中的数据读到HTTPBuffer中
#ifdef WIN32
_setmode(fileno(stdin), O_TEXT);
#endif
}
}
//释放malloc了的资源
static void FreeHTTPEnv(){}
static char *Request(char *key, char *sOut)
{
return sOut;
}
/*
函数介绍:获取二进制分割符
输入参数:
输出参数:二进制分割符
返回值:成功返回0,不成功则非0
*/
int GetBoundary(char *boundary)
{
int ml = (int)strlen(M_CONTENT_TYPE);
int cl;
int i;
int sPos = ml + 11;//11 byte length just '; boundary='
*boundary = 0x0;
if (CONTENT_LENGTH == NULL)
{
return 1;
}
if (CONTENT_TYPE == NULL)
{
return 2;
}
if (!strncmp((char*)CONTENT_TYPE, M_CONTENT_TYPE, ml))
{
*boundary++ = '-';
*boundary++ = '-';
cl = (int)strlen((char*)CONTENT_TYPE);
for (i=sPos; i<cl; i++)
{
*boundary++ = CONTENT_TYPE[i];//不明白为什么往boundary填充"--"
}
*boundary = 0x0;
return 0;
}
return 3;
}
/*
函数介绍:获取表单类型
输入参数:表单类型字符串
输出参数:类型值
返回值:0:表单域;1:文件域
*/
int GetFormType(char *sIn)
{
char key[255]={0};
char *p;
sprintf(key, "filename=%c", 34);
p = strstr(sIn, key);
return p!=NULL;
}
/*
函数介绍:获取表单名
输入参数:表单类型字符串
输出参数:表单名称字符串sOut
返回值:
*/
void GetFormName(char *sIn, char *sOut)
{
char key[255]={0};
char *p;
sprintf(key, "name=%c", 34);
p = strstr(sIn, key);
*sOut = 0x0;
if (p)
{
p += (int)strlen(key);
while(*p && (*p != '"'))
{
*sOut++ = *p++;
}
}
}
/*
函数介绍:从文件类型获取文件扩展名,文件类型太多,使用的时候自己补充
输入参数:待判断扩展名的文件名字符串
输出参数:增加文件扩展名后的文件名字符串
返回值:
*/
void GetFileExtendA(char *sIn, char *sOut)
{
char buf[16]={0};
BinToHex((unsigned char*)sIn, buf, 4);
if (!strncmp(sIn, "MZ", 2))
{
strcpy(sOut, "exe");//exe程序文件
}
else if (!strncmp(sIn, "PK", 2))
{
strcpy(sOut, "zip");//zip文件
}
else if (!strncmp(sIn, "BM", 2))
{
strcpy(sOut, "bmp");//bmp文件
}
else if (!strncmp(sIn+1, "PDF", 3))
{
strcpy(sOut, "pdf");//pdf文件
}
else if (!strncmp(sIn, "Rar", 3))
{
strcpy(sOut, "rar");//rar文件
}
else if (!strncmp(sIn, "GIF", 3))
{
strcpy(sOut, "gif");//gif文件
}
else if (!strncmp(sIn+1, "PNG", 3))
{
strcpy(sOut, "png");//png文件
}
else if (!strncmp(sIn, "FWS", 3))
{
strcpy(sOut, "swf");//swf文件
}
else if (!strncmp(sIn, "CWS", 3))
{
strcpy(sOut, "swf");//flash swf文件
}
else if (!strncmp(sIn, "RIFF", 4))
{
strcpy(sOut, "avi");//avi文件
}
else if (!strncmp(buf, "1F8B", 4))
{
strcpy(sOut, "gz");//gzip件
}
else if (!strncmp(buf, "FFD8FFE0", 8))
{
strcpy(sOut, "jpg");//jpg文件
}
else if (!strncmp(buf, "D0CF11E0", 8))
{
strcpy(sOut, "doc");//doc文件,这里的格式判断我是用UltraEdit打开,查看它的十六进制格式所得的结果,其实有8个字节长度,内容为:D0CF11E0A1B11AEE。第9位起才是Chr(0)
}
else if (!strncmp(buf, "FFFB9404", 8))
{
strcpy(sOut, "mp3");//mp3文件
}
else if (!strncmp(buf, "XML", 8))
{
strcpy(sOut, "xml");//XML文件
}
else
{
*sOut = 0x0;
}
}
/*
函数介绍:从Content-Type获取文件名
输入参数:待取出文件名的字符串
输出参数:文件名字符串
返回值:
*/
void GetFileExtendB(char *sIn, char *sOut)
{
char *key = ".";
char *p;
p = strstr(sIn, key);
*sOut = 0x0;
if (p)
{
p += (int)strlen(key);
while(*p && (*p != '"'))
{
*sOut++ = *p++;
}
}
}
/*
函数介绍:解析数据,保存到队列
输入参数:待解析的URL字符串sIn,第一个二进制分隔符位置加二进制分隔符长度加"/r/n/r/n"字符长度的值,
两个二进制分割符中的数据
输出参数:
返回值:
*/
void DBParse(unsigned char *sIn, unsigned long sPos, unsigned long sl)
{
unsigned char type = 0, name[255]={0}, *value, extend[255]={0};
// unsigned long size = 0;
int cl = (int)strlen(CGI_CRLF); //2
unsigned long hl;//head length
unsigned long vl;
unsigned long dl;
char *buf = (char *)malloc(sl+1);
char *tmp;
memset(buf, 0x0, sl+1);
memcpy(buf, sIn + sPos, sl);//两个二进制分割符之间的字符考到buf
hl = substring(0, buf, sl, "\r\n\r\n");//BUF中'\'的位置
tmp = (char *)malloc(hl + 1);
memset(tmp, 0x0, hl + 1);
memcpy(tmp, buf, hl);//将BUF中末尾的"\r\n\r\n"字符串截断放到tmp中
type = GetFormType(tmp);
GetFormName(tmp, (char *)name);
//fwrite(buf, sl, 1, stdout);
dl = hl + cl + cl;
vl = sl - dl;
value = (unsigned char *)malloc(vl + 1);
memset(value, 0x0, vl+1);
memcpy(value, buf + dl, vl);
if (type == 1 && vl > 0)
{
GetFileExtendA((char*)value, (char *)extend);
//printf("Extend From Byte: %s\n", extend);
if (extend[0] == 0x0)
{
GetFileExtendB(tmp, (char *)extend);
}
}
if (Q.IsInit==1)
{
Q.push(&UQ, type, name, value, extend, vl);//将上载队列结构_UploadQueue_压入队列
Q.serial++; //队列元素个数累加
}
free(value);
free(tmp);
free(buf);
}
/*
函数介绍:上传入口函数
输入参数:sIn = stdin里面的数据
sl = CONTENT_LENGTH
key = 二进制分割符boundary
输出参数:
返回值:成功返回1
*/
int UImport(char* sIn, unsigned long sl, char* key)
{
int kl = (int)strlen(key);
int cl = (int)strlen(CGI_CRLF);//2
unsigned long sPos, ePos;
sPos = substring(0, sIn, sl, key);
while (sPos != -1)
{
ePos = substring(sPos + kl, sIn, sl, key);//第二个二进制分隔符的起始位置
if (ePos == -1)
{
break;
}
//printf("sPos: %d ePos: %d ePos - sPos = %d\n", sPos, ePos, ePos - sPos);
DBParse((unsigned char*)sIn, sPos+kl+cl, ePos - sPos - kl - cl * 2);//两个二进制分割符中的数据
sPos = substring(ePos, sIn, sl, key);
}
return 1;
}
/*
函数介绍:上传文件出口函数
输入参数:name = 表单名
输出参数:
返回值:返回PUploadQueue结构
*/
PUploadQueue UExport(PUploadQueue *Q, char *name)
{
PUploadQueue PQ = *Q;
PUploadQueue R = NULL;
while(PQ != NULL)
{
if (!strcmp((char*)PQ->name, name))
{
R = PQ;
break;
}
PQ= PQ->next;//指向队列中下个元素
}
return R;
}
void InitCUpload(PCUpload p)
{
/*
p->GetBoundary = &GetBoundary;
p->GetFormType = &GetFormType;
p->GetFormName = &GetFormName;
p->GetFileExtendA = &GetFileExtendA;
p->GetFileExtendB = &GetFileExtendB;
p->DBParse = &DBParse;
p->UImport = &UImport;
p->UExport = &UExport;
*/
}
/**************queue*****************/
//以下就是队列了,就不用解释了,都是基本知识
/*
函数介绍:初始化队列函数
输入参数:队列结构的对象p
输出参数:
返回值:
*/
void InitQueue(PQueue p){
p->IsInit = 1;
p->serial = 0; //队列元素个数
//p->push = &push;
//p->pop = &pop;
//p->IsEmpty = &IsEmpty;
//p->SaveQueue = &SaveQueue;
}
/*
函数介绍:获得环境变量函数
输入参数:环境变量参数
输出参数:环境变量值
返回值:
*/
void GetEnv(string& szValue, const char* lpszName)
{
char *lpszValue= getenv(lpszName);
szValue = lpszValue != 0 ? lpszValue : "";
}
public:
CCGIParamsEx()
:UQ(0)
,HTTPBuffer(0)
,REQUEST_METHOD(0)
,CONTENT_TYPE(0)
,CONTENT_LENGTH(0)
,REMOTE_ADDR(0)
,ulVersion((unsigned long)-1)
{
}
int Init()
{
GetCookies(m_cookies); //以';'为分割符解码
GetCGIParams(m_urlParams);//以'&'为分割符解码
GetEnv(m_szRemoteAddr, "REMOTE_ADDR");
unsigned long i = 0;
long k = 0;
char *lpszCookies = 0;
char boundary[255]={0}, szLog[256]={0};
time_t timer = time(0);
struct tm * now = localtime(&timer);
InitHTTPRequest(&R);
// g_Log.Format("[debug] %d", __LINE__);
// return 0;
// g_Log.Format("[info] %d", __LINE__);
if (REQUEST_METHOD==NULL)
{
// g_Log.Format("[info] %d", __LINE__);
// printf("This is CGI program only!\n");
}
else if (!strcmp((char*)REQUEST_METHOD, HTTP_GET) || !strcmp((char*)REQUEST_METHOD, HTTP_POST))//用GET方法或用POST方法
{
// g_Log.Format("[info] %d", __LINE__);
// printf("Status: 200%s", CGI_CRLF);
// printf("Content-Type: text/plain%s", CGI_CRLF);
// printf("%s", CGI_CRLF);
if (!GetBoundary(boundary))//获取二进制分割符
{
//printf("boundary: %s\n", boundary);
UImport((char*)HTTPBuffer, atoi((char*)CONTENT_LENGTH), boundary);
}
// g_Log.Format("[info] %d", __LINE__);
Q.SaveQueue(m_FormDatas, UQ);//对全部Form域进行处理
// g_Log.Format("[info] %d", m_FormDatas.size());
for (i=0;i<Q.serial;i++)
{
// g_Log.Format("[info] %d", __LINE__);
Q.pop(&UQ);//清空队列
}
}
else
{
// printf("Status: 200%s", CGI_CRLF);
// printf("Content-Type: text/html%s", CGI_CRLF);
// //printf("%s", CGI_CRLF);
// printf("Only GET or POST request can be response!\n");
}
if (R.IsInit==1)
{
if (HTTPBuffer!=NULL)
{
free(HTTPBuffer);//释放资源
}
//R.FreeHTTPEnv();
}
return 0;
}
public:
string m_szRemoteAddr;
CGIFROMDATAS m_FormDatas;
CGIPARAMS m_cookies;
CGIPARAMS m_urlParams;
};
#endif