ssm.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #include <errno.h>
  2. #include <stdarg.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <fcntl.h>
  9. #include <ctype.h>
  10. #include <limits.h>
  11. #include <sys/select.h>
  12. #include <sys/time.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include "libsam3a/libsam3a.h"
  16. char *argv0;
  17. #include "arg.h"
  18. ////////////////////////////////////////////////////////////////////////////////
  19. #define SSM_CHANNEL_MAX 24
  20. #define SSM_MSG_MAX 4096
  21. typedef struct Channel Channel;
  22. struct Channel {
  23. int fdin;
  24. Sam3AConnection *ct;
  25. char name[SSM_CHANNEL_MAX]; /* channel name (normalized) */
  26. char inpath[PATH_MAX]; /* input path */
  27. char outpath[PATH_MAX]; /* output path */
  28. Channel *next;
  29. };
  30. static Channel * channel_add(const char *);
  31. static Channel * channel_find(const char *);
  32. static Channel * channel_join(const char *, Sam3AConnection *);
  33. static void channel_leave(Channel *);
  34. static Channel * channel_new(const char *);
  35. static void channel_normalize_name(char *);
  36. static void channel_normalize_path(char *);
  37. static int channel_open(Channel *);
  38. static void channel_print(Channel *, const char *, int);
  39. static int channel_reopen(Channel *);
  40. static void channel_rm(Channel *);
  41. static void create_dirtree(const char *);
  42. static void create_filepath(char *, size_t, const char *, const char *, const char *);
  43. static void handle_channels_input(Sam3ASession *, Channel *);
  44. static int read_line(int, char *, size_t);
  45. static Channel *channels = NULL;
  46. static Channel *channelmaster = NULL;
  47. static char *ssmpath = NULL;
  48. ////////////////////////////////////////////////////////////////////////////////
  49. /* creates directories bottom-up, if necessary */
  50. static void
  51. create_dirtree(const char *dir)
  52. {
  53. char tmp[PATH_MAX], *p;
  54. struct stat st;
  55. size_t len;
  56. strncpy(tmp, dir, sizeof(tmp));
  57. len = strlen(tmp);
  58. if (len > 0 && tmp[len - 1] == '/')
  59. tmp[len - 1] = '\0';
  60. if ((stat(tmp, &st) != -1) && S_ISDIR(st.st_mode))
  61. return; /* dir exists */
  62. for (p = tmp + 1; *p; p++) {
  63. if (*p != '/')
  64. continue;
  65. *p = '\0';
  66. mkdir(tmp, S_IRWXU);
  67. printf("made dir: %s\n", tmp);
  68. *p = '/';
  69. }
  70. mkdir(tmp, S_IRWXU);
  71. printf("made dir: %s\n", tmp);
  72. }
  73. static void
  74. channel_normalize_path(char *s)
  75. {
  76. for (; *s; s++) {
  77. if (isalpha((unsigned char)*s))
  78. *s = tolower((unsigned char)*s);
  79. else if (!isdigit((unsigned char)*s) && !strchr(".#&+!-", *s))
  80. *s = '_';
  81. }
  82. }
  83. static void
  84. channel_normalize_name(char *s)
  85. {
  86. char *p;
  87. while (*s == '&' || *s == '#')
  88. s++;
  89. for (p = s; *s; s++) {
  90. if (!strchr(" ,&#\x07", *s)) {
  91. *p = *s;
  92. p++;
  93. }
  94. }
  95. *p = '\0';
  96. }
  97. static void
  98. create_filepath(char *filepath, size_t len, const char *path,
  99. const char *channel, const char *suffix)
  100. {
  101. int r;
  102. if (channel[0]) {
  103. r = snprintf(filepath, len, "%s/%s", path, channel);
  104. if (r < 0 || (size_t)r >= len)
  105. goto error;
  106. create_dirtree(filepath);
  107. r = snprintf(filepath, len, "%s/%s/%s", path, channel, suffix);
  108. if (r < 0 || (size_t)r >= len)
  109. goto error;
  110. } else {
  111. r = snprintf(filepath, len, "%s/%s", path, suffix);
  112. if (r < 0 || (size_t)r >= len)
  113. goto error;
  114. }
  115. return;
  116. error:
  117. fprintf(stderr, "%s: path to irc directory too long\n", argv0);
  118. exit(1);
  119. }
  120. static int
  121. channel_open(Channel *c)
  122. {
  123. int fd;
  124. struct stat st;
  125. /* make "in" fifo if it doesn't exist already. */
  126. if (lstat(c->inpath, &st) != -1) {
  127. if (!(st.st_mode & S_IFIFO))
  128. return -1;
  129. } else if (mkfifo(c->inpath, S_IRWXU)) {
  130. return -1;
  131. }
  132. c->fdin = -1;
  133. fd = open(c->inpath, O_RDONLY | O_NONBLOCK, 0);
  134. if (fd == -1)
  135. return -1;
  136. c->fdin = fd;
  137. return 0;
  138. }
  139. static int
  140. channel_reopen(Channel *c)
  141. {
  142. if (c->fdin > 2) {
  143. close(c->fdin);
  144. c->fdin = -1;
  145. }
  146. return channel_open(c);
  147. }
  148. static Channel *
  149. channel_new(const char *name)
  150. {
  151. Channel *c;
  152. char channelpath[PATH_MAX];
  153. strncpy(channelpath, name, sizeof(channelpath));
  154. //channel_normalize_path(channelpath);
  155. if (!(c = calloc(1, sizeof(Channel)))) {
  156. fprintf(stderr, "%s: calloc: %s\n", argv0, strerror(errno));
  157. exit(1);
  158. }
  159. strncpy(c->name, name, sizeof(c->name));
  160. //channel_normalize_name(c->name);
  161. create_filepath(c->inpath, sizeof(c->inpath), ssmpath,
  162. channelpath, "in");
  163. create_filepath(c->outpath, sizeof(c->outpath), ssmpath,
  164. channelpath, "out");
  165. return c;
  166. }
  167. static Channel *
  168. channel_find(const char *name)
  169. {
  170. Channel *c;
  171. char chan[SSM_CHANNEL_MAX];
  172. strncpy(chan, name, sizeof(chan));
  173. //channel_normalize_name(chan);
  174. for (c = channels; c; c = c->next) {
  175. if (!strcmp(chan, c->name))
  176. return c; /* already handled */
  177. }
  178. return NULL;
  179. }
  180. static Channel *
  181. channel_add(const char *name)
  182. {
  183. Channel *c;
  184. c = channel_new(name);
  185. if (channel_open(c) == -1) {
  186. fprintf(stderr, "%s: cannot create channel: %s: %s\n",
  187. argv0, name, strerror(errno));
  188. free(c);
  189. return NULL;
  190. }
  191. if (!channels) {
  192. channels = c;
  193. } else {
  194. c->next = channels;
  195. channels = c;
  196. }
  197. return c;
  198. }
  199. static Channel *
  200. channel_join(const char *name, Sam3AConnection *ct)
  201. {
  202. Channel *c;
  203. if (!(c = channel_find(name)))
  204. c = channel_add(name);
  205. c->ct = ct;
  206. return c;
  207. }
  208. static void
  209. channel_rm(Channel *c)
  210. {
  211. Channel *p;
  212. if (channels == c) {
  213. channels = channels->next;
  214. } else {
  215. for (p = channels; p && p->next != c; p = p->next)
  216. ;
  217. if (p && p->next == c)
  218. p->next = c->next;
  219. }
  220. free(c);
  221. }
  222. static void
  223. channel_leave(Channel *c)
  224. {
  225. if (c->fdin > 2) {
  226. close(c->fdin);
  227. c->fdin = -1;
  228. }
  229. /* remove "in" file on leaving the channel */
  230. unlink(c->inpath);
  231. channel_rm(c);
  232. }
  233. static void
  234. channel_print(Channel *c, const char* buf, int bufsize)
  235. {
  236. FILE *fp = NULL;
  237. time_t t = time(NULL);
  238. if (!(fp = fopen(c->outpath, "a")))
  239. return;
  240. fprintf(fp, "%lu ", (unsigned long)t);
  241. for (int i = 0; i < bufsize; i++)
  242. fprintf(fp, "%c", buf[i]);
  243. fprintf(fp, "\n");
  244. fclose(fp);
  245. }
  246. ////////////////////////////////////////////////////////////////////////////////
  247. static void ccbError(Sam3AConnection *ct) {
  248. fprintf(stderr,
  249. "\n===============================\nCONNECTION_ERROR: "
  250. "[%s]\n===============================\n",
  251. ct->error);
  252. }
  253. static void ccbDisconnected(Sam3AConnection *ct) {
  254. fprintf(stderr, "\n===============================\nCONNECTION_"
  255. "DISCONNECTED\n===============================\n");
  256. char name[SSM_CHANNEL_MAX];
  257. memcpy(name, ct->destkey, SSM_CHANNEL_MAX - 1);
  258. name[SSM_CHANNEL_MAX - 1] = '\0';
  259. Channel* c = channel_find(name);
  260. channel_leave(c);
  261. }
  262. static void ccbConnected(Sam3AConnection *ct) {
  263. fprintf(stderr, "\n===============================\nCONNECTION_CONNECTED\n==="
  264. "============================\n");
  265. char name[SSM_CHANNEL_MAX];
  266. memcpy(name, ct->destkey, SSM_CHANNEL_MAX - 1);
  267. name[SSM_CHANNEL_MAX - 1] = '\0';
  268. channel_join(name, ct);
  269. }
  270. static void ccbAccepted(Sam3AConnection *ct) {
  271. fprintf(stderr, "\n===============================\nCONNECTION_ACCEPTED\n===="
  272. "===========================\n");
  273. fprintf(stderr, "FROM: %s\n===============================\n", ct->destkey);
  274. char name[SSM_CHANNEL_MAX];
  275. memcpy(name, ct->destkey, SSM_CHANNEL_MAX - 1);
  276. name[SSM_CHANNEL_MAX - 1] = '\0';
  277. channel_join(name, ct);
  278. }
  279. static void ccbSent(Sam3AConnection *ct) {
  280. fprintf(stderr, "\n===============================\nCONNECTION_WANTBYTES\n==="
  281. "============================\n");
  282. }
  283. static void ccbRead(Sam3AConnection *ct, const void *buf, int bufsize) {
  284. fprintf(stderr,
  285. "\n===============================\nCONNECTION_GOTBYTES "
  286. "(%d)\n===============================\n",
  287. bufsize);
  288. Channel *c;
  289. for (c = channels; c; c = c->next)
  290. if (c->ct == ct)
  291. break;
  292. if (!c)
  293. fprintf(stderr, "Channel not found\n");
  294. channel_print(c, buf, bufsize);
  295. }
  296. static void ccbDestroy(Sam3AConnection *ct) {
  297. fprintf(stderr, "\n===============================\nCONNECTION_DESTROY\n====="
  298. "==========================\n");
  299. }
  300. static const Sam3AConnectionCallbacks ccb = {
  301. .cbError = ccbError,
  302. .cbDisconnected = ccbDisconnected,
  303. .cbConnected = ccbConnected,
  304. .cbAccepted = ccbAccepted,
  305. .cbSent = ccbSent,
  306. .cbRead = ccbRead,
  307. .cbDestroy = ccbDestroy,
  308. };
  309. ////////////////////////////////////////////////////////////////////////////////
  310. static void scbError(Sam3ASession *ses) {
  311. fprintf(stderr,
  312. "\n===============================\nSESION_ERROR: "
  313. "[%s]\n===============================\n",
  314. ses->error);
  315. }
  316. static void scbCreated(Sam3ASession *ses) {
  317. Sam3AConnection *conn;
  318. fprintf(stderr, "\n===============================\nSESION_CREATED\n");
  319. fprintf(stderr, "\rPRIV: %s\n", ses->privkey);
  320. fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey);
  321. channelmaster = channel_add(""); /* master channel */
  322. if ((conn = sam3aStreamAccept(ses, &ccb)) == NULL) {
  323. fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n");
  324. sam3aCancelSession(ses);
  325. return;
  326. }
  327. }
  328. static void scbDisconnected(Sam3ASession *ses) {
  329. fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n===="
  330. "===========================\n");
  331. }
  332. static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) {
  333. fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n==="
  334. "============================\n");
  335. }
  336. static void scbDestroy(Sam3ASession *ses) {
  337. fprintf(stderr, "\n===============================\nSESION_DESTROYED\n======="
  338. "========================\n");
  339. }
  340. static const Sam3ASessionCallbacks scb = {
  341. .cbError = scbError,
  342. .cbCreated = scbCreated,
  343. .cbDisconnected = scbDisconnected,
  344. .cbDatagramRead = scbDGramRead,
  345. .cbDestroy = scbDestroy,
  346. };
  347. ////////////////////////////////////////////////////////////////////////////////
  348. static int
  349. read_line(int fd, char *buf, size_t bufsiz)
  350. {
  351. size_t i = 0;
  352. char c = '\0';
  353. do {
  354. if (read(fd, &c, sizeof(char)) != sizeof(char))
  355. return -1;
  356. buf[i++] = c;
  357. } while (c != '\n' && i < bufsiz);
  358. buf[i - 1] = '\0'; /* eliminates '\n' */
  359. return 0;
  360. }
  361. static void
  362. handle_channels_input(Sam3ASession *ses, Channel *c)
  363. {
  364. char buf[SSM_MSG_MAX];
  365. if (read_line(c->fdin, buf, sizeof(buf)) == -1) {
  366. if (channel_reopen(c) == -1)
  367. channel_rm(c);
  368. return;
  369. }
  370. if (c == channelmaster) {
  371. if (!sam3aIsValidPubKey(buf)) {
  372. fprintf(stderr, "Invalid public key input in master channel\n");
  373. return;
  374. }
  375. if (sam3aStreamConnect(ses, &ccb, buf) == NULL) {
  376. fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n");
  377. sam3aCancelSession(ses);
  378. return;
  379. }
  380. } else {
  381. if (sam3aIsActiveConnection(c->ct)) {
  382. channel_print(c, buf, strlen(buf));
  383. sam3aSend(c->ct, buf, strlen(buf));
  384. } else {
  385. fprintf(stderr, "Connection of channel %s is not active", c->name);
  386. return;
  387. }
  388. }
  389. }
  390. #define HOST SAM3A_HOST_DEFAULT
  391. int main(int argc, char *argv[]) {
  392. ssmpath = getenv("SSM_DIR");
  393. Sam3ASession ses;
  394. Sam3AConnection *conn;
  395. //
  396. libsam3a_debug = 0;
  397. //
  398. if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT,
  399. SAM3A_DESTINATION_TRANSIENT,
  400. SAM3A_SESSION_STREAM) < 0) {
  401. fprintf(stderr, "FATAL: can't create main session!\n");
  402. return 1;
  403. }
  404. //
  405. while (sam3aIsActiveSession(&ses)) {
  406. fd_set rds, wrs;
  407. Channel *c;
  408. int res, maxfd = 0;
  409. struct timeval to;
  410. //
  411. FD_ZERO(&rds);
  412. FD_ZERO(&wrs);
  413. if ((conn = sam3aStreamAccept(&ses, &ccb)) == NULL) {
  414. fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION: %s\n", ses.error);
  415. sam3aCancelSession(&ses);
  416. return -1;
  417. }
  418. if (sam3aIsActiveSession(&ses) &&
  419. (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0)
  420. break;
  421. for (c = channels; c; c = c->next) {
  422. if (c->fdin > maxfd) {
  423. maxfd = c->fdin;
  424. }
  425. FD_SET(c->fdin, &rds);
  426. }
  427. sam3ams2timeval(&to, 1000);
  428. res = select(maxfd + 1, &rds, &wrs, NULL, &to);
  429. if (res < 0) {
  430. if (errno == EINTR)
  431. continue;
  432. fprintf(stderr, "FATAL: select() error!\n");
  433. break;
  434. }
  435. if (res == 0) {
  436. fprintf(stdout, ".");
  437. fflush(stdout);
  438. } else {
  439. if (sam3aIsActiveSession(&ses))
  440. sam3aProcessSessionIO(&ses, &rds, &wrs);
  441. for (c = channels; c; c = c->next) {
  442. if (FD_ISSET(c->fdin, &rds)) {
  443. handle_channels_input(&ses, c);
  444. }
  445. }
  446. }
  447. }
  448. //
  449. sam3aCloseSession(&ses);
  450. //
  451. return 0;
  452. }