• Publicidad

Transporte SMS

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Transporte SMS

Notapor jcovenas » 2013-06-11 08:40 @403

Estimados, nuevamente escribo para consultar sobre un script de Perl que, aunque un poco viejito, me pareció muy útil.

El script recibe la dirección del remitente en $ARGV[0], el número de teléfono como $ARGV[1] y el cuerpo del mensaje en STDIN.

Sólo debemos recibir un mensaje por usuario, luego envía el cuerpo del mensaje al dæmon gnokii-smsd para su envío por sms.

El inconveniente que tengo es que al recibir el correo formateado en HTML, envía "Basurita" al sms. Sin embargo, sí funciona cuando se envía el mensaje sin formato de texto.

Me gustaría que pudieran ayudarme a entender el código para realizar las modificaciones adecuadas. También me gustaría saber si se puede activar el log que parece traer, no me refiero al syslog. Muchísimas gracias.

Adjunto el script.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl -Tw
  2. #
  3. # sms transport for postfix
  4. # uses gnokii for delivery
  5. #
  6. #-------------------------------------------------------------------
  7. # Jeremy Laidman, AnswerZ
  8. # Version 1.0 - March 2003
  9. #
  10. # Installation into postfix:
  11. #
  12. # 1) Copy this script into /usr/local/sbin/sms-transport.
  13. #
  14. # 2) Create the user "sms" with their own group "sms", eg:
  15. #       useradd -r sms -c "SMS Delivery User for Postfix"
  16. #
  17. # 3) Add these lines
  18. #       /etc/postfix/transport:
  19. #               sms     sms:localhost
  20. #
  21. #       /etc/postfix/master.cf
  22. #               sms     unix    -       n       n       -       1       pipe
  23. #                  flags= user=sms:uucp argv=/usr/local/sbin/sms-transport $sender $user
  24. #    Note that the group "uucp" needs to match the group that owns
  25. #    the serial device configured by /etc/gnokii.conf, eg /dev/ttyS0.
  26. #
  27. # 4) Add "sms" to mynetworks parameter in main.cf, eg:
  28. #       mydestination = $myhostname, localhost.$mydomain, sms
  29. #
  30. # 5) Run "postfix reload".
  31. #
  32. # 6) Send mail to [email protected]
  33. #
  34. # 7) Create alias entries mapping usernames to numbers
  35. #       /etc/postfix/aliases
  36. #               freddy: [email protected]
  37. #
  38. # <img src="http://perlenespanol.com/foro/images/smilies/icon_cool.gif" alt="8)" title="Cool" /> Run "newaliases".
  39. #
  40. # 9) Send mail to freddy.
  41. #
  42. #-------------------------------------------------------------------
  43. # Configuration Options Here (will be in config file one day)
  44.  
  45. # if non-empty, will reject messages not from matching addresses
  46. # a leading dot means any subdomain
  47. # if empty, allows from anyone
  48. # if non-mepty there's an implicit '' => 'deny'
  49.  
  50. $senders_file = "/usr/local/etc/sms-transport.senders";
  51. $msgdel_file  = "/usr/local/etc/sms-transport.msgdel";
  52.  
  53. $gnokii_path = "/usr/local/bin/gnokii";
  54.  
  55. #$gnokii_path="/usr/bin/gnokii-test";
  56. $maxsmschars = 158;                    # 160 seems to cause some problems
  57.  
  58. $debug = 0;
  59.  
  60. my %sender_addresses;
  61. my @msgdel;
  62.  
  63. #-------------------------------------------------------------------
  64.  
  65. # We receive the sender address as @ARGV[0], the number as @ARGV[1]
  66. # and the message body on STDIN.  We should only receive one message
  67. # per user.
  68. #
  69. # All headers are stripped and only the body is sent.
  70. #
  71. # Any recipients in the wrong format are bounced with EX_NOUSER=67.
  72. # Errors in calling gnokii are assumed to be temporary and are answered
  73. # with EX_TEMPFAIL=75.  Messages with senders (from-space) that don't
  74. # end in anything in the local domains list are bounced with EX_NOPERM=77.
  75. #
  76. #-------------------------------------------------------------------
  77. #
  78. # Constants for returning status back to postfix.
  79. # (see sysexits.h for the full list)
  80. $EX_NOUSER   = 67;
  81. $EX_TEMPFAIL = 75;
  82. $EX_NOPERM   = 77;
  83. $EX_NOINPUT  = 66;
  84.  
  85. #-------------------------------------------------------------------
  86.  
  87. my $sender;
  88. my $recipient;
  89. my $message;
  90.  
  91. #-------------------------------------------------------------------
  92.  
  93. #require 'syslog.pl';
  94. $myname   = "sms-transport";
  95. $facility = 'mail';
  96. my $mypid = $$;                        # global, even when we fork
  97.  
  98. #openlog($myname,'cons,pid',$facility);
  99.  
  100. sub syslog {
  101.     my ( $level, $msg ) = @_;
  102.     my $pri = $facility . "." . $level;
  103.     $ENV{'HOME'}     = "/tmp";
  104.     $ENV{'PATH'}     = "";
  105.     $ENV{'BASH_ENV'} = "";
  106.  
  107.     # the way we're calling system makes it easy to trust $msg
  108.     # as long as it's not too big, so we just untaint it
  109.     if ( $msg =~ /(.*)/s ) {
  110.         $msg = $1;
  111.     }
  112.     else {
  113.         die "Problem with untainting log message\n";
  114.     }
  115.     $msg =~ s/\n/ /g;
  116.     $msg = substr( $msg, 0, 255 )
  117.         if length($msg) > 255;
  118.     system( "/usr/bin/logger", "-p", $pri, "-t", $myname . "[" . $mypid . "]", "--", $msg );
  119. }
  120.  
  121. sub bailout {
  122.     my ( $rc, $msg ) = @_;
  123.     print STDERR "$msg", "\n";
  124.     syslog( 'error', $msg );
  125.  
  126.     #   closelog();
  127.     exit $rc;
  128. }
  129.  
  130. sub check_perms {
  131.     my $sender = lc(shift);
  132.     my $test;
  133.     return 1 unless scalar(%sender_addresses);
  134.  
  135.     # strip enclosing brackets except for <>
  136.     if ( $sender =~ /<[^>]+>/ ) {
  137.         $sender = $1;
  138.     }
  139.  
  140.     foreach $test ( keys %sender_addresses ) {
  141.         $test = lc($test);
  142.         if ( $test =~ /\@/ ) {
  143.  
  144.             # an email address
  145.             return 1
  146.                 if $test eq $sender;
  147.         }
  148.         else {
  149.  
  150.             # a domain
  151.             $sender =~ s/^.*@//;
  152.             if ( $test =~ /^\./ ) {
  153.  
  154.                 # allow subdomains
  155.                 return 1
  156.                     if $test eq $sender;
  157.                 return 1
  158.                     if $test eq substr( $sender, -length($test) );
  159.             }
  160.             else {
  161.  
  162.                 # exact match only
  163.                 return 1
  164.                     if $test eq $sender;
  165.             }
  166.         }
  167.     }
  168.     return 0;                          # failed
  169. }
  170.  
  171. sub process_email {
  172.     my $in_headers = 1;
  173.     my $msg        = "";
  174.     my $subject    = "";
  175.     my $sender;
  176.     my $linenum = 0;
  177.  
  178.     while (<STDIN>) {
  179.         chomp;
  180.         if ($in_headers) {
  181.  
  182.             # headers
  183.             if ( length($_) == 0 ) {
  184.                 $in_headers = 0;
  185.                 next;
  186.             }
  187.             if ( $_ =~ /^subject:\s*/ ) {
  188.                 $subject = $';
  189.             }
  190.         }
  191.         else {
  192.  
  193.             # body
  194.             $msg .= "\r\n" if $msg =~ /\S/;
  195.             $msg .= $_;
  196.         }
  197.         last if $linenum++ > 500;
  198.     }
  199.  
  200.     # no body?  return subject
  201.     $msg = $subject if ( !length($msg) );
  202.  
  203.     return $msg;
  204. }
  205.  
  206. sub send_message {
  207.     my $recipient_num = shift;
  208.     my $message       = shift;
  209.  
  210.     if ( length($message) > $maxsmschars ) {
  211.         syslog( 'info', "Truncated message from " . length($message) . " to " . $maxsmschars . "-3 chars\n" );
  212.         $message = substr( $message, 0, $maxsmschars - 3 ) . "...";
  213.     }
  214.  
  215.     # gnokii expects message on stdin
  216.     $pid = open( GNOKII, "|-" );
  217.     if ($pid) {                        # parent
  218.         print GNOKII "$message";
  219.  
  220.         #open(FILE,">/tmp/testmsg"); print FILE "$message"; close(FILE);
  221.         close GNOKII or bailout( $EX_TEMPFAIL, "Unable to send message to $gnokii_path" );
  222.     }
  223.     else {                             # child
  224.         ( -x $gnokii_path )
  225.             || bailout( $EX_TEMPFAIL, "Unable to exec $gnokii_path: $!" );
  226.         $ENV{'HOME'} = "/tmp";
  227.         $ENV{'PATH'} = "";
  228.  
  229.         # force into uucp and lock groups
  230.         # FIXME: should be configurable or automagic
  231.         #my $groups="54 233 14";
  232.         #syslog('info',"Changing groups from [".$(."] to [$groups]");
  233.         #$) = $groups;
  234.         exec( $gnokii_path, "--sendsms", "$recipient_num" )
  235.             || bailout( $EX_TEMPFAIL, "Unable to exec $gnokii_path: $!" );
  236.  
  237.         # will never get here
  238.     }
  239. }
  240.  
  241. sub clean_message {
  242.     my $msg = shift;
  243.     $msg =~ s/(\r\n)+$//;              # remove trailing crlf
  244.                                        # delete message bits that we don't want
  245.     my $regex;
  246.     my $changed = 0;
  247.     foreach $regex (@msgdel) {
  248.         syslog( 'debug', "Testing if msg [$msg] matches /$regex/" )
  249.             if $debug;
  250.         if ( $msg =~ s/$regex// ) {
  251.             $changed++;
  252.         }
  253.     }
  254.     if ($changed) {
  255.         syslog( 'debug', "Message deletions - new message: [$msg]\n" )
  256.             if $debug;
  257.     }
  258.     return $msg;
  259. }
  260.  
  261. #-------------------------------------------------------------------
  262.  
  263. $0 .= "";
  264.  
  265. $sender_given    = $ARGV[0];
  266. $recipient_given = $ARGV[1];
  267. if ( defined($sender_given) and $sender_given =~ /([-A-Za-z0-9_+]+\@[-A-Za-z0-9.]+)/ ) {
  268.     $sender = $1;                      # now is untainted
  269. }
  270. else {
  271.     bailout( $EX_NOUSER, "Sender address not given" )
  272.         if !defined($sender_given);
  273.     bailout( $EX_NOUSER, "Invalid sender address: $sender_given" );
  274. }
  275. if ( defined($recipient_given) and $recipient_given =~ /^(\d+)/ ) {
  276.     $recipient = $1;                   # now is untainted
  277. }
  278. else {
  279.     bailout( $EX_NOUSER, "Mobile number not given" )
  280.         if !defined($recipient_given);
  281.     bailout( $EX_NOUSER, "Not a mobile number: <$recipient_given>" );
  282.     exit $EX_NOUSER;
  283. }
  284.  
  285. open( FILE, "<$senders_file" ) or die "Unable to get list of senders from $senders_file: $!\n";
  286. while (<FILE>) {
  287.     chomp;
  288.     next if /^\s*#/;
  289.     next if /^\s*$/;
  290.  
  291.     # lines are colon-delimited, like aliases
  292.     my ( $addr, $perm );
  293.     ( $addr, $perm ) = split(/\s*:\s*/);
  294.     next unless defined($addr) and length($addr);
  295.     next unless defined($perm) and length($perm);
  296.     $perm = lc($perm);
  297.     if ( $perm =~ /(permit|deny)/ ) {
  298.         $sender_addresses{$addr} = $1;
  299.     }
  300. }
  301. close(FILE);
  302.  
  303. if ( open( FILE, "<$msgdel_file" ) ) {
  304.     while (<FILE>) {
  305.         chomp;
  306.         next if /^\s*#/;
  307.         next if /^\s*$/;
  308.  
  309.         # each line is a regex
  310.         push @msgdel, $_;
  311.     }
  312.     close(FILE);
  313.     syslog( 'debug', "Message deletions read in: " . join( ',', @msgdel ) . "\n" )
  314.         if $debug;
  315. }
  316.  
  317. $message = process_email;
  318. $message = clean_message($message);
  319. length($message)
  320.     || bailout( $EX_NOINPUT, "No message body - nothing to send" );
  321.  
  322. syslog( 'info', "Said <$sender> to <$recipient>:\n[$message] " . length($message) . " chars\n" );
  323.  
  324. if ( !check_perms($sender) ) {
  325.     bailout( $EX_NOPERM, "Permission denied for <$sender>" );
  326. }
  327.  
  328. send_message( $recipient, $message );
  329. syslog( 'info', "GNOKII sent message OK\n" );
  330.  
  331. # ALl OK if it got this far.
  332. #closelog();
  333.  
Coloreado en 0.008 segundos, usando GeSHi 1.0.8.4
Última edición por explorer el 2013-06-11 12:28 @561, editado 1 vez en total
Razón: Formateado de código con Perltidy
jcovenas
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2012-03-29 11:21 @514

Publicidad

Re: Transporte SMS

Notapor explorer » 2013-06-11 15:28 @686

Bueno, el asunto no es fácil...

Yo, para conseguir una buena solución, haría lo siguiente:
  • recibir el correo electrónico y esperar que el cuerpo del mensaje esté en formato MIME multipart
  • si no está en ese formato, tratar el cuerpo del mensaje como de solo texto
  • si está en ese formato, localizar la parte de solo texto
  • si está esa parte de solo texto, usarlo como fuente para el SMS
  • si no está la parte de solo texto, o no está en formato , buscar la parte escrita en HTML
  • extraer el texto de la parte HTML y usarlo como fuente para el SMS
Una forma fácil y rápida de resolverlo, pero no segura, es filtrar todo el contenido HTML, con la esperanza de que solo quede el texto.

Algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.     $msg =~ s{<.+?>}{}g;                   # eliminar todo el marcado HTML
  2.     $msg =~ s{\s+}{ }g;                    # reducir todos los conjuntos de espacios en blanco, a uno solo
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Transporte SMS

Notapor jcovenas » 2013-06-11 21:02 @918

Muchas gracias, estimado. Una consulta: cuando se refiere a no seguro, ¿se refiere a que es posible que la indicada expresión regular podría dar errores incontrolados o a que es probable perder datos (como los adjuntos)?

Gracias por la ayuda.
jcovenas
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2012-03-29 11:21 @514

Re: Transporte SMS

Notapor explorer » 2013-06-12 05:22 @265

No, no me refiero a eso, sino a que con esas dos líneas no es suficiente para garantizar que estamos extrayendo el texto que nos interesa mandar por SMS. Por ejemplo, los adjuntos se suelen enviar en formato uuencode, que es una ristra de caracteres de 7 bits, por lo que para la expresión regular serán iguales a un texto.

También, si se trata de un correo electrónico con formato MIME multipart, los propios delimitadores de cada parte están escritos con caracteres normales, y eso tampoco lo sabe la expresión regular.

La expresión regular no cometerá fallos. Solo hace lo que le hemos pedido: quitar el marcado HTML, nada más.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Transporte SMS

Notapor jcovenas » 2013-06-19 16:41 @736

Hola de nuevo.

Apliqué las expresiones regulares pero al parecer no hace ninguna limpieza.

Por otro lado, dado que mi script toma el mensaje de STDIN, ¿cómo puedo ver lo que contiene esa variable a fin de ver qué es lo está reteniendo realmente y empezar a limpiar el indicado mensaje?
jcovenas
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2012-03-29 11:21 @514

Re: Transporte SMS

Notapor explorer » 2013-06-19 20:58 @915

jcovenas escribiste:Apliqué las expresiones regulares pero al parecer no hace ninguna limpieza.
Quizás es que no había nada que limpiar. ¿Puedes poner un ejemplo de lo que se recibe por la entrada estándar? Puede ser algo inventado, o falsear los datos sensibles.

jcovenas escribiste:Por otro lado, dado que mi script toma el mensaje de STDIN, ¿cómo puedo ver lo que contiene esa variable a fin de ver qué es lo está reteniendo realmente y empezar a limpiar el indicado mensaje?
Puedes poner un print() en el lugar adecuado para que lo mande a la salida estándar o hacia un archivo de registro de actividad.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 58 invitados