linklist.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. * Copyright (c) 2018 Markus Hennecke <markus-hennecke@markus-hennecke.de>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <sys/queue.h>
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #include <dirent.h>
  20. #include <err.h>
  21. #include <fcntl.h>
  22. #include <stdbool.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <unistd.h>
  27. #include "filehelper.h"
  28. #include "handler.h"
  29. #include "helper.h"
  30. #include "template.h"
  31. struct _link {
  32. TAILQ_ENTRY(_link) entries;
  33. struct memmap *link;
  34. struct memmap *descr;
  35. struct memmap *sort;
  36. bool sub;
  37. bool ssl;
  38. char *linkname;
  39. bool selected;
  40. };
  41. struct _link_list {
  42. TAILQ_HEAD(, _link) links;
  43. };
  44. static void _link_free(struct _link *);
  45. static struct _link *_link_new_at(int, char *);
  46. static struct tmpl_data *_link_get_tmpl_data(struct _link *,
  47. struct request *, bool);
  48. static int _link_cmp(struct _link *, struct _link *);
  49. static void _link_list_insert_link(struct _link_list *,
  50. struct _link *);
  51. static void _link_list_free(struct _link_list *);
  52. static struct _link_list *_link_list_new_at(int, char *);
  53. static void _link_list_remove_unselected_subs(
  54. struct _link_list *_lst);
  55. int
  56. _link_cmp(struct _link *_a, struct _link *_b)
  57. {
  58. size_t cmplen = (_a->sort->size <= _b->sort->size)
  59. ? _a->sort->size : _b->sort->size;
  60. int result = strncmp((char *)_a->sort->data, (char *)_b->sort->data,
  61. cmplen);
  62. return result;
  63. }
  64. void
  65. _link_list_insert_link(struct _link_list *_lst, struct _link *_link)
  66. {
  67. struct _link *l = TAILQ_FIRST(&_lst->links);
  68. while (l) {
  69. int cmp = _link_cmp(_link, l);
  70. if (cmp > 0) {
  71. l = TAILQ_NEXT(l, entries);
  72. } else {
  73. TAILQ_INSERT_BEFORE(l, _link, entries);
  74. return;
  75. }
  76. }
  77. TAILQ_INSERT_TAIL(&_lst->links, _link, entries);
  78. }
  79. void
  80. _link_list_free(struct _link_list *_lst)
  81. {
  82. struct _link *l;
  83. while ((l = TAILQ_FIRST(&_lst->links))) {
  84. TAILQ_REMOVE(&_lst->links, l, entries);
  85. _link_free(l);
  86. }
  87. free(_lst);
  88. }
  89. struct _link_list *
  90. _link_list_new_at(int _fd, char *_cur_page)
  91. {
  92. struct dirent *dirent;
  93. struct _link_list *lst = malloc(sizeof(struct _link_list));
  94. TAILQ_INIT(&lst->links);
  95. int dirfd = dup(_fd);
  96. if (dirfd == -1)
  97. err(1, NULL);
  98. DIR *dir = fdopendir(dirfd);
  99. if (dir) {
  100. while ((dirent = readdir(dir)) != NULL) {
  101. if (dirent->d_name[0] == '.')
  102. continue;
  103. struct _link *l = _link_new_at(_fd, dirent->d_name);
  104. if (l != NULL) {
  105. l->selected = (strcmp(l->linkname,
  106. _cur_page) == 0);
  107. _link_list_insert_link(lst, l);
  108. }
  109. }
  110. closedir(dir);
  111. } else {
  112. free(lst);
  113. lst = NULL;
  114. }
  115. return lst;
  116. }
  117. struct _link *
  118. _link_new_at(int _fd, char *_dirname)
  119. {
  120. struct _link *link = calloc(1, sizeof(struct _link));
  121. int dirfd = openat(_fd, _dirname, O_RDONLY | O_DIRECTORY);
  122. if (-1 == dirfd)
  123. err(1, NULL);
  124. // Try to mmap the files LINK and SORT
  125. link->link = memmap_new_at(dirfd, "LINK");
  126. if (link->link == NULL)
  127. goto bailout;
  128. link->descr = memmap_new_at(dirfd, "DESCR");
  129. if (link->descr == NULL)
  130. goto bailout;
  131. link->sort = memmap_new_at(dirfd, "SORT");
  132. if (link->sort == NULL)
  133. goto bailout;
  134. struct stat sb;
  135. if (fstatat(dirfd, "SUB", &sb, 0) != -1)
  136. link->sub = ((sb.st_mode & S_IFREG) != 0);
  137. if (fstatat(dirfd, "SSL", &sb, 0) != -1)
  138. link->ssl = ((sb.st_mode & S_IFREG) != 0);
  139. link->linkname = strdup(_dirname);
  140. close(dirfd);
  141. return link;
  142. bailout:
  143. _link_free(link);
  144. close(dirfd);
  145. return NULL;
  146. }
  147. void
  148. _link_free(struct _link *_link)
  149. {
  150. memmap_free(_link->link);
  151. memmap_free(_link->descr);
  152. memmap_free(_link->sort);
  153. free(_link->linkname);
  154. free(_link);
  155. }
  156. void
  157. _link_list_remove_unselected_subs(struct _link_list *_lst)
  158. {
  159. struct _link *topic = TAILQ_FIRST(&_lst->links);
  160. struct _link *next;
  161. bool selected = false;
  162. while (topic) {
  163. selected = topic->selected;
  164. next = TAILQ_NEXT(topic, entries);
  165. if (next && next->sub) {
  166. struct _link *sub_start = next;
  167. while (next && next->sub) {
  168. selected |= next->selected;
  169. next = TAILQ_NEXT(next, entries);
  170. }
  171. if (!selected) {
  172. // Remove all SUB entries from sub_start to next
  173. struct _link *rem = sub_start;
  174. do {
  175. struct _link *next_rem
  176. = TAILQ_NEXT(rem, entries);
  177. TAILQ_REMOVE(&_lst->links, rem,
  178. entries);
  179. _link_free(rem);
  180. rem = next_rem;
  181. } while (rem != next);
  182. }
  183. }
  184. topic = next;
  185. }
  186. }
  187. struct tmpl_data *
  188. _link_get_tmpl_data(struct _link *_l, struct request *_req, bool _js)
  189. {
  190. char *linkdata = strndup(_l->link->data, memmap_chomp(_l->link));
  191. char *aref, *jslink, *link;
  192. if ((asprintf(&link, "%s%s/%s.html", _req->path, _req->lang,
  193. _l->linkname) == -1))
  194. err(1, NULL);
  195. if ((asprintf(&aref, "<a href=\"%s%s\">%s</a>",
  196. (_l->ssl || _req->page_info->ssl)
  197. ? "https://" CMS_HOSTNAME
  198. : "",
  199. link, linkdata) == -1))
  200. err(1, NULL);
  201. struct tmpl_data *data = tmpl_data_new();
  202. tmpl_data_set_variablen(data, "NR", _l->sort->data,
  203. memmap_chomp(_l->sort));
  204. tmpl_data_set_variablen(data, "DESCR", _l->descr->data,
  205. _l->descr->size);
  206. tmpl_data_move_variable(data, "LINK", aref);
  207. if (_js) {
  208. if ((asprintf(&jslink, "onclick=\"javascript:location.replace"
  209. "('%s')\"", link) == -1))
  210. err(1, NULL);
  211. tmpl_data_move_variable(data, "JSLINK", jslink);
  212. }
  213. if (_l->sub)
  214. tmpl_data_set_variable(data, "SUB", "1");
  215. if (_l->selected)
  216. tmpl_data_set_variable(data, "SELECTED", "1");
  217. free(linkdata);
  218. free(link);
  219. return data;
  220. }
  221. struct tmpl_loop *
  222. request_get_links(struct request *_req)
  223. {
  224. struct _link_list *lst = _link_list_new_at(_req->lang_dir, _req->page);
  225. if (lst == NULL)
  226. return NULL;
  227. _link_list_remove_unselected_subs(lst);
  228. struct tmpl_loop *loop = tmpl_loop_new("LINK_LOOP");
  229. struct _link *l;
  230. TAILQ_FOREACH(l, &lst->links, entries) {
  231. struct tmpl_data *data = _link_get_tmpl_data(l, _req, true);
  232. tmpl_loop_add_data(loop, data);
  233. }
  234. // TODO: add logout if we have a valid login session
  235. _link_list_free(lst);
  236. return loop;
  237. }
  238. struct tmpl_loop *
  239. request_get_language_links(struct request *_req)
  240. {
  241. struct tmpl_loop *loop = tmpl_loop_new("LANGUAGE_LINKS");
  242. int contentfd = dup(_req->content_dir);
  243. DIR *dir = fdopendir(contentfd);
  244. if (dir) {
  245. struct dirent *dirent;
  246. while ((dirent = readdir(dir))) {
  247. if (dirent->d_name[0] == '.')
  248. continue;
  249. if (strcmp(dirent->d_name, _req->page) == 0)
  250. continue;
  251. int fd = openat(_req->content_dir, dirent->d_name,
  252. O_DIRECTORY | O_RDONLY);
  253. if (-1 == fd) {
  254. warn(NULL);
  255. continue;
  256. }
  257. if (dir_exists_at(fd, _req->page)) {
  258. char *lang_link;
  259. if ((asprintf(&lang_link,
  260. "<a href=\"%s%s/%s.html\">"
  261. "<img src=\""
  262. CMS_CONFIG_URL_IMAGES
  263. "flag_%s.png\" alt=\"%s\"/>"
  264. "</a>",
  265. _req->path, dirent->d_name,
  266. _req->page, dirent->d_name,
  267. dirent->d_name) == -1))
  268. err(1, NULL);
  269. struct tmpl_data *d = tmpl_data_new();
  270. tmpl_data_move_variable(d, "LANGUAGE_LINK",
  271. lang_link);
  272. tmpl_loop_add_data(loop, d);
  273. }
  274. close(fd);
  275. }
  276. }
  277. return loop;
  278. }