]> git.llucax.com Git - software/sercom-old.git/blob - src/sc_fetch
Se mejora aclaración sobre subject inválido.
[software/sercom-old.git] / src / sc_fetch
1 #!/usr/bin/php4
2 <?php // vim: set binary noeol et sw=4 sts=4:
3
4 // Incluyo directorio del ejecutable como posible directorio de bibliotecas
5 set_include_path(get_include_path().':'.dirname($argv[0]));
6
7 require_once 'T/general.php';
8
9 define('R_ERR', 0);
10 define('R_OK',  1);
11
12 // cargo extensión IMAP
13 T_dl('imap');
14
15 $mconf = $CONF['mail'];
16 $gconf = $CONF['general'];
17
18 $mailbox = @$mconf['mailbox'];
19 switch (@$mconf['protocol']) {
20     case 'mbox':
21         $mailbox = $mconf['server'];
22         break;
23     case 'pop':
24     case 'imap':
25     case 'nntp':
26         $port    = @$mconf['port'] ? ":{$mconf['port']}" : '';
27         $options = @$mconf['options'] ? "/{$mconf['options']}" : '';
28         $mailbox = "{{$mconf['server']}$port/{$mconf['protocol']}$options}$mailbox";
29         break;
30     default:
31         logsdie("Error: el protocolo {$mconf['protocol']} no está soportado");
32 }
33
34 #$mbox = imap_open($mailbox, @$mconf['user'], @$mconf['pass']);
35
36 /*
37 echo "Mailboxes\n";
38 $folders = imap_list($mbox, $mailbox, '*');
39
40 if ($folders == false) {
41     echo "Call failed\n";
42 } else {
43     while (list ($key, $val) = each($folders)) {
44         echo "$val\n";
45     }
46 }
47 */
48
49 /*
50 echo "Headers in INBOX\n";
51 $headers = imap_headers($mbox);
52
53 if ($headers) {
54     while (list ($key, $val) = each ($headers)) {
55         echo "$val\n";
56     }
57 } else {
58     echo "Call failed\n";
59 }
60
61 for ($i = 1; $i <= imap_num_msg($mbox); $i++)
62 {
63     $header = imap_headerinfo($mbox, $i);
64     echo $header->fromaddress . "\n";
65     #var_dump(imap_headerinfo($mbox, $i, 80, 80));
66 }
67 */
68
69 $claves = $gconf['claves'];
70
71 // Errores tratando de conectar al mail
72 $mbox_errcount = 0;
73
74 // Sin cesar.
75 while (1) {
76     // Reseteo intervalo
77     $intervalo = $gconf['intervalo'];
78     // Abro mailbox o chillo.
79     if (!($mbox = @imap_open($mailbox, @$mconf['user'], @$mconf['pass']))) {
80         if ($mbox_errcount * $intervalo > 120) { // 2 horas sin poder conectarse
81             logs('Hace 2 horas que no se puede conectar al servidor ('.imap_last_error().')', ERROR);
82             $mbox_errcount = 0;
83         } else {
84             logs('No se pudo conectar al servidor ('.imap_last_error().')', WARNING);
85         }
86         $mbox_errcount++;
87         sleep($intervalo);
88         continue;
89     }
90     $mbox_errcount = 0;
91     logs('Conectado como '.@$mconf['user']." a $mailbox", DEBUG);
92     if (imap_num_msg($mbox) and $hdr = imap_headerinfo($mbox, 1)) {
93         logs(sprintf("Nuevo mail '%s' de %s", decode_header($hdr->subject), decode_header($hdr->subject)));
94         @list($padron, $ej, $ent, $codigo) = validar_cabecera($hdr);
95         if ($padron) {
96             logs('Cabecera válida', DEBUG);
97             $intento = new T_Intento($padron, $ej, $ent);
98             if (!($err = $intento->validar_entrega($codigo, $claves))) {
99                 logs('Entrega aceptada ('.$intento->to_line().')');
100                 if (!($err = preparar_entrega($intento, $mbox, 1, $gconf['data_dir']))) {
101                     logs('Intento preparado', DEBUG);
102                     $res = $intento->hacer_entrega($hdr->fromaddress);
103                     if (PEAR::isError($res)) {
104                         logs('Error al hacer entrega (' . $res->getMessage() . ')', ERROR);
105                         enviar_respuesta(R_ERR, $hdr->fromaddress, "Error interno al preparar entrega.\n\nSe envió un mensaje al administrador avisando del problema.", $intento);
106                         enviar_respuesta(R_ERR, $mconf['admin'], "Error de la DB al preparar entrega:\n" . $res->getMessage(), $intento);
107                     } else {
108                         logs('Intento encolado para compilar', DEBUG);
109                         enviar_respuesta(R_OK, $hdr->fromaddress, null, $intento);
110                         guardar_mbox($mbox, 1, $hdr, @$mconf['mbox_bak']);
111                     }
112                 } else {
113                     logs("Error al preparar entrega ($err)", ERROR);
114                     enviar_respuesta(R_ERR, $hdr->fromaddress, "Error interno al preparar entrega.\n\nSe envió un mensaje al administrador avisando del problema.", $intento);
115                     enviar_respuesta(R_ERR, $mconf['admin'], "Error al preparar entrega:\n$err", $intento);
116                 }
117             } else {
118                 logs("Entrega rechazada ($err)", WARNING);
119                 enviar_respuesta(R_ERR, $hdr->fromaddress, $err, $intento);
120             }
121         } else {
122             logs('Entrega rechazada (subject inválido)', WARNING);
123             enviar_respuesta(R_ERR, $hdr->fromaddress, "Asunto (subject) inválido.\n\n"
124                     . "Recuerde que el formato del asunto es estricto:\n"
125                     . "[padrón] [ejercicio].[entrega] [código]\n\n"
126                     . "Donde [padrón] es su número de padrón, [ejercicio] es el número de\n"
127                     . "ejercicio (1 a 4), [entrega] el número de entrega (1 entrega, 2 reentrega)\n"
128                     . "y [código] es el código verificador que le fue asignado por la cátedra.\n"
129             );
130         }
131         imap_delete($mbox, 1);
132         logs('Mail borrado', DEBUG);
133         imap_expunge($mbox);
134         logs('Mensajes purgandos', DEBUG);
135         $intervalo = 0; // Que tome el próximo mail enseguida.
136     } else {
137         logs('No hay mail nuevo', DEBUG);
138     }
139     logs('Cerrando conexión', DEBUG);
140     imap_close($mbox);
141     sleep($intervalo);
142 }
143
144
145 /*
146 do {
147     $mail = imap_fetchstructure($mbox, 1);
148     var_dump($mail);exit;
149     for ($mail->parts as $id => $part) {
150         if ($part->type == TYPEAPPLICATION) {
151         }
152     }
153 } while (!validate_header($header));
154
155
156 file_put_contents('attach', fetchbody_decoded($mbox, 4, 2))
157     or die("no se puede escribir archivo de salida\n");
158
159 imap_close($mbox);
160 */
161
162 function enviar_respuesta($tipo, $to, $mensaje = '', $intento = null) {
163     global $mconf;
164     $subject = '[' . $mconf['prefijo'] . '] Entrega ';
165     if ($tipo == R_OK) $estado = 'ACEPTADA';
166     else               $estado = 'RECHAZADA';
167     $subject .= $estado;
168     $body .= "Estado: $estado\n";
169     if ($mensaje) $body .= "\n$mensaje\n";
170     if ($intento) $body .= "\n" . $intento->__toString() . "\n";
171     logs("Envío de mail '$subject' a '$to'\n$body\n", DEBUG);
172     $headers = <<<EOT
173 From: {$mconf['from']}
174 Reply-To: {$mconf['admin']}
175 Return-Path: {$mconf['admin']}
176 X-Mailer: $NAME $VERSION
177 X-Priority: 5
178 EOT;
179     mail(decode_header($to), $subject, $body, $headers);
180     return true;
181 }
182
183 /**
184  * @returns array($padron, $ej, $entrega, $codigo) o false si no es válida.
185  */
186 function validar_cabecera($hdr) {
187     // Subject: padron nro_ej nro_entrega clave_alumno
188     $subject = decode_header($hdr->subject);
189     if (preg_match('/^\s*(\d{5})\s+([1-4])\.([12])\s+(.*)$/', $subject, $m)) {
190         return array_slice($m, 1, 5);
191     }
192     return false;
193 }
194
195 function preparar_entrega($intento, $mbox, $msgid, $dir) {
196     logs('Acá debería verificar el cuerpo del mensaje', DEBUG);
197     $path = "$dir/" . $intento->path('intentos');
198     $mail = imap_fetchstructure($mbox, $msgid);
199     if (!mkdir_p($path)) return 'No se pudo crear el directorio';
200     foreach ($mail->parts as $id => $part) {
201         $fname = part_filename($part);
202         if ($fname) {
203             logs("Escribiendo archivo '$fname' [enc={$part->encoding}]", DEBUG);
204             $body = imap_fetchbody($mbox, $msgid, $id + 1);
205             if (!file_put_contents("$path/$fname", decode_body($body, $part->encoding))) return "Error al guardar el archivo $fname";
206         }
207         //if (part_is_source($part) $has_sources = true;
208         //elseif (part_mime_type($part) == 'application/zip') $has_sources = true;
209     }
210     return '';
211 }
212
213 function part_is_source($part) {
214     if (part_mime_type($part) == 'application/zip') return true;
215 /*    if (part_mime_type($part) == 'application/octet-stream') {
216         $extension = (part_filename($part));
217         if (,  {
218         }
219     } */
220     return false;
221 }
222
223 function part_filename($part) {
224     if (!@$part->dparameters or !is_array($part->dparameters)) {
225         return '';
226     }
227     foreach ($part->dparameters as $param) {
228         if ($param->attribute == 'FILENAME') {
229             return $param->value;
230         }
231     }
232     return '';
233 }
234
235 function part_mime_type($part) {
236     switch ($part->type) {
237         case TYPETEXT:
238             return 'text/'.strtolower($part->subtype);
239         case TYPEMULTIPART:
240             return 'multipart/'.strtolower($part->subtype);
241         case TYPEMESSAGE:
242             return 'message/'.strtolower($part->subtype);
243         case TYPEAPPLICATION:
244             return 'application/'.strtolower($part->subtype);
245         case TYPEAUDIO:
246             return 'audio/'.strtolower($part->subtype);
247         case TYPEIMAGE:
248             return 'image/'.strtolower($part->subtype);
249         case TYPEVIDEO:
250             return 'video/'.strtolower($part->subtype);
251         case TYPEOTHER:
252         default:
253             return 'other/'.strtolower($part->subtype);
254     }
255 }
256
257 function decode_body($body, $encoding) {
258     switch ($encoding) {
259         case ENC7BIT:            return fix_eol($body);
260         case ENC8BIT:            return fix_eol($body); //imap_8bit($body);
261         case ENCBINARY:          return imap_binary($body);
262         case ENCBASE64:          return imap_base64($body);
263         case ENCQUOTEDPRINTABLE: return imap_qprint($body);
264         case ENCOTHER:           return $body;
265     }
266     logs('Encoding no reconocido.', WARNING);
267     return $body;
268 }
269
270 function mkdir_p($target) {
271     if (is_dir($target) or empty($target))               return 1;
272     if (file_exists($target) and !is_dir($target))       return 0;
273     if (mkdir_p(substr($target,0,strrpos($target,'/')))) return mkdir($target);
274     return 0;
275 }
276
277 function guardar_mbox($mbox, $msgid, $hdr, $mbox_fname) {
278     if ($mbox_fname) {
279         $fo = @fopen($mbox_fname, 'a');
280         if (!$fo) {
281             logserr("No se pudo abrir mbox '$mbox_fname'");
282             return false;
283         }
284         fputs($fo, sprintf("From %s@%s %s\n", $hdr->from[0]->mailbox, $hdr->from[0]->host, date('D M j H:i:s Y')));
285         fputs($fo, fix_eol(imap_fetchheader($mbox, $msgid, FT_PREFETCHTEXT)));
286         fputs("\n");
287         fputs($fo, fix_eol(imap_body($mbox, $msgid)));
288         fclose($fo);
289     }
290     return true;
291 }
292
293 function fix_eol($str) {
294     return str_replace("\r\n", "\n", $str);
295 }
296
297 function decode_header($str) {
298     $elems = imap_mime_header_decode($str);
299     $result = '';
300     foreach ($elems as $elem) {
301         $result .= $elem->text;
302     }
303     return $result;
304 }
305
306 ?>