summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile12
-rw-r--r--README.md39
-rwxr-xr-xrtv4d-dlbin0 -> 31224 bytes
-rw-r--r--rtv4d-dl.c491
-rw-r--r--tcp.c266
-rw-r--r--test.c22
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
new file mode 100755
index 0000000..67d1b41
--- /dev/null
+++ b/rtv4d-dl
Binary files differ
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;
+}
diff --git a/tcp.c b/tcp.c
new file mode 100644
index 0000000..d30e7b5
--- /dev/null
+++ b/tcp.c
@@ -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
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..d24517d
--- /dev/null
+++ b/test.c
@@ -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;
+}