--- /dev/null
+/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+ @STOP */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ncurses.h>
+
+#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
+#define PORT "50000"
+
+#define streq(a,b) (!strcmp(a,b))
+
+int g_row, g_col;
+
+void free_screen(void) {
+ endwin();
+}
+
+void init_screen(void) {
+ initscr();
+ cbreak();
+ noecho();
+ keypad(stdscr, ERR);
+ nodelay(stdscr, TRUE);
+}
+
+void handle_winch(int x) {
+ (void)x;
+ struct winsize w;
+ signal(SIGWINCH, SIG_IGN);
+
+ endwin();
+ init_screen();
+ refresh();
+ clear();
+
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+ g_row = w.ws_row;
+ g_col = w.ws_col;
+
+ signal(SIGWINCH, handle_winch);
+}
+
+int g_sockfd = -1;
+
+void free_connection(void) {
+ close(g_sockfd);
+ g_sockfd = -1;
+
+ /* the program should be at an end. If we're reconnecting, then at
+ the top level (main) we'd free, even though the main's sockfd is now
+ out-of-date, we can simply ignore that and take the result of socket
+ init */
+}
+
+/* always returns an accessible socket */
+int init_connection(char * serv, char * port) {
+ int status, sockfd;
+ struct addrinfo hints, * res;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
+
+ if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
+ fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
+ exit(1);
+ }
+
+ if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
+ { goto error; }
+ if (connect(sockfd, res->ai_addr, res->ai_addrlen))
+ { goto error; }
+
+ freeaddrinfo(res);
+
+ g_sockfd = sockfd;
+ return sockfd;
+error:
+ perror("init_connection");
+ exit(1);
+ __builtin_unreachable();
+}
+
+int main (int argc, char ** argv) {
+ char * argv0 = argv[0];
+ char * serv = SERV, * port = PORT, * name = "anonymous";
+
+ #define PREFIX_MAX 21
+ char raw[(2 << 9) + (2 << 13)];
+ char * sendbuf = raw + PREFIX_MAX, * recvbuf = raw + (2 << 9);
+ size_t sendlen = 0, sendmax = (2 << 9) - PREFIX_MAX,
+ recvlen = 0, recvmax = 2 << 13;
+
+ int sockfd;
+
+ while (++argv, --argc) {
+ if (streq(*argv, "-help")) {
+ printf("%s: HELP\n", argv0);
+ return 1;
+ }
+ if (argc - 1)
+ { --argc; ++argv; }
+ else {
+ printf("%s: %s requires argument\n", argv0, *argv);
+ return 1;
+ }
+ if (streq(*(argv-1), "-serv")) {
+ printf("serv: %s\n", *argv);
+ serv = *argv;
+ }
+ else if (streq(*(argv-1), "-port")) {
+ printf("port: %s\n", *argv);
+ }
+ else if (streq(*(argv-1), "-name")) {
+ printf("name: %s\n", *argv);
+ }
+ }
+
+ atexit(free_screen);
+ init_screen();
+ signal(SIGWINCH, handle_winch);
+ printw("Connecting to %s:%s as %s\n", serv, port, name);
+ refresh();
+ sockfd = init_connection(serv, port);
+ float interval = 1 / 30;
+ int ch;
+ time_t t;
+ struct tm * tm;
+ while (1) {
+ /* send */
+ ch = getch();
+ if (ch != -1 && ch != '\n') {
+ sendbuf[sendlen++] = ch;
+ } else if (ch == '\n') {
+ t = time(NULL);
+ tm = gmtime(&t);
+ strftime(sendbuf - PREFIX_MAX, PREFIX_MAX, "<%Y/%m/%d %H:%M:%S ", tm);
+ sendbuf[sendlen++] = '\n';
+ send(sockfd, sendbuf - PREFIX_MAX, sendlen + PREFIX_MAX, 0);
+ sendlen = 0;
+ memset(sendbuf, 0, sendlen);
+ }
+ /* recv */
+ recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT);
+ /* render */
+ clear();
+ mvaddnstr(0, 0, recvbuf, recvlen);
+ mvaddnstr(g_row - 1, 0, sendbuf, sendlen);
+ refresh();
+ /* sleep */
+ sleep(interval);
+ }
+}
--- /dev/null
+#!/usr/bin/ruby
+# written by emil
+#
+# short guide
+#
+# -serv specifies server,
+# -port specifies port,
+# -name specifies your display name,
+#
+# Type "BYE" to stop it after it connects to the moon.
+# Type Enter and nothing else to refresh the messages.
+# Type messages and send them, hopefully everyone hears you just fine.
+#
+# h = { "BYE" => 1, "QUIT" => 1, "SAY" => 2, "CLEAR" => 3, "NICK" => 4, "RECONNECT" => 5, "DELAY" => 6 }
+# 1 exits,
+# 2 says something verbatim (including a raw newline),
+# 3 clears the terminal,
+# 4 sets the nick (needs arg),
+# 5 reconnects,
+# 6 sets delays ( needs arg)
+
+require 'socket'
+require 'readline'
+
+@serv = '7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion'
+@port = 50000
+
+@name = "anonymous"
+
+def read_from(socket)
+ begin # probably too large of a read size...
+ puts socket.read_nonblock(2 << 16)
+ rescue
+ end
+end
+
+def post(socket, msg)
+ update_prefix
+ socket.puts(@prefix + msg)
+end
+
+def update_prefix
+ date = Time.now.utc.strftime("%Y/%m/%d %k:%M:%S")
+ @prefix = "<#{date} #{@name}> "
+end
+
+def connect
+ puts "Connecting to " + @serv + ":" + @port.to_s + " as " + @name
+ socket = TCPSocket.new(@serv, @port)
+ if not socket
+ puts "Failed to connect."
+ exit 1
+ end
+ return socket
+end
+
+def main
+ begin
+ ARGV.each_with_index do |element,index|
+ if element == "-name"
+ @name = ARGV[index + 1]
+ end
+ if element == "-serv"
+ @serv = ARGV[index + 1]
+ end
+ if element == "-port"
+ @port = ARGV[index + 1].to_i
+ end
+ end
+ rescue
+ end
+ socket = connect
+ begin
+ delay = 0
+
+ h = { "BYE" => 1, "QUIT" => 1, "SAY" => 2, "CLEAR" => 3, "NICK" => 4, "RECONNECT" => 5, "DELAY" => 6 }
+
+ update_prefix
+ while msg = Readline.readline(@prefix, true)
+ if not msg.empty?
+ msg.strip!
+ word = msg.split(/^([\w]*)$|^([\w]*) (.*)$/)[1..]
+ case h[word[0]]
+ when h["BYE"]
+ post(socket, "BYE")
+ exit 0
+ when h["CLEAR"]
+ puts "\e[1;1H\e[2J"
+ when h["NICK"]
+ if not word[1].empty?
+ @name = word[1]
+ end
+ when h["RECONNECT"]
+ socket.close
+ socket = connect
+ when h["DELAY"]
+ delay = word[1].to_i
+ when h["SAY"]
+ if not word.length > 1
+ word.push("")
+ end
+ post(socket, word[1])
+ else
+ post(socket, msg)
+ end
+ end
+
+ read_from(socket)
+ sleep delay
+ update_prefix
+ end
+ ensure
+ puts "\nSTOPPING NOW."
+ socket.close
+ end
+end
+
+main