/* Ejemplo usando tcpsock dll
Mini Fenix WebServer by SplinterGU
*/
PROGRAM WebServer;
import "tcpsock.dll";

global
    clients=0;

    logfd;

    DocumentRoot = "webpages";
    AccessLog = "logs/access.log";

begin

	full_screen=false;

	set_title("Mini Fenix WebServer");

	tcpsock_init(); // Inicializamos la librería tcpsock

	write(0,5,5,0,"Clients:");
	write_var(0,5+text_width(0,"Clients:"),5,0,clients);

    server_thread();

end;

function log(string slog)
private
    strint str;
begin
    logfd = fopen(AccessLog, O_RDWR);
    if (!logfd)
        logfd = fopen(AccessLog, O_WRITE);
    end
    fseek(logfd, 0, SEEK_END);
    fputs(logfd,ftime("%d/%m/%Y-%H:%M:%S> ",time()) + slog);
    fclose(logfd);
end

process server_thread()
private
    int socket_listen; // socket_listen para recoger peticiones
    int connection=0;
    int ipaddr;
    int i;
    int informacion; // enviaremos a los clientes números aleatorios entre 0 y 32768
begin

    log("Fenix Web Server, Started!");

    socket_listen=tcpsock_open(); // nuevo socket
    tcpsock_bind(socket_listen,80); // asociamos socket_listen con el puerto 1200
    tcpsock_listen(socket_listen,64); // numero de conexiones a escuchar igual a clients

    while(!key(_ESC))
        tcpsock_fdzero(0);
        tcpsock_fdset(0,socket_listen);
        if (tcpsock_select(0,0)>0)
            connection=tcpsock_accept(socket_listen, &ipaddr);
            if(connection>0)
                process_client(connection, ipaddr);
            end
        end
    	frame;
	end;

onexit
	tcpsock_quit(); // Cerramos la librería tcpsock

end

process process_client(int sock, int ipaddr)
private
    char msg[2048];
    string hdrFields[128];
    string request[3];
    rlen, slen, n, pos, d1, d2, cnt;
    fp;
    string lang;
begin
    clients++;

    log("Connect from ip "+((ipaddr)&0ffh)+"."+((ipaddr>>8)&0ffh)+"."+((ipaddr>>16)&0ffh)+"."+((ipaddr>>24)&0ffh));

    while(!key(_esc))
        // Esto se podria hacer todo un fdset general, pero como cada frame se ejecuta por separado no hay problemas aca
        tcpsock_fdzero(1);
        tcpsock_fdset(1,sock);
        if (tcpsock_select(1,0)>0 && tcpsock_fdisset(1,sock))
            // En una aplicacion real, aca se debe poner un loop hasta recibir el paquete completo
            rlen=tcpsock_recv(sock,&msg,sizeof(msg));
            if(rlen<=0)
                break;
            end

            // Suponemos que aca tenemos el paquete completo
            cnt=split("["+chr(13)+chr(10)+"]+", msg, &hdrFields, sizeof(hdrFields));

            for(n=1;n<cnt;n++)
                if(strcasecmp(substr(hdrFields[n],0,17),"Accept-Language: ")==0)
                    lang=substr(hdrFields[n],17);
                end
            end

            cnt=split("[ ]+", hdrFields[0], &request, sizeof(request));

            log("Request ["+hdrFields[0]+"]");

            if(request[0]!="GET")
                msg=request[2]+" 405 Method Not Allowed";
                tcpsock_send(sock,&msg,len(request[2])+23);
                break;
            end

            /* Expand */

            while((pos=find(request[1],"%")) != -1)
                d1=asc(ucase(substr(request[1], pos+1, 1)));
                d2=asc(ucase(substr(request[1], pos+2, 1)));

                if(d1>=asc("A"))
                    d1-=asc("A")-10;
                else
                    d1-=asc("0");
                end

                if(d2>=asc("A"))
                    d2-=asc("A")-10;
                else
                    d2-=asc("0");
                end

                request[1]=substr(request[1], 0, pos)+chr(d1*16+d2)+substr(request[1], pos+3);
            end

            /* Comprueba si se quiere acceder a otro directorio anterior al DocumentRoot */
            if(find(request[1], "..") != -1)
                msg=request[2]+" 403 Forbidden";
                tcpsock_send(sock,&msg,len(request[2])+14);
                break;
            end

            // Aca usando los modo de manejo de archivos GZ, podemos dar soporte a archivos paginas gzip
            if(substr(request[1],-1,1)=="/")
                fp = fopen(DocumentRoot+request[1]+"index.html."+lang, O_READ);
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.htm."+lang, O_READ);
                end
                fp = fopen(DocumentRoot+request[1]+"index."+lang+".html", O_READ);
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index."+lang+".htm", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.html", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.htm", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.html.html", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.htm.html", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.html.htm", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+"index.htm.htm", O_READ);
                end
            else
                fp = fopen(DocumentRoot+request[1]+"."+lang, O_READ);
                if (!fp)
                    fp = fopen(DocumentRoot+request[1], O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+".html", O_READ);
                end
                if (!fp)
                    fp = fopen(DocumentRoot+request[1]+".htm", O_READ);
                end
            end

            if (!fp)
                msg=request[2]+" 404 Not Found";
                tcpsock_send(sock,&msg,14+len(request[2]));
                break;
            end

            while(!feof(fp))
                slen=fread(fp, msg);
                tcpsock_send(sock,&msg,slen);
                frame;
            end
            fclose(fp);
            break;
        end

        frame;
    end

onexit
	tcpsock_close(sock);// Cierra el socket
    clients--;

end
