1
/* $Id: plugin_kvv.c 944 2009-01-06 06:46:50Z michael $
2
* $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/plugin_kvv.c $
4
* plugin kvv (karlsruher verkehrsverbund)
6
* Copyright (C) 2006 Till Harbaum <till@harbaum.org>
7
* Copyright (C) 2006 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
9
* This file is part of LCD4Linux.
11
* LCD4Linux is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; either version 2, or (at your option)
16
* LCD4Linux is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30
* int plugin_init_kvv (void)
31
* adds various functions
32
* void plugin_exit_kvv (void)
36
/* define the include files you need */
47
/* network specific includes */
48
#include <sys/types.h>
49
#include <sys/socket.h>
50
#include <netinet/in.h>
52
#include <arpa/inet.h>
54
/* these should always be included */
60
/* these can't be configured as it doesn't make sense to change them */
61
#define HTTP_SERVER "www.init-ka.de"
62
#define HTTP_REQUEST "/webfgi/StopInfoInplace.aspx?ID=%s"
63
#define USER_AGENT "lcd4linux - KVV plugin (http://ssl.bulix.org/projects/lcd4linux/wiki/plugin_kvv)"
65
#define DEFAULT_STATION_ID "89" /* Hauptbahnhof */
69
* 12_701 = Berufsakademie
72
/* total max values to calculate shm size */
74
#define MAX_LINE_LENGTH 8
75
#define MAX_STATION_LENGTH 40
78
char line[MAX_LINE_LENGTH + 1];
79
char station[MAX_STATION_LENGTH + 1];
85
kvv_entry_t entry[MAX_LINES];
88
static char *station_id = NULL;
89
static char *proxy_name = NULL;
91
static pid_t pid = -1;
92
static int refresh = 60;
93
static int abbreviate = 0;
95
static int initialized = 0;
97
static int shmid = -1;
98
static kvv_shm_t *shm = NULL;
100
#define SECTION "Plugin:KVV"
102
#define TIMEOUT_SHORT 1 /* wait this long for additional data */
103
#define TIMEOUT_LONG 10 /* wait this long for initial data */
105
/* search an element in the result string */
106
static int get_element(char *input, char *name, char **data)
110
int state = 0; /* nothing found yet */
112
/* search entire string */
125
/* ignore white spaces */
127
if (strncasecmp(input, name, strlen(name)) == 0) {
129
skip = strlen(name) - 1;
138
while (*input && (*input++ != '>'))
155
/* serach an attribute within an element */
156
static int get_attrib(char *input, char *name, char **data)
160
int state = 0; /* nothing found */
162
/* search in this element */
163
while (*input != '>') {
164
/* ignore white spaces */
165
if (((*input != ' ') && (*input != '\t')) && (skip == 0)) {
168
if (strncasecmp(input, name, strlen(name)) == 0) {
170
skip = strlen(name) - 1;
182
if (*input == '\"') {
184
while (*input++ != '\"')
201
static int http_open(char *name)
203
struct sockaddr_in server;
204
struct hostent *host_info;
209
sock = socket(PF_INET, SOCK_STREAM, 0);
211
perror("failed to create socket");
215
/* Erzeuge die Socketadresse des Servers
216
* Sie besteht aus Typ, IP-Adresse und Portnummer */
217
memset(&server, 0, sizeof(server));
218
if ((addr = inet_addr(name)) != INADDR_NONE) {
219
memcpy((char *) &server.sin_addr, &addr, sizeof(addr));
221
/* Wandle den Servernamen in eine IP-Adresse um */
222
host_info = gethostbyname(name);
223
if (NULL == host_info) {
224
error("[KVV] Unknown server: %s", name);
227
memcpy((char *) &server.sin_addr, host_info->h_addr, host_info->h_length);
230
server.sin_family = AF_INET;
231
server.sin_port = htons(port);
233
/* Baue die Verbindung zum Server auf */
234
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
235
perror("can't connect to server");
242
static void get_text(char *input, char *end, char *dest, int dlen)
244
int state = 0; /* nothing yet, outside any element */
253
if (cnt < (dlen - 1))
254
dest[cnt++] = *input;
261
else if (*input == '>')
266
if (strncasecmp(input, end, strlen(end)) == 0) {
278
static void process_station_string(char *str)
283
/* some strings to replace */
285
"Hauptbahnhof", "Hbf.",
288
"Schienenersatzverkehr", "Ersatzv.",
289
"Marktplatz", "Marktpl.",
297
*q++ = (last << 6) | (*p & 0x3f);
299
} else if ((*p & 0xe0) == 0xc0) {
308
/* erase multiple spaces and replace umlauts */
310
last = 1; /* no leading spaces */
312
if ((!last) || (*p != ' ')) {
314
/* translate from latin1 to hd44780 */
315
if (*p == (char) 228) /* lower a umlaut */
317
else if (*p == (char) 223) /* sz ligature */
319
else if (*p == (char) 246) /* lower o umlaut */
321
else if (*p == (char) 252) /* lower u umlaut */
332
/* replace certain (long) words with e.g. abbreviations if enabled */
335
for (i = 0; i < (int) (sizeof(repl) / (2 * sizeof(char *))); i++) {
336
if ((p = strstr(str, repl[2 * i])) != NULL) {
338
/* move new string */
339
memcpy(p, repl[2 * i + 1], strlen(repl[2 * i + 1]));
340
/* move rest of string down */
341
memmove(p + strlen(repl[2 * i + 1]),
342
p + strlen(repl[2 * i]), strlen(str) - (p - str) - strlen(repl[2 * i]) + 1);
348
static void kvv_client( __attribute__ ((unused))
355
char server_name[] = HTTP_SERVER;
358
/* connect to proxy if given, to server otherwise */
359
if ((proxy_name != NULL) && (strlen(proxy_name) != 0))
360
connect_to = proxy_name;
362
connect_to = server_name;
364
info("[KVV] Connecting to %s", connect_to);
368
sock = http_open(connect_to);
370
error("[KVV] Error accessing server/proxy: %s", strerror(errno));
373
/* create and set get request */
374
if (snprintf(obuffer, sizeof(obuffer),
375
"GET http://%s" HTTP_REQUEST " HTTP/1.1\n"
376
"Host: %s\n" "User-Agent: " USER_AGENT "\n\n", server_name, station_id,
377
server_name) >= (int) sizeof(obuffer)) {
379
info("[KVV] Warning, request has been truncated!");
382
info("[KVV] Sending first (GET) request ...");
383
send(sock, obuffer, strlen(obuffer), 0);
393
tv.tv_sec = count ? TIMEOUT_SHORT : TIMEOUT_LONG;
396
i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
403
i = recv(sock, ibuffer + count, sizeof(ibuffer) - count - 1, 0);
409
ibuffer[count] = 0; /* terminate string */
413
info("[KVV] empty/no reply");
416
char *input, *cookie, *name = NULL, *value = NULL;
417
int input_len, cookie_len, name_len, value_len;
419
/* buffer to html encode value */
425
cookie = strstr(ibuffer, "Set-Cookie:");
427
cookie += strlen("Set-Cookie:");
429
while (*cookie == ' ')
432
while (cookie[cookie_len] != ';')
435
/* find input element */
436
input_len = get_element(ibuffer, "input", &input);
440
char *input_end = input;
441
while (*input_end != '>')
443
while (*input_end != '\"')
445
*(input_end + 1) = 0;
447
name_len = get_attrib(input, "name", &name);
448
value_len = get_attrib(input, "value", &value);
450
for (value_enc_len = 0, i = 0; i < value_len; i++) {
451
if (isalnum(value[i]))
452
value_enc[value_enc_len++] = value[i];
454
sprintf(value_enc + value_enc_len, "%%%02X", 0xff & value[i]);
460
cookie[cookie_len] = 0;
464
value[value_len] = 0;
465
if (value_enc_len >= 0)
466
value_enc[value_enc_len] = 0;
468
sock = http_open(connect_to);
471
if (snprintf(obuffer, sizeof(obuffer),
472
"POST http://%s" HTTP_REQUEST " HTTP/1.1\n"
474
"User-Agent: " USER_AGENT "\n"
476
"Content-Type: application/x-www-form-urlencoded\n"
477
"Content-Length: %d\n"
479
server_name, station_id, server_name, cookie, name_len + value_enc_len + 1, name,
480
value_enc) >= (int) sizeof(obuffer)) {
482
info("[KVV] Warning, request has been truncated!");
485
info("[KVV] Sending second (POST) request ...");
486
send(sock, obuffer, strlen(obuffer), 0);
496
tv.tv_sec = count ? TIMEOUT_SHORT : TIMEOUT_LONG;
499
i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
501
i = recv(sock, ibuffer + count, sizeof(ibuffer) - count - 1, 0);
505
while (i > 0); /* leave on select or read error */
509
/* printf("Result (%d):\n%s\n", count, ibuffer); */
511
/* close connection */
515
info("[KVV] empty/no reply");
518
int last_was_stop = 0;
521
int td_len, i, overflow = 0;
523
/* lock shared memory */
526
/* free allocated memory */
529
if (strstr(ibuffer, "Die Daten konnten nicht abgefragt werden.") != NULL) {
530
info("[KVV] Server returned error!");
531
/* printf("%s\n", ibuffer); */
536
/* scan through all <td> entries and search the line nums */
538
if ((td_len = get_element(td, "td", &td)) > 0) {
542
/* time does not have a class but comes immediately after stop :-( */
545
get_text(td, "td", str, sizeof(str));
547
/* time needs special treatment */
548
if (strncasecmp(str, "sofort", strlen("sofort")) == 0)
551
/* skip everything that is not a number */
556
/* and convert remaining to number */
562
shm->entry[shm->entries - 1].time = i;
567
/* linenum and stopname fields have proper classes */
568
if ((attr_len = get_attrib(td, "class", &attr)) > 0) {
570
if (strncasecmp(attr, "lineNum", strlen("lineNum")) == 0) {
572
get_text(td, "td", str, sizeof(str));
574
if (shm->entries < MAX_LINES) {
575
/* allocate a new slot */
577
shm->entry[shm->entries - 1].time = -1;
578
memset(shm->entry[shm->entries - 1].line, 0, MAX_LINE_LENGTH + 1);
579
memset(shm->entry[shm->entries - 1].station, 0, MAX_STATION_LENGTH + 1);
581
/* add new lines entry */
582
strncpy(shm->entry[shm->entries - 1].line, str, MAX_LINE_LENGTH);
584
overflow = 1; /* don't add further entries */
587
if (strncasecmp(attr, "stopname", strlen("stopname")) == 0) {
589
get_text(td, "td", str, sizeof(str));
592
/* stopname may need further tuning */
593
process_station_string(str);
596
strncpy(shm->entry[shm->entries - 1].station, str, MAX_STATION_LENGTH);
602
} while (td_len >= 0);
613
static int kvv_fork(void)
618
info("[KVV] creating client thread");
620
/* set this here to prevent continous retries if init fails */
623
/* create communication buffer */
624
shmid = shm_create((void **) &shm, sizeof(kvv_shm_t));
628
error("[KVV] Shared memory allocation failed!");
632
/* attach client thread */
633
mutex = mutex_create();
634
pid = thread_create("plugin_kvv", kvv_client, NULL);
637
error("[KVV] Unable to fork client: %s", strerror(errno));
641
info("[KVV] forked client with pid %d", pid);
645
static void kvv_start(void)
647
static int started = 0;
657
/* parse parameter */
658
if ((p = cfg_get(SECTION, "StationID", DEFAULT_STATION_ID)) != NULL) {
659
station_id = malloc(strlen(p) + 1);
660
strcpy(station_id, p);
662
info("[KVV] Using station %s", station_id);
664
if ((p = cfg_get(SECTION, "Proxy", NULL)) != NULL) {
665
proxy_name = malloc(strlen(p) + 1);
666
strcpy(proxy_name, p);
667
info("[KVV] Using proxy \"%s\"", proxy_name);
670
if (cfg_number(SECTION, "Port", 0, 0, 65535, &val) > 0) {
672
info("[KVV] Using port %d", port);
674
info("[KVV] Using default port %d", port);
677
if (cfg_number(SECTION, "Refresh", 0, 0, 65535, &val) > 0) {
679
info("[KVV] Using %d seconds refresh interval", refresh);
681
info("[KVV] Using default refresh interval of %d seconds", refresh);
684
if (cfg_number(SECTION, "Abbreviate", 0, 0, 65535, &val) > 0) {
686
info("[KVV] Abbreviation enabled: %s", abbreviate ? "on" : "off");
688
info("[KVV] Default abbreviation setting: %s", abbreviate ? "on" : "off");
693
static void kvv_line(RESULT * result, RESULT * arg1)
695
int index = (int) R2N(arg1);
699
if (kvv_fork() != 0) {
700
SetResult(&result, R_STRING, "");
706
if (index < shm->entries) {
707
SetResult(&result, R_STRING, shm->entry[index].line);
709
SetResult(&result, R_STRING, "");
714
static void kvv_station(RESULT * result, RESULT * arg1)
716
int index = (int) R2N(arg1);
720
if (kvv_fork() != 0) {
721
SetResult(&result, R_STRING, "");
727
if (shm->error && index == 0)
728
SetResult(&result, R_STRING, "Server Err");
730
if (index < shm->entries)
731
SetResult(&result, R_STRING, shm->entry[index].station);
733
SetResult(&result, R_STRING, "");
739
static void kvv_time(RESULT * result, RESULT * arg1)
741
int index = (int) R2N(arg1);
746
if (kvv_fork() != 0) {
747
SetResult(&result, R_STRING, "");
753
if (index < shm->entries)
754
value = shm->entry[index].time;
756
SetResult(&result, R_NUMBER, &value);
761
static void kvv_time_str(RESULT * result, RESULT * arg1)
763
int index = (int) R2N(arg1);
767
if (kvv_fork() != 0) {
768
SetResult(&result, R_STRING, "");
774
if (index < shm->entries) {
776
sprintf(str, "%d", shm->entry[index].time);
777
SetResult(&result, R_STRING, str);
779
SetResult(&result, R_STRING, "");
784
/* plugin initialization */
785
int plugin_init_kvv(void)
787
/* register all our cool functions */
788
AddFunction("kvv::line", 1, kvv_line);
789
AddFunction("kvv::station", 1, kvv_station);
790
AddFunction("kvv::time", 1, kvv_time);
791
AddFunction("kvv::time_str", 1, kvv_time_str);
795
void plugin_exit_kvv(void)
797
/* kill client thread if it's running */
803
/* free shared mem and its mutex */
805
shm_destroy(shmid, shm);
806
mutex_destroy(mutex);