~siretart/lcd4linux/debian

« back to all changes in this revision

Viewing changes to plugin_kvv.c

  • Committer: Reinhard Tartler
  • Date: 2011-04-27 17:24:15 UTC
  • mto: This revision was merged to the branch mainline in revision 750.
  • Revision ID: siretart@tauware.de-20110427172415-6n4aptmvmz0eztvm
Tags: upstream-0.11.0~svn1143
ImportĀ upstreamĀ versionĀ 0.11.0~svn1143

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 $
 
3
 *
 
4
 * plugin kvv (karlsruher verkehrsverbund)
 
5
 *
 
6
 * Copyright (C) 2006 Till Harbaum <till@harbaum.org>
 
7
 * Copyright (C) 2006 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 
8
 *
 
9
 * This file is part of LCD4Linux.
 
10
 *
 
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)
 
14
 * any later version.
 
15
 *
 
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.
 
20
 *
 
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.
 
24
 *
 
25
 */
 
26
 
 
27
/* 
 
28
 * exported functions:
 
29
 *
 
30
 * int plugin_init_kvv (void)
 
31
 *  adds various functions
 
32
 * void plugin_exit_kvv (void)
 
33
 *
 
34
 */
 
35
 
 
36
/* define the include files you need */
 
37
#include "config.h"
 
38
 
 
39
#include <stdio.h>
 
40
#include <stdlib.h>
 
41
#include <unistd.h>
 
42
#include <string.h>
 
43
#include <ctype.h>
 
44
#include <errno.h>
 
45
#include <signal.h>
 
46
 
 
47
/* network specific includes */
 
48
#include <sys/types.h>
 
49
#include <sys/socket.h>
 
50
#include <netinet/in.h>
 
51
#include <netdb.h>
 
52
#include <arpa/inet.h>
 
53
 
 
54
/* these should always be included */
 
55
#include "debug.h"
 
56
#include "plugin.h"
 
57
#include "cfg.h"
 
58
#include "thread.h"
 
59
 
 
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)"
 
64
 
 
65
#define DEFAULT_STATION_ID    "89"      /* Hauptbahnhof */
 
66
 
 
67
/* example ids: 
 
68
 * 89     = Hauptbahnhof
 
69
 * 12_701 = Berufsakademie
 
70
 */
 
71
 
 
72
/* total max values to calculate shm size */
 
73
#define MAX_LINES           4
 
74
#define MAX_LINE_LENGTH     8
 
75
#define MAX_STATION_LENGTH 40
 
76
 
 
77
typedef struct {
 
78
    char line[MAX_LINE_LENGTH + 1];
 
79
    char station[MAX_STATION_LENGTH + 1];
 
80
    int time;
 
81
} kvv_entry_t;
 
82
 
 
83
typedef struct {
 
84
    int entries, error;
 
85
    kvv_entry_t entry[MAX_LINES];
 
86
} kvv_shm_t;
 
87
 
 
88
static char *station_id = NULL;
 
89
static char *proxy_name = NULL;
 
90
static int port = 80;
 
91
static pid_t pid = -1;
 
92
static int refresh = 60;
 
93
static int abbreviate = 0;
 
94
 
 
95
static int initialized = 0;
 
96
static int mutex = 0;
 
97
static int shmid = -1;
 
98
static kvv_shm_t *shm = NULL;
 
99
 
 
100
#define SECTION   "Plugin:KVV"
 
101
 
 
102
#define TIMEOUT_SHORT 1         /* wait this long for additional data */
 
103
#define TIMEOUT_LONG  10        /* wait this long for initial data */
 
104
 
 
105
/* search an element in the result string */
 
106
static int get_element(char *input, char *name, char **data)
 
107
{
 
108
    int skip = 0;
 
109
    int len = 0;
 
110
    int state = 0;              /* nothing found yet */
 
111
 
 
112
    /* search entire string */
 
113
    while (*input) {
 
114
 
 
115
        if (skip == 0) {
 
116
            switch (state) {
 
117
            case 0:
 
118
                if (*input == '<')
 
119
                    state = 1;
 
120
                else
 
121
                    state = 0;
 
122
                break;
 
123
 
 
124
            case 1:
 
125
                /* ignore white spaces */
 
126
                if (*input != ' ') {
 
127
                    if (strncasecmp(input, name, strlen(name)) == 0) {
 
128
                        state = 2;
 
129
                        skip = strlen(name) - 1;
 
130
                    } else
 
131
                        state = 0;
 
132
                }
 
133
                break;
 
134
 
 
135
            case 2:
 
136
                if (*input == ' ') {
 
137
                    *data = ++input;
 
138
                    while (*input && (*input++ != '>'))
 
139
                        len++;
 
140
 
 
141
                    return len;
 
142
                } else
 
143
                    state = 0;
 
144
                break;
 
145
            }
 
146
        } else if (skip)
 
147
            skip--;
 
148
 
 
149
        input++;
 
150
    }
 
151
 
 
152
    return -1;
 
153
}
 
154
 
 
155
/* serach an attribute within an element */
 
156
static int get_attrib(char *input, char *name, char **data)
 
157
{
 
158
    int skip = 0;
 
159
    int len = 0;
 
160
    int state = 0;              /* nothing found */
 
161
 
 
162
    /* search in this element */
 
163
    while (*input != '>') {
 
164
        /* ignore white spaces */
 
165
        if (((*input != ' ') && (*input != '\t')) && (skip == 0)) {
 
166
            switch (state) {
 
167
            case 0:
 
168
                if (strncasecmp(input, name, strlen(name)) == 0) {
 
169
                    state = 1;
 
170
                    skip = strlen(name) - 1;
 
171
                }
 
172
                break;
 
173
 
 
174
            case 1:
 
175
                if (*input == '=')
 
176
                    state = 2;
 
177
                else
 
178
                    state = 0;
 
179
                break;
 
180
 
 
181
            case 2:
 
182
                if (*input == '\"') {
 
183
                    *data = ++input;
 
184
                    while (*input++ != '\"')
 
185
                        len++;
 
186
 
 
187
                    return len;
 
188
                } else
 
189
                    state = 0;
 
190
 
 
191
                break;
 
192
            }
 
193
        } else if (skip)
 
194
            skip--;
 
195
 
 
196
        input++;
 
197
    }
 
198
    return -1;
 
199
}
 
200
 
 
201
static int http_open(char *name)
 
202
{
 
203
    struct sockaddr_in server;
 
204
    struct hostent *host_info;
 
205
    unsigned long addr;
 
206
    int sock;
 
207
 
 
208
    /* create socket */
 
209
    sock = socket(PF_INET, SOCK_STREAM, 0);
 
210
    if (sock < 0) {
 
211
        perror("failed to create socket");
 
212
        return -1;
 
213
    }
 
214
 
 
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));
 
220
    } else {
 
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);
 
225
            return -1;
 
226
        }
 
227
        memcpy((char *) &server.sin_addr, host_info->h_addr, host_info->h_length);
 
228
    }
 
229
 
 
230
    server.sin_family = AF_INET;
 
231
    server.sin_port = htons(port);
 
232
 
 
233
    /* Baue die Verbindung zum Server auf */
 
234
    if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
 
235
        perror("can't connect to server");
 
236
        return -1;
 
237
    }
 
238
 
 
239
    return sock;
 
240
}
 
241
 
 
242
static void get_text(char *input, char *end, char *dest, int dlen)
 
243
{
 
244
    int state = 0;              /* nothing yet, outside any element */
 
245
    int cnt = 0;
 
246
 
 
247
    while (*input) {
 
248
        switch (state) {
 
249
        case 0:
 
250
            if (*input == '<')
 
251
                state = 1;
 
252
            else {
 
253
                if (cnt < (dlen - 1))
 
254
                    dest[cnt++] = *input;
 
255
            }
 
256
            break;
 
257
 
 
258
        case 1:
 
259
            if (*input == '/')
 
260
                state = 2;
 
261
            else if (*input == '>')
 
262
                state = 0;
 
263
            break;
 
264
 
 
265
        case 2:
 
266
            if (strncasecmp(input, end, strlen(end)) == 0) {
 
267
                dest[cnt++] = 0;
 
268
                return;
 
269
            }
 
270
            break;
 
271
        }
 
272
 
 
273
        input++;
 
274
    }
 
275
 
 
276
}
 
277
 
 
278
static void process_station_string(char *str)
 
279
{
 
280
    char *p, *q;
 
281
    int last, i;
 
282
 
 
283
    /* some strings to replace */
 
284
    char *repl[] = {
 
285
        "Hauptbahnhof", "Hbf.",
 
286
        "Bahnhof", "Bhf.",
 
287
        "Karlsruhe", "KA",
 
288
        "Schienenersatzverkehr", "Ersatzv.",
 
289
        "Marktplatz", "Marktpl.",
 
290
    };
 
291
 
 
292
    /* decode utf8 */
 
293
    p = q = str;
 
294
    last = 0;
 
295
    while (*p) {
 
296
        if (last) {
 
297
            *q++ = (last << 6) | (*p & 0x3f);
 
298
            last = 0;
 
299
        } else if ((*p & 0xe0) == 0xc0) {
 
300
            last = *p & 3;
 
301
        } else
 
302
            *q++ = *p;
 
303
 
 
304
        p++;
 
305
    }
 
306
    *q++ = 0;
 
307
 
 
308
    /* erase multiple spaces and replace umlauts */
 
309
    p = q = str;
 
310
    last = 1;                   /* no leading spaces */
 
311
    while (*p) {
 
312
        if ((!last) || (*p != ' ')) {
 
313
 
 
314
            /* translate from latin1 to hd44780 */
 
315
            if (*p == (char) 228)       /* lower a umlaut */
 
316
                *q++ = (char) 0xe1;
 
317
            else if (*p == (char) 223)  /* sz ligature */
 
318
                *q++ = (char) 0xe2;
 
319
            else if (*p == (char) 246)  /* lower o umlaut */
 
320
                *q++ = (char) 0xef;
 
321
            else if (*p == (char) 252)  /* lower u umlaut */
 
322
                *q++ = (char) 0xf5;
 
323
            else
 
324
                *q++ = *p;
 
325
        }
 
326
 
 
327
        last = (*p == ' ');
 
328
        p++;
 
329
    }
 
330
    *q++ = 0;
 
331
 
 
332
    /* replace certain (long) words with e.g. abbreviations if enabled */
 
333
    if (abbreviate) {
 
334
 
 
335
        for (i = 0; i < (int) (sizeof(repl) / (2 * sizeof(char *))); i++) {
 
336
            if ((p = strstr(str, repl[2 * i])) != NULL) {
 
337
 
 
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);
 
343
            }
 
344
        }
 
345
    }
 
346
}
 
347
 
 
348
static void kvv_client( __attribute__ ((unused))
 
349
                       void *dummy)
 
350
{
 
351
    char ibuffer[8192];
 
352
    char obuffer[1024];
 
353
    int count, i, sock;
 
354
 
 
355
    char server_name[] = HTTP_SERVER;
 
356
    char *connect_to;
 
357
 
 
358
    /* connect to proxy if given, to server otherwise */
 
359
    if ((proxy_name != NULL) && (strlen(proxy_name) != 0))
 
360
        connect_to = proxy_name;
 
361
    else
 
362
        connect_to = server_name;
 
363
 
 
364
    info("[KVV] Connecting to %s", connect_to);
 
365
 
 
366
    while (1) {
 
367
 
 
368
        sock = http_open(connect_to);
 
369
        if (sock < 0) {
 
370
            error("[KVV] Error accessing server/proxy: %s", strerror(errno));
 
371
            return;
 
372
        }
 
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)) {
 
378
 
 
379
            info("[KVV] Warning, request has been truncated!");
 
380
        }
 
381
 
 
382
        info("[KVV] Sending first (GET) request ...");
 
383
        send(sock, obuffer, strlen(obuffer), 0);
 
384
 
 
385
        count = 0;
 
386
        do {
 
387
            fd_set rfds;
 
388
            struct timeval tv;
 
389
 
 
390
            FD_ZERO(&rfds);
 
391
            FD_SET(sock, &rfds);
 
392
 
 
393
            tv.tv_sec = count ? TIMEOUT_SHORT : TIMEOUT_LONG;
 
394
            tv.tv_usec = 0;
 
395
 
 
396
            i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
 
397
            if (i < 0) {
 
398
                perror("select");
 
399
                exit(1);
 
400
            }
 
401
 
 
402
            if (i != 0) {
 
403
                i = recv(sock, ibuffer + count, sizeof(ibuffer) - count - 1, 0);
 
404
                count += i;
 
405
            }
 
406
        }
 
407
        while (i > 0);
 
408
 
 
409
        ibuffer[count] = 0;     /* terminate string */
 
410
        close(sock);
 
411
 
 
412
        if (!count)
 
413
            info("[KVV] empty/no reply");
 
414
 
 
415
        if (count > 0) {
 
416
            char *input, *cookie, *name = NULL, *value = NULL;
 
417
            int input_len, cookie_len, name_len, value_len;
 
418
 
 
419
            /* buffer to html encode value */
 
420
            char value_enc[512];
 
421
            int value_enc_len;
 
422
 
 
423
            /* find cookie */
 
424
            cookie_len = 0;
 
425
            cookie = strstr(ibuffer, "Set-Cookie:");
 
426
            if (cookie) {
 
427
                cookie += strlen("Set-Cookie:");
 
428
 
 
429
                while (*cookie == ' ')
 
430
                    cookie++;
 
431
 
 
432
                while (cookie[cookie_len] != ';')
 
433
                    cookie_len++;
 
434
            }
 
435
            /* find input element */
 
436
            input_len = get_element(ibuffer, "input", &input);
 
437
 
 
438
 
 
439
            if (input_len > 0) {
 
440
                char *input_end = input;
 
441
                while (*input_end != '>')
 
442
                    input_end++;
 
443
                while (*input_end != '\"')
 
444
                    input_end--;
 
445
                *(input_end + 1) = 0;
 
446
 
 
447
                name_len = get_attrib(input, "name", &name);
 
448
                value_len = get_attrib(input, "value", &value);
 
449
 
 
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];
 
453
                    else {
 
454
                        sprintf(value_enc + value_enc_len, "%%%02X", 0xff & value[i]);
 
455
                        value_enc_len += 3;
 
456
                    }
 
457
                }
 
458
 
 
459
                if (cookie_len >= 0)
 
460
                    cookie[cookie_len] = 0;
 
461
                if (name_len >= 0)
 
462
                    name[name_len] = 0;
 
463
                if (value_len >= 0)
 
464
                    value[value_len] = 0;
 
465
                if (value_enc_len >= 0)
 
466
                    value_enc[value_enc_len] = 0;
 
467
 
 
468
                sock = http_open(connect_to);
 
469
 
 
470
                /* send POST */
 
471
                if (snprintf(obuffer, sizeof(obuffer),
 
472
                             "POST http://%s" HTTP_REQUEST " HTTP/1.1\n"
 
473
                             "Host: %s\n"
 
474
                             "User-Agent: " USER_AGENT "\n"
 
475
                             "Cookie: %s\n"
 
476
                             "Content-Type: application/x-www-form-urlencoded\n"
 
477
                             "Content-Length: %d\n"
 
478
                             "\n%s=%s",
 
479
                             server_name, station_id, server_name, cookie, name_len + value_enc_len + 1, name,
 
480
                             value_enc) >= (int) sizeof(obuffer)) {
 
481
 
 
482
                    info("[KVV] Warning, request has been truncated!");
 
483
                }
 
484
 
 
485
                info("[KVV] Sending second (POST) request ...");
 
486
                send(sock, obuffer, strlen(obuffer), 0);
 
487
 
 
488
                count = 0;
 
489
                do {
 
490
                    fd_set rfds;
 
491
                    struct timeval tv;
 
492
 
 
493
                    FD_ZERO(&rfds);
 
494
                    FD_SET(sock, &rfds);
 
495
 
 
496
                    tv.tv_sec = count ? TIMEOUT_SHORT : TIMEOUT_LONG;
 
497
                    tv.tv_usec = 0;
 
498
 
 
499
                    i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
 
500
                    if (i > 0) {
 
501
                        i = recv(sock, ibuffer + count, sizeof(ibuffer) - count - 1, 0);
 
502
                        count += i;
 
503
                    }
 
504
                }
 
505
                while (i > 0);  /* leave on select or read error */
 
506
 
 
507
                ibuffer[count] = 0;
 
508
 
 
509
                /* printf("Result (%d):\n%s\n", count, ibuffer); */
 
510
 
 
511
                /* close connection */
 
512
                close(sock);
 
513
 
 
514
                if (!count)
 
515
                    info("[KVV] empty/no reply");
 
516
 
 
517
                if (count > 0) {
 
518
                    int last_was_stop = 0;
 
519
                    char *td = ibuffer;
 
520
                    char str[32];
 
521
                    int td_len, i, overflow = 0;
 
522
 
 
523
                    /* lock shared memory */
 
524
                    mutex_lock(mutex);
 
525
 
 
526
                    /* free allocated memory */
 
527
                    shm->entries = 0;
 
528
 
 
529
                    if (strstr(ibuffer, "Die Daten konnten nicht abgefragt werden.") != NULL) {
 
530
                        info("[KVV] Server returned error!");
 
531
                        /* printf("%s\n", ibuffer); */
 
532
                        shm->error = 1;
 
533
                    } else
 
534
                        shm->error = 0;
 
535
 
 
536
                    /* scan through all <td> entries and search the line nums */
 
537
                    do {
 
538
                        if ((td_len = get_element(td, "td", &td)) > 0) {
 
539
                            char *attr, *p;
 
540
                            int attr_len;
 
541
 
 
542
                            /* time does not have a class but comes immediately after stop :-( */
 
543
                            if (last_was_stop) {
 
544
                                td += td_len + 1;
 
545
                                get_text(td, "td", str, sizeof(str));
 
546
 
 
547
                                /* time needs special treatment */
 
548
                                if (strncasecmp(str, "sofort", strlen("sofort")) == 0)
 
549
                                    i = 0;
 
550
                                else {
 
551
                                    /* skip everything that is not a number */
 
552
                                    p = str;
 
553
                                    while (!isdigit(*p))
 
554
                                        p++;
 
555
 
 
556
                                    /* and convert remaining to number */
 
557
                                    i = atoi(p);
 
558
                                }
 
559
 
 
560
                                /* save time */
 
561
                                if (!overflow)
 
562
                                    shm->entry[shm->entries - 1].time = i;
 
563
 
 
564
                                last_was_stop = 0;
 
565
                            }
 
566
 
 
567
                            /* linenum and stopname fields have proper classes */
 
568
                            if ((attr_len = get_attrib(td, "class", &attr)) > 0) {
 
569
 
 
570
                                if (strncasecmp(attr, "lineNum", strlen("lineNum")) == 0) {
 
571
                                    td += td_len + 1;
 
572
                                    get_text(td, "td", str, sizeof(str));
 
573
 
 
574
                                    if (shm->entries < MAX_LINES) {
 
575
                                        /* allocate a new slot */
 
576
                                        shm->entries++;
 
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);
 
580
 
 
581
                                        /* add new lines entry */
 
582
                                        strncpy(shm->entry[shm->entries - 1].line, str, MAX_LINE_LENGTH);
 
583
                                    } else
 
584
                                        overflow = 1;   /* don't add further entries */
 
585
                                }
 
586
 
 
587
                                if (strncasecmp(attr, "stopname", strlen("stopname")) == 0) {
 
588
                                    td += td_len + 1;
 
589
                                    get_text(td, "td", str, sizeof(str));
 
590
 
 
591
 
 
592
                                    /* stopname may need further tuning */
 
593
                                    process_station_string(str);
 
594
 
 
595
                                    if (!overflow)
 
596
                                        strncpy(shm->entry[shm->entries - 1].station, str, MAX_STATION_LENGTH);
 
597
 
 
598
                                    last_was_stop = 1;
 
599
                                }
 
600
                            }
 
601
                        }
 
602
                    } while (td_len >= 0);
 
603
 
 
604
                    mutex_unlock(mutex);
 
605
                }
 
606
            }
 
607
        }
 
608
 
 
609
        sleep(refresh);
 
610
    }
 
611
}
 
612
 
 
613
static int kvv_fork(void)
 
614
{
 
615
    if (initialized)
 
616
        return 0;
 
617
 
 
618
    info("[KVV] creating client thread");
 
619
 
 
620
    /* set this here to prevent continous retries if init fails */
 
621
    initialized = 1;
 
622
 
 
623
    /* create communication buffer */
 
624
    shmid = shm_create((void **) &shm, sizeof(kvv_shm_t));
 
625
 
 
626
    /* catch error */
 
627
    if (shmid < 0) {
 
628
        error("[KVV] Shared memory allocation failed!");
 
629
        return -1;
 
630
    }
 
631
 
 
632
    /* attach client thread */
 
633
    mutex = mutex_create();
 
634
    pid = thread_create("plugin_kvv", kvv_client, NULL);
 
635
 
 
636
    if (pid < 0) {
 
637
        error("[KVV] Unable to fork client: %s", strerror(errno));
 
638
        return -1;
 
639
    }
 
640
 
 
641
    info("[KVV] forked client with pid %d", pid);
 
642
    return 0;
 
643
}
 
644
 
 
645
static void kvv_start(void)
 
646
{
 
647
    static int started = 0;
 
648
    int val;
 
649
    char *p;
 
650
 
 
651
 
 
652
    if (started)
 
653
        return;
 
654
 
 
655
    started = 1;
 
656
 
 
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);
 
661
    }
 
662
    info("[KVV] Using station %s", station_id);
 
663
 
 
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);
 
668
    }
 
669
 
 
670
    if (cfg_number(SECTION, "Port", 0, 0, 65535, &val) > 0) {
 
671
        port = val;
 
672
        info("[KVV] Using port %d", port);
 
673
    } else {
 
674
        info("[KVV] Using default port %d", port);
 
675
    }
 
676
 
 
677
    if (cfg_number(SECTION, "Refresh", 0, 0, 65535, &val) > 0) {
 
678
        refresh = val;
 
679
        info("[KVV] Using %d seconds refresh interval", refresh);
 
680
    } else {
 
681
        info("[KVV] Using default refresh interval of %d seconds", refresh);
 
682
    }
 
683
 
 
684
    if (cfg_number(SECTION, "Abbreviate", 0, 0, 65535, &val) > 0) {
 
685
        abbreviate = val;
 
686
        info("[KVV] Abbreviation enabled: %s", abbreviate ? "on" : "off");
 
687
    } else {
 
688
        info("[KVV] Default abbreviation setting: %s", abbreviate ? "on" : "off");
 
689
    }
 
690
 
 
691
}
 
692
 
 
693
static void kvv_line(RESULT * result, RESULT * arg1)
 
694
{
 
695
    int index = (int) R2N(arg1);
 
696
 
 
697
    kvv_start();
 
698
 
 
699
    if (kvv_fork() != 0) {
 
700
        SetResult(&result, R_STRING, "");
 
701
        return;
 
702
    }
 
703
 
 
704
    mutex_lock(mutex);
 
705
 
 
706
    if (index < shm->entries) {
 
707
        SetResult(&result, R_STRING, shm->entry[index].line);
 
708
    } else
 
709
        SetResult(&result, R_STRING, "");
 
710
 
 
711
    mutex_unlock(mutex);
 
712
}
 
713
 
 
714
static void kvv_station(RESULT * result, RESULT * arg1)
 
715
{
 
716
    int index = (int) R2N(arg1);
 
717
 
 
718
    kvv_start();
 
719
 
 
720
    if (kvv_fork() != 0) {
 
721
        SetResult(&result, R_STRING, "");
 
722
        return;
 
723
    }
 
724
 
 
725
    mutex_lock(mutex);
 
726
 
 
727
    if (shm->error && index == 0)
 
728
        SetResult(&result, R_STRING, "Server Err");
 
729
    else {
 
730
        if (index < shm->entries)
 
731
            SetResult(&result, R_STRING, shm->entry[index].station);
 
732
        else
 
733
            SetResult(&result, R_STRING, "");
 
734
    }
 
735
 
 
736
    mutex_unlock(mutex);
 
737
}
 
738
 
 
739
static void kvv_time(RESULT * result, RESULT * arg1)
 
740
{
 
741
    int index = (int) R2N(arg1);
 
742
    double value = -1.0;
 
743
 
 
744
    kvv_start();
 
745
 
 
746
    if (kvv_fork() != 0) {
 
747
        SetResult(&result, R_STRING, "");
 
748
        return;
 
749
    }
 
750
 
 
751
    mutex_lock(mutex);
 
752
 
 
753
    if (index < shm->entries)
 
754
        value = shm->entry[index].time;
 
755
 
 
756
    SetResult(&result, R_NUMBER, &value);
 
757
 
 
758
    mutex_unlock(mutex);
 
759
}
 
760
 
 
761
static void kvv_time_str(RESULT * result, RESULT * arg1)
 
762
{
 
763
    int index = (int) R2N(arg1);
 
764
 
 
765
    kvv_start();
 
766
 
 
767
    if (kvv_fork() != 0) {
 
768
        SetResult(&result, R_STRING, "");
 
769
        return;
 
770
    }
 
771
 
 
772
    mutex_lock(mutex);
 
773
 
 
774
    if (index < shm->entries) {
 
775
        char str[8];
 
776
        sprintf(str, "%d", shm->entry[index].time);
 
777
        SetResult(&result, R_STRING, str);
 
778
    } else
 
779
        SetResult(&result, R_STRING, "");
 
780
 
 
781
    mutex_unlock(mutex);
 
782
}
 
783
 
 
784
/* plugin initialization */
 
785
int plugin_init_kvv(void)
 
786
{
 
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);
 
792
    return 0;
 
793
}
 
794
 
 
795
void plugin_exit_kvv(void)
 
796
{
 
797
    /* kill client thread if it's running */
 
798
    if (initialized) {
 
799
        /* kill client */
 
800
        if (pid != -1)
 
801
            thread_destroy(pid);
 
802
 
 
803
        /* free shared mem and its mutex */
 
804
        if (shm) {
 
805
            shm_destroy(shmid, shm);
 
806
            mutex_destroy(mutex);
 
807
        }
 
808
    }
 
809
 
 
810
    if (station_id)
 
811
        free(station_id);
 
812
    if (proxy_name)
 
813
        free(proxy_name);
 
814
}