html/sms.pl


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