swad-core/swad_media.c

2014 lines
68 KiB
C

// swad_media.c: processing of image/video uploaded in a form
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2019 Antonio Cañas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General 3 License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/
#define _GNU_SOURCE // For strcasestr, asprintf
#include <linux/limits.h> // For PATH_MAX
#include <stdbool.h> // For boolean type
#include <stdio.h> // For asprintf
#include <stdlib.h> // For exit, system, malloc, free, etc
#include <string.h> // For string functions
#include <sys/stat.h> // For lstat
#include <sys/types.h> // For lstat
#include <sys/wait.h> // For the macro WEXITSTATUS
#include <unistd.h> // For unlink, lstat
#include "swad_box.h"
#include "swad_config.h"
#include "swad_cookie.h"
#include "swad_database.h"
#include "swad_global.h"
#include "swad_file.h"
#include "swad_file_browser.h"
#include "swad_form.h"
#include "swad_media.h"
/*****************************************************************************/
/****************************** Public constants *****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Internal constants ****************************/
/*****************************************************************************/
const char *Med_StringsTypeDB[Med_NUM_TYPES] =
{
"none", // Med_NONE
"jpg", // Med_JPG
"gif", // Med_GIF
"mp4", // Med_MP4
"webm", // Med_WEBM
"ogg", // Med_OGG
"youtube", // Med_YOUTUBE
"embed", // Med_EMBED
};
const char *Med_Extensions[Med_NUM_TYPES] =
{
"", // Med_NONE
"jpg", // Med_JPG
"gif", // Med_GIF
"mp4", // Med_MP4
"webm", // Med_WEBM
"ogg", // Med_OGG
"", // Med_YOUTUBE
"", // Med_EMBED
};
#define Med_MAX_SIZE_GIF (5UL * 1024UL * 1024UL) // 5 MiB
#define Med_MAX_SIZE_MP4 (5UL * 1024UL * 1024UL) // 5 MiB
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
#define Med_NUM_FORM_TYPES 4
typedef enum
{
Med_FORM_NONE = 0,
Med_FORM_FILE = 1,
Med_FORM_YOUTUBE = 2,
Med_FORM_EMBED = 3,
} Med_FormType_t;
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/************************* Internal global variables *************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Internal prototypes ***************************/
/*****************************************************************************/
static void Med_ResetMediaExceptURLAndTitle (struct Media *Media);
static void Med_FreeMediaURL (struct Media *Media);
static void Med_FreeMediaTitle (struct Media *Media);
static void Med_PutIconMediaUploader (const char UniqueId[Frm_MAX_BYTES_ID + 1],
const char *IdSuffix,
const char *FunctionName,
const char *Icon,
const char *Title);
static void Med_PutHiddenFormTypeMediaUploader (const char UniqueId[Frm_MAX_BYTES_ID + 1],
const char *IdSuffix,
struct ParamUploadMedia *ParamUploadMedia,
Med_FormType_t FormType);
static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction);
static Med_FormType_t Usr_GetFormTypeFromForm (struct ParamUploadMedia *ParamUploadMedia);
static void Usr_GetURLFromForm (const char *ParamName,struct Media *Media);
static void Usr_GetTitleFromForm (const char *ParamName,struct Media *Media);
static void Med_GetAndProcessFileFromForm (const char *ParamFile,
struct Media *Media);
static bool Med_DetectIfAnimated (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1]);
static void Med_ProcessJPG (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1]);
static void Med_ProcessGIF (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1]);
static void Med_ProcessVideo (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1]);
static int Med_ResizeImage (struct Media *Media,
const char PathFileOriginal[PATH_MAX + 1],
const char PathFileProcessed[PATH_MAX + 1]);
static int Med_GetFirstFrame (const char PathFileOriginal[PATH_MAX + 1],
const char PathFileProcessed[PATH_MAX + 1]);
static void Med_GetAndProcessYouTubeFromForm (const char *ParamURL,
struct Media *Media);
static void Med_GetAndProcessEmbedFromForm (const char *ParamURL,
struct Media *Media);
static bool Med_MoveTmpFileToDefDir (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *Extension);
static void Med_ShowJPG (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia);
static void Med_ShowGIF (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia);
static void Med_ShowVideo (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia);
static void Med_ShowYoutube (struct Media *Media,const char *ClassMedia);
static void Med_ShowEmbed (struct Media *Media,const char *ClassMedia);
static void Med_AlertThirdPartyCookies (void);
static Med_Type_t Med_GetTypeFromStrInDB (const char *Str);
static Med_Type_t Med_GetTypeFromExtAndMIME (const char *Extension,
const char *MIMEType);
static const char *Med_GetStringTypeForDB (Med_Type_t Type);
/*****************************************************************************/
/********************** Media (image/video) constructor **********************/
/*****************************************************************************/
// Every struct Media must be initialized with this constructor function after it is declared
// Every call to constructor must have a call to destructor
void Med_MediaConstructor (struct Media *Media)
{
Med_ResetMediaExceptURLAndTitle (Media);
Media->URL = NULL;
Media->Title = NULL;
}
/*****************************************************************************/
/********************** Media (image/video) destructor ***********************/
/*****************************************************************************/
// Every call to constructor must have a call to destructor
void Med_MediaDestructor (struct Media *Media)
{
Med_ResetMedia (Media);
}
/*****************************************************************************/
/********************* Reset media (image/video) fields **********************/
/*****************************************************************************/
// Media must be constructed before calling this function
void Med_ResetMedia (struct Media *Media)
{
Med_ResetMediaExceptURLAndTitle (Media);
Med_FreeMediaURL (Media);
Med_FreeMediaTitle (Media);
}
/*****************************************************************************/
/***************** Reset media fields except URL and title *******************/
/*****************************************************************************/
static void Med_ResetMediaExceptURLAndTitle (struct Media *Media)
{
Media->MedCod = -1L;
Media->Action = Med_ACTION_NO_MEDIA;
Media->Status = Med_STATUS_NONE;
Media->Name[0] = '\0';
Media->Type = Med_TYPE_NONE;
}
/*****************************************************************************/
/******************************* Free image URL ******************************/
/*****************************************************************************/
static void Med_FreeMediaURL (struct Media *Media)
{
// Media->URL is initialized to NULL in constructor
if (Media->URL)
{
free ((void *) Media->URL);
Media->URL = NULL;
}
}
/*****************************************************************************/
/****************************** Free image title *****************************/
/*****************************************************************************/
static void Med_FreeMediaTitle (struct Media *Media)
{
// Media->Title is initialized to NULL in constructor
if (Media->Title)
{
free ((void *) Media->Title);
Media->Title = NULL;
}
}
/*****************************************************************************/
/**** Get media name, title and URL from a query result and copy to struct ***/
/*****************************************************************************/
void Med_GetMediaDataByCod (struct Media *Media)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumRows;
size_t Length;
/***** Get data of a media from database *****/
NumRows = DB_QuerySELECT (&mysql_res,"can not get data of a post",
"SELECT Type," // row[0]
"Name," // row[1]
"URL," // row[2]
"Title" // row[3]
" FROM media WHERE MedCod=%ld",
Media->MedCod);
/***** Result should have a unique row *****/
if (NumRows == 0) // Media not found
/***** Reset media data *****/
Med_ResetMedia (Media);
else if (NumRows == 1)
{
/***** Get row *****/
row = mysql_fetch_row (mysql_res);
/***** Convert type string (row[0]) to type *****/
Media->Type = Med_GetTypeFromStrInDB (row[0]);
/***** Set status of media file *****/
Media->Status = (Media->Type != Med_TYPE_NONE) ? Med_STORED_IN_DB :
Med_STATUS_NONE;
/***** Copy media name (row[1]) to struct *****/
Str_Copy (Media->Name,row[1],
Med_BYTES_NAME);
/***** Copy media URL (row[2]) to struct *****/
// Media->URL can be empty or filled with previous value
// If filled ==> free it
Med_FreeMediaURL (Media);
if (row[2][0])
{
/* Get and limit length of the URL */
Length = strlen (row[2]);
if (Length > Cns_MAX_BYTES_WWW)
Length = Cns_MAX_BYTES_WWW;
if ((Media->URL = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image URL.");
Str_Copy (Media->URL,row[2],
Length);
}
/***** Copy media title (row[3]) to struct *****/
// Media->Title can be empty or filled with previous value
// If filled ==> free it
Med_FreeMediaTitle (Media);
if (row[3][0])
{
/* Get and limit length of the title */
Length = strlen (row[3]);
if (Length > Med_MAX_BYTES_TITLE)
Length = Med_MAX_BYTES_TITLE;
if ((Media->Title = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image title.");
Str_Copy (Media->Title,row[3],
Length);
}
}
else
Lay_ShowErrorAndExit ("Internal error in database when getting media data.");
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/********* Draw input fields to upload an image/video inside a form **********/
/*****************************************************************************/
void Med_PutMediaUploader (int NumMediaInForm,const char *ClassInput)
{
extern const char *Hlp_Multimedia;
extern const char *Txt_Multimedia;
extern const char *Txt_Image_video;
extern const char *Txt_Title_attribution;
extern const char *Txt_Link;
struct ParamUploadMedia ParamUploadMedia;
char Id[Frm_MAX_BYTES_ID + 1];
/***** Set names of parameters depending on number of media in form *****/
Med_SetParamNames (&ParamUploadMedia,NumMediaInForm);
/***** Create unique id for this media uploader *****/
Frm_SetUniqueId (Id);
/***** Start media uploader container *****/
fprintf (Gbl.F.Out,"<div class=\"MED_UPLOADER\">"); // container
/***** Icon 'clip' *****/
fprintf (Gbl.F.Out,"<div id=\"%s_med_ico\">" // <id>_med_ico
"<a href=\"\""
" onclick=\"mediaActivateMediaUploader('%s');return false;\">"
"<img src=\"%s/paperclip.svg\""
" alt=\"%s\" title=\"%s\""
" class=\"ICO_HIGHLIGHT ICOx16\" />"
"</a>"
"</div>", // <id>_med_ico
Id,Id,
Cfg_URL_ICON_PUBLIC,
Txt_Multimedia,Txt_Multimedia);
/***** Start media uploader *****/
fprintf (Gbl.F.Out,"<div id=\"%s_med_upl\"" // container <id>_med_upl
" style=\"display:none;\" />",
Id);
/***** Start box *****/
Box_StartBox (NULL,Txt_Multimedia,NULL,
Hlp_Multimedia,Box_NOT_CLOSABLE);
/***** Action to perform on media *****/
Par_PutHiddenParamUnsigned (ParamUploadMedia.Action,(unsigned) Med_ACTION_NEW_MEDIA);
/***** Icons *****/
/* Start icons */
fprintf (Gbl.F.Out,"<div class=\"PREF_CONTAINERS\">" // icons containers
"<div class=\"PREF_CONTAINER\">"); // icons container
/* Upload icon */
Med_PutIconMediaUploader (Id,"ico_upl", // <id>_ico_upl
"mediaClickOnActivateUpload",
"file-image.svg",
Txt_Image_video);
/* YouTube icon */
Med_PutIconMediaUploader (Id,"ico_you", // <id>_ico_you
"mediaClickOnActivateYoutube",
"youtube-brands.svg",
"YouTube");
/* Embed icon */
Med_PutIconMediaUploader (Id,"ico_emb", // <id>_ico_emb
"mediaClickOnActivateEmbed",
"code.svg",
"Embed");
/* End icons */
fprintf (Gbl.F.Out,"</div>" // icons container
"</div>"); // icons containers
/***** Hidden field with form type *****/
/* Upload file */
Med_PutHiddenFormTypeMediaUploader (Id,"par_upl", // <id>_par_upl
&ParamUploadMedia,Med_FORM_FILE);
/* YouTube embedded video */
Med_PutHiddenFormTypeMediaUploader (Id,"par_you", // <id>_par_you
&ParamUploadMedia,Med_FORM_YOUTUBE);
/* Other embedded media */
Med_PutHiddenFormTypeMediaUploader (Id,"par_emb", // <id>_par_emb
&ParamUploadMedia,Med_FORM_EMBED);
/***** Media file *****/
fprintf (Gbl.F.Out,"<div>"
"<input id=\"%s_fil\" type=\"file\"" // <id>_fil
" name=\"%s\" accept=\"image/,video/\""
" class=\"%s\" disabled=\"disabled\""
" style=\"display:none;\" />"
"</div>", // <id>_fil
Id,
ParamUploadMedia.File,
ClassInput);
/***** Media URL *****/
fprintf (Gbl.F.Out,"<div>"
"<input id=\"%s_url\" type=\"url\"" // <id>_url
" name=\"%s\" placeholder=\"%s\""
" class=\"%s\" maxlength=\"%u\" value=\"\""
" disabled=\"disabled\""
" style=\"display:none;\" />"
"</div>", // <id>_url
Id,
ParamUploadMedia.URL,Txt_Link,
ClassInput,Cns_MAX_CHARS_WWW);
/***** Media title *****/
fprintf (Gbl.F.Out,"<div>"
"<input id=\"%s_tit\" type=\"text\"" // <id>_tit
" name=\"%s\" placeholder=\"%s\""
" class=\"%s\" maxlength=\"%u\" value=\"\""
" disabled=\"disabled\""
" style=\"display:none;\" />"
"</div>", // <id>_tit
Id,
ParamUploadMedia.Title,Txt_Title_attribution,
ClassInput,Med_MAX_CHARS_TITLE);
/***** End box *****/
Box_EndBox ();
/***** End media uploader *****/
fprintf (Gbl.F.Out,"</div>"); // container <id>_med_upl
/***** End media uploader container *****/
fprintf (Gbl.F.Out,"</div>"); // container
}
/*****************************************************************************/
/*********************** Put an icon in media uploader ***********************/
/*****************************************************************************/
static void Med_PutIconMediaUploader (const char UniqueId[Frm_MAX_BYTES_ID + 1],
const char *IdSuffix,
const char *FunctionName,
const char *Icon,
const char *Title)
{
/***** Icon to activate form in media uploader *****/
fprintf (Gbl.F.Out,"<div id=\"%s_%s\" class=\"PREF_OFF\">" // <id>_IdSuffix
"<a href=\"\" onclick=\"%s('%s');return false;\">"
"<img src=\"%s/%s\" alt=\"%s\" title=\"%s\""
" class=\"ICO_HIGHLIGHT ICOx16\" />"
"</a>"
"</div>", // <id>_IdSuffix
UniqueId,IdSuffix,
FunctionName,UniqueId,
Cfg_URL_ICON_PUBLIC,Icon,Title,Title);
}
/*****************************************************************************/
/******** Put a hidden input field with form type in media uploader **********/
/*****************************************************************************/
static void Med_PutHiddenFormTypeMediaUploader (const char UniqueId[Frm_MAX_BYTES_ID + 1],
const char *IdSuffix,
struct ParamUploadMedia *ParamUploadMedia,
Med_FormType_t FormType)
{
/***** Hidden field with form type *****/
/* Upload file */
fprintf (Gbl.F.Out,"<input type=\"hidden\" id=\"%s_%s\"" // <id>_IdSuffix
" name=\"%s\" value=\"%u\" disabled=\"disabled\" />",
UniqueId,IdSuffix,
ParamUploadMedia->FormType,(unsigned) FormType);
}
/*****************************************************************************/
/******************** Get media (image/video) from form **********************/
/*****************************************************************************/
// Media constructor must be called before calling this function
// If NumMediaInForm < 0, params have no suffix
// If NumMediaInForm >= 0, the number is a suffix of the params
void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media,
void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media),
const char *SectionForAlerts)
{
extern const char *Txt_Error_sending_or_processing_image_video;
struct ParamUploadMedia ParamUploadMedia;
Med_Action_t Action;
Med_FormType_t FormType;
/***** Set names of parameters depending on number of media in form *****/
Med_SetParamNames (&ParamUploadMedia,NumMediaInForm);
/***** Get action and initialize media (image/video)
(except title, that will be get after the media file) *****/
Action = Med_GetMediaActionFromForm (ParamUploadMedia.Action);
/***** Get the media (image/video) name and the file *****/
switch (Action)
{
case Med_ACTION_NEW_MEDIA: // Upload new image/video
/***** Get form type *****/
FormType = Usr_GetFormTypeFromForm (&ParamUploadMedia);
/***** Get new media *****/
switch (FormType)
{
case Med_FORM_FILE:
Media->Action = Med_ACTION_NEW_MEDIA;
/* Get image/video (if present ==>
process and create temporary file) */
Med_GetAndProcessFileFromForm (ParamUploadMedia.File,Media);
/* Check status of media after getting and processing it */
if (Media->Status == Med_PROCESSED)
{
Usr_GetURLFromForm (ParamUploadMedia.URL,Media);
Usr_GetTitleFromForm (ParamUploadMedia.Title,Media);
}
break;
case Med_FORM_YOUTUBE:
Media->Action = Med_ACTION_NEW_MEDIA;
/* Get and process embed YouTube video from form */
Med_GetAndProcessYouTubeFromForm (ParamUploadMedia.URL,Media);
break;
case Med_FORM_EMBED:
Media->Action = Med_ACTION_NEW_MEDIA;
/* Get and process other embed media from form */
Med_GetAndProcessEmbedFromForm (ParamUploadMedia.URL,Media);
break;
default: // No media form selected
Media->Action = Med_ACTION_NO_MEDIA;
break;
}
/***** Check status of media *****/
if (FormType != Med_FORM_NONE && // A media form is selected
Media->Status != Med_PROCESSED) // No media successfully processed
{
/* Create alert with warning */
Ale_CreateAlert (Ale_WARNING,SectionForAlerts,
Txt_Error_sending_or_processing_image_video);
/* Reset media (no media will be saved into database) */
Med_ResetMedia (Media);
}
break;
case Med_ACTION_KEEP_MEDIA: // Keep current image/video unchanged
Media->Action = Med_ACTION_KEEP_MEDIA;
/***** Get media name *****/
if (GetMediaFromDB != NULL)
GetMediaFromDB (NumMediaInForm,Media);
break;
default: // Unknown action
Media->Action = Med_ACTION_NO_MEDIA;
break;
}
}
/*****************************************************************************/
/********* Set parameters names depending on number of media in form *********/
/*****************************************************************************/
// If NumMediaInForm < 0, params have no suffix
// If NumMediaInForm >= 0, the number is a suffix of the params
void Med_SetParamNames (struct ParamUploadMedia *ParamUploadMedia,int NumMediaInForm)
{
if (NumMediaInForm < 0) // One unique media in form ==> no suffix needed
{
Str_Copy (ParamUploadMedia->Action ,"MedAct",
Med_MAX_BYTES_PARAM_UPLOAD_MEDIA);
Str_Copy (ParamUploadMedia->FormType,"MedFrm",
Med_MAX_BYTES_PARAM_UPLOAD_MEDIA);
Str_Copy (ParamUploadMedia->File ,"MedFil",
Med_MAX_BYTES_PARAM_UPLOAD_MEDIA);
Str_Copy (ParamUploadMedia->Title ,"MedTit",
Med_MAX_BYTES_PARAM_UPLOAD_MEDIA);
Str_Copy (ParamUploadMedia->URL ,"MedURL",
Med_MAX_BYTES_PARAM_UPLOAD_MEDIA);
}
else // Several video/images in form ==> add suffix
{
snprintf (ParamUploadMedia->Action ,sizeof (ParamUploadMedia->Action),
"MedAct%u",
NumMediaInForm);
snprintf (ParamUploadMedia->FormType,sizeof (ParamUploadMedia->Action),
"MedFrm%u",
NumMediaInForm);
snprintf (ParamUploadMedia->File ,sizeof (ParamUploadMedia->File),
"MedFil%u",
NumMediaInForm);
snprintf (ParamUploadMedia->Title ,sizeof (ParamUploadMedia->Title),
"MedTit%u",
NumMediaInForm);
snprintf (ParamUploadMedia->URL ,sizeof (ParamUploadMedia->URL),
"MedURL%u",
NumMediaInForm);
}
}
/*****************************************************************************/
/************************* Get media action from form ************************/
/*****************************************************************************/
static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction)
{
/***** Get parameter with the action to perform on media *****/
return (Med_Action_t)
Par_GetParToUnsignedLong (ParamAction,
0,
Med_NUM_ACTIONS - 1,
(unsigned long) Med_ACTION_DEFAULT);
}
/*****************************************************************************/
/********************* Get from form the type of form ************************/
/*****************************************************************************/
static Med_FormType_t Usr_GetFormTypeFromForm (struct ParamUploadMedia *ParamUploadMedia)
{
return (Med_FormType_t) Par_GetParToUnsignedLong (ParamUploadMedia->FormType,
0,
Med_NUM_FORM_TYPES - 1,
(unsigned long) Med_FORM_NONE);
}
/*****************************************************************************/
/********************* Get from form the type of form ************************/
/*****************************************************************************/
static void Usr_GetURLFromForm (const char *ParamName,struct Media *Media)
{
char URL[Cns_MAX_BYTES_WWW + 1];
size_t Length;
/***** Get media URL from form *****/
Par_GetParToText (ParamName,URL,Cns_MAX_BYTES_WWW);
/* If the URL coming from the form is empty, keep current media URL unchanged
If not empty, copy it to current media URL */
if ((Length = strlen (URL)) > 0)
{
/* Overwrite current URL (empty or coming from database)
with the URL coming from the form */
Med_FreeMediaURL (Media);
if ((Media->URL = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for media URL.");
Str_Copy (Media->URL,URL,
Length);
}
}
/*****************************************************************************/
/********************* Get from form the type of form ************************/
/*****************************************************************************/
static void Usr_GetTitleFromForm (const char *ParamName,struct Media *Media)
{
char Title[Med_MAX_BYTES_TITLE + 1];
size_t Length;
/***** Get image/video title from form *****/
Par_GetParToText (ParamName,Title,Med_MAX_BYTES_TITLE);
/* If the title coming from the form is empty, keep current media title unchanged
If not empty, copy it to current media title */
if ((Length = strlen (Title)) > 0)
{
/* Overwrite current title (empty or coming from database)
with the title coming from the form */
Med_FreeMediaTitle (Media);
if ((Media->Title = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for media title.");
Str_Copy (Media->Title,Title,
Length);
}
}
/*****************************************************************************/
/**************************** Get media from form ****************************/
/*****************************************************************************/
static void Med_GetAndProcessFileFromForm (const char *ParamFile,
struct Media *Media)
{
struct Param *Param;
char FileNameImgSrc[PATH_MAX + 1];
char *PtrExtension;
size_t LengthExtension;
char MIMEType[Brw_MAX_BYTES_MIME_TYPE + 1];
char PathFileOrg[PATH_MAX + 1]; // Full name of original uploaded file
/***** Set media status *****/
Media->Status = Med_STATUS_NONE;
/***** Get filename and MIME type *****/
Param = Fil_StartReceptionOfFile (ParamFile,FileNameImgSrc,MIMEType);
if (!FileNameImgSrc[0]) // No file present
return;
/* Get filename extension */
if ((PtrExtension = strrchr (FileNameImgSrc,(int) '.')) == NULL)
return;
// PtrExtension now points to last '.' in file
PtrExtension++;
// PtrExtension now points to first char in extension
LengthExtension = strlen (PtrExtension);
if (LengthExtension < Fil_MIN_BYTES_FILE_EXTENSION ||
LengthExtension > Fil_MAX_BYTES_FILE_EXTENSION)
return;
/* Check extension */
Media->Type = Med_GetTypeFromExtAndMIME (PtrExtension,MIMEType);
if (Media->Type == Med_TYPE_NONE)
return;
/***** Assign a unique name for the media *****/
Cry_CreateUniqueNameEncrypted (Media->Name);
/***** Create private directories if not exist *****/
/* Create private directory for images/videos if it does not exist */
Fil_CreateDirIfNotExists (Cfg_PATH_MEDIA_PRIVATE);
/* Create temporary private directory for images/videos if it does not exist */
Fil_CreateDirIfNotExists (Cfg_PATH_MEDIA_TMP_PRIVATE);
/***** End the reception of original not processed media
(it may be very big) into a temporary file *****/
Media->Status = Med_STATUS_NONE;
snprintf (PathFileOrg,sizeof (PathFileOrg),
"%s/%s_original.%s",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name,PtrExtension);
if (Fil_EndReceptionOfFile (PathFileOrg,Param)) // Success
{
/***** Detect if animated GIF *****/
if (Media->Type == Med_GIF)
if (!Med_DetectIfAnimated (Media,PathFileOrg))
Media->Type = Med_JPG;
/***** Process media depending on the media file extension *****/
switch (Media->Type)
{
case Med_JPG:
Med_ProcessJPG (Media,PathFileOrg);
break;
case Med_GIF:
Med_ProcessGIF (Media,PathFileOrg);
break;
case Med_MP4:
case Med_WEBM:
case Med_OGG:
Med_ProcessVideo (Media,PathFileOrg);
break;
default:
break;
}
}
/***** Remove temporary original file *****/
if (Fil_CheckIfPathExists (PathFileOrg))
unlink (PathFileOrg);
}
/*****************************************************************************/
/********************* Detect if a GIF image is animated *********************/
/*****************************************************************************/
// Return true if animated
// Return false if static or error
static bool Med_DetectIfAnimated (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1])
{
char PathFileTxtTmp[PATH_MAX + 1];
char Command[128 + PATH_MAX * 2];
int ReturnCode;
FILE *FileTxtTmp; // Temporary file with the output of the command
int NumFrames = 0;
/***** Build path to temporary text file *****/
snprintf (PathFileTxtTmp,sizeof (PathFileTxtTmp),
"%s/%s.txt",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name);
/***** Execute system command to get number of frames in GIF *****/
snprintf (Command,sizeof (Command),
"identify -format '%%n\n' %s | head -1 > %s",
PathFileOrg,PathFileTxtTmp);
ReturnCode = system (Command);
if (ReturnCode == -1)
return false; // Error
ReturnCode = WEXITSTATUS(ReturnCode);
if (ReturnCode != 0)
return false; // Error
/***** Read temporary file *****/
if ((FileTxtTmp = fopen (PathFileTxtTmp,"rb")) == NULL)
return false; // Error
if (fscanf (FileTxtTmp,"%d",&NumFrames) != 1)
return false; // Error
fclose (FileTxtTmp);
/***** Remove temporary file *****/
unlink (PathFileTxtTmp);
return (NumFrames > 1); // NumFrames > 1 ==> Animated
}
/*****************************************************************************/
/************* Process original image generating processed JPG ***************/
/*****************************************************************************/
static void Med_ProcessJPG (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1])
{
extern const char *Txt_The_file_could_not_be_processed_successfully;
char PathFileJPGTmp[PATH_MAX + 1]; // Full name of temporary processed file
/***** Convert original media to temporary JPG processed file
by calling to program that makes the conversion *****/
snprintf (PathFileJPGTmp,sizeof (PathFileJPGTmp),
"%s/%s.%s",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name,Med_Extensions[Med_JPG]);
if (Med_ResizeImage (Media,PathFileOrg,PathFileJPGTmp) == 0) // On success ==> 0 is returned
/* Success */
Media->Status = Med_PROCESSED;
else // Error processing media
{
/* Remove temporary destination media file */
if (Fil_CheckIfPathExists (PathFileJPGTmp))
unlink (PathFileJPGTmp);
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
}
}
/*****************************************************************************/
/******* Process original GIF image generating processed PNG and GIF *********/
/*****************************************************************************/
static void Med_ProcessGIF (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1])
{
extern const char *Txt_The_file_could_not_be_processed_successfully;
extern const char *Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X;
struct stat FileStatus;
char PathFilePNGTmp[PATH_MAX + 1]; // Full name of temporary processed file
char PathFileGIFTmp[PATH_MAX + 1]; // Full name of temporary processed file
char FileSizeStr[Fil_MAX_BYTES_FILE_SIZE_STRING + 1];
/***** Check size of media file *****/
if (lstat (PathFileOrg,&FileStatus) == 0) // On success ==> 0 is returned
{
/* Success */
if (FileStatus.st_size <= (__off_t) Med_MAX_SIZE_GIF)
{
/* File size correct */
/***** Get first frame of orifinal GIF file
and save it on temporary PNG file */
snprintf (PathFilePNGTmp,sizeof (PathFilePNGTmp),
"%s/%s.png",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name);
if (Med_GetFirstFrame (PathFileOrg,PathFilePNGTmp) == 0) // On success ==> 0 is returned
{
/* Success */
/***** Move original GIF file to temporary GIF file *****/
snprintf (PathFileGIFTmp,sizeof (PathFileGIFTmp),
"%s/%s.%s",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name,Med_Extensions[Med_GIF]);
if (rename (PathFileOrg,PathFileGIFTmp)) // Fail
{
/* Remove temporary PNG file */
if (Fil_CheckIfPathExists (PathFilePNGTmp))
unlink (PathFilePNGTmp);
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
}
else // Success
Media->Status = Med_PROCESSED;
}
else // Error getting first frame
{
/* Remove temporary PNG file */
if (Fil_CheckIfPathExists (PathFilePNGTmp))
unlink (PathFilePNGTmp);
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
}
}
else // Size exceeded
{
/* Show warning alert */
Fil_WriteFileSizeBrief ((double) Med_MAX_SIZE_GIF,FileSizeStr);
Ale_CreateAlert (Ale_WARNING,NULL,
Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X,
FileSizeStr);
}
}
else // Error getting file data
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
}
/*****************************************************************************/
/*********** Process original MP4 video generating processed MP4 *************/
/*****************************************************************************/
static void Med_ProcessVideo (struct Media *Media,
const char PathFileOrg[PATH_MAX + 1])
{
extern const char *Txt_The_file_could_not_be_processed_successfully;
extern const char *Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X;
struct stat FileStatus;
char PathFileTmp[PATH_MAX + 1]; // Full name of temporary processed file
char FileSizeStr[Fil_MAX_BYTES_FILE_SIZE_STRING + 1];
/***** Check size of media file *****/
if (lstat (PathFileOrg,&FileStatus) == 0) // On success ==> 0 is returned
{
/* Success */
if (FileStatus.st_size <= (__off_t) Med_MAX_SIZE_MP4)
{
/* File size correct */
/***** Move original video file to temporary MP4 file *****/
snprintf (PathFileTmp,sizeof (PathFileTmp),
"%s/%s.%s",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name,Med_Extensions[Media->Type]);
if (rename (PathFileOrg,PathFileTmp)) // Fail
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
else // Success
Media->Status = Med_PROCESSED;
}
else // Size exceeded
{
/* Show warning alert */
Fil_WriteFileSizeBrief ((double) Med_MAX_SIZE_MP4,FileSizeStr);
Ale_CreateAlert (Ale_WARNING,NULL,
Txt_The_size_of_the_file_exceeds_the_maximum_allowed_X,
FileSizeStr);
}
}
else // Error getting file data
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_file_could_not_be_processed_successfully);
}
/*****************************************************************************/
/****************************** Resize image *********************************/
/*****************************************************************************/
// Return 0 on success
// Return != 0 on error
static int Med_ResizeImage (struct Media *Media,
const char PathFileOriginal[PATH_MAX + 1],
const char PathFileProcessed[PATH_MAX + 1])
{
char Command[256 + PATH_MAX * 2];
int ReturnCode;
snprintf (Command,sizeof (Command),
"convert %s -resize '%ux%u>' -quality %u %s",
PathFileOriginal,
Media->Width,
Media->Height,
Media->Quality,
PathFileProcessed);
ReturnCode = system (Command);
if (ReturnCode == -1)
Lay_ShowErrorAndExit ("Error when running command to process media.");
ReturnCode = WEXITSTATUS(ReturnCode);
return ReturnCode;
}
/*****************************************************************************/
/************ Process original media generating processed media **************/
/*****************************************************************************/
// Return 0 on success
// Return != 0 on error
static int Med_GetFirstFrame (const char PathFileOriginal[PATH_MAX + 1],
const char PathFileProcessed[PATH_MAX + 1])
{
char Command[128 + PATH_MAX * 2];
int ReturnCode;
snprintf (Command,sizeof (Command),
"convert '%s[0]' %s",
PathFileOriginal,
PathFileProcessed);
ReturnCode = system (Command);
if (ReturnCode == -1)
Lay_ShowErrorAndExit ("Error when running command to process media.");
ReturnCode = WEXITSTATUS(ReturnCode);
return ReturnCode;
}
/*****************************************************************************/
/************* Get link from form and transform to YouTube code **************/
/*****************************************************************************/
static void Med_GetAndProcessYouTubeFromForm (const char *ParamURL,
struct Media *Media)
{
extern const char Str_BIN_TO_BASE64URL[64 + 1];
char *PtrHost = NULL;
char *PtrPath = NULL;
char *PtrParams = NULL;
char *PtrCode = NULL;
size_t CodeLength;
enum
{
WRONG, // Bad formed YouTube URL
SHORT, // youtu.be
FULL, // www.youtube.com/watch?
EMBED, // www.youtube.com/embed/
} YouTube = WRONG;
bool CodeFound = false;
/***** Set media status *****/
Media->Status = Med_STATUS_NONE;
/***** Get embed URL from form *****/
Usr_GetURLFromForm (ParamURL,Media);
/***** Process URL trying to convert it to a YouTube embed URL *****/
if (Media->URL)
if (Media->URL[0]) // URL given by user is not empty
{
/* Examples of valid YouTube URLs:
https://www.youtube.com/watch?v=xu9IbeF9CBw
https://www.youtube.com/watch?v=xu9IbeF9CBw&t=10
https://youtu.be/xu9IbeF9CBw
https://youtu.be/xu9IbeF9CBw?t=10
https://www.youtube.com/embed/xu9IbeF9CBw
https://www.youtube.com/embed/xu9IbeF9CBw?start=10
*/
/***** Step 1: Skip scheme *****/
if (!strncasecmp (Media->URL,"https://",8)) // URL starts by https://
PtrHost = &Media->URL[8];
else if (!strncasecmp (Media->URL,"http://" ,7)) // URL starts by http://
PtrHost = &Media->URL[7];
else if (!strncasecmp (Media->URL,"//" ,2)) // URL starts by //
PtrHost = &Media->URL[2];
else
PtrHost = &Media->URL[0];
if (PtrHost[0])
{
/***** Step 2: Skip host *****/
if (!strncasecmp (PtrHost,"youtu.be/" , 9)) // Host starts by youtu.be/
{
YouTube = SHORT;
PtrPath = &PtrHost[9];
}
else if (!strncasecmp (PtrHost,"www.youtube.com/",16)) // Host starts by www.youtube.com/
{
YouTube = FULL;
PtrPath = &PtrHost[16];
}
else if (!strncasecmp (PtrHost,"youtube.com/" ,12)) // Host starts by youtube.com/
{
YouTube = FULL;
PtrPath = &PtrHost[12];
}
/* Check pointer to path */
if (PtrPath)
{
if (!PtrPath[0])
YouTube = WRONG;
}
else
YouTube = WRONG;
if (YouTube != WRONG)
{
/***** Step 3: Skip path *****/
if (YouTube == FULL)
{
if (!strncasecmp (PtrPath,"watch?",6)) // Path starts by watch?
PtrParams = &PtrPath[6];
else if (!strncasecmp (PtrPath,"embed/",6)) // Path starts by embed/
{
YouTube = EMBED;
PtrParams = &PtrPath[6];
}
else
YouTube = WRONG;
}
else
PtrParams = &PtrPath[0];
/* Check pointer to params */
if (PtrParams)
{
if (!PtrParams[0])
YouTube = WRONG;
}
else
YouTube = WRONG;
if (YouTube != WRONG)
{
/***** Step 4: Search for video code *****/
switch (YouTube)
{
case SHORT:
PtrCode = PtrParams;
break;
case FULL:
/* Search for v= */
PtrCode = strcasestr (PtrPath,"v=");
if (PtrCode)
PtrCode += 2;
break;
case EMBED:
PtrCode = PtrParams;
break;
default:
PtrCode = NULL;
break;
}
/* Check pointer to code */
if (PtrCode)
{
if (!PtrCode[0])
YouTube = WRONG;
}
else
YouTube = WRONG;
if (YouTube != WRONG)
{
/***** Step 5: Get video code *****/
CodeLength = strspn (PtrCode,Str_BIN_TO_BASE64URL);
if (CodeLength > 0 &&
CodeLength <= Med_BYTES_NAME)
CodeFound = true; // Success!
}
}
}
}
}
/***** Set or reset media *****/
if (CodeFound)
{
/* Copy code */
strncpy (Media->Name,PtrCode,CodeLength);
Media->Name[CodeLength] = '\0';
/* Set media type and status */
Media->Type = Med_YOUTUBE;
Media->Status = Med_PROCESSED;
}
else
/* Reset media */
Med_ResetMedia (Media);
}
/*****************************************************************************/
/************************ Get embed link from form ***************************/
/*****************************************************************************/
static void Med_GetAndProcessEmbedFromForm (const char *ParamURL,
struct Media *Media)
{
extern const char Str_BIN_TO_BASE64URL[64 + 1];
char *PtrHost = NULL;
bool URLFound = false;
/***** Set media status *****/
Media->Status = Med_STATUS_NONE;
/***** Get embed URL from form *****/
Usr_GetURLFromForm (ParamURL,Media);
/***** Process URL trying to convert it to a YouTube embed URL *****/
if (Media->URL)
if (Media->URL[0]) // URL given by user is not empty
{
/* Examples of valid embed URLs:
//www.slideshare.net/slideshow/embed_code/key/yngasD9sIZ7GQV
*/
/***** Step 1: Skip scheme *****/
if (!strncasecmp (Media->URL,"https://",8)) // URL starts by https://
PtrHost = &Media->URL[8];
else if (!strncasecmp (Media->URL,"http://" ,7)) // URL starts by http://
PtrHost = &Media->URL[7];
else if (!strncasecmp (Media->URL,"//" ,2)) // URL starts by //
PtrHost = &Media->URL[2];
/***** Check if a URL is found *****/
if (PtrHost)
if (PtrHost[0])
URLFound = true; // Success!
}
/***** Set or reset media *****/
if (URLFound)
{
/* Set media type and status */
Media->Type = Med_EMBED;
Media->Status = Med_PROCESSED;
}
else
/* Reset media */
Med_ResetMedia (Media);
}
/*****************************************************************************/
/**** Remove media, keep media or store media, depending on media action *****/
/*****************************************************************************/
void Med_RemoveKeepOrStoreMedia (long CurrentMedCodInDB,struct Media *Media)
{
switch (Media->Action)
{
case Med_ACTION_NO_MEDIA:
/* Remove possible current media */
Med_RemoveMedia (Media->MedCod);
/* Reset media data */
Med_ResetMedia (Media);
break;
case Med_ACTION_KEEP_MEDIA:
/* Keep current media */
Media->MedCod = CurrentMedCodInDB;
Med_GetMediaDataByCod (Media);
break;
case Med_ACTION_NEW_MEDIA:
/* Remove possible current media */
Med_RemoveMedia (Media->MedCod);
/* New media received and processed sucessfully? */
if (Media->Status == Med_PROCESSED) // The new media received has been processed
{
/* Move processed media to definitive directory */
Med_MoveMediaToDefinitiveDir (Media);
if (Media->Status == Med_MOVED)
/* Store media in database */
Med_StoreMediaInDB (Media); // Set Media->MedCod
else
/* Reset media data */
Med_ResetMedia (Media);
}
else
/* Reset media data */
Med_ResetMedia (Media);
break;
}
}
/*****************************************************************************/
/**** Move temporary processed media file to definitive private directory ****/
/*****************************************************************************/
void Med_MoveMediaToDefinitiveDir (struct Media *Media)
{
char PathMedPriv[PATH_MAX + 1];
/***** Check trivial case *****/
if (Media->Status == Med_PROCESSED)
{
switch (Media->Type)
{
case Med_JPG:
case Med_GIF:
case Med_MP4:
case Med_WEBM:
case Med_OGG:
/***** Create private subdirectory for media if it does not exist *****/
snprintf (PathMedPriv,sizeof (PathMedPriv),
"%s/%c%c",
Cfg_PATH_MEDIA_PRIVATE,
Media->Name[0],
Media->Name[1]);
Fil_CreateDirIfNotExists (PathMedPriv);
/***** Move files *****/
switch (Media->Type)
{
case Med_JPG:
/* Move JPG */
if (Med_MoveTmpFileToDefDir (Media,PathMedPriv,
Med_Extensions[Med_JPG]))
Media->Status = Med_MOVED; // Success
break;
case Med_GIF:
/* Move PNG */
if (Med_MoveTmpFileToDefDir (Media,PathMedPriv,
"png"))
/* Move GIF */
if (Med_MoveTmpFileToDefDir (Media,PathMedPriv,
Med_Extensions[Med_GIF]))
Media->Status = Med_MOVED; // Success
break;
case Med_MP4:
case Med_WEBM:
case Med_OGG:
/* Move MP4 or WEBM or OGG */
if (Med_MoveTmpFileToDefDir (Media,PathMedPriv,
Med_Extensions[Media->Type]))
Media->Status = Med_MOVED; // Success
break;
default:
break;
}
break;
case Med_YOUTUBE:
case Med_EMBED:
// Nothing to do with files ==> Processing successfully finished
Media->Status = Med_MOVED; // Success
break;
default:
break;
}
}
/***** If fail ==> reset media *****/
if (Media->Status != Med_MOVED) // Fail
Med_ResetMedia (Media);
}
/*****************************************************************************/
/******* Move temporary processed file to definitive private directory *******/
/*****************************************************************************/
// Return true on success
// Return false on error
static bool Med_MoveTmpFileToDefDir (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *Extension)
{
char PathFileTmp[PATH_MAX + 1]; // Full name of temporary processed file
char PathFile[PATH_MAX + 1]; // Full name of definitive processed file
/***** Temporary processed media file *****/
snprintf (PathFileTmp,sizeof (PathFileTmp),
"%s/%s.%s",
Cfg_PATH_MEDIA_TMP_PRIVATE,Media->Name,Extension);
/***** Definitive processed media file *****/
snprintf (PathFile,sizeof (PathFile),
"%s/%s.%s",
PathMedPriv,Media->Name,Extension);
/***** Move JPG file *****/
if (rename (PathFileTmp,PathFile)) // Fail
{
Ale_ShowAlert (Ale_ERROR,"Can not move file.");
return false;
}
return true; // Success
}
/*****************************************************************************/
/************************ Store media into database **************************/
/*****************************************************************************/
void Med_StoreMediaInDB (struct Media *Media)
{
/***** Trivial case *****/
if (Media->Status != Med_MOVED)
{
Med_ResetMedia (Media); // No media inserted in database
return;
}
/***** Insert media into database *****/
Media->MedCod = DB_QueryINSERTandReturnCode ("can not create media",
"INSERT INTO media"
" (Type,Name,URL,Title)"
" VALUES"
" ('%s','%s','%s','%s')",
Med_GetStringTypeForDB (Media->Type),
Media->Name ? Media->Name : "",
Media->URL ? Media->URL : "",
Media->Title ? Media->Title : "");
Media->Status = Med_STORED_IN_DB;
}
/*****************************************************************************/
/****** Show a user uploaded media (in a test question, timeline, etc.) ******/
/*****************************************************************************/
void Med_ShowMedia (struct Media *Media,
const char *ClassContainer,const char *ClassMedia)
{
bool PutLink;
char PathMedPriv[PATH_MAX + 1];
/***** If no media to show ==> nothing to do *****/
if (Media->Status != Med_STORED_IN_DB)
return;
if (Media->Type == Med_TYPE_NONE)
return;
/***** Start media container *****/
fprintf (Gbl.F.Out,"<div class=\"%s\">",ClassContainer);
switch (Media->Type)
{
case Med_JPG:
case Med_GIF:
case Med_MP4:
case Med_WEBM:
case Med_OGG:
/***** Show uploaded file *****/
/* If no media to show ==> nothing to do */
if (!Media->Name)
return;
if (!Media->Name[0])
return;
/* Start optional link to external URL */
PutLink = false;
if (Media->URL)
if (Media->URL[0])
PutLink = true;
if (PutLink)
fprintf (Gbl.F.Out,"<a href=\"%s\" target=\"_blank\">",Media->URL);
/* Create a temporary public directory used to show the media */
Brw_CreateDirDownloadTmp ();
/* Build path to private directory with the media */
snprintf (PathMedPriv,sizeof (PathMedPriv),
"%s/%c%c",
Cfg_PATH_MEDIA_PRIVATE,
Media->Name[0],
Media->Name[1]);
/* Show media */
switch (Media->Type)
{
case Med_JPG:
Med_ShowJPG (Media,PathMedPriv,ClassMedia);
break;
case Med_GIF:
Med_ShowGIF (Media,PathMedPriv,ClassMedia);
break;
case Med_MP4:
case Med_WEBM:
case Med_OGG:
Med_ShowVideo (Media,PathMedPriv,ClassMedia);
break;
default:
break;
}
/* End optional link to external URL */
if (PutLink)
fprintf (Gbl.F.Out,"</a>");
break;
case Med_YOUTUBE:
/***** Show embed YouTube video *****/
Med_ShowYoutube (Media,ClassMedia);
break;
case Med_EMBED:
/***** Show other embed media *****/
Med_ShowEmbed (Media,ClassMedia);
break;
default:
break;
}
/***** End media container *****/
fprintf (Gbl.F.Out,"</div>");
}
/*****************************************************************************/
/************************** Show a user uploaded JPG *************************/
/*****************************************************************************/
static void Med_ShowJPG (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
char FileNameMedia[NAME_MAX + 1];
char FullPathMediaPriv[PATH_MAX + 1];
char URL_JPG[PATH_MAX + 1];
/***** Build private path to JPG *****/
snprintf (FileNameMedia,sizeof (FileNameMedia),
"%s.%s",
Media->Name,Med_Extensions[Med_JPG]);
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s",
PathMedPriv,FileNameMedia);
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv))
{
/***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMedia);
/***** Build URL pointing to symbolic link *****/
snprintf (URL_JPG,sizeof (URL_JPG),
"%s/%s/%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
Gbl.FileBrowser.TmpPubDir.L,
Gbl.FileBrowser.TmpPubDir.R,
FileNameMedia);
/***** Show media *****/
fprintf (Gbl.F.Out,"<img src=\"%s\" class=\"%s\" alt=\"\"",URL_JPG,ClassMedia);
if (Media->Title)
if (Media->Title[0])
fprintf (Gbl.F.Out," title=\"%s\"",Media->Title);
fprintf (Gbl.F.Out," lazyload=\"on\" />"); // Lazy load of the media
}
else
fprintf (Gbl.F.Out,"%s",Txt_File_not_found);
}
/*****************************************************************************/
/************************** Show a user uploaded GIF *************************/
/*****************************************************************************/
static void Med_ShowGIF (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
char FileNameMedia[NAME_MAX + 1];
char FullPathMediaPriv[PATH_MAX + 1];
char URL_GIF[PATH_MAX + 1];
char URL_PNG[PATH_MAX + 1];
/***** Build private path to animated GIF image *****/
snprintf (FileNameMedia,sizeof (FileNameMedia),
"%s.%s",
Media->Name,Med_Extensions[Med_GIF]);
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s",
PathMedPriv,FileNameMedia);
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv)) // The animated GIF image
{
/***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMedia);
/***** Create URL pointing to symbolic link *****/
snprintf (URL_GIF,sizeof (URL_GIF),
"%s/%s/%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
Gbl.FileBrowser.TmpPubDir.L,
Gbl.FileBrowser.TmpPubDir.R,
FileNameMedia);
/***** Build private path to static PNG image *****/
snprintf (FileNameMedia,sizeof (FileNameMedia),
"%s.png",
Media->Name);
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s",
PathMedPriv,FileNameMedia);
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv)) // The static PNG image
{
/***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMedia);
/***** Create URL pointing to symbolic link *****/
snprintf (URL_PNG,sizeof (URL_PNG),
"%s/%s/%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
Gbl.FileBrowser.TmpPubDir.L,
Gbl.FileBrowser.TmpPubDir.R,
FileNameMedia);
/***** Show static PNG and animated GIF *****/
fprintf (Gbl.F.Out,"<div class=\"MED_PLAY\""
" onmouseover=\"toggleOnGIF(this,'%s');\""
" onmouseout=\"toggleOffGIF(this,'%s');\">",
URL_GIF,
URL_PNG);
/* Image */
fprintf (Gbl.F.Out,"<img src=\"%s\" class=\"%s\" alt=\"\"",
URL_PNG,
ClassMedia);
if (Media->Title)
if (Media->Title[0])
fprintf (Gbl.F.Out," title=\"%s\"",Media->Title);
fprintf (Gbl.F.Out," lazyload=\"on\" />"); // Lazy load of the media
/* Overlay with GIF label */
fprintf (Gbl.F.Out,"<span class=\"MED_PLAY_ICO\">"
"GIF"
"</span>");
fprintf (Gbl.F.Out,"</div>");
}
else
fprintf (Gbl.F.Out,"%s",Txt_File_not_found);
}
else
fprintf (Gbl.F.Out,"%s",Txt_File_not_found);
}
/*****************************************************************************/
/************************ Show a user uploaded video *************************/
/*****************************************************************************/
static void Med_ShowVideo (struct Media *Media,
const char PathMedPriv[PATH_MAX + 1],
const char *ClassMedia)
{
extern const char *Txt_File_not_found;
char FileNameMediaPriv[NAME_MAX + 1];
char FullPathMediaPriv[PATH_MAX + 1];
char URL_Video[PATH_MAX + 1];
/***** Build private path to video *****/
snprintf (FileNameMediaPriv,sizeof (FileNameMediaPriv),
"%s.%s",
Media->Name,Med_Extensions[Media->Type]);
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s",
PathMedPriv,FileNameMediaPriv);
/***** Check if private media file exists *****/
if (Fil_CheckIfPathExists (FullPathMediaPriv))
{
/***** Create symbolic link from temporary public directory to private file
in order to gain access to it for showing/downloading *****/
Brw_CreateTmpPublicLinkToPrivateFile (FullPathMediaPriv,FileNameMediaPriv);
/***** Create URL pointing to symbolic link *****/
snprintf (URL_Video,sizeof (URL_Video),
"%s/%s/%s/%s",
Cfg_URL_FILE_BROWSER_TMP_PUBLIC,
Gbl.FileBrowser.TmpPubDir.L,
Gbl.FileBrowser.TmpPubDir.R,
FileNameMediaPriv);
/***** Show media *****/
fprintf (Gbl.F.Out,"<video src=\"%s\""
" preload=\"metadata\" controls=\"controls\""
" class=\"%s\"",
URL_Video,ClassMedia);
if (Media->Title)
if (Media->Title[0])
fprintf (Gbl.F.Out," title=\"%s\"",Media->Title);
fprintf (Gbl.F.Out," lazyload=\"on\">" // Lazy load of the media
"Your browser does not support HTML5 video."
"</video>");
}
else
fprintf (Gbl.F.Out,"%s",Txt_File_not_found);
}
/*****************************************************************************/
/*********************** Show an embed YouTube video *************************/
/*****************************************************************************/
static void Med_ShowYoutube (struct Media *Media,const char *ClassMedia)
{
/***** Check if YouTube code exists *****/
if (Media->Name[0]) // YouTube code
{
if (Gbl.Usrs.Me.UsrDat.Prefs.AcceptThirdPartyCookies)
{
/***** Show linked external media *****/
// Example of code given by YouTube:
// <iframe width="560" height="315"
// src="https://www.youtube.com/embed/xu9IbeF9CBw"
// frameborder="0"
// allow="accelerometer; autoplay; encrypted-media;
// gyroscope; picture-in-picture" allowfullscreen>
// </iframe>
fprintf (Gbl.F.Out,"<div class=\"MED_VIDEO_CONT\">"
"<iframe src=\"https://www.youtube.com/embed/%s\""
" frameborder=\"0\""
" allow=\"accelerometer; autoplay; encrypted-media;"
" gyroscope; picture-in-picture\""
" allowfullscreen=\"allowfullscreen\""
" class=\"%s\"",
Media->Name,ClassMedia);
if (Media->Title)
if (Media->Title[0])
fprintf (Gbl.F.Out," title=\"%s\"",Media->Title);
fprintf (Gbl.F.Out,">"
"</iframe>"
"</div>");
}
else
/***** Alert to inform about third party cookies *****/
Med_AlertThirdPartyCookies ();
}
}
/*****************************************************************************/
/*************************** Show an embed media *****************************/
/*****************************************************************************/
static void Med_ShowEmbed (struct Media *Media,const char *ClassMedia)
{
/***** Check if embed URL exists *****/
if (Media->URL[0]) // Embed URL
{
if (Gbl.Usrs.Me.UsrDat.Prefs.AcceptThirdPartyCookies)
{
/***** Show linked external media *****/
// Example of code given by Slideshare:
// <iframe src="//www.slideshare.net/slideshow/embed_code/key/yngasD9sIZ7GQV"
// width="595" height="485" frameborder="0"
// marginwidth="0" marginheight="0" scrolling="no"
// style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;"
// allowfullscreen>
// </iframe>
fprintf (Gbl.F.Out,"<div class=\"MED_EMBED_CONT\">"
"<iframe src=\"%s\""
" frameborder=\"0\""
" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\""
" allowfullscreen=\"allowfullscreen\""
" class=\"%s\"",
Media->URL,ClassMedia);
if (Media->Title)
if (Media->Title[0])
fprintf (Gbl.F.Out," title=\"%s\"",Media->Title);
fprintf (Gbl.F.Out,">"
"</iframe>"
"</div>");
}
else
/***** Alert to inform about third party cookies *****/
Med_AlertThirdPartyCookies ();
}
}
/*****************************************************************************/
/********** Remove several media files and entries in database ***************/
/*****************************************************************************/
static void Med_AlertThirdPartyCookies (void)
{
extern const char *Txt_To_watch_multimedia_content_from_another_website_you_have_to_accept_third_party_cookies_in_your_personal_settings;
extern const char *Txt_Settings;
/***** Alert to inform about third party cookies *****/
/* Start alert */
Ale_ShowAlertAndButton1 (Ale_INFO,Txt_To_watch_multimedia_content_from_another_website_you_have_to_accept_third_party_cookies_in_your_personal_settings);
/* Put form to change cookies preferences */
if (!Gbl.Form.Inside)
Lay_PutContextualLinkIconText (ActReqEdiSet,Coo_COOKIES_ID,NULL,
"cog.svg",
Txt_Settings);
/* End alert */
Ale_ShowAlertAndButton2 (ActUnk,NULL,NULL,NULL,Btn_NO_BUTTON,NULL);
}
/*****************************************************************************/
/********** Remove several media files and entries in database ***************/
/*****************************************************************************/
void Med_RemoveMediaFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res)
{
MYSQL_ROW row;
unsigned NumMed;
long MedCod;
/***** Go over result removing media files *****/
for (NumMed = 0;
NumMed < NumMedia;
NumMed++)
{
/***** Get media code (row[0]) *****/
row = mysql_fetch_row (mysql_res);
MedCod = Str_ConvertStrCodToLongCod (row[0]);
/***** Remove media files *****/
Med_RemoveMedia (MedCod);
}
}
/*****************************************************************************/
/********** Remove one media from filesystem and from database ***************/
/*****************************************************************************/
void Med_RemoveMedia (long MedCod)
{
char PathMedPriv[PATH_MAX + 1];
char FullPathMediaPriv[PATH_MAX + 1];
struct Media Media;
/***** Trivial case *****/
if (MedCod <= 0)
return;
/***** Initialize media *****/
Med_MediaConstructor (&Media);
/***** Get media data *****/
Media.MedCod = MedCod;
Med_GetMediaDataByCod (&Media);
/***** Step 1. Remove media files from filesystem *****/
switch (Media.Type)
{
case Med_JPG:
case Med_GIF:
case Med_MP4:
case Med_WEBM:
case Med_OGG:
if (Media.Name[0])
{
/***** Build path to private directory with the media *****/
snprintf (PathMedPriv,sizeof (PathMedPriv),
"%s/%c%c",
Cfg_PATH_MEDIA_PRIVATE,
Media.Name[0],
Media.Name[1]);
/***** Remove files *****/
switch (Media.Type)
{
case Med_JPG:
/***** Remove private JPG file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s.%s",
PathMedPriv,Media.Name,Med_Extensions[Med_JPG]);
unlink (FullPathMediaPriv);
break;
case Med_GIF:
/***** Remove private GIF file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s.%s",
PathMedPriv,Media.Name,Med_Extensions[Med_GIF]);
unlink (FullPathMediaPriv);
/***** Remove private PNG file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s.png",
PathMedPriv,Media.Name);
unlink (FullPathMediaPriv);
break;
case Med_MP4:
case Med_WEBM:
case Med_OGG:
/***** Remove private video file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s.%s",
PathMedPriv,Media.Name,Med_Extensions[Media.Type]);
unlink (FullPathMediaPriv);
break;
default:
break;
}
// Public links are removed automatically after a period
}
break;
default:
break;
}
/***** Step 2. Remove entry for this media from database *****/
DB_QueryDELETE ("can not remove media",
"DELETE FROM media WHERE MedCod=%ld",
MedCod);
/***** Free media *****/
Med_MediaDestructor (&Media);
}
/*****************************************************************************/
/************************ Get media type from string *************************/
/*****************************************************************************/
static Med_Type_t Med_GetTypeFromStrInDB (const char *Str)
{
Med_Type_t Type;
for (Type = (Med_Type_t) 0;
Type < Med_NUM_TYPES;
Type++)
if (!strcasecmp (Str,Med_StringsTypeDB[Type]))
return Type;
return Med_TYPE_NONE;
}
/*****************************************************************************/
/************************ Get media type from extension **********************/
/*****************************************************************************/
static Med_Type_t Med_GetTypeFromExtAndMIME (const char *Extension,
const char *MIMEType)
{
/***** Extensions and MIME types allowed to convert to JPG *****/
if (!strcasecmp (Extension,"jpg" ) ||
!strcasecmp (Extension,"jpeg") ||
!strcasecmp (Extension,"png" ) ||
!strcasecmp (Extension,"tif" ) ||
!strcasecmp (Extension,"tiff") ||
!strcasecmp (Extension,"bmp" ))
if (!strcmp (MIMEType,"image/jpeg" ) ||
!strcmp (MIMEType,"image/pjpeg" ) ||
!strcmp (MIMEType,"image/png" ) ||
!strcmp (MIMEType,"image/x-png" ) ||
!strcmp (MIMEType,"image/tiff" ) ||
!strcmp (MIMEType,"image/bmp" ) ||
!strcmp (MIMEType,"application/octet-stream") ||
!strcmp (MIMEType,"application/octetstream" ) ||
!strcmp (MIMEType,"application/octet" ))
return Med_JPG;
/***** Extensions and MIME types allowed to convert to GIF *****/
if (!strcasecmp (Extension,"gif"))
if (!strcmp (MIMEType,"image/gif" ) ||
!strcmp (MIMEType,"application/octet-stream") ||
!strcmp (MIMEType,"application/octetstream" ) ||
!strcmp (MIMEType,"application/octet" ))
return Med_GIF;
/***** Extensions and MIME types allowed to convert to MP4 *****/
if (!strcasecmp (Extension,"mp4"))
if (!strcmp (MIMEType,"video/mp4" ) ||
!strcmp (MIMEType,"application/octet-stream") ||
!strcmp (MIMEType,"application/octetstream" ) ||
!strcmp (MIMEType,"application/octet" ))
return Med_MP4;
/***** Extensions and MIME types allowed to convert to WEBM *****/
if (!strcasecmp (Extension,"webm"))
if (!strcmp (MIMEType,"video/webm" ) ||
!strcmp (MIMEType,"application/octet-stream") ||
!strcmp (MIMEType,"application/octetstream" ) ||
!strcmp (MIMEType,"application/octet" ))
return Med_WEBM;
/***** Extensions and MIME types allowed to convert to OGG *****/
if (!strcasecmp (Extension,"ogg"))
if (!strcmp (MIMEType,"video/ogg" ) ||
!strcmp (MIMEType,"application/octet-stream") ||
!strcmp (MIMEType,"application/octetstream" ) ||
!strcmp (MIMEType,"application/octet" ))
return Med_OGG;
return Med_TYPE_NONE;
}
/*****************************************************************************/
/*************** Get string media type in database from type *****************/
/*****************************************************************************/
static const char *Med_GetStringTypeForDB (Med_Type_t Type)
{
/***** Check if type is out of valid range *****/
if (Type > (Med_Type_t) (Med_NUM_TYPES - 1))
return Med_StringsTypeDB[Med_TYPE_NONE];
/***** Get string from type *****/
return Med_StringsTypeDB[Type];
}