/* *********************************************************************
This source file is a part of the standard library of Scol
For the latest info, see http://www.scolring.org
Copyright (c) 2013 Stephane Bisaro aka Iri.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt
********************************************************************* */
/*
* Common network standard functions
* See http://redmine.scolring.org/projects/tutorials/wiki/Scol_usage
* for more informations
*/
/*! \file net.pkg
* \author Scol team
* \version 0.1
* \copyright GNU Lesser General Public License 2.0 or later
* \brief Scol Standard Library - Common network API
*
* Required : lib/std/string.pkg
*
**/
proto std_netBuildListFromParams = fun [S] [[S S] r1];;
/* fun [S] [[ S S] r1] (key, value) */
fun std_netparseurlparams2 (szParams)=
std_netBuildListFromParams szParams;;
fun std_netparseurlparams (str, flag)= // flag = 0 -> GET, 1 -> POST
if !flag then
let strfind "?" str 0 -> pos in
if pos == nil then
nil
else
std_netparseurlparams2 substr str pos+1 strlen str
else
std_netparseurlparams2 str;;
fun std_getshalshinurl (s, start)=
let strfind "/" s start -> pos1 in
let strfind ":" s start -> pos2 in
if (pos2 < pos1) && (pos2 != nil) then pos2
else pos1;;
fun std_netgetdomain (url, lProtocol)=
if lProtocol == nil then
nil
else
if !strcmp substr url 0 strlen hd lProtocol hd lProtocol then
let std_getshalshinurl substr url strlen hd lProtocol strlen url strlen hd lProtocol -> end in
let substr url strlen hd lProtocol end -> szRes in
if std_szIsPattern szRes "[aZ][09].-_" then
szRes
else
nil
else
std_netgetdomain url tl lProtocol;;
/*! \brief Return the domain (or the IP address if any) in an url.
*
* "http://www.example.com:1234/dir/file.html" -> "www.example.com"
*
* "http://www.example.com/dir/file.html" -> "www.example.com"
*
* "http://1.2.3.4:1234/dir/file.html" -> "1.2.3.4"
*
* \ingroup std_net
* Prototype : fun [S] S
*
* \param S : an url
*
* \return S : the domain or nil if undefined
* \remark Known protocols : http, https, file, ftp, ftps, sftp, scol.
**/
fun std_netGetDomain (url)=
let "https://"::"http://"::"file://"::"ftp://"::"sftp://"::"ftps://"::"scol://"::nil -> lProtocol in
std_netgetdomain url lProtocol;;
/*! \brief Parse a received request.
*
* \ingroup std_net
* Prototype :fun [S] [S S S S S S S S S S [[S S] r1]]
*
* \param S : a string received request
*
* \return [S S S S S S S S S S [[S S] r1]] : a tuple :
* - S : the "host",
* - S : the "port" (80 by default),
* - S : the "user agent",
* - S : the used HTTP "protocol" (generally 1.0 or 1.1),
* - S : the "verb" (GET, POST, ...),
* - S : the data type (= the value of "Content-Type" header),
* - S : the language (= the value of "Content-Language" or "Accept-Language" header),
* - S : the size(lenght) of :
* -# the "url", if the "verb" is "GET",
* -# the "body" (exactly the value of the "Content-Lenght" header), if the "verb" is "POST",
* - S : the asked "url" (without the host),
* - S : the "body", the body content, if the "verb" is "POST" (should be nil with "GET"),
* - [[S S] r1] : a list with key, value which have been received in the request. If unable
* to find keys,values but the string is not empty, the string is returned in the first item
* of the tuple (in this case, the list has one element (tuple) only).
*
* \remark If a value is not found, it will be nil, except for the port.
**/
fun std_netParseRequest (szReq)=
let [nil nil nil nil "other" nil nil nil nil nil nil] -> [host port agent protocol verb ctype clang len url body params] in
(
let strfindi "Host: " szReq 0 -> pos in
(
set host = if pos == nil then
nil
else
let strfind " " szReq pos+6 -> end1 in
let strfind "\n" szReq pos+6 -> end2 in
let strfind ":" szReq pos+6 -> end3 in
let if end1 == nil then end2
else if end2 == nil then end1
else if end1 > end2 then end2
else end1 -> end in
let if end > end3 then end3 else end -> end in
strtrim substr szReq pos+6 end-pos-6;
set pos = strfindi ":" szReq pos+6;
set port = if pos == nil then
"80"
else
let strfind " " szReq pos+1 -> end1 in
let strfind "\n" szReq pos+1 -> end2 in
let if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 -> end in
strtrim substr szReq pos+1 end-pos-1;
);
let strfindi "User-Agent:" szReq 0 -> pos in
set agent = if pos == nil then
nil
else
let strfind ")" szReq pos -> end in
if end == nil then
let strfind " " szReq pos+11 -> end1 in
let strfind "\n" szReq pos+11 -> end2 in
(
set end = if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 ;
strtrim substr szReq pos+11 end-pos-11
)
else
strtrim substr szReq pos+11 end-pos-11;
let strfindi "HTTP/" szReq 0 -> pos in
set protocol = if pos == nil then
nil
else
let strfind " " szReq pos+5 -> end1 in
let strfind "\n" szReq pos+5 -> end2 in
let if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 -> end in
strtrim substr szReq pos+5 end-pos-5;
let strfindi "Content-Type: " szReq 0 -> pos in
set ctype = if pos == nil then
nil
else
let strfind "\n" szReq pos+14 -> end in
strtrim substr szReq pos+14 end-pos-14;
let strfindi "Content-Language: " szReq 0 -> pos in
set clang = if pos == nil then
nil
else
let strfind "\n" szReq pos+18 -> end in
strtrim substr szReq pos+18 end-pos-18;
if clang == nil then
let strfindi "Accept-Language: " szReq 0 -> pos in
set clang = if pos == nil then
nil
else
let strfind "\n" szReq pos+17 -> end in
strtrim substr szReq pos+17 end-pos-17
else
nil;
let strfindi "GET " szReq 0 -> pos in
if pos != nil then
(
set verb = "GET";
let strfind " " szReq pos+4 -> end1 in
let strfind "\n" szReq pos+4 -> end2 in
let if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 -> end in
set url = if pos == nil then
nil
else
strtrim substr szReq pos+4 end-pos-4;
set params = std_netparseurlparams url 0;
set len = itoa strlen url;
)
else
nil;
let strfindi "POST " szReq 0 -> pos in
if pos != nil then
(
set verb = "POST";
let strfind " " szReq pos+5 -> end1 in
let strfind "\n" szReq pos+5 -> end2 in
let if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 -> end in
set url = if pos == nil then
nil
else
strtrim substr szReq pos+5 end-pos-5;
let strfindi "Content-Length: " szReq 0 -> pos in
let strfind " " szReq pos+16 -> end1 in
let strfind "\n" szReq pos+16 -> end2 in
let if end1 == nil then end2 else if end2 == nil then end1 else if end1 > end2 then end2 else end1 -> end in
set len = strtrim substr szReq pos+16 end-pos-16;
set pos = strfind "\13\10\13\10" szReq 0; // body starts always after
set body = substr szReq pos+4 strlen szReq;
set params = std_netparseurlparams body 1;
)
else
nil;
/*_fooS host;
_fooS port;
_fooS agent;
_fooS protocol;
_fooS verb;
_fooS ctype;
_fooS clang;
_fooS len;
_fooS url;
_fooS body;*/
[host port agent protocol verb ctype len url body params]
);;
/*! \brief Return the header of a received request object.
*
* \ingroup std_net
* Prototype :fun [S] S
*
* \param S : a valid request string
*
* \return S : the header or nil if the request is invalid
*/
fun std_netGetHeader (szReq)=
let strfind "\13\10\13\10" szReq 0 -> pos in
if pos == nil then
szReq
else
substr szReq 0 pos;;
/*! \brief Return the body of a received request object.
*
* \ingroup std_net
* Prototype :fun [S] S
*
* \param S : a valid request string
*
* \return S : the body or nil if no body found (with "GET" by example)
*/
fun std_netGetBody (szReq)=
let strfind "\13\10\13\10" szReq 0 -> pos in
if pos == nil then
nil
else
substr szReq pos+4 strlen szReq;;
/*! \brief Function to convert a string parameters like a=b&c=d ...
* to a list key,value. The given string should be already parsed (the params only).
*
* \ingroup std_net
* Prototype :fun [S] [[S S] r1]
*
* \param S : an already parsed string, typically from an url (GET) or a body (POST)
*
* "param1=a¶m2=b" -> [["param1" "a"] :: ["param2" "b"] :: nil
*
* \return [[S S] r1] : this list or nil if the request is invalid
* \remark If this function is unable to find keys,values, it returns
* a list with one tuple only, the full string is in the first item of
* the tuple.
* \see std_netGetParamsInUrl
*/
fun std_netBuildListFromParams (szParams)=
if std_szIsEmpty szParams then
nil
else
let strfind "=" szParams 0 -> pos1 in
if pos1 == nil then
[szParams nil] :: nil
else
let strfind "&" szParams pos1 -> pos2 in
if pos2 != nil then
/*let _fooS strcat "KEY = " substr szParams 0 pos1 -> _ in
let _fooS strcat "VALUE = " substr szParams pos1+1 pos2-pos1-1 -> _ in*/
[substr szParams 0 pos1 substr szParams pos1+1 pos2-pos1-1] :: std_netBuildListFromParams substr szParams pos2+1 strlen szParams
else
/*let _fooS strcat "KEY = " substr szParams 0 pos1 -> _ in
let _fooS strcat "VALUE = " substr szParams pos1+1 strlen szParams -> _ in*/
[substr szParams 0 pos1 substr szParams pos1+1 strlen szParams] :: nil;;
/*! \brief Return the params of a received GET request string.
*
* "http://domain.tld/dir/file.ext?param1=a¶m2=b" -> "param1=a¶m2=b"
*
* \ingroup std_net
* Prototype :fun [S] S
*
* \param S : a valid request string
*
* \return S : the params or nil if not found
* \see std_netBuildListFromParams
*/
fun std_netGetParamsInUrl (szUrl)=
if std_szIsEmpty szUrl then
nil
else
let strfind "?" szUrl 0 -> pos in
if pos == nil then
nil
else
substr szUrl pos+1 strlen szUrl;;
fun std_netcheckcbconnection (szUrl, cbfun, iRes)=
exec cbfun with [iRes];;
/*! \brief Check if a server can be joined
*
* \ingroup std_net
* Prototype :fun [S fun [I] I] I
*
* \param S : an url to check
* \param fun [I] I : a function to call when the connection is checked.
* Its argument will be 1 if success.
*
* \return I : always 0
*/
fun std_netCheckConnection (szUrl, cbfun)=
_rflINETisConnected szUrl @std_netcheckcbconnection cbfun;
0;;