Introduction
Introduction Statistics Contact Development Disclaimer Help
gopher.c - frontends - front-ends for some sites (experiment)
Log
Files
Refs
README
LICENSE
---
gopher.c (9547B)
---
1 #include <sys/socket.h>
2 #include <sys/types.h>
3
4 #include <ctype.h>
5 #include <errno.h>
6 #include <locale.h>
7 #include <netdb.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <wchar.h>
15
16 #include "https.h"
17 #include "json.h"
18 #include "twitch.h"
19 #include "util.h"
20
21 #define OUT(s) (fputs((s), stdout))
22 #define OUTTEXT(s) gophertext(stdout, s, strlen(s))
23 #define OUTLINK(s) gophertext(stdout, s, strlen(s))
24
25 extern char **environ;
26
27 static const char *baserel = "/twitch.cgi";
28 static const char *host = "127.0.0.1", *port = "70";
29
30 /* page variables */
31 static char *title = "", *pagetitle = "";
32
33 void
34 line(int _type, const char *username, const char *selector)
35 {
36 putchar(_type);
37 OUTTEXT(username);
38 putchar('\t');
39 OUTLINK(selector);
40 printf("\t%s\t%s\r\n", host, port);
41 }
42
43 void
44 error(const char *s)
45 {
46 line('3', s, "");
47 }
48
49 void
50 info(const char *s)
51 {
52 line('i', s, "");
53 }
54
55 void
56 dir(const char *username, const char *selector)
57 {
58 line('1', username, selector);
59 }
60
61 void
62 html(const char *username, const char *selector)
63 {
64 line('h', username, selector);
65 }
66
67 void
68 page(int _type, const char *username, const char *page)
69 {
70 putchar(_type);
71 OUTTEXT(username);
72 putchar('\t');
73 printf("%s?p=%s", baserel, page);
74 printf("\t%s\t%s\r\n", host, port);
75 }
76
77 static int
78 gamecmp_name(const void *v1, const void *v2)
79 {
80 struct game *g1 = (struct game *)v1;
81 struct game *g2 = (struct game *)v2;
82
83 return strcmp(g1->name, g2->name);
84 }
85
86 void
87 header(void)
88 {
89 putchar('i');
90 if (title[0]) {
91 OUTTEXT(title);
92 OUT(" - ");
93 }
94 if (pagetitle[0]) {
95 OUTTEXT(pagetitle);
96 OUT(" - ");
97 }
98 printf("Twitch.tv\t%s\t%s\t%s\r\n", "", host, port);
99 info("---");
100 page('1', "Featured", "featured");
101 page('1', "Games", "games");
102 page('1', "VODS", "vods");
103 dir("Source-code", "/git/frontends");
104 page('1', "Links", "links");
105 info("---");
106 }
107
108 void
109 footer(void)
110 {
111 printf(".\r\n");
112 }
113
114 void
115 render_links(void)
116 {
117 header();
118 info("");
119 html("mpv player", "URL:https://mpv.io/installation/");
120 html("youtube-dl", "URL:https://github.com/ytdl-org/youtube-d…
121 html("VLC", "URL:https://www.videolan.org/");
122 html("Twitch.tv API", "URL:https://dev.twitch.tv/docs");
123 footer();
124 }
125
126 void
127 render_games_top(struct games_response *r)
128 {
129 struct game *game;
130 size_t i;
131
132 header();
133 info("Name");
134 for (i = 0; i < r->nitems; i++) {
135 game = &(r->data[i]);
136
137 putchar('1');
138 OUTTEXT(game->name);
139 printf("\t%s?p=streams&game_id=", baserel);
140 OUTLINK(game->id);
141 printf("\t%s\t%s\r\n", host, port);
142 }
143 footer();
144 }
145
146 void
147 render_streams(struct streams_response *r, const char *game_id)
148 {
149 struct stream *stream;
150 char buf[256], title[256];
151 size_t i;
152
153 header();
154 if (!game_id[0])
155 printf("i%-20s %-20s %-50s %7s\t%s\t%s\t%s\r\n",
156 "Game", "Name", "Title", "Viewers", "", host, po…
157 else
158 printf("i%-20s %-50s %7s\t%s\t%s\t%s\r\n",
159 "Name", "Title", "Viewers", "", host, port);
160
161 for (i = 0; i < r->nitems; i++) {
162 stream = &(r->data[i]);
163
164 if (stream->user)
165 putchar('h');
166 else
167 putchar('i');
168
169 if (!game_id[0]) {
170 if (stream->game) {
171 if (utf8pad(buf, sizeof(buf), stream->ga…
172 OUTTEXT(buf);
173 } else {
174 printf("%20s", "");
175 }
176 OUT(" ");
177 }
178
179 if (utf8pad(buf, sizeof(buf), stream->user_name, 20, ' '…
180 OUTTEXT(buf);
181 OUT(" ");
182
183 if (stream->language[0])
184 snprintf(title, sizeof(title), "[%s] %s", stream…
185 if (utf8pad(buf, sizeof(buf), title, 50, ' ') !=…
186 OUTTEXT(buf);
187 else {
188 if (utf8pad(buf, sizeof(buf), stream->title, 50,…
189 OUTTEXT(buf);
190 }
191
192 printf(" %7lld\t", stream->viewer_count);
193
194 if (stream->user) {
195 OUT("URL:https://www.twitch.tv/");
196 OUTLINK(stream->user->login);
197 }
198 printf("\t%s\t%s\r\n", host, port);
199 }
200 footer();
201 }
202
203 void
204 render_videos_atom(struct videos_response *r, const char *login)
205 {
206 struct video *video;
207 size_t i;
208
209 OUT("<feed xmlns=\"http://www.w3.org/2005/Atom\" xml:lang=\"en\"…
210 for (i = 0; i < r->nitems; i++) {
211 video = &(r->data[i]);
212
213 OUT("<entry>\n");
214 OUT("\t<title type=\"text\">");
215 xmlencode(video->title);
216 OUT("</title>\n");
217 OUT("\t<link rel=\"alternate\" type=\"text/html\" href=\…
218 xmlencode(video->url);
219 OUT("\" />\n");
220 OUT("\t<id>");
221 xmlencode(video->url);
222 OUT("</id>\n");
223 OUT("\t<updated>");
224 xmlencode(video->created_at);
225 OUT("</updated>\n");
226 OUT("\t<published>");
227 xmlencode(video->created_at);
228 OUT("</published>\n");
229 OUT("</entry>\n");
230 }
231 OUT("</feed>\n");
232 }
233
234 void
235 render_videos(struct videos_response *r, const char *login)
236 {
237 struct video *video;
238 char buf[256];
239 size_t i;
240
241 header();
242
243 page('7', "Submit Twitch login name to list VODs", "vods");
244 info("");
245
246 /* no results or no user_id parameter: quick exit */
247 if (r == NULL) {
248 footer();
249 return;
250 }
251
252 /* link to Atom format (text). */
253 if (login[0]) {
254 OUT("0Atom feed for ");
255 OUTLINK(login);
256 printf("\t%s?p=vods&format=atom&login=", baserel);
257 OUTLINK(login);
258 printf("\t%s\t%s\r\n", host, port);
259 info("");
260 }
261
262 printf("i%-20s %-50s %-10s %7s\t%s\t%s\t%s\r\n",
263 "Created", "Title", "Duration", "Views", "", host, port);
264
265 for (i = 0; i < r->nitems; i++) {
266 video = &(r->data[i]);
267
268 putchar('h');
269 if (utf8pad(buf, sizeof(buf), video->created_at, 20, ' '…
270 OUTLINK(buf);
271 OUT(" ");
272 if (utf8pad(buf, sizeof(buf), video->title, 50, ' ') != …
273 OUTLINK(buf);
274 OUT(" ");
275 if (utf8pad(buf, sizeof(buf), video->duration, 10, ' ') …
276 OUTLINK(buf);
277
278 printf(" %7lld\t", video->view_count);
279 OUT("URL:");
280 OUTLINK(video->url);
281 printf("\t%s\t%s\r\n", host, port);
282 }
283 footer();
284 }
285
286 void
287 handle_streams(void)
288 {
289 struct streams_response *r;
290 struct users_response *ru = NULL;
291 struct games_response *rg = NULL;
292 char game_id[32] = "";
293 char *p, *querystring;
294
295 pagetitle = "Streams";
296
297 /* parse "game_id" parameter */
298 if ((querystring = getenv("QUERY_STRING"))) {
299 if ((p = getparam(querystring, "game_id"))) {
300 if (decodeparam(game_id, sizeof(game_id), p) == …
301 game_id[0] = '\0';
302 }
303 }
304
305 if (game_id[0])
306 r = twitch_streams_bygame(game_id);
307 else
308 r = twitch_streams();
309
310 if (r == NULL)
311 return;
312
313 /* find detailed games data with streams */
314 if (!game_id[0])
315 rg = twitch_streams_games(r);
316
317 /* find detailed user data with streams */
318 ru = twitch_streams_users(r);
319
320 if (pledge("stdio", NULL) == -1)
321 exit(1);
322
323 render_streams(r, game_id);
324
325 free(r);
326 free(rg);
327 free(ru);
328 }
329
330 void
331 handle_videos(void)
332 {
333 struct videos_response *r = NULL;
334 struct users_response *ru = NULL;
335 char user_id[32] = "", login[64] = "", format[6] = "";
336 char *p, *querystring;
337
338 pagetitle = "Videos";
339
340 /* parse "user_id" or "login" parameter */
341 if ((querystring = getenv("QUERY_STRING"))) {
342 if ((p = getparam(querystring, "user_id"))) {
343 if (decodeparam(user_id, sizeof(user_id), p) == …
344 user_id[0] = '\0';
345 }
346 if ((p = getparam(querystring, "login"))) {
347 if (decodeparam(login, sizeof(login), p) == -1)
348 login[0] = '\0';
349 }
350 if ((p = getparam(querystring, "format"))) {
351 if (decodeparam(format, sizeof(format), p) == -1)
352 format[0] = '\0';
353 }
354 }
355
356 /* login: if not set as query string parameter then use gopher s…
357 parameter */
358 if (login[0] == '\0') {
359 if (!(p = getenv("X_GOPHER_SEARCH"))) /* geomyidae */
360 p = getenv("SEARCHREQUEST"); /* gophernicus */
361 if (p && decodeparam(login, sizeof(login), p) == -1)
362 login[0] = '\0';
363 }
364
365 /* no parameter given, show form */
366 if (!user_id[0] && !login[0]) {
367 if (pledge("stdio", NULL) == -1)
368 exit(1);
369
370 render_videos(r, "");
371 return;
372 }
373
374 if (user_id[0]) {
375 r = twitch_videos_byuserid(user_id);
376 } else {
377 ru = twitch_users_bylogin(login);
378 if (ru && ru->nitems > 0)
379 r = twitch_videos_byuserid(ru->data[0].id);
380 }
381
382 if (pledge("stdio", NULL) == -1)
383 exit(1);
384
385 if (r && r->nitems > 0)
386 title = r->data[0].user_name;
387
388 if (!strcmp(format, "atom"))
389 render_videos_atom(r, login);
390 else
391 render_videos(r, login);
392
393 free(ru);
394 free(r);
395 }
396
397 void
398 handle_games_top(void)
399 {
400 struct games_response *r;
401
402 pagetitle = "Top 100 games";
403
404 if (!(r = twitch_games_top()))
405 return;
406
407 if (pledge("stdio", NULL) == -1)
408 exit(1);
409
410 /* sort by name alphabetically, NOTE: the results are the top 100
411 sorted by viewcount. View counts are not visible in the new
412 Helix API data). */
413 qsort(r->data, r->nitems, sizeof(r->data[0]), gamecmp_name);
414
415 render_games_top(r);
416
417 free(r);
418 }
419
420 void
421 handle_links(void)
422 {
423 if (pledge("stdio", NULL) == -1)
424 exit(1);
425
426 pagetitle = "Links";
427
428 render_links();
429 }
430
431 int
432 main(void)
433 {
434 char *p, path[256] = "", *querystring;
435
436 setlocale(LC_CTYPE, "");
437
438 if (pledge("stdio dns inet rpath unveil", NULL) == -1 ||
439 unveil(TLS_CA_CERT_FILE, "r") == -1 ||
440 unveil(NULL, NULL) == -1) {
441 exit(1);
442 }
443
444 if ((p = getenv("SERVER_NAME")))
445 host = p;
446 if ((p = getenv("SERVER_PORT")))
447 port = p;
448
449 if (!(querystring = getenv("QUERY_STRING")))
450 querystring = "";
451
452 if ((p = getparam(querystring, "p"))) {
453 if (decodeparam(path, sizeof(path), p) == -1)
454 path[0] = '\0';
455 }
456
457 if (!strcmp(path, "") ||
458 !strcmp(path, "featured") ||
459 !strcmp(path, "streams")) {
460 /* featured / by game id */
461 handle_streams();
462 } else if (!strcmp(path, "topgames") ||
463 !strcmp(path, "games")) {
464 handle_games_top();
465 } else if (!strcmp(path, "videos") ||
466 !strcmp(path, "vods")) {
467 handle_videos();
468 } else if (!strcmp(path, "links")) {
469 handle_links();
470 } else {
471 error("Not Found");
472 printf(".\r\n");
473 exit(1);
474 }
475
476 return 0;
477 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.