gopher.c - frontends - front-ends for some sites (experiment)
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       gopher.c (7168B)
       ---
            1 #include <sys/socket.h>
            2 #include <sys/types.h>
            3 
            4 #include <ctype.h>
            5 #include <errno.h>
            6 #include <netdb.h>
            7 #include <stdarg.h>
            8 #include <stdio.h>
            9 #include <stdlib.h>
           10 #include <string.h>
           11 #include <unistd.h>
           12 
           13 #include "https.h"
           14 #include "util.h"
           15 #include "youtube.h"
           16 
           17 #define OUT(s) (fputs((s), stdout))
           18 #define OUTTEXT(s) gophertext(stdout, s, strlen(s));
           19 #define OUTLINK(s) gophertext(stdout, s, strlen(s));
           20 
           21 static const char *host = "127.0.0.1", *port = "70", *requestpath = "/";
           22 
           23 void
           24 line(int _type, const char *username, const char *selector)
           25 {
           26         putchar(_type);
           27         OUTTEXT(username);
           28         putchar('\t');
           29         OUTLINK(selector);
           30         printf("\t%s\t%s\r\n", host, port);
           31 }
           32 
           33 void
           34 error(const char *s)
           35 {
           36         line('3', s, "");
           37 }
           38 
           39 void
           40 info(const char *s)
           41 {
           42         line('i', s, "");
           43 }
           44 
           45 void
           46 header(void)
           47 {
           48 }
           49 
           50 void
           51 footer(void)
           52 {
           53         fputs(".\r\n", stdout);
           54 }
           55 
           56 int
           57 render_search(struct search_response *r)
           58 {
           59         struct item *v;
           60         size_t i;
           61 
           62         if (pledge("stdio", NULL) == -1)
           63                 exit(1);
           64 
           65         header();
           66 
           67         for (i = 0; i < r->nitems; i++) {
           68                 v = &(r->items[i]);
           69 
           70                 if (i > 0) {
           71                         info("");
           72                         info("");
           73                 }
           74                 if (v->id[0])
           75                         putchar('h');
           76                 else
           77                         putchar('i');
           78 
           79                 switch (v->linktype) {
           80                 case Channel:
           81                         OUT("[Channel] ");
           82                         OUTTEXT(v->channeltitle);
           83                         break;
           84                 case Movie:
           85                         OUT("[Movie] ");
           86                         OUTTEXT(v->title);
           87                         break;
           88                 case Playlist:
           89                         OUT("[Playlist] ");
           90                         OUTTEXT(v->title);
           91                         break;
           92                 default:
           93                         OUTTEXT(v->title);
           94                         break;
           95                 }
           96 
           97                 OUT("\t");
           98                 if (v->id[0]) {
           99                         OUT("URL:https://www.youtube.com/embed/");
          100                         OUTLINK(v->id);
          101                 }
          102                 printf("\t%s\t%s\r\n", host, port);
          103 
          104                 if (v->id[0]) {
          105                         printf("1Details\t%s?v=", requestpath);
          106                         OUTLINK(v->id);
          107                         printf("\t%s\t%s\r\n", host, port);
          108                 }
          109 
          110                 if (v->channelid[0]) {
          111                         OUT("1");
          112                         OUTTEXT(v->channeltitle);
          113                         printf("\t%s?c=", requestpath);
          114                         OUTLINK(v->channelid);
          115                         printf("\t%s\t%s\r\n", host, port);
          116                 } else if (v->userid[0]) {
          117                         OUT("1");
          118                         OUTTEXT(v->channeltitle);
          119                         printf("\t%s?u=", requestpath);
          120                         OUTLINK(v->userid);
          121                         printf("\t%s\t%s\r\n", host, port);
          122                 } else if (v->channeltitle[0]) {
          123                         OUT("i");
          124                         OUTTEXT(v->channeltitle);
          125                         printf("\t%s\t%s\t%s\r\n", "", host, port);
          126                 }
          127 
          128                 if (v->channelid[0] || v->userid[0]) {
          129                         OUT("hAtom feed of ");
          130                         OUTTEXT(v->channeltitle);
          131                         OUT("\t");
          132                         OUTLINK("URL:https://www.youtube.com/feeds/videos.xml?");
          133                         if (v->channelid[0]) {
          134                                 OUT("channel_id=");
          135                                 OUTLINK(v->channelid);
          136                         } else if (v->userid[0]) {
          137                                 OUT("user=");
          138                                 OUTLINK(v->userid);
          139                         }
          140                         printf("\t%s\t%s\r\n", host, port);
          141                 }
          142                 if (v->duration[0]) {
          143                         OUT("iDuration:      " );
          144                         OUTTEXT(v->duration);
          145                         printf("\t%s\t%s\t%s\r\n", "", host, port);
          146                 }
          147                 if (v->publishedat[0]) {
          148                         OUT("iPublished:     ");
          149                         OUTTEXT(v->publishedat);
          150                         printf("\t%s\t%s\t%s\r\n", "", host, port);
          151                 }
          152                 if (v->viewcount[0]) {
          153                         OUT("iViews:         ");
          154                         printnumsep(v->viewcount);
          155                         printf("\t%s\t%s\t%s\r\n", "", host, port);
          156                 }
          157         }
          158         footer();
          159 
          160         return 0;
          161 }
          162 
          163 int
          164 render_video(struct video_response *r)
          165 {
          166         char buf[256];
          167         const char *s, *e;
          168         /* softwrap: try to wrap on word boundary, otherwise hardwrap */
          169         int i, sw = 72, hw = 9999;
          170 
          171         if (pledge("stdio", NULL) == -1)
          172                 exit(1);
          173 
          174         header();
          175 
          176         info("");
          177 
          178         OUT("hTitle:     ");
          179         OUTTEXT(r->title);
          180         OUT("\tURL:https://www.youtube.com/embed/");
          181         OUTTEXT(r->id);
          182         printf("\t%s\t%s\r\n", host, port);
          183 
          184         OUT("hURL:       ");
          185         OUT("https://www.youtube.com/embed/");
          186         OUTTEXT(r->id);
          187         OUT("\tURL:");
          188         OUT("https://www.youtube.com/embed/");
          189         OUTTEXT(r->id);
          190         printf("\t%s\t%s\r\n", host, port);
          191 
          192         if (r->lengthseconds > 0) {
          193                 OUT("iLength:    ");
          194                 if (durationstr(r->lengthseconds, buf, sizeof(buf)) < sizeof(buf))
          195                         OUTTEXT(buf);
          196                 printf("\t%s\t%s\t%s\r\n", "", host, port);
          197         }
          198 
          199         OUT("hThumbnail: https://i.ytimg.com/vi/");
          200         OUTTEXT(r->id);
          201         OUT("/hqdefault.jpg\tURL:https://i.ytimg.com/vi/");
          202         OUTLINK(r->id);
          203         printf("/hqdefault.jpg\t%s\t%s\r\n", host, port);
          204 
          205         if (r->author[0]) {
          206                 OUT("1Channel:   ");
          207                 OUTTEXT(r->author);
          208                 printf("\t%s?c=%s\t%s\t%s\r\n", requestpath, r->channelid, host, port);
          209 
          210                 if (r->channelid[0]) {
          211                         OUT("hAtom feed\tURL:https://www.youtube.com/feeds/videos.xml?channel_id=");
          212                         OUTTEXT(r->channelid);
          213                         printf("\t%s\t%s\r\n", host, port);
          214                 }
          215         }
          216 
          217         OUT("iViews:     ");
          218         snprintf(buf, sizeof(buf), "%ld", r->viewcount);
          219         printnumsep(buf);
          220         printf("\t%s\t%s\t%s\r\n", "", host, port);
          221 
          222         if (r->publishdate[0]) {
          223                 OUT("iPublished: ");
          224                 OUTTEXT(r->publishdate);
          225                 printf("\t%s\t%s\t%s\r\n", "", host, port);
          226         }
          227 
          228         if (r->uploaddate[0]) {
          229                 OUT("iUploaded:  ");
          230                 OUTTEXT(r->uploaddate);
          231                 printf("\t%s\t%s\t%s\r\n", "", host, port);
          232         }
          233 
          234         if (r->shortdescription[0]) {
          235                 info("");
          236                 info("");
          237 
          238                 /* multi-line text */
          239                 i = 0;
          240                 for (s = e = r->shortdescription; ; e++) {
          241                         if (!i)
          242                                 putchar('i');
          243                         if (*e == '\n' || *e == '\0' ||
          244                            i > hw || (i > sw && (*e == ' ' || *e == '\t'))) {
          245                                 i = 0;
          246                                 gophertext(stdout, s, e - s);
          247                                 printf("\t%s\t%s\t%s\r\n", "", host, port);
          248                                 if (*e == '\0')
          249                                         break;
          250                                 s = e + 1;
          251                         } else {
          252                                 /* start of rune, wrongly assume 1 rune is 1 column for now */
          253                                 if (!((unsigned char)*e & 0x80))
          254                                         i++;
          255                         }
          256                 }
          257         }
          258 
          259         footer();
          260 
          261         return 0;
          262 }
          263 
          264 int
          265 main(void)
          266 {
          267         struct search_response *r = NULL;
          268         struct video_response *vr = NULL;
          269         const char *channelid = "", *userid = "", *videoid = "";
          270         const char *querystring = "", *rawsearch = "", *p;
          271         char search[1024] = "";
          272 
          273         if (pledge("stdio dns inet rpath unveil", NULL) == -1)
          274                 exit(1);
          275         if (unveil(TLS_CA_CERT_FILE, "r") == -1 ||
          276             unveil(NULL, NULL) == -1)
          277                 exit(1);
          278 
          279         if ((p = getenv("SERVER_NAME")))
          280                 host = p;
          281         if ((p = getenv("SERVER_PORT")))
          282                 port = p;
          283         if ((p = getenv("PATH_INFO")))
          284                 requestpath = p;
          285         if ((p = getenv("QUERY_STRING")))
          286                 querystring = p;
          287 
          288         p = NULL;
          289         if (querystring[0] == '?')
          290                 querystring++;
          291         if (querystring[0] == 'c' && querystring[1] == '=') {
          292                 channelid = querystring + 2;
          293                 p = querystring = "";
          294         } else if (querystring[0] == 'u' && querystring[1] == '=') {
          295                 userid = querystring + 2;
          296                 p = querystring = "";
          297         } else if (querystring[0] == 'v' && querystring[1] == '=') {
          298                 videoid = querystring + 2;
          299                 p = querystring = "";
          300         }
          301 
          302         if (querystring[0])
          303                 p = querystring;
          304         if (!p)
          305                 p = getenv("X_GOPHER_SEARCH"); /* geomyidae */
          306         if (!p)
          307                 p = getenv("SEARCHREQUEST"); /* gophernicus */
          308         if (p)
          309                 rawsearch = p;
          310         if (p && !uriencode(p, search, sizeof(search))) {
          311                 error("Invalid search");
          312                 footer();
          313                 return 0;
          314         }
          315 
          316         OUT("7Search");
          317         if (rawsearch[0]) {
          318                 OUT(": ");
          319                 OUT(rawsearch);
          320         }
          321         printf("\t%s\t%s\t%s\r\n", requestpath, host, port);
          322 
          323         info("");
          324         line('1', "<- back to main directory", requestpath);
          325 
          326         if (search[0]) {
          327                 r = youtube_search(search, "", "relevance");
          328         } else if (channelid[0]) {
          329                 r = youtube_channel_videos(channelid);
          330         } else if (userid[0]) {
          331                 r = youtube_user_videos(userid);
          332         } else if (videoid[0]) {
          333                 vr = youtube_video(videoid);
          334                 if (!vr || vr->isfound == 0) {
          335                         error("No video found");
          336                         footer();
          337                         return 0;
          338                 }
          339                 render_video(vr);
          340                 return 0;
          341         } else {
          342                 footer();
          343                 return 0;
          344         }
          345         info("");
          346 
          347         if (!r || r->nitems == 0) {
          348                 error("No videos found");
          349                 footer();
          350                 return 0;
          351         }
          352 
          353         render_search(r);
          354 
          355         return 0;
          356 }