diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | README.md | 39 | ||||
-rwxr-xr-x | rtv4d-dl | bin | 0 -> 31224 bytes | |||
-rw-r--r-- | rtv4d-dl.c | 491 | ||||
-rw-r--r-- | tcp.c | 266 | ||||
-rw-r--r-- | test.c | 22 |
6 files changed, 830 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5de2d4a --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +default: + @echo \ \ \ \ \*\*\* rtv4d-dl Makefile - kratka navodila za namestitev \*\*\* + @echo - če še niste, preberite README.md in se pozanimajte o uporabi + @echo - če še niste, z \`make prepare\` namestite C \(potrebuje sudo in apt\) + @echo - če še niste, z \`make compile\` izdelajte binarne programe + @echo to je vse, hvala. + +prepare: + sudo apt install build-essential gcc + +compile: + gcc rtv4d-dl.c -o rtv4d-dl -Wall -lm -I. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c171023 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# rtv4d-dl: program za prenašanje videoposnetkov iz arhiva TV SLO + +## legalne stvari +z uporabo programa se strinjate z naslednjimi stavki: +* avtorja ne boste tožili in se strinjate, da je program povsem legalen. +* program uporabljate na lastno odgovornost. +* če ni kje drugje avtor napisal drugače ali če to ni zakonsko drugače: + - ne smete distribuirati programa in izdelovali kopij. + - smete ga uporabljati samo za zasebno nekomercialno uporabo. + +## funkcije +* prenašanje videoposnetkov iz arhiva kot odklenjene videodatoteke + +## možnosti uporabe programa +* prenos oddaje: `rtv4d-dl oddaja <URL/ID oddaje> [izhodna datoteka]` + - primer: `rtv4d-dl oddaja 4d.rtvslo.si/arhiv/vreme/89614963` + - primer: `rtv4d-dl oddaja 89614963 89614963.mp4` +* prenos metapodatkov oddaje: `rtv4d-dl meta-oddaja <URL> [datoteka]` + - primer: `rtv4d-dl meta-oddaja 89614963 89614963.txt` + - opomba: nekateri metapodatki veljajo omejeno časa (video URL) +* prenos sličice oddaje: `rtv4d-dl slicica-oddaja <URL> [datoteka]` + - primer: `rtv4d-dl slicica-oddaja 89614963 89614963.jpg` + +# dodatne informacije: +* pisanje v STDOUT: kot pot datoteke napišite `/dev/stdout` (samo POSIX) +* program se poveže na 4D strežnik 4d.rtvslo.si, spremenite izv. kodo za drugega + +# trenutna izdaja programa: +* program je bil nazadnje ročno testiran 9. decembra 2020 in takrat je DELOVAL. +* različica: 0.0.0 + +# o +* program je spisan 100% v C programskem jeziku +* ne potrebuje nobenih knižnjic, razen seveda `stdio.h` in `stdlib.h` (še) +* navodila za grajenje (na Debian): + - `make` + +# še za narediti +* dodati podporo za varno povezavo, sedaj gre vsa komunikacija z RTV4D kot HTTP. diff --git a/rtv4d-dl b/rtv4d-dl Binary files differnew file mode 100755 index 0000000..67d1b41 --- /dev/null +++ b/rtv4d-dl diff --git a/rtv4d-dl.c b/rtv4d-dl.c new file mode 100644 index 0000000..aab20a6 --- /dev/null +++ b/rtv4d-dl.c @@ -0,0 +1,491 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <tcp.c> +#define NIZ_DODATEK(a) #a +#define NIZ(a) NIZ_DODATEK(a) +#define RTV_CLIENT_ID "82013fb3a531d5414f478747c1aca622" /* enak za vse */ +#define RTV_CLIENT_ID_SIZEOF 32+1 +#define RTV_VIDEO_TIP 1 +#define RTV_AUDIO_TIP 2 +#define RTV_NEPOZNAN_TIP 0 +#define RTV_NACIN argv[1] +#define RTV_URL argv[2] +#define RTV_P_SIZEOF 128 /* privzeti sizeof */ +#define RTV_TEST_NASLOV "Vreme ob 22h" +#define RTV_TEST_ZANRI "\"INFORMATIVNE VSEBINE\"," \ + "\"DNEVNO INFORMATIVNE ODDAJE\",\"Vremenska poročila\"" +#define RTV_TEST_OPIS "Vreme je na sporedu vsak dan po Poročilih, pred in po" \ + " Dnevniku ter po Odmevih. Napoved vremena pa vas čaka tudi vsak delavnik" \ + " zjutraj v rubriki oddaje Dobro jutro. \\r\\n" +#define RTV_TEST_ID 89614963 +#define RTV_TEST_DOLZINA 155 +#define RTV_TEST_TIP_ODDAJE_IME "Vreme" +#define RTV_TEST_TIP_ODDAJE_ID 608 +#define RTV_TEST_PROGRAM "SLO1" +#define RTV_TEST_OBJAVLJENO "2010-12-03 23:20:10" +#define RTV_TEST_PREDVAJANO "2010-12-03 23:20:10" +#define RTV_TEST_TIP_POSNETKA 4D_VIDEO_TIP +#define RTV_TEST_SLICICA "http://img.rtvslo.si/_up/ava/ava_misc/show_logos" \ + "/VREME_VECERNO-980.jpg" +#define RTV_TEST_KONTROLNA_VREDNOST 1186735842 +#define RTV_TEST_VELIKOST 9396765 +#define RTV_NAPISI_NAPAKA 1 +#define RTV_NAPISI_INFO 1 +#define RTV_NAPISI_OPOZORILO 1 +#define RTV_NAPISI_HROSC 1 +#define RTV_NAPISI(kaj, frmt, ...) /* pazi na format string RCE! */ \ + do { if ( RTV_NAPISI_ ##kaj ) fprintf(stderr, \ + "[" #kaj "] %s@%s:" NIZ(__LINE__) " " frmt "\n", \ + __func__, __FILE__, ##__VA_ARGS__ ); } while(0); +#define RTV_USER_AGENT "Mozilla/5.0 equivalent (rtv4d-dl; C " \ + NIZ(__STDC_VERSION__) " GCC " __VERSION__ "; " __DATE__ " " __TIME__ "; " \ + __FILE__ ":" NIZ(__LINE__) ")" +#define RTV_HTTP_GET \ + "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: " RTV_USER_AGENT \ + "\r\nAccept: */*\r\nX-Requested-With: tcp.c (" __FILE__ ":" NIZ(__LINE__) \ + ")\r\nConnection: close\r\n\r\n" +#define RTV_HTTP_TIMEOUT 69 /* sekund */ +#define RTV_NE_BO_POSLAL "ToJeMogočeBackdoor!" +#define RTV_API_META_URL "http://api.rtvslo.si/ava/getRecordingDrm/" \ + "%u?client_id=" RTV_CLIENT_ID +#define RTV_API_META_URL_SIZEOF 128 /* vključno z nul znakom */ +#define RTV_API_MEDIA_URL "http://api.rtvslo.si/ava/getMedia/%u/?client_id=" \ + RTV_CLIENT_ID "&jwt=%s" +#define RTV_API_MEDIA_URL_SIZEOF 64 + RTV_CLIENT_ID_SIZEOF + RTV_JWT_SIZEOF +#define RTV_JWT_SIZEOF 43+1 +#define RTV_PREDVAJALNIK_URL "http://4d.rtvslo.si/arhiv/v/%u" +#define RTV_PREDVAJALNIK_URL_SIZEOF 32+1 + 12 + +struct meta_oddaja { + size_t naslov_sizeof; + char * naslov; /* Vreme ob 22h */ + unsigned int tip_oddaje_id; /* 608 */ + size_t zanri_sizeof; + char * zanri; /* INFORMATIVNE VSEBINE, DNEVNO INFORMATIVNE ODDAJE, Vrem... */ + size_t opis_sizeof; + char * opis; /* Vreme je na sporedu vsak dan po Poročilih, pred in po Dn... */ + unsigned int id; /* 89614963 */ + unsigned int dolzina; /* 155 */ + size_t jwt_sizeof; /* 44 */ + char * jwt; /* 1lKYcP7j8iNwqVQeQC8htsrBN47LpiHq3sm5bFlxRys */ + size_t tip_oddaje_ime_sizeof; + char * tip_oddaje_ime; /* Vreme */ + size_t program_sizeof; + char * program; /* SLO1 */ + size_t objavljeno_sizeof; + char * objavljeno; /* 2010-12-03 23:20:10 */ + unsigned short int tip_posnetka; /* RTV_VIDEO_TIP */ + size_t slicica_sizeof; + char * slicica; /* http://img.rtvslo.si/_up/ava/ava_misc/show_logos/VREM... */ + size_t predvajano_sizeof; + char * predvajano; /* 2010-12-03 23:20:10 */ + /* unsigned long long int kontrolna_vrednost; */ + unsigned long long int velikost; /* število bajtov posnetka */ + char get_meta_url[RTV_API_META_URL_SIZEOF]; /* http://api.rtvslo.si/ava/... */ + char get_media_url[RTV_API_MEDIA_URL_SIZEOF]; /* http://api.rtvslo.si/av... */ + char predvajalnik_url[RTV_PREDVAJALNIK_URL_SIZEOF]; /* http://4d.rtvslo.... */ + size_t posnetek_url_sizeof; + char * posnetek_url; /* http://progressive.rtvslo.si/encrypted00/2010/12... */ +}; +struct meta_oddaja * meta_oddaja_alloc () { + struct meta_oddaja * m = malloc(sizeof(struct meta_oddaja)); + m->naslov = malloc(sizeof(char)*RTV_P_SIZEOF);m->naslov_sizeof = RTV_P_SIZEOF; + m->zanri = malloc(sizeof(char)*RTV_P_SIZEOF); m->zanri_sizeof = RTV_P_SIZEOF; + m->opis = malloc(sizeof(char)*RTV_P_SIZEOF); m->opis_sizeof = RTV_P_SIZEOF; + m->tip_oddaje_ime=malloc(sizeof(char)*RTV_P_SIZEOF); + m->tip_oddaje_ime_sizeof = RTV_P_SIZEOF; + m->program = malloc(sizeof(char)*RTV_P_SIZEOF);m->program_sizeof=RTV_P_SIZEOF; + m->slicica = malloc(sizeof(char)*RTV_P_SIZEOF);m->slicica_sizeof=RTV_P_SIZEOF; + m->jwt = malloc(sizeof(char)*RTV_P_SIZEOF); m->jwt_sizeof=RTV_P_SIZEOF; + m->objavljeno = malloc(sizeof(char)*RTV_P_SIZEOF); + m->objavljeno_sizeof = RTV_P_SIZEOF; + m->predvajano = malloc(sizeof(char)*RTV_P_SIZEOF); + m->predvajano_sizeof = RTV_P_SIZEOF; + m->posnetek_url = malloc(sizeof(char)*RTV_P_SIZEOF); + m->posnetek_url_sizeof = RTV_P_SIZEOF; + return m; +} +int meta_oddaja_free (struct meta_oddaja * m) { + free(m->naslov); m->naslov = NULL; + free(m->zanri); m->zanri = NULL; + free(m->opis); m->opis = NULL; + free(m->tip_oddaje_ime); m->tip_oddaje_ime = NULL; + free(m->program); m->program = NULL; + free(m->slicica); m->slicica = NULL; + free(m->jwt); m->jwt = NULL; + free(m->objavljeno); m->objavljeno = NULL; + free(m->predvajano); m->predvajano = NULL; + free(m->posnetek_url); m->posnetek_url = NULL; + free(m); m = NULL; + return 0; +} +int niz_realloc (char * s, size_t * v) { + *v = (*v)*2; + s = realloc(s, sizeof(char)*(*v)); + return 0; +} +int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ + unsigned int p = 80; /* HTTP port ^~~~~~~~ glej man open_memstream, fmemopen*/ + char * k; /* označuje konec imena gostitelja */ + char t; /* začasna shramba za končni znak, ko je le-ta null */ + int c = -42069; /* connection descriptor */ + int ret; /* izhodni status write funkcij */ + char * path; /* kaže na path requesta */ + int returnstatus = 0; + char * header; /* za headerje */ + FILE * headerstream; + size_t header_sizeof = 0; + long long int contentlength = -69420; + char * request_buf; /* za request buffer */ +#define RTV_URL_HOST (u+7) + if (u == NULL) { + RTV_NAPISI(NAPAKA, "URL je null!"); + return 1; + } + if (strncmp(u, "http://", 7) != 0) { + RTV_NAPISI(NAPAKA, "URL protokol ni HTTP oziroma URL ni pravilen"); + return 2; + } + k = strchr(RTV_URL_HOST, '/'); + if (k - strchrnul(RTV_URL_HOST, ':') > 0) { /* pred pathom je PORT */ + k = strchrnul(RTV_URL_HOST, ':'); + p = atoi(k+1); + } + if (p > 65535) { /* port je nepodpisan, če gre v minus bo itak ZELO > 65535 */ + RTV_NAPISI(NAPAKA, "port je neveljaven"); + return 3; + } + if (k == NULL) + k = strchrnul(RTV_URL_HOST, '/'); + path = strchr(RTV_URL_HOST, '/'); + if (path == NULL) { + u[6] = '\0'; /* na koncu popravimo nazaj, izkoristimo ta memory xD */ + path = u+5; + } else { + path++; + } + t = *k; + *k = '\0'; + RTV_NAPISI(INFO, "Vzpostavljam TCP povezavo na http://%s:%d ...", + RTV_URL_HOST, p); + c = spawn_conn(RTV_URL_HOST, p); + if (c < 0) { + RTV_NAPISI(NAPAKA, "TCP povezava ni uspela!"); + returnstatus = 4; + goto http_get_returncleanly; + } else { + RTV_NAPISI(INFO, "Povezan na strežnik."); + } + request_buf = malloc (sizeof(char) * ( sizeof(RTV_HTTP_GET) + + strlen(RTV_URL_HOST)+ strlen(path) + 1 )); + sprintf(request_buf, RTV_HTTP_GET, path, RTV_URL_HOST); /* =dovolj prostora */ + RTV_NAPISI(INFO, "Pošiljam %lu bajtov ...", strlen(request_buf)); + ret = sync_write(c, request_buf, strlen(request_buf), RTV_HTTP_TIMEOUT); + if (ret != 0) { + returnstatus = 5; + RTV_NAPISI(NAPAKA, "TCP časovna omejitev pri pošiljanju je potekla"); + goto http_get_returncleanly; + } else { + RTV_NAPISI(INFO, "Uspešno poslano."); + } + headerstream = open_memstream(&header, &header_sizeof); + while (1) { + rewind(headerstream); + ret = read_until(c, headerstream, RTV_HTTP_TIMEOUT, "\n", -1); /* unsig!*/ + fflush(headerstream); + if (ret != 0) { + returnstatus = 6; + RTV_NAPISI(NAPAKA, "Branje headerja ni uspelo!"); + goto http_get_returncleanly; + } + if (strncasecmp("Content-Length: ", header, strlen("Content-Length: ")) + == 0) { + contentlength = strtoll(header+16, NULL, 10); + RTV_NAPISI(INFO, "Pridobil %lld bajtni odgovor.", contentlength); + } + if (strncmp("\r\n", header, 2) == 0 || header[0] == '\n') + break; + } + if (contentlength == -69420) { + RTV_NAPISI(OPOZORILO, "Manjka Content-Length, berem do konca."); + } else { + RTV_NAPISI(INFO, "Začetek telesa odgovora. Berem in pišem."); + } + fclose(headerstream); + ret = read_until(c, r, RTV_HTTP_TIMEOUT, RTV_NE_BO_POSLAL, contentlength); + if (ret != 0) { + returnstatus = 7; + RTV_NAPISI(NAPAKA, "Branje in pisanje telesa odgovora ni uspelo!"); + goto http_get_returncleanly; + } + RTV_NAPISI(INFO, "Prejel in uspešno shranil/prebral HTTP odgovor."); + http_get_returncleanly: + free(request_buf); + request_buf = NULL; + free(header); + header = NULL; + if(c != -69420 && kill_conn(c) < 0) /* kill_conn se ne izvede, če prvi ni 1.*/ + RTV_NAPISI(OPOZORILO, "TCP povezave ni uspelo prekiniti"); + *k = t; + u[6] = '/'; + return returnstatus; +} +int rtv_meta_izpolni(struct meta_oddaja * m) { + int returnstatus = 0; + FILE * odgstream; + char * odg; + size_t sizeloc; + size_t i, j; + char * metakeys[] = {"\"title\"", "\"showId\"", "\"genre\"", \ + "\"showDescription\"", "\"duration\"", "\"jwt\"", "\"showName\"", \ + "\"source\"", "\"publishDate\"", "\"mediaType\"", "\"orig\"", \ + "\"broadcastDate\"", /* sedaj pa za getMedia query */ "\"http\"" }; + char * cp; +#define RTV_META_IZPOLNI_METAKEYS_SIZEOF 12 /* hkrati offset za getMedia */ +#define RTV_META_IZPOLNI_METAKEYS_GETMEDIA_FINAL \ + (RTV_META_IZPOLNI_METAKEYS_SIZEOF)+1 /* zadnji+1 za getMedia */ +#define RTV_META_IZPOLNI_VALUE (odg+i+strlen(metakeys[j])+2) +#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE + snprintf(m->get_meta_url, RTV_API_META_URL_SIZEOF, RTV_API_META_URL, m->id); + snprintf(m->predvajalnik_url, RTV_PREDVAJALNIK_URL_SIZEOF, RTV_PREDVAJALNIK_URL, m->id); + odgstream = open_memstream(&odg, &sizeloc); + returnstatus = http_get(m->get_meta_url, odgstream); /* shranimo metapodat. */ + if (returnstatus != 0) { + RTV_NAPISI(NAPAKA, "Zahteva za metapodatke iz API strežnika je spodletela"); + goto rtv_meta_izpolni_returncleanly; + } + fflush(odgstream); + for (i = 0; i < sizeloc; i++) { + for (j = 0; j < RTV_META_IZPOLNI_METAKEYS_SIZEOF; j++) { + if (strncmp(odg+i, metakeys[j], strlen(metakeys[j])) == 0) { + switch (j) { + case 0: /* title */ +#define strtollu strtoull +#define strtolu strtoul +#define strtolld strtoll +#define strtold strtol +#define strtod strtol +#define strtou strtoul +#define RTV_META_IZPOLNI_V(imem, format, tip) /* za variabilne nize */ \ + cp[0] = '\0'; /* omejimo json string, ne potrebujemo strncpy */ \ + m->imem = realloc(m->imem, sizeof(tip) * \ + strlen(RTV_META_IZPOLNI_VALUE_INTERNAL)+1); \ + m->imem##_sizeof = strlen(RTV_META_IZPOLNI_VALUE_INTERNAL)+1; \ + strcpy(m->imem, RTV_META_IZPOLNI_VALUE_INTERNAL); +#define RTV_META_IZPOLNI_I(imem, format, tip) /* za številke */ \ + m->imem = strto##format(RTV_META_IZPOLNI_VALUE_INTERNAL, NULL, 10); +#define RTV_META_IZPOLNI(imem, oblika, format, splitter, tip) \ + cp = strchr(RTV_META_IZPOLNI_VALUE_INTERNAL, splitter); \ + if (cp == NULL) { \ + RTV_NAPISI(OPOZORILO, "Napaka pri iskanju podatka " #imem); \ + strcpy(m->imem, "Napaka pri pridobivanju."); \ + } else { \ + oblika(imem, format, tip) \ + RTV_NAPISI(HROSC, "Shranil metapodatek " #imem ": %" #format, \ + m->imem); \ + } +#pragma GCC diagnostic ignored "-Wint-conversion" /* macro dela strcpy na str */ + RTV_META_IZPOLNI(naslov, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 1: /* showId */ + RTV_META_IZPOLNI(tip_oddaje_id, RTV_META_IZPOLNI_I, u, '"', unsigned int); + break; + case 2: /* genre je "asdasd","asdasd","sdfsfd" */ + RTV_META_IZPOLNI(zanri, RTV_META_IZPOLNI_V, s, ']', char); + memmove(m->zanri, (m->zanri)+1, strlen(m->zanri)+1); + break; + case 3: /* showDescription */ + RTV_META_IZPOLNI(opis, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 4: /* duration */ +#undef RTV_META_IZPOLNI_VALUE_INTERNAL +#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE-1 + RTV_META_IZPOLNI(dolzina, RTV_META_IZPOLNI_I, u, '"', unsigned int); +#undef RTV_META_IZPOLNI_VALUE_INTERNAL +#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE + break; + case 5: /* jwt */ + RTV_META_IZPOLNI(jwt, RTV_META_IZPOLNI_V, s, '"', char); + if (RTV_JWT_SIZEOF != m->jwt_sizeof) { + RTV_NAPISI(OPOZORILO, "Shranil nepričakovano dolg JWT! Je vdor?"); + } + break; + case 6: /* showName */ + RTV_META_IZPOLNI(tip_oddaje_ime, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 7: /* source */ + RTV_META_IZPOLNI(program, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 8: /* publishDate */ + RTV_META_IZPOLNI(objavljeno, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 9: /* mediaType */ + if (RTV_META_IZPOLNI_VALUE[0] == 'v') { + m->tip_posnetka = RTV_VIDEO_TIP; + } else { + m->tip_posnetka = RTV_NEPOZNAN_TIP; + } + break; + case 10: /* orig */ /* sličica */ + RTV_META_IZPOLNI(slicica, RTV_META_IZPOLNI_V, s, '"', char); + break; + case 11: /* broadcastDate */ + RTV_META_IZPOLNI(predvajano, RTV_META_IZPOLNI_V, s, '"', char); + break; +#pragma GCC diagnostic warning "-Wint-conversion" /* smo prej izključili */ + default: + RTV_NAPISI(OPOZORILO, "Doseg nedefinirane kode!"); + } + } + } + } + snprintf(m->get_media_url, RTV_API_MEDIA_URL_SIZEOF, RTV_API_MEDIA_URL, m->id, m->jwt); + rewind(odgstream); + fflush(odgstream); + returnstatus = http_get(m->get_media_url, odgstream); /* shranimo video url */ + if (returnstatus != 0) { + RTV_NAPISI(NAPAKA, "Zahteva za ključ videa iz API strežnika je spodletela"); + goto rtv_meta_izpolni_returncleanly; + } + fflush(odgstream); + for (i = 0; i < ftell(odgstream); i++) { + for (j = RTV_META_IZPOLNI_METAKEYS_SIZEOF; + j < RTV_META_IZPOLNI_METAKEYS_GETMEDIA_FINAL; j++) { + if (strncmp(odg+i, metakeys[j], strlen(metakeys[j])) == 0) { + switch (j-RTV_META_IZPOLNI_METAKEYS_SIZEOF) { + case 0: /* http */ /* videofile */ + RTV_META_IZPOLNI(posnetek_url, RTV_META_IZPOLNI_V, s, '"', char); + break; + default: + RTV_NAPISI(OPOZORILO, "Doseg nedefinirane kode!"); + } + } + } + } + + rtv_meta_izpolni_returncleanly: + fclose(odgstream); + free(odg); + odg = NULL; + return returnstatus; +} +int main (int argc, char ** argv) { + if (argc < 1+1) { + fprintf(stderr, "preberi README.md pred uporabo programa, saj vsebuje navodila in ostalo.\n"); + return 1; + } + struct meta_oddaja * m = meta_oddaja_alloc(); + char fn[69]; /* <id>.txt / <id>.mp4 - NE USER INPUT */ + char * e; /* char pointer for the memes */ + FILE * fd; + unsigned short int returnstatus = 0; + if (RTV_URL != NULL) { + m->id = atoi(RTV_URL); + if (strrchr(RTV_URL, '/') != NULL) + m->id = atoi(strrchr(RTV_URL, '/')+1); + if (m->id <= 0) { + fprintf(stderr, "IDja oddaje ni uspelo dobiti. preverite vnos.\n"); + returnstatus = 3; + goto returncleanly; + } + } else { + m->id = RTV_TEST_ID; + } + switch (RTV_NACIN[0]) { + case 't': /* test - program se preizkusi brez prenosa datotek */ + RTV_NAPISI(NAPAKA, "samotestiranje še ni izdelano!"); + break; + case 'o': /* oddaja - prenese oddajo v datoteko */ + if (rtv_meta_izpolni(m) != 0) { + RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje."); + returnstatus = 4; + goto returncleanly; + } + if (argc < 4) { + e = strrchr(m->posnetek_url, '.'); + snprintf(fn, 68, "%u%.4s", m->id, e); + fd = fopen(fn, "w"); + } else { + fd = fopen(argv[3], "w"); + } + if (fd == NULL) { + RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo."); + returnstatus = 5; + goto returncleanly; + } + if (http_get(m->posnetek_url, fd) == 0) { + RTV_NAPISI(INFO, "uspešno shranjeno."); + } else { + RTV_NAPISI(NAPAKA, "Nekaj je spodletelo pri http_get()"); + returnstatus = 6; + } + break; + case 'm': /* meta-oddaja - prenese metapodatke o oddaji */ + if (rtv_meta_izpolni(m) != 0) { + RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje."); + returnstatus = 4; + goto returncleanly; + } + if (argc < 4) { + /* e = strrchr(m->posnetek_url, '.'); */ + snprintf(fn, 68, "%u%s", m->id, ".txt"); + fd = fopen(fn, "w"); + } else { + fd = fopen(argv[3], "w"); + } + if (fd == NULL) { + RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo."); + returnstatus = 5; + goto returncleanly; + } + fprintf(fd, "id: %u\nnaslov: %s\ntip_oddaje_id: %u\nzanri: %s\nopis: %s\n" + "dolzina: %u\njwt: %s\ntip_oddaje_ime: %s\nprogram: %s\n" + "objavljeno: %s\ntip_posnetka: %s\nslicica: %s\npredvajano: %s\n" + "velikost: %llu\nget_meta_url: %s\nget_media_url: %s\n" + "predvajalnik_url: %s\nposnetek_url: %s\n", m->id, m->naslov, + m->tip_oddaje_id, m->zanri, m->opis, m->dolzina, m->jwt, + m->tip_oddaje_ime, m->program, m->objavljeno, + m->tip_posnetka == RTV_VIDEO_TIP ? "RTV_VIDEO_TIP":"RTV_NEPOZNAN_TIP", + m->slicica, m->predvajano, m->velikost, m->get_meta_url, + m->get_media_url, m->predvajalnik_url, m->posnetek_url); + fclose(fd); + break; + case 's': /* sličica - prenese sličico oddaje */ + if (rtv_meta_izpolni(m) != 0) { + RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje."); + returnstatus = 4; + goto returncleanly; + } + if (argc < 4) { + e = strrchr(m->slicica, '.'); + snprintf(fn, 68, "%u%.4s", m->id, e); + fd = fopen(fn, "w"); + } else { + fd = fopen(argv[3], "w"); + } + if (fd == NULL) { + RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo."); + returnstatus = 5; + goto returncleanly; + } + if (http_get(m->slicica, fd) == 0) { + RTV_NAPISI(INFO, "uspešno shranjeno."); + } else { + RTV_NAPISI(NAPAKA, "Nekaj je spodletelo pri http_get()"); + returnstatus = 6; + } + break; + default: + fprintf(stderr, "opcija ne obstaja! poskusi test.\n"); + returnstatus = 2; + goto returncleanly; + break; + } + returncleanly: + meta_oddaja_free(m); + m = NULL; + return returnstatus; +} @@ -0,0 +1,266 @@ +#pragma once +#include <arpa/inet.h> +#include <sys/socket.h> +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include <sys/select.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <string.h> +#include <sys/time.h> +#define ERR_INET_ADDR "0.9.9.0" +union ip_conv { + unsigned char c[4]; + struct in_addr in; +}; +struct in_addr hostname_to_ip (const char * hostname) { + struct hostent *he; /* STATIC! */ + union ip_conv ipconverter; + struct in_addr *error_addr = malloc(sizeof(struct in_addr)); + /* int ret = */ inet_aton(ERR_INET_ADDR, error_addr); /* TODO: chek for err */ + if ( (he = gethostbyname(hostname)) == NULL ) { + herror("gethostbyname"); + return *error_addr; + } else { + memcpy(ipconverter.c, he->h_addr, 4); // fuck yes now it works + // fprintf(stderr, "debug: %s\n", inet_ntoa(ipconverter.in)); + return ipconverter.in; + } +} +int spawn_conn (const char * address, const int port) { + int ret; + int conn_fd; + struct sockaddr_in server_addr = { 0 }; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + ret = inet_pton(AF_INET, address, &server_addr.sin_addr); + if (ret != 1) { + if (ret == -1) + perror("inet_pton"); + fprintf(stderr, "%s is not an IPv4, trying to resolve ...\n", address); + struct in_addr ret = hostname_to_ip(address); + struct in_addr error_addr; + inet_aton(ERR_INET_ADDR, &error_addr); + if (memcmp(&ret, &error_addr, 4) == 0) { + fprintf(stderr, "failed to resolve.\n"); + return -1; + } + server_addr.sin_addr = ret; + fprintf(stderr, "resolved to %s.\n", inet_ntoa(server_addr.sin_addr)); + } + conn_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + if(conn_fd == -1) { + perror("socket"); + return -1; + } + ret = connect(conn_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); + if (ret == 1) { + perror("connect"); + return -1; + } + return conn_fd; +} + +int kill_conn (int conn_fd) { + int ret = shutdown(conn_fd, SHUT_RDWR); // preprečimo tako read kot write.:wq + if (ret == -1) { + perror("shutdown"); + return -1; + } + ret = close(conn_fd); + if (ret == -1) { + perror("close"); + return -1; + } + return 0; +} + +int read_until(int conn_fd, FILE * out, unsigned int timeout, const char * ma, + unsigned long long int max_bytes) { + int ret = 0; + unsigned int match = 0; + struct timeval start, stop; + gettimeofday(&start, NULL); + char c[2] = {'\0', '\0'}; + while (1) { + ret = read(conn_fd, c, 1); + if (ret == -1) { + if (errno == EWOULDBLOCK) { + } else { + fprintf(stderr, "%s@" __FILE__ ":%d read(): %s%d\n", __func__, __LINE__, strerror(errno), ret); + return 1; + } + } else if (ret == 0) { /* strežnik ni poslal ničesar */ + fprintf(stderr, "%s@" __FILE__ ":%d read(): server closed connection\n", __func__, __LINE__); + return 0; + } else { + fputc(c[0], out); + max_bytes--; + if (max_bytes <= 0) { + return 0; + } + if (ma[match] == c[0]) { + match++; + if (match == strlen(ma)) { + return 0; + } + } else { + match = 0; + } + } + gettimeofday(&stop, NULL); + if (stop.tv_sec - start.tv_sec > timeout) { + fprintf(stderr, "%s@" __FILE__ ":%d E_TIMEOUT %ld-%ld>%u\n", __func__, + __LINE__, stop.tv_sec, start.tv_sec, timeout); + return 2; + } + } + return 0; +} + +int sync_write(int conn_fd, const char * req, int len, unsigned int timeout) { + int ret = write(conn_fd, req, len); + struct timeval start, stop; + gettimeofday(&start, NULL); + if (ret == -1) { + if (errno == EBADF) { + fprintf(stderr, "tcp.c: sync_write: write EBADF: %s\n", strerror(errno)); + return -1; + } + while (errno == EWOULDBLOCK) { + ret = write(conn_fd, req, len); + if(ret != -1) { + return 0; + } + gettimeofday(&stop, NULL); + if (stop.tv_sec - start.tv_sec > timeout) { + fprintf(stderr, "tcp.c: sync_write: E_TIMEOUT %ld-%ld>%u\n", + start.tv_sec, stop.tv_sec, timeout); + return -2; + } + } + } + return 0; +} + + +#if __INCLUDE_LEVEL__ == 0 + static volatile int sigint = 0; + void intHandler(int sig) { + signal(sig, SIG_IGN); + if (sig == SIGINT) + sigint++; + if (sigint >= 3) // some people smash CtrlC multiple times to force quit! + exit(130); + signal(sig, intHandler); + } + int main (int argc, char ** argv) { + if (argc != 1+2) { + fprintf(stderr, "usage: %s ip.v4.ad.dr port\n", argv[0]); + return 1; + } + signal(SIGINT, intHandler); + #define ADDRESS_ARG argv[1] + #define PORT_ARG argv[2] + int conn_fd = spawn_conn(ADDRESS_ARG, atoi(PORT_ARG)); + if (conn_fd < 0) { + fprintf(stderr, "error connecting!\n"); + return 2; + } else { + fprintf(stderr, "suc. conn with fd %d\n\n", conn_fd); + } + int buf = 0; + #define READ_MAX_SIZE 1024 + char read_buf[READ_MAX_SIZE]; + int i = 0; + char input; + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK); + while (1) { + if (sigint > 0) { + // fprintf(stderr, "\n" USERSTRING_EXIT_CONFIRM " (y/N)\n"); + // char gotchar = getchar(); + // if (gotchar == 'y' || gotchar == 'Y' || gotchar == 'd' || + // gotchar == 'D' || gotchar == 'j' || gotchar == 'J') { + // kill_conn(conn_fd); + // return 0; + // } + if (kill_conn(conn_fd) == 0) { + fprintf(stderr, "\nconnection killed successfully, exiting ...\n"); + } else { + fprintf(stderr, "\nconnection killing FAILED, exiting ...\n"); + } + return 0; + } + buf = read(conn_fd, read_buf, READ_MAX_SIZE); + if (buf == -1 && errno != EWOULDBLOCK) { + fprintf(stderr, "\nerror reading from socket.\n"); + if (kill_conn(conn_fd) != 0) { + fprintf(stderr, "\nerror killing socket\n"); + return 3; + } + return 4; + } + if (buf == 0) { + fprintf(stderr, "\nserver closed socket\n"); + if (kill_conn(conn_fd) != 0) { + fprintf(stderr, "\nerror killing socket\n"); + return 5; + } + return 6; + } + if (errno == EWOULDBLOCK && buf == -1) { + // no data is on socket; sockets are non blocking. + } else { + buf = write(STDOUT_FILENO, read_buf, buf); + if(buf == -1) { + fprintf(stderr, "\nerror writing to stdout\n"); + return 7; + } + } + buf = read(STDIN_FILENO, read_buf, READ_MAX_SIZE); + if (buf == -1 && errno != EWOULDBLOCK) { + fprintf(stderr, "\nerror reading from stdin\n"); + return 8; + } + if (buf == 0) { + // fprintf(stderr, "\neof on stdin\n"); // that's okay + // return 9; + } + if (errno == EWOULDBLOCK && buf == -1) { + // no data in stdin + } else { + i = write(conn_fd, read_buf, buf); + if (i == -1) { + if(errno != EWOULDBLOCK) { + fprintf(stderr, "\nerror writing to socket\n"); + if(kill_conn(conn_fd) != 0) { + fprintf(stderr, "\nerror killing connection\n"); + return 10; + } + return 12; + } else { + while (i == -1) { + if (errno == EWOULDBLOCK) { + fprintf(stderr, "\nwrite to socket blocked, trying again\n"); + i = write (conn_fd, read_buf, buf); + } else { + fprintf(stderr, "\nerror writing to socket\n"); + if (kill_conn(conn_fd) != 0) { + fprintf(stderr, "\nerror killing connection\n"); + return 13; + } + return 14; + } + } + } + } + } + buf = 0; + } + } +#endif @@ -0,0 +1,22 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +int main (int argc, char ** argv) { + char * metavalues[] = { "abc", "def", "ghi", "jkl" }; + fprintf(stdout, "%s\n", { "a", "b", "c", "d" }[3]); + // const char b[5] = "aaaaa"; + // const char a[5] = "aaaaa"; + // fprintf(stdout, "%d\n", strncasecmp(a, b, 325432)); + // if (1 == 0 && fprintf(stderr, "test\n")); + // FILE * fd = fopen("/tmp/b.mp4", "r"); + // unsigned long long int s = 0; + // unsigned long long int b = 0; + // int c = fgetc(fd); + // while (!feof(fd)) { + // s = s + c; + // b++; + // c = fgetc(fd); + // } + // fprintf(stdout, "\n%llu, %llu\n", b, s); + return 0; +} |