Go Back

0 /*

1 * ISC License

2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>

3 */

4 #include <stdlib.h>

5 #include <string.h>

6 #include <stdint.h>

7 #include <unistd.h>

8 #include "strlcpy.h"

9 #include "strnstr.h"

10 #include "macro.h"

11 #include "error.h"

12 #include "sandbox.h"

13 #include "gemini.h"

14 #include "page.h"

15 #include "request.h"

16 #define PARSER_INTERNAL

17 #include "parser.h"

18

19 /* fetch status code and metadata */

20 int parse_response(int fd, size_t length, char *meta, size_t len, int *code,

21 int *bytes_read) {

22

23 char *ptr;

24 char buf[MAX_URL];

25 size_t i;

26 int found;

27

28 found = 0;

29 for (i = 0; i < sizeof(buf) && i < len && i < length; i++) {

30 int ret = read(fd, &buf[i], 1);

31 if (ret != 1) return -1;

32 if (i && buf[i] == '\n' && buf[i - 1] == '\r') {

33 found = 1;

34 break;

35 }

36 }

37 if (!found || i < 1) return -1;

38 *bytes_read = i + 1;

39 buf[i - 1] = '\0';

40

41 ptr = strchr(buf, ' ');

42 if (!ptr) return ERROR_INVALID_METADATA;

43 *ptr = 0;

44 i = atoi(buf);

45 if (!i) return ERROR_INVALID_STATUS;

46

47 memset(meta, 0, len);

48 strlcpy(meta, ptr + 1, len);

49

50 *code = gemini_status_code(i);

51 return 0;

52 }

53

54 void parser_request(int in, int out) {

55 parser_sandbox(out, "vgmi [request]");

56 while (1) { /* TODO: send error code */

57

58 int ret, bytes;

59 size_t length;

60 struct request request = {0};

61

62 if (vread(in, &length, sizeof(length))) break;

63 ret = parse_response(in, length, V(request.meta),

64 &request.status, &bytes);

65 if (request.status == -1) ret = ERROR_INVALID_STATUS;

66 if (ret) {

67 uint8_t byte;

68 request.status = -1;

69 for (; bytes < (signed)length; bytes++)

70 read(in, P(byte));

71 write(out, P(request.status));

72 write(out, P(ret));

73 continue;

74 }

75

76 write(out, P(request.status));

77 if (request.status == GMI_SUCCESS && !request.meta[0]) {

78 STRLCPY(request.meta, "text/gemini");

79 }

80 write(out, V(request.meta));

81 if (request.status == GMI_SUCCESS) {

82 request.page.mime = parser_mime(V(request.meta));

83 request.page.offset = bytes;

84 } else {

85 request.page.mime = 0;

86 request.page.offset = 0;

87 }

88 write(out, P(request.page.mime));

89 write(out, P(request.page.offset));

90

91 if (request.status == GMI_SUCCESS &&

92 is_gemtext(V(request.meta))) {

93 if (parse_links(in, length - bytes, out)) {

94 break;

95 }

96 } else {

97 uint8_t byte;

98 size_t i;

99 for (i = bytes; i < length; i++) read(in, &byte, 1);

100 }

101 }

102 }

103