DB_DataObject.
--- /dev/null
+sercom (0.2) unstable; urgency=low
+
+ * Se mejora el log.
+ * Se agrega un umask para que los archivos creados puedan ser leidos por el
+ grupo.
+
+ -- Leandro Lucarella <luca@llucax.hn.org> Mon, 30 Aug 2004 10:48:56 -0300
+
+sercom (0.1) unstable; urgency=low
+
+ * Primera version alpha que solo compila.
+
+ -- Leandro Lucarella <llucare@fi.uba.ar> Sun, 29 Aug 2004 15:20:25 -0300
+
--- /dev/null
+Corrector Automático para Taller de Programación I (75.42)
+Universidad de Buenos Aires - Facultad de Ingeniería
+
+
--- /dev/null
+PEAR:
+=====
+DB_DataObject 1.7.2
--- /dev/null
+-- Esquema de la base de datos
+
+CREATE TABLE curso
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ anio INTEGER(4) NOT NULL,
+ cuatrimestre INTEGER(1) NOT NULL,
+ curso INTEGER NOT NULL,
+ -- campos
+ descripcion VARCHAR(150),
+ UNIQUE (anio, cuatrimestre, curso)
+);
+
+CREATE TABLE inscripto
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ padron INTEGER NOT NULL,
+ curso_id INTEGER NOT NULL,
+ -- campos
+ mail VARCHAR(150) NOT NULL,
+ activo INTEGER(1) NOT NULL DEFAULT 1,
+ UNIQUE (padron, curso_id)
+);
+
+CREATE TABLE docente
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ nombre VARCHAR(150) NOT NULL,
+ -- campos
+ mail VARCHAR(150) NOT NULL,
+ corrige INTEGER(1) NOT NULL DEFAULT 1,
+ UNIQUE (nombre)
+);
+
+CREATE TABLE curso_docente
+(
+ -- clave
+ curso_id INTEGER NOT NULL,
+ docente_id INTEGER NOT NULL,
+ PRIMARY KEY (docente_id, curso_id)
+);
+
+CREATE TABLE ejercicio
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ nombre VARCHAR(150) NOT NULL,
+ -- campos
+ numero INTEGER NOT NULL,
+ docente_id INTEGER NOT NULL, -- Autor
+ UNIQUE (nombre)
+);
+
+CREATE TABLE entrega
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ curso_id INTEGER NOT NULL,
+ nro_ejercicio INTEGER NOT NULL,
+ entrega INTEGER NOT NULL,
+ -- campos
+ ejercicio_id INTEGER NOT NULL,
+ desde DATE NOT NULL,
+ hasta DATE NOT NULL,
+ UNIQUE (curso_id, nro_ejercicio, entrega)
+);
+
+CREATE TABLE caso_de_prueba
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ ejercicio_id INTEGER NOT NULL,
+ nombre VARCHAR(255) NOT NULL,
+ -- campos
+ privado INTEGER(1) NOT NULL,
+ activo INTEGER(1) NOT NULL DEFAULT 1,
+ parametros VARCHAR(255),
+ salidas VARCHAR(255),
+ UNIQUE (ejercicio_id, nombre)
+);
+
+CREATE TABLE intento
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ inscripto_id INTEGER NOT NULL,
+ entrega_id INTEGER NOT NULL,
+ numero INTEGER NOT NULL,
+ -- campos
+ llegada DATE NOT NULL,
+ inicio_pruebas DATE,
+ fin_pruebas DATE,
+ compila_ok INTEGER(1),
+ notificado INTEGER(1) NOT NULL DEFAULT 0,
+ mail_respuesta VARCHAR(150) NOT NULL,
+ observaciones VARCHAR(255),
+ UNIQUE (entrega_id, inscripto_id, llegada)
+);
+
+CREATE TABLE correccion
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ entrega_id INTEGER NOT NULL,
+ inscripto_id INTEGER NOT NULL,
+ -- campos
+ intento_id INTEGER NOT NULL, -- Último intento exitoso
+ docente_id INTEGER NOT NULL, -- Corrector
+ nota INTEGER(2),
+ observaciones VARCHAR(255),
+ UNIQUE (inscripto_id, entrega_id)
+);
+
+CREATE TABLE prueba
+(
+ id INTEGER PRIMARY KEY,
+ -- clave
+ intento_id INTEGER NOT NULL,
+ caso_de_prueba_id INTEGER NOT NULL,
+ -- campos
+ inicio DATE NOT NULL,
+ fin DATE,
+ memoria_ok INTEGER(1),
+ salida_ok INTEGER(1),
+ observaciones VARCHAR(255),
+ UNIQUE (intento_id, caso_de_prueba_id)
+);
+
+-- vim: set et sw=4 ts=4 sts=4 :
--- /dev/null
+
+; Opciones generales para todos los programadas de sercom
+[general]
+; Nivel de reporte de errores.
+error_reporting = E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR
+; Nivel de loggeo: L_NON, L_ERR, L_WRN, L_INF, L_DBG, L_ALL
+; (ver T/logconstants.php)
+loglevel = L_ALL & ~L_DBG
+; Archivo de log (si no se especifica, se usa STDERR)
+logfile = /var/log/sercom.log
+; Directorio base en donde almacenar los archivos de datos (entregas, intentos)
+data_dir = /var/lib/sercom
+; Claves para validar la identidad del alumno (a usar para crear el código)
+claves = xxx
+; Cantidad de segundos a esperar para ver si hay una nueva entrega
+intervalo = 5
+; Usuario con el cual ejecutar las pruebas
+user = sercom
+
+[mail]
+; E-Mail del administrador, a donde enviar errores graves, avisos, etc
+mail_admin = luca@llucax.hn.org
+; Dirección desde la cual enviar mails
+from = "TALLER DE PROGRAMACION I <mat7542@fi.uba.ar>"
+; Descripción del programa de mail (X-Mailer)
+mailer = "sercom v0.2"
+; Prefijo a poner en el subject
+prefijo = "[CORRECTOR]"
+; Mailbox local en donde hacer backup de los mails entregados
+; (si está vacío no se hace backup)
+mbox_bak = /var/lib/sercom/entregas.mbox
+
+; pop
+;server = pop.example.com
+;user = user
+;pass = pass
+
+; imap
+;server = imap.example.com
+;user = test
+;pass = test
+;protocol = imap
+;mailbox = "INBOX"
+
+; mbox
+;protocol = mbox
+; path al mbox relativo al home del usuario
+;server = taller/sercom/mbox
+
+; imap - con TSL
+server = mail.fi.uba.ar
+user = user
+pass = xxxx
+protocol = imap
+options = novalidate-cert/ssl
+
+
+[DB_DataObject]
+database = sqlite:////var/lib/sercom/corrector.sqlite
+schema_location = T/DBO/schema/
+class_location = T/DBO/
+require_prefix = T/DBO/
+class_prefix = T_DBO_
+;debug = 1
+
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Caso_de_prueba extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'caso_de_prueba';
+ var $id;
+ var $ejercicio_id;
+ var $nombre;
+ var $privado;
+ var $activo;
+ var $parametros;
+ var $salidas;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Caso_de_prueba', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'ejercicio_id' => DB_DATAOBJECT_INT,
+ 'nombre' => DB_DATAOBJECT_STR,
+ 'privado' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'activo' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'parametros' => DB_DATAOBJECT_STR,
+ 'salidas' => DB_DATAOBJECT_STR,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Correccion extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'correccion';
+ var $id;
+ var $entrega_id;
+ var $inscripto_id;
+ var $intento_id;
+ var $docente_id;
+ var $nota;
+ var $observaciones;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Correccion', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'entrega_id' => DB_DATAOBJECT_INT,
+ 'inscripto_id' => DB_DATAOBJECT_INT,
+ 'intento_id' => DB_DATAOBJECT_INT,
+ 'docente_id' => DB_DATAOBJECT_INT,
+ 'nota' => DB_DATAOBJECT_INT,
+ 'observaciones' => DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Curso extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'curso';
+ var $id;
+ var $anio;
+ var $cuatrimestre;
+ var $curso;
+ var $descripcion;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Curso', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'anio' => DB_DATAOBJECT_INT,
+ 'cuatrimestre' => DB_DATAOBJECT_INT,
+ 'curso' => DB_DATAOBJECT_INT,
+ 'descripcion' => DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+ function toLine()
+ {
+ return $this->anio . $this->cuatrimestre . $this->curso;
+ }
+
+ /**
+ *
+ */
+ function resultado_de_prueba($prueba, $salida, $descripcion = "")
+ {
+ if($memoria)$memoria = 1;
+ else $memoria = 0;
+ if($salida)$salida = 1;
+ else $salida = 0;
+ $date = date("YmdHis");
+ $this->db->query("INSERT INTO prueba
+ (cuatrimestre, padron, ejercicio, entrega, intento, caso_de_prueba, memoria_ok, salida_ok, descripcion)
+ VALUES ($this->cuatrimestre, $this->padron, $this->ejercicio, $this->entrega, $this->intento, $prueba->id,
+ $prueba->memoria, $salida, '$descripcion')");
+ $this->db->query("UPDATE intento SET fin_pruebas = '$date'
+ WHERE cuatrimestre = {$this->cuatrimestre} AND
+ padron = {$this->padron} AND
+ ejercicio = {$this->ejercicio} AND
+ entrega = {$this->entrega} AND
+ intento = {$this->intento}");
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Curso_docente extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'curso_docente';
+ var $curso_id;
+ var $docente_id;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Curso_docente', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'curso_id' => DB_DATAOBJECT_INT,
+ 'docente_id' => DB_DATAOBJECT_INT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('curso_id', 'docente_id');
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Docente extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'docente';
+ var $id;
+ var $nombre;
+ var $mail;
+ var $corrige;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Docente', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'nombre' => DB_DATAOBJECT_STR,
+ 'mail' => DB_DATAOBJECT_STR,
+ 'corrige' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+ /**
+ *
+ * @return array Cursos en los que está el docente.
+ */
+ function getCursos($activo = true)
+ {
+ $c = DB_DataObject::factory('curso');
+ if ($activo)
+ {
+ $c->anio = date('Y');
+ $c->cuatrimestre = (date('n') < 8) ? 1 : 2; // Hasta agosto es 1er cuat
+ }
+ $cd = DB_DataObject::factory('curso_docente');
+ $cd->selectAs();
+ $cd->joinAdd($c);
+ $cd->joinAdd($this);
+ $cd->find();
+ $ret = array();
+ while ($cd->fetch())
+ {
+ $c->get($cd->curso_id);
+ $ret[] = $c;
+ }
+ return $ret;
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Ejercicio extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'ejercicio';
+ var $id;
+ var $nombre;
+ var $numero;
+ var $docente_id;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Ejercicio', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'nombre' => DB_DATAOBJECT_STR,
+ 'numero' => DB_DATAOBJECT_INT,
+ 'docente_id' => DB_DATAOBJECT_INT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+ function getFrom($nombre)
+ {
+ $this->nombre = $nombre;
+ return $this->find(true);
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Entrega extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'entrega';
+ var $id;
+ var $curso_id;
+ var $nro_ejercicio;
+ var $entrega;
+ var $ejercicio_id;
+ var $desde;
+ var $hasta;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Entrega', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'curso_id' => DB_DATAOBJECT_INT,
+ 'nro_ejercicio' => DB_DATAOBJECT_INT,
+ 'entrega' => DB_DATAOBJECT_INT,
+ 'ejercicio_id' => DB_DATAOBJECT_INT,
+ 'desde' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'hasta' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+ function getFrom($curso_id, $ejercicio, $entrega)
+ {
+ $this->curso_id = $curso_id;
+ $this->nro_ejercicio = $ejercicio;
+ $this->entrega = $entrega;
+ return $this->find(true);
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Inscripto extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'inscripto';
+ var $id;
+ var $padron;
+ var $curso_id;
+ var $mail;
+ var $activo;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Inscripto', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'padron' => DB_DATAOBJECT_INT,
+ 'curso_id' => DB_DATAOBJECT_INT,
+ 'mail' => DB_DATAOBJECT_STR,
+ 'activo' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+ function getFrom($padron, $activo = true)
+ {
+ $this->padron = $padron;
+ if (!is_null($activo)) $this->activo = $activo ? 1 : 0;
+ return $this->find(true);
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Intento extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'intento';
+ var $id;
+ var $inscripto_id;
+ var $entrega_id;
+ var $numero;
+ var $llegada;
+ var $inicio_prueba;
+ var $fin_prueba;
+ var $compila_ok;
+ var $notificado;
+ var $mail_respuesta;
+ var $observaciones;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Intento', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'inscripto_id' => DB_DATAOBJECT_INT,
+ 'entrega_id' => DB_DATAOBJECT_INT,
+ 'numero' => DB_DATAOBJECT_INT,
+ 'llegada' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'inicio_prueba' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'fin_prueba' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'compila_ok' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'notificado' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'mail_respuesta' => DB_DATAOBJECT_STR,
+ 'observaciones' => DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+
+class T_DBO_Prueba extends DB_DataObject
+{
+ var $_database = 'taller';
+ var $__table = 'prueba';
+ var $id;
+ var $intento_id;
+ var $caso_de_prueba_id;
+ var $inicio;
+ var $fin;
+ var $memoria_ok;
+ var $salida_ok;
+ var $observaciones;
+
+ /* Static get */
+ function staticGet($k, $v = NULL)
+ {
+ return DB_DataObject::staticGet('T_DBO_Prueba', $k, $v);
+ }
+
+ // now define your table structure.
+ // key is column name, value is type
+ function table()
+ {
+ return array(
+ 'id' => DB_DATAOBJECT_INT,
+ 'intento_id' => DB_DATAOBJECT_INT,
+ 'caso_de_prueba_id' => DB_DATAOBJECT_INT,
+ 'inicio' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'fin' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
+ 'memoria_ok' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'salida_ok' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
+ 'observaciones' => DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT,
+ );
+ }
+
+ // now define the keys.
+ function keys()
+ {
+ return array('id');
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+
+[inscripto]
+curso_id = curso:id
+
+[curso_docente]
+curso_id = curso:id
+docente_id = docente:id
+
+[ejercicio]
+docente_id = docente:id
+
+[entrega]
+curso_id = curso:id
+ejercicio_id = ejercicio:id
+
+[caso_de_prueba]
+ejercicio_id = ejercicio:id
+
+[intento]
+inscripto_id = inscripto:id
+entrega_id = entrega:id
+
+[prueba]
+intento_id = intento:id
+caso_de_prueba_id = caso_de_prueba:id
+
+[correccion]
+entrega_id = entrega:id
+inscripto_id = inscripto:id
+intento_id = intento:id
+docente_id = docente:id
+
+; vim: set et sw=4 sts=4 :
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+require_once 'DB.php';
+require_once 'DB/DataObject.php';
+require_once 'T/code.php';
+require_once 'T/Prueba.php';
+
+/// @todo Poner todos los querys con quoting ('?').
+/// @todo Mejorar manejo de errores con PEAR.
+class T_Intento
+{
+ var $base_dir;
+ var $cuatrimestre;
+ var $padron;
+ var $entrega;
+ var $ejercicio;
+ var $intento;
+
+ /**
+ * Constructor.
+ */
+ function T_Intento($padron = null, $ej = null, $ent = null, $intento = null, $cuat = null)
+ {
+ $this->__construct($padron, $ej, $ent, $intento, $cuat);
+ }
+
+ /**
+ * Constructor (PHP5).
+ */
+ function __construct($padron = null, $ej = null, $ent = null, $intento = null, $cuat = null)
+ {
+ $this->padron = $padron;
+ $this->ejercicio = $ej;
+ $this->entrega = $ent;
+ $this->intento = $intento;
+ $this->cuatrimestre = $cuat;
+ $this->base_dir = 'intentos';
+ }
+
+ /**
+ * Representación de string
+ */
+ function __toString()
+ {
+ return <<<EOT
+Entrega:
+ Padrón: {$this->padron}
+ Ejercicio: {$this->ejercicio}
+ Entrega: {$this->entrega}
+ Cuatrimestre: {$this->cuatrimestre}
+ Intento: {$this->intento}
+EOT;
+ }
+
+ /**
+ * Muestra el intento como una línea.
+ */
+ function to_line()
+ {
+ return "{$this->padron} {$this->ejercicio}.{$this->entrega}.{$this->intento} {$this->cuatrimestre}";
+ }
+
+ /**
+ * Toma de los atributos publicos padron, entrega y ejercicio
+ * En el atributo publico intento deja el numero de intento que corresponderia
+ * a esta entrega del alumno y en cuatrimestre el cuatrimestre en que cursa
+ * @param code Código verificador
+ * @param clave Clave a utilizar para validar el código
+ * @return string con la descripcion del error, si esta vacio fue ok
+ */
+ function validar_entrega($code, $claves)
+ {
+ $query = "SELECT C.id, C.anio, C.cuatrimestre, C.curso
+ FROM alumno A JOIN cuatrimestre C ON (A.id_cuatrimestre = C.id)
+ WHERE A.padron = {$this->padron} AND C.activo = 1";
+ $inscripto = DB_DataObject::factory('inscripto');
+ if (!$inscripto->getFrom($this->padron)) return 'El alumno no está inscripto';
+ $inscripto->getLinks();
+ $cuat = $inscripto->_curso_id->toLine();
+ $this->cuatrimestre = $cuat;
+ if (!T_chequear_codigo($code, $this->padron, $claves))
+ return 'La clave de la entrega no es válida';
+ $this->llegada = time();
+ $date = date('Y-m-d H:i:s', $this->llegada);
+ $entrega = DB_DataObject::factory('entrega');
+ $entrega->getFrom($inscripto->curso_id, $this->ejercicio, $this->entrega);
+var_dump($this);
+var_dump($inscripto->id);
+var_dump($inscripto->curso_id);
+var_dump($entrega->id);
+# 2005-01-06, 2005-02-26 20:00:00, 2005-02-03 19:48:15
+var_dump($entrega->desde, $entrega->hasta, $date);
+var_dump($entrega->desde <= $date);
+var_dump($entrega->hasta >= $date);
+ if (!($entrega->desde <= $date and $entrega->hasta >= $date)) return 'El ejercicio está siendo entregado fuera de fecha';
+ $intento = DB_DataObject::factory('intento');
+ $intento->id_entrega = $entrega->id;
+ $intento->id_inscripto = $inscripto->id;
+ $cant = $intento->find();
+ $this->intento = 1 + $cant;
+ $this->id_entrega = $entrega->id;
+ $this->id_inscripto = $inscripto->id;
+ return '';
+ }
+
+ /**
+ * Toma de los atributos publicos cuatrimestre, padron, entrega, ejercicio, mail e intento
+ * Debería llamarse antes a validar_entrega siempre
+ * En el atributo publico intento deja el numero de intento del alumno
+ * @param mail Mail con el que se recibió la entrega
+ * @return resultado del query
+ */
+ function hacer_entrega($mail)
+ {
+ $intento = DB_DataObject::factory('intento');
+ $intento->inscripto_id = $this->id_inscripto;
+ $intento->entrega_id = $this->id_entrega;
+ $intento->numero = $this->intento;
+ $intento->llegada = date('Y-m-d H:i:s', $this->llegada);
+ $intento->mail_respuesta = $mail;
+ return $intento->insert();
+ }
+
+ /**
+ * Pone en los atributos publicos cuatrimestre, padron, entrega, ejercicio e intento
+ * retorna true si encuentra alguno y false sino.
+ */
+ function proximo_a_probar()
+ {
+ $result = $this->db->getRow("SELECT cuatrimestre, padron, ejercicio, entrega, intento, mail
+ FROM intento
+ WHERE inicio_pruebas is null
+ ORDER BY llegada
+ LIMIT 1");
+ if (DB::isError($result) or is_null($result)) return false;
+ $date = date('YmdHis');
+ $this->cuatrimestre = $result[0];
+ $this->padron = $result[1];
+ $this->ejercicio = $result[2];
+ $this->entrega = $result[3];
+ $this->intento = $result[4];
+ $this->db->query("UPDATE intento SET inicio_pruebas = '$date'
+ WHERE cuatrimestre = {$this->cuatrimestre} AND
+ padron = {$this->padron} AND
+ ejercicio = {$this->ejercicio} AND
+ entrega = {$this->entrega} AND
+ intento = {$this->intento}");
+ return $result[5];
+ }
+
+ /**
+ * Toma de los atributos publicos cuatrimestre, padron, entrega, ejercicio e intento
+ * el parametro indica si compilo ok es true, sino es false
+ */
+ function informar_compilacion($compilo_ok)
+ {
+ if ($compilo_ok) $compilo_ok = 1;
+ else $compilo_ok = 0;
+ $date = date('YmdHis');
+
+ return $this->db->query("
+ UPDATE intento SET fin_pruebas = '$date' AND compila_ok = $compilo_ok
+ WHERE cuatrimestre = {$this->cuatrimestre} AND
+ padron = {$this->padron} AND
+ ejercicio = {$this->ejercicio} AND
+ entrega = {$this->entrega} AND
+ intento = {$this->intento}");
+ }
+
+ /**
+ * Toma de los atributos publicos cuatrimestre, padron, entrega, ejercicio e intento
+ * carga el atributo publico caso_de_prueba con el proximo caso de prueba a usar
+ * devuelve true si encuentra alguno, sino devuelve false
+ */
+ function pedir_caso_de_prueba()
+ {
+ $prueba = $this->db->getRow("
+ SELECT C.caso_de_prueba, C.privado, C.descripcion, C.parametros, C.salidas
+ FROM caso_de_prueba as C
+ WHERE C.cuatrimestre = $this->cuatrimestre AND
+ C.ejercicio = $this->ejercicio AND
+ C.entrega = $this->entrega AND
+ C.caso_de_prueba NOT IN (
+ SELECT P.caso_de_prueba from prueba as P
+ WHERE P.cuatrimestre = $this->cuatrimestre
+ AND P.entrega = $this->entrega
+ AND P.ejercicio = $this->ejercicio
+ AND P.padron = $this->padron
+ AND P.intento = $this->intento
+ )");
+ if (!is_null($prueba) && !DB::isError($prueba)) {
+ list($id, $priv, $desc, $params, $salidas) = $prueba;
+ return new T_Prueba($id, $priv, $desc, $params, $salidas);
+ }
+ return $prueba;
+ }
+
+ /**
+ * Toma de los atributos publicos cuatrimestre, padron, entrega, ejerciico e intento
+ * Carga los resultados de una prueba en un nuevo registro en prueba
+ */
+ function resultado_de_prueba($prueba, $salida, $descripcion = "")
+ {
+ if($memoria)$memoria = 1;
+ else $memoria = 0;
+ if($salida)$salida = 1;
+ else $salida = 0;
+ $date = date("YmdHis");
+ $this->db->query("INSERT INTO prueba
+ (cuatrimestre, padron, ejercicio, entrega, intento, caso_de_prueba, memoria_ok, salida_ok, descripcion)
+ VALUES ($this->cuatrimestre, $this->padron, $this->ejercicio, $this->entrega, $this->intento, $prueba->id,
+ $prueba->memoria, $salida, '$descripcion')");
+ $this->db->query("UPDATE intento SET fin_pruebas = '$date'
+ WHERE cuatrimestre = {$this->cuatrimestre} AND
+ padron = {$this->padron} AND
+ ejercicio = {$this->ejercicio} AND
+ entrega = {$this->entrega} AND
+ intento = {$this->intento}");
+ }
+
+ /**
+ * Busca una de los intentos no notificados cual tiene todas las pruebas terminadas y la
+ * hora de llegada + $hora es menor que ahora. el registro despues se marca como notificado.
+ * Deja en los atributos publicos cuatrimestre, padron, ejercicio, entrega, intento y mail
+ * en mail deja la suma del mail del intento y del mail del alumno
+ * devuelve el/los mails a donde mandar la notificación si encuentra alguno y false sino
+ */
+ function notificacion_pendiente($horas = 1)
+ {
+ $date = date("YmdHis");
+ $date -= 10000*$horas;
+ $row = $this->db->getRow("SELECT I.cuatrimestre as cuatrimestre, I.padron, I.ejercicio as ejercicio,
+ I.entrega as entrega, I.intento, I.mail
+ FROM intento I JOIN prueba P ON (I.ejercicio = P.ejercicio AND
+ I.cuatrimestre = P.cuatrimestre AND
+ I.entrega = P.entrega AND
+ I.padron = P.padron AND
+ I.intento = P.intento)
+ WHERE notificado is null
+ AND '$date' >= I.llegada
+ GROUP BY I.cuatrimestre, I.padron, I.ejercicio, I.entrega, I.intento, I.mail
+ HAVING count(P.memoria_ok) = (
+ SELECT count(1) FROM caso_de_prueba C
+ WHERE ejercicio = C.ejercicio AND
+ entrega = C.entrega AND
+ cuatrimestre = C.cuatrimestre)");
+
+ if(is_null($row) || is_a($row, "db_error")) return false;
+ $this->cuatrimestre = $row[0];
+ $this->padron = $row[1];
+ $this->ejercicio = $row[2];
+ $this->entrega = $row[3];
+ $this->intento = $row[4];
+ $mail = $row[5];
+ $this->db->query("UPDATE intento SET notificado = 1
+ WHERE cuatrimestre = $this->cuatrimestre and
+ padron = $this->padron and
+ ejercicio = $this->ejercicio and
+ entrega = $this->entrega and
+ intento = $this->intento");
+ $mail2 = $this->db->getOne("select mail_alumno FROM alumno
+ WHERE cuatrimestre = $this->cuatrimestre AND
+ padron = $this->padron");
+ if(strtolower($mail) != strtolower($mail2)) $mail .= ";$mail2";
+
+ return $mail;
+ }
+
+ /**
+ * Obtiene el directorio en el cual se almacena el intento.
+ */
+ function path() {
+ return $this->base_path() . "/{$this->padron}/{$this->intento}";
+ }
+
+ /**
+ * Obtiene el directorio base de la entrega.
+ * El directorio base no contempla los datos propios del intento, como
+ * padrón e intento.
+ * @param base_dir Directorio base, si no se especifica se usa el directorio
+ * base del intento.
+ */
+ function base_path($base_dir = null) {
+ if (is_null($base_dir)) $base_dir = $this->base_dir;
+ return "$base_dir/{$this->cuatrimestre}/{$this->ejercicio}-{$this->entrega}";
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sts=4 sw=4 :
+
+class T_Prueba
+{
+ var $id;
+ var $privado;
+ var $desc;
+ var $params;
+ var $salidas;
+ var $memoria;
+ var $stdin;
+ var $stdout;
+ var $stderr;
+
+ /**
+ * Constructor.
+ */
+ function T_Prueba($id, $priv, $desc, $params, $salidas)
+ {
+ $this->__construct($id, $priv, $desc, $params, $salidas);
+ }
+
+ /**
+ * Constructor (PHP5).
+ */
+ function __construct($id, $priv, $desc, $params, $salidas)
+ {
+ $this->id = $id;
+ $this->privado = $priv ? true : false;
+ $this->desc = $desc;
+ $this->params = $params;
+ $this->salidas = preg_split('/\s*,\s*/', $salidas);
+ $this->memoria = false;
+ $this->stdout = 'stdout';
+ }
+
+ function to_line()
+ {
+ return "id = {$this->id}, priv = {$this->privado}, desc = {$this->desc}, "
+ . "params = {$this->params}, mem = {$this->memoria}";
+ }
+
+ function __toString()
+ {
+ return <<<EOT
+Caso de Prueba: $this->id
+Privado: $this->privado
+Descripción: $this->desc
+Parámetros: $this->params
+Memoria: $this->memoria
+
+EOT
+ . 'Salidas: ' . join(',', $this->salidas);
+ }
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php // vim: set binary noeol et sw=4 sts=4:
+
+define('DB_DATAOBJECT_NO_OVERLOAD', 0);
+
+// Seteo umask para que el grupo pueda leer.
+umask(00027);
+
+// constantes de logging
+require_once 'T/logconstants.php';
+
+// obtengo configuración
+foreach (array('.', '/etc', '/etc/sercom') as $dir) {
+ $cf = "$dir/sercom.ini";
+ if (is_readable($cf)) {
+ if (!($CONF = @parse_ini_file($cf, true))) {
+ fputs(STDERR, "No se pudo abrir archivo de configuración '$cf' ($php_errormsg)!\n");
+ exit(1);
+ }
+ }
+}
+unset($cf, $dir);
+
+// Configuración de DB_DataObject
+require_once 'PEAR.php';
+require_once 'DB/DataObject.php';
+
+// this is the code used to load and store DataObjects Configuration.
+$DBO_options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+
+// because PEAR::getstaticProperty was called with and & (get by reference)
+// this will actually set the variable inside that method (a quasi static variable)
+$DBO_options = $CONF['DB_DataObject'];
+
+//unset($DBO_options);
+
+// shortcut
+$gconf = $CONF['general'];
+
+// nivel de errores mostrados
+if (isset($gconf['error_reporting'])) error_reporting($gconf['error_reporting']);
+
+// errores
+ini_set('track_errors', true);
+
+// locale
+$lang = getenv('LANG');
+setlocale(LC_ALL, $lang);
+
+// si no está seteado el nivel de logueo, uso el del archivo de config
+if (!isset($LOGLEVEL)) $LOGLEVEL = isset($gconf['loglevel']) ? $gconf['loglevel'] : L_INF;
+
+// Abro file pointer para loguear.
+if (!isset($LOGFP)) {
+ if (@$gconf['logfile']) {
+ $LOGFP = fopen($gconf['logfile'], 'a');
+ if (!$LOGFP) {
+ fputs(STDERR, "No se pudo abrir archivo de log '{$gconf['logfile']}' ($php_errormsg)!\n");
+ exit(2);
+ }
+ } else {
+ $LOGFP = STDERR;
+ }
+}
+
+// compatibilidad hacia adelante
+if (!function_exists('file_put_contents')) {
+ function file_put_contents($filename, $data) {
+ $fo = fopen($filename, 'w');
+ if ($fo === false) return false;
+ if (($ret = fwrite($fo, $data)) === false) return false;
+ if (!fclose($fo)) return false;
+ return $ret;
+ }
+}
+if (!function_exists('stream_get_contents')) {
+ function stream_get_contents($fp) {
+ $ret = '';
+ while (!feof($fp)) {
+ $ret .= fread($fp, 4096);
+ }
+ return $ret;
+ }
+}
+
+// carga de extensiones
+require_once 'T/dl.php';
+// funciones de logging
+require_once 'T/log.php';
+// funciones de códigos verificadores
+require_once 'T/code.php';
+// manejador de intentos
+require_once 'T/Intento.php';
+// data object
+
+unset($gconf);
+
+logs("Iniciando {$argv[0]}...");
+
+?>
\ No newline at end of file
--- /dev/null
+#!/usr/bin/php4
+<?php // vim: set binary noeol et sw=4 sts=4:
+
+require_once 'T/general.php';
+
+define('R_ERR', 0);
+define('R_OK', 1);
+
+// cargo extensión IMAP
+T_dl('imap');
+
+$mconf = $CONF['mail'];
+$gconf = $CONF['general'];
+
+$mailbox = @$mconf['mailbox'];
+switch (@$mconf['protocol'] ? $mconf['protocol'] : 'mbox') {
+ case 'mbox':
+ $mailbox = $mconf['server'];
+ break;
+ case 'pop':
+ case 'imap':
+ case 'nntp':
+ $port = @$mconf['port'] ? ":{$mconf['port']}" : '';
+ $options = @$mconf['options'] ? "/{$mconf['options']}" : '';
+ $mailbox = "{{$mconf['server']}$port/{$mconf['protocol']}$options}$mailbox";
+ break;
+ default:
+ die("Error: el protocolo {$mconf['protocol']} no está soportado.\n");
+}
+
+#$mbox = imap_open($mailbox, @$mconf['user'], @$mconf['pass']);
+
+/*
+echo "Mailboxes\n";
+$folders = imap_list($mbox, $mailbox, '*');
+
+if ($folders == false) {
+ echo "Call failed\n";
+} else {
+ while (list ($key, $val) = each($folders)) {
+ echo "$val\n";
+ }
+}
+*/
+
+/*
+echo "Headers in INBOX\n";
+$headers = imap_headers($mbox);
+
+if ($headers) {
+ while (list ($key, $val) = each ($headers)) {
+ echo "$val\n";
+ }
+} else {
+ echo "Call failed\n";
+}
+
+for ($i = 1; $i <= imap_num_msg($mbox); $i++)
+{
+ $header = imap_headerinfo($mbox, $i);
+ echo $header->fromaddress . "\n";
+ #var_dump(imap_headerinfo($mbox, $i, 80, 80));
+}
+*/
+
+$claves = $gconf['claves'];
+
+// Sin cesar.
+while (1) {
+ // Reseteo intervalo
+ $intervalo = $gconf['intervalo'];
+ // Abro mailbox o chillo.
+ if (!($mbox = imap_open($mailbox, @$mconf['user'], @$mconf['pass']))) {
+ logs('No se pudo conectar al servidor ('.imap_last_error().')', L_ERR);
+ sleep($intervalo);
+ }
+ logs('Conectado como '.@$mconf['user']." a $mailbox", L_DBG);
+ if (imap_num_msg($mbox) and $hdr = imap_headerinfo($mbox, 1)) {
+ logs("Nuevo mail '{$hdr->subject}' de {$hdr->fromaddress}");
+ @list($padron, $ej, $ent, $codigo) = validar_cabecera($hdr);
+ if ($padron) {
+ logs('Cabecera válida', L_DBG);
+ $intento = new T_Intento($padron, $ej, $ent);
+ if (!($err = $intento->validar_entrega($codigo, $claves))) {
+ logs('Entrega aceptada ('.$intento->to_line().')');
+ if (!($err = preparar_entrega($intento, $mbox, 1, $gconf['data_dir']))) {
+ logs('Intento preparado', L_DBG);
+ $res = $intento->hacer_entrega($hdr->fromaddress);
+ if (PEAR::isError($res)) {
+ logs('Error al hacer entrega (' . $res->getMessage() . ')', L_ERR);
+ enviar_respuesta(R_ERR, $hdr->fromaddress, "Error al preparar entrega:\nError en la base de datos.\n\nSe envió un mensaje al administrador avisando del problema.", $intento);
+ enviar_respuesta(R_ERR, $mconf['mail_admin'], "Error al preparar entrega:\n" . $res->getMessage(), $intento);
+ } else {
+ logs('Intento encolado para compilar');
+ enviar_respuesta(R_OK, $hdr->fromaddress, null, $intento);
+ guardar_mbox($mbox, 1, $hdr, @$mconf['mbox_bak']);
+ }
+ } else {
+ logs("Error al preparar entrega ($err)", L_ERR);
+ enviar_respuesta(R_ERR, $hdr->fromaddress, "Error al preparar entrega:\n$err", $intento);
+ enviar_respuesta(R_ERR, $mconf['mail_admin'], "Error al preparar entrega:\n$err.\n\nSe envió un mensaje al administrador avisando del problema.", $intento);
+ }
+ } else {
+ logs("Entrega rechazada ($err)");
+ enviar_respuesta(R_ERR, $hdr->fromaddress, $err, $intento);
+ }
+ } else {
+ logs('Entrega rechazada (subject inválido)');
+ enviar_respuesta(R_ERR, $hdr->fromaddress, "Asunto (subject) inválido.\n\n"
+ . "Recuerde que el formato del asunto es estricto:\n"
+ . "[padrón] [ejercicio].[entrega] [código]\n\n"
+ . "Donde [padrón] es su número de padrón, [ejercicio] es el número de\n"
+ . "ejercicio (1 a 4), [entrega] el número de entrega (1 o 2) y [código]\n"
+ . "es el código verificador que le fue asignado por la cátedra.\n"
+ );
+ }
+ imap_delete($mbox, 1);
+ logs('Mail borrado');
+ imap_expunge($mbox);
+ logs('Mensajes purgandos', L_DBG);
+ $intervalo = 0; // Que tome el próximo mail enseguida.
+ } else {
+ logs('No hay mail nuevo', L_DBG);
+ }
+ logs('Cerrando conexión', L_DBG);
+ imap_close($mbox);
+ sleep($intervalo);
+}
+
+
+/*
+do {
+ $mail = imap_fetchstructure($mbox, 1);
+ var_dump($mail);exit;
+ for ($mail->parts as $id => $part) {
+ if ($part->type == TYPEAPPLICATION) {
+ }
+ }
+} while (!validate_header($header));
+
+
+file_put_contents('attach', fetchbody_decoded($mbox, 4, 2))
+ or die("no se puede escribir archivo de salida\n");
+
+imap_close($mbox);
+*/
+
+function enviar_respuesta($tipo, $to, $mensaje = '', $intento = null) {
+ global $mconf;
+ $subject = $mconf['prefijo'] . ' Entrega ';
+ if ($tipo == R_OK) $estado = 'ACEPTADA';
+ else $estado = 'RECHAZADA';
+ $subject .= $estado;
+ $body .= "Estado: $estado\n";
+ if ($mensaje) $body .= "\n$mensaje\n";
+ if ($intento) $body .= "\n" . $intento->__toString() . "\n";
+ logs("Envío de mail '$subject' a '$to'\n$body\n", L_DBG);
+ $headers = <<<EOT
+From: {$mconf['from']}
+Reply-To: {$mconf['mail_admin']}
+X-Mailer: {$mconf['mailer']}
+X-Priority: 5
+EOT;
+ mail($to, $subject, $body, $headers);
+ return true;
+}
+
+/**
+ * @returns array($padron, $ej, $entrega, $codigo) o false si no es válida.
+ */
+function validar_cabecera($hdr) {
+ // Subject: padron nro_ej nro_entrega clave_alumno
+ if (preg_match('/^\s*(\d{5})\s+([1-4])\.([12])\s+(.*)$/', $hdr->subject, $m)) {
+ return array_slice($m, 1, 5);
+ }
+ return false;
+}
+
+function preparar_entrega($intento, $mbox, $msgid, $dir) {
+ logs('Acá debería verificar el cuerpo del mensaje', L_DBG);
+ $mail = imap_fetchstructure($mbox, $msgid);
+ foreach ($mail->parts as $id => $part) {
+ $fname = part_filename($part);
+ if ($fname) {
+ logs("Escribiendo archivo '$fname' [enc={$part->encoding}]", L_DBG);
+ $body = imap_fetchbody($mbox, $msgid, $id + 1);
+ $path = "$dir/" . $intento->path();
+ if (!mkdir_p($path)) return 'No se pudo crear el directorio';
+ if (!file_put_contents("$path/$fname", decode_body($body, $part->encoding))) return "Error al guardar el archivo $fname";
+ }
+ //if (part_is_source($part) $has_sources = true;
+ //elseif (part_mime_type($part) == 'application/zip') $has_sources = true;
+ }
+ return '';
+}
+
+function part_is_source($part) {
+ if (part_mime_type($part) == 'application/zip') return true;
+/* if (part_mime_type($part) == 'application/octet-stream') {
+ $extension = (part_filename($part));
+ if (, {
+ }
+ } */
+ return false;
+}
+
+function part_filename($part) {
+ if (!@$part->dparameters or !is_array($part->dparameters)) {
+ return '';
+ }
+ foreach ($part->dparameters as $param) {
+ if ($param->attribute == 'FILENAME') {
+ return $param->value;
+ }
+ }
+ return '';
+}
+
+function part_mime_type($part) {
+ switch ($part->type) {
+ case TYPETEXT:
+ return 'text/'.strtolower($part->subtype);
+ case TYPEMULTIPART:
+ return 'multipart/'.strtolower($part->subtype);
+ case TYPEMESSAGE:
+ return 'message/'.strtolower($part->subtype);
+ case TYPEAPPLICATION:
+ return 'application/'.strtolower($part->subtype);
+ case TYPEAUDIO:
+ return 'audio/'.strtolower($part->subtype);
+ case TYPEIMAGE:
+ return 'image/'.strtolower($part->subtype);
+ case TYPEVIDEO:
+ return 'video/'.strtolower($part->subtype);
+ case TYPEOTHER:
+ default:
+ return 'other/'.strtolower($part->subtype);
+ }
+}
+
+function decode_body($body, $encoding) {
+ switch ($encoding) {
+ case ENC7BIT: return fix_eol($body);
+ case ENC8BIT: return fix_eol($body); //imap_8bit($body);
+ case ENCBINARY: return imap_binary($body);
+ case ENCBASE64: return imap_base64($body);
+ case ENCQUOTEDPRINTABLE: return imap_qprint($body);
+ case ENCOTHER: return $body;
+ }
+ logs('Encoding no reconocido.', L_ERR);
+ return $body;
+}
+
+function mkdir_p($target) {
+ if (is_dir($target) or empty($target)) return 1;
+ if (file_exists($target) and !is_dir($target)) return 0;
+ if (mkdir_p(substr($target,0,strrpos($target,'/')))) return mkdir($target);
+ return 0;
+}
+
+function guardar_mbox($mbox, $msgid, $hdr, $mbox_fname) {
+ if ($mbox_fname) {
+ $fo = @fopen($mbox_fname, 'a');
+ if (!$fo) {
+ logserr("No se pudo abrir mbox '$mbox_fname'");
+ return false;
+ }
+ fputs($fo, sprintf("From %s@%s %s\n", $hdr->from[0]->mailbox, $hdr->from[0]->host, date('D M j H:i:s Y')));
+ fputs($fo, fix_eol(imap_fetchheader($mbox, $msgid, FT_PREFETCHTEXT)));
+ fputs("\n");
+ fputs($fo, fix_eol(imap_body($mbox, $msgid)));
+ fclose($fo);
+ }
+ return true;
+}
+
+function fix_eol($str) {
+ return str_replace("\r\n", "\n", $str);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+#!/usr/bin/php
+<?php // vim: set binary noeol et sw=4 sts=4:
+
+require_once 'T/general.php';
+
+define('R_ERR', 0);
+define('R_OK', 1);
+
+$LOGLEVEL = L_ALL;
+
+$gconf = $CONF['general'];
+
+// Obtengo id de usuario con el cual ejecutar las pruebas.
+if (!($usrinfo = @posix_getpwnam($gconf['user']))) {
+ logserr("No existe el usuario '{$gconf['user']}'");
+ exit(10); // salgo con error
+}
+$UID = $usrinfo['uid'];
+$GID = $usrinfo['gid'];
+/*if (!@posix_seteuid($usrinfo['uid'])) {
+ logserr("No se puede cambiar el uid efectivo a '{$usrinfo['uid']}'");
+ exit(11); // salgo con error
+}*/
+
+$intento = new T_Intento;
+
+// Sin cesar.
+while (1) {
+ if (!($mail = $intento->proximo_a_probar())) {
+ logs('No hay intento para probar', L_DBG);
+ sleep($gconf['intervalo']);
+ continue;
+ }
+ logs('Nuevo intento a probar (' . $intento->to_line() . ')');
+
+ $intento_dir = "{$gconf['data_dir']}/" . $intento->path();
+ $entrega_dir = "{$gconf['data_dir']}/" . $intento->base_path('entregas');
+
+ if (is_readable("$entrega_dir/Makefile")) {
+ $makefile = "$entrega_dir/Makefile";
+//XXX @copy("$entrega_dir/Makefile", "$intento_dir/Makefile")
+// or logserr("Error al copiar Makefile (de '$entrega_dir/Makefile' a '$intento_dir/Makefile'");
+ // Fallback
+ } elseif (is_readable("{$gconf['data_dir']}/Makefile")) {
+ $makefile = "{$gconf['data_dir']}/Makefile";
+//XXX if (!@copy("{$gconf['data_dir']}/Makefile", "$intento_dir/Makefile")) {
+ } else {
+ enviar_respuesta_error_log($mail, 'No hay un Makefile disponible', $intento);
+ continue;
+ }
+
+ $currdir = getcwd();
+ if (!@chdir($intento_dir)) {
+ enviar_respuesta_error_log($mail, 'Error al cambiar al directorio del tp', $intento);
+ continue;
+ }
+ logs("Cambio de directorio '$currdir' -> '$intento_dir'", L_DBG);
+ logs('Ejecutando el comando: make -f '.escapeshellarg($makefile), L_DBG);
+ if (exec_get_info('make -f '.escapeshellarg($makefile), $ret, $err, $out)) {
+ if ($ret) {
+ logs('Error al compilar');
+ logs("Código de retorno: $ret, mensaje: $err)", L_DBG);
+ //XXX $intento->informar_compilacion(false);
+ enviar_respuesta(R_ERR, $mail, "ERROR AL COMPILAR!\n\n$err\n\nCódigo de retorno: $ret\n", $intento);
+ continue;
+ } else {
+ logs('Compilado OK');
+ //XXX $intento->informar_compilacion(true);
+ // TODO mail acumulativo
+ // enviar_respuesta(R_OK, $mail, 'El intento fue compilado con éxito!', $intento);
+ }
+ } else {
+ //XXX $intento->informar_compilacion(false);
+ enviar_respuesta_error_log($mail, 'No se pudo ejecutar make', $intento);
+ continue;
+ }
+ if (!@mkdir('chroot')) {
+ enviar_respuesta_error_log($mail, 'Error al crear directorio para chroot', $intento);
+ continue;
+ }
+ if (!@rename('tp', 'chroot/tp')) {
+ enviar_respuesta_error_log($mail, 'Error al mover el tp al chroot', $intento);
+ continue;
+ }
+ if (!@copy("{$gconf['data_dir']}/redir", 'chroot/redir')) {
+ enviar_respuesta_error_log($mail, 'Error al copiar redireccionador al chroot', $intento);
+ continue;
+ }
+ if ($err = chmod_own_grp('chroot', 02770, $UID, $GID)) {
+ enviar_respuesta_error_log($mail, $err, $intento);
+ continue;
+ }
+ if ($err = chmod_own_grp('chroot/tp', 0550, $UID, $GID)) {
+ enviar_respuesta_error_log($mail, $err, $intento);
+ continue;
+ }
+ if ($err = chmod_own_grp('chroot/redir', 0550, 0, 0)) {
+ enviar_respuesta_error_log($mail, $err, $intento);
+ continue;
+ }
+
+ // comienza la ejecución de casos de prueba
+ while ($prueba = $intento->pedir_caso_de_prueba()) {
+ logs('Prueba: ' . $prueba->to_line(), L_DBG);
+ // ejecuta con fork
+ $pid = pcntl_fork();
+ if ($pid == -1) {
+ // Error al forkear
+ $intento->resultado_de_prueba($prueba, false, 'Error al forkear proceso');
+ enviar_respuesta_error_log($mail, 'Error al forkear proceso', $intento);
+ continue 2;
+ } elseif ($pid) {
+ // Estamos en el padre, controlamos el tiempo.
+ logs("En el padre (hijo: $pid)", L_DBG);
+ // TODO controlar tiempo.
+ pcntl_waitpid($pid, $exitcode);
+ logs("Fin de ejecución de caso de prueba (hijo: $pid, ret: $exitcode)", L_DBG);
+ $stderr = false; // FIXME ver si salida de error es vacia.
+ if ($exitcode or $stderr) {
+ logs('Comando ejecutado ERROR', L_DBG);
+ if ($ret) {
+ $msg = "El programa salió con código de error $ret";
+ $msgs[] = $msg;
+ logs($msg, L_DBG);
+ }
+ if ($stderr) {
+ $msg = "El programa imprimió mensajes en la salida de error: '$stderr'";
+ $msgs[] = $msg;
+ logs($msg, L_DBG);
+ }
+ $msg = join("\n", $msgs);
+ $intento->resultado_de_prueba($prueba, false, $msg);
+ enviar_respuesta(R_ERR, $mail, $msg, $intento);
+ exit(0); // salgo ok del hijo
+ } else { // Sin errores en la ejecución.
+ logs('Comando ejecutado OK', L_DBG);
+ // TODO
+ $salidas = array();
+ foreach ($prueba->salidas as $salida) {
+ // TODO hacer diffs.
+ if ($salida == 'stdout') {
+ $salidas[$salida] = $stdout;
+ } else {
+ $salidas[$salida] = file_get_contents($salida);
+ }
+ }
+ logs('Salidas: ' . var_export($salidas, true), L_DBG);
+ $intento->resultado_de_prueba($prueba, true, 'Salidas: ' . var_export($salidas, true));
+ enviar_respuesta(R_ERR, $mail, 'Salidas: ' . var_export($salidas, true), $intento);
+ exit(0); // salgo ok del hijo
+ }
+ } else {
+ // Estamos en el hijo, corremos en chroot.
+ logs('En el hijo', L_DBG);
+ // Hago chroot.
+ if (!@chroot('chroot')) {
+ $intento->resultado_de_prueba($prueba, false, 'Error al hacer chroot');
+ enviar_respuesta_error_log($mail, 'Error al hacer chroot', $intento);
+ exit(1); // salgo del hijo
+ }
+ logs('Chrooteado, cwd = '.getcwd(), L_DBG);
+ if (!@posix_setuid($usrinfo['uid'])) {
+ $intento->resultado_de_prueba($prueba, false, "Error al cambiar al uid '{$usrinfo['uid']}'");
+ enviar_respuesta_error_log($mail, "Error al cambiar al uid '{$usrinfo['uid']}'", $intento);
+ exit(2); // salgo del hijo
+ }
+ // TODO poner stdout y stderr en archivos para hacer diff o hacer diffs en memoria
+ $stdout = null;
+ if (in_array('stdout', $prueba->salidas)) $stdout = '';
+ logs('Se ejecutará: chroot chroot /tp ' . escapeshellarg($prueba->params), L_DBG);
+ if (!pcntl_exec('/redir', array('tp', 'stdin', 'stdout', 'stderr', '/tp', '0', $prueba->params))) {
+ logserr('No se pudo ejecutar el comando');
+ $intento->resultado_de_prueba($prueba, false, 'No se pudo ejecutar el tp');
+ enviar_respuesta_error_log($mail, 'No se pudo ejecutar el tp', $intento);
+ exit(4); // salgo del hijo
+ }
+ exit(100); // salgo del hijo (no deberia llegar nunca aca).
+ }
+ break; // FIXME
+ }
+
+ // TODO make clean
+}
+
+
+/**
+ * Ejecuta un comando devolviendo el código de error y las salidas.
+ *
+ * @param cmd Comando a ejecutar.
+ * @param ret Código de retorno.
+ * @param err Salida de error (se obtiene sólo si es != null).
+ * @param out Salida estándar (se obtiene sólo si es != null).
+ * @param in Entrada a enviarle al comando (null si no se le envía nada).
+ * @return true si se ejecutó el comando, false si no.
+ */
+function exec_fds($cmd, &$ret, $in = false, $out = false, $in = false) {
+ $descriptors = array();
+ if ($in) $descriptors[0] = get_fd_type($in, 'r');
+ if ($out) $descriptors[1] = get_fd_type($out, 'w');
+ if ($err) $descriptors[2] = get_fd_type($err, 'w');
+var_dump($descriptors);
+ $proc = proc_open($cmd, $descriptors, $pipes);
+ if (is_resource($proc)) {
+ $ret = proc_close($proc);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Obtiene un tipo de descriptor para proc_open().
+ *
+ * @param fd Tipo de descriptor: si es true, se usa un pipe, si es un array
+ * se usa el array y si es otra cosa se usa un archivo con este
+ * nombre.
+ * @param mode Modo de apertura, sólo se usa si es true o string.
+ * @return Array a pasar a proc_open().
+ */
+function get_fd_type($fd, $mode = false) {
+ if ($fd === true) {
+ return array('pipe', $mode);
+ } elseif (is_array($fd)) {
+ return $fd;
+ }
+ return array('file', $fd, $mode);
+}
+
+/**
+ * Ejecuta un comando devolviendo el código de error y las salidas.
+ *
+ * @param cmd Comando a ejecutar.
+ * @param ret Código de retorno.
+ * @param err Salida de error (se obtiene sólo si es != null).
+ * @param out Salida estándar (se obtiene sólo si es != null).
+ * @param in Entrada a enviarle al comando (null si no se le envía nada).
+ * @return true si se ejecutó el comando, false si no.
+ */
+function exec_get_info($cmd, &$ret, &$err, &$out, $in = null) {
+ $descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
+ $proc = proc_open($cmd, $descriptors, $pipes);
+ if (is_resource($proc)) {
+ if (is_null($in)) {
+ fputs($pipes[0], $in);
+ }
+ if (is_null($out)) {
+ $out = stream_get_contents($pipes[1]);
+ }
+ if (is_null($err)) {
+ $err = stream_get_contents($pipes[2]);
+ }
+ foreach (array(0,1,2) as $i) {
+ fclose($pipes[$i]);
+ }
+ $ret = proc_close($proc);
+ return true;
+ }
+ return false;
+}
+
+function enviar_respuesta($tipo, $to, $mensaje = '', $intento = null) {
+ $mconf = $GLOBALS['CONF']['mail'];
+ $subject = $mconf['prefijo'] . ' Prueba ';
+ if ($tipo == R_OK) $estado = 'OK';
+ else $estado = 'ERROR';
+ $subject .= $estado;
+ $body .= "Estado: $estado\n";
+ if ($mensaje) $body .= "\n$mensaje\n";
+ if ($intento) $body .= "\n" . $intento->__toString() . "\n";
+ logs("Envío de mail '$subject' a '$to'\n$body\n", L_DBG);
+ $headers = <<<EOT
+From: {$mconf['from']}
+Reply-To: {$mconf['mail_admin']}
+X-Mailer: {$mconf['mailer']}
+X-Priority: 5
+EOT;
+ mail($to, $subject, $body, $headers);
+ mail($GLOBALS['CONF']['mail_admin'], $subject, $body, $headers);
+ return true;
+}
+
+/**
+ * Cambia permisos, dueño y grupo a un archivo, devuelve string con error o
+ * false si no hay error.
+ */
+function chmod_own_grp($file, $mod, $own, $grp) {
+ if (!@chmod($file, $mod)) return "Error al cambiar permisos [$mod] a '$file'";
+ if (!@chown($file, $own)) return "Error al cambiar dueño [$own] a '$file'";
+ if (!@chgrp($file, $grp)) return "Error al cambiar grupo [$grp] a '$file'";
+ return false;
+}
+
+function enviar_respuesta_error_log($to, $msg = '', $intento = null) {
+ logserr($msg);
+ enviar_respuesta(R_ERR, $to, "ERROR: $msg\n\nSe envió un mail al administrador para revisar el problema.\n", $intento);
+ enviar_respuesta(R_ERR, $GLOBALS['CONF']['mail']['mail_admin'], $msg, $intento);
+}
+
+?>
\ No newline at end of file