/* $Id$ * $URL$ * * plugin kvv (karlsruher verkehrsverbund) * * Copyright (C) 2006 Till Harbaum * Copyright (C) 2006 The LCD4Linux Team * * This file is part of LCD4Linux. * * LCD4Linux is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * LCD4Linux is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * exported functions: * * int plugin_init_kvv (void) * adds various functions * void plugin_exit_kvv (void) * */ /* define the include files you need */ #include "config.h" #include #include #include #include #include #include #include /* network specific includes */ #include #include #include #include #include /* these should always be included */ #include "debug.h" #include "plugin.h" #include "cfg.h" #include "thread.h" /* these can't be configured as it doesn't make sense to change them */ #define HTTP_SERVER "www.init-ka.de" #define HTTP_REQUEST "/webfgi/StopInfoInplace.aspx?ID=%s" #define USER_AGENT "lcd4linux - KVV plugin (http://ssl.bulix.org/projects/lcd4linux/wiki/plugin_kvv)" #define DEFAULT_STATION_ID "89" /* Hauptbahnhof */ /* example ids: * 89 = Hauptbahnhof * 12_701 = Berufsakademie */ /* total max values to calculate shm size */ #define MAX_LINES 4 #define MAX_LINE_LENGTH 8 #define MAX_STATION_LENGTH 40 typedef struct { char line[MAX_LINE_LENGTH + 1]; char station[MAX_STATION_LENGTH + 1]; int time; } kvv_entry_t; typedef struct { int entries, error; kvv_entry_t entry[MAX_LINES]; } kvv_shm_t; static char *station_id = NULL; static char *proxy_name = NULL; static int port = 80; static pid_t pid = -1; static int refresh = 60; static int abbreviate = 0; static int initialized = 0; static int mutex = 0; static int shmid = -1; static kvv_shm_t *shm = NULL; #define SECTION "Plugin:KVV" #define TIMEOUT_SHORT 1 /* wait this long for additional data */ #define TIMEOUT_LONG 10 /* wait this long for initial data */ /* search an element in the result string */ static int get_element(char *input, char *name, char **data) { int skip = 0; int len = 0; int state = 0; /* nothing found yet */ /* search entire string */ while (*input) { if (skip == 0) { switch (state) { case 0: if (*input == '<
# automatically generated from http://www.digitv.fi/sivu.asp?path=1;8224;9519
# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy
T 538000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE
T 658000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE
T 618000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE
} } while (i > 0); ibuffer[count] = 0; /* terminate string */ close(sock); if (!count) info("[KVV] empty/no reply"); if (count > 0) { char *input, *cookie, *name = NULL, *value = NULL; int input_len, cookie_len, name_len, value_len; /* buffer to html encode value */ char value_enc[512]; int value_enc_len; /* find cookie */ cookie_len = 0; cookie = strstr(ibuffer, "Set-Cookie:"); if (cookie) { cookie += strlen("Set-Cookie:"); while (*cookie == ' ') cookie++; while (cookie[cookie_len] != ';') cookie_len++; } /* find input element */ input_len = get_element(ibuffer, "input", &input); if (input_len > 0) { char *input_end = input; while (*input_end != '>') input_end++; while (*input_end != '\"') input_end--; *(input_end + 1) = 0; name_len = get_attrib(input, "name", &name); value_len = get_attrib(input, "value", &value); for (value_enc_len = 0, i = 0; i < value_len; i++) { if (isalnum(value[i])) value_enc[value_enc_len++] = value[i]; else { sprintf(value_enc + value_enc_len, "%%%02X", 0xff & value[i]); value_enc_len += 3; } } if (cookie_len >= 0) cookie[cookie_len] = 0; if (name_len >= 0) name[name_len] = 0; if (value_len >= 0) value[value_len] = 0; if (value_enc_len >= 0) value_enc[value_enc_len] = 0; sock = http_open(connect_to); /* send POST */ if (snprintf(obuffer, sizeof(obuffer), "POST http://%s" HTTP_REQUEST " HTTP/1.1\n" "Host: %s\n" "User-Agent: " USER_AGENT "\n" "Cookie: %s\n" "Content-Type: application/x-www-form-urlencoded\n" "Content-Length: %d\n" "\n%s=%s", server_name, station_id, server_name, cookie, name_len + value_enc_len + 1, name, value_enc) >= (int) sizeof(obuffer)) { info("[KVV] Warning, request has been truncated!"); } info("[KVV] Sending second (POST) request ..."); send(sock, obuffer, strlen(obuffer), 0); count = 0; do { fd_set rfds; struct timeval tv; FD_ZERO(&rfds); FD_SET(sock, &rfds); tv.tv_sec = count ? TIMEOUT_SHORT : TIMEOUT_LONG; tv.tv_usec = 0; i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (i > 0) { i = recv(sock, ibuffer + count, sizeof(ibuffer) - count - 1, 0); count += i; } } while (i > 0); /* leave on select or read error */ ibuffer[count] = 0; /* printf("Result (%d):\n%s\n", count, ibuffer); */ /* close connection */ close(sock); if (!count) info("[KVV] empty/no reply"); if (count > 0) { int last_was_stop = 0; char *td = ibuffer; char str[32]; int td_len, i, overflow = 0; /* lock shared memory */ mutex_lock(mutex); /* free allocated memory */ shm->entries = 0; if (strstr(ibuffer, "Die Daten konnten nicht abgefragt werden.") != NULL) { info("[KVV] Server returned error!"); /* printf("%s\n", ibuffer); */ shm->error = 1; } else shm->error = 0; /* scan through all entries and search the line nums */ do { if ((td_len = get_element(td, "td", &td)) > 0) { char *attr, *p; int attr_len; /* time does not have a class but comes immediately after stop :-( */ if (last_was_stop) { td += td_len + 1; get_text(td, "td", str, sizeof(str)); /* time needs special treatment */ if (strncasecmp(str, "sofort", strlen("sofort")) == 0) i = 0; else { /* skip everything that is not a number */ p = str; while (!isdigit(*p)) p++; /* and convert remaining to number */ i = atoi(p); } /* save time */ if (!overflow) shm->entry[shm->entries - 1].time = i; last_was_stop = 0; } /* linenum and stopname fields have proper classes */ if ((attr_len = get_attrib(td, "class", &attr)) > 0) { if (strncasecmp(attr, "lineNum", strlen("lineNum")) == 0) { td += td_len + 1; get_text(td, "td", str, sizeof(str)); if (shm->entries < MAX_LINES) { /* allocate a new slot */ shm->entries++; shm->entry[shm->entries - 1].time = -1; memset(shm->entry[shm->entries - 1].line, 0, MAX_LINE_LENGTH + 1); memset(shm->entry[shm->entries - 1].station, 0, MAX_STATION_LENGTH + 1); /* add new lines entry */ strncpy(shm->entry[shm->entries - 1].line, str, MAX_LINE_LENGTH); } else overflow = 1; /* don't add further entries */ } if (strncasecmp(attr, "stopname", strlen("stopname")) == 0) { td += td_len + 1; get_text(td, "td", str, sizeof(str)); /* stopname may need further tuning */ process_station_string(str); if (!overflow) strncpy(shm->entry[shm->entries - 1].station, str, MAX_STATION_LENGTH); last_was_stop = 1; } } } } while (td_len >= 0); mutex_unlock(mutex); } } } sleep(refresh); } } static int kvv_fork(void) { if (initialized) return 0; info("[KVV] creating client thread"); /* set this here to prevent continous retries if init fails */ initialized = 1; /* create communication buffer */ shmid = shm_create((void **) &shm, sizeof(kvv_shm_t)); /* catch error */ if (shmid < 0) { error("[KVV] Shared memory allocation failed!"); return -1; } /* attach client thread */ mutex = mutex_create(); pid = thread_create("plugin_kvv", kvv_client, NULL); if (pid < 0) { error("[KVV] Unable to fork client: %s", strerror(errno)); return -1; } info("[KVV] forked client with pid %d", pid); return 0; } static void kvv_line(RESULT * result, RESULT * arg1) { int index = (int) R2N(arg1); if (kvv_fork() != 0) { SetResult(&result, R_STRING, ""); return; } mutex_lock(mutex); if (index < shm->entries) { SetResult(&result, R_STRING, shm->entry[index].line); } else SetResult(&result, R_STRING, ""); mutex_unlock(mutex); } static void kvv_station(RESULT * result, RESULT * arg1) { int index = (int) R2N(arg1); if (kvv_fork() != 0) { SetResult(&result, R_STRING, ""); return; } mutex_lock(mutex); if (shm->error && index == 0) SetResult(&result, R_STRING, "Server Err"); else { if (index < shm->entries) SetResult(&result, R_STRING, shm->entry[index].station); else SetResult(&result, R_STRING, ""); } mutex_unlock(mutex); } static void kvv_time(RESULT * result, RESULT * arg1) { int index = (int) R2N(arg1); double value = -1.0; if (kvv_fork() != 0) { SetResult(&result, R_STRING, ""); return; } mutex_lock(mutex); if (index < shm->entries) value = shm->entry[index].time; SetResult(&result, R_NUMBER, &value); mutex_unlock(mutex); } static void kvv_time_str(RESULT * result, RESULT * arg1) { int index = (int) R2N(arg1); if (kvv_fork() != 0) { SetResult(&result, R_STRING, ""); return; } mutex_lock(mutex); if (index < shm->entries) { char str[8]; sprintf(str, "%d", shm->entry[index].time); SetResult(&result, R_STRING, str); } else SetResult(&result, R_STRING, ""); mutex_unlock(mutex); } /* plugin initialization */ int plugin_init_kvv(void) { int val; char *p; /* register all our cool functions */ AddFunction("kvv::line", 1, kvv_line); AddFunction("kvv::station", 1, kvv_station); AddFunction("kvv::time", 1, kvv_time); AddFunction("kvv::time_str", 1, kvv_time_str); /* parse parameter */ if ((p = cfg_get(SECTION, "StationID", DEFAULT_STATION_ID)) != NULL) { station_id = malloc(strlen(p) + 1); strcpy(station_id, p); } info("[KVV] Using station %s", station_id); if ((p = cfg_get(SECTION, "Proxy", NULL)) != NULL) { proxy_name = malloc(strlen(p) + 1); strcpy(proxy_name, p); info("[KVV] Using proxy \"%s\"", proxy_name); } if (cfg_number(SECTION, "Port", 0, 0, 65535, &val) > 0) { port = val; info("[KVV] Using port %d", port); } else { info("[KVV] Using default port %d", port); } if (cfg_number(SECTION, "Refresh", 0, 0, 65535, &val) > 0) { refresh = val; info("[KVV] Using %d seconds refresh interval", refresh); } else { info("[KVV] Using default refresh interval of %d seconds", refresh); } if (cfg_number(SECTION, "Abbreviate", 0, 0, 65535, &val) > 0) { abbreviate = val; info("[KVV] Abbreviation enabled: %s", abbreviate ? "on" : "off"); } else { info("[KVV] Default abbreviation setting: %s", abbreviate ? "on" : "off"); } return 0; } void plugin_exit_kvv(void) { /* kill client thread if it's running */ if (initialized) { /* kill client */ if (pid != -1) thread_destroy(pid); /* free shared mem and its mutex */ if (shm) { shm_destroy(shmid, shm); mutex_destroy(mutex); } } if (station_id) free(station_id); if (proxy_name) free(proxy_name); }