diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c624161..e8ce76f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,8 @@ file( server/domo-server.cpp broker/nats-broker.cpp web/web-server.cpp - web/handler/exit-handler.cpp + tinyweb/tinyweb.c + tinyweb/tools.c ) add_executable (domo-iot ${source_files}) diff --git a/src/server/domo-server.cpp b/src/server/domo-server.cpp index 4d35fe4..65d47ec 100644 --- a/src/server/domo-server.cpp +++ b/src/server/domo-server.cpp @@ -31,7 +31,7 @@ #include "domo-server.h" #define kDocumentRoot "." -#define kPort "8081" +#define kPort 8081 /*! ---------------------------------------------------------------------------- * @fn DomoServer diff --git a/src/tinyweb/tinyweb.c b/src/tinyweb/tinyweb.c index 16ccd47..7607246 100644 --- a/src/tinyweb/tinyweb.c +++ b/src/tinyweb/tinyweb.c @@ -13,9 +13,9 @@ #include #include -//TinyWeb 增加与完善功能,by lzpong 2016/11/24 +// TinyWeb ajouté et amélioré les fonctions, par lzpong 2016/11/24 -//值大,发送文件时磁盘和CPU性能更好,占用内存增加 +// Valeur élevée, meilleures performances du disque et du processeur lors de l'envoi de fichiers, utilisation accrue de la mémoire #define TW_SEND_SIZE 1024*1024*16 typedef struct tw_file_t { diff --git a/src/tinyweb/tinyweb.h b/src/tinyweb/tinyweb.h index 5b43d3b..e62689d 100644 --- a/src/tinyweb/tinyweb.h +++ b/src/tinyweb/tinyweb.h @@ -32,34 +32,33 @@ #if TinyWeb_Function_Description //TinyWeb功能说明 auth lzpong 2016/11/24 -功能基于 libuv 跨平台库 - -0.支持设置文档编码,默认 utf-8 -1.支持使用HTTP: GET/POST方式访问 -2.支持Socket, WebSocket 连接 -3.支持返回404错误页面 -4.支持指定根目录(默认程序所在目录) -5.支持任意格式文件访问(带/不带扩展名, 文件下载) - a.支持静态网页访问:html/htm - b.支持其他静态文件:js, css, png, jpeg/jpg, gif, ico, txt, xml, json, log, wam, wav, mp3, mp4, apk 等 - c.支持其他文件格式, 默认文件类型为:"application/octet-stream" - d.支持不带扩展名文件访问 - e.支持 Range 请求参数下载大文件(Range: bytes=sizeFrom-[sizeTo],支持负反向计算) -6.支持默认index页面(index.html/index.htm),可以自定义设置 -7.支持目录列表 -8.不允许访问根目录上级文件或文件夹 -9.支持回调 - a.接收到HTTP请求后先回调(此功能便于程序返回自定义功能),回调失败或返回0时执行普通http响应 - b.WebSocket 数据回调 - c.socket 数据回调 -10.支持x64,支持超过2G大文件 -11.支持cookie/setcookie -12.支持添加自定义头部信息 -13.支持POST较大的数据(支持分包发送的http Post内容) -==============stable,future -14.支持分包发送的http头部(http get) +Fonctionnalités basées sur la bibliothèque multiplateforme libuv +0. Encodage de document de réglage d assistance, utf-8 par défaut +1.Support via HTTP: GET / POST +2.Support Socket, connexion WebSocket +3. Assistance renvoyant une page d erreur 404 +4. Assistance pour spécifier le répertoire racine (le répertoire dans lequel se trouve le programme par défaut) +5.Accès aux fichiers au format arbitraire (avec / sans extension, téléchargement de fichier) +Soutenir l accès statique aux pages Web: html / htm +Soutenir d autres fichiers statiques: js, css, png, jpeg / jpg, gif, ico, txt, xml, json, journal, wam, wav, mp3, mp4, apk, etc. +c. Prend en charge d’autres formats de fichier, le type de fichier par défaut est: "application / octet-stream" +d. Accès aux fichiers de support sans extension +Paramètres de demande de prise en charge de plage pour télécharger des fichiers volumineux (plage: octets = tailleFrom- [tailleVers], prise en charge du calcul inverse négatif) +6. Soutenez la page d’index par défaut (index.html / index.htm), vous pouvez personnaliser les paramètres. +7. Liste de répertoires d assistance +8. Ne pas autoriser l accès aux fichiers ou dossiers racine +9. Rappel d assistance +Rappel après réception de la demande HTTP (cette fonction facilite le retour du programme à la fonction personnalisée), et la réponse http ordinaire est exécutée lorsque le rappel échoue ou renvoie 0 +b. rappel de données WebSocket +Rappel de données c.socket +10.Support x64, supporte plus de 2G gros fichiers +11.Support cookie / setcookie +12. Prise en charge de l ajout d'informations d'en-tête personnalisées +13.Support POST données plus volumineuses (soutien post-contenu http envoyé par sous-traitance) +=============== stable, futur +14. en-tête http de soutien (http get) #endif #ifdef __cplusplus @@ -67,7 +66,7 @@ extern "C" { #endif typedef struct tw_peerAddr { - uchar flag;//标志字节 ([0~7]: [0]是否需要保持连接 [1]是否WebSocket [2]是否WebSocket文本帧) + uchar flag;//flag ([0 ~ 7]: [0] Reste connecté ou non [1] WebSocket [2] Cadre WebSocket ou ushort port; ushort fport; size_t sk; @@ -78,52 +77,53 @@ typedef struct tw_peerAddr { typedef struct tw_reqHeads { uchar method;//0:Socket 1:GET 2:POST char host[260]; //IP:port, domain - char path[512]; //路径 - char query[1500];//参数 - char* data; //数据 + char path[512]; //Chemin + char query[1500];//Paramètre + char* data; //Les données char cookie[260];//cookie size_t contentLen;//Content Lenth - size_t len; //接收的数据长度 + size_t len; //Longueur des données reçues long long Range_frm, Range_to; }tw_reqHeads; -//服务配置 +// Configuration du service typedef struct tw_config { //private data: uv_tcp_t _server; //public data: - uchar dirlist:1; //是否允许列出目录 - char* doc_dir; //Web根目录,绝对路径,末尾带斜杠'\'(uninx为'/'); 默认程序文件所在目录 - char* doc_index;//默认主页文件名,逗号分隔; 默认"index.html,index.htm" + uchar dirlist:1; // Autoriser ou non la liste des répertoires + char* doc_dir; // Répertoire racine Web, chemin absolu, avec la barre oblique '\' à la fin (uninx est '/'); répertoire dans lequel se trouve le fichier de programme par défaut + char* doc_index;// Nom du fichier de page d'accueil par défaut, séparé par des virgules; par défaut "index.html, index.htm" char* ip; //服务的IP地址 is only ipV4, can be NULL or "" or "*", which means "0.0.0.0" - ushort port; //服务监听端口 - char* charset; //文档编码(默认utf-8) - //数据 - void* data;//用户数据,如对象指针 + ushort port; // L'adresse IP du service est uniquement ipV4, peut être NULL ou "" ou "*", ce qui signifie "0.0.0.0" + char* charset; // Encodage de document (utf-8 par défaut) + // Les données + void* data;// Données utilisateur, telles que les pointeurs d'objet - //客户端接入 + // Accès client char (*on_connect)(void* data, uv_stream_t* client, tw_peerAddr* pa); - //返回非0表示已经处理处理请求 - //返回0表示没有适合的处理请求,将自动查找文件/文件夹,若未找到则发送404响应 - //此功能便于程序返回自定义功能 - //heads成员不需要free - //pa->flag:标志字节 ([0~7]: [0]是否需要保持连接 [1]是否WebSocket [2]是否WebSocket文本帧 + // Retour non nul signifie que la demande de traitement a été traitée. + // Return 0 signifie qu'il n'y a pas de demande de traitement appropriée, il trouvera automatiquement le fichier / dossier, s'il n'est pas trouvé, envoie une réponse 404 + // Cette fonction est pratique pour que le programme revienne à la fonction personnalisée. + // les membres chefs n'ont pas besoin d'être libres + // pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket char (*on_request)(void* data, uv_stream_t* client, tw_peerAddr* pa, tw_reqHeads* heads); - //Socket 或 WebSocket 数据, 可以通过buf->flag 或 pa->flag判断 - //buf成员不需要free - //pa->flag:标志字节 ([0~7]: [0]是否需要保持连接 [1]是否WebSocket [2]是否WebSocket文本帧 + // Les données de socket ou WebSocket peuvent être jugées par buf-> flag ou pa-> flag + // les membres de buf n'ont pas besoin d'être libres + // pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket char (*on_data)(void* data, uv_stream_t* client, tw_peerAddr* pa, membuf_t* buf); - //Socket 检测到错误(此时链接可能已经断开) - //错误消息格式:"%d:%s,%s" - //pa->flag:标志字节 ([0~7]: [0]是否需要保持连接 [1]是否WebSocket [2]是否WebSocket文本帧 + + // Le socket a détecté une erreur (le lien peut être cassé à ce moment) + // Format du message d'erreur: "% d:% s,% s" + // pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket char (*on_error)(void* data, uv_stream_t* client, tw_peerAddr* pa,int errcode, char* errstr); - //Socket 已关闭(此时链接已经断开) - //flag:标志字节 ([0~7]: [0]是否需要保持连接<非长连接为http> [1]是否WebSocket + // Le socket est fermé (le lien a été déconnecté pour le moment) + // indicateur: octet d'indicateur ([0 ~ 7]: [0] s'il faut conserver la connexion [1] s'il faut WebSocket char (*on_close)(void* data, uv_stream_t* client, tw_peerAddr* pa); } tw_config; @@ -131,7 +131,7 @@ typedef struct tw_config { //start web server, start with the config //loop: if is NULL , it will be uv_default_loop() //conf: the server config -//返回值不为0表示错误代码,用uv_err_name(n),和uv_strerror(n)查看原因字符串 +// Si la valeur renvoyée n'est pas 0, cela indique le code d'erreur. Utilisez uv_err_name (n) et uv_strerror (n) pour afficher la chaîne de motif. int tinyweb_start(uv_loop_t* loop, tw_config* conf); //stop TinyWeb @@ -140,55 +140,55 @@ void tinyweb_stop(uv_loop_t* loop); //================================================= -//制造头部 SetCookie 字段和值 -//set_cookie: 缓存区(至少 42+strlen(domain)=strlen(path) ) -//ckLen: set_cookie的长度 -//expires: 多少秒后过期 -//domain: Domain, 域名或IP地址 -//path: Path, 可以是 heads->path +// Crée les champs et les valeurs de l'en-tête SetCookie +// set_cookie: zone de cache (au moins 42 + strlen (domaine) = strlen (chemin)) +// ckLen: longueur de set_cookie +// expire: combien de secondes vont expirer +// domaine: domaine, nom de domaine ou adresse IP +// chemin: chemin, peut être heads-> chemin void tw_make_setcookie(char* set_cookie, int ckLen, const char* key, const char* val, int expires, char* domain, char* path); -//制造头部 delete cookie +// créer un cookie d'en-tête void tw_make_delcookie(char* del_cookie, int ckLen, char* key); -//返回格式华的HTTP响应内容 (需要free返回数据) -//status:http状态,如:"200 OK" -//ext_heads:额外的头部字符串,如:"head1: this-is-head1\r\nSetCookie: TINY_SSID=Tiny1531896250879; Expires=...\r\n" -//content_type:文件类型,如:"text/html" ;可以调用tw_get_content_type()得到 -//content:使用utf-8编码格式的数据,特别是html文件类型的响应 -//content_length:0或-1自动计算 content 长度(c_str, end with NULL) -//respone_size:if not NULL,可以获取发送的数据长度 the size of respone will be writen to request -//returns malloc()ed c_str, need free() by caller +// Renvoie le format du contenu de la réponse HTTP (nécessite des données de retour gratuites) +// status: statut http, tel que: "200 OK" +// ext_heads: chaînes d'en-tête supplémentaires, telles que: "head1: this-is-head1 \ r \ nSetCookie: TINY_SSID = Tiny1531896250879; Expires = ... \ r \ n" +// contenu_type: type de fichier, tel que: "text / html"; vous pouvez l'obtenir en appelant tw_get_content_type () +// contenu: utiliser les données de format de codage UTF-8, en particulier la réponse du type de fichier html +// longueur_contenu: 0 ou -1 calcule automatiquement la longueur du contenu (c_str, se termine par NULL) +// respone_size: si non NULL, vous pouvez obtenir la longueur des données envoyées, la taille de respone sera écrite pour demander +// retourne malloc () ed c_str, need free () par appelant char* tw_format_http_respone(uv_stream_t* client, const char* status, const char* ext_heads, const char* content_type, const char* content, size_t content_length, size_t* respone_size); -//根据扩展名返回文件类型 content_type -//可以传入路径/文件名/扩展名 +// Retourne le type de fichier content_type en fonction de l'extension +// Vous pouvez passer le chemin / nom du fichier / extension const char* tw_get_content_type(const char* fileExt); -//发送数据到客户端; 如果是短连接,则发送完后会关闭连接 -//data:待发送数据 -//len: 数据长度, -1 将自动计算数据长度 -//need_copy_data:是否需要复制数据 -//need_free_data:是否需要free数据, 如果need_copy_data非零则忽略此参数 +// Envoie des données au client, s'il s'agit d'une connexion courte, la connexion sera fermée après l'envoi +// data: données à envoyer +// len: longueur des données, -1 calculera automatiquement la longueur des données +// need_copy_data: s'il faut copier des données +// need_free_data: si des données libres sont nécessaires, si need_copy_data est différent de zéro, ce paramètre est ignoré void tw_send_data(uv_stream_t* client, const void* data, size_t len, char need_copy_data, char need_free_data); -//发送'200 OK' 响应; 不会释放(free)传入的数据(u8data) -//content_type:Content Type 文档类型 -//u8data:utf-8编码的数据 -//content_length:数据长度,为0或-1时自动计算(strlen)(c_str, end with NULL) -//respone_size:获取响应最终发送的数据长度,为0表示放不需要取此长度 +// Envoie une réponse '200 OK', les données entrantes (u8data) ne seront pas libérées +// contenu_type: type de document Type de contenu +// u8data: données codées UTF-8 +// longueur_contenu: longueur des données, calculée automatiquement lorsque la valeur est 0 ou -1 (strlen) (c_str, se termine par NULL) +// respone_size: Récupère la longueur des données envoyées par la réponse. Une valeur de 0 signifie que vous n'avez pas besoin de prendre cette longueur void tw_send_200_OK(uv_stream_t* client, const char* ext_heads, const char* content_type, const void* u8data, size_t content_length, size_t* respone_size); -//http协议发送文件,异步 -//file_path: 文件路径 +// fichier d'envoi de protocole http, asynchrone +// chemin_fichier: chemin du fichier void tw_http_send_file(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads, const char* content_type, const char* file_path); -//发送301响应,路径永久重定位 +// Envoie une réponse 301, le chemin est déplacé de façon permanente void tw_301_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads); -//发送302响应,路径临时重定位 +// Envoyer une réponse 302, le chemin est temporairement déplacé void tw_302_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads); -//关闭客户端连接 +// ferme la connexion du client void tw_close_client(uv_stream_t* client); #ifdef __cplusplus diff --git a/src/tinyweb/tools.c b/src/tinyweb/tools.c new file mode 100644 index 0000000..453f31b --- /dev/null +++ b/src/tinyweb/tools.c @@ -0,0 +1,1715 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tools.h" + +#include + +#include +#include +#include +#include +#include +#include + +//-----------------------------------------------------------------------------------membuf c-str win/linux +#pragma region membuf c - str + +#include + +// ----------------------------------------------------------------------------------- + +//初始化 +void membuf_init(membuf_t *buf, size_t initial_buffer_size) +{ + memset(buf, 0, sizeof(membuf_t)); + buf->data = initial_buffer_size > 0 ? (uchar *)calloc(1, initial_buffer_size) : NULL; + //memset(buf->data, 0, initial_buffer_size); + buf->buffer_size = initial_buffer_size; +} + +// ----------------------------------------------------------------------------------- + +//释放buffer +void membuf_uninit(membuf_t *buf) +{ + if (buf->data) + free(buf->data); + memset(buf, 0, sizeof(membuf_t)); +} + +// ----------------------------------------------------------------------------------- + +//清除数据(数据覆盖为NULL),并缩小buffer大小 +void membuf_clear(membuf_t *buf, size_t maxSize) +{ + if (buf->data && buf->size) + { + if (maxSize > 1 && buf->buffer_size > maxSize) + { + uchar *p = (uchar *)realloc(buf->data, maxSize); + //防止realloc分配失败,或返回的地址一样 + assert(p); + if (p != buf->data) + buf->data = p; + buf->size = 0; + buf->buffer_size = maxSize; + } + else + { + buf->size = 0; + } + memset(buf->data, 0, buf->buffer_size); + } +} + +// ----------------------------------------------------------------------------------- + +////扩展buffer大小 +void membuf_reserve(membuf_t *buf, size_t extra_size) +{ + if (extra_size > buf->buffer_size - buf->size) + { + //calculate new buffer size + size_t new_buffer_size = buf->buffer_size == 0 ? extra_size : buf->buffer_size << 1; + size_t new_data_size = buf->size + extra_size; + while (new_buffer_size < new_data_size) + new_buffer_size <<= 1; + + // malloc/realloc new buffer + uchar *p = (uchar *)realloc(buf->data, new_buffer_size); // realloc new buffer + //防止realloc分配失败,或返回的地址一样 + assert(p); + if (p != buf->data) + buf->data = p; + memset((buf->data + buf->size), 0, new_buffer_size - buf->size); + buf->buffer_size = new_buffer_size; + } +} + +// ----------------------------------------------------------------------------------- + +//截断(释放)多余的内存 或者增加内存,至 size+4 的大小; 后面4字节填充0 +void membuf_trunc(membuf_t *buf) +{ + if (buf->buffer_size > (buf->size + 4) || buf->buffer_size < (buf->size + 4)) + { + uchar *p = (uchar *)realloc(buf->data, buf->size + 4); // realloc new buffer + //防止realloc分配失败,或返回的地址一样 + assert(p); + if (p && p != buf->data) + buf->data = p; + memset((buf->data + buf->size), 0, 4); + buf->buffer_size = buf->size + 4; + } +} + +// ----------------------------------------------------------------------------------- + +//添加C-style字符串 +size_t membuf_append(membuf_t *buf, const char *str) +{ + if (str == NULL) + return 0; + size_t size = strlen(str); + membuf_reserve(buf, size); + memmove((buf->data + buf->size), str, size); + buf->size += size; + return size; +} + +// ----------------------------------------------------------------------------------- + +//添加数据 +size_t membuf_append_data(membuf_t *buf, const void *data, size_t size) +{ + assert(data && size > 0); + membuf_reserve(buf, size); + memmove((buf->data + buf->size), data, size); + buf->size += size; + return size; +} + +// ----------------------------------------------------------------------------------- + +//按格式添加数据 +size_t membuf_append_format(membuf_t *buf, const char *fmt, ...) +{ + assert(fmt); + va_list ap, ap2; + va_start(ap, fmt); + size_t size = vsnprintf(0, 0, fmt, ap) + 1; + va_end(ap); + membuf_reserve(buf, size); + va_start(ap2, fmt); + vsnprintf((char *)(buf->data + buf->size), size, fmt, ap2); + va_end(ap2); + buf->size += --size; + return size; +} + +// ----------------------------------------------------------------------------------- + +//插入数据:offset位置,data数据,size数据大小 +void membuf_insert(membuf_t *buf, size_t offset, void *data, size_t size) +{ + assert(offset < buf->size); + membuf_reserve(buf, size); + memcpy((buf->data + offset + size), buf->data + offset, buf->size - offset); + memcpy((buf->data + offset), data, size); + buf->size += size; +} + +// ----------------------------------------------------------------------------------- + +//从末尾移动数据(不会填充为NULL,仅更改size) +void membuf_move(membuf_t *buf, size_t offset, size_t size) +{ + assert(offset < buf->size); + if (offset + size >= buf->size) + { + buf->size = offset; + } + else + { + //memmove() 用来复制内存内容(可以处理重叠的内存块):void * memmove(void *dest, const void *src, size_t num); + memmove((buf->data + offset), buf->data + offset + size, buf->size - offset - size); + buf->size -= size; + } + if (buf->buffer_size >= buf->size) + buf->data[buf->size] = 0; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------文件/文件夹检测 win/linux +#pragma region 文件 / 文件夹检测 + +// ----------------------------------------------------------------------------------- + +//获取工作目录路径,不带'/' +char *getWorkPath() +{ + static char CurPath[260] = {0}; + getcwd(CurPath, 259); + return CurPath; +} + +// ----------------------------------------------------------------------------------- + +//建立目录,递归建立 +int makeDir(const char *path, int mod) +{ + char pth[513]; + strncpy(pth, path, 512); + char *p = strrchr(pth, '\\'); + if (!p) + p = (char *)strrchr(pth, '/'); + if (p) + { + if (strlen(p) == 1) + *p = 0; + p = strrchr(pth, '\\'); + if (!p) + p = (char *)strrchr(pth, '/'); + if (p) + { + *p = 0; + if (!isExist(pth)) + makeDir(pth, mod); + } + } + return mkdir(path, mod); +} + +// ----------------------------------------------------------------------------------- + +//获取程序文件所在路径,不带'/' +char *getProcPath() +{ + static char CurPath[260] = {0}; + int cnt = readlink("/proc/self/exe", CurPath, 259); + if (cnt > 0 || cnt < 260) + { + //获取程序路径,即去掉程序名,包括去掉最后的'/' + int i; + for (i = cnt - 1; i > 0 && CurPath[i] != '/'; --i) + { + CurPath[i] = 0; + } + if (i > 2 && CurPath[i] == '/') + CurPath[i] = 0; + } + return CurPath; +} + +// ----------------------------------------------------------------------------------- + +//路径是否存在(0:不存在 1:存在:文件 2:存在:文件夹) +char isExist(const char *path) +{ + if (path && access(path, F_OK) == 0) + { + struct stat info; + stat(path, &info); + if (S_ISDIR(info.st_mode) || S_ISLNK(info.st_mode)) //dir or link + return 2; + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------------------- + +//是否目录(1:是目录 0;非目录/不存在) +char isDir(const char *path) +{ + struct stat info; + if (path && !stat(path, &info)) // && opendir(path)!=NULL) + { + if (S_ISDIR(info.st_mode) || S_ISLNK(info.st_mode)) //dir or link + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------------------- + +//是否文件(1:是文件 0:非文件/不存在) +char isFile(const char *path) +{ + if (path) + { + struct stat info; + stat(path, &info); + if (S_ISREG(info.st_mode)) //普通文件 + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------------------- + +// Return list directory Json string, need free the return +char *listDir(const char *fullpath, const char *reqPath) +{ + int fnum = 0; + char tmp[1024]; + struct tm *mtime; + DIR *dp; + struct dirent *fileInfo; + struct stat statbuf; + membuf_t buf; + membuf_init(&buf, 2048); + membuf_append_format(&buf, "{\"path\":\"%s\",\"files\":[\r\n\0\0", reqPath); + + //文件(size>-1) 或 目录(size=-1) [name:"file1.txt",mtime:"2016-11-28 16:25:46",size:123],\r\n + if ((dp = opendir(fullpath)) != NULL) + { + while ((fileInfo = readdir(dp)) != NULL) + { + snprintf(tmp, 1023, "%s/%s\0\0", fullpath, fileInfo->d_name); + stat(tmp, &statbuf); //stat函数需要传入绝对路径或相对(工作目录的)路径 + mtime = localtime(&statbuf.st_mtime); + if (S_ISDIR(statbuf.st_mode)) + { + if (strncmp(fileInfo->d_name, ".", 1) == 0) + continue; + membuf_append_format(&buf, "{\"name\":\"%s/\",\"mtime\":\"%d-%02d-%02d %02d:%02d:%02d\",\"size\":\"-\",\"type\":\"D\"},\n", fileInfo->d_name, (1900 + mtime->tm_year), (1 + mtime->tm_mon), mtime->tm_mday, mtime->tm_hour, mtime->tm_min, mtime->tm_sec); + } + else + { + fnum++; + membuf_append_format(&buf, "{\"name\":\"%s\",\"mtime\":\"%d-%02d-%02d %02d:%02d:%02d\",\"size\":%ld,\"type\":\"F\"},\n", fileInfo->d_name, (1900 + mtime->tm_year), (1 + mtime->tm_mon), mtime->tm_mday, mtime->tm_hour, mtime->tm_min, mtime->tm_sec, statbuf.st_size); + } + } + closedir(dp); + } + //membuf_remove(&buf, buf.size - 1, 1); + buf.data[--buf.size] = 0; + buf.data[--buf.size] = 0; + membuf_append_format(&buf, "],total:%d}", fnum); + membuf_trunc(&buf); + return (char *)buf.data; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------编码转换 win/linux +#pragma region 编码转换 + +/***************************************************************************** +* 将一个字符的Unicode(UCS-2和UCS-4)编码转换成UTF-8编码. +* +* 参数: +* unic 字符的Unicode编码值 +* pOutput 指向输出的用于存储UTF8编码值的缓冲区的指针 +* outsize pOutput缓冲的大小 +* +* 返回值: +* 返回转换后的字符的UTF8编码所占的字节数, 如果出错则返回 0 . +* +* 注意: +* 1. UTF8没有字节序问题, 但是Unicode有字节序要求; +* 字节序分为大端(Big Endian)和小端(Little Endian)两种; +* 在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位) +* 2. 请保证 pOutput 缓冲区有最少有 6 字节的空间大小! +****************************************************************************/ +int enc_unicode_to_utf8_one(size_t unic, uchar *pOutput, int outSize) +{ + assert(pOutput != NULL); + assert(outSize >= 6); + + if (unic <= 0x0000007F) + { + // U-00000000 - U-0000007F: 0xxxxxxx + *pOutput = (unic & 0x7F); + return 1; + } + else if (unic >= 0x00000080 && unic <= 0x000007FF) + { + // * U-00000080 - U-000007FF: 110xxxxx 10xxxxxx + *pOutput = ((unic >> 6) & 0x1F) | 0xC0; + *(pOutput + 1) = (unic & 0x3F) | 0x80; + return 2; + } + else if (unic >= 0x00000800 && unic <= 0x0000FFFF) + { + // U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx + *pOutput = ((unic >> 12) & 0x0F) | 0xE0; + *(pOutput + 1) = ((unic >> 6) & 0x3F) | 0x80; + *(pOutput + 2) = (unic & 0x3F) | 0x80; + return 3; + } + else if (unic >= 0x00010000 && unic <= 0x001FFFFF) + { + // U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + *pOutput = ((unic >> 18) & 0x07) | 0xF0; + *(pOutput + 1) = ((unic >> 12) & 0x3F) | 0x80; + *(pOutput + 2) = ((unic >> 6) & 0x3F) | 0x80; + *(pOutput + 3) = (unic & 0x3F) | 0x80; + return 4; + } + else if (unic >= 0x00200000 && unic <= 0x03FFFFFF) + { + // U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + *pOutput = ((unic >> 24) & 0x03) | 0xF8; + *(pOutput + 1) = ((unic >> 18) & 0x3F) | 0x80; + *(pOutput + 2) = ((unic >> 12) & 0x3F) | 0x80; + *(pOutput + 3) = ((unic >> 6) & 0x3F) | 0x80; + *(pOutput + 4) = (unic & 0x3F) | 0x80; + return 5; + } + else if (unic >= 0x04000000 && unic <= 0x7FFFFFFF) + { + // U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + *pOutput = ((unic >> 30) & 0x01) | 0xFC; + *(pOutput + 1) = ((unic >> 24) & 0x3F) | 0x80; + *(pOutput + 2) = ((unic >> 18) & 0x3F) | 0x80; + *(pOutput + 3) = ((unic >> 12) & 0x3F) | 0x80; + *(pOutput + 4) = ((unic >> 6) & 0x3F) | 0x80; + *(pOutput + 5) = (unic & 0x3F) | 0x80; + return 6; + } + return 0; +} + +// ----------------------------------------------------------------------------------- + +int enc_get_utf8_size(const unsigned char pInput) +{ + unsigned char c = pInput; + // 0xxxxxxx 返回0 0x0 + // 10xxxxxx 不存在 0x80 + // 110xxxxx 返回2 0xC0 + // 1110xxxx 返回3 0xE0 + // 11110xxx 返回4 0xF0 + // 111110xx 返回5 0xF8 + // 1111110x 返回6 0xFC + if (c < 0x80) + return 1; + if (c >= 0x80 && c < 0xC0) + return -1; + if (c >= 0xC0 && c < 0xE0) + return 2; + if (c >= 0xE0 && c < 0xF0) + return 3; + if (c >= 0xF0 && c < 0xF8) + return 4; + if (c >= 0xF8 && c < 0xFC) + return 5; + if (c >= 0xFC) + return 6; + return 1; +} +/***************************************************************************** +* 将一个字符的UTF8编码转换成Unicode(UCS-2和UCS-4)编码. +* +* 参数: +* pInput 指向输入缓冲区, 以UTF-8编码 +* Unic 指向输出缓冲区, 其保存的数据即是Unicode编码值, +* 类型为ulong . +* +* 返回值: +* 成功则返回该字符的Unicode编码所占用的字节数; 失败则返回0. +* +* 注意: +* 1. UTF8没有字节序问题, 但是Unicode有字节序要求; +* 字节序分为大端(Big Endian)和小端(Little Endian)两种; +* 在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位) +****************************************************************************/ +int enc_utf8_to_unicode_one(const uchar *pInput, uchar *Unic) +{ + assert(pInput != NULL && Unic != NULL); + + // b1 表示UTF-8编码的pInput中的高字节, b2 表示次高字节, ... + char b1, b2, b3, b4, b5, b6; + + *Unic = 0x0; // 把 *Unic 初始化为全零 + int utfbytes = enc_get_utf8_size(*pInput); + uchar *pOutput = (uchar *)Unic; + + switch (utfbytes) + { + case 1: //1字节 + *pOutput = *pInput; + break; + case 2: //2字节 + b1 = *pInput; + b2 = *(pInput + 1); + if ((b2 & 0xC0) != 0x80) + return 0; + *pOutput = (b1 << 6) + (b2 & 0x3F); + *(pOutput + 1) = (b1 >> 2) & 0x07; + break; + case 3: + b1 = *pInput; + b2 = *(pInput + 1); + b3 = *(pInput + 2); + if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)) + return 0; + *pOutput = (b2 << 6) + (b3 & 0x3F); + *(pOutput + 1) = (b1 << 4) + ((b2 >> 2) & 0x0F); + break; + case 4: + b1 = *pInput; + b2 = *(pInput + 1); + b3 = *(pInput + 2); + b4 = *(pInput + 3); + if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) || ((b4 & 0xC0) != 0x80)) + return 0; + *pOutput = (b3 << 6) + (b4 & 0x3F); + *(pOutput + 1) = (b2 << 4) + ((b3 >> 2) & 0x0F); + *(pOutput + 2) = ((b1 << 2) & 0x1C) + ((b2 >> 4) & 0x03); + break; + case 5: + b1 = *pInput; + b2 = *(pInput + 1); + b3 = *(pInput + 2); + b4 = *(pInput + 3); + b5 = *(pInput + 4); + if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80)) + return 0; + *pOutput = (b4 << 6) + (b5 & 0x3F); + *(pOutput + 1) = (b3 << 4) + ((b4 >> 2) & 0x0F); + *(pOutput + 2) = (b2 << 2) + ((b3 >> 4) & 0x03); + *(pOutput + 3) = (b1 << 6); + break; + case 6: + b1 = *pInput; + b2 = *(pInput + 1); + b3 = *(pInput + 2); + b4 = *(pInput + 3); + b5 = *(pInput + 4); + b6 = *(pInput + 5); + if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80) || ((b6 & 0xC0) != 0x80)) + return 0; + *pOutput = (b5 << 6) + (b6 & 0x3F); + *(pOutput + 1) = (b5 << 4) + ((b6 >> 2) & 0x0F); + *(pOutput + 2) = (b3 << 2) + ((b4 >> 4) & 0x03); + *(pOutput + 3) = ((b1 << 6) & 0x40) + (b2 & 0x3F); + break; + default: + utfbytes = 0; + break; + } + return utfbytes; +} + +// ----------------------------------------------------------------------------------- + +char *enc_u2u8(char *data, uint *len) +{ + size_t t, i; + membuf_t buf; + membuf_init(&buf, 128); + (*len)--; + for (i = 0; i <= *len;) + { + if (buf.buffer_size - buf.size < 7) + membuf_reserve(&buf, 7); + t = enc_unicode_to_utf8_one(*(uint *)(data + i), (buf.data + buf.size), 7); + if (t == 0) + break; + buf.size += t; + } + membuf_trunc(&buf); + *len = buf.size; + return (char *)buf.data; +} + +// ----------------------------------------------------------------------------------- + +char *enc_u82u(char *data, uint *len) +{ + size_t t, i; + membuf_t buf; + membuf_init(&buf, 128); + for (i = 0; i < *len;) + { + if (buf.buffer_size - buf.size < 4) + membuf_reserve(&buf, 4); + t = enc_utf8_to_unicode_one((uchar *)(data + i), (uchar *)(buf.data + buf.size)); + if (t == 0) + break; + buf.size += 2; + i += t; + } + membuf_trunc(&buf); + *len = buf.size; + return (char *)buf.data; +} + +// ----------------------------------------------------------------------------------- + +//代码转换:从一种编码转为另一种编码 +size_t code_convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen, char *outbuf, uint *outlen) +{ + iconv_t cd; + size_t rc = 0, len = *outlen; + char **pin = &inbuf; + char **pout = &outbuf; + + cd = iconv_open(to_charset, from_charset); + if (cd == 0) + return -1; + memset(outbuf, 0, len); + if (iconv(cd, pin, (size_t *)&inlen, pout, (size_t *)&len) == -1) + rc = -1; + iconv_close(cd); + *outlen -= len; //返回已用长度 + return rc; +} + +// ----------------------------------------------------------------------------------- + +//GB2312 to unicode(need free) 返回字串长度为:实际长度+1, 末尾\0站一字节(需要释放) +char *GB2U(char *pszGbs, uint *aLen) +{ + size_t len = *aLen * 4; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("gb2312", "unicode", pszGbs, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +// ----------------------------------------------------------------------------------- + +//unicode to utf8(need free) 返回字串长度为:实际长度+1, 末尾\0站一字节(需要释放) +char *U2U8(char *wszUnicode, uint *aLen) +{ + size_t len = *aLen; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("unicode", "utf-8", wszUnicode, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +// ----------------------------------------------------------------------------------- + +//utf8 to unicode(need free) 返回字串长度为:实际长度+1, 末尾\0站一字节(需要释放) +char *U82U(char *szU8, uint *aLen) +{ + size_t len = *aLen * 2; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("utf-8", "unicode", szU8, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +// ----------------------------------------------------------------------------------- + +//unicode to GB2312(need free) 返回字串(需要释放)长度为:实际长度+1, 末尾\0站一字节 +char *U2GB(char *wszUnicode, uint *aLen) +{ + size_t len = *aLen; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("unicode", "gb2312", wszUnicode, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +// ----------------------------------------------------------------------------------- + +//GB2312 to utf8(need free) 返回字串(需要释放)长度为:实际长度+1, 末尾\0站一字节 +char *GB2U8(char *pszGbs, uint *aLen) +{ + size_t len = *aLen * 3; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("gb2312", "utf-8", pszGbs, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +// ----------------------------------------------------------------------------------- + +//utf8 to GB2312(need free) 返回字串(需要释放)长度为:实际长度+1, 末尾\0站一字节 +char *U82GB(char *szU8, uint *aLen) +{ + size_t len = *aLen; + char *outbuf = (char *)malloc(len + 1); + outbuf[0] = 0; + size_t rc = code_convert("utf-8", "gb2312", szU8, *aLen, outbuf, &len); + if (rc < 0) + *aLen = rc; + else + *aLen = len + 1; + return outbuf; +} + +/*************************************************************************** +* 函数名称: UTF8ToUCS2 +* 功能描述: 转换UTF8格式到UCS2格式(UCS2是双字节编码,Unicode是其中一种) +* 日 期: 2008-05-22 13:36:56 +* 作 者: lianxiuzhu +* 参数说明: binUTF8 - UTF8字节流数组 +* uCount - UTF8字节流数组中的字节数 +* binUCS2 - UCS2字节流数组 +* 返 回 值: 转换到UCS2字节流数组中的U16单元个数 +***************************************************************************/ +size_t UTF8ToUCS2(const uchar *binUTF8, size_t uCount, ushort *binUCS2) +{ + size_t uLength = 0; + uchar *szTemp = (uchar *)binUTF8; + while ((uint)(szTemp - binUTF8) < uCount) + { + if (*szTemp <= 0x7F) //0xxxxxxx + { + binUCS2[uLength] = binUCS2[uLength] | (ushort)(*szTemp & 0x7F); + szTemp = szTemp + 1; + } + else if (*szTemp <= 0xDF) //110xxxxx 10xxxxxx + { + binUCS2[uLength] = binUCS2[uLength] | (ushort)(*(szTemp + 1) & 0x3F); + binUCS2[uLength] = binUCS2[uLength] | ((ushort)(*(szTemp)&0x1F) << 6); + szTemp = szTemp + 2; + } + else if (*szTemp <= 0xEF) //1110xxxx 10xxxxxx 10xxxxxx + { + binUCS2[uLength] = binUCS2[uLength] | (ushort)(*(szTemp + 2) & 0x3F); + binUCS2[uLength] = binUCS2[uLength] | ((ushort)(*(szTemp + 1) & 0x3F) << 6); + binUCS2[uLength] = binUCS2[uLength] | ((ushort)(*(szTemp)&0x0F) << 12); + szTemp = szTemp + 3; + } + else + { + return 0; + } + + uLength++; + } + return uLength; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------url编码解码 win/linux +#pragma region url编码解码 + +// ----------------------------------------------------------------------------------- + +//url编码 (len为buf的长度) +char *url_encode(const char *url, uint *len) +{ + if (!url) + return NULL; + membuf_t buf; + const char *p; + const char urlunsafe[] = "\r\n \"#%&+:;<=>?@[\\]^`{|}"; + const char hex[] = "0123456789ABCDEF"; + char enc[3] = {'%', 0, 0}; + len--; + membuf_init(&buf, strlen(url) + 1); + for (p = url; *p; p++) + { + if ((p - url) > *len) + break; + if (*p < ' ' || *p > '~' || strchr(urlunsafe, *p)) + { + enc[1] = hex[*p >> 4]; + enc[2] = hex[*p & 0x0f]; + membuf_append_data(&buf, enc, 3); + } + else + { + membuf_append_data(&buf, p, 1); + } + } + membuf_trunc(&buf); + *len = buf.size; + return (char *)buf.data; +} + +// ----------------------------------------------------------------------------------- + +//url解码 +char *url_decode(char *url) +{ + char *o, *s; + uint tmp; + + for (o = s = url; *s; s++, o++) + { + if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) + { + *o = (char)tmp; + s += 2; + } + else + { + *o = *s; + } + } + *o = '\0'; + return url; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------Base64编码解码 win/linux +#pragma region Base64编码解码 + +char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +char base64_end = '='; + +// ----------------------------------------------------------------------------------- + +inline char is_base64(uchar c) +{ + return (isalnum(c) || (c == '+') || (c == '/')); +} + +// ----------------------------------------------------------------------------------- + +//Base64编码,需要释放返回值(need free return) +char *base64_Encode(const uchar *bytes_to_encode, uint in_len) +{ + membuf_t ret; + int i = 0, j = 0; + uchar char_array_3[3]; + uchar char_array_4[4]; + + membuf_init(&ret, in_len * 3); //初始化缓存字节数为 长度的3被 + + while (in_len--) + { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) + { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i < 4); i++) + membuf_append_data(&ret, &base64_table[char_array_4[i]], 1); + i = 0; + } + } + + if (i) + { + for (j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + membuf_append_data(&ret, &base64_table[char_array_4[j]], 1); + + while ((i++ < 3)) + membuf_append_data(&ret, &base64_end, 1); + } + return (char *)ret.data; +} + +// ----------------------------------------------------------------------------------- + +//Base64解码,需要释放返回值(need free return) +char *base64_Decode(const char *encoded_string) +{ + size_t in_len = strlen(encoded_string); + int i = 0; + int j = 0; + size_t in_ = 0; + uchar char_array_4[4], char_array_3[3]; + membuf_t ret; + membuf_init(&ret, strlen(encoded_string) / 3 + 1); + + while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) + { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) + { + for (i = 0; i < 4; i++) + //char_array_4[i] = strstr(base64_table,(char*)&char_array_4[i])[0]; + char_array_4[i] = strchr(base64_table, char_array_4[i]) - base64_table; + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + membuf_append_data(&ret, &char_array_3[i], 1); + i = 0; + } + } + + if (i) + { + for (j = i; j < 4; j++) + char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + //char_array_4[j] = strstr(base64_table, (char*)&char_array_4[j])[0]; + char_array_4[j] = strchr(base64_table, char_array_4[j]) - base64_table; + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) + membuf_append_data(&ret, &char_array_3[j], 1); + } + return (char *)ret.data; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------Hash1加密 win/linux +#pragma region Hash1加密 + +/**************** +* Rotate a 32 bit integer by n bytes +****************/ +#if defined(__GNUC__) && defined(__i386__) +static inline u32 rol(u32 x, int n) +{ + __asm__("roll %%cl,%0" + : "=r"(x) + : "0"(x), "c"(n)); + return x; +} +#else +#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) +#endif + +// ----------------------------------------------------------------------------------- + +void hash1_Reset(SHA1_CONTEXT *hd) +{ + hd->bFinal = 0; + hd->h0 = 0x67452301; + hd->h1 = 0xefcdab89; + hd->h2 = 0x98badcfe; + hd->h3 = 0x10325476; + hd->h4 = 0xc3d2e1f0; + hd->nblocks = 0; + hd->count = 0; + memset(hd->buf, 0, 64); +} + +/* +* Transform the message X which consists of 16 32-bit-words +*/ +static void hash1_transform(SHA1_CONTEXT *hd, uchar *data) +{ + uint a, b, c, d, e, tm; + uint x[16]; + + /* get values from the chaining vars */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + +#ifdef BIG_ENDIAN_HOST + memcpy(x, data, 64); +#else + { + int i; + uchar *p2; + for (i = 0, p2 = (uchar *)x; i < 16; i++, p2 += 4) + { + p2[3] = *data++; + p2[2] = *data++; + p2[1] = *data++; + p2[0] = *data++; + } + } +#endif + +#define K1 0x5A827999L +#define K2 0x6ED9EBA1L +#define K3 0x8F1BBCDCL +#define K4 0xCA62C1D6L +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) (x ^ y ^ z) +#define F3(x, y, z) ((x & y) | (z & (x | y))) +#define F4(x, y, z) (x ^ y ^ z) + +#define M(i) (tm = x[i & 0x0f] ^ x[(i - 14) & 0x0f] ^ x[(i - 8) & 0x0f] ^ x[(i - 3) & 0x0f], (x[i & 0x0f] = rol(tm, 1))) + +#define R(a, b, c, d, e, f, k, m) \ + do \ + { \ + e += rol(a, 5) + f(b, c, d) + k + m; \ + b = rol(b, 30); \ + } while (0) + + R(a, b, c, d, e, F1, K1, x[0]); + R(e, a, b, c, d, F1, K1, x[1]); + R(d, e, a, b, c, F1, K1, x[2]); + R(c, d, e, a, b, F1, K1, x[3]); + R(b, c, d, e, a, F1, K1, x[4]); + R(a, b, c, d, e, F1, K1, x[5]); + R(e, a, b, c, d, F1, K1, x[6]); + R(d, e, a, b, c, F1, K1, x[7]); + R(c, d, e, a, b, F1, K1, x[8]); + R(b, c, d, e, a, F1, K1, x[9]); + R(a, b, c, d, e, F1, K1, x[10]); + R(e, a, b, c, d, F1, K1, x[11]); + R(d, e, a, b, c, F1, K1, x[12]); + R(c, d, e, a, b, F1, K1, x[13]); + R(b, c, d, e, a, F1, K1, x[14]); + R(a, b, c, d, e, F1, K1, x[15]); + R(e, a, b, c, d, F1, K1, M(16)); + R(d, e, a, b, c, F1, K1, M(17)); + R(c, d, e, a, b, F1, K1, M(18)); + R(b, c, d, e, a, F1, K1, M(19)); + R(a, b, c, d, e, F2, K2, M(20)); + R(e, a, b, c, d, F2, K2, M(21)); + R(d, e, a, b, c, F2, K2, M(22)); + R(c, d, e, a, b, F2, K2, M(23)); + R(b, c, d, e, a, F2, K2, M(24)); + R(a, b, c, d, e, F2, K2, M(25)); + R(e, a, b, c, d, F2, K2, M(26)); + R(d, e, a, b, c, F2, K2, M(27)); + R(c, d, e, a, b, F2, K2, M(28)); + R(b, c, d, e, a, F2, K2, M(29)); + R(a, b, c, d, e, F2, K2, M(30)); + R(e, a, b, c, d, F2, K2, M(31)); + R(d, e, a, b, c, F2, K2, M(32)); + R(c, d, e, a, b, F2, K2, M(33)); + R(b, c, d, e, a, F2, K2, M(34)); + R(a, b, c, d, e, F2, K2, M(35)); + R(e, a, b, c, d, F2, K2, M(36)); + R(d, e, a, b, c, F2, K2, M(37)); + R(c, d, e, a, b, F2, K2, M(38)); + R(b, c, d, e, a, F2, K2, M(39)); + R(a, b, c, d, e, F3, K3, M(40)); + R(e, a, b, c, d, F3, K3, M(41)); + R(d, e, a, b, c, F3, K3, M(42)); + R(c, d, e, a, b, F3, K3, M(43)); + R(b, c, d, e, a, F3, K3, M(44)); + R(a, b, c, d, e, F3, K3, M(45)); + R(e, a, b, c, d, F3, K3, M(46)); + R(d, e, a, b, c, F3, K3, M(47)); + R(c, d, e, a, b, F3, K3, M(48)); + R(b, c, d, e, a, F3, K3, M(49)); + R(a, b, c, d, e, F3, K3, M(50)); + R(e, a, b, c, d, F3, K3, M(51)); + R(d, e, a, b, c, F3, K3, M(52)); + R(c, d, e, a, b, F3, K3, M(53)); + R(b, c, d, e, a, F3, K3, M(54)); + R(a, b, c, d, e, F3, K3, M(55)); + R(e, a, b, c, d, F3, K3, M(56)); + R(d, e, a, b, c, F3, K3, M(57)); + R(c, d, e, a, b, F3, K3, M(58)); + R(b, c, d, e, a, F3, K3, M(59)); + R(a, b, c, d, e, F4, K4, M(60)); + R(e, a, b, c, d, F4, K4, M(61)); + R(d, e, a, b, c, F4, K4, M(62)); + R(c, d, e, a, b, F4, K4, M(63)); + R(b, c, d, e, a, F4, K4, M(64)); + R(a, b, c, d, e, F4, K4, M(65)); + R(e, a, b, c, d, F4, K4, M(66)); + R(d, e, a, b, c, F4, K4, M(67)); + R(c, d, e, a, b, F4, K4, M(68)); + R(b, c, d, e, a, F4, K4, M(69)); + R(a, b, c, d, e, F4, K4, M(70)); + R(e, a, b, c, d, F4, K4, M(71)); + R(d, e, a, b, c, F4, K4, M(72)); + R(c, d, e, a, b, F4, K4, M(73)); + R(b, c, d, e, a, F4, K4, M(74)); + R(a, b, c, d, e, F4, K4, M(75)); + R(e, a, b, c, d, F4, K4, M(76)); + R(d, e, a, b, c, F4, K4, M(77)); + R(c, d, e, a, b, F4, K4, M(78)); + R(b, c, d, e, a, F4, K4, M(79)); + + /* Update chaining vars */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; +} + +// ----------------------------------------------------------------------------------- + +// Update the message digest with the contents of INBUF with length INLEN. +void hash1_Write(SHA1_CONTEXT *hd, uchar *inbuf, size_t inlen) +{ + if (hd->bFinal) + hash1_Reset(hd); + if (hd->count == 64) + { /* flush the buffer */ + hash1_transform(hd, hd->buf); + hd->count = 0; + hd->nblocks++; + } + if (!inbuf) + return; + if (hd->count) + { + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; + hash1_Write(hd, NULL, 0); + if (!inlen) + return; + } + + while (inlen >= 64) + { + hash1_transform(hd, inbuf); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; +} + + +// ----------------------------------------------------------------------------------- + +/* The routine final terminates the computation and returns the digest. +* The handle is prepared for a new cycle, but adding bytes to the +* handle will the destroy the returned buffer. +* Returns: 20 bytes representing the digest. +*/ +void hash1_Final(SHA1_CONTEXT *hd) +{ + uint t, msb, lsb; + uchar *p; + + hash1_Write(hd, NULL, 0); /* flush */ + ; + + t = hd->nblocks; + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = t >> 26; + /* add the count */ + t = lsb; + if ((lsb += hd->count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if (hd->count < 56) + { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while (hd->count < 56) + hd->buf[hd->count++] = 0; /* pad */ + } + else + { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while (hd->count < 64) + hd->buf[hd->count++] = 0; + hash1_Write(hd, NULL, 0); /* flush */ + ; + memset(hd->buf, 0, 56); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = msb >> 24; + hd->buf[57] = msb >> 16; + hd->buf[58] = msb >> 8; + hd->buf[59] = msb; + hd->buf[60] = lsb >> 24; + hd->buf[61] = lsb >> 16; + hd->buf[62] = lsb >> 8; + hd->buf[63] = lsb; + hash1_transform(hd, hd->buf); + + p = hd->buf; +#ifdef BIG_ENDIAN_HOST +#define X(a) \ + do \ + { \ + *(u32 *)p = hd->h##a; \ + p += 4; \ + } while (0) +#else /* little endian */ +#define X(a) \ + do \ + { \ + *p++ = hd->h##a >> 24; \ + *p++ = hd->h##a >> 16; \ + *p++ = hd->h##a >> 8; \ + *p++ = hd->h##a; \ + } while (0) +#endif + X(0); + X(1); + X(2); + X(3); + X(4); +#undef X + //Hash1 operation finally + hd->bFinal = 1; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------WebSocket win/linux +#pragma region WebSocket Tool + + +// ----------------------------------------------------------------------------------- + +//WebSocket握手Key计算 +char *WebSocketHandShak(const char *key) +{ + char akey[137] = {0}; + char acc[165] = {0}, *p; + int len; + SHA1_CONTEXT hd; + // + //char* p=strstr(header, "Sec-WebSocket-Key:");//不需要查找 Upgrade:为 "websocket" + //if (p) + //{ + //p += 19; + //char* p2=strst(p, "\r\n"); + strncpy(akey, key, 99); + strncpy(akey + strlen(key), "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36); + + hash1_Reset(&hd); + hash1_Write(&hd, (uchar *)akey, strlen(akey)); + hash1_Final(&hd); + p = base64_Encode(hd.buf, 20); + len = snprintf(acc, 164, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n", p); + free(p); + + p = (char *)malloc(len + 1); + memcpy(p, acc, len); + p[len] = 0; + return p; + //} + //else + // return NULL; +} + +// ----------------------------------------------------------------------------------- + +inline void WebSocketDoMask(char *data, ulong len, char *mask) +{ + ulong i = 0; + for (i = 0; i < len; i++) + data[i] = data[i] ^ mask[i % 4]; +} + +// ----------------------------------------------------------------------------------- + +//从帧中取得实际数据 +ulong WebSocketGetData(WebSocketHandle *handle, char *data, ulong len) +{ + if (!handle || !data) + return 0; + handle->isEof = (char)(data[0] >> 7); //是否结束 + handle->dfExt = (data[0] & 0x70); //扩展码 + handle->type = data[0] & 0xF; //OPCode + char hasMask = (char)(data[1] >> 7); + char Mask[4]; + membuf_t *buf = &handle->buf; + //Payload长度是ExtensionData长度与ApplicationData长度之和。 + //ExtensionData长度可能是0,这种情况下,Payload长度即是ApplicationData长度(默认ExtensionData长度是0) + ulong tLen; //本次真实数据长度 + ulong Len = data[1] & 0x7f; //Payload长度 + //当前帧,第一截数据 + if (Len < 126) //如果其值在0-125,则是payload的真实长度(ApplicationData长度,ExtensionData长度为0) + { + if (hasMask) + { + if ((len - 6) > 0) //防止结尾帧数据不够长度的错误 + { + memcpy(Mask, &data[2], 4); + tLen = len - 6; + Len = (Len > 0 && Len > tLen) ? tLen : Len; + membuf_append_data(buf, &data[6], Len); + WebSocketDoMask((char *)(buf->data + buf->size - Len), Len, Mask); + } + } + else //没用掩码 + if ((len - 2) > 0) + { + tLen = len - 2; + Len = (Len > 0 && Len > tLen) ? tLen : Len; + membuf_append_data(buf, &data[2], Len); + } + } + else if (Len == 126) //如果值是126,则后面2个字节形成的16位无符号整型数(ushort)的值是payload的真实长度,掩码就紧更着后面 + { + Len = data[2] * 0x100UL + (uchar)data[3]; //逐字节转换 + if (hasMask) + { + if ((len - 8) > 0) + { + memcpy(Mask, &data[4], 4); //防止结尾帧数据不够长度的错误 + tLen = len - 8; + Len = (Len > 0 && Len > tLen) ? tLen : Len; + membuf_append_data(buf, &data[8], Len); + WebSocketDoMask((char *)(buf->data + buf->size - Len), Len, Mask); + } + } + else //没用掩码 + if ((len - 4) > 0) + { + tLen = len - 4; + Len = Len > tLen ? tLen : Len; + membuf_append_data(buf, &data[4], tLen); + } + } + else if (Len == 127) //如果值是127,则后面8个字节形成的64位无符号整型数(uint64)的值是payload的真实长度,掩码就紧更着后面 + { + //Len = data[2] * 0x100000000000000ULL + data[3] * 0x1000000000000ULL + data[4] * 0x10000000000ULL + data[5] * 0x100000000ULL + // +data[6]*0x1000000ULL+data[7]*0x10000ULL+data[8]*0x100ULL+data[9]*0x1ULL;//逐字节转换 + Len = data[6] * 0x1000000ULL + data[7] * 0x10000ULL + data[8] * 0x100ULL + (uchar)data[9]; //逐字节转换为ulong + if (hasMask) + { + if ((len - 14) > 0) //防止结尾帧数据不够长度的错误 + { + memcpy(Mask, &data[10], 4); + tLen = len - 14; + Len = Len > tLen ? tLen : Len; + membuf_append_data(buf, &data[14], Len); + WebSocketDoMask((char *)(buf->data + buf->size - Len), Len, Mask); + } + } + else //没用掩码 + if ((len - 10) > 0) + { + tLen = len - 10; + Len = Len > tLen ? tLen : Len; + membuf_append_data(buf, &data[10], Len); + } + } + return Len; +} + +// ----------------------------------------------------------------------------------- + +//转换为一个WebSocket帧,无mask (need free return) +char *WebSocketMakeFrame(uchar *data, ulong *dlen, uchar op) +{ + if (data == NULL) + return NULL; + membuf_t buf; + membuf_init(&buf, 129); + //第一byte,10000000, fin = 1, rsv1 rsv2 rsv3均为0, opcode = 0x01,即数据为文本帧 + buf.data[0] = 0x80 + op; //0x81 最后一个包 |(无扩展协议)| 控制码(0x1表示文本帧) + if (*dlen > 0) + { //要有数据 + if (*dlen <= 125) + { + //数据长度 + buf.data[1] = (uchar)*dlen; + buf.size = 2; + } + else if (*dlen <= 65535) + { + buf.data[1] = 0x7E; + //数据长度 + buf.data[2] = (*dlen >> 8) & 255; + buf.data[3] = (*dlen) & 255; + buf.size = 4; + } + else + { + buf.data[1] = 0x7F; + //数据长度,前4字节留空,保存32位数据大小 + //buf.data[2] = (*dlen >> 56) & 255; + //buf.data[3] = (*dlen >> 48) & 255; + //buf.data[4] = (*dlen >> 40) & 255; + //buf.data[5] = (*dlen >> 32) & 255; + buf.data[6] = (*dlen >> 24) & 255; + buf.data[7] = (*dlen >> 16) & 255; + buf.data[8] = (*dlen >> 8) & 255; + buf.data[9] = (*dlen) & 255; + buf.size = 10; + } + } + membuf_append_data(&buf, data, *dlen); + *dlen = buf.size; + membuf_trunc(&buf); + return (char *)buf.data; +} + +#pragma endregion + +//-----------------------------------------------------------------------------------工具/杂项 win/linux +#pragma region 工具 / 杂项 + +// ----------------------------------------------------------------------------------- + +inline int day_of_year(int y, int m, int d) +{ + int k, leap, s; + int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + leap = (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); + s = d; + for (k = 1; k < m; k++) + { + s += days[k]; + } + if (leap == 1 && m > 2) + s += 1; + return s; +} + +// ----------------------------------------------------------------------------------- + +//获取格林制(GMT)时间: "Wed, 18 Jul 2018 06:02:42 GMT" +//szDate: 存放GMT时间的缓存区(至少 char[30]),外部传入 +//szLen: szDate的长度大小 +//addSecond: 当前时间加上多少秒 +char *getGmtTime(char *szDate, int szLen, int addSecond) +{ + time_t rawTime; + struct tm *timeInfo; + time(&rawTime); + rawTime += addSecond; + timeInfo = gmtime(&rawTime); + strftime(szDate, szLen, "%a, %d %b %Y %H:%M:%S GMT", timeInfo); + return szDate; +} + +// ----------------------------------------------------------------------------------- + +//字符串转换成时间戳(秒),字符串格式为:"2016-08-03 06:56:36" +llong str2stmp(const char *strTime) +{ + if (strTime != NULL) + { + struct tm sTime; + memset(&sTime, 0, sizeof(struct tm)); + sTime.tm_isdst = -1; // daylight savings time flag +#ifdef __GNUC__ + if (strchr(strTime, '-')) + { + if (strlen(strTime) > 10) + strptime(strTime, "%Y-%m-%d %H:%M:%S", &sTime); + else + strptime(strTime, "%Y-%m-%d", &sTime); + } + else + { + if (strlen(strTime) > 10) + strptime(strTime, "%Y/%m/%d %H:%M:%S", &sTime); + else + strptime(strTime, "%Y/%m/%d", &sTime); + } +#else + if (strlen(strTime) > 10) + { + if (strchr(strTime, '-')) + sscanf(strTime, "%d-%d-%d %d:%d:%d", &sTime.tm_year, &sTime.tm_mon, &sTime.tm_mday, &sTime.tm_hour, &sTime.tm_min, &sTime.tm_sec); + else + sscanf(strTime, "%d/%d/%d %d:%d:%d", &sTime.tm_year, &sTime.tm_mon, &sTime.tm_mday, &sTime.tm_hour, &sTime.tm_min, &sTime.tm_sec); + } + else + { + if (strchr(strTime, '-')) + sscanf(strTime, "%d-%d-%d", &sTime.tm_year, &sTime.tm_mon, &sTime.tm_mday); + else + sscanf(strTime, "%d/%d/%d", &sTime.tm_year, &sTime.tm_mon, &sTime.tm_mday); + } + sTime.tm_year -= 1900; + sTime.tm_mon -= 1; + if (sTime.tm_year > 1100) //windows 下不能超过 3000-12-31, 千年虫 + sTime.tm_year = 1100; +#endif + return mktime(&sTime); + } + else + { + return time(0); + } +} + +// ----------------------------------------------------------------------------------- + +//时间戳(秒)转换成字符串,字符串格式为:"2016-08-03 06:56:36" +char *stmp2str(llong t, char *str, int strlen) +{ + if (t < 1000000) + t = time(0); + struct tm *sTime = localtime((time_t *)&t); + if (sTime) + strftime(str, strlen, "%Y-%m-%d %H:%M:%S", sTime); + return str; +} + + +// ----------------------------------------------------------------------------------- + +//从头比较字符串,返回相同的长度,不区分大小写 +size_t strinstr(const char *s1, const char *s2) +{ + const char *cur = s1; + while (s1 && *s1 > 0 && s2 && *s2 > 0) + { + if (*s1 == *s2 || (isalpha(*s1) && isalpha(*s2) && abs(*s1 - *s2) == 32)) + s1++, s2++; + else + break; + } + return s1 - cur; +} + +// ----------------------------------------------------------------------------------- + +//int32 转二进制字符串 +char *u2b(uint n) +{ + static char b[33] = {0}; + b[31] = '0'; + int i = 31; + uint p = 1; + for (; i >= 0; i--, p <<= 1) + { + b[i] = (n & p) ? '1' : '0'; + } + return b; +} + +// ----------------------------------------------------------------------------------- + +//int64 转二进制字符串 +char *u2b64(ullong n) +{ + static char b[65] = {0}; + b[63] = '0'; + int i = 63; + ullong p = 1; + for (; i >= 0; i--, p <<= 1) + { + b[i] = (n & p) ? '1' : '0'; + } + return b; +} + +// ----------------------------------------------------------------------------------- + +//获取当前时间信息 +tm_u GetLocaTime() +{ + struct timeval tv; //timespec + struct tm *p; + tm_u tmu; + + gettimeofday(&tv, NULL); + p = localtime(&tv.tv_sec); + + tmu.tm_sec = p->tm_sec; + tmu.tm_min = p->tm_min; + tmu.tm_hour = p->tm_hour; + tmu.tm_mday = p->tm_mday; + tmu.tm_mon = p->tm_mon + 1; + tmu.tm_year = p->tm_year + 1900; + tmu.tm_wday = p->tm_wday; + tmu.tm_yday = p->tm_yday; + tmu.tm_isdst = p->tm_isdst; + tmu.tm_vsec = tv.tv_sec; + tmu.tm_usec = tv.tv_usec; + return tmu; +} + +// ----------------------------------------------------------------------------------- + +//获取当天已逝去的秒数 +size_t GetDaySecond() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec % 86400); +} + +// ----------------------------------------------------------------------------------- + +static char CurIPv4[17] = {0}; +static char CurIPv6[50] = {0}; +static char CurMac[25] = {0}; + +#include +#include +#include +#include + +// ----------------------------------------------------------------------------------- + +//#include +//获取 IP,MAC 参见 http://www.cnblogs.com/lzpong/p/6956439.html +//获取 MAC 地址 +static char *getMac(char *mac, char *dv) +{ + struct ifreq ifreq; + int sock; + if (!mac || !dv) + return mac; + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket "); + return mac; + } + strcpy(ifreq.ifr_name, dv); + if (ioctl(sock, SIOCGIFHWADDR, &ifreq) < 0) + { + perror("ioctl "); + return mac; + } + sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", //以太网MAC地址的长度是48位 + (unsigned char)ifreq.ifr_hwaddr.sa_data[0], + (unsigned char)ifreq.ifr_hwaddr.sa_data[1], + (unsigned char)ifreq.ifr_hwaddr.sa_data[2], + (unsigned char)ifreq.ifr_hwaddr.sa_data[3], + (unsigned char)ifreq.ifr_hwaddr.sa_data[4], + (unsigned char)ifreq.ifr_hwaddr.sa_data[5]); + return mac; +} + +// ----------------------------------------------------------------------------------- + +//获取IP地址 +static int GetIP_v4_and_v6_linux(int family) +{ + struct ifaddrs *ifaplist = NULL, *ifap = NULL; + void *tmpAddrPtr = NULL; + + getifaddrs(&ifaplist); + ifap = ifaplist; + while (ifap != NULL) + { + if (ifap->ifa_addr->sa_family == family) + { //AF_INET check it is IP4 + // is a valid IP4 Address + tmpAddrPtr = &((struct sockaddr_in *)ifap->ifa_addr)->sin_addr; + inet_ntop(AF_INET, tmpAddrPtr, CurIPv4, INET_ADDRSTRLEN); + if (strcmp(CurIPv4, "127.0.0.1") != 0) + { + getMac(CurMac, ifap->ifa_name); + break; + } + } + else if (ifap->ifa_addr->sa_family == family) + { //AF_INET6 check it is IP6 + // is a valid IP6 Address + tmpAddrPtr = &((struct sockaddr_in *)ifap->ifa_addr)->sin_addr; + inet_ntop(AF_INET6, tmpAddrPtr, CurIPv6, INET6_ADDRSTRLEN); + if (strcmp(CurIPv6, "::") != 0 && strcmp(CurIPv6, "::1") != 0) + { + getMac(CurMac, ifap->ifa_name); + break; + } + } + ifap = ifap->ifa_next; + } + if (ifaplist) + { + freeifaddrs(ifaplist); + ifaplist = NULL; + } + return -1; +} + +// ----------------------------------------------------------------------------------- + +//获取网卡地址 +const char *GetMacAddr() +{ + if (CurMac[0] < 1) + GetIP_v4_and_v6_linux(AF_INET); + return CurMac; +} + +// ----------------------------------------------------------------------------------- + +//获取IPv4地址 (第一个IPv4) +const char *GetIPv4() +{ + if (CurIPv4[0] < 1) + GetIP_v4_and_v6_linux(AF_INET); + return CurIPv4; +} + +// ----------------------------------------------------------------------------------- + +//获取IPv6地址 (第一个IPv6) +const char *GetIPv6() +{ + if (CurIPv6[0] < 1) + GetIP_v4_and_v6_linux(AF_INET6); + return CurIPv6; +} + +#pragma endregion diff --git a/src/tinyweb/tools.h b/src/tinyweb/tools.h new file mode 100644 index 0000000..9619542 --- /dev/null +++ b/src/tinyweb/tools.h @@ -0,0 +1,263 @@ +#pragma once +#ifndef __TOOLS_H__ +#define __TOOLS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef long long llong; +typedef unsigned long long ullong; + +#define bitAdd(a, b) ((a) | (b)) +#define bitHas(a, b) ((a) & (b)) +#define bitRemove(a, b) ((a) & ~(b)) +#define Min(a, b) (((a) > (b)) ? (b) : (a)) +#define Max(a, b) (((a) > (b)) ? (a) : (b)) + +//-----------------------------------------------------------------------------------membuf c-str win/unix + +typedef struct membuf_t +{ + uchar *data; + size_t size; + size_t buffer_size; +} membuf_t; + +// ----------------------------------------------------------------------------------- + +void membuf_init(membuf_t *buf, size_t initial_buffer_size); +void membuf_uninit(membuf_t *buf); +size_t membuf_append(membuf_t *buf, const char *str); +size_t membuf_append_data(membuf_t *buf, const void *data, size_t size); +size_t membuf_append_format(membuf_t *buf, const char *fmt, ...); +void membuf_insert(membuf_t *buf, size_t offset, void *data, size_t size); +void membuf_move(membuf_t *buf, size_t offset, size_t size); +void membuf_clear(membuf_t *buf, size_t maxSize); +void membuf_reserve(membuf_t *buf, size_t extra_size); +void membuf_trunc(membuf_t *buf); + +#define _INLINE static inline + +//-----------------------------------------------------------------------------------membuf c-str win/unix + +_INLINE size_t membuf_append_byte(membuf_t *buf, uchar b) +{ + return membuf_append_data(buf, &b, sizeof(b)); +} + +_INLINE size_t membuf_append_int(membuf_t *buf, int i) +{ + return membuf_append_data(buf, &i, sizeof(i)); +} + +_INLINE size_t membuf_append_uint(membuf_t *buf, uint ui) +{ + return membuf_append_data(buf, &ui, sizeof(ui)); +} + +_INLINE size_t membuf_append_long(membuf_t *buf, long i) +{ + return membuf_append_data(buf, &i, sizeof(i)); +} + +_INLINE size_t membuf_append_ulong(membuf_t *buf, ulong ui) +{ + return membuf_append_data(buf, &ui, sizeof(ui)); +} + +_INLINE size_t membuf_append_short(membuf_t *buf, short s) +{ + return membuf_append_data(buf, &s, sizeof(s)); +} + +_INLINE size_t membuf_append_ushort(membuf_t *buf, ushort us) +{ + return membuf_append_data(buf, &us, sizeof(us)); +} + +_INLINE size_t membuf_append_float(membuf_t *buf, float f) +{ + return membuf_append_data(buf, &f, sizeof(f)); +} + +_INLINE size_t membuf_append_double(membuf_t *buf, double d) +{ + return membuf_append_data(buf, &d, sizeof(d)); +} + +_INLINE size_t membuf_append_ptr(membuf_t *buf, void *ptr) +{ + return membuf_append_data(buf, &ptr, sizeof(ptr)); +} + +//-----------------------------------------------------------------------------------File / folder detection win/unix + +//获取工作目录路径,不带'/' +char *getWorkPath(); + +//获取程序文件所在路径,不带'/' +char *getProcPath(); + +//建立目录,递归建立 (mod: linux系统需要,权限模式,, windows系统不需要) +int makeDir(const char *path, int mod); +//路径是否存在(0:不存在 1:存在:文件 2:存在:文件夹) +char isExist(const char *path); +//是否文件(1:是文件 0:非文件/不存在) +char isFile(const char *path); +//是否目录(1:是目录 0;非目录/不存在) +char isDir(const char *path); + +//返回列表目录Json字符串,need free the return +//{"path":"/","files":[{"name":"file.txt","mtime":"2014-04-18 23:24:05","size":463,"type":"F"}]} +char *listDir(const char *fullpath, const char *reqPath); + +//----------------------------------------------------------------------------------- Transcoding win/unix + +//GB2312 to unicode(need free return) 返回字串(需要释放)长度为:实际长度+2,返回长度小于0为:失败, 末尾\0\0占一字节 +char *GB2U(char *pszGbs, uint *aLen); +//unicode to utf8(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为:失败, 末尾\0占一字节 +char *U2U8(char *wszUnicode, uint *aLen); +//utf8 to unicode(need free return) 返回字串(需要释放)长度为:实际长度+2,返回长度小于0为:失败, 末尾\0\0占一字节 +char *U82U(char *szU8, uint *aaLen); +//unicode to GB2312(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为:失败, 末尾\0占一字节 +char *U2GB(char *wszUnicode, uint *aLen); + +//GB2312 to utf8(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为:失败, 末尾\0占一字节 +char *GB2U8(char *pszGbs, uint *aLen); +//utf8 to GB2312(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为:失败, 末尾\0占一字节 +char *U82GB(char *szU8, uint *aLen); + +char *enc_u82u(char *data, uint *len); +char *enc_u2u8(char *data, uint *len); + +//-----------------------------------------------------------------------------------Base64 encode decode win/unix + +//Base64编码,需要释放返回值(need free return) +char *base64_Encode(const uchar *bytes_to_encode, uint in_len); +//Base64解码,需要释放返回值(need free return) +char *base64_Decode(const char *encoded_string); + +//-----------------------------------------------------------------------------------SHA1计算摘要 win/unix + +typedef struct SHA1_CONTEXT +{ + char bFinal : 1; //是否计算完成 + uint h0, h1, h2, h3, h4; + uint nblocks; + uint count; + uchar buf[64]; //返回SHA1结果不是字符串,是定长的20字节数据,中间可能有'\0',,要作为字符输出:printf("%02X ", sh.buf[i]); +} SHA1_CONTEXT; + +//初始化/重置结构体 +void hash1_Reset(SHA1_CONTEXT *hd); + +//使用长度为 inlen 的 inbuf 内容更新消息摘要。 +void hash1_Write(SHA1_CONTEXT *hd, uchar *inbuf, size_t inlen); + +/*结束计算并返回摘要。 + *句柄准备用于新的循环,但是向句柄添加字节将破坏返回的缓冲区。 + *返回:表示摘要的20个字节。 + */ +void hash1_Final(SHA1_CONTEXT *hd); + +//-----------------------------------------------------------------------------------url encode decode win/unix + +//url编码 (len为url的长度) +char *url_encode(const char *url, uint *len); +//url解码 +char *url_decode(char *url); + +//-----------------------------------------------------------------------------------WebSocket win/unix + +//WebSocket以文本传输的时候,都为UTF - 8编码,是WebSocket协议允许的唯一编码 +//服务端收数据用 +typedef struct WebSocketHandle +{ + membuf_t buf; //原始帧数据 + //data[0] + uchar isEof : 1; //是否是结束帧 data[0]>>7 + uchar dfExt : 3; //是否有扩展定义 (data[0]>>4) & 0x7 + /*控制码/帧类型 type 的定义data[0] & 0xF + 0x0表示附加数据帧 + 0x1表示文本数据帧 + 0x2表示二进制数据帧 + 0x3-7暂时无定义,为以后的非控制帧保留 + 0x8表示连接关闭 + 0x9表示ping + 0xA表示pong + 0xB-F暂时无定义,为以后的控制帧保留 */ + uchar type : 4; +} WebSocketHandle; + +//传入http头,返回WebSocket握手Key,非http升级ws则返回NULL +//需要释放返回值(need free return) +char *WebSocketHandShak(const char *key); + +//从帧中取得实际数据 (帧不应超过32位大小) +ulong WebSocketGetData(WebSocketHandle *handle, char *data, ulong len); + +//转换为一个WebSocket帧,无mask (need free return) +//op:控制码/帧类型 0x0表示附加数据帧 0x1表示文本数据帧 ... +char *WebSocketMakeFrame(uchar *data, ulong *len, uchar op); + +//-----------------------------------------------------------------------------------工具函数/杂项 win/unix + +//获取IPv4地址 (第一个IPv4) +const char *GetIPv4(); +//获取IPv6地址 (第一个IPv6) +const char *GetIPv6(); +//获取网卡地址 +const char *GetMacAddr(); + +typedef struct tm_u +{ + int tm_sec; /*秒 seconds after the minute - [0,59] */ + int tm_min; /*分 minutes after the hour - [0,59] */ + int tm_hour; /*时 hours since midnight - [0,23] */ + int tm_mday; /*天 day of the month - [1,31] */ + int tm_mon; /*月 months since January - [1,12] */ + int tm_year; /*年 years since 1900 */ + int tm_wday; /*星期 days since Sunday - [0,6 0:周日] */ + int tm_yday; /*年中的天数 days since January 1 - [0,365] */ + int tm_isdst; /*夏令时标志 daylight savings time flag */ + llong tm_vsec; /*时间戳 seconds from 1900/1/1 0:0:0 */ + int tm_usec; /*微妙 microseconds */ +} tm_u; + +//获取当前时间信息 +tm_u GetLocaTime(); + +//获取当天已逝去的秒数 +size_t GetDaySecond(); + +//获取格林制(GMT)时间: "Wed, 18 Jul 2018 06:02:42 GMT" +//szDate: 存放GMT时间的缓存区(至少 char[30]),外部传入 +//szLen: szDate的长度大小 +//addSecond: 当前时间加上多少秒 +char *getGmtTime(char *szDate, int szLen, int addSecond); + +//字符串转换成时间戳(秒),字符串格式为:"2016-08-03 06:56:36" +llong str2stmp(const char *strTime); + +//时间戳(秒)转换成字符串,字符串格式为:"2016-08-03 06:56:36" +char *stmp2str(llong t, char *str, int strlen); + +//从头比较字符串,返回相同的长度,不区分大小写 +size_t strinstr(const char *s1, const char *s2); + +//int32 转二进制字符串 +char *u2b(uint n); +//int64 转二进制字符串 +char *u2b64(ullong n); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //__TOOLS_H__ diff --git a/src/web/web-server.cpp b/src/web/web-server.cpp index e85af3a..16b6e78 100644 --- a/src/web/web-server.cpp +++ b/src/web/web-server.cpp @@ -28,20 +28,100 @@ /*------------------------------- INCLUDES ----------------------------------*/ -#include "CivetServer.h" - -#include "web/handler/exit-handler.h" +#include #include "web-server.h" -#define EXIT_URI "/exit" +//404前回调(未找到页面/文件时回调,此功能便于程序返回自定义功能);返回0表示没有适合的处理请求,需要发送404错误 +char on_request(void* data, uv_stream_t* client, tw_peerAddr* pa, tw_reqHeads* heads) +{ +// struct sockaddr_in serveraddr, peeraddr; +// char serv_ip[17],peer_ip[17], tmp[1024]; +// int addrlen = sizeof(struct sockaddr); +// int r; +// +// //获取clientAddr: http://www.codes51.com/article/detail_113112.html +// //本地接入地址 +// r = uv_tcp_getsockname((uv_tcp_t*)client, (struct sockaddr*)&serveraddr, &addrlen); +// //网络字节序转换成主机字符序 +// uv_ip4_name(&serveraddr, (char*)serv_ip, sizeof(serv_ip)); +// //客户端的地址 +// r = uv_tcp_getpeername((uv_tcp_t*)client, (struct sockaddr*)&peeraddr, &addrlen); +// //网络字节序转换成主机字符序 +// uv_ip4_name(&peeraddr, (char*)peer_ip, sizeof(peer_ip)); +// +// sprintf(tmp, "

Page not found:

%s
%s



server:%s:%d\t\tpeer:%s:%d\n", heads->path, (heads->query?heads->query:""), serv_ip, ntohs(serveraddr.sin_port), peer_ip, ntohs(peeraddr.sin_port)); +//#ifdef _MSC_VER //Windows下需要转换编码 +// size_t ll = strlen(tmp); +// char *ch = GB2U8(tmp, &ll); +// tw_send_200_OK(client, "text/html", ch, -1, 0); +// free(ch); +//#else //linux 下,系统是和源代码文件编码都是是utf8的,就不需要转换 +// tw_send_200_OK(client, "text/html", tmp, -1, 0); +//#endif // _MSC_VER +// + printf(" sk:%zd Request:\n",pa->sk); + printf(" Query: %s\n",heads->query); + printf(" Path: %s\n",heads->path); + printf(" Host: %s\n",heads->host); + printf(" Cookie: %s\n", heads->cookie); + printf(" Range: %lld-%lld\n",heads->Range_frm,heads->Range_to); + printf(" data(%zd): %s\n", heads->len,heads->data); + if (!heads->cookie) + { + char ck[512]; + tw_make_setcookie(ck, 255,"TINYSSID","FDSAFdfafdsafds", 3600 * 8, NULL, heads->path); + tw_make_setcookie(ck + strlen(ck),255,"TINYSSID2","faFDSAF45dsafds", 0, heads->host, NULL); + sprintf(ck + strlen(ck), "WWW-Authenticate: Basic realm=\".\"\r\n"); + size_t len; + char* rp = tw_format_http_respone(client, "401 Unauthorized", ck, "text/plan", "", -1, &len); + tw_send_data(client, rp, len, 0, 1); + return 1; + } + return 0; +} + +char on_socket_data(void* data, uv_stream_t* client, tw_peerAddr* pa, membuf_t* buf) +{ + if (buf->size < 1) + return 1;//防止发生数据为空 + if (pa->flag & 0x2) { //WebSocket + printf((const char*)buf->data, buf->size < 256 ? buf->size : 256); + printf("-------------------------------------------ws:%zd dlen=%zd\n%s\n-------------------------------------------\n",pa->sk, buf->size, buf->data); + ulong len = buf->size; + char* p = WebSocketMakeFrame(buf->data, &len, 1);//文本帧 + tw_send_data(client, p, len, 0, 1); + } else { //Socket + printf("-------------------------------------------sk:%zd dlen=%zd\n%s\n-------------------------------------------\n",pa->sk, buf->size, buf->data); + tw_send_data(client, buf->data, buf->size, 1, 0); + } + return 1; +} + +char on_close(void* data, uv_stream_t* client, tw_peerAddr* pa) +{ + //printf("closed: sk=%zd [%s:%d] from:%s:%d cli:%d\n", pa->sk, pa->ip, pa->port, pa->fip, pa->fport, client->loop->active_tcp_streams); + return 0; +} + +char on_connect(void* data, uv_stream_t* client, tw_peerAddr* pa) +{ + //printf("connected: sk=%zd [%s:%d] from:%s:%d cli:%d\n",pa->sk,pa->ip,pa->port,pa->fip,pa->fport, client->loop->active_tcp_streams); + return 0; +} + +char on_error(void* data, uv_stream_t* client, tw_peerAddr* pa, int errcode, char* errstr) +{ + //printf("error: sk=%zd [%s:%d] from:%s:%d cli:%d %s\n", pa->sk, pa->ip, pa->port, pa->fip, pa->fport, client->loop->active_tcp_streams,errstr); + return 0; +} /*! ---------------------------------------------------------------------------- * @fn WebServer * * @brief Constructor of the Web Server Object. */ -WebServer::WebServer(void) : m_server(NULL) +WebServer::WebServer(void) { } @@ -53,7 +133,6 @@ WebServer::WebServer(void) : m_server(NULL) WebServer::~WebServer(void) { - delete m_server; } /*! ---------------------------------------------------------------------------- @@ -61,18 +140,22 @@ WebServer::~WebServer(void) * * @brief Setup the Web server */ -int WebServer::setup(const char *a_document_root, const char *a_port, struct event_base *an_evt_loop) +int WebServer::setup(char *a_document_root, int a_port, uv_loop_t *an_evt_loop) { - std::vector the_options; + memset(&m_conf, 0, sizeof(m_conf)); + m_conf.dirlist = 1;//目录列表 + //conf.ip = NULL;// "127.0.0.1"; + m_conf.port = a_port; + m_conf.doc_dir = a_document_root; + m_conf.doc_index = NULL;// Default homepage + //m_conf.doc_dir = NULL;// The directory where the default program files are located - the_options.push_back("document_root"); - the_options.push_back(a_document_root); - the_options.push_back("listening_ports"); - the_options.push_back(a_port); - - m_server = new CivetServer(the_options); - - m_server->addHandler(EXIT_URI, new ExitHandler(an_evt_loop)); + m_conf.on_request = on_request; + m_conf.on_data = on_socket_data; + m_conf.on_close = on_close; + m_conf.on_connect = on_connect; + m_conf.on_error = on_error; + tinyweb_start(an_evt_loop, &m_conf); return 0; } diff --git a/src/web/web-server.h b/src/web/web-server.h index c01d545..438dd7e 100644 --- a/src/web/web-server.h +++ b/src/web/web-server.h @@ -27,14 +27,11 @@ #define _WEB_SERVER_H /*------------------------------- INCLUDES ----------------------------------*/ - -#include +#include +#include /*---------------------------------- Deps -----------------------------------*/ -class CivetServer; -struct event_base; - /*--------------------------------- CLASS ----------------------------------*/ class WebServer @@ -43,10 +40,10 @@ public: WebServer(void); ~WebServer(void); - int setup(const char *a_document_root, const char *a_port, struct event_base *an_evt_loop); + int setup(char *a_document_root, int a_port, uv_loop_t *an_evt_loop); private: - CivetServer *m_server; + tw_config m_conf; }; #endif /* __WEB_SERVER_H */