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 }