gopher.c - frontends - front-ends for some sites (experiment)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
gopher.c (4954B)
---
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 <time.h>
12 #include <unistd.h>
13
14 #include "https.h"
15 #include "reddit.h"
16 #include "util.h"
17
18 #define OUT(s) (fputs((s), stdout))
19 #define OUTLINK(s) gophertext(stdout, s, strlen(s))
20 #define OUTTEXT(s) gophertext(stdout, s, strlen(s))
21
22 static const char *baserel = "/reddit.cgi";
23 static const char *host = "127.0.0.1", *port = "70";
24
25 static char before[16], after[16], subreddit[1024];
26
27 void
28 line(int _type, const char *username, const char *selector)
29 {
30 putchar(_type);
31 OUTTEXT(username);
32 putchar('\t');
33 OUTLINK(selector);
34 printf("\t%s\t%s\r\n", host, port);
35 }
36
37 void
38 error(const char *s)
39 {
40 line('3', s, "");
41 }
42
43 void
44 info(const char *s)
45 {
46 line('i', s, "");
47 }
48
49 void
50 dir(const char *username, const char *selector)
51 {
52 line('1', username, selector);
53 }
54
55 void
56 html(const char *username, const char *selector)
57 {
58 line('h', username, selector);
59 }
60
61 void
62 page(int _type, const char *username, const char *page)
63 {
64 putchar(_type);
65 OUTTEXT(username);
66 putchar('\t');
67 printf("%s?p=%s", baserel, page);
68 printf("\t%s\t%s\r\n", host, port);
69 }
70
71 void
72 header(void)
73 {
74 }
75
76 void footer(void)
77 {
78 printf(".\r\n");
79 }
80
81 void
82 printitem(struct item *item)
83 {
84 if (!item || !item->title[0])
85 return;
86
87 struct tm *tm = gmtime(&(item->created_utc));
88
89 putchar('h');
90 OUTTEXT(item->title);
91 OUT(" by ");
92 OUTTEXT(item->author);
93 OUT(" at ");
94 printf("%04d-%02d-%02d %02d:%02d",
95 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
96 tm->tm_hour, tm->tm_min);
97 OUT("\tURL:");
98 OUTLINK(item->url);
99 printf("\t%s\t%s\r\n", host, port);
100
101 /* if global (no subreddit), show where it was posted */
102 if (!subreddit[0]) {
103 printf("1Posted in r/");
104 OUTTEXT(item->subreddit);
105 OUT("\t");
106 OUTTEXT(baserel);
107 OUT("?subreddit=");
108 OUTTEXT(item->subreddit);
109 printf("\t%s\t%s\r\n", host, port);
110 }
111
112 printf("hUpvotes: %ld, downvotes %ld, comments: %ld",
113 item->ups, item->downs, item->num_comments);
114 OUT("\tURL:https://old.reddit.com/r/");
115 OUTTEXT(item->subreddit);
116 OUTTEXT("/comments/");
117 if (reddit_isvalidlink(item->name) && !strncmp(item->name, "t3_", 3))
118 OUTTEXT(item->name + 3);
119 printf("/\t%s\t%s\r\n", host, port);
120
121 /* TODO: also noticed "spoiler" as value, ignore? */
122 if (item->thumbnail[0] &&
123 strcmp(item->thumbnail, "self") &&
124 strcmp(item->thumbnail, "default")) {
125 putchar('h');
126 OUT("Thumbnail");
127 OUT("\tURL:");
128 OUTTEXT(item->thumbnail);
129 printf("\t%s\t%s\r\n", host, port);
130 }
131
132 if (item->is_video) {
133 putchar('h');
134 OUT("Video, duration: ");
135 printf("%ld, url: ", item->duration);
136 OUTTEXT(item->dash_url);
137 OUT("\tURL:");
138 OUTTEXT(item->dash_url);
139 printf("\t%s\t%s\r\n", host, port);
140 }
141
142 info("");
143 }
144
145 int
146 render_pagination(struct list_response *r)
147 {
148 int i = 0;
149
150 if (r->before[0]) {
151 printf("1Previous page");
152 OUT("\t");
153 OUTTEXT(baserel);
154 OUT("?subreddit=");
155 OUTTEXT(subreddit);
156 OUT("&before=");
157 OUTTEXT(r->before);
158 printf("\t%s\t%s\r\n", host, port);
159 i++;
160 }
161 if (r->after[0]) {
162 printf("1Next page");
163 OUT("\t");
164 OUTTEXT(baserel);
165 OUT("?subreddit=");
166 OUTTEXT(subreddit);
167 OUT("&after=");
168 OUTTEXT(r->after);
169 printf("\t%s\t%s\r\n", host, port);
170 i++;
171 }
172 return i;
173 }
174
175 int
176 render(struct list_response *r)
177 {
178 size_t i;
179
180 header();
181
182 if (pledge("stdio", NULL) == -1)
183 exit(1);
184
185 if (render_pagination(r))
186 info("");
187
188 for (i = 0; i < r->nitems; i++)
189 printitem(&(r->items[i]));
190
191 render_pagination(r);
192
193 footer();
194
195 return 0;
196 }
197
198 static void
199 usage(void)
200 {
201 printf("3Specify a subreddit\t\t%s\t%s\r\n", host, port);
202 printf(".\r\n");
203 exit(1);
204 }
205
206 int
207 main(int argc, char *argv[])
208 {
209 struct list_response *r;
210 char *querystring, *p, *search;
211
212 if (pledge("stdio dns inet rpath unveil", NULL) == -1)
213 exit(1);
214 if (unveil(TLS_CA_CERT_FILE, "r") == -1 ||
215 unveil(NULL, NULL) == -1)
216 exit(1);
217
218 if ((p = getenv("SERVER_NAME")))
219 host = p;
220 if ((p = getenv("SERVER_PORT")))
221 port = p;
222
223 if (!(querystring = getenv("QUERY_STRING")))
224 querystring = "";
225
226 if ((p = getparam(querystring, "subreddit"))) {
227 if (decodeparam(subreddit, sizeof(subreddit), p) == -1)
228 subreddit[0] = '\0';
229 } else {
230 if (!(search = getenv("X_GOPHER_SEARCH"))) /* geomyidae */
231 search = getenv("SEARCHREQUEST"); /* gophernicus */
232
233 if (search && !uriencode(search, subreddit, sizeof(subreddit)))
234 usage();
235 }
236
237 if ((p = getparam(querystring, "before"))) {
238 if (decodeparam(before, sizeof(before), p) == -1)
239 before[0] = '\0';
240 if (!reddit_isvalidlink(before))
241 before[0] = '\0';
242 }
243
244 if ((p = getparam(querystring, "after"))) {
245 if (decodeparam(after, sizeof(after), p) == -1)
246 after[0] = '\0';
247 if (!reddit_isvalidlink(after))
248 after[0] = '\0';
249 }
250
251 r = reddit_list(subreddit, 100, before, after);
252 if (!r || r->nitems == 0) {
253 error("No items found");
254 printf(".\r\n");
255 exit(1);
256 }
257
258 render(r);
259
260 return 0;
261 }