/************************************************************************* * Copyright 2009/2010/2011 Ralph Spitzner (rasp@spitzner.org) * * This file is part of v2Yahdr. * * Yahdr 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 3 of the License, or * (at your option) any later version. * * v2Yahdr 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 v2Yahdr. If not, see . **************************************************************************/ #include "ydefs.h" /* httpd - _very_ simple http server. listen -> accept -> do something -> close/die */ static int sockfd ,newsockfd, clilen; static struct sockaddr_in serv_addr,cli_addr; /*** send out channellist tv first. ***/ static void send_chans(FILE *to) { char *name; int type; rc = sqlite3_prepare_v2(db,"select distinct(name),type from serv order by type,name", -1, &stmt, 0); if( rc ) { fprintf(stderr, "SQL error: %d : %s @ %s %d\n", rc, sqlite3_errmsg(db),__FILE__,__LINE__); return ; } else { cols = sqlite3_column_count(stmt); // execute the statement do { rc = sqlite3_step(stmt); switch( rc ) { case SQLITE_DONE: break; case SQLITE_ROW: // print results for this row type = sqlite3_column_int(stmt,1); name = (char*)sqlite3_column_text(stmt,0); fprintf(to,"%d:%s\n",type,name); break; default: fprintf(stderr, "Error: %d : %s\n", rc, sqlite3_errmsg(db)); break; } } while( rc==SQLITE_ROW ); // finalize the statement to release resources sqlite3_finalize(stmt); } } /**** send now / next data for channel 'name' ****/ static void get_nownext(FILE *out,char *inname) { SERVICE *sp; char sql[255]; char name[255]; time_t now; //sqlite3 *epg_db; sqlite3_stmt *epg_stmt; int epg_rc; uint8_t found; escape_hy(inname,name,255); /* epg_rc = sqlite3_open(EPG_DB_NAME,&epg_db); if(epg_rc) { fprintf(stderr, "Can't open database: %s @ %d %s\n", sqlite3_errmsg(epg_db),__LINE__,__FILE__); return; }*/ sp = services; found = false; while(sp) { while(sp) { if(!strcmp(sp->name,name)) { break; } sp = sp->next; } if(sp == NULL) { fprintf(out,"404: Channel >%s< not found@NR@\n",name); return; } now = time(0); sprintf(sql,"select * from epg where tsid='%d' and s_id='%d' and start_time <= '%d' order by start_time desc limit 1", sp->tsid, sp->s_id, (int)now); epg_rc = sqlite3_prepare_v2(epg_db, sql, -1, &epg_stmt, 0); if( epg_rc ) { fprintf(stderr, "SQL error: %d : %s @ %s %d\n", rc, sqlite3_errmsg(epg_db),__FILE__,__LINE__); //printf("close testurns %s",sqlite3_close(epg_db) == SQLITE_BUSY ? "BUSY" : "other"); return; // exit(0); } epg_rc = sqlite3_step(epg_stmt); if(epg_rc == SQLITE_ROW) { found = true; fprintf(out,"%s@SEP@%d@SEP@%d@SEP@%s@SEP@%s@SEP@%s@SEP@@NR@\n", name, sqlite3_column_int(epg_stmt,3), /// start sqlite3_column_int(epg_stmt,4), /// duration sqlite3_column_text(epg_stmt,5), sqlite3_column_text(epg_stmt,6), sqlite3_column_text(epg_stmt,7) ); } sqlite3_finalize(epg_stmt); sprintf(sql,"select * from epg where tsid='%d' and s_id='%d' and start_time >= '%d' order by start_time limit 1", sp->tsid, sp->s_id, (int)now); epg_rc = sqlite3_prepare_v2(epg_db, sql, -1, &epg_stmt, 0); if( epg_rc ) { fprintf(stderr, "SQL error: %d : %s @ %s %d\n", rc, sqlite3_errmsg(epg_db),__FILE__,__LINE__); sqlite3_close(epg_db); return; //exit(0); } epg_rc = sqlite3_step(epg_stmt); if(epg_rc == SQLITE_ROW) { found = true; fprintf(out,"%s@SEP@%d@SEP@%d@SEP@%s@SEP@%s@SEP@%s@SEP@@NR@\n", name, sqlite3_column_int(epg_stmt,3), /// start sqlite3_column_int(epg_stmt,4), /// duration sqlite3_column_text(epg_stmt,5), sqlite3_column_text(epg_stmt,6), sqlite3_column_text(epg_stmt,7) ); } sqlite3_finalize(epg_stmt); if(found == true) { break; } else { sp=sp->next; } } // sqlite3_close(epg_db); } /*** look for matching substring, escape "'"'s ***/ static void search_epg(FILE *out,char *what) { char find[255]; char sql[512]; //sqlite3 *epg_db; sqlite3_stmt *epg_stmt; int epg_rc; uint16_t tsid,s_id; char *name,*full; SERVICE *sp; time_t now; uint8_t found; now = time(0); found = false; /*epg_rc = sqlite3_open(EPG_DB_NAME,&epg_db); if(epg_rc) { fprintf(stderr, "Can't open database: %s @ %d %s\n", sqlite3_errmsg(epg_db),__LINE__,__FILE__); return; }*/ full = strstr(what,"&full"); if(full != NULL) { *full = '\0'; } escape_hy(what,&find[0],255); if(full != NULL) { sprintf(sql,"select * from epg where name like '%%%s%%' or long_des like '%%%s%%' and start_time >= '%d' group by event_id", find, find, (int)now); } else { sprintf(sql,"select * from epg where name like '%%%s%%' and start_time >= '%d' group by event_id", find, (int)now); } epg_rc = sqlite3_prepare_v2(epg_db, sql, -1, &epg_stmt, 0); if( epg_rc ) { fprintf(stderr, "SQL error: %d : %s @ %s %d\n", rc, sqlite3_errmsg(epg_db),__FILE__,__LINE__); exit(0); } else { do { epg_rc = sqlite3_step(epg_stmt); switch( epg_rc ) { case SQLITE_DONE: break; case SQLITE_ROW: tsid = sqlite3_column_int(epg_stmt, 0); s_id = sqlite3_column_int(epg_stmt, 1); sp = services; name = NULL; while(sp) { if((sp->tsid == tsid) && (sp->s_id == s_id)) { name = &sp->name[0]; break; } sp = sp->next; } fprintf(out,"%s@SEP@%d@SEP@%d@SEP@%s@SEP@%s@SEP@%s@SEP@@NR@\n", name != NULL ? name : "???", sqlite3_column_int(epg_stmt, 3), sqlite3_column_int(epg_stmt, 4), sqlite3_column_text(epg_stmt,5), sqlite3_column_text(epg_stmt,6), sqlite3_column_text(epg_stmt,7) ); found = true; break; default: fprintf(stderr, "Error: %d : %s\n", rc, sqlite3_errmsg(epg_db)); break; } } while( epg_rc==SQLITE_ROW ); sqlite3_finalize(epg_stmt); } if(found == false) { fprintf(out,"Nothing found for >%s<\n",find); } //sqlite3_close(epg_db); } /** send out epg data for requested channel/time let the other end (php) do the prettyprinting.... request may look like: ZDF&start=12345678 || ZDF&start=now **/ static void send_epg(FILE *out,char *inreq) { char *p; time_t start; char sql[1024]; char req[255]; //sqlite3 *epg_db; sqlite3_stmt *epg_stmt; int epg_rc; uint16_t tsid,s_id; time_t start_time,stop_time; uint16_t duration,event_id; const unsigned char *name,*short_des,*long_des; char *stop; uint8_t found; SERVICE *sp; stop_time = 0; escape_hy(inreq,req,255); /* epg_rc = sqlite3_open(EPG_DB_NAME,&epg_db); if(epg_rc) { fprintf(stderr, "Can't open database: %s @ %d %s\n", sqlite3_errmsg(epg_db),__LINE__,__FILE__); return; }*/ p = strstr(req,"&start="); stop = strstr(req,"&stop="); if(stop != NULL) { *stop = '\0'; stop = stop + 6; stop_time = atol(stop); } if(p != NULL) { if(!strncmp(p+7,"now",3)) { start = time(0); } else { start = atol(p+7); /// check if there's a \n at end ??? } } else start = 0; *p = '\0'; /// request is servicename now sp = services; found = false; while(sp) { while(sp) { if(!strcmp(sp->name,req)) { tsid = sp->tsid; s_id = sp->s_id; break; } sp = sp->next; } if(sp == NULL) { fprintf(out,"No events found for >%s<\n",req); return; } if(stop_time == 0) { sprintf(sql,"select event_id,start_time,duration,name,short_des,long_des from epg where tsid='%d' and s_id='%d' and start_time >= '%d' order by start_time", tsid, s_id, (int)start); } else { sprintf(sql,"select event_id,start_time,duration,name,short_des,long_des from epg where tsid='%d' and s_id='%d' and start_time >= '%d' and start_time <= '%d' order by start_time", tsid, s_id, (int)start, (int)stop_time); } epg_rc = sqlite3_prepare_v2(epg_db, sql, -1, &epg_stmt, 0); if( epg_rc ) { fprintf(stderr, "SQL error: %d : %s @ %s %d\n", epg_rc, sqlite3_errmsg(epg_db),__FILE__,__LINE__); exit(0); } else { do { epg_rc = sqlite3_step(epg_stmt); switch( epg_rc ) { case SQLITE_DONE: break; case SQLITE_ROW: event_id = sqlite3_column_int(epg_stmt,0); start_time = sqlite3_column_int(epg_stmt,1); duration = sqlite3_column_int(epg_stmt,2); name = sqlite3_column_text(epg_stmt,3); short_des = sqlite3_column_text(epg_stmt,4); long_des = sqlite3_column_text(epg_stmt,5); fprintf(out,"%s@SEP@%d@SEP@%d@SEP@%d@SEP@%s@SEP@%s@SEP@%s@SEP@@NR@\n", req, event_id, (int)start_time, duration, name, short_des, long_des); found = true; break; default: fprintf(stderr, "Error: %d : %s\n", epg_rc, sqlite3_errmsg(epg_db)); break; } } while( epg_rc==SQLITE_ROW ); sqlite3_finalize(epg_stmt); } if(found == true) { break; } else { sp = sp->next; } } //sqlite3_close(epg_db); } static void handlereq(int fd) { FILE *in,*out; char getstr[255],*rptr,*p; char outbuf[255]; CURL *handle; char *decodedURL; int length; iconv_t ic; size_t inlen,outlen,convlen; //printf("handle http\n"); handle = curl_easy_init(); if(!(in = fdopen(fd,"r"))) { perror("httpd infile"); return; //just forget it } if(!(out = fdopen(fd,"w"))) { perror("httpd outfile\n"); return; } while(1) { if(fgets(getstr,255,in) <= 0) return; if((rptr = strstr(getstr,"GET /"))) break; else { INFO("ignored: ;fd %d; >%s<\n",fd,getstr); } fflush(in); } rptr += 5; if((p = strstr(rptr," HTTP/"))) *p = '\0'; fprintf(out,"HTTP/1.1 200 OK\nContent-Type: text/plain; charset=iso_8859-1\nConnection: close\n\n"); decodedURL = curl_easy_unescape(handle,rptr, strlen(rptr),&length); /// web coding gone inlen = strlen(decodedURL); outlen = 255; bzero(&outbuf,255); p = &outbuf[0]; ic = iconv_open("iso_8859-1","UTF-8"); if(ic == (iconv_t)-1) { perror("iconv open"); return; } convlen = iconv(ic, &decodedURL,&inlen , &p, &outlen); if(convlen == -1) { perror("iconv"); return; } iconv_close(ic); curl_easy_cleanup(handle); /// 'real' string now in outbuf /** since we're not embedded, there's no need to be cryptic */ if(!strcmp(outbuf,"getchannels")) send_chans(out); else if(!strncmp(outbuf,"tuneto=",7)) { if(tuneto(outbuf+7)) fprintf(out,"OK\n"); else fprintf(out,"Not tuned !\n"); } else if(!strncmp(outbuf,"getepg=",7)) { send_epg(out,outbuf+7); } else if(!strncmp(outbuf,"search=",7)) { search_epg(out,outbuf+7); } else if(!strncmp(outbuf,"nownext=",8)) { get_nownext(out,outbuf+8); } else if(!strncmp(outbuf,"record",6)) { sched_rec(out,outbuf+7); } else fprintf(out,"Sorry, don't understand \"%s\"\n",outbuf); fflush(out); fclose(in); fclose(out); close(fd); } void *httpd(void *bla) { sockfd = socket(AF_INET, SOCK_STREAM , 0); if (sockfd < 0) { printf("httpd can't create socket\n"); return NULL ; } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(HTTP_PORT); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { printf("httpd can't bind to port %d\n",HTTP_PORT); return NULL; } listen(sockfd,5); clilen = sizeof(cli_addr); while (1) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t *)&clilen); if (newsockfd < 0) { printf("httpd ERROR on accept\n"); sleep(10); continue; } handlereq(newsockfd); } }