engine.vala 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using Gtk;
  2. using GLib;
  3. enum LinkType {
  4. GOPHE,
  5. HYPER,
  6. }
  7. class Engine : Object {
  8. Window main_window;
  9. Entry url_entry;
  10. TextView text_view;
  11. Spinner spinner;
  12. List<string> history;
  13. List<string> future;
  14. Regex gopher_url_regex;
  15. Regex gopher_line_regex;
  16. public Engine (Window main_window, Entry url_entry, TextView text_view, Spinner spinner) {
  17. this.main_window = main_window;
  18. this.url_entry = url_entry;
  19. this.text_view = text_view;
  20. this.spinner = spinner;
  21. history = new List<string> ();
  22. future = new List<string> ();
  23. fix_cursor ();
  24. gopher_url_regex = /^(gopher:\/\/)?(?<host>[^\/:]*)(:(?<port>[0-9]+))?(\/((?<gophertype>.))(?<selector>.*))?\/?$/;
  25. gopher_line_regex = /(?<gopher_type>.)(?<text>[^\t]*)(\t(?<selector>[^\t]*))?(\t(?<host>[^\t]*))?(\t(?<port>[^\t]*))?/;
  26. if(url_entry.text != "") {
  27. gopher_load (url_entry.text, true);
  28. }
  29. }
  30. public void fix_cursor () {
  31. var w = text_view.get_window (TextWindowType.TEXT);
  32. w.set_cursor (new Gdk.Cursor.from_name (Gdk.Display.get_default (), "default"));
  33. }
  34. public void visit (string url, bool note) {
  35. url_entry.set_text (url);
  36. if (note) {
  37. history.prepend (url);
  38. future = new List<string> ();
  39. }
  40. }
  41. public void back () {
  42. if (history.length () > 1) {
  43. future.prepend (history.nth_data (0));
  44. history.remove_link (history.first ());
  45. string url = history.nth_data (0);
  46. gopher_load (url, false);
  47. }
  48. }
  49. public void forward () {
  50. if (future.length () > 0) {
  51. history.prepend (future.nth_data (0));
  52. string url = future.nth_data (0);
  53. future.remove_link (future.first ());
  54. gopher_load (url, false);
  55. }
  56. }
  57. public async void gopher_load (string url, bool note) {
  58. string host;
  59. int port;
  60. char gopher_type;
  61. string selector;
  62. bool success;
  63. string text = "";
  64. if (!gopher_parse_url (url, out host, out port, out gopher_type, out selector))
  65. return;
  66. spinner.start ();
  67. success = yield gopher_request (url, host, port, selector, note, out text);
  68. if(!success) {
  69. spinner.stop();
  70. return;
  71. }
  72. visit (url, note);
  73. gopher_text_to_buffer (text, gopher_type, text_view);
  74. fix_cursor ();
  75. spinner.stop();
  76. }
  77. public bool gopher_parse_url (string url,
  78. out string host,
  79. out int port,
  80. out char gopher_type,
  81. out string selector) {
  82. MatchInfo match_info;
  83. host = "";
  84. port = 0;
  85. gopher_type = '0';
  86. selector = "/";
  87. if (gopher_url_regex.match (url, 0, out match_info)) {
  88. host = match_info.fetch_named ("host");
  89. string s_port = match_info.fetch_named ("port");
  90. if (s_port == null) s_port = "70";
  91. if (s_port == "") s_port = "70";
  92. port = int.parse(s_port);
  93. string gt = match_info.fetch_named ("gophertype");
  94. if (gt == null) {
  95. gopher_type = '1';
  96. }
  97. else {
  98. gopher_type = gt[0];
  99. }
  100. selector = match_info.fetch_named ("selector");
  101. if (selector == null) selector = "";
  102. // DBG
  103. stdout.printf("HOST: %s\n", host);
  104. stdout.printf("PORT: %d\n", port);
  105. stdout.printf("TYPE: %c\n", gopher_type);
  106. stdout.printf("SLCT: %s\n", selector);
  107. return true;
  108. }
  109. else {
  110. stdout.printf ("Invalid URL: %s\n", url);
  111. return false;
  112. }
  113. }
  114. public async bool gopher_request (string input_url,
  115. string host, int port, string selector,
  116. bool note,
  117. out string all_lines) {
  118. try {
  119. // Resolve hostname to IP address:
  120. Resolver resolver = Resolver.get_default ();
  121. List<InetAddress> addresses = resolver.lookup_by_name (host, null);
  122. InetAddress address = addresses.nth_data (0);
  123. // Connect:
  124. SocketClient client = new SocketClient ();
  125. SocketConnection conn = yield client.connect_async (new InetSocketAddress (address, (uint16) port));
  126. // Send HTTP GET request
  127. string message = "%s\r\n".printf (selector);
  128. yield conn.output_stream.write_async (message.data);
  129. // Receive response
  130. conn.socket.set_blocking (true);
  131. DataInputStream response = new DataInputStream (conn.input_stream);
  132. // response.set_newline_type(DataStreamNewlineType.CR_LF);
  133. size_t my_len;
  134. all_lines = response.read_upto ("", 0, out my_len);
  135. } catch (Error e) {
  136. stdout.printf ("Error: %s\n", e.message);
  137. return false;
  138. }
  139. return true;
  140. }
  141. void gopher_text_to_buffer (string lines, char gopher_type, TextView text_view) {
  142. TextIter iter;
  143. TextBuffer buf;
  144. buf = new TextBuffer (null);
  145. buf.get_start_iter (out iter);
  146. foreach(unowned string line in lines.split("\n")) {
  147. if (gopher_type == '0') {
  148. buf.insert(ref iter, line + "\n", -1);
  149. }
  150. else if (gopher_type == '1') {
  151. gopher_page_handle_line (line, buf, ref iter);
  152. }
  153. else {
  154. stdout.printf ("Unhandled gopher type: %c\n", gopher_type);
  155. return;
  156. }
  157. }
  158. text_view.set_buffer (buf);
  159. }
  160. void gopher_page_handle_line (string line, TextBuffer buf, ref TextIter iter) {
  161. MatchInfo match_info;
  162. if (line == "." || line == ".\r") {
  163. buf.insert(ref iter, line + "\n", -1);
  164. return;
  165. }
  166. if (!gopher_line_regex.match (line, 0, out match_info)) {
  167. stdout.printf ("Gopher parsing error: %s\n", line);
  168. return;
  169. }
  170. char line_type = match_info.fetch_named ("gopher_type")[0];
  171. string text = match_info.fetch_named ("text");
  172. string line_selector = match_info.fetch_named ("selector");
  173. string line_host = match_info.fetch_named ("host");
  174. string s_line_port = match_info.fetch_named ("port");
  175. if (s_line_port == null) s_line_port = "70";
  176. int line_port = int.parse (s_line_port);
  177. if (line_type == 'i' || line_type == 'I') {
  178. buf.insert(ref iter, text + "\n", -1);
  179. }
  180. else if (line_type == '0' || line_type == '1') {
  181. string url = "gopher://%s:%d/%c%s".printf (line_host, line_port, line_type, line_selector);
  182. insert_link_to (text, url, LinkType.GOPHE,
  183. buf, ref iter);
  184. }
  185. else if (line_type == 'h' || line_type == 'H') {
  186. string url = line_selector;
  187. if (url.has_prefix ("URL:")) {
  188. url = url.substring (4);
  189. }
  190. insert_link_to (text, url, LinkType.HYPER,
  191. buf, ref iter);
  192. }
  193. else {
  194. stdout.printf ("Unknown gopher line type: %s\n", line);
  195. }
  196. }
  197. void insert_link_to (string line, string url, LinkType ty,
  198. TextBuffer buf, ref TextIter iter) {
  199. string color = "";
  200. switch (ty) {
  201. case LinkType.GOPHE:
  202. color = "blue";
  203. break;
  204. case LinkType.HYPER:
  205. color = "red";
  206. break;
  207. }
  208. TextTag tag = buf.create_tag (null,
  209. "foreground", color,
  210. "underline", Pango.Underline.SINGLE,
  211. null);
  212. tag.set_data ("link", url);
  213. tag.event.connect ((event_object, event, iter) => {
  214. if (event.type == Gdk.EventType.BUTTON_PRESS) {
  215. string link = tag.get_data ("link");
  216. switch (ty) {
  217. case LinkType.GOPHE:
  218. gopher_load (link, true);
  219. break;
  220. case LinkType.HYPER:
  221. try {
  222. if (!Gtk.show_uri (null, link, 0)) {
  223. stderr.printf ("Couldn't open URL: %s.\n", link);
  224. }
  225. }
  226. catch (Error e) {
  227. stderr.printf ("Couldn't open URL: %s.\n", link);
  228. }
  229. break;
  230. }
  231. return true;
  232. }
  233. else {
  234. return true;
  235. }
  236. });
  237. buf.insert_with_tags(ref iter, line + "\n", -1, tag, null);
  238. }
  239. }