Indice


Volver al Indice Autores


Esta práctica ha sido realizada por el grupo 4 de prácticas.
Toda la programación ha sido realizada por Guillem Rull Fort. Mientras que Jose María Rodríguez Valls se ha limitado ha hacer la documentación pertinente.

Volver al Indice Introducción


Tendremos que instalar el servidor Apache y posteriormente configurarlo correctamente para que se puedan ejecutar los archivos CGI.
El objetivo es poder apreciar el funcionamiento de dos formularios dados con anterioridad.

Será importante comprobar los permisos de escritura para poder almacenar los datos en un fichero plano.

El servidor tendrá que estar configurado para trabajar en localhost. Le asignaremos un puerto distinto al puerto por defecto (80).

Volver al Indice Entorno


Inicialmente dispondremos de una máquina con sistema operativo linux pero sin ningún servidor web instalado.
La idea es realizar la práctica para trabajo local.


Volver al Indice Implementación

MakeFile


El MakeFile es un archivo de procesamiento por lotes encargado de establecer algunos parámetros en la compilación de archivos.
Utilizar este archivo no es exclusivamente necesario. Puesto que podríamos compilar directamente con el gcc.
El motivo de usar esto es simplemente para ahorrarnos tener que escribir todos los parámetros de compilación cada vez que deseamos compilar.

A continuación mostramos las principales líneas de este archivo con una corta explicación:

Make File

//Indicamos los archivos ha compilar
all: comanda llistar_comandes

...

//Realiza el linkaje del CGI comanda
comanda: comanda.o util.o
        $(CC) comanda.o util.o -o ../cgi-bin/comanda

//Realiza el linkaje del CGI llistar_comandes
llistar_comandes: llistar_comandes.o util.o
        $(CC) llistar_comandes.o util.o -o ../cgi-bin/llistar_comandes
...


Como podemos ver el archivo genera un procesamiento por lotes añadiendo todas las opciones de compilación.
De este modo solo tendremos que ejecutar make cada vez que deseemos compilar los CGI.

Compilar los CGI

//Entramos en el directorio que contiene los CGI
cd apache/cgi-bin/

//Ejecutamos el MakeFile
make

//Compila y Linka los archivos
gcc -c comanda.c
gcc comanda.o util.o -o ../cgi-bin/comanda
gcc -c llistar_comandes.c
gcc -c llistar_comandes.o utils.o -o ../cgi-bin/llistar_comandes


Comanda


El CGI comanda será el encargado de almacenar los datos del pedido recibido y almacenarlos dentro de un archivo denominado comandes.txt .
Este archivo es el que mantendrá el estado para que al hacer el listado de pedidos podamos observar los datos.

Será necesario que la carpeta /cgi-bin/ tenga los permisos de escritura correspondientes. Puesto que sino el CGI no podría modificar este archivo.

Los datos serán enviados por el cliente mediante el método GET. El servidor almacena los datos y posteriormente muestra un resultado al cliente.

//Definimos una estructura para almacenar el dato y valor
//enviados por el cliente...
typedef struct {
    char name[128];
    char val[128];
} entry;

//Declaramos los métodos y funciones que vamos a utilizar...
void getword(char *word, char *line, char stop);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);
void model_preu(char *model_preu, char *model, int *preu);

//Estructura utilizada para almacenar los datos en el archivo...
typedef struct {
    int n_taules;
    char model_taula[128];
    int preu_taula;
    int n_cadires;
    char model_cadira[128];
    int preu_cadira;
    int n_llums;
    char model_llum[128];
    int preu_llum;
} tcomanda;

void escriure_comanda(FILE* f, const tcomanda* comanda);


//Cuerpo principal del programa...
main(int argc, char *argv[]) {
    entry entries[10000];
    register int x, m = 0;
    char *cl;
    char model[100];
    tcomanda comanda; //Datos ha almacenar...
    int preu_total;
    FILE *f;   //archivo para almacenar...

    //Enviamos la cabezera HTML
    printf("Content-type: text/html%c%c",10,10);


    //En caso que no nos envíen los datos vía GET mostramos un error...
    if(strcmp(getenv("REQUEST_METHOD"),"GET")) {
        printf("This script should be referenced with a METHOD of GET.\n");
        printf("If you don't understand this, see this ");
        printf("<A HREF=\"http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/fill-out-forms/overview.html\">forms overview</A>.%c",10);
        exit(1);
    }

    //Obtenemos la variable de entorno QUERY_STRING para obtener los datos enviados
    cl = getenv("QUERY_STRING");
    if(cl == NULL) {
        printf("No query information to decode.\n");
        exit(1);
    }

    //Separamos cada uno de los campos recibidos, split por &...
    //Después almacenamos los datos recibidos dentro de una estructura temporal
    for(x = 0; cl[0] != '\0'; x++) {
        m = x;
        getword(entries[x].val, cl, '&');
        plustospace(entries[x].val);
        unescape_url(entries[x].val);
        getword(entries[x].name, entries[x].val, '=');
    }

    //Almacenamos todos los campos recibidos dentro de la estructura
    for(x = 0; x < m; x++) {
        if(!strcmp(entries[x].name, "n_taules")) {
comanda.n_taules = atoi(entries[x].val);
        }
        if(!strcmp(entries[x].name, "model_preu_taula")) {
model_preu(entries[x].val, comanda.model_taula, &comanda.preu_taula);
        }
        if(!strcmp(entries[x].name, "n_cadires")) {
comanda.n_cadires = atoi(entries[x].val);
        }
        if(!strcmp(entries[x].name, "model_preu_cadira")) {
model_preu(entries[x].val, comanda.model_cadira, &comanda.preu_cadira);
        }
        if(!strcmp(entries[x].name, "n_llums")) {
comanda.n_llums = atoi(entries[x].val);
        }
        if(!strcmp(entries[x].name, "model_preu_llum")) {
model_preu(entries[x].val, comanda.model_llum, &comanda.preu_llum);
        }
    }

//Verificamos la consistencia de los datos recibidos...
if(comanda.n_taules == 0 && comanda.n_cadires == 0 && comanda.n_llums == 0) {
     printf("<H1>Comanda buida!</H1>%c", 10);
     printf("No s'ha donat cap quantitat més gran de 0%c", 10);
} else {
     //Creamos el archivo donde almacenaremos los datos recibidos...
     f = fopen("comandes.txt", "a");
     if(!f) {
      printf("<H1>Error en guardar comanda!</H1>%c", 10);
      printf("No s'ha pogut obrir el fitxer on es guarden les dades de les comandes%c", 10);
      exit(1);
     }
     //Escribimos la estructura de datos que contiene la información recibida...
     escriure_comanda(f, &comanda);
     fclose(f);

        //Enviamos un aviso al cliente indicandole el exito de la operación...
        printf("<H1>Dades de la comanda</H1>%c", 10);
        printf("<ul>%c", 10);
        //Mostramos los datos recibidos...
        if(comanda.n_taules > 0) {
printf("<li> <code>%d taules model %s amb preu %d</code>%c", comanda.n_taules,
comanda.model_taula, comanda.preu_taula, 10);
        }
        if(comanda.n_cadires > 0) {
printf("<li> <code>%d cadires model %s amb preu %d</code>%c", comanda.n_cadires,
comanda.model_cadira, comanda.preu_cadira, 10);
        }
        if(comanda.n_llums > 0) {
printf("<li> <code>%d llums model %s amb preu %d</code>%c", comanda.n_llums,
comanda.model_llum, comanda.preu_llum, 10);
        }
   printf("</ul>%c", 10);
   printf("<br>%c", 10);
   preu_total = comanda.n_taules*comanda.preu_taula +
                + comanda.n_cadires*comanda.preu_cadira +
                   + comanda.n_llums*comanda.preu_llum;
   printf("Preu total: %d%c", preu_total, 10);
    }

  //Mostramos el link para volver...
  printf("<br>%c", 10);
  printf("<br>%c", 10);
  printf("<a href=\"/main.html\">Tornar a la plana principal</a>%c", 10);
}

//Copiamos una cadena dentro de la estructura...
void model_preu(char *model_preu, char *model, int *preu) {
    char str_preu[100];
    int x = ind(model_preu, '-');
    /*x != -1*/
    memcpy(model, model_preu, x);
    strcpy(str_preu, &model_preu[x+1]);
    *preu = atoi(str_preu);
}

//Escribe los datos de la estructura dentro del archivo
//pasado por parámetro...
void escriure_comanda(FILE* f, const tcomanda* comanda) {
    fprintf(f, "%d\n", comanda->n_taules);
    fprintf(f, "%s\n", comanda->model_taula);
    fprintf(f, "%d\n", comanda->preu_taula);
    fprintf(f, "%d\n", comanda->n_cadires);
    fprintf(f, "%s\n", comanda->model_cadira);
    fprintf(f, "%d\n", comanda->preu_cadira);
    fprintf(f, "%d\n", comanda->n_llums);
    fprintf(f, "%s\n", comanda->model_llum);
    fprintf(f, "%d\n", comanda->preu_llum);
    fprintf(f, "\n");
}


Llistar_comandes


El CGI llistar_Comandes es el encargado de abrir el fichero de texto que contiene todos los pedidos y mostrar un listado al cliente.

Previamente el usuario tendrá que loginarse para ser autentificado por el servidor. Los datos se enviaran mediante el método POST puesto que es más seguro.


//Estructura para almacenar temporalmente el campo y valor
//enviados por el usuario...
typedef struct {
    char *name;
    char *val;
} entry;

//Declaración de métodos y funciones...
char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);

//Declaración de las constantes usuario y password...
define USERID "pxc"
define PASSWORD "abc"

//Estructura del fichero de texto a leer...
typedef struct {
    int n_taules;
    char model_taula[128];
    int preu_taula;
    int n_cadires;
    char model_cadira[128];
    int preu_cadira;
    int n_llums;
    char model_llum[128];
    int preu_llum;
} tcomanda;

//Declaración de funciones de lectura...
int llegir_comanda(FILE *f, tcomanda *comanda);
int read_line(FILE *f, char *str);

//Cuerpo principal del programa...
main(int argc, char *argv[]) {
    entry entries[MAX_ENTRIES];
    register int x,m=0;
    int cl;
    char *userid;
    char *password;
    FILE *f;
    tcomanda comanda;
    //Enviamos la cabecera HMTL...
    printf("Content-type: text/html%c%c",10,10);

    //Comprobamos con que método nos envían los datos...
    if(strcmp(getenv("REQUEST_METHOD"),"POST")) {
printf("This script should be referenced with a METHOD of POST.\n");
printf("If you don't understand this, see this ");
printf("<A HREF=\"http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/fill-out-forms/overview.html\">forms overview</A>.%c",10);
exit(1);
    }

    //Comprobamos el tipo de codificación...
    if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")){
        printf("This script can only be used to decode form results. \n");
        exit(1);
    }

    cl = atoi(getenv("CONTENT_LENGTH"));

    //Separamos los campos y valores enviados por el usuario...
    for(x = 0; cl && (!feof(stdin)); x++) {
        m = x;
        entries[x].val = fmakeword(stdin, '&', &cl);
        plustospace(entries[x].val);
        unescape_url(entries[x].val);
        entries[x].name = makeword(entries[x].val, '=');
    }

    //Recogemos los campos usuario y password...
    for(x = 0; x < m; x++) {
        if(!strcmp(entries[x].name, "userid")) {
           userid = entries[x].val;
        }
        if(!strcmp(entries[x].name, "password")) {
           password = entries[x].val;
        }
    }


//Comprobamos que el password y usuario sean correctos...
if(!strcmp(userid, USERID) && !strcmp(password, PASSWORD)) {
        //Abrimos el fichero de texto...
        f = fopen("comandes.txt", "r");
        if(!f) { //No existen pedidos en el archivo...
           printf("<H1>No hi ha cap comanda</H1>%c", 10);
        } else { //Pasamos a leer el archivo...
           printf("<H1>Llistat de comandes</H1>%c", 10);
           printf("<br>%c", 10);

           //Leemos los pedidos contenidos en el fichero y enviamos
           //Los datos al cliente...
           while(llegir_comanda(f, &comanda)) {
              printf("<hr>%c", 10);
              printf("<ul>%c", 10);
              if(comanda.n_taules > 0) {
printf("<li> <code>%d taules model %s amb preu %d. Total: %d</code>%c",
comanda.n_taules, comanda.model_taula, comanda.preu_taula,
comanda.n_taules*comanda.preu_taula, 10);
              }
              if(comanda.n_cadires > 0) {
printf("<li> <code>%d cadires model %s amb preu %d. Total: %d</code>%c",
comanda.n_cadires, comanda.model_cadira, comanda.preu_cadira,
comanda.n_cadires*comanda.preu_cadira, 10);
              }
                 if(comanda.n_llums > 0) {
printf("<li> <code>%d llums model %s amb preu %d. Total: %d</code>%c",
comanda.n_llums, comanda.model_llum, comanda.preu_llum,
comanda.n_llums*comanda.preu_llum, 10);
              }
              printf("</ul>%c", 10);
              printf("Preu total: %d%c", comanda.n_taules*comanda.preu_taula +
                     comanda.n_cadires*comanda.preu_cadira +
                     + comanda.n_llums*comanda.preu_llum,
                     10);
           }
           printf("<hr>%c", 10);
           fclose(f);
        }

    } else { //El usuario o el password son incorrectos...
        printf("<H1>Error de login!</H1>%c", 10);
        printf("El username i/o el password son incorrectes%c", 10);
    }

    //Visualizar link de volver...
    printf("<br>%c", 10);
    printf("<br>%c", 10);
    printf("<a href=\"/main.html\">Tornar a la plana principal</a>%c", 10);
}

//Leemos el archivo de pedidos y lo introducimos dentro de la estructura...
int llegir_comanda(FILE *f, tcomanda *comanda) {
    char str[128];

    if (!read_line(f, str)) return 0;
    comanda->n_taules = atoi(str);
    read_line(f, str);
    strcpy(comanda->model_taula, str);
    read_line(f, str);
    comanda->preu_taula = atoi(str);

    read_line(f, str);
    comanda->n_cadires = atoi(str);
    read_line(f, str);
    strcpy(comanda->model_cadira, str);
    read_line(f, str);
    comanda->preu_cadira = atoi(str);

    read_line(f, str);
    comanda->n_llums = atoi(str);
    read_line(f, str);
    strcpy(comanda->model_llum, str);
    read_line(f, str);
    comanda->preu_llum = atoi(str);

    read_line(f, str);

    return 1;
}

//Leer una cadena de caracteres del archivo...
int read_line(FILE *f, char *str) {
    int i = 0;
    char c = fgetc(f);
    if(c == EOF) return 0;
    while(c != '\n') {
        str[i] = c;
        i++;
        c = fgetc(f);
    }
    str[i] = '\0';
    return 1;
}


Volver al Indice Resultados


Hemos conseguido que toda la práctica funcione con completa normalidad.

CGI


Esta práctica sirve de gran utilidad para aprender los principios básicos de la tecnología CGI.

Ventajas de usar CGI

- Al ser un CGI es un algoritmo compilado, eso hace que sea más rápido en su ejecución puesto que no es interpretado, solo ejecutado.

Inconvenientes de usar CGI

- Tener que escribir mucho para hacer pequeñas tareas como esta práctica.
- Problemas de seguridad. Cada CGI se ejecuta en un hilo diferente.
Si un cliente ejecuta reiteradamente un mismo CGI abrirá muchos hilos de ejecución en el servidor.
El servidor puede llegar a caer por falta de recursos.
- Las salidas HTML están integradas dentro del código. Es difícil de mantener.

Apache


El apache permite trabajar de forma transparente. Es decir, no es necesario tener en cuenta el servidor a la hora de programar.

Nuestra opinión es que parece ser un servidor complejo a la hora de realizar configuraciones.
Otros servidores como el IIS (Internet Informacion Server) parecen más intuitivos en este sentido.

Volver al Indice Conclusiones


El trabajo realizado pasa por una primera fase de documentación para poder aprender el funcionamiento de los CGI.
Empezamos estudiando los ejemplos anteriores.

Después pasamos ha instalar el servidor Apache siguiendo los pasos indicados en la memoria de la práctica.
Seguidamente verificamos los formularios y elaboramos la programación de los CGI.

Por último realizamos las pruebas de funcionamiento y la documentación de la práctica.