cgi 操作封装

#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

原文地址:https://www.cnblogs.com/zzxap/p/2175774.html