void _shttpd_get_file(struct conn *c, struct stat *stp) { char date[64], lm[64], etag[64], range[64] = ""; size_t n, status = 200; unsigned long r1, r2; const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK"; big_int_t cl; /* Content-Length */ if (c->mime_type.len == 0) _shttpd_get_mime_type(c->ctx, c->uri, strlen(c->uri), &c->mime_type); cl = (big_int_t) stp->st_size; /* If Range: header specified, act accordingly */ if (c->ch.range._v.v_vec.len > 0 && (n = sscanf(c->ch.range._v.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) { status = 206; (void) lseek(c->loc.chan.fd, r1, SEEK_SET); cl = n == 2 ? r2 - r1 + 1: cl - r1; (void) _shttpd_snprintf(range, sizeof(range), "Content-Range: bytes %lu-%lu/%lu\r\n", r1, r1 + cl - 1, (unsigned long) stp->st_size); msg = "Partial Content"; } /* Prepare Etag, Date, Last-Modified headers */ (void) strftime(date, sizeof(date), fmt, localtime(&_shttpd_current_time)); (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime)); (void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx", (unsigned long) stp->st_mtime, (unsigned long) stp->st_size); /* * We do not do io_inc_head here, because it will increase 'total' * member in io. We want 'total' to be equal to the content size, * and exclude the headers length from it. */ c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size, "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "Etag: \"%s\"\r\n" "Content-Type: %.*s\r\n" "Content-Length: %lu\r\n" "Accept-Ranges: bytes\r\n" "%s\r\n", status, msg, date, lm, etag, c->mime_type.len, c->mime_type.ptr, cl, range); c->status = status; c->loc.content_len = cl; c->loc.io_class = &_shttpd_io_file; c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY; if (c->method == METHOD_HEAD) _shttpd_stop_stream(&c->loc); }
void* rho_dispatch(struct conn *c, const char* path) { RouteRef route; if ( _shttpd_match_extension(c->uri,"css,js,html,htm,png,bmp,jpg") ) return NULL; if ((route = _alloc_route(c->uri)) != NULL) { if (_parse_route(route)) { struct stat st; //is this an actual file or folder if (_shttpd_stat(path, &st) != 0) return route; //is this a folder if (S_ISDIR(st.st_mode)) { //check if there is controller.rb to run char filename[FILENAME_MAX]; int len = strlen(path); char* slash = path[len-1] == '\\' || path[len-1] == '/' ? "" : "/"; _shttpd_snprintf(filename,sizeof(filename),"%s%s%s",path,slash,"controller.iseq"); if ((_shttpd_stat(filename, &st) == 0)&&(!S_ISDIR(st.st_mode))) { printf("Run controller on this url: %s\n", c->uri); return route; } } } _free_route(route); } return NULL; }
static void systray(void *arg) { WNDCLASS cls; HWND hWnd; MSG msg; (void) memset(&cls, 0, sizeof(cls)); cls.lpfnWndProc = (WNDPROC) WindowProc; cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); cls.lpszClassName = "shttpd v." VERSION; if (!RegisterClass(&cls)) _shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO); else if ((hWnd = CreateWindow(cls.lpszClassName, "", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL) _shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO); ShowWindow(hWnd, SW_HIDE); ni.cbSize = sizeof(ni); ni.uID = ID_TRAYICON; ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; ni.hIcon = LoadIcon(NULL, IDI_APPLICATION); ni.hWnd = hWnd; _shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server"); ni.uCallbackMessage = WM_USER; Shell_NotifyIcon(NIM_ADD, &ni); while (GetMessage(&msg, hWnd, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
DIR * opendir(const char *name) { DIR *dir = NULL; char path[FILENAME_MAX]; wchar_t wpath[FILENAME_MAX]; if (name == NULL || name[0] == '\0') { errno = EINVAL; } else if ((dir = malloc(sizeof(*dir))) == NULL) { errno = ENOMEM; } else { _shttpd_snprintf(path, sizeof(path), "%s/*", name); fix_directory_separators(path); MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath)); dir->handle = FindFirstFileW(wpath, &dir->info); if (dir->handle != INVALID_HANDLE_VALUE) { dir->result.d_name[0] = '\0'; } else { free(dir); dir = NULL; } } return (dir); }
int _shttpd_run_cgi(struct conn *c, const char *prog) { struct env_block blk; char dir[FILENAME_MAX], *p; int ret, pair[2]; prepare_environment(c, prog, &blk); pair[0] = pair[1] = -1; /* CGI must be executed in its own directory */ (void) _shttpd_snprintf(dir, sizeof(dir), "%s", prog); for (p = dir + strlen(dir) - 1; p > dir; p--) if (*p == '/') { *p++ = '\0'; break; } if (shttpd_socketpair(pair) != 0) { ret = -1; } else if (_shttpd_spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) { ret = -1; (void) closesocket(pair[0]); (void) closesocket(pair[1]); } else { ret = 0; c->loc.chan.sock = pair[0]; } return (ret); }
char* rho_resolve_url(char* url, const char* root,const char *index_names) { char path[URI_MAX]; struct stat st; char* tmp_url; char* ret; int full_len; char* full_path = strstr(url,"http://"); if (full_path) { return full_path; } if (strlen(url) + strlen(root) >= sizeof(path)) { tmp_url = url; } else { _shttpd_snprintf(path, sizeof(path), "%s%s", root, url); if ( _shttpd_stat(path, &st) == -1 ) { tmp_url = url; } else if ( S_ISDIR(st.st_mode) ) { tmp_url = _rho_resolve_index(url,path,index_names); } else { tmp_url = url; } } #if defined(__SYMBIAN32__) || defined(OS_WINDOWS) || defined(OS_WINCE) full_len = strlen(get_home_url())+strlen(tmp_url)+1; ret = malloc(full_len); _shttpd_snprintf(ret, full_len, "%s%s", get_home_url(), tmp_url); #else full_len = strlen(localhost)+strlen(tmp_url)+1; ret = malloc(full_len); _shttpd_snprintf(ret, full_len, "%s%s", localhost, tmp_url); #endif free(tmp_url); return ret; }
static char* _rho_resolve_index(char* url,char* path,const char *index_names) { char filename[FILENAME_MAX]; struct stat st; //check if there is controller.rb int len = strlen(path); char* slash = path[len-1] == '\\' || path[len-1] == '/' ? "" : "/"; _shttpd_snprintf(filename,sizeof(filename),"%s%s%s",path,slash,"controller.iseq"); if ((_shttpd_stat(filename, &st) == 0)&&(!S_ISDIR(st.st_mode))) { return url; } len = strlen(url); if (url[len-1]!='/') { char* tmp_url = malloc(len+2); _shttpd_snprintf(tmp_url,len+2,"%s/",url); free(url); return tmp_url; } return url; }
void _shttpd_log_access(FILE *fp, const struct conn *c) { static const struct vec dash = {"-", 1}; const struct vec *user = &c->ch.user._v.v_vec; const struct vec *referer = &c->ch.referer._v.v_vec; const struct vec *user_agent = &c->ch.useragent._v.v_vec; char date[64], buf[URI_MAX], *q1 = "\"", *q2 = "\""; if (user->len == 0) user = ‐ if (referer->len == 0) { referer = ‐ q1 = ""; } if (user_agent->len == 0) { user_agent = ‐ q2 = ""; } (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S", localtime(&c->birth_time)); (void) _shttpd_snprintf(buf, sizeof(buf), "%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s", inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr, date, _shttpd_tz_offset, c->request ? c->request : "-", c->status, (unsigned long) c->loc.io.total, q1, referer->len, referer->ptr, q1, q2, user_agent->len, user_agent->ptr, q2); if (fp != NULL) { (void) fprintf(fp, "%s\n", buf); (void) fflush(fp); } }
/* * Log function */ void _shttpd_elog(int flags, struct conn *c, const char *fmt, ...) { char date[64], buf[URI_MAX]; int len; FILE *fp = c == NULL ? NULL : c->ctx->error_log; va_list ap; /* Print to stderr */ if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) { va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); (void) fputc('\n', stderr); va_end(ap); } strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y", localtime(&_shttpd_current_time)); len = _shttpd_snprintf(buf, sizeof(buf), "[%s] [error] [client %s] \"%s\" ", date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-", c && c->request ? c->request : "-"); va_start(ap, fmt); (void) vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; if (fp != NULL && (flags & (E_FATAL | E_LOG))) { (void) fprintf(fp, "%s\n", buf); (void) fflush(fp); } if (flags & E_FATAL) exit(EXIT_FAILURE); }
void* rho_dispatch(struct conn *c, const char* path) { RouteRef route; if ( _shttpd_match_extension(c->uri,"css,js,html,htm,png,bmp,jpg") ) return NULL; #ifdef __SYMBIAN32__ if ( strstr(_shttpd_known_http_methods[c->method].ptr, "GET" ) ) webview_set_current_location(c->uri); #endif if ((route = _alloc_route(c->uri)) != NULL) { if (_parse_route(route)) { struct stat st; //is this an actual file or folder if (_shttpd_stat(path, &st) != 0) return route; //is this a folder if (S_ISDIR(st.st_mode)) { //check if there is controller.rb to run char filename[FILENAME_MAX]; int len = strlen(path); char* slash = path[len-1] == '\\' || path[len-1] == '/' ? "" : "/"; _shttpd_snprintf(filename,sizeof(filename),"%s%s%s",path,slash,"controller.iseq"); if ((_shttpd_stat(filename, &st) == 0)&&(!S_ISDIR(st.st_mode))) { RAWLOG_INFO1("Run controller on this url: %s", c->uri); return route; } } } _free_route(route); } return NULL; }
static int write_file(struct stream *stream, const void *buf, size_t len) { struct stat st; struct stream *rem = &stream->conn->rem; int n, fd = stream->chan.fd; assert(fd != -1); n = write(fd, buf, len); DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n)); if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) { (void) fstat(fd, &st); stream->io.head = stream->headers_len = _shttpd_snprintf(stream->io.buf, stream->io.size, "HTTP/1.1 %d OK\r\n" "Content-Length: %lu\r\nConnection: close\r\n\r\n", stream->conn->status, st.st_size); _shttpd_stop_stream(stream); } return (n); }
static int read_cgi(struct stream *stream, void *buf, size_t len) { struct headers parsed; char status[4]; int n; assert(stream->chan.sock != -1); assert(stream->flags & FLAG_R); stream->flags &= ~FLAG_DONT_CLOSE; n = recv(stream->chan.sock, buf, len, 0); if (stream->flags & FLAG_HEADERS_PARSED) return (n); if (n <= 0 && ERRNO != EWOULDBLOCK) { _shttpd_send_server_error(stream->conn, 500, "Error running CGI"); return (n); } /* * CGI script may output Status: and Location: headers, which * may alter the status code. Buffer in headers, parse * them, send correct status code and then forward all data * from CGI script back to the remote end. * Reply line was alredy appended to the IO buffer in * decide_what_to_do(), with blank status code. */ stream->flags |= FLAG_DONT_CLOSE; io_inc_head(&stream->io, n); stream->headers_len = _shttpd_get_headers_len(stream->io.buf, stream->io.head); if (stream->headers_len < 0) { stream->flags &= ~FLAG_DONT_CLOSE; _shttpd_send_server_error(stream->conn, 500, "Bad headers sent"); _shttpd_elog(E_LOG, stream->conn, "CGI script sent invalid headers: " "[%.*s]", stream->io.head - CGI_REPLY_LEN, stream->io.buf + CGI_REPLY_LEN); return (0); } /* * If we did not received full headers yet, we must not send any * data read from the CGI back to the client. Suspend sending by * setting tail = head, which tells that there is no data in IO buffer */ if (stream->headers_len == 0) { stream->io.tail = stream->io.head; return (0); } /* Received all headers. Set status code for the connection. */ (void) memset(&parsed, 0, sizeof(parsed)); _shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed); stream->content_len = parsed.cl.v_big_int; stream->conn->status = (int) parsed.status.v_big_int; /* If script outputs 'Location:' header, set status code to 302 */ if (parsed.location.v_vec.len > 0) stream->conn->status = 302; /* * If script did not output neither 'Location:' nor 'Status' headers, * set the default status code 200, which means 'success'. */ if (stream->conn->status == 0) stream->conn->status = 200; /* Append the status line to the beginning of the output */ (void) _shttpd_snprintf(status, sizeof(status), "%3d", stream->conn->status); (void) memcpy(stream->io.buf + 9, status, 3); DBG(("read_cgi: content len %lu status %s", stream->content_len, status)); /* Next time, pass output directly back to the client */ assert((big_int_t) stream->headers_len <= stream->io.total); stream->io.total -= stream->headers_len; stream->io.tail = 0; stream->flags |= FLAG_HEADERS_PARSED; /* Return 0 because we've already shifted the head */ return (0); }
int _shttpd_spawn_process(struct conn *c, const char *prog, char *envblk, char *envp[], int sock, const char *dir) { HANDLE a[2], b[2], h[2], me; DWORD flags; char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX]; FILE *fp; STARTUPINFOA si; PROCESS_INFORMATION pi; me = GetCurrentProcess(); flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; /* FIXME add error checking code here */ CreatePipe(&a[0], &a[1], NULL, 0); CreatePipe(&b[0], &b[1], NULL, 0); DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags); DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags); (void) memset(&si, 0, sizeof(si)); (void) memset(&pi, 0, sizeof(pi)); /* XXX redirect CGI errors to the error log file */ si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdOutput = h[1]; si.hStdInput = h[0]; /* If CGI file is a script, try to read the interpreter line */ interp = c->ctx->options[OPT_CGI_INTERPRETER]; if (interp == NULL) { if ((fp = fopen(prog, "r")) != NULL) { (void) fgets(line, sizeof(line), fp); if (memcmp(line, "#!", 2) != 0) line[2] = '\0'; /* Trim whitespaces from interpreter name */ for (p = &line[strlen(line) - 1]; p > line && isspace(*p); p--) *p = '\0'; (void) fclose(fp); } interp = line + 2; (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s", line + 2, line[2] == '\0' ? "" : " ", prog); } if ((p = strrchr(prog, '/')) != NULL) prog = p + 1; (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog); (void) _shttpd_snprintf(line, sizeof(line), "%s", dir); fix_directory_separators(line); fix_directory_separators(cmdline); /* * Spawn reader & writer threads before we create CGI process. * Otherwise CGI process may die too quickly, loosing the data */ spawn_stdio_thread(sock, b[0], stdinput, 0); spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len); if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) { _shttpd_elog(E_LOG, c, "redirect: CreateProcess(%s): %d", cmdline, ERRNO); return (-1); } else { CloseHandle(h[0]); CloseHandle(h[1]); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } return (0); }