save.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * for GET or POST to /magic/save/foo.
  3. * add incoming data to foo.data.
  4. * send foo.html as reply.
  5. *
  6. * supports foo.data with "exclusive use" mode to prevent interleaved saves.
  7. * thus http://cm.bell-labs.com/magic/save/t?args should access:
  8. * -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data
  9. * --rw-rw-r-- M 21470 ehg web 73 May 21 18:17 /usr/web/save/t.html
  10. */
  11. #include <u.h>
  12. #include <libc.h>
  13. #include <bio.h>
  14. #include "httpd.h"
  15. #include "httpsrv.h"
  16. enum
  17. {
  18. MaxLog = 24*1024, /* limit on length of any one log request */
  19. LockSecs = MaxLog/500, /* seconds to wait before giving up on opening the data file */
  20. };
  21. static int
  22. dangerous(char *s)
  23. {
  24. if(s == nil)
  25. return 1;
  26. /*
  27. * This check shouldn't be needed;
  28. * filename folding is already supposed to have happened.
  29. * But I'm paranoid.
  30. */
  31. while(s = strchr(s,'/')){
  32. if(s[1]=='.' && s[2]=='.')
  33. return 1;
  34. s++;
  35. }
  36. return 0;
  37. }
  38. /*
  39. * open a file which might be locked.
  40. * if it is, spin until available
  41. */
  42. int
  43. openLocked(char *file, int mode)
  44. {
  45. char buf[ERRMAX];
  46. int tries, fd;
  47. for(tries = 0; tries < LockSecs*2; tries++){
  48. fd = open(file, mode);
  49. if(fd >= 0)
  50. return fd;
  51. errstr(buf, sizeof buf);
  52. if(strstr(buf, "locked") == nil)
  53. break;
  54. sleep(500);
  55. }
  56. return -1;
  57. }
  58. void
  59. main(int argc, char **argv)
  60. {
  61. HConnect *c;
  62. Dir *dir;
  63. Hio *hin, *hout;
  64. char *s, *t, *fn;
  65. int n, nfn, datafd, htmlfd;
  66. c = init(argc, argv);
  67. if(dangerous(c->req.uri)){
  68. hfail(c, HSyntax);
  69. exits("failed");
  70. }
  71. if(hparseheaders(c, HSTIMEOUT) < 0)
  72. exits("failed");
  73. hout = &c->hout;
  74. if(c->head.expectother){
  75. hfail(c, HExpectFail, nil);
  76. exits("failed");
  77. }
  78. if(c->head.expectcont){
  79. hprint(hout, "100 Continue\r\n");
  80. hprint(hout, "\r\n");
  81. hflush(hout);
  82. }
  83. s = nil;
  84. if(strcmp(c->req.meth, "POST") == 0){
  85. hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc);
  86. if(hin != nil){
  87. alarm(HSTIMEOUT);
  88. s = hreadbuf(hin, hin->pos);
  89. alarm(0);
  90. }
  91. if(s == nil){
  92. hfail(c, HBadReq, nil);
  93. exits("failed");
  94. }
  95. t = strchr(s, '\n');
  96. if(t != nil)
  97. *t = '\0';
  98. }else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){
  99. hunallowed(c, "GET, HEAD, PUT");
  100. exits("unallowed");
  101. }else
  102. s = c->req.search;
  103. if(s == nil){
  104. hfail(c, HNoData, "save");
  105. exits("failed");
  106. }
  107. if(strlen(s) > MaxLog)
  108. s[MaxLog] = '\0';
  109. n = snprint(c->xferbuf, HBufSize, "at %ld %s\n", time(0), s);
  110. nfn = strlen(c->req.uri) + 64;
  111. fn = halloc(c, nfn);
  112. /*
  113. * open file descriptors & write log line
  114. */
  115. snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri);
  116. htmlfd = open(fn, OREAD);
  117. if(htmlfd < 0 || (dir = dirfstat(htmlfd)) == nil){
  118. hfail(c, HNotFound, c->req.uri);
  119. exits("failed");
  120. return;
  121. }
  122. snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri);
  123. datafd = openLocked(fn, OWRITE);
  124. if(datafd < 0){
  125. errstr(c->xferbuf, sizeof c->xferbuf);
  126. if(strstr(c->xferbuf, "locked") != nil)
  127. hfail(c, HTempFail, c->req.uri);
  128. else
  129. hfail(c, HNotFound, c->req.uri);
  130. exits("failed");
  131. }
  132. seek(datafd, 0, 2);
  133. write(datafd, c->xferbuf, n);
  134. close(datafd);
  135. sendfd(c, htmlfd, dir, hmkcontent(c, "text", "html", nil), nil);
  136. exits(nil);
  137. }