[
  {
    "path": ".gitignore",
    "content": ".emacs\nnanochat\nvar\n\n/autom4te.cache/\n/config/\nMakefile\nMakefile.in\naclocal.m4\napp.info\ncompile\nconfig.h\nconfig.h.in\nconfig.log\nconfig.status\nconfigure\nconfigure.scan\ncoverage_report\ndepcomp\ninstall-sh\nlibtool\nm4\nmissing\nstamp-h1\n*~\n*.html\n*.log\n*.o\n*.la\n*.so*\n*.a\n.deps\n*.zip\n*.lo\n*.gcno\n*.gcda\nconfig.guess\nconfig.sub\n"
  },
  {
    "path": "Makefile.am",
    "content": "SUBDIRS = lib/nanomsg lib/vedis lib/sodium lib/parson lib/base64 src\n\ninstall-data-hook:\n\t@echo bin dir $(bindir)\n\t@echo \"=============================================================\"\n\t@echo \"|            NanoChat was successfully installed \"\n\t@echo \"-------------------------------------------------------------\"\n\t@echo \"| NanoChat was installed in $(bindir) directory. \"\n\t@echo \"| Just type 'nanochat --help' there to get started.\"\n\t@echo \"=============================================================\"\n"
  },
  {
    "path": "README.md",
    "content": "NanoChat\n=====\n\nNanoChat is a **peer-to-peer**, **end-to-end encrypted** and **discoverable** chat application\nthat can be used inside command line. It is totally server-less and every peer can discover\nother peers in the same subnet without having the network address of them in advance.\n\nIt can be compiled and works on both **Linux** and **OSX** system platforms.\n\nHow to use\n-----\n\nCall `nanochat` with desired flags. In order to see the list of available flags,\nuse `--help` as follows:\n\n```\nNanoChat help\n-------------\nAvaliable flags:\n    Help:          -h --help\n    Host IP:       -H --host {host IP}\n    Broadcast IP:  -B --broadcast {broadcast IP}\n    RPC Port:      -P --port {port}\n    Discoverable:  -d --discoverable\n    Secure:        -s --secure\n```\n\n- `--host {host IP}` flag is used to specify the network interface IP which you want to use. By default\nit is selected by NanoChat.\n- `--broadcast {broadcast IP}` flag is used to specify the network interface broadcast IP\nwhich you want to use. By default it is selected by NanoChat.\n- `--port` flag is used to specify the RPC port which is listening to answer chat requests after creating\nthe requested chat room. Its default value is `1991`.\n- `--discoverable` flag is used to let NanoChat to be disovered by other peers, otherwise it doesn't\nanswer to discovery packet and remains hidden from others.\n- `--secure` flag is used to make NanoChat secure for end to end message encryption with\nasymmetric/public-key cryptography.\n\n-----\n\nIn order to see available commands inside NanoChat use `/help` command as follows:\n\n```shell\n$ nanochat --discoverable\nNanoChat shell was started.\n>> /help\nAvailable commands:\n  /help                    prints this text\n  /probe                   find online peers\n  /list peers              list online peers\n  /list rooms              list availabe rooms\n  /connect {host} {port}   connect to remote client\n  /attach {room}           attach to room\n  /quit                    quit nanochat console\n>> ...\n```\n\n- `/probe` command discovers the subnet for other online NanoChat peers.\n- `/list peers` command list discovered online peers' host and port address.\n- `/list rooms` command list available rooms.\n- `/connect {host} {port}` command connects you to other peers.\n- `/attach {room}` command is used to enter to a created room by other peers.\n- `/quit` command closes the NanoChat console.\n\nInstallation\n-----\n\nNanoChat uses [GNU Autotools](https://en.wikipedia.org/wiki/GNU_Build_System). So you need to have them installed in\nyour system. Also it needs [GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection)\nor [Clang](https://en.wikipedia.org/wiki/Clang),\n[GNU Make](https://www.google.co.uk/?ion=1&espv=2#q=gnu%20make) and\n[GNU Readline](https://en.wikipedia.org/wiki/GNU_Readline) library.\nOther dependencies are included in the `NanoChat/lib` directory and will be compiled and linked\nwith autotools automatically. So there is no need to get and build it manually.\n\n**Debian dependencies:**\n```\n$ apt-get install gcc make automake autoconf libreadline-dev\n```\n\n**Fedora dependencies:**\n```\n$ yum install gcc make automake autoconf libreadline-devel\n```\n\n**OSX dependencies:**\n```\n$ port install gcc make automake autoconf readline\n```\n\nAfter making sure that your system has required dependencies, clone the repo and follow installation steps:\n\n```\n$ git clone https://github.com/hamidreza-s/NanoChat.git\n$ cd NanoChat\n$ autoreconf -i\n$ ./configure\n$ make && make install\n...\n=============================================================\n|            NanoChat was successfully installed \n-------------------------------------------------------------\n| NanoChat was installed in /usr/local/bin directory. \n| Just type 'nanochat --help' there to get started.\n=============================================================\n```\n\nNow `nanochat` executable file is accessible in the path of your shell.\n\nHow it works\n-----\n\nMy main purpose for writing NanoChat is learning things, so I think it is\ngood to know for you how it works and what tools and protocols it uses.\n\n- For discovering other online peers in the same subnet it uses raw UDP broadcasting.\n- For inter-node communication it uses some [libnanomsg](http://nanomsg.org) scalibility protocols,\nfor instance REQREP protocol for RPC and PAIR for one to one chat.\n- For user's input/output multiplexing it uses [select](https://en.wikipedia.org/wiki/Select_(Unix))\nPOSIX-compliant syscall.\n- For storing user's information it uses [libvedis](https://vedis.symisc.net) embedded datastore engine.\n- For line-editing and history capabilities of commands it\nuses [libreadline](https://en.wikipedia.org/wiki/GNU_Readline).\n- For end to end encryption it uses [libsodium](https://github.com/jedisct1/libsodium) public-key cryptography.\n- For message serialization it uses [libparson](https://github.com/kgabis/parson) which is a JSON parser.\n- For encoding and decoding encrypted messages before sending over wire it uses base64 codec.\n\nContribution\n-----\n\nComments, contributions and patches are greatly appreciated.\n\nLicense\n-----\nThe MIT License (MIT).\n"
  },
  {
    "path": "TODO.md",
    "content": "TODO\n=====\n\n- peer rpc (req/rep protocol)\n- discovery (udp broadcasting)\n- one to one chat (pair protocol)\n- group chat (bus protocol)\n- integrated error handling\n- integrated logging and debug mode\n- include libnanomsg in deps directory\n- use autotools to be portable\n- use libreadline and ncurses to be TUI\n"
  },
  {
    "path": "configure.ac",
    "content": "AC_PREREQ([2.69])\nAC_INIT([nanochat], [0.1], [hamidreza.s@gmail.com])\nAM_INIT_AUTOMAKE([foreign])\n\nNANOMSG_VERSION=0.8-beta\nAC_SUBST([NANOMSG_VERSION])\n\nVEDIS_VERSION=1.2.6\nAC_SUBST([VEDIS_VERSION])\n\nSODIUM_VERSION=1.0.9\nAC_SUBST([SODIUM_VERSION])\n\nPARSON_VERSION=1.0.0\nAC_SUBST([PARSON_VERSION])\n\nBASE64_VERSION=1.0.0\nAC_SUBST([BASE64_VERSION])\n\nAC_PROG_CC\nAC_LANG([C])\n\nAC_HEADER_STDC\nAC_CHECK_HEADERS([stdio.h stdlib.h unistd.h getopt.h string.h time.h \\\n                  sys/select.h stdarg.h pthread.h pwd.h sys/socket.h \\\n                  arpa/inet.h ifaddrs.h])\n\nAC_TYPE_SIZE_T\nAC_CONFIG_FILES([lib/nanomsg/Makefile \\\n                 lib/vedis/Makefile \\\n\t\t lib/sodium/Makefile \\\n\t\t lib/parson/Makefile \\\n                 lib/base64/Makefile \\\n                 src/Makefile \\\n\t\t Makefile])\n\nAC_CANONICAL_HOST\ncase \"${host_os}\" in\n    *linux*)\n        # Define on Linux to enable all library features\n\tNC_OS_SPECIFIC_LDADD=\"-l:libnanomsg.a -l:libvedis.a -l:libsodium.a -l:libparson.a -l:libbase64.a\"\n\tNC_EXTRA_LDADD=-lanl \n        ;;\n    *freebsd*)\n        # Define on FreeBSD to enable all library features\n\tNC_OS_SPECIFIC_LDADD=\"-lnanomsg -lvedis -lsodium -lparson -lbase64\"\n\tNC_EXTRA_LDADD=\n        ;;\n    *darwin*)\n        # Define on Darwin to enable all library features\n\tNC_OS_SPECIFIC_LDADD=\"-lnanomsg -lvedis -lsodium -lparson -lbase64\"\n\tNC_EXTRA_LDADD=\n        ;;\n    *)\n        AC_MSG_ERROR([unsupported system: ${host_os}.])\n        ;;\nesac\nAC_SUBST([NC_OS_SPECIFIC_LDADD])\nAC_SUBST([NC_EXTRA_LDADD])\n\nAC_OUTPUT\n"
  },
  {
    "path": "lib/base64/.gitignore",
    "content": "base64-1.0.0\n"
  },
  {
    "path": "lib/base64/Makefile.am",
    "content": "all-local:\n\t@if [ ! -f ./base64-$(BASE64_VERSION)/lib/libbase64.a ]; then \\\n\t\ttar -zxf ./base64-$(BASE64_VERSION).tar.gz && \\\n\t\t\tcd ./base64-$(BASE64_VERSION)/src && \\\n\t\t\tgcc -c base64.c  -I../include && \\\n\t\t\tar -cvq libbase64.a base64.o && \\\n\t\t\tcd .. && \\\n\t\t\tmkdir -p lib && \\\n\t\t\tmv src/libbase64.a lib/libbase64.a; \\\n\tfi;\n\nclean-local:\n\trm -f ./base64-$(BASE64_VERSION)/src/base64.o\n\nuninstall-local:\n\trm -rf ./base64-$(BASE64_VERSION)\n"
  },
  {
    "path": "lib/nanomsg/.gitignore",
    "content": "nanomsg-0.8-beta\n"
  },
  {
    "path": "lib/nanomsg/Makefile.am",
    "content": "all-local:\n\t@if [ ! -f ./nanomsg-$(NANOMSG_VERSION)/lib/libnanomsg.a ]; then \\\n\t\ttar -zxf ./nanomsg-$(NANOMSG_VERSION).tar.gz && \\\n\t\t\tcd ./nanomsg-$(NANOMSG_VERSION) && \\\n\t\t\t./configure --prefix=`pwd` && \\\n\t\t\tmake && \\\n\t\t\tmake install; \\\n\tfi;\n\nclean-local:\n\tcd ./nanomsg-$(NANOMSG_VERSION) && \\\n\t\tmake clean\n\nuninstall-local:\n\tcd ./nanomsg-$(NANOMSG_VERSION) && \\\n\t\tmake uninstall\n"
  },
  {
    "path": "lib/parson/.gitignore",
    "content": "parson-1.0.0\n"
  },
  {
    "path": "lib/parson/Makefile.am",
    "content": "all-local:\n\t@if [ ! -f ./parson-$(PARSON_VERSION)/lib/libparson.a ]; then \\\n\t\ttar -zxf ./parson-$(PARSON_VERSION).tar.gz && \\\n\t\t\tcd ./parson-$(PARSON_VERSION)/src && \\\n\t\t\tgcc -c parson.c  -I../include && \\\n\t\t\tar -cvq libparson.a parson.o && \\\n\t\t\tcd .. && \\\n\t\t\tmkdir -p lib && \\\n\t\t\tmv src/libparson.a lib/libparson.a; \\\n\tfi;\n\nclean-local:\n\trm -f ./parson-$(PARSON_VERSION)/src/parson.o\n\nuninstall-local:\n\trm -rf ./parson-$(PARSON_VERSION)\n"
  },
  {
    "path": "lib/sodium/.gitignore",
    "content": "libsodium-1.0.9\n"
  },
  {
    "path": "lib/sodium/Makefile.am",
    "content": "all-local:\n\t@if [ ! -f ./libsodium-$(SODIUM_VERSION)/lib/libsodium.a ]; then \\\n\t\ttar -zxf ./libsodium-$(SODIUM_VERSION).tar.gz && \\\n\t\t\tcd ./libsodium-$(SODIUM_VERSION) && \\\n\t\t\t./configure --prefix=`pwd` && \\\n\t\t\tmake && \\\n\t\t\tmake install; \\\n\tfi;\n\nclean-local:\n\tcd ./libsodium-$(SODIUM_VERSION) && \\\n\t\tmake clean\n\nuninstall-local:\n\tcd ./libsodium-$(SODIUM_VERSION) && \\\n\t\tmake uninstall\n"
  },
  {
    "path": "lib/vedis/.gitignore",
    "content": "vedis-1.2.6\n"
  },
  {
    "path": "lib/vedis/Makefile.am",
    "content": "all-local:\n\t@if [ ! -f ./vedis-$(VEDIS_VERSION)/lib/libvedis.a ]; then \\\n\t\ttar -zxf ./vedis-$(VEDIS_VERSION).tar.gz && \\\n\t\t\tcd ./vedis-$(VEDIS_VERSION)/src && \\\n\t\t\tgcc -c vedis.c -I../include && \\\n\t\t\tar -cvq libvedis.a vedis.o && \\\n\t\t\tcd .. && \\\n\t\t\tmkdir -p lib && \\\n\t\t\tmv src/libvedis.a lib/libvedis.a; \\\n\tfi;\n\nclean-local:\n\trm -f ./vedis-$(VEDIS_VERSION)/src/vedis.o\n\nuninstall-local:\n\trm -rf ./vedis-$(VEDIS_VERSION)\n"
  },
  {
    "path": "src/Makefile.am",
    "content": "all-local:\n\tmkdir -p $(top_srcdir)/var\n\trm -f $(top_srcdir)/var/nanochat.conf\n\techo \"log_file=./var/nanochat.log\" >> $(top_srcdir)/var/nanochat.conf\n\ttouch $(top_srcdir)/var/nanochat.log\n\nclean-local:\n\trm -f $(top_srcdir)/var/nanochat.conf\n\trm -f $(top_srcdir)/var/nanochat.log\n\nAM_CFLAGS = -Wall \n\nAM_CFLAGS += -I$(top_srcdir)/lib/nanomsg/nanomsg-$(NANOMSG_VERSION)/include\nAM_CFLAGS += -L$(top_srcdir)/lib/nanomsg/nanomsg-$(NANOMSG_VERSION)/lib\n\nAM_CFLAGS += -I$(top_srcdir)/lib/vedis/vedis-$(VEDIS_VERSION)/include\nAM_CFLAGS += -L$(top_srcdir)/lib/vedis/vedis-$(VEDIS_VERSION)/lib\n\nAM_CFLAGS += -I$(top_srcdir)/lib/sodium/libsodium-$(SODIUM_VERSION)/include\nAM_CFLAGS += -L$(top_srcdir)/lib/sodium/libsodium-$(SODIUM_VERSION)/lib\n\nAM_CFLAGS += -I$(top_srcdir)/lib/parson/parson-$(PARSON_VERSION)/include\nAM_CFLAGS += -L$(top_srcdir)/lib/parson/parson-$(PARSON_VERSION)/lib\n\nAM_CFLAGS += -I$(top_srcdir)/lib/base64/base64-$(BASE64_VERSION)/include\nAM_CFLAGS += -L$(top_srcdir)/lib/base64/base64-$(BASE64_VERSION)/lib\n\nbin_PROGRAMS = nanochat\nnanochat_SOURCES = \\\n\tnc.h \\\n\tnc.c \\\n\tnc_param.c \\\n\tnc_netif.c \\\n\tnc_utils.c \\\n\tnc_names.c \\\n\tnc_udp.c \\\n\tnc_disco.c \\\n\tnc_rpc.c \\\n\tnc_shell.c \\\n\tnc_otoc.c \\\n\tnc_conf.c \\\n\tnc_log.c \\\n\tnc_dal.c \\\n\tnc_array.c \\\n\tnc_crypto.c \\\n\tnc_json.c \\\n\tnc_msg.c\n\nnanochat_LDADD = -lpthread -lreadline\nnanochat_LDADD += $(NC_OS_SPECIFIC_LDADD)\nnanochat_LDADD += $(NC_EXTRA_LDADD)\n"
  },
  {
    "path": "src/nc.c",
    "content": "#include \"nc.h\"\n\nint\nmain(int argc, char **argv)\n{\n  nc_opts opts;\n\n  /* @NOTE: conf and log must start first respectively */\n  nc_conf_start();\n  nc_log_start();\n  \n  nc_param_get_opts(&opts, argc, argv);\n\n  nc_dal_start(&opts);\n  nc_disco_start(&opts);\n  nc_rpc_start(&opts);\n  nc_crypto_start(&opts);\n  nc_shell_start(&opts);\n\n  return 0;\n}\n"
  },
  {
    "path": "src/nc.h",
    "content": "#ifndef NC_H\n#define NC_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <string.h>\n#include <time.h>\n#include <sys/select.h>\n#include <stdarg.h>\n#include <pthread.h>\n#include <pwd.h>\n\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <ifaddrs.h>\n\n#include <readline/readline.h>\n#include <readline/history.h>\n\n#include <nanomsg/nn.h>\n#include <nanomsg/reqrep.h>\n#include <nanomsg/pair.h>\n\n#include <vedis.h>\n#include <sodium.h>\n#include <parson.h>\n\n#define DISCO_PORT \"1881\" /* not changable */\n#define DEFAULT_RPC_PORT \"1991\"\n#define INIT_OTOC_PORT 1992\n\n#define HOST_MAX 20\n#define URL_MAX 30\n#define PORT_MAX 6\n#define OTOC_PORT_LEN 6\n#define NOW_STR_LEN 26\n#define CONF_KEY_LEN 1024\n#define CONF_VAL_LEN 1024\n\n/* shell commands */\n#define SCMD_NAME_LEN 32\n#define SCMD_MAX 10\n\n/* otoc shell commands */\n#define OCMD_NAME_LEN 32\n#define OCMD_MAX 10\n\n/* discovery */\n#define DCMD_BUF 64\n#define DCMD_LEN 8\n#define DCMD_CODE_LEN 2\n#define DCMD_PROBE_ACK_CODE \"00\"\n#define DCMD_PROBE_REQUEST_CODE \"01\"\n#define DCMD_PROBE_RESPONSE_CODE \"02\"\n#define DCMD_PROBE_TIMEOUT_SEC 3 \n\n/* rpc commands */\n#define RCMD_LEN 2\n#define RCMD_OTOC \"01\"\n\n/* one to one message types */\n#define OTOC_MTYPE_LEN 8\n#define OTOC_MTYPE_PKEY \"publ_key\" /* public key */\n#define OTOC_MTYPE_RTXT \"text_raw\" /* raw text */\n#define OTOC_MTYPE_STXT \"text_sec\" /* encrypted text */\n#define OTOC_MTYPE_RFLE \"file_raw\" /* raw file */\n#define OTOC_MTYPE_SFLE \"file_sec\" /* encrypted file */\n\n/* names */\n#define USERNAME_MAX 32\n#define HOSTNAME_MAX 32\n\n/* room */\n#define ROOMS_LIMIT 16\n\n/* global variablea */\nextern unsigned char my_publickey[crypto_box_PUBLICKEYBYTES];\nextern unsigned char my_secretkey[crypto_box_SECRETKEYBYTES];\nextern unsigned char peers_publickey\n                     [ROOMS_LIMIT]\n                     [crypto_box_PUBLICKEYBYTES];\n\ntypedef struct nc_opts {\n  char host[HOST_MAX];\n  char broadcast[HOST_MAX];\n  char port[PORT_MAX]; /* RPC port */\n  char url[URL_MAX];\n  int secure;\n  int verbose;\n  int discoverable;\n} nc_opts;\n\ntypedef struct nc_conf_rec {\n  char key[CONF_KEY_LEN];\n  char val[CONF_VAL_LEN];\n} nc_conf_rec;\n\ntypedef struct nc_shell_cmd {\n  int code;\n  char name[SCMD_NAME_LEN];\n  int (*func)(char *cmd, nc_opts *opts);\n} nc_shell_cmd;\n\ntypedef struct nc_otoc_cmd {\n  int code;\n  char name[OCMD_NAME_LEN];\n  int (*func)(char *cmd);\n} nc_otoc_cmd;\n\ntypedef struct nc_array {\n  char **array_ptr;\n  int array_len;\n  int string_len;\n} nc_array;\n\n/* --- parameters --- */\nvoid nc_param_get_opts(nc_opts *opts, int argc, char **argv);\n\n/* --- logger --- */\nvoid nc_log_start();\nvoid nc_log_write(const char *tag, const char *msg);\nvoid nc_log_writef(const char *tag, const char *msgf, ...);\n\n/* --- utilities --- */\nvoid nc_utils_print_help();\nint nc_utils_count_char(const char *str, const char chr);\nvoid nc_utils_now_str(char *time_str);\nvoid nc_utils_del_new_line(char *str);\nint nc_utils_has_new_line(char *str);\nvoid nc_utils_make_url(char *url, char *host, char *port);\nint nc_utils_get_rec_sockfd(int sock);\nvoid nc_utils_empty_string(char *str);\nvoid nc_utils_die(char *str);\n\n/* --- disco --- */\nvoid nc_disco_start(nc_opts *opts);\nint nc_disco_probe(nc_opts *opts);\n\n/* --- rpc --- */\nvoid nc_rpc_start(nc_opts *opts);\n\n/* --- shell --- */\nvoid nc_shell_start(nc_opts *opts);\nvoid nc_shell_register_cmd(char *name, int (*func)(char *cmd, nc_opts *opts));\n\n/* --- one to one chat --- */\nvoid nc_otoc_start(nc_opts *opts, int pair_raw_sock);\nvoid nc_otoc_register_cmd(char *name, int (*func)(char *cmd));\n\n/* --- config --- */\nvoid nc_conf_start();\nvoid nc_conf_get(nc_conf_rec *conf_rec);\n\n/* --- network interfaces --- */\nint nc_netif_get_addrs(char *inet, char *broadcast);\n\n/* --- udp --- */\nint nc_udp_send(char *ip, char *port, char *body, int broadcast);\n\n/* --- names --- */\nint nc_names_get_hostname(char *buf, int len);\nint nc_names_get_username(char *buf, int len);\n\n/* --- data access layer --- */\nvoid nc_dal_start(nc_opts *opts);\nint nc_dal_save_room(int room_code);\nint nc_dal_save_peer(char **record);\nint nc_dal_print_peers();\nint nc_dal_print_rooms();\n\n/* --- array --- */\nnc_array* nc_array_string_new(int array_len, int string_len);\nint nc_array_string_set(nc_array *array, int index, char *string);\nchar* nc_array_string_get(nc_array *array, int index);\nint nc_array_string_len(nc_array *array);\n\n/* --- crypto --- */\nvoid nc_crypto_start(nc_opts *opts);\n\n/* --- json --- */\nint nc_json_extract_otoc_msg(char **msg, char **type, int *original_body_len, char **body);\nint nc_json_make_otoc_msg(char **type, char **body, int original_body_len, char **msg);\n\n/* --- message --- */\nvoid nc_msg_encode(int head_len, int body_len, char *head, char *body, char **buf);\nvoid nc_msg_free(char *buf);\n\n#endif\n"
  },
  {
    "path": "src/nc_array.c",
    "content": "#include \"nc.h\"\n\nnc_array*\nnc_array_string_new(int array_len, int string_len)\n{\n  int i;\n  char **array_ptr = (char**) malloc(array_len * sizeof(char**));\n\n  for(i = 0; i < array_len; i++) {\n    array_ptr[i] = (char*) malloc(string_len * sizeof(char));\n  }\n  \n  nc_array *array = (nc_array*) malloc(sizeof(nc_array*));\n  array->array_ptr = array_ptr;\n  array->array_len = array_len;\n  array->string_len = string_len;\n\n  return array;\n}\n\nint\nnc_array_string_set(nc_array *array, int index, char *string)\n{\n  strncpy(array->array_ptr[index], string, array->string_len);\n  return 0;\n}\n\nchar*\nnc_array_string_get(nc_array *array, int index)\n{\n  return array->array_ptr[index];\n}\n\nint\nnc_array_string_len(nc_array *array)\n{\n  return array->array_len;\n}\n\nint\nnc_array_string_free(nc_array *array)\n{\n  int i;\n  for(i = 0; i < array->array_len; i++) {\n    free(array->array_ptr[i]);\n  }\n  free(array->array_ptr);\n  return 0;\n}\n"
  },
  {
    "path": "src/nc_conf.c",
    "content": "#include \"nc.h\"\n\nstatic FILE *conf_fp;\n\nvoid\nnc_conf_start()\n{\n  conf_fp = fopen(\"./var/nanochat.conf\", \"r\");\n}\n\nvoid\nnc_conf_get(nc_conf_rec *conf_rec)\n{\n  char *buf = NULL;\n  size_t buf_size = CONF_KEY_LEN + CONF_VAL_LEN + 2;\n\n  while(getline(&buf, &buf_size, conf_fp) != -1) {\n    \n    char *key_val;\n\n    if((key_val = strstr(buf, conf_rec->key)) != NULL) {\n      \n      strcpy(conf_rec->val, strchr(buf, '=') + 1);\n      \n      nc_utils_del_new_line(conf_rec->val);\n      \n      break;\n\n    }\n  }\n}\n"
  },
  {
    "path": "src/nc_crypto.c",
    "content": "#include \"nc.h\"\n\nunsigned char my_publickey[crypto_box_PUBLICKEYBYTES];\nunsigned char my_secretkey[crypto_box_SECRETKEYBYTES];\nunsigned char peers_publickey\n              [ROOMS_LIMIT]\n              [crypto_box_PUBLICKEYBYTES];\n\nvoid\nnc_crypto_start(nc_opts *opts)\n{\n  if(!opts->secure)\n    return;\n\n  nc_log_writef(\"info\", \"Crypto mode has been started.\");\n  crypto_box_keypair(my_publickey, my_secretkey);\n}\n"
  },
  {
    "path": "src/nc_dal.c",
    "content": "#include \"nc.h\"\n\n#define ROOMS_HASH_TABLE \"otoc\"\n#define PEERS_HASH_TABLE \"peers\"\n\nstatic vedis *vedis_ptr;\n\nint nc_dal_print_array(char* cmd);\n\nvoid\nnc_dal_start(nc_opts *opts)\n{\n  int rc;\n\n  rc = vedis_open(&vedis_ptr, \":mem:\");\n  if(rc != VEDIS_OK) {\n    nc_utils_die(\"nc:dal:start:open\");\n  }\n\n  nc_log_writef(\"info\", \"Data Access Layer has been started.\");\n}\n\nint\nnc_dal_save_room(int room_code)\n{\n  int rc;\n  char time_str[NOW_STR_LEN];\n\n  nc_utils_now_str(time_str);\n  \n  rc = vedis_exec_fmt(vedis_ptr, \"HSET %s '%d' '%s'\",\n\t\t      ROOMS_HASH_TABLE, room_code, time_str);\n\n  if(rc != VEDIS_OK) {\n    nc_utils_die(\"nc:dal:save:otoc\");\n  }\n\n  return 0;\n}\n\nint\nnc_dal_save_peer(char **record)\n{\n  int rc;\n  char time_str[NOW_STR_LEN];\n  \n  nc_utils_now_str(time_str);\n\n  rc = vedis_exec_fmt(vedis_ptr, \"HSET %s '%s' '%s'\",\n\t\t      PEERS_HASH_TABLE, *record, time_str);\n\n  if(rc != VEDIS_OK) {\n    nc_utils_die(\"nc:dal:set:peer\");\n  }\n\n  return 0;\n}\n\nint\nnc_dal_print_peers()\n{\n  char cmd[32] = \"HKEYS \";\n  strcat(cmd, PEERS_HASH_TABLE); \n  if(nc_dal_print_array(cmd))\n    fprintf(stdout, \"[-] No peers!\\n\");\n\n  return 0;\n}\n\nint\nnc_dal_print_rooms()\n{\n  char cmd[32] = \"HKEYS \";\n  strcat(cmd, ROOMS_HASH_TABLE);\n  if(nc_dal_print_array(cmd))\n    fprintf(stdout, \"[-] No rooms!\\n\");\n\n  return 0;\n}\nint\nnc_dal_print_array(char *cmd)\n{\n  int rc;\n  vedis_value *result_ptr;\n\n  rc = vedis_exec(vedis_ptr, cmd, -1);\n  if(rc != VEDIS_OK)\n    nc_utils_die(\"nc:dal:print:array:execute\");\n\n  rc = vedis_exec_result(vedis_ptr, &result_ptr);\n  if(rc != VEDIS_OK)\n    nc_utils_die(\"nc:dal:print:array:result\");\n\n  if(vedis_value_is_null(result_ptr)) {\n    \n    return 1;\n\n  } else if(vedis_value_is_array(result_ptr)) {\n\n    vedis_value *entry_ptr;\n\n    while((entry_ptr = vedis_array_next_elem(result_ptr)) != 0) {\n      \n      if(!vedis_value_is_null(entry_ptr)) {\n\n\tconst char *result_str = vedis_value_to_string(entry_ptr, 0);\n\tfprintf(stdout, \"[+] %s\\n\", result_str);\n\t\n      }\n    }\n\n  } else {\n\n    const char *result_str;\n    result_str = vedis_value_to_string(result_ptr, 0);\n    fprintf(stdout, \"[+] %s\\n\", result_str);\n    return 0;\n\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "src/nc_disco.c",
    "content": "#include \"nc.h\"\n\nstatic void\nhandle_request(int sock, struct sockaddr_in sock_remote_addr,\n\t       char *body, int recv_str_len, char *remote_addr,\n\t       int remote_port, nc_opts *opts);\n\nvoid*\nnc_disco_loop(void *opts)\n{\n  int sock;\n\n  struct sockaddr_in sock_local_addr;\n  struct sockaddr_in sock_remote_addr;\n  unsigned short sock_local_port;\n  unsigned short sock_remote_port;\n  \n  socklen_t sock_remote_len = sizeof(sock_remote_addr);\n\n  char recv_str[DCMD_BUF];\n  int recv_str_len;\n\n  sock_local_port = atoi(DISCO_PORT);\n  memset(&sock_local_addr, 0, sizeof(sock_local_addr));\n  sock_local_addr.sin_family = AF_INET;\n  sock_local_addr.sin_addr.s_addr = htonl(INADDR_ANY);\n  sock_local_addr.sin_port = htons(sock_local_port);\n\n  if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)\n    nc_utils_die(\"nc:disco:loop:sock\");\n\n  if(bind(sock, (struct sockaddr *) &sock_local_addr, sizeof(sock_local_addr)) < 0)\n    nc_utils_die(\"nc:disco:loop:bind\");\n\n  nc_log_writef(\"info\", \"Disco was started on port %s.\", DISCO_PORT);\n\n  for(;;) {\n    \n    if((recv_str_len = recvfrom(sock, recv_str, DCMD_BUF, 0,\n\t\t\t\t(struct sockaddr *) &sock_remote_addr,\n\t\t\t\t&sock_remote_len)) < 0)\n      nc_utils_die(\"nc:disco:loop:recvfrom\");\n\n    sock_remote_port = ntohs(sock_remote_addr.sin_port);\n    recv_str[recv_str_len] = '\\0';\n\n    /* do not handle requests initiated from myself */\n    if(strcmp(inet_ntoa(sock_remote_addr.sin_addr),\n\t      ((nc_opts *) opts)->host) != 0) {      \n\n      nc_log_writef(\"info\", \"Discovery from %s:%d got: %s\",\n\t\t    inet_ntoa(sock_remote_addr.sin_addr),\n\t\t    sock_remote_port,\n\t\t    recv_str);\n      \n      handle_request(sock, sock_remote_addr, recv_str, recv_str_len,\n\t\t     inet_ntoa(sock_remote_addr.sin_addr),\n\t\t     sock_remote_port,\n\t\t     opts);\n    }\n  }\n}\n\nint\nnc_disco_probe(nc_opts *opts)\n{\n  return nc_udp_send(opts->broadcast, DISCO_PORT,\n\t\t     DCMD_PROBE_REQUEST_CODE, 1);\n}\n\nvoid\nnc_disco_start(nc_opts *opts)\n{\n  if(!opts->discoverable)\n    return;\n  \n  nc_log_writef(\"info\", \"Discoverable mode has been started.\");\n  \n  pthread_t disco_loop;\n  pthread_create(&disco_loop, NULL, nc_disco_loop, opts);\n}\n\nvoid\nhandle_request(int sock, struct sockaddr_in sock_remote_addr,\n\t       char *body, int recv_str_len, char *remote_addr,\n\t       int remote_port, nc_opts *opts)\n{\n  char *body_payload;\n  int response_len = DCMD_CODE_LEN+ PORT_MAX + HOST_MAX + USERNAME_MAX + HOSTNAME_MAX + 3;\n  char response[response_len];\n  char username[USERNAME_MAX];\n  char hostname[HOSTNAME_MAX];\n\n  if(strncmp(body, DCMD_PROBE_REQUEST_CODE, DCMD_CODE_LEN) == 0) {\n    /* --- request --- */\n\n    nc_names_get_hostname(hostname, HOSTNAME_MAX);\n    nc_names_get_username(username, USERNAME_MAX);\n    \n    /* reply response to peer's DISCO_PORT with my IP and RPC PORT */\n    body_payload = body + DCMD_CODE_LEN;\n    strcpy(response, DCMD_PROBE_RESPONSE_CODE);\n    strcat(response, username);\n    strcat(response, \"@\");\n    strcat(response, hostname);\n    strcat(response, \"/\");\n    strcat(response, opts->host);\n    strcat(response, \":\");\n    strcat(response, opts->port);\n    nc_log_writef(\"info\", \"Incoming discovery (UDP) request. code: %s, body: %s\",\n\t\t  DCMD_PROBE_REQUEST_CODE, body_payload);\n\n    nc_udp_send(inet_ntoa(sock_remote_addr.sin_addr),\n\t\tDISCO_PORT, response, 0);\n    \n    /* reply ack to sock */\n    if(sendto(sock, DCMD_PROBE_ACK_CODE, DCMD_CODE_LEN, 0,\n\t      (struct sockaddr *) &sock_remote_addr,\n\t      sizeof(sock_remote_addr)) == -1)\n      nc_utils_die(\"nc:disco:handler_request:sendto\");\n    \n  } else if(strncmp(body, DCMD_PROBE_RESPONSE_CODE, DCMD_CODE_LEN) == 0) {\n    /* --- response --- */\n\n    /* receive RPC port as response */\n    body_payload = body + DCMD_CODE_LEN;\n    nc_log_writef(\"info\", \"Incoming discovery (UDP) response. code: %s, body: %s\",\n\t\t  DCMD_PROBE_RESPONSE_CODE, body_payload);\n\n    /* store available peers */\n    nc_dal_save_peer(&body_payload);\n    \n  } else {\n\n    /* unknown command */\n    nc_log_writef(\"info\", \"Incoming discovery (UDP) uknown command: %s\", body);\n    \n  }\n}\n"
  },
  {
    "path": "src/nc_json.c",
    "content": "#include \"nc.h\"\n\nint\nnc_json_make_otoc_msg(char **type, char **body, int original_body_len, char **msg)\n{\n  JSON_Value *root_value = json_value_init_object();\n  JSON_Object *root_object = json_value_get_object(root_value);\n  json_object_set_string(root_object, \"type\", *type);\n  json_object_set_string(root_object, \"body\", *body);\n  json_object_set_number(root_object, \"original_body_len\", original_body_len);\n  *msg = json_serialize_to_string(root_value);\n\n  return 0;\n}\n\nint\nnc_json_extract_otoc_msg(char **msg, char **type, int *original_body_len, char **body)\n{\n  JSON_Value *root_value;\n  JSON_Object *root_object;\n  root_value = json_parse_string(*msg);\n  root_object = json_value_get_object(root_value);\n  *type = json_object_get_string(root_object, \"type\");\n  *body = json_object_get_string(root_object, \"body\");\n  *original_body_len = json_object_get_number(root_object, \"original_body_len\");\n  \n  return 0;\n}\n"
  },
  {
    "path": "src/nc_log.c",
    "content": "#include \"nc.h\"\n\nstatic FILE *log_fp;\npthread_mutex_t log_lock;\n\nvoid\nnc_log_start()\n{\n  nc_conf_rec conf_rec = {\"log_file\"};\n  nc_conf_get(&conf_rec);\n  log_fp = fopen(conf_rec.val, \"a\");\n  pthread_mutex_init(&log_lock, NULL);\n}\n\nvoid\nnc_log_write(const char *tag, const char *msg)\n{\n  time_t now;\n  time(&now);\n\n  pthread_mutex_lock(&log_lock);\n  fprintf(log_fp, \"%s[%s]: %s\\n\", ctime(&now), tag, msg);\n  fflush(log_fp);\n  pthread_mutex_unlock(&log_lock);\n}\n\nvoid\nnc_do_log_writef(const char *tag, const char *msgf, va_list vargs)\n{\n  pthread_mutex_lock(&log_lock);\n  vfprintf(log_fp, msgf, vargs);\n  fprintf(log_fp, \"\\n\");\n  fflush(log_fp);\n  va_end(vargs);\n  pthread_mutex_unlock(&log_lock);\n}\n\nvoid\nnc_log_writef(const char *tag, const char *msgf, ...)\n{\n  va_list vargs;\n  int new_msgf_size;\n  char time_str[NOW_STR_LEN];\n\n  nc_utils_now_str(time_str);\n  new_msgf_size = strlen(time_str) + strlen(tag) + strlen(msgf) + 6;\n\n  char new_msgf[new_msgf_size];\n\n  strcpy(new_msgf, time_str);\n  strcat(new_msgf, \" [\");\n  strcat(new_msgf, tag);\n  strcat(new_msgf, \"] \");\n  strcat(new_msgf, msgf);\n\n  va_start(vargs, msgf);\n  nc_do_log_writef(tag, new_msgf, vargs);\n}\n"
  },
  {
    "path": "src/nc_msg.c",
    "content": "#include \"nc.h\"\n\nvoid\nnc_msg_encode(int head_len, int body_len, char *head, char *body, char **buf)\n{\n  *buf = (char*) malloc(head_len + body_len + 3);\n  sprintf(*buf, \"%d %s %d %s\", head_len, head, body_len, body);\n}\n\nvoid\nnc_msg_free(char *buf)\n{\n  free(buf);\n}\n"
  },
  {
    "path": "src/nc_names.c",
    "content": "#include \"nc.h\"\n\nint\nnc_names_get_hostname(char *buf, int len)\n{\n  gethostname(buf, len);\n  return 0;\n}\n\nint\nnc_names_get_username(char *buf, int len)\n{\n  struct passwd *pw;\n  uid_t uid;\n\n  uid = geteuid();\n  pw = getpwuid(uid);\n  if(pw) {\n    strcpy(buf, pw->pw_name);\n    return 0;\n  }\n\n  return 1;\n}\n"
  },
  {
    "path": "src/nc_netif.c",
    "content": "#include \"nc.h\"\n\nint\nnc_netif_get_addrs(char *inet, char *broadcast)\n{\n  struct ifaddrs *ifap, *ifa;\n  struct sockaddr_in *sa;\n  struct sockaddr_in *bc;\n  \n  getifaddrs(&ifap);\n  for(ifa = ifap; ifa; ifa = ifa->ifa_next) {\n\n    if(ifa->ifa_addr->sa_family == AF_INET) {\n\n      sa = (struct sockaddr_in *) ifa->ifa_addr;\n      bc = (struct sockaddr_in *) ifa->ifa_broadaddr; /* ifa->ifa_dstaddr; */ \n      \n      if((strncmp(ifa->ifa_name, \"en0\", 3) == 0) ||\n\t (strncmp(ifa->ifa_name, \"en1\", 4) == 0) ||\n\t (strncmp(ifa->ifa_name, \"em0\", 3) == 0) ||\n\t (strncmp(ifa->ifa_name, \"eth0\", 4) == 0)) {\n\t\n\tstrcpy(inet, inet_ntoa(sa->sin_addr));\n\tstrcpy(broadcast, inet_ntoa(bc->sin_addr));\n\t\n\tfreeifaddrs(ifap);\n\treturn 0;\n      }\n    }\n  }\n\n  freeifaddrs(ifap);\n  return 1;\n}\n"
  },
  {
    "path": "src/nc_otoc.c",
    "content": "#include \"nc.h\"\n\nstatic nc_otoc_cmd cmds[OCMD_MAX];\nstatic int cmd_current_code = 0;\n\nstatic int func_cmd_help(char *cmd);\nstatic int func_cmd_leave(char *cmd);\n\nvoid\nnc_otoc_start(nc_opts *opts, int pair_raw_sock)\n{\n  int shell_fd_sock;\n  int pair_fd_sock;\n\n  fd_set readfds;\n  int maxfds;\n  char time_str[NOW_STR_LEN];\n\n  enum {init, none,\n\ttext_sent, text_received,\n\tpkey_sent, pkey_received} last_action = init;\n\n  shell_fd_sock = fileno(stdin);\n  pair_fd_sock = nc_utils_get_rec_sockfd(pair_raw_sock);\n  maxfds = pair_fd_sock + 1;\n\n  /* --- start of sending public key --- */\n  if(opts->secure) {\n\n    char *msg = NULL;\n    char *msg_type = OTOC_MTYPE_PKEY;\n    char *msg_body = NULL;\n\n    /* --- start of encoding public key --- */\n    const char *plain_pkey = (const char*) my_publickey;\n    int plain_pkey_len = crypto_box_PUBLICKEYBYTES;\n    char encoded_pkey[Base64encode_len(plain_pkey_len)];\n    Base64encode(encoded_pkey, plain_pkey, plain_pkey_len);\n    nc_log_writef(\"debug\", \"encode my public key: %s\", encoded_pkey);\n    /* --- end of encoding public key --- */\n\n    msg_body = encoded_pkey;\n    nc_json_make_otoc_msg(&msg_type, &msg_body, plain_pkey_len, &msg);\n    nn_send(pair_raw_sock, msg, strlen(msg), 0);\n    nc_log_writef(\"debug\", \"one to one chat sent public key: %s\", msg);\n    last_action = pkey_sent;\n    \n  }\n  /* --- end of sending public key --- */\n  \n  /* --- start of shel command registration --- */\n  nc_otoc_register_cmd(\"/help\", func_cmd_help);\n  nc_otoc_register_cmd(\"/leave\", func_cmd_leave);\n  /* --- end of shell commmand registration --- */\n\n  for(;;) {\n\n    switch(last_action) {\n    case init:\n    case pkey_sent:\n      fprintf(stdout,\n\t      \">> Entering (room code %d) ...\\n\",\n\t      pair_raw_sock);\n      fprintf(stdout, \">>> \");\n      fflush(stdout); \n      break;\n    case text_received: \n      fprintf(stdout, \">>> \");\n      fflush(stdout); \n      break;\n    case text_sent: \n      fprintf(stdout, \">>> \"); \n      fflush(stdout); \n      break;\n    case none:\n      fprintf(stdout, \">>> \");\n      fflush(stdout);\n      break;\n    case pkey_received:\n      break;\n    }\n\n    FD_ZERO(&readfds);\n    FD_SET(pair_fd_sock, &readfds);\n    FD_SET(shell_fd_sock, &readfds);\n\n    select(maxfds, &readfds, NULL, NULL, NULL);\n\n    if(FD_ISSET(shell_fd_sock, &readfds)) {      \n\n      char *buf = NULL;\n      size_t buf_sz = 1024;\n      int i;\n\n      nc_utils_now_str(time_str);      \n      getline(&buf, &buf_sz, stdin);\n\n      if(buf[0] == '\\n') {\n\t\n\tlast_action = none;\n\n      } else if(buf[0] == '/') {\n\n\tnc_utils_del_new_line(buf);\n\tfor(i = 0; i < cmd_current_code; i++) {\n\t  \n\t  if(strstr(buf, cmds[i].name)) {\n\n\t    if(cmds[i].func(buf) < 0) {\n\t      return;\n\t    }\n\n\t    break;\n\t  }\n\t}\n\n\tlast_action = none;\n\n      } else {\n\n\tif(opts->secure) {\n\n\t  /* --- make ciphermsg with key pairs --- */\n\t  int ciphermsg_len = crypto_box_MACBYTES + strlen(buf);\n\t  unsigned char nonce[crypto_box_NONCEBYTES];\n\t  unsigned char ciphermsg[ciphermsg_len];\n\n\t  /* @TODO: use random nonce */\n\t  //randombytes_buf(nonce, sizeof nonce);\n\t  memset(nonce, '\\0', sizeof nonce);\n\t  \n\t  crypto_box_easy(ciphermsg, (const unsigned char*) buf,\n\t  \t\t  strlen(buf), nonce,\n\t  \t\t  peers_publickey[pair_raw_sock], my_secretkey);\n\n\t  /* --- make msg encoded with base64  --- */\n\t  const char *plain_ciphermsg = (const char*) ciphermsg;\n\t  int plain_ciphermsg_len = ciphermsg_len;\n\t  int encoded_ciphermsg_len = Base64encode_len(plain_ciphermsg_len);\n\t  char encoded_ciphermsg[encoded_ciphermsg_len];\n\n\t  Base64encode(encoded_ciphermsg, plain_ciphermsg, plain_ciphermsg_len);\n\t  nc_log_writef(\"debug\", \"encode ciphermsg: %s\", encoded_ciphermsg);\n\n\t  /* --- serialize msg with json --- */\n\t  char *msg = NULL;\n\t  char *msg_type = OTOC_MTYPE_STXT;\n\t  char *msg_body = NULL;\n\n\t  msg_body = encoded_ciphermsg;\n\t  nc_json_make_otoc_msg(&msg_type, &msg_body, strlen(buf), &msg);\n\t  nc_log_writef(\"debug\", \"serialize encoded ciphermsg: %s\", msg);\n\t  \n\t  /* --- send msg --- */\n\t  nn_send(pair_raw_sock, msg, strlen(msg), 0);\n\n\t} else {\n\n\t  char *msg = NULL;\n\t  char *msg_type = OTOC_MTYPE_RTXT;\n\n\t  nc_json_make_otoc_msg(&msg_type, &buf, strlen(buf), &msg);\n\t  nn_send(pair_raw_sock, msg, strlen(msg), 0);\n\t  fprintf(stdout, \"[%s] >>> %s\", time_str, buf);\n\t  fflush(stdout);\n\t  nc_utils_del_new_line(buf);\n\t  nc_log_writef(\"debug\", \"one to one chat sent: %s\", buf);\n\t  nc_utils_empty_string(buf);\n\t  last_action = text_sent;\n\n\t}\n\n      }\n      \n    } else if(FD_ISSET(pair_fd_sock, &readfds)) {\n\n      char *buf = NULL;\n      char *msg_body = NULL;\n      char *msg_type = NULL;\n      int original_msg_body_len;\n      \n      nn_recv(pair_raw_sock, &buf, NN_MSG, 0);\n      nc_json_extract_otoc_msg(&buf, &msg_type, &original_msg_body_len, &msg_body);\n\n      nc_utils_del_new_line(buf);\n      nc_log_writef(\"debug\", \"one to one chat received: %s\", msg_type);\n      nn_freemsg(buf);\n      \n      if(strncmp(msg_type, OTOC_MTYPE_PKEY, OTOC_MTYPE_LEN) == 0) {\n\n\t/* public key message */\n\n\t/* --- start of decoding public key --- */\n\tint plain_pkey_len = Base64decode_len(msg_body);\n\tchar plain_pkey[plain_pkey_len];\n\n\tBase64decode(plain_pkey, (const char*) msg_body);\n\tstrncpy(peers_publickey[pair_raw_sock], plain_pkey, crypto_box_PUBLICKEYBYTES);\n\tnc_log_writef(\"debug\", \"decoded peer's public key was stored.\");\n\t/* --- end of decoding public key --- */\n\t\n\tlast_action = pkey_received;\n\t\n      } else if(strncmp(msg_type, OTOC_MTYPE_RTXT, OTOC_MTYPE_LEN) == 0) {\n\n\t/* raw text message */\n\n\tnc_utils_now_str(time_str);\n\tfprintf(stdout, \"\\r[%s] <<< %s\", time_str, msg_body);\n\tfflush(stdout);\n\tlast_action = text_received;\n\n      } else if(strncmp(msg_type, OTOC_MTYPE_STXT, OTOC_MTYPE_LEN) == 0) {\n\n\t/* secure text message */\n\n\tif(!opts->secure) {\n\n\t  char *alert =\n\t    \"\\r[%s] <<< { ... encrypted message ... }\\n\"\n\t    \"==========================================\\n\"\n\t    \"Your peer uses NanoChat undre secure mode.\\n\"\n\t    \"Use -s flag to see his encrypted messages.\\n\"\n\t    \"==========================================\\n\";\n\n\t  nc_utils_now_str(time_str);\n\t  fprintf(stdout, alert, time_str);\n\t  fflush(stdout);\n\t  last_action = text_received;\n\n\t} else {\n\n\t  /* --- decode  secure msg body --- */\n\t  int plain_ciphermsg_len = Base64decode_len(msg_body);\n\t  char plain_ciphermsg[plain_ciphermsg_len];\n\n\t  Base64decode(plain_ciphermsg, (const char*) msg_body);\n\n\t  /* --- decrypt ciphermsg --- */\n\n\t  int decrypted_len = original_msg_body_len;\n\t  int ciphermsg_len = crypto_box_MACBYTES + decrypted_len;\n\t  unsigned char decrypted[decrypted_len];\n\t  unsigned char nonce[crypto_box_NONCEBYTES];\n\t  memset(nonce, '\\0', sizeof nonce);\n\n\t  crypto_box_open_easy(decrypted, plain_ciphermsg, ciphermsg_len, nonce,\n\t\t\t       peers_publickey[pair_raw_sock], my_secretkey);\n\n\t  /* --- show decrypted msg body --- */\n\n\t  decrypted[original_msg_body_len] = '\\0';\n\t\n\t  nc_utils_now_str(time_str);\n\t  fprintf(stdout, \"\\r[%s] <<< %s\", time_str, decrypted);\n\t  fflush(stdout);\n\t  last_action = text_received;\n\n\t}\n      }\n    }\n    \n  }\n\n}\n\nvoid\nnc_otoc_register_cmd(char *cmd_name, int (*func)(char *cmd))\n{\n  cmds[cmd_current_code].code = cmd_current_code;\n  cmds[cmd_current_code].func = func;\n  strcpy(cmds[cmd_current_code].name, cmd_name);\n\n  cmd_current_code++;\n}\n\nint\nfunc_cmd_help(char *cmd)\n{\n  printf(\"Available commands:\\n\"\n\t \"  /help                prints this text\\n\"\n\t \"  /leave               leave current room\\n\"\n\t );\n  return 0;\n}\n\nint\nfunc_cmd_leave(char *cmd)\n{\n  printf(\"leaving room ...\\n\");\n  return -1;\n}\n"
  },
  {
    "path": "src/nc_param.c",
    "content": "#include \"nc.h\"\n\nvoid\nnc_param_get_opts(nc_opts *opts, int argc, char **argv)\n{\n  int c;\n  int rc;\n  \n  opts->host[0] = '\\0';\n  opts->broadcast[0] = '\\0';\n  opts->port[0] = '\\0';\n  opts->url[0] = '\\0';\n  opts->verbose = 0;\n  opts->secure = 0;\n  opts->discoverable = 0;\n  \n  for(;;) {\n    \n    static struct option long_options[] = {\n      {\"verbose\", no_argument, 0, 'v'},\n      {\"secure\", no_argument, 0, 's'},\n      {\"discoverable\", no_argument, 0, 'd'},\n      {\"help\", no_argument, 0, 'h'},\n      {\"host\", required_argument, 0, 'H'},\n      {\"broadcast\", required_argument, 0, 'B'},\n      {\"port\", required_argument, 0, 'P'},\n      {0, 0, 0, 0}\n    };\n\n    int option_index = 0;\n\n    c = getopt_long(argc, argv, \"vsdhH:P:\", long_options, &option_index);\n\n    if(c == -1 || c == 0)\n      break;\n\n    switch(c) {\n\n    case 'v':\n      opts->verbose = 1;\n      break;\n\n    case 's':\n      opts->secure = 1;\n      break;\n\n    case 'd':\n      opts->discoverable = 1;\n      break;\n      \n    case 'h':\n      nc_utils_print_help();\n      exit(0);\n      \n    case 'H':\n      if(strlen(optarg) >= HOST_MAX) {\n\tfprintf(stderr, \"Error: Host is to long!\\n\");\n\texit(1);\n      }\n      strcpy(opts->host, optarg);\n      break;\n\n    case 'B':\n      if(strlen(optarg) >= HOST_MAX) {\n\tfprintf(stderr, \"Error: Broadcast is to long!\\n\");\n\texit(1);\n      }\n      strcpy(opts->broadcast, optarg);\n      break;\n      \n    case 'P':\n      if(strlen(optarg) >= PORT_MAX) {\n\tfprintf(stderr, \"Error: Port can not be more than 5 character!\\n\");\n\texit(1);\n      }\n      strcpy(opts->port, optarg);\n      break;\n\n    case '?':\n    default:\n      break;\n    }\n  }\n\n  if(!opts->discoverable) {\n    if(opts->host[0] == '\\0') {\n\n      rc = nc_netif_get_addrs(opts->host, opts->broadcast);\n      if(rc != 0) {\n\tfprintf(stderr, \"Error: no network interface was found!\\n\");\n\texit(0);\n      }\n      \n    }\n\n  } else {\n\n    if(opts->host[0] == '\\0' || opts->broadcast[0] == '\\0') {\n      \n      rc = nc_netif_get_addrs(opts->host, opts->broadcast);\n      if(rc != 0) {\n\tfprintf(stderr, \"Error: no network interface was found!\\n\");\n\texit(0);\n      }\n    }\n  }\n\n  if(opts->port[0] == '\\0') {\n    strcpy(opts->port, DEFAULT_RPC_PORT);\n  }\n\n  nc_utils_make_url(opts->url, opts->host, opts->port);\n}\n\n"
  },
  {
    "path": "src/nc_rpc.c",
    "content": "#include \"nc.h\"\n\nvoid*\nnc_rpc_loop(void *void_opts)\n{\n  nc_opts *opts = (nc_opts*) void_opts;\n  int sock = nn_socket(AF_SP, NN_REP);\n  nn_bind(sock, opts->url);\n  nc_log_writef(\"info\", \"RPC has been started on URL: %s.\", opts->url);\n  \n  for(;;) {\n    char *buf = NULL;\n    int bytes = nn_recv(sock, &buf, NN_MSG, 0);\n    int cmp_rc;\n    \n    nc_log_writef(\"info\", \"Incoming RPC request: %s.\", buf);\n\n    if(strncmp(buf, RCMD_OTOC, RCMD_LEN) == 0) {\n\n      /* one to one chat */\n      \n      char otoc_url[URL_MAX];\n      static int otoc_port_init = INIT_OTOC_PORT;\n      char otoc_port[OTOC_PORT_LEN];\n      int otoc_sock = nn_socket(AF_SP, NN_PAIR);\n\n      snprintf(otoc_port, OTOC_PORT_LEN, \"%d\", otoc_port_init++);\n      nc_utils_make_url(otoc_url, opts->host, otoc_port);\n      nn_bind(otoc_sock, otoc_url);\n\n      nc_log_writef(\"info\", \"New oto chat was connected on URL: %s.\", otoc_url);\n      nn_send(sock, otoc_port, OTOC_PORT_LEN, 0);\n\n      nc_dal_save_room(otoc_sock);\n      \n      fprintf(stdout, \"\\r[*] You have new room. type '/list rooms'.\\n>> \");\n      fflush(stdout);\n\n    } else {\n\n      /* unknown command */\n      \n      nn_send(sock, buf, bytes, 0);\n    }\n\n    nc_utils_empty_string(buf);\n    nn_freemsg(buf);\n  }\n  return NULL;\n}\n\nvoid\nnc_rpc_start(nc_opts *opts)\n{\n  pthread_t rpc_loop;\n  pthread_create(&rpc_loop, NULL, nc_rpc_loop, opts);\n}\n"
  },
  {
    "path": "src/nc_shell.c",
    "content": "#include \"nc.h\"\n\nstatic nc_shell_cmd cmds[SCMD_MAX];\nstatic int cmd_current_code = 0;\nstatic int current_room_sock;\n\nstatic int func_cmd_help(char *cmd, nc_opts *opts);\nstatic int func_cmd_ping(char *cmd, nc_opts *opts);\nstatic int func_cmd_quit(char *cmd, nc_opts *opts);\nstatic int func_cmd_connect(char *cmd, nc_opts *opts);\nstatic int func_cmd_attach(char *cmd, nc_opts *opts);\nstatic int func_cmd_probe(char *cmd, nc_opts *opts);\nstatic int func_cmd_list(char *cmd, nc_opts *otps);\n\nvoid\nnc_shell_start(nc_opts *opts)\n{\n  fprintf(stdout, \"NanoChat shell was started.\\n\");\n\n  /* --- start of shell command registeration --- */\n  nc_shell_register_cmd(\"/help\", func_cmd_help);\n  nc_shell_register_cmd(\"/ping\", func_cmd_ping);\n  nc_shell_register_cmd(\"/quit\", func_cmd_quit);\n  nc_shell_register_cmd(\"/connect\", func_cmd_connect);\n  nc_shell_register_cmd(\"/attach\", func_cmd_attach);\n  nc_shell_register_cmd(\"/probe\", func_cmd_probe);\n  nc_shell_register_cmd(\"/list\", func_cmd_list);\n  /* --- end of shell command registration --- */\n  \n  for(;;) {\n    \n    char *buf = NULL;\n    int i;\n    int found;\n\n    found = 0;\n    buf = readline(\">> \");\n\n    if(buf[0] == 0) {\n\n      found = 1;\n\n    } else {\n\n      nc_utils_del_new_line(buf);\n      add_history(buf);\n      for(i = 0; i < cmd_current_code; i++) {\n\t\n\tif(strstr(buf, cmds[i].name)) {\n\t  \n\t  if(cmds[i].func(buf, opts) < 0) {\n\t    return;\n\t  }\n\t  \n\t  found = 1;\n\t  break;\n\t}\n      \n      }\n      \n    }\n\n    if(!found)\n      printf(\"Command not found!\\n\"\n\t     \"Type '/help' to list available commands.\\n\");\n    \n  }\n}\n\nvoid\nnc_shell_register_cmd(char *cmd_name, int (*func)(char *cmd, nc_opts *opts))\n{\n  cmds[cmd_current_code].code = cmd_current_code;\n  cmds[cmd_current_code].func = func;\n  strcpy(cmds[cmd_current_code].name, cmd_name);\n  \n  cmd_current_code++;\n}\n\nint\nfunc_cmd_help(char *cmd, nc_opts *otps)\n{\n  printf(\"Available commands:\\n\"\n\t \"  /help                    prints this text\\n\"\n\t \"  /probe                   find online peers\\n\"\n\t \"  /list peers              list online peers\\n\"\n\t \"  /list rooms              list availabe rooms\\n\"\n\t \"  /connect {host} {port}   connect to remote client\\n\"\n\t \"  /attach {room}           attach to room\\n\"\n\t \"  /quit                    quit nanochat console\\n\"\n\t );\n\n  return 0;\n}\n\nint\nfunc_cmd_ping(char *cmd, nc_opts *opts)\n{\n  printf(\"pong\\n\");\n  return 0;\n}\n\nint\nfunc_cmd_quit(char *cmd, nc_opts *opts)\n{\n  printf(\"Bye!\\n\");\n  return -1;\n}\n\nint\nfunc_cmd_attach(char *cmd, nc_opts *opts)\n{\n  int pair_raw_sock;\n  int rc;\n\n  rc = sscanf(cmd, \"/attach %d\", &pair_raw_sock);\n\n  if(rc != 1) {\n    printf(\"Error: correct format is 'attach room'.\\n\");\n  }\n\n  nc_log_writef(\"info\", \"Attached to room code: %d\", pair_raw_sock);\n\n  current_room_sock = pair_raw_sock;\n  nc_otoc_start(opts, pair_raw_sock);\n\n  return 0;\n}\n\nint\nfunc_cmd_connect(char *cmd, nc_opts *opts)\n{\n  char host_req[HOST_MAX];\n  char port_req[PORT_MAX];\n  char url_req[URL_MAX];\n\n  char host_pair[HOST_MAX];\n  char port_pair[PORT_MAX];\n  char url_pair[URL_MAX];\n\n  int rc;  \n  char *buf = NULL;\n  int sock_req;\n  int sock_pair;\n\n  rc = sscanf(cmd, \"/connect %s %s\", host_req, port_req);\n\n  if(rc != 2) {\n    fprintf(stdout, \"Error: correct format is 'connect host port'.\\n\");\n    return 1;\n  }\n\n  nc_utils_make_url(url_req, host_req, port_req);\n\n  nc_log_writef(\"info\", \"One to one chat request to %s was issued.\", url_req);\n\n  sock_req = nn_socket(AF_SP, NN_REQ);\n  nn_connect(sock_req, url_req);\n  nn_send(sock_req, RCMD_OTOC, RCMD_LEN, 0);\n  nn_recv(sock_req, &buf, NN_MSG, 0);\n  nn_shutdown(sock_req, 0);\n\n  strcpy(host_pair, host_req);\n  strncpy(port_pair, buf, OTOC_PORT_LEN);\n  nn_freemsg(buf);\n\n  nc_utils_make_url(url_pair, host_pair, port_pair);\n\n  nc_log_writef(\"info\", \"One to one chat request was handshaked in %s.\", url_pair);\n\n  sock_pair = nn_socket(AF_SP, NN_PAIR);\n  nn_connect(sock_pair, url_pair);\n  \n  current_room_sock = sock_pair;\n  nc_otoc_start(opts, sock_pair);\n\n  return 0;\n}\n\nint\nfunc_cmd_probe(char *cmd, nc_opts *opts)\n{\n  int rc;\n  int i;\n  \n  nc_log_writef(\"info\", \"User requested probe command.\");\n  rc = nc_disco_probe(opts);\n\n  fprintf(stdout, \"Starting to probing \");\n  fflush(stdout);\n  for(i = 0; i < DCMD_PROBE_TIMEOUT_SEC; i++) {\n    sleep(1);\n    fprintf(stdout, \".\");\n    fflush(stdout);\n  }\n  fprintf(stdout, \"\\n\");\n\n  if(rc > 0)\n    fprintf(stdout, \"Ops, cannot send probe request!\\n\");\n  \n  fprintf(stdout, \"Done. Type '/list peers' to see available peers.\\n\");\n\n  return 0;\n}\n\nint\nfunc_cmd_list(char *cmd, nc_opts *opts)\n{\n  char list_arg[32];\n  int rc;\n\n  rc = sscanf(cmd, \"/list %s\", list_arg);\n\n  if(rc != 1) {\n    fprintf(stdout, \"Error: correct format is 'list peers / rooms'.\\n\");\n    return 1;\n  }\n\n  if(strcmp(list_arg, \"peers\") == 0) {\n    nc_dal_print_peers();\n    return 0;\n  } else if(strcmp(list_arg, \"rooms\") == 0) {\n    nc_dal_print_rooms();\n    return 0;\n  } else {\n    fprintf(stdout, \"Error: correct format is 'list peers / rooms'.\\n\");\n    return 1;\n  }\n}\n"
  },
  {
    "path": "src/nc_udp.c",
    "content": "#include \"nc.h\"\n\nint\nnc_udp_send(char *ip, char *port, char *body, int broadcast)\n{\n  int sock;\n  struct sockaddr_in broadcast_addr;\n  int broadcast_permission;\n  char *send_str = body;\n  unsigned int send_str_len;\n\n  if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)\n    return 1;\n\n  if(broadcast == 1) {\n    broadcast_permission = 1;\n    if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST,\n\t\t  (void *) &broadcast_permission,\n\t\t  sizeof(broadcast_permission)) < 0)\n      return 2;\n  }\n\n  memset(&broadcast_addr, 0, sizeof(broadcast_addr));\n  broadcast_addr.sin_family = AF_INET;\n  broadcast_addr.sin_addr.s_addr = inet_addr(ip);\n  broadcast_addr.sin_port = htons(atoi(port));\n\n  send_str_len = strlen(send_str);\n\n  if(sendto(sock, send_str, send_str_len, 0,\n\t    (struct sockaddr *) &broadcast_addr,\n\t    sizeof(broadcast_addr)) != send_str_len)\n    return 3;\n\n  return 0;\n}\n\n"
  },
  {
    "path": "src/nc_utils.c",
    "content": "#include \"nc.h\"\n\nvoid\nnc_utils_print_help()\n{\n  fprintf(stdout,\n\t  \"NanoChat help\\n\"\n\t  \"-------------\\n\"\n\t  \"Avaliable flags:\\n\"\n\t  \"    Help:          -h --help\\n\"\n\t  \"    Host IP:       -H --host {host IP}\\n\"\n\t  \"    Broadcast IP:  -B --broadcast {broadcast IP}\\n\"\n\t  \"    RPC Port:      -P --port {port}\\n\"\n\t  \"    Discoverable:  -d --discoverable\\n\"\n\t  \"    Verbose:       -v --verbose\\n\"\n\t  \"    Secure:        -s --secure\\n\"\n\t  );\n}\n\nint\nnc_utils_count_char(const char *str, const char chr)\n{\n  char *chr_ptr;\n  int count;\n\n  count = 0;\n  chr_ptr = strchr(str, chr);\n  \n  while(chr_ptr != NULL) {\n    ++count;\n    chr_ptr = strchr(chr_ptr + 1, chr);\n  };\n\n  return count;\n}\n\nvoid\nnc_utils_now_str(char *time_str)\n{\n  time_t now;\n\n  time(&now);\n  strftime(time_str, NOW_STR_LEN, \"%Y-%m-%d %H:%M:%S\",\n\t   localtime(&now));  \n}\n\nvoid\nnc_utils_del_new_line(char *str)\n{\n  char *new_line;\n  \n  if((new_line = strchr(str, '\\n')) != NULL) {\n    *new_line = '\\0';\n  }\n}\n\nint\nnc_utils_has_new_line(char *str)\n{\n  char *new_line = NULL;\n\n  if((new_line = strchr(str, '\\n')) == NULL) {\n    return 0;\n  }\n\n  return 1;\n}\n\nvoid\nnc_utils_make_url(char *url, char *host, char *port)\n{\n  url[0] = '\\0';\n  strcat(url, \"tcp://\");\n  strcat(url, host);\n  strcat(url, \":\");\n  strcat(url, port);\n}\n\nint\nnc_utils_get_rec_sockfd(int sock)\n{\n  int rcvfd;\n  size_t rcvfd_sz = sizeof(rcvfd);\n  nn_getsockopt(sock, NN_SOL_SOCKET, NN_RCVFD, &rcvfd, &rcvfd_sz);\n  return rcvfd;\n}\n\nvoid\nnc_utils_empty_string(char *str)\n{\n  memset(str, '\\0', strlen(str));\n}\n\nvoid\nnc_utils_die(char *str)\n{\n  perror(str);\n  exit(1);\n}\n\n"
  }
]