978a1f913d4aa187251a98332fe3b9b7514a3036
[Biblio-RFID.git] / scripts / RFID-JSONP-server.pl
1 #!/usr/bin/perl
2
3 =head1 NAME
4
5 RFID-JSONP-server - simpliest possible JSONP server which provides local web interface to RFID readers
6
7 =head1 USAGE
8
9   ./scripts/RFID-JSONP-server.pl [--debug] [--listen=127.0.0.1:9000] [--reader=filter]
10
11 =cut
12
13 use strict;
14 use warnings;
15
16 use Data::Dump qw/dump/;
17
18 use JSON::XS;
19 use IO::Socket::INET;
20 use LWP::UserAgent;
21 use URI;
22
23 my $debug = 1;
24 my $listen = '127.0.0.1:9000';
25 $listen = ':9000';
26 my $reader;
27 my $koha_url = 'http://ffzg.koha-dev.rot13.org:8080';
28
29 use Getopt::Long;
30
31 GetOptions(
32         'debug!'    => \$debug,
33         'listen=s', => \$listen,
34         'reader=s', => \$reader,
35         'koha=s',       => \$koha_url,
36 ) || die $!;
37
38 our $rfid_sid_cache;
39
40 sub rfid_borrower {
41         my $hash = shift;
42         if ( my $json = $rfid_sid_cache->{ $hash->{sid} } ) {
43                 return $json;
44         }
45         my $ua = LWP::UserAgent->new;
46         my $url = URI->new( $koha_url . '/cgi-bin/koha/ffzg/rfid-borrower.pl');
47         $url->query_form(
48                   RFID_SID => $hash->{sid}
49                 , OIB => $hash->{OIB}
50                 , JMBAG => $hash->{JMBAG}
51         );
52         warn "GET ",$url->as_string;
53         my $response = $ua->get($url);
54         if ( $response->is_success ) {
55                 my $json = decode_json $response->decoded_content;
56                 $rfid_sid_cache->{ $hash->{sid} } = $json;
57                 return $json;
58         } else {
59                 warn "ERROR ", $response->status_line;
60         }
61 }
62
63 use lib 'lib';
64 use Biblio::RFID::RFID501;
65 use Biblio::RFID::Reader;
66 my $rfid = Biblio::RFID::Reader->new( shift @ARGV );
67
68 my $index_html;
69 {
70         local $/ = undef;
71         $index_html = <DATA>;
72         $index_html =~ s{http://koha.example.com:8080}{$koha_url}sg;
73 }
74
75 my $server_url;
76
77 sub http_server {
78
79         my $server = IO::Socket::INET->new(
80                 Proto     => 'tcp',
81                 LocalAddr => $listen,
82                 Listen    => SOMAXCONN,
83                 Reuse     => 1
84         );
85                                                                   
86         die "can't setup server: $!" unless $server;
87
88         $server_url = 'http://' . $listen;
89         print "Server $0 ready at $server_url\n";
90
91         while (my $client = $server->accept()) {
92                 $client->autoflush(1);
93                 my $request = <$client>;
94
95                 warn "WEB << $request\n" if $debug;
96                 my $path;
97
98                 if ($request =~ m{^GET (/.*) HTTP/1.[01]}) {
99                         my $method = $1;
100                         my $param;
101                         if ( $method =~ s{\?(.+)}{} ) {
102                                 foreach my $p ( split(/[&;]/, $1) ) {
103                                         my ($n,$v) = split(/=/, $p, 2);
104                                         $param->{$n} = $v;
105                                 }
106                                 warn "WEB << param: ",dump( $param ) if $debug;
107                         }
108                         $path = $method;
109
110                         if ( $path eq '/' ) {
111                                 print $client "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n$index_html";
112                         } elsif ( $path =~ m{^/(examples/.+)} ) {
113                                 $path = $1; # FIXME prefix with dir for installation
114                                 my $size = -s $path;
115                                 warn "static $path $size bytes\n";
116                                 my $content_type = 'text/plain';
117                                 $content_type = 'application/javascript' if $path =~ /\.js/;
118                                 print $client "HTTP/1.0 200 OK\r\nContent-Type: $content_type\r\nContent-Length: $size\r\n\r\n";
119                                 {
120                                         local $/ = undef;
121                                         open(my $fh, '<', $path) || die "can't open $path: $!";
122                                         while(<$fh>) {
123                                                 print $client $_;
124                                         }
125                                         close($fh);
126                                 }
127                         } elsif ( $method =~ m{/scan} ) {
128                                 my @tags = $rfid->tags;
129                                 my $json = { time => time() };
130                                 foreach my $tag ( @tags ) {
131                                         my $hash = $rfid->to_hash( $tag );
132                                         $hash->{sid}  = $tag;
133                                         if ( $hash->{tag_type} eq 'SmartX' ) {
134                                                 my $borrower = rfid_borrower $hash;
135                                                 if ( exists $borrower->{error} ) {
136                                                         warn "ERROR ", dump($borrower);
137                                                 } else {
138                                                         $hash->{borrower} = $borrower->{borrower};
139                                                 }
140                                         } else {
141                                                 $hash->{security} = uc unpack 'H*', $rfid->afi( $tag );
142                                         }
143                                         push @{ $json->{tags} }, $hash;
144                                 };
145                                 warn "#### ", encode_json($json);
146                                 print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n",
147                                         $param->{callback}, "(", encode_json($json), ")\r\n";
148                         } elsif ( $method =~ m{/program} ) {
149
150                                 my $status = 501; # Not implementd
151
152                                 foreach my $p ( keys %$param ) {
153                                         next unless $p =~ m/^(E[0-9A-F]{15})$/;
154                                         my $tag = $1;
155                                         my $content = Biblio::RFID::RFID501->from_hash({ content => $param->{$p} });
156                                         $content    = Biblio::RFID::RFID501->blank if $param->{$p} eq 'blank';
157                                         $status = 302;
158
159                                         warn "PROGRAM $tag $content\n";
160                                         $rfid->write_blocks( $tag => $content );
161                                         $rfid->write_afi(    $tag => chr( $param->{$p} =~ /^130/ ? 0xDA : 0xD7 ) );
162                                 }
163
164                                 print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
165
166                         } elsif ( $method =~ m{/secure(.js)} ) {
167
168                                 my $json = $1;
169
170                                 my $status = 501; # Not implementd
171
172                                 foreach my $p ( keys %$param ) {
173                                         next unless $p =~ m/^(E[0-9A-F]{15})$/;
174                                         my $tag = $1;
175                                         my $data = $param->{$p};
176                                         $status = 302;
177
178                                         warn "SECURE $tag $data\n";
179                                         $rfid->write_afi( $tag => hex($data) );
180                                 }
181
182                                 if ( $json ) {
183                                         print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n",
184                                                 $param->{callback}, "({ ok: 1 })\r\n";
185                                 } else {
186                                         print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
187                                 }
188
189                         } else {
190                                 print $client "HTTP/1.0 404 Unkown method\r\n\r\n";
191                         }
192                 } else {
193                         print $client "HTTP/1.0 500 No method\r\n\r\n";
194                 }
195                 close $client;
196         }
197
198         die "server died";
199 }
200
201 http_server;
202
203 __DATA__
204 <html>
205 <head>
206 <title>RFID JSONP</title>
207 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
208 <style type="text/css">
209 .status {
210         background: #ff8;
211 }
212
213 .da {
214         background: #fcc;
215 }
216
217 .d7 {
218         background: #cfc;
219 }
220
221 label[for=pull-reader] {
222         position: absolute;
223         top: 1em;
224         right: 1em;
225         background: #eee;
226 }
227
228 </style>
229 <script type="text/javascript">
230
231 // mock console
232 if(!window.console) {
233         window.console = new function() {
234                 this.info = function(str) {};
235                 this.debug = function(str) {};
236         };
237 }
238
239
240 function got_visible_tags(data,textStatus) {
241         var html = 'No tags in range';
242         if ( data.tags ) {
243                 html = '<ul class="tags">';
244                 $.each(data.tags, function(i,tag) {
245                         console.debug( i, tag );
246                         html += '<li><tt class="' + tag.security + '">' + tag.sid;
247                         var borrowernumber = tag.content || tag.borrower.cardnumber;
248
249                         if ( borrowernumber ) {
250                                 html += ' <a href="http://koha.example.com:8080/cgi-bin/koha/members/member.pl?member=' + borrowernumber + '" title="lookup in Koha" target="koha-lookup">' + borrowernumber + '</a>';
251                                 html += '</tt>';
252 /*
253                                 html += '<form method=get action=program style="display:inline">'
254                                         + '<input type=hidden name='+tag.sid+' value="blank">'
255                                         + '<input type=submit value="Blank" onclick="return confirm(\'Blank tag '+tag.sid+'\')">'
256                                         + '</form>'
257                                 ;
258 */
259                         } else {
260                                 html += '</tt>';
261                                 html += ' <form method=get action=program style="display:inline">'
262                                         + '<!-- <input type=checkbox name=secure value='+tag.sid+' title="secure tag"> -->'
263                                         + '<input type=text name='+tag.sid+' size=12>'
264                                         + '<input type=submit value="Program">'
265                                         + '</form>'
266                                 ;
267                         }
268                 });
269                 html += '</ul>';
270         }
271
272         var arrows = Array( 8592, 8598, 8593, 8599, 8594, 8600, 8595, 8601 );
273
274         html = '<div class=status>'
275                 + textStatus
276                 + ' &#' + arrows[ data.time % arrows.length ] + ';'
277                 + '</div>'
278                 + html
279                 ;
280         $('#tags').html( html );
281         window.setTimeout(function(){
282                 scan_tags();
283         },200); // re-scan every 200ms
284 };
285
286 function scan_tags() {
287         console.info('scan_tags');
288         if ( $('input#pull-reader').attr('checked') )
289                 $.getJSON("/scan?callback=?", got_visible_tags);
290 }
291
292 $(document).ready(function() {
293                 $('input#pull-reader').click( function() {
294                         scan_tags();
295                 });
296                 $('input#pull-reader').attr('checked', true); // force check on load
297
298                 $('div#tags').click( function() {
299                         $('input#pull-reader').attr('checked', false);
300                 } );
301
302                 scan_tags();
303 });
304 </script>
305 </head>
306 <body>
307
308 <h1>RFID tags in range</h1>
309
310 <label for=pull-reader>
311 <input id=pull-reader type=checkbox checked=1>
312 active
313 </label>
314
315 <div id="tags">
316 RFID reader not found or driver program not started.
317 </div>
318
319 </body>
320 </html>