123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280(*********************************************************************************)(* Ojs-base *)(* *)(* Copyright (C) 2014-2021 INRIA. All rights reserved. *)(* *)(* This program is free software; you can redistribute it and/or modify *)(* it under the terms of the GNU General Public License as *)(* published by the Free Software Foundation, version 3 of the License. *)(* *)(* 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 Library General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public *)(* License along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)(** *)openOjs_server.ServeropenLwt.InfixmoduletypeS=sigmoduleP:Ojs_filetree.Types.Pvalaccess_forbidden:Ojs_base.Path.t->P.server_msgvalcreation_forbidden:Ojs_base.Path.t->P.server_msgvaldeletion_forbidden:Ojs_base.Path.t->P.server_msgvalrenaming_forbidden:Ojs_base.Path.t->Ojs_base.Path.t->P.server_msgclassfiletree:(P.server_msg->(P.client_msg->unitLwt.t)->unitLwt.t)->(P.server_msg->unitLwt.t)->id:string->Ojs_base.Path.t->objectvalmutablefile_filter:Ojs_base.Path.t->boolmethodafter_add_file:Ojs_base.Path.t->unitmethodafter_get_tree:Ojs_filetree.Types.file_treelist->Ojs_filetree.Types.file_treelistmethodbefore_add_file:Ojs_base.Path.t->unitmethodcan_add_dir:string->boolmethodcan_add_file:string->boolmethodcan_delete:string->boolmethodcan_rename:string->string->boolmethodhandle_add_dir:(P.server_msg->unitLwt.t)->Ojs_filetree.Types.path->unitLwt.tmethodhandle_add_file:(P.server_msg->unitLwt.t)->Ojs_filetree.Types.path->string->unitLwt.tmethodhandle_call:(P.server_msg->unitLwt.t)->P.client_msg->unitLwt.tmethodhandle_delete:(P.server_msg->unitLwt.t)->Ojs_filetree.Types.path->unitLwt.tmethodhandle_message:(P.server_msg->unitLwt.t)->P.client_msg->unitLwt.tmethodhandle_rename:(P.server_msg->unitLwt.t)->Ojs_filetree.Types.path->Ojs_filetree.Types.path->unitLwt.tmethodid:stringmethodroot:Ojs_base.Path.tmethodset_file_filter:(Ojs_base.Path.t->bool)->unitendclassfiletrees:(P.app_server_msg->(P.app_client_msg->unitLwt.t)->unitLwt.t)->(P.app_server_msg->unitLwt.t)->((P.server_msg->(P.client_msg->unitLwt.t)->unitLwt.t)->(P.server_msg->unitLwt.t)->id:string->Ojs_base.Path.t->filetree)->objectvalmutablefiletrees:filetreeOjs_server.Server.SMap.tmethodadd_filetree:id:Ojs_server.Server.SMap.key->Ojs_base.Path.t->filetreemethodfiletree:Ojs_server.Server.SMap.key->filetreemethodhandle_call:(P.app_server_msg->unitLwt.t)->P.app_client_msg->unitLwt.tmethodhandle_message:(P.app_server_msg->unitLwt.t)->P.app_client_msg->unitLwt.tendendmoduleMake(P:Ojs_filetree.Types.P)=structmoduleP=Pletaccess_forbiddenpath=P.SError(Printf.sprintf"Forbidden access to %S"(Ojs_base.Path.to_stringpath))letcreation_forbiddenpath=P.SError(Printf.sprintf"Forbidden creation of %S"(Ojs_base.Path.to_stringpath))letdeletion_forbiddenpath=P.SError(Printf.sprintf"Forbidden deletion of %S "(Ojs_base.Path.to_stringpath))letrenaming_forbiddenpath1path2=P.SError(Printf.sprintf"Forbidden renaming of %S to %S"(Ojs_base.Path.to_stringpath1)(Ojs_base.Path.to_stringpath2))(*c==v=[File.file_of_string]=1.1====*)letfile_of_string~files=letoc=open_outfileinoutput_stringocs;close_outoc(*/c==v=[File.file_of_string]=1.1====*)classfiletree(broadcall:P.server_msg->(P.client_msg->unitLwt.t)->unitLwt.t)(broadcast:P.server_msg->unitLwt.t)~idroot=object(self)valmutablefile_filter=(fun(_:Ojs_base.Path.t)->true)methodset_file_filterf=file_filter<-fmethodid:string=idmethodroot:Ojs_base.Path.t=rootmethodcan_add_filefile=truemethodcan_add_dirdir=truemethodcan_deletefile=truemethodcan_renamefile1file2=truemethodbefore_add_file(filename:Ojs_base.Path.t)=()methodafter_add_file(filename:Ojs_base.Path.t)=()methodhandle_add_filereply_msgpathcontents=letnorm=Ojs_base.Path.normalizepathinletfile=Ojs_base.Path.to_string(Ojs_base.Path.append_pathrootnorm)inmatchself#can_add_filefilewithfalse->reply_msg(creation_forbiddenpath)|true->letcontents=matchBase64.decodecontentswith|Okc->c|Error(`Msgmsg)->failwithmsginself#before_add_filenorm;file_of_string~filecontents;self#after_add_filenorm;letmime=Magic_mime.lookupfileinreply_msgP.SOk>>=fun()->broadcast(P.SAdd_file(path,mime))methodhandle_add_dirreply_msgpath=letnorm=Ojs_base.Path.normalizepathinletdir=Ojs_base.Path.to_string(Ojs_base.Path.append_pathrootnorm)inmatchself#can_add_dirdirwith|false->reply_msg(creation_forbiddenpath)|true->tryUnix.mkdirdir0o755;reply_msgP.SOk>>=fun()->broadcast(P.SAdd_dirpath)withUnix.Unix_error(e,s1,s2)->letmsg=Printf.sprintf"Could not create %s: %s"(Ojs_base.Path.to_stringpath)(Unix.error_messagee)inreply_msg(P.SErrormsg)methodhandle_deletereply_msgpath=letnorm=Ojs_base.Path.normalizepathinletfile=Ojs_base.Path.to_string(Ojs_base.Path.append_pathrootnorm)inprerr_endline("handle_delete, file="^file);matchself#can_deletefilewith|false->reply_msg(deletion_forbiddenpath)|true->ifnot(Sys.is_directoryfile)thentrySys.removefile;reply_msgP.SOk>>=fun()->broadcast(P.SDeletepath)withSys_errormsg->failwithmsgelsematchSys.command(Printf.sprintf"rm -fr %s"(Filename.quotefile))with0->reply_msgP.SOk>>=fun()->broadcast(P.SDeletepath)|n->letmsg=Printf.sprintf"Could not delete %s"(Ojs_base.Path.to_stringpath)inreply_msg(P.SErrormsg)methodhandle_renamereply_msgpath1path2=letnorm1=Ojs_base.Path.normalizepath1inletfile1=Ojs_base.Path.to_string(Ojs_base.Path.append_pathrootnorm1)inletnorm2=Ojs_base.Path.normalizepath2inletfile2=Ojs_base.Path.to_string(Ojs_base.Path.append_pathrootnorm2)inmatchself#can_renamefile1file2withfalse->reply_msg(renaming_forbiddenpath1path2)|true->trySys.renamefile1file2;reply_msgP.SOk>>=fun()->broadcast(P.SDeletepath1)>>=fun()->ifSys.is_directoryfile2thenbroadcast(P.SAdd_dirpath2)else(letmime=Magic_mime.lookupfile2inbroadcast(P.SAdd_file(path2,mime)))withSys_errormsg->letmsg=Printf.sprintf"Could not rename %S to %S: %s"(Ojs_base.Path.to_stringpath1)(Ojs_base.Path.to_stringpath2)msginreply_msg(P.SErrormsg)methodafter_get_treefiles=filesmethodhandle_message(send_msg:'srv->unitLwt.t)(msg:'clt)=self#handle_callsend_msgmsgmethodhandle_call(reply_msg:'srv->unitLwt.t)(msg:'clt)=matchmsgwithP.Get_tree->letfiles=Files.file_trees_of_dir~filepred:file_filterrootinletfiles=self#after_get_treefilesinreply_msg(P.STreefiles)|P.Add_file(path,contents)->self#handle_add_filereply_msgpathcontents|P.Add_dirpath->self#handle_add_dirreply_msgpath|P.Deletepath->self#handle_deletereply_msgpath|P.Rename(path1,path2)->self#handle_renamereply_msgpath1path2|_->reply_msg(P.SError"Unhandled message")endclassfiletrees(broadcall:P.app_server_msg->(P.app_client_msg->unitLwt.t)->unitLwt.t)(broadcast:P.app_server_msg->unitLwt.t)(spawn:(P.server_msg->(P.client_msg->unitLwt.t)->unitLwt.t)->(P.server_msg->unitLwt.t)->id:string->Ojs_base.Path.t->filetree)=object(self)valmutablefiletrees=(SMap.empty:filetreeSMap.t)methodfiletreeid=trySMap.findidfiletreeswithNot_found->failwith(Printf.sprintf"No filetree with id %S"id)methodadd_filetree~idroot=letbroadcallmsgcb=letcbmsg=matchP.unpack_client_msgmsgwith|Some(_,msg)->cbmsg|_->Lwt.return_unitinbroadcall(P.pack_server_msgidmsg)cbinletbroadcastmsg=broadcast(P.pack_server_msgidmsg)inletft=spawnbroadcallbroadcast~idrootinfiletrees<-SMap.addidftfiletrees;ftmethodhandle_message(send_msg:P.app_server_msg->unitLwt.t)(msg:P.app_client_msg)=matchP.unpack_client_msgmsgwith|Some(id,msg)->letsend_msgmsg=send_msg(P.pack_server_msgidmsg)in(self#filetreeid)#handle_messagesend_msgmsg|None->Lwt.return_unitmethodhandle_call(return:P.app_server_msg->unitLwt.t)(msg:P.app_client_msg)=matchP.unpack_client_msgmsgwith|Some(id,msg)->letreply_msgmsg=return(P.pack_server_msgidmsg)in(self#filetreeid)#handle_callreply_msgmsg|None->Lwt.return_unitendend