Version 18.63

This commit is contained in:
Antonio Cañas Vargas 2019-03-02 21:49:28 +01:00
parent 97610ac7b7
commit 5758e3c7f6
2 changed files with 1026 additions and 0 deletions

868
swad_media.c Normal file
View File

@ -0,0 +1,868 @@
// 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 *********************************/
/*****************************************************************************/
#include <linux/limits.h> // For PATH_MAX
#include <stdbool.h> // For boolean type
#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_config.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] =
{
"", // Med_UNKNOWN
"jpg", // Med_JPG
"gif", // Med_GIF
};
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/************************* Internal global variables *************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Internal prototypes ***************************/
/*****************************************************************************/
static Med_Action_t Med_GetMediaActionFromForm (const char *ParamAction);
static void Med_GetAndProcessFileFromForm (struct Media *Media,
const char *ParamFile);
static int Med_ProcessMedia (struct Media *Media,
const char *FileNameOriginal,
const char *FileNameProcessed);
static Med_Type_t Med_GetTypeFromExtension (const char *Extension);
/*****************************************************************************/
/********************* Reset media (image/video) fields **********************/
/*****************************************************************************/
// 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_ResetMediaExceptTitleAndURL (Media);
Media->Title = NULL;
Media->URL = NULL;
}
/*****************************************************************************/
/***************** Reset image fields except title and URL *******************/
/*****************************************************************************/
void Med_ResetMediaExceptTitleAndURL (struct Media *Media)
{
Media->Action = Med_ACTION_NO_MEDIA;
Media->Status = Med_FILE_NONE;
Media->Name[0] = '\0';
Media->Type = Med_UNKNOWN;
}
/*****************************************************************************/
/************************ Free media (image/video) ***************************/
/*****************************************************************************/
// Every call to constructor must have a call to destructor
void Med_MediaDestructor (struct Media *Media)
{
Med_FreeMediaTitle (Media);
Med_FreeMediaURL (Media);
}
/*****************************************************************************/
/****************************** Free image title *****************************/
/*****************************************************************************/
void Med_FreeMediaTitle (struct Media *Media)
{
// Media->Title must be initialized to NULL after declaration
if (Media->Title)
{
free ((void *) Media->Title);
Media->Title = NULL;
}
}
/*****************************************************************************/
/******************************* Free image URL ******************************/
/*****************************************************************************/
void Med_FreeMediaURL (struct Media *Media)
{
// Media->URL must be initialized to NULL after declaration
if (Media->URL)
{
free ((void *) Media->URL);
Media->URL = NULL;
}
}
/*****************************************************************************/
/**** Get media name, title and URL from a query result and copy to struct ***/
/*****************************************************************************/
void Med_GetMediaNameTitleAndURLFromRow (const char *Name,
const char *TypeStr,
const char *Title,
const char *URL,
struct Media *Media)
{
size_t Length;
/***** Copy image name to struct *****/
Str_Copy (Media->Name,Name,
Med_BYTES_NAME);
/***** Convert type string to type *****/
Media->Type = Med_GetTypeFromStrInDB (TypeStr);
/***** Set status of image file *****/
Media->Status = Media->Name[0] ? Med_NAME_STORED_IN_DB :
Med_FILE_NONE;
/***** Copy image title to struct *****/
// Media->Title can be empty or filled with previous value
// If filled ==> free it
Med_FreeMediaTitle (Media);
if (Title[0])
{
/* Get and limit length of the title */
Length = strlen (Title);
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,Title,
Length);
}
/***** Copy image URL to struct *****/
// Media->URL can be empty or filled with previous value
// If filled ==> free it
Med_FreeMediaURL (Media);
if (URL[0])
{
/* Get and limit length of the URL */
Length = strlen (URL);
if (Length > Med_MAX_BYTES_TITLE)
Length = Med_MAX_BYTES_TITLE;
if ((Media->URL = (char *) malloc (Length + 1)) == NULL)
Lay_ShowErrorAndExit ("Error allocating memory for image URL.");
Str_Copy (Media->URL,URL,
Length);
}
}
/*****************************************************************************/
/********* Draw input fields to upload an image/video inside a form **********/
/*****************************************************************************/
void Med_PutMediaUploader (int NumMediaInForm,const char *ClassMediaTitURL)
{
extern const char *Txt_Image;
extern const char *Txt_optional;
extern const char *Txt_Image_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 container *****/
fprintf (Gbl.F.Out,"<div class=\"MED_UPL_CON\">");
/***** Action to perform on media *****/
Par_PutHiddenParamUnsigned (ParamUploadMedia.Action,(unsigned) Med_ACTION_NEW_MEDIA);
/***** Media file *****/
fprintf (Gbl.F.Out,"<label class=\"MED_UPL_BUT\">"
"<img src=\"%s/camera.svg\""
" alt=\"%s\" title=\"%s (%s)\""
" class=\"MED_UPL_ICO\" />"
"<input type=\"file\" name=\"%s\" accept=\"image/*\""
" class=\"MED_UPL_FIL\""
" onchange=\"mediaUploadOnSelectFile (this,'%s');\" />"
"<span id=\"%s_fil\" class=\"MED_UPL_NAM\" />"
"</span>"
"</label>",
Gbl.Prefs.URLIcons,
Txt_Image,Txt_Image,Txt_optional,
ParamUploadMedia.File,
Id,Id);
/***** Media title/attribution and URL *****/
fprintf (Gbl.F.Out,"<div id=\"%s_tit_url\" style=\"display:none;\">",
Id);
fprintf (Gbl.F.Out,"<input type=\"text\" name=\"%s\""
" placeholder=\"%s (%s)\""
" class=\"%s\" maxlength=\"%u\" value=\"\" />",
ParamUploadMedia.Title,
Txt_Image_title_attribution,Txt_optional,
ClassMediaTitURL,Med_MAX_CHARS_TITLE);
fprintf (Gbl.F.Out,"<br />"
"<input type=\"url\" name=\"%s\""
" placeholder=\"%s (%s)\""
" class=\"%s\" maxlength=\"%u\" value=\"\" />",
ParamUploadMedia.URL,
Txt_Link,Txt_optional,
ClassMediaTitURL,Cns_MAX_CHARS_WWW);
fprintf (Gbl.F.Out,"</div>");
/***** End container *****/
fprintf (Gbl.F.Out,"</div>");
}
/*****************************************************************************/
/******************** Get media (image/video) from form **********************/
/*****************************************************************************/
// 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))
{
struct ParamUploadMedia ParamUploadMedia;
char Title[Med_MAX_BYTES_TITLE + 1];
char URL[Cns_MAX_BYTES_WWW + 1];
size_t Length;
/***** Set names of parameters depending on number of media in form *****/
Med_SetParamNames (&ParamUploadMedia,NumMediaInForm);
/***** First, get action and initialize media (image/video)
(except title, that will be get after the media file) *****/
Media->Action = Med_GetMediaActionFromForm (ParamUploadMedia.Action);
Media->Status = Med_FILE_NONE;
Media->Name[0] = '\0';
Media->Type = Med_UNKNOWN;
/***** Secondly, get the media (image/video) name and the file *****/
switch (Media->Action)
{
case Med_ACTION_NEW_MEDIA: // Upload new image/video
/***** Get new media (if present ==> process and create temporary file) *****/
Med_GetAndProcessFileFromForm (Media,ParamUploadMedia.File);
switch (Media->Status)
{
case Med_FILE_NONE: // No new image/video received
Media->Action = Med_ACTION_NO_MEDIA;
Media->Name[0] = '\0';
Media->Type = Med_UNKNOWN;
break;
case Med_FILE_RECEIVED: // New image/video received, but not processed
Media->Status = Med_FILE_NONE;
Media->Name[0] = '\0';
Media->Type = Med_UNKNOWN;
break;
default:
break;
}
break;
case Med_ACTION_KEEP_MEDIA: // Keep current image/video unchanged
/***** Get media name *****/
if (GetMediaFromDB != NULL)
GetMediaFromDB (NumMediaInForm,Media);
break;
case Med_ACTION_CHANGE_MEDIA: // Replace old image/video by new one
/***** Get new image/video (if present ==> process and create temporary file) *****/
Med_GetAndProcessFileFromForm (Media,ParamUploadMedia.File);
if (Media->Status != Med_FILE_PROCESSED && // No new media received-processed successfully
GetMediaFromDB != NULL)
/* Get media (image/video) name */
GetMediaFromDB (NumMediaInForm,Media);
break;
case Med_ACTION_NO_MEDIA: // Do not use image/video (remove current image/video if exists)
break;
}
/***** Third, get image/video title from form *****/
Par_GetParToText (ParamUploadMedia.Title,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);
}
/***** By last, get media URL from form *****/
Par_GetParToText (ParamUploadMedia.URL,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);
}
}
/*****************************************************************************/
/********* 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->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->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 media from form ****************************/
/*****************************************************************************/
static void Med_GetAndProcessFileFromForm (struct Media *Media,
const char *ParamFile)
{
extern const char *Txt_The_image_could_not_be_processed_successfully;
struct Param *Param;
char FileNameImgSrc[PATH_MAX + 1];
char *PtrExtension;
size_t LengthExtension;
char MIMEType[Brw_MAX_BYTES_MIME_TYPE + 1];
char PathImgPriv[PATH_MAX + 1];
char FileNameOrig[PATH_MAX + 1]; // Full name of original uploaded file
char FileNameTmp[PATH_MAX + 1]; // Full name of temporary processed file
struct stat FileStatus;
bool WrongType = false;
/***** Set media file status *****/
Media->Status = Med_FILE_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_GetTypeFromExtension (PtrExtension);
if (Media->Type == Med_UNKNOWN)
return;
/* Check if the file type is image/ or application/octet-stream */
if (strncmp (MIMEType,"image/",strlen ("image/")))
if (strcmp (MIMEType,"application/octet-stream"))
if (strcmp (MIMEType,"application/octetstream"))
if (strcmp (MIMEType,"application/octet"))
WrongType = true;
if (WrongType)
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 */
snprintf (PathImgPriv,sizeof (PathImgPriv),
"%s/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA);
Fil_CreateDirIfNotExists (PathImgPriv);
/* Create temporary private directory for images/videos if it does not exist */
snprintf (PathImgPriv,sizeof (PathImgPriv),
"%s/%s/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP);
Fil_CreateDirIfNotExists (PathImgPriv);
/***** Remove old temporary private files *****/
Fil_RemoveOldTmpFiles (PathImgPriv,Cfg_TIME_TO_DELETE_IMAGES_TMP_FILES,false);
/***** End the reception of original not processed media
(it may be very big) into a temporary file *****/
Media->Status = Med_FILE_NONE;
snprintf (FileNameOrig,sizeof (FileNameOrig),
"%s/%s/%s/%s_original.%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP,
Media->Name,PtrExtension);
if (Fil_EndReceptionOfFile (FileNameOrig,Param)) // Success
{
Media->Status = Med_FILE_RECEIVED;
/* Depending on the media file extension... */
switch (Media->Type)
{
case Med_JPG:
/***** Convert original media to temporary JPEG processed file
by calling to program that makes the conversion *****/
snprintf (FileNameTmp,sizeof (FileNameTmp),
"%s/%s/%s/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP,
Media->Name);
if (Med_ProcessMedia (Media,FileNameOrig,FileNameTmp) == 0) // Return 0 on success
/* Success */
Media->Status = Med_FILE_PROCESSED;
else // Error processing media
{
/* Error ==> remove temporary destination media file */
if (Fil_CheckIfPathExists (FileNameTmp))
unlink (FileNameTmp);
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,Txt_The_image_could_not_be_processed_successfully);
}
/***** Remove temporary original file *****/
unlink (FileNameOrig);
break;
case Med_GIF:
/***** Check size of media file *****/
if (lstat (FileNameOrig,&FileStatus)) // On success ==> 0 is returned
{
// Error on lstat
/* Show error alert */
Ale_ShowAlert (Ale_ERROR,"Can not check gif file.");
}
else
{
if (FileStatus.st_size <= 5*1024*1024)
{
/***** Move original file to temporary file *****/
snprintf (FileNameTmp,sizeof (FileNameTmp),
"%s/%s/%s/%s.gif",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP,
Media->Name);
if (rename (FileNameOrig,FileNameTmp)) // Fail
Ale_ShowAlert (Ale_ERROR,"No se puede cambiar el nombre..."); // TODO: Need translation!!!!
else // Success
Media->Status = Med_FILE_PROCESSED;
}
else
Ale_ShowAlert (Ale_WARNING,"El tama&ntilde;o del archivo excede el l&iacute;mite..."); // TODO: Need translation!!!!
}
/***** Remove temporary original file *****/
unlink (FileNameOrig);
break;
default:
return;
}
}
}
/*****************************************************************************/
/************ Process original media generating processed media **************/
/*****************************************************************************/
// Return 0 on success
// Return != 0 on error
static int Med_ProcessMedia (struct Media *Media,
const char *FileNameOriginal,
const char *FileNameProcessed)
{
char Command[1024 + PATH_MAX * 2];
int ReturnCode;
snprintf (Command,sizeof (Command),
"convert %s -resize '%ux%u>' -quality %u %s",
FileNameOriginal,
Media->Width,
Media->Height,
Media->Quality,
FileNameProcessed);
ReturnCode = system (Command);
if (ReturnCode == -1)
Lay_ShowErrorAndExit ("Error when running command to process media.");
ReturnCode = WEXITSTATUS(ReturnCode);
return ReturnCode;
}
/*****************************************************************************/
/**** Move temporary processed media file to definitive private directory ****/
/*****************************************************************************/
void Med_MoveMediaToDefinitiveDirectory (struct Media *Media)
{
char PathImgPriv[PATH_MAX + 1];
char FileNameImgTmp[PATH_MAX + 1]; // Full name of temporary processed file
char FileNameImg[PATH_MAX + 1]; // Full name of definitive processed file
/***** Create subdirectory if it does not exist *****/
snprintf (PathImgPriv,sizeof (PathImgPriv),
"%s/%s/%c%c",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Media->Name[0],
Media->Name[1]);
Fil_CreateDirIfNotExists (PathImgPriv);
switch (Media->Type)
{
case Med_JPG:
/***** Temporary processed file *****/
snprintf (FileNameImgTmp,sizeof (FileNameImgTmp),
"%s/%s/%s/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP,
Media->Name);
/***** Definitive processed file *****/
snprintf (FileNameImg,sizeof (FileNameImg),
"%s/%s/%c%c/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Media->Name[0],
Media->Name[1],
Media->Name);
break;
case Med_GIF:
/***** Temporary processed file *****/
snprintf (FileNameImgTmp,sizeof (FileNameImgTmp),
"%s/%s/%s/%s.gif",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,Cfg_FOLDER_IMG_TMP,
Media->Name);
/***** Definitive processed file *****/
snprintf (FileNameImg,sizeof (FileNameImg),
"%s/%s/%c%c/%s.gif",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Media->Name[0],
Media->Name[1],
Media->Name);
break;
default:
Lay_ShowErrorAndExit ("Wrong media type.");
break;
}
/***** Move file *****/
if (rename (FileNameImgTmp,FileNameImg)) // Fail
Ale_ShowAlert (Ale_ERROR,"Can not move file.");
else // Success
Media->Status = Med_FILE_MOVED;
}
/*****************************************************************************/
/****** Show a user uploaded media (in a test question, timeline, etc.) ******/
/*****************************************************************************/
void Med_ShowMedia (struct Media *Media,
const char *ClassContainer,const char *ClassMedia)
{
extern const char *Txt_Image_not_found;
char FileNameImgPriv[NAME_MAX + 1];
char FullPathMediaPriv[PATH_MAX + 1];
char URL[PATH_MAX + 1];
bool PutLink;
/***** If no media to show ==> nothing to do *****/
if (!Media->Name)
return;
if (!Media->Name[0])
return;
if (Media->Type == Med_UNKNOWN)
return;
if (Media->Status != Med_NAME_STORED_IN_DB)
return;
/***** Create a temporary public directory used to show the media *****/
Brw_CreateDirDownloadTmp ();
/***** Build private path to media *****/
switch (Media->Type)
{
case Med_JPG:
snprintf (FileNameImgPriv,sizeof (FileNameImgPriv),
"%s.jpg",
Media->Name);
break;
case Med_GIF:
snprintf (FileNameImgPriv,sizeof (FileNameImgPriv),
"%s.gif",
Media->Name);
break;
default:
return;
}
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s/%c%c/%s",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Media->Name[0],
Media->Name[1],
FileNameImgPriv);
/***** 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,FileNameImgPriv);
/***** Create URL pointing to symbolic link *****/
snprintf (URL,sizeof (URL),
"%s/%s/%s/%s",
Cfg_URL_SWAD_PUBLIC,Cfg_FOLDER_FILE_BROWSER_TMP,
Gbl.FileBrowser.TmpPubDir,
FileNameImgPriv);
/***** Show media *****/
/* Check if optional link is present */
PutLink = false;
if (Media->URL)
if (Media->URL[0])
PutLink = true;
/* Start media container */
fprintf (Gbl.F.Out,"<div class=\"%s\">",ClassContainer);
/* Start optional link to external URL */
if (PutLink)
fprintf (Gbl.F.Out,"<a href=\"%s\" target=\"_blank\">",Media->URL);
/* Media */
fprintf (Gbl.F.Out,"<img src=\"%s\" class=\"%s\" alt=\"\"",URL,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
/* End optional link to external URL */
if (PutLink)
fprintf (Gbl.F.Out,"</a>");
/* End media container */
fprintf (Gbl.F.Out,"</div>");
}
else
Ale_ShowAlert (Ale_WARNING,Txt_Image_not_found);
}
/*****************************************************************************/
/**** Remove private file with an image/video, given the image/video name ****/
/*****************************************************************************/
void Med_RemoveMediaFilesFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res)
{
unsigned NumMed;
/***** Go over result removing media files *****/
for (NumMed = 0;
NumMed < NumMedia;
NumMed++)
Med_RemoveMediaFileFromRow (mysql_res);
}
void Med_RemoveMediaFileFromRow (MYSQL_RES *mysql_res)
{
MYSQL_ROW row;
/***** Get media name (row[0]) and type (row[1]) *****/
row = mysql_fetch_row (mysql_res);
/***** Remove image file *****/
Med_RemoveMediaFile (row[0],Med_GetTypeFromStrInDB (row[1]));
}
void Med_RemoveMediaFile (const char *Name,Med_Type_t Type)
{
char FullPathMediaPriv[PATH_MAX + 1];
if (Name[0])
{
switch (Type)
{
case Med_JPG:
/***** Build path to private file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s/%c%c/%s.jpg",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Name[0],
Name[1],
Name);
/***** Remove private file *****/
unlink (FullPathMediaPriv);
break;
case Med_GIF:
/***** Build path to private file *****/
snprintf (FullPathMediaPriv,sizeof (FullPathMediaPriv),
"%s/%s/%c%c/%s.gif",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_MEDIA,
Name[0],
Name[1],
Name);
/***** Remove private file *****/
unlink (FullPathMediaPriv);
break;
case Med_UNKNOWN:
Lay_ShowErrorAndExit ("Wrong media type.");
break;
}
// Public links are removed automatically after a period
}
}
/*****************************************************************************/
/************************ Get media type from string *************************/
/*****************************************************************************/
Med_Type_t Med_GetTypeFromStrInDB (const char *Str)
{
Med_Type_t Type;
for (Type = (Med_Type_t) 1; // Skip unknown type
Type < Med_NUM_TYPES;
Type++)
if (!strcasecmp (Str,Med_StringsTypeDB[Type]))
return Type;
return Med_UNKNOWN;
}
/*****************************************************************************/
/************************ Get media type from extension **********************/
/*****************************************************************************/
static Med_Type_t Med_GetTypeFromExtension (const char *Extension)
{
/***** Extensions allowed to convert to JPG *****/
if (!strcasecmp (Extension,"jpg"))
return Med_JPG;
if (!strcasecmp (Extension,"jpeg"))
return Med_JPG;
if (!strcasecmp (Extension,"png"))
return Med_JPG;
/***** Extensions allowed to convert to GIF *****/
if (!strcasecmp (Extension,"gif"))
return Med_GIF;
return Med_UNKNOWN;
}
/*****************************************************************************/
/*************** Get string media type in database from type *****************/
/*****************************************************************************/
const char *Med_GetStringTypeForDB (Med_Type_t Type)
{
/***** Check if type is out of valid range *****/
if (Type < (Med_Type_t) 1 ||
Type > (Med_Type_t) (Med_NUM_TYPES - 1))
return Med_StringsTypeDB[Med_UNKNOWN];
/***** Get string from type *****/
return Med_StringsTypeDB[Type];
}

158
swad_media.h Normal file
View File

@ -0,0 +1,158 @@
// swad_media.h: processing of image/video uploaded in a form
#ifndef _SWAD_IMG
#define _SWAD_IMG
/*
SWAD (Shared Workspace At a Distance in Spanish),
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 Public 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 ***********************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Public constants ******************************/
/*****************************************************************************/
#define Med_BYTES_NAME Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64
#define Med_MAX_CHARS_TITLE (128 - 1) // 127
#define Med_MAX_BYTES_TITLE ((Med_MAX_CHARS_TITLE + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
#define Med_MAX_CHARS_ATTRIBUTION (256 - 1) // 255
#define Med_MAX_BYTES_ATTRIBUTION ((Med_MAX_CHARS_ATTRIBUTION + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 4095
/*****************************************************************************/
/******************************* Public types ********************************/
/*****************************************************************************/
/***** Action to perform when editing a form with an image/video *****/
#define Med_NUM_ACTIONS 4
typedef enum
{
Med_ACTION_NEW_MEDIA, // Upload new image/video
Med_ACTION_KEEP_MEDIA, // Keep current image/video unchanged
Med_ACTION_CHANGE_MEDIA, // Change existing image/video by a new one
Med_ACTION_NO_MEDIA, // Do not use image/video (remove current image/video if exists)
} Med_Action_t;
#define Med_ACTION_DEFAULT Med_ACTION_NO_MEDIA
/***** Status of an image/video file *****/
/*
No file Original file Temporary Definitive Name of the image/video
uploaded uploaded by user processed file processed file stored in database
--------- --------------------------- ------------------ ------------------ ---------------------
Med_NONE Med_FILE_RECEIVED Med_FILE_PROCESSED Med_FILE_MOVED Med_NAME_STORED_IN_DB
--------- --------------------------- ------------------ ------------------ ---------------------
-> upload-file -> -> process-file -> b -> move-file -> -> insert-name ->
--------- --------------------------- ------------------ ------------------ ---------------------
file.ext / / / xx-unique-name
| | |
var var var
| | |
www www www
| | |
swad swad swad
| | |
med med med
| | |
tmp tmp xx (2 first chars)
| | |
xx-unique-name_original.ext xx-unique-name.jpg xx-unique-name.jpg
xx-unique-name: a unique name encrypted starting by two random chars xx
*/
typedef enum
{
Med_FILE_NONE,
Med_FILE_RECEIVED,
Med_FILE_PROCESSED,
Med_FILE_MOVED,
Med_NAME_STORED_IN_DB,
} Med_FileStatus_t;
#define Med_NUM_TYPES 3
typedef enum
{
Med_UNKNOWN,
Med_JPG,
Med_GIF,
} Med_Type_t;
/***** Struct used to get images/videos from forms *****/
struct Media
{
Med_Action_t Action;
Med_FileStatus_t Status;
char Name[Med_BYTES_NAME + 1];
Med_Type_t Type;
char *Title; // Title/attribution (it must be initialized to NULL
// in order to not trying to free it when no memory allocated)
char *URL; // URL, i.e. link to original big photo or video
// (it must be initialized to NULL
// in order to not trying to free it when no memory allocated)
unsigned Width;
unsigned Height;
unsigned Quality;
};
/***** Parameters used in a form to upload an image/video *****/
#define Med_MAX_BYTES_PARAM_UPLOAD_MEDIA (16 - 1)
struct ParamUploadMedia
{
char Action[Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1];
char File [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1];
char Title [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1];
char URL [Med_MAX_BYTES_PARAM_UPLOAD_MEDIA + 1];
};
/*****************************************************************************/
/***************************** Public prototypes *****************************/
/*****************************************************************************/
void Med_MediaConstructor (struct Media *Media);
void Med_ResetMediaExceptTitleAndURL (struct Media *Media);
void Med_MediaDestructor (struct Media *Media);
void Med_FreeMediaTitle (struct Media *Media);
void Med_FreeMediaURL (struct Media *Media);
void Med_GetMediaNameTitleAndURLFromRow (const char *Name,
const char *TypeStr,
const char *Title,
const char *URL,
struct Media *Media);
void Med_PutMediaUploader (int NumMediaInForm,const char *ClassMediaTitURL);
void Med_GetMediaFromForm (int NumMediaInForm,struct Media *Media,
void (*GetMediaFromDB) (int NumMediaInForm,struct Media *Media));
void Med_SetParamNames (struct ParamUploadMedia *ParamUploadMedia,int NumMediaInForm);
void Med_MoveMediaToDefinitiveDirectory (struct Media *Media);
void Med_ShowMedia (struct Media *Media,
const char *ClassContainer,const char *ClassMedia);
void Med_RemoveMediaFilesFromAllRows (unsigned NumMedia,MYSQL_RES *mysql_res);
void Med_RemoveMediaFileFromRow (MYSQL_RES *mysql_res);
void Med_RemoveMediaFile (const char *Name,Med_Type_t Type);
Med_Type_t Med_GetTypeFromStrInDB (const char *Str);
const char *Med_GetStringTypeForDB (Med_Type_t Type);
#endif