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");