bool CHTTPRequestHandler::handleImageRequest(std::string p_sObjectId, CHTTPMessage* pRequest, CHTTPMessage* pResponse)
{
	/*cout << "image request" << endl;
	cout << pRequest->getVarAsInt("width") << "x" << pRequest->getVarAsInt("height") << endl;*/
	
  std::stringstream sSql;
  //OBJECT_TYPE       nObjectType;
  std::string       sExt;
  std::string       sPath;
  std::string       sMimeType;
  SQLQuery  				qry;

  
  string sDevice = pRequest->virtualFolderLayout();
  if(pRequest->GetVarExists("vfolder")) {
    sDevice = pRequest->getGetVar("vfolder");
    if(sDevice == "none")
      sDevice = "";
  }

  object_id_t objectId = HexToInt(p_sObjectId);  
  DbObject* tmp = DbObject::createFromObjectId(objectId, &qry, sDevice);
  if(tmp == NULL) {
		CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str());
    return false;
  }

  DbObject obj = *tmp;
  delete tmp;

/*
  bool result = false;

  // real image
  if(obj->type() >= ITEM_IMAGE_ITEM && obj->type() < ITEM_IMAGE_ITEM_MAX) {
    result = handleRealImageRequest(obj, pRequest, pResponse);
  }
  // audio file embedded image
  else if(obj->type() >= ITEM_AUDIO_ITEM && obj->type() < ITEM_AUDIO_ITEM_MAX) {
    result = handleAudioEmbeddedImageRequest(obj, pRequest, pResponse);
  }
  // video file thumbnail image
  else if(obj->type() >= ITEM_VIDEO_ITEM && obj->type() < ITEM_VIDEO_ITEM_MAX) {
    result = handleVideoImageRequest(obj, pRequest, pResponse);
  }
  else {
		CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unsupported image request on object type %d", obj->type());
	}

  delete obj;
  return result;
  */
  

  
  
  string sql = qry.build(SQL_GET_OBJECT_DETAILS, objectId, sDevice);
  qry.select(sql);
    
  if(qry.eof()) {
		CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str());
		return false;
	}

	sPath = qry.result()->asString("PATH") + qry.result()->asString("FILE_NAME");
  sExt  = ExtractFileExt(sPath);
  sMimeType = pRequest->DeviceSettings()->MimeType(sExt);
    
	bool audioFile = false;
	bool videoFile = false;
  bool hasCached = false;
	
	//OBJECT_TYPE type = (OBJECT_TYPE)qry.result()->asInt("TYPE");
	if(obj.type() >= ITEM_IMAGE_ITEM && obj.type() < ITEM_IMAGE_ITEM_MAX) {
		//cout << "request image file " << sPath << endl;
	}
	else if(obj.type() >= ITEM_AUDIO_ITEM && obj.type() < ITEM_AUDIO_ITEM_MAX) {
		//cout << "request image from audio file " << sPath << endl;
		audioFile = true;
	}
	else if(obj.type() >= ITEM_VIDEO_ITEM && obj.type() < ITEM_VIDEO_ITEM_MAX) {
		//cout << "request image from video file " << sPath << endl;
		videoFile = true;

    if(qry.result()->asUInt("ALBUM_ART_ID") > 0) {
      sPath = PathFinder::findThumbnailsDir() + qry.result()->asString("ALBUM_ART_ID") + ".jpg";
      hasCached = fuppes::File::exists(sPath);
      sExt = "jpg";
      sMimeType = pRequest->DeviceSettings()->MimeType(sExt);
    }
	}
	else {
		CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unsupported image request on object type %d", obj.type());
		return false;
	}
    
  if(!fuppes::File::exists(sPath)) {
    CSharedLog::Log(L_EXT, __FILE__, __LINE__, "file: %s not found", sPath.c_str());
    return false;
  }
	//cout << "image request: " << sPath << endl;

	
	int width = pRequest->getVarAsInt("width");
	int height = pRequest->getVarAsInt("height");
	/*int less = pRequest->getVarAsInt("less");
	int greater = pRequest->getVarAsInt("greater");*/
	
	// transcode | scale request via GET
	// and/or embedded image from audio file
	if((width > 0 || height > 0 || audioFile || videoFile) && !hasCached) {
		CSharedLog::Log(L_EXT, __FILE__, __LINE__, "GET transcode %s - %dx%d",  sPath.c_str(), width, height);
		
		size_t inSize = 0;
		size_t outSize = 0;
		unsigned char* inBuffer = (unsigned char*)malloc(1);
		unsigned char* outBuffer = (unsigned char*)malloc(1);
		char tmpMime[100];
		//memset(tmpMime, 0, 1);
		
		// embedded image from audio or video file
		bool transcode = true;
		if(audioFile || videoFile) {

      string plugin;
			if(audioFile) {
        plugin = "taglib";
			}
      else if(videoFile) {
        plugin = "ffmpegthumbnailer";
				transcode = false;
			}

			CMetadataPlugin* metadata = CPluginMgr::metadataPlugin(plugin);
			if(!metadata) {
				CSharedLog::Log(L_EXT, __FILE__, __LINE__, "metadata plugin %s not found", plugin.c_str());
				free(inBuffer);
				free(outBuffer);
				//free(tmpMime);
			  return false;
      }
      
      
			metadata->openFile(sPath);
			inSize = 0;
			if(!metadata->readImage(&tmpMime[0], &inBuffer, &inSize)) {
				metadata->closeFile();
				CSharedLog::Log(L_EXT, __FILE__, __LINE__, "metadata plugin %s failed to read embedded image", "taglib");
				free(inBuffer);
				free(outBuffer);
				//free(tmpMime);
		    return false;
			}
			metadata->closeFile();

      // get the mime type and the extension of the extracted file      
      sMimeType = tmpMime;
      sExt = pRequest->DeviceSettings()->extensionByMimeType(sMimeType);        
			delete metadata;
		} // embedded image



    // an actual image file or a cached image is requested
		else {

      if(hasCached) {
				transcode = false;        
      }
      
			std::fstream fsImg;
			fsImg.open(sPath.c_str(), ios::binary|ios::in);
		  if(fsImg.fail() == 1) {
				CSharedLog::Log(L_EXT, __FILE__, __LINE__, "failed to load image file %s", sPath.c_str());
				free(inBuffer);
				free(outBuffer);
				//free(tmpMime);
			  return false;
			}
			fsImg.seekg(0, ios::end); 
  		inSize = streamoff(fsImg.tellg()); 
  		fsImg.seekg(0, ios::beg);
			inBuffer = (unsigned char*)realloc(inBuffer, inSize);
			fsImg.read((char*)inBuffer, inSize);
			fsImg.close();
		} // image file

		if(transcode) {
			CTranscoderBase* transcoder = CPluginMgr::transcoderPlugin("magickWand");
			if(transcoder == NULL) {
				CSharedLog::Log(L_EXT, __FILE__, __LINE__, "image magick transcoder not available");
				free(inBuffer);
				free(outBuffer);
				//free(tmpMime);
			  return false;
			}
		
			CFileSettings* settings = new CFileSettings(pRequest->DeviceSettings()->FileSettings(sExt));
		
			// TODO fixme. Robert: What are we supposed to be fixing?
			if(!settings->pImageSettings) {
				settings->pImageSettings = new CImageSettings();
			}
			settings->pImageSettings->nHeight = height;
			settings->pImageSettings->nWidth = width;
			settings->pImageSettings->bGreater = true;
			settings->pImageSettings->bLess = true;
		
			transcoder->TranscodeMem(settings,
															 (const unsigned char**)&inBuffer, inSize, &outBuffer, &outSize);
			delete settings;
			delete transcoder;

			pResponse->SetBinContent((char*)outBuffer, outSize);

      // todo: get the actual image dimensions
      //width = width;
      //height = height:
      sMimeType = pRequest->DeviceSettings()->MimeType(sExt);


		} else { // transcode
			pResponse->SetBinContent((char*)inBuffer, inSize);

      width = obj.details()->width();
      height = obj.details()->height();
		}

    
/*
		pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK);
#warning todo: set correct mime type 
    // the current mimeType variable only holds a dummy mime type set above
		pResponse->SetContentType(sMimeType);
*/
		free(inBuffer);
		free(outBuffer);
		//free(tmpMime);
		//return true;
	} // embedded audio or width|height via GET
	

  // a real image file
  else {

    width = obj.details()->width();
    height = obj.details()->height();
    
	  if(pRequest->DeviceSettings()->DoTranscode(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"))) {
		  CSharedLog::Log(L_EXT, __FILE__, __LINE__, "transcode %s",  sPath.c_str());
   
		  sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"));
		  if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_GET) {          
			  DbObject object(qry.result());
			  if(!pResponse->TranscodeContentFromFile(sPath, &object)) {
				  return false;
			  }
		  }
		  else if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_HEAD) {
			  // mark the head response as chunked so
			  // the correct header will be build
			  pResponse->SetIsBinary(true);
				
			  if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_CHUNKED) {
				  pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_CHUNKED);
			  }
			  else if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_STREAM) {
				  pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_NONE);
			  }
		  }
	  }
	  else {
		  sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"));
		  pResponse->LoadContentFromFile(sPath);
	  }

  } // a real image file



  
  // dlna
  if(//(pRequest->dlnaGetContentFeatures() == true) &&
     (pRequest->DeviceSettings()->dlnaVersion() != CMediaServerSettings::dlna_none)) {

    std::string mimeType;
    std::string profile;
    DLNA::getImageProfile(sExt, width, height, profile, mimeType);
    sMimeType = mimeType;

    std::string dlnaFeatures = CContentDirectory::buildDlnaInfo(false, profile);
    std::string dlnaMode = "Interactive";

    pResponse->dlnaContentFeatures(dlnaFeatures);
    pResponse->dlnaTransferMode(dlnaMode);        
  }
  
	// we always set the response type to "200 OK"
	// if the message should be a "206 partial content" 
	// CHTTPServer will change the type
	pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK);
	pResponse->SetContentType(sMimeType);

  return true;
}
Exemple #2
0
bool CUPnPSearch::prepareSQL()
{
	if(m_query.length() > 0 && m_queryCount.length() > 0) {
		return true;
	}

  string sOpenBr;
	string sProp;
	string sOp;
	string sVal;
	string sCloseBr;
	string sLogOp;
  string sPrevLog;
	bool   bNumericProp = false;
	bool   bLikeOp  = false;
  bool   bBuildOK = false;
  bool   bVirtualSearch = false;
  bool   bFirst = true;
  
  stringstream sSql;
  SQLQuery qry;
  
  string sDevice = virtualFolderLayout();
  bVirtualSearch = !sDevice.empty();

  stringstream tmp;
  
  unsigned int nContainerId = GetObjectIDAsUInt(); //GetContainerIdAsUInt();
  if(nContainerId > 0) {
    
    if(m_sParentIds.length() == 0) {
      stringstream sIds;
      sIds << nContainerId;    
      BuildParentIdList(&qry, sIds.str(), sDevice, &m_sParentIds);      
      m_sParentIds = m_sParentIds + sIds.str();      
      //cout << "PARENT ID LIST: " << m_sParentIds << endl; fflush(stdout);
    }
  }
  
  
	m_query = qry.build(SQL_SEARCH_PART_SELECT_FIELDS, 0, sDevice); //"select * ";
	m_queryCount  = qry.build(SQL_SEARCH_PART_SELECT_COUNT, 0, sDevice); //"select count(*) as COUNT ";


  
  /*sSql <<
    "from " <<
    "  MAP_OBJECTS m, OBJECTS o " <<
    "  left join OBJECT_DETAILS d on (d.ID = o.DETAIL_ID) " <<
    "where " <<
    "  o.DEVICE " << sDevice << " and " <<
    "  m.DEVICE " << sDevice << " and " <<
    "  o.OBJECT_ID = m.OBJECT_ID "; */


  sSql << qry.build(SQL_SEARCH_PART_FROM, 0, sDevice);
  
  if(m_sParentIds.length() > 0) {
    sSql << " and " <<
      "  PARENT_ID in (" << m_sParentIds << ") ";        
  }
  
	m_sSearchCriteria = StringReplace(m_sSearchCriteria, "&quot;", "\"");
  m_sSearchCriteria = StringReplace(m_sSearchCriteria, "@", ":");


  RegEx rxSearch("(\\(*) *([\\w+:*\\w*]+) ([=|!=|<|<=|>|>=|contains|doesNotContain|derivedfrom|exists]+) (\".*?[^\\\\]\"|true|false) *(\\)*) *([and|or]*)");
	if(rxSearch.Search(m_sSearchCriteria.c_str())) {
	  do {
		  //cout <<  rxSearch.Match(1) << " X " << rxSearch.Match(2) << " X " << rxSearch.Match(3) << " X " << rxSearch.Match(4) << " X " << rxSearch.Match(5) << " X " << rxSearch.Match(6) << endl;
		
		  sOpenBr  = rxSearch.Match(1);
			sProp    = rxSearch.Match(2);
		  sOp      = rxSearch.Match(3);
			sVal     = rxSearch.Match(4);
			sCloseBr = rxSearch.Match(5);
			sLogOp   = rxSearch.Match(6);
			
			if(sOp.compare("exists") == 0) {
				bBuildOK = false;

				if(sProp.compare(":refID") == 0) {
					bBuildOK = true;

					sProp = "REF_ID";
						
					if(sVal.compare("true") == 0)
					  sOp = ">";
				  else if (sVal.compare("false") == 0)
						sOp = "=";
				
					sVal = "0";
				}
        
			}
			else {
				
				bBuildOK = true;
				
				// replace property
				if(sProp.compare("upnp:class") == 0) {
				  sProp = "TYPE";
					bNumericProp = true;
				}
				else if(sProp.compare("dc:title") == 0) {
				  sProp = "TITLE";
					bNumericProp = false;
				}
        else if(sProp.compare("upnp:artist") == 0) {
				  sProp = "AV_ARTIST";
					bNumericProp = false;
				}
				else if(sProp.compare("upnp:genre") == 0) {
				  sProp = "AV_GENRE";
					bNumericProp = false;
				}
				else if(sProp.compare("upnp:album") == 0) {
				  sProp = "AV_ALBUM";
					bNumericProp = false;
				}
        else if(sProp.compare("res:protocolInfo") == 0) {
				  sPrevLog = sLogOp;
					continue;
				}
				else {
				  bBuildOK = false;
				}
				
				
				// replace operator
				bLikeOp = false;
				if(sOp.compare("contains") == 0) {
				  if(bNumericProp)
					  sOp = "in";
					else
					  sOp = "like";
						
					bLikeOp = true;
				}
				else if(sOp.compare("derivedfrom") == 0) {
				  sOp = "in";
				}
        else if(sOp.compare("=") == 0) {          
        }
				else {
				  bBuildOK = false;
				}

				// trim value
				//cout << "Val: " << sVal << " => ";
			  sVal = sVal.substr(1, sVal.length() - 2);
				//cout << sVal << endl;
				
				// replace value
				if(sProp.compare("TYPE") == 0) { 
          sOp = "in";

          tmp.str("");

				  if(sVal.compare("object.item") == 0) {
						sOp = ">=";
            tmp << ITEM;
					}
					else if(sVal.compare("object.item.imageItem") == 0) {
            sOp = "between";
					  tmp << ITEM_IMAGE_ITEM << " and " << (ITEM_IMAGE_ITEM_MAX - 1); //sVal = "(110, 111)";
          }
					else if(sVal.compare("object.item.audioItem") == 0) {
            sOp = "between";
					  tmp << ITEM_AUDIO_ITEM << " and " << (ITEM_AUDIO_ITEM_MAX - 1); //sVal = "(120, 121, 122)";	
          }
					else if(sVal.compare("object.item.videoItem") == 0){
            sOp = "between";
					  tmp << ITEM_VIDEO_ITEM << " and " << (ITEM_VIDEO_ITEM_MAX - 1); //sVal = "(130, 131, 132, 133)";
          }
					else if(sVal.compare("object.container.person.musicArtist") == 0) {
            sOp = "=";
					  tmp << CONTAINER_PERSON_MUSIC_ARTIST; //sVal = "(11)";
          }
					else if(sVal.compare("object.container.album.musicAlbum") == 0) {
            sOp = "=";
					  tmp << CONTAINER_ALBUM_MUSIC_ALBUM; //sVal = "(31)";
          }          
          else if(sVal.compare("object.container.genre.musicGenre") == 0) {
            sOp = "=";
					  tmp << CONTAINER_GENRE_MUSIC_GENRE; //sVal = "(41)";
          }
          else if(sVal.compare("object.container.genre.movieGenre") == 0) {
            sOp = "=";
					  tmp << CONTAINER_GENRE_MOVIE_GENRE;
          }
          else if (sVal.compare("object.container.playlistContainer") == 0) {
            sOp = "=";
					  tmp << CONTAINER_PLAYLIST_CONTAINER; //sVal = "201)";
          }
					else
					  bBuildOK = false;

          if(bBuildOK)
            sVal = tmp.str();
          tmp.str("");          
				} 
				else if (!bNumericProp) {
				  if(bLikeOp)
				    sVal = "'%" + sVal + "%'";
					else
						sVal = "'" + sVal + "'";
				}
				
			} // != exists
		
		  if(bBuildOK) {
        if(bFirst) {
          sSql << " and ";
          bFirst = false;
        }        
  			sSql << sPrevLog << " " << sOpenBr << sProp << " " << sOp << " " << sVal << sCloseBr << " ";
        sPrevLog = sLogOp;
      }
			else {
			  cout << "error parsing search request!" << endl <<
          "please file a bugreport containing the following lines: " << endl << endl <<
          "=== CUT ===" << endl <<
          m_sSearchCriteria << endl <<
          "=== CUT ===" << endl;
      }
			  
			
		}	while (rxSearch.SearchAgain());
	}
	
	m_query += sSql.str();
	m_queryCount += sSql.str();
	
	sSql.str("");
  
  // order by
  sSql << " order by " << m_sortCriteriaSQL << " ";

  // limit
	if((m_nRequestedCount > 0) || (m_nStartingIndex > 0)) {
    sSql << " limit " << m_nStartingIndex << ", ";
    if(m_nRequestedCount == 0)
      sSql << "-1";
    else
      sSql << m_nRequestedCount;
	}

  // order by and limit are not needed
  // in a count request  
	m_query += sSql.str();

  //cout << m_query << endl;
  
  return true;
}
bool CHTTPRequestHandler::handleAVItemRequest(std::string p_sObjectId, CHTTPMessage* pRequest, CHTTPMessage* pResponse, bool audio, std::string requestExt)
{
  std::stringstream sSql;
  //OBJECT_TYPE       nObjectType;
  std::string       sExt;
  std::string       sPath;
  std::string       sMimeType;
  string            targetExt;
  SQLQuery				  qry;
  bool              bResult = true;  
  bool              transcode = false;
  
  unsigned int      objectId = HexToInt(p_sObjectId);
  
  string sDevice = pRequest->virtualFolderLayout();
  string sql = qry.build(SQL_GET_OBJECT_DETAILS, objectId, sDevice);
  qry.select(sql);
  if(qry.eof()) {
    CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str());
    return false;
  }
  
  // TODO Object Types are still on the todo list
  //nObjectType = (OBJECT_TYPE)atoi(pDb->GetResult()->asString("TYPE").c_str());
  //cout << "OBJECT_TYPE: " << nObjectType << endl;
      
  sPath = qry.result()->asString("PATH") + qry.result()->asString("FILE_NAME");
  sExt  = ExtractFileExt(sPath);
  
  if(!fuppes::File::exists(sPath)) {
    CSharedLog::Log(L_EXT, __FILE__, __LINE__, "file: %s not found", sPath.c_str());
    return false;
  }


  // check for subtitles
  if(!audio && requestExt.compare("srt") == 0) {
    sPath = TruncateFileExt(sPath) + "." + requestExt;
    cout << "SUB REQUEST: " << sPath << "*" << endl;
    if(!fuppes::File::exists(sPath))
      return false;

    pResponse->LoadContentFromFile(sPath);
    pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK);
    pResponse->SetContentType("application/x-subrip");
    return true;
  }

  transcode = pRequest->DeviceSettings()->DoTranscode(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"));
  sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"));
  targetExt = pRequest->DeviceSettings()->Extension(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"));


  if(!transcode) {
    pResponse->LoadContentFromFile(sPath);
  }  
  else {
    CSharedLog::Log(L_EXT, __FILE__, __LINE__, "transcode %s",  sPath.c_str());
 
    if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_GET) {  
      DbObject object(qry.result());
      bResult = pResponse->TranscodeContentFromFile(sPath, &object);
    }
    else if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_HEAD) {
      // mark the head response as chunked so
      // the correct header will be build
      pResponse->SetIsBinary(true);
      bResult = true;

      if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_CHUNKED) {
        pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_CHUNKED);
      }
      else if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_STREAM) {
        pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_NONE);
      }
    }
  }
  
  

  // dlna
  if(//(CPluginMgr::dlnaPlugin() != NULL) &&
     //(pRequest->dlnaGetContentFeatures() == true) &&
     (pRequest->DeviceSettings()->dlnaVersion() != CMediaServerSettings::dlna_none)) {

    bool hasProfile = false;
    std::string profile;
    if(audio) {

      int channels = 0;
      int bitrate = 0;
      if(!transcode) {
        channels = qry.result()->asInt("A_CHANNELS");
        bitrate = qry.result()->asInt("A_BITRATE");
      }

      if(audio) {
        hasProfile = DLNA::getAudioProfile(targetExt, channels, bitrate, profile, sMimeType);
      }
      else {
        //hasProfile = CPluginMgr::dlnaPlugin()->getVideoProfile(targetExt, channels, bitrate, &profile, &sMimeType);
      }
    }
    else {
      //CPluginMgr::dlnaPlugin()->getVideoProfile();
    }

    //if(hasProfile) {
      std::string dlnaFeatures = CContentDirectory::buildDlnaInfo(transcode, profile);
      std::string dlnaMode = (transcode ? "Streaming" : "Interactive");

      pResponse->dlnaContentFeatures(dlnaFeatures);
      pResponse->dlnaTransferMode(dlnaMode);
    //}
  } // end dlna
  
  // we always set the response type to "200 OK"
  // if the message should be a "206 partial content" 
  // CHTTPServer will change the type
  pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK);
  pResponse->SetContentType(sMimeType);
  
  bResult = true;
    
  
  return bResult;
}