Esempio n. 1
0
/**
 * @short This shortcut returns the given file contents. 
 * 
 * It sets all the compilant headers (TODO), cache and so on.
 * 
 * This is the recomended way to send static files; it even can use sendfile Linux call 
 * if suitable (TODO).
 * 
 * It does no security checks, so caller must be security aware.
 */
onion_connection_status onion_shortcut_response_file(const char *filename, onion_request *request, onion_response *res){
	int fd=open(filename,O_RDONLY|O_CLOEXEC);
	
	if (fd<0)
		return OCS_NOT_PROCESSED;

	if(O_CLOEXEC == 0) { // Good compiler know how to cut this out
		int flags=fcntl(fd, F_GETFD);
		if (flags==-1){
			ONION_ERROR("Retrieving flags from file descriptor");
		}
		flags|=FD_CLOEXEC;
		if (fcntl(fd, F_SETFD, flags)==-1){
			ONION_ERROR("Setting O_CLOEXEC to file descriptor");
		}
	}
	
	struct stat st;
	if (stat(filename, &st)!=0){
		ONION_WARNING("File does not exist: %s",filename);
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	if (S_ISDIR(st.st_mode)){
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	size_t length=st.st_size;
	
	char etag[64];
	onion_shortcut_etag(&st, etag);
		
	const char *range=onion_request_get_header(request, "Range");
	if (range){
		strncat(etag,range,sizeof(etag)-1);
	}
	onion_response_set_header(res, "Etag", etag);
	
	if (range && strncmp(range,"bytes=",6)==0){
		onion_response_set_code(res, HTTP_PARTIAL_CONTENT);
		//ONION_DEBUG("Need just a range: %s",range);
		char tmp[1024];
		strncpy(tmp, range+6, 1024);
		char *start=tmp;
		char *end=tmp;
		while (*end!='-' && *end) end++;
		if (*end=='-'){
			*end='\0';
			end++;
			
			//ONION_DEBUG("Start %s, end %s",start,end);
			size_t ends, starts;
			if (*end)
				ends=atol(end);
			else
				ends=length;
			starts=atol(start);
			length=ends-starts+1;
			lseek(fd, starts, SEEK_SET);
			snprintf(tmp,sizeof(tmp),"bytes %d-%d/%d",(unsigned int)starts, (unsigned int)ends, (unsigned int)st.st_size);
			//onion_response_set_header(res, "Accept-Ranges","bytes");
			onion_response_set_header(res, "Content-Range",tmp);
		}
	}
	
	onion_response_set_length(res, length);
	onion_response_set_header(res, "Content-Type", onion_mime_get(filename) );
	ONION_DEBUG("Mime type is %s",onion_mime_get(filename));

  ONION_DEBUG0("Etag %s", etag);
  const char *prev_etag=onion_request_get_header(request, "If-None-Match");
  if (prev_etag && (strcmp(prev_etag, etag)==0)){
    ONION_DEBUG0("Not modified");
    onion_response_set_length(res, 0);
    onion_response_set_code(res, HTTP_NOT_MODIFIED);
    onion_response_write_headers(res);
    close(fd);
    return OCS_PROCESSED;
  }
	onion_response_write_headers(res);
	if ((onion_request_get_flags(request)&OR_HEAD) == OR_HEAD){ // Just head.
		length=0;
	}
	
	if (length){
#ifdef USE_SENDFILE
		if (request->connection.listen_point->write==(void*)onion_http_write){ // Lets have a house party! I can use sendfile!
			onion_response_write(res,NULL,0);
			ONION_DEBUG("Using sendfile");
			int r=sendfile(request->connection.fd, fd, NULL, length);
			ONION_DEBUG("Wrote %d, should be %d (%s)", r, length, r==length ? "ok" : "nok");
			if (r!=length || r<0){
				ONION_ERROR("Could not send all file (%s)", strerror(errno));
				close(fd);
				return OCS_INTERNAL_ERROR;
			}
			res->sent_bytes+=length;
			res->sent_bytes_total+=length;
		}
		else
#endif
		{ // Ok, no I cant, do it as always.
			int r=0,w;
			size_t tr=0;
			char tmp[4046];
			if (length>sizeof(tmp)){
				size_t max=length-sizeof(tmp);
				while( tr<max ){
					r=read(fd,tmp,sizeof(tmp));
					tr+=r;
					if (r<0)
						break;
					w=onion_response_write(res, tmp, r);
					if (w!=r){
						ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
						break;
					}
				}
			}
			if (sizeof(tmp) >= (length-tr)){
				r=read(fd, tmp, length-tr);
				w=onion_response_write(res, tmp, r);
				if (w!=r){
					ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
				}
			}
		}
	}
	close(fd);
	return OCS_PROCESSED;
}
Esempio n. 2
0
/**
 * @short Write the properties of a path.
 * 
 * Given a path, and optionally a file at that path, writes the XML of its properties.
 * 
 * If no file given, data is for that path, else it is for the path+filename.
 * 
 * @param writer XML writer to write the data to
 * @param realpath The real path on the file server, used to check permissions and read data TODO
 * @param urlpath The base URL of the element, needed in the response. Composed with filename if filename!=NULL.
 * @param filename NULL if that element itself, else the subelement (file in path).
 * @param props Bitmask of the properties the user asked for.
 * 
 * @return 0 is ok, 1 if could not stat file.
 */
int onion_webdav_write_props(xmlTextWriterPtr writer, const char *basepath, 
														 const char *realpath, const char *urlpath, const char *filename, 
														 int props){
	ONION_DEBUG("Info for path '%s', urlpath '%s', file '%s', basepath '%s'", realpath, urlpath, filename, basepath);
	// Stat the thing itself
	char tmp[PATH_MAX];
	if (filename)
		snprintf(tmp, sizeof(tmp), "%s/%s", realpath, filename);
	else{
		if (strlen(realpath)>=sizeof(tmp)-1){
			ONION_ERROR("File path too long");
			return 1;
		}
		strncpy(tmp, realpath, sizeof(tmp)-1);
	}
	struct stat st;
	if (stat(tmp, &st)<0){
		ONION_ERROR("Error on %s: %s", tmp, strerror(errno));
		return 1;
	}
	while (*urlpath=='/') // No / at the begining.
		urlpath++;

	if (filename){
		if (urlpath[0]==0)
			snprintf(tmp, sizeof(tmp), "%s/%s", basepath, filename);
		else
			snprintf(tmp, sizeof(tmp), "%s/%s/%s", basepath, urlpath, filename);
	}
	else{
		if (urlpath[0]==0)
			snprintf(tmp, sizeof(tmp), "%s/", basepath);
		else{
			snprintf(tmp, sizeof(tmp), "%s/%s", basepath, urlpath);
		}
	}
	if (S_ISDIR(st.st_mode) && urlpath[0]!=0)
		strncat(tmp,"/", sizeof(tmp) - 1);
	ONION_DEBUG0("Props for %s", tmp);
	
	xmlTextWriterStartElement(writer, BAD_CAST "D:response");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:lp1" ,BAD_CAST "DAV:");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:g0" ,BAD_CAST "DAV:");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:a" ,BAD_CAST "http://apache.org/dav/props/");
	
		xmlTextWriterWriteElement(writer, BAD_CAST "D:href", BAD_CAST  tmp); 
		
		/// OK
		xmlTextWriterStartElement(writer, BAD_CAST "D:propstat");
			xmlTextWriterStartElement(writer, BAD_CAST "D:prop");
				if (props&WD_RESOURCE_TYPE){
					xmlTextWriterStartElement(writer, BAD_CAST "lp1:resourcetype");
						if (S_ISDIR(st.st_mode)){ // no marker for other resources
							xmlTextWriterStartElement(writer, BAD_CAST "D:collection");
							xmlTextWriterEndElement(writer);
						}
					xmlTextWriterEndElement(writer);
				}
				if (props&WD_LAST_MODIFIED){
					char time[32];
					onion_shortcut_date_string(st.st_mtime, time);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getlastmodified", BAD_CAST time); 
				}
				if (props&WD_CREATION_DATE){
					char time[32];
					onion_shortcut_date_string_iso(st.st_mtime, time);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:creationdate", BAD_CAST time );
				}
				if (props&WD_CONTENT_LENGTH && !S_ISDIR(st.st_mode)){
					snprintf(tmp, sizeof(tmp), "%d", (int)st.st_size);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getcontentlength", BAD_CAST tmp);
				}
				if (props&WD_CONTENT_TYPE && S_ISDIR(st.st_mode)){
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getcontenttype", BAD_CAST "httpd/unix-directory");
				}
				if (props&WD_ETAG){
					char etag[32];
					onion_shortcut_etag(&st, etag);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getetag", BAD_CAST etag);
				}
				if (props&WD_EXECUTABLE && !S_ISDIR(st.st_mode)){
					if (st.st_mode&0111)
						xmlTextWriterWriteElement(writer, BAD_CAST "a:executable", BAD_CAST "true");
					else
						xmlTextWriterWriteElement(writer, BAD_CAST "a:executable", BAD_CAST "false");
				}

			xmlTextWriterEndElement(writer);
			xmlTextWriterWriteElement(writer, BAD_CAST "D:status", BAD_CAST  "HTTP/1.1 200 OK"); 
		xmlTextWriterEndElement(writer); // /propstat

		/// NOT FOUND
		xmlTextWriterStartElement(writer, BAD_CAST "D:propstat");
			xmlTextWriterStartElement(writer, BAD_CAST "D:prop");
				if (props&WD_CONTENT_LENGTH && S_ISDIR(st.st_mode)){
					snprintf(tmp, sizeof(tmp), "%d", (int)st.st_size);
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:getcontentlength", BAD_CAST "");
				}
				if (props&WD_CONTENT_TYPE && !S_ISDIR(st.st_mode)){
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:getcontenttype", BAD_CAST "");
				}
				if (props&WD_DISPLAY_NAME){
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:displayname", BAD_CAST "");
				}
			xmlTextWriterEndElement(writer);
			xmlTextWriterWriteElement(writer, BAD_CAST "D:status", BAD_CAST  "HTTP/1.1 404 Not Found"); 
		xmlTextWriterEndElement(writer); // /propstat
		
	xmlTextWriterEndElement(writer);
	
	return 0;
}