gopher.c - frontends - front-ends for some sites (experiment) | |
Log | |
Files | |
Refs | |
README | |
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_"… | |
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(subre… | |
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 } |