1use strict; 2use Irssi 20020300; 3use 5.6.0; 4use Socket; 5use POSIX; 6 7use vars qw($VERSION %IRSSI %HELP); 8$HELP{sms} = " 9SMS <handle or phone number> <text> 10 11Sends sms to handle from addressbook (see HELP addsms and listsms) 12or phone number. 13"; 14$HELP{addsms} = " 15ADDSMS <handle> <phone number> 16 17Adds 'handle' with phone 'phone number' to addressbook, 18or change phone number of existing handle. 19"; 20$HELP{delsms} = " 21DELSMS <handle or number from listsms> 22 23Deletes entry from addressbook. 24"; 25$HELP{listsms} = " 26LISTSMS [handle match] 27 28Lists addressbook. 29"; 30$VERSION = "1.5b"; 31%IRSSI = ( 32 authors => "Maciek \'fahren\' Freudenheim", 33 contact => "fahren\@bochnia.pl", 34 name => "SMS", 35 description => "/ADDSMS, /DELSMS, /LISTSMS and /SMS - phone address-book with smssender, for now supports only Polish operators", 36 license => "GNU GPLv2 or later", 37 changed => "Fri Jan 10 03:54:07 CET 2003" 38); 39 40Irssi::theme_register([ 41 'sms_sending', '>> Sending SMS to %_$0%_ /$1/', 42 'sms_sent', '>> Message to %_$0%_ has been sent.', 43 'sms_esent', '>> Message $1/$2 to %_$0%_ has been sent.', 44 'sms_notsent', '>> Message to %_$0%_ has %_NOT%_ been sent.', 45 'sms_enotsent', '>> Message $1/$2 to %_$0%_ has %_NOT%_ been sent.', 46 'sms_stat', '>> Total of %_$0%_ entries: %_$1%_ from PLUS, %_$2%_ from ERA, %_$3%_ from IDEA.', 47 'sms_listline', '[%W$[!-2]0%n]%| $[9]1%_:%_ $2 /$[-4]3/', 48]); 49 50# Chanelog: 51## version 1.5b 52# $ENV{HOME}/.irssi -> Irssi::get_irssi_dir 53## version 1.5 54# - added new prefixes 55## version 1.4 56# - sorting /smslist 57# - do not lowercasing handles 58## version 1.3 59# - fixed smsfork(), ifork() 60# - added help 61## version 1.2d 62# - ... more ERA() fixes 63## version 1.2c 64# - added parsing of 'request cannot be processed at this time' in IDEA() 65# - added [act/total] in ERA() split messages 66## version 1.2b 67# - more fixes in ERA() messages spliting 68## version 1.2 69# - fixed long message spliting in ERA() 70## version 1.1 71# - fixed IDEA() 72# - inf. ifork() loop fixed (found by Lam) 73# - fixed regex matching in /delsms, /listsms and /sms 74# - changed kill() to POSIX::_exit() 75## version 1.0 76# - forking before sending SMS 77 78my $smssender = getlogin || getpwuid($<) || "anonymous"; 79my $smsfile = Irssi::get_irssi_dir . "/smslist"; 80my (@smslist, $fh, %ftag); 81 82sub cmd_sms { 83 my ($target, $text) = split(/ +/, $_[0], 2); 84 my $window = Irssi::active_win(); 85 my $phone; 86 87 if ($text eq "") { 88 Irssi::print("Usage: /SMS <handle or phone number> <text>"); 89 return; 90 } 91 92 if (isnumber($target)) { 93 if ($phone = corrnum($target)) { 94 my $net = smsnet($phone); 95 $window->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_sending', smsnum($phone), $net); 96 &$net($phone, $text) unless &smsfork; 97 } else { 98 Irssi::print("%R>>%n Wrong number."); 99 } 100 } else { 101 my $i = 0; 102 my $handle = lc($target); 103 my $all = $handle eq "*"? 1 : 0; 104 for my $sms (@smslist) { 105 next unless ($all || lc($sms->{handle}) eq $handle); 106 $i++; 107 my $net = smsnet($sms->{phone}); 108 $window->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_sending', $sms->{handle}, $net); 109 &$net($sms->{phone}, $text) unless &smsfork; 110 } 111 Irssi::print("%R>>%n Can't find %_$target%_ in address book.") unless $i; 112 } 113} 114 115sub cmd_addsms { 116 my ($handle, $num) = split(/ +/, $_[0], 2); 117 my $phone; 118 119 unless ($phone = corrnum($num)) { 120 Irssi::print("Usage: /ADDSMS <handle> <phone number>"); 121 return; 122 } 123 124 for my $sms (@smslist) { 125 if (lc($sms->{handle}) eq lc($handle)) { 126 Irssi::print(">> Changing phone number for %_$handle%_ /to $phone/"); 127 $sms->{phone} = $phone; 128 &savesms; 129 return; 130 } 131 } 132 my $sms = {}; 133 $sms->{handle} = $handle; 134 $sms->{phone} = $phone; 135 Irssi::print(">> Adding %_$handle%_ with num %_$phone%_."); 136 push @smslist, $sms; 137 &savesms; 138} 139 140sub cmd_delsms { 141 my $handle = shift; 142 143 if ($handle eq "") { 144 Irssi::print("Usage: /DELSMS <handle or number from listsms>"); 145 return; 146 } 147 148 my @num; 149 $handle = lc($handle); 150 151 if ($handle =~ /^[0-9]+$/) { 152 push @num, $handle - 1; 153 } else { 154 my $all = $handle eq "*"? 1 : 0; 155 @smslist = sort { lc($a->{handle}) cmp lc($b->{handle}) } @smslist; 156 for (my $i = 0; $i < @smslist; $i++) { 157 push @num, $i if ($all || lc($smslist[$i]->{handle}) eq $handle); 158 } 159 } 160 for my $n (reverse(@num)) { 161 if (my($sms) = splice(@smslist, $n, 1)) { 162 Irssi::print(">> Deleted %_$sms->{handle}%_."); 163 } 164 } 165 166 &savesms; 167} 168 169sub cmd_listsms { 170 my $match = shift || "*"; 171 my $window = Irssi::active_win(); 172 173 if (@smslist == 0) { 174 Irssi::print("%R>>%n Your SMSLIST is empty."); 175 return; 176 } 177 my $all = $match eq "*"? 1 : 0; 178 @smslist = sort { lc($a->{handle}) cmp lc($b->{handle}) } @smslist; 179 my $i = 1; 180 for my $sms (@smslist) { 181 next unless $all || $sms->{handle} =~ /\Q$match\E/i; 182 $window->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_listline', $i++, $sms->{handle}, $sms->{phone}, smsnet($sms->{phone})); 183 } 184 &smsstat if $match eq "*"; 185} 186 187sub smsstat { 188 my ($plus, $era, $idea) = (0, 0, 0); 189 190 for my $sms (@smslist) { 191 for ($sms->{phone}) { 192 /^6(0[1,3,5,7,9]|91)/ and $plus++; 193 /^6(0[0,2,4,6,8]|92)/ and $era++; 194 /^50/ and $idea++; 195 } 196 } 197 Irssi::active_win()->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_stat', scalar(@smslist), $plus, $era, $idea); 198} 199 200sub savesms { 201 local *fp; 202 open (fp, ">", $smsfile) or die "Couldn't open $smsfile for writing"; 203 for my $sms (@smslist) { 204 print(fp "$sms->{handle} $sms->{phone}\n"); 205 } 206 close(fp); 207} 208 209sub loadsms { 210 @smslist = (); 211 return unless (-e $smsfile); 212 local *fp; 213 open(fp, "<", $smsfile); 214 local $/ = "\n"; 215 while (<fp>) { 216 chop; 217 my $sms = {}; 218 ($sms->{handle}, $sms->{phone}) = split(/ /); 219 push(@smslist, $sms); 220 } 221 close(fp); 222 Irssi::print("Loaded address book:"); 223 &smsstat; 224} 225 226sub isnumber { 227 return ($_[0] =~ /^([+]|[0-9])[0-9]{6,}$/); 228} 229 230sub corrnum { 231 my $num = shift; 232 233 return 0 unless isnumber($num); 234 235 if ($num =~ /^\+/) { 236 return 0 unless $num =~ s/^(\+48)//g; 237 } 238 $num =~ s/^(48)//; 239 240 return $num; 241} 242 243sub smsnum { 244 my $num = shift; 245 for my $sms (@smslist) { 246 if ($sms->{phone} eq $num) { 247 return $sms->{handle} 248 } 249 } 250 return $num; 251} 252 253sub smsnet { 254 for (@_) { 255 /^6(0[13579]|9[135])/ and return "PLUS"; 256 /^6(0[02468]|9[24])/ and return "ERA"; 257 /^50/ and return "IDEA"; 258 } 259 return "UNKNOWN"; 260} 261 262sub urlencode { 263 my $ret = shift; 264 $ret =~ s/([^a-zA-Z0-9])/sprintf("%%%.2x", ord($1));/eg; 265 return $ret; 266} 267 268sub smsfork { 269 my ($rh, $wh); 270 pipe($rh, $wh); 271 my $pid = fork(); 272 unless (defined $pid) { 273 Irssi::print("%R>>%n Failed to fork() :/ - $!"); 274 close $rh; close $wh; 275 return 1; 276 } elsif ($pid) { # parent 277 close $wh; 278 $ftag{$rh} = Irssi::input_add(fileno($rh), INPUT_READ, \&ifork, $rh); 279 Irssi::pidwait_add($pid); 280 } else { # child 281 close $rh; 282 $fh = $wh; 283 } 284 return $pid; 285} 286 287sub smskill { 288 print($fh "finished\n"); 289 close $fh; 290 POSIX::_exit(1); 291} 292 293sub ifork { 294 my $rh= shift; 295 my $ret = 0; 296 while (<$rh>) { 297 /^sent (.+)/ and Irssi::active_win()->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_sent', smsnum($1)), last; 298 /^esent ([0-9]+)\s([0-9]+)\s([0-9]+)$/ and Irssi::active_win()->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_esent', smsnum($1), $2, $3), last; 299 /^notsent (.+)/ and Irssi::active_win()->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_notsent', smsnum($1)), last; 300 /^enotsent ([0-9]+)\s([0-9]+)\s([0-9]+)$/ and Irssi::active_win()->printformat(MSGLEVEL_CLIENTNOTICE, 'sms_enotsent', smsnum($1), $2, $3), last; 301 /^info (.+)/ and Irssi::print("$1"), last; 302 /^finished$/ and $ret = 1, last; 303 } 304 return unless $ret; 305 Irssi::input_remove($ftag{$rh}); 306 delete $ftag{$rh}; 307 close $rh; 308} 309 310sub sconnect { 311 my $target = shift; 312 my ($proto, $iaddr, $saddr); 313 314 $proto = getprotobyname('tcp'); 315 $iaddr = inet_aton($target); 316 socket(SOCK, PF_INET, SOCK_STREAM, $proto) || return 0; 317 local $SIG{ALRM} = sub { 318 print($fh "info %R>>%n connect() to $target timeouted :/\n"); 319 close SOCK; 320 return 0; 321 }; 322 alarm 10; 323 unless (connect(SOCK, sockaddr_in(80, $iaddr))) { 324 print($fh "info %R>>%n Couldn't connect to $target: $!\n"); 325 close SOCK; 326 return 0; 327 } 328 alarm 0; 329 my $old = select(SOCK); $| = 1; select($old); 330 return 1; 331} 332 333sub PLUS { 334 my ($phone, $text) = @_; 335 &smskill unless sconnect("sms.plusgsm.pl"); 336 my $tosend = "tprefix=" . substr($phone, 0, 3) . "&numer=" . substr($phone, 3) . "&odkogo=$smssender&tekst=" . urlencode($text) . "&dzien=dzisiaj&godz=&min="; 337 print SOCK "POST /sms/sendsms.php HTTP/1.0\n"; 338 print SOCK "Host: www.text.plusgsm.pl:80\n"; 339 print SOCK "Accept: */*\n"; 340 print SOCK "Content-type: application/x-www-form-urlencoded\n"; 341 print SOCK "Content-length: " . length($tosend) . "\n\n"; 342 print SOCK "$tosend\r\n"; 343 while (<SOCK>) { 344 /wiadomo�� zosta�a wys�ana/ and print($fh "sent $phone\n"), last; 345 /nie zosta� wys�any/ and print($fh "notsent $phone\n"), last; 346 } 347 close SOCK; 348 &smskill; 349} 350 351sub ERA { 352 my ($phone, $cutme) = @_; 353 my $ml = 126 - length($smssender); 354 my $cl = length($cutme); 355 my $total = int($cl / $ml) + (($cl%$ml)? 1 : 0); 356 if ($total > 1) { 357 $ml -= (4 + length($total) * 2); 358 $total = int($cl / $ml) + (($cl%$ml)? 1 : 0); 359 printf($fh "info >> Spliting SMS to $total messages.\n"); 360 } 361 my $act = 0; 362 while ($cutme =~ s/.{1,$ml}//) { 363 my ($cookie, $code, $tosend, $text); 364 &smskill unless sconnect("boa.eragsm.pl"); 365 $act++; 366 $text = "<$act/$total> " if $total > 1; 367 $text .= $&; 368 print SOCK "POST /sms/sendsms.asp?sms=1 HTTP/1.0\n"; 369 print SOCK "Host: boa.eragsm.com.pl:80\n"; 370 print SOCK "Accept: */*\n\r\n"; 371 while (<SOCK>) { 372 $cookie = $1 if /Set\-Cookie\:\ ([^\;]+?)\;/; 373 $code = $1 if /name\=\"Code\"\ value\=\"(.+?)\"/; 374 } 375 close SOCK; 376 $tosend = "numer=$phone&bookopen=&message=" . urlencode($text) . "&podpis=$smssender&kontakt=&Nadaj=Nadaj&code=$code&Kasuj=Kasuj&Telefony=Telefony"; 377 &smskill unless sconnect("boa.eragsm.pl"); 378 print SOCK "POST /sms/sendsms.asp HTTP/1.0\n"; 379 print SOCK "Host: boa.eragsm.com.pl:80\n"; 380 print SOCK "Accept: */*\n"; 381 print SOCK "Cookie: $cookie\n"; 382 print SOCK "Referer: http://boa.eragsm.com.pl/sms/sendsms.asp\n"; 383 print SOCK "Content-type: application/x-www-form-urlencoded\n"; 384 print SOCK "Content-length: " . length($tosend) . "\n\n"; 385 print SOCK "$tosend\r\n"; 386 if ($total > 1) { 387 while (<SOCK>) { 388 /nie zosta�a wys�ana!/ and print($fh "enotsent $phone $act $total\n"), last; 389 /zosta�a wys�ana/ and print($fh "esent $phone $act $total\n"), last; 390 } 391 } else { 392 while (<SOCK>) { 393 /nie zosta�a wys�ana!/ and print($fh "notsent $phone\n"), last; 394 /zosta�a wys�ana/ and print($fh "sent $phone\n"), last; 395 } 396 } 397 close SOCK; 398 } 399 &smskill; 400} 401 402sub IDEA { 403 my ($phone, $text) = @_; 404 my ($sec, $min, $hour, $day, $mon, $year) = (localtime)[0,1,2,3,4,5]; 405 $year += 1900; 406 $mon += 1; 407 &smskill unless sconnect("sms.idea.pl"); 408 my $tosend = "LANGUAGE=pl&NETWORK=smsc1&DELIVERY_TIME=0&SENDER=$smssender&RECIPIENT=$phone&VALIDITY_PERIOD=24&DELIVERY_DATE=$day&DELIVERY_MONTH=$mon&DELIVERY_YEAR=$year&DELIVERY_HOUR=$hour&DELIVERY_MIN=$min&NOTIFICATION_FLAG=false&NOTIFICATION_ADDRESS=&SHORT_MESSAGE=" . urlencode($text) . "&SUBMIT=Wyslij"; 409 print SOCK "POST /sendsms.asp HTTP/1.0\n"; 410 print SOCK "Host: sms.idea.pl:80\n"; 411 print SOCK "Accept: */*\n"; 412 print SOCK "Content-type: application/x-www-form-urlencoded\n"; 413 print SOCK "Content-length: " . length($tosend) . "\n\n"; 414 print SOCK "$tosend\r\n"; 415 while (<SOCK>) { 416 /SMS nie zostanie/ and print($fh "notsent $phone\n"), last; 417 /doby zosta� wyczerpany/ and print($fh "notsent $phone\n"), last; 418 /zosta�a wys�ana/ and print($fh "sent $phone\n"), last; 419 /request cannot be processed/ and print($fh "notsent $phone\n"), last; 420 } 421 close SOCK; 422 &smskill; 423} 424 425sub UNKNOWN { 426 print($fh "info %R>>%n Sorry, sms.pl supports only polish operators :/\n"); 427 &smskill; 428} 429 430&loadsms; 431 432Irssi::command_bind("sms", "cmd_sms"); 433Irssi::command_bind("addsms", "cmd_addsms"); 434Irssi::command_bind("smsadd", "cmd_addsms"); 435Irssi::command_bind("smsdel", "cmd_delsms"); 436Irssi::command_bind("delsms", "cmd_delsms"); 437Irssi::command_bind("listsms", "cmd_listsms"); 438Irssi::command_bind("smslist", "cmd_listsms"); 439Irssi::command_bind("smsstat", "smsstat"); 440