? v5$@};`_e]m P6Y]\a.DL;eA;dZ+ IFxݣ{eѴUR+mfLVq͇ʗ.^nÿ )ZfLC>_ } (%ſCLM$SFB߰|{%7paKCXu"`14rrl0f1 ?\By8;4u~9pah8Tcr¥RJG? >:^ͰX/2:s e*Ͷ-?k@2ւ(zX$OcxpE4Oȕ12iot ?EvSYq$g{g).YU"Dsڞ@P2%scL~ù76O.Z) e@yimB["WsWz(9=bOM iNP9`􈣁j'c =yŏM&d8Y|&_ m IDG(lM#HG>V.,"g wCԂ [-F F(=Ł~M^'Fn4Ye:\cwLvIW(Mg&@%*؏Ҽdn$W;اrקГIʱP -j10yMBRJe ~-Wrn፣sdFt)HCf$ ~U>M>nMoKκ"S,]_=[wExJOlr}f8˓w!fgIήnq1 H-u nDYiSn@zo=EI{s7-_*_k#jT3E,d:h^aPؚ)$L_FT_Y5  ʨ0qIy񀼗YE-VMQZS+ڟo/ilhxB)?֐ms@x1W@vpHzn+a%&#Ea77Di؏}#Nvg",t?)~v g$ץf՗Z7G&ѿm0۶=j21n wS5T=CjT|̞⍋~,qxKLXFީ_`$[!Go~lףN\țWBmU:ͮԻ2<:;SYD&b𸘣,nϐ3ϹYm y,ىF3OV(G+*2k *߳ʗ4H5(g k0F$ysUt~ fyJ/['ҁ0) kK<8vG{qz@#-rix Ed`T˘*RyVI3lc-Y|3!~h,{9b" ]0ѡ\=mߚ}FΞ])A@!%"!0$]ʹeZ}#Q!ϦJ(YXE#i]kp_QSPҧ(2V} L3vD5}m?~Hyn}2yM[%|YΓۈ1c(#X\{|HQn73kLL!X cG0(P>*ZK"mO .,̀E/%:c 1jJRG#^7:y&G (,eM ajUS! Q- s,Wn"0FdȠ@s&fvPcSLF::ԐzkԽ.S/OשּׁԌTrg\dy`D6~k|{})4EHd3**,l ;Za裚'njaݴmܬ}놔m35*#"JIRF>,NiG|r&Udcˊm/Ќ}q>| :d(^>R@'s3FBuni60aҬh9ͨEo8LT) "6J޸( *f+BiZBv nS¶eAEw_N+|Ue1tD N ɍ:Z˿DT~;[w:"ر{=ģ | --1b{ѹLtʌȈm`S ;НETFZ3-_ bjO;M-KfftHbkcAj'%O|]8LlgOf9>$R1Kߺ"ZQ H4rW=;&g1}9 WUl=ϫlcQ#YF/R:;CN0~6`t|LeuMz-P(s7ʋνY֑SJi)y(2GOBIU.G$?:?1T<~izڗ\hU߷aTz8mHeNnDcd@ Du WVL aae磛I \sS`ה1|5c}J9ΰZCaEkQNר '^HnQI7̈03`r]mt9ΰZCa~v,2[ {(l %8A.BKSA֡\TΈċI՗F ~y LֳHbCVpN.g},V0NQluG6@'o VxKnZBQVjb^y"nP_+J#s3(.IW42PD1a#!X).U!BwP(;u~5jTCw9˴D07h{yKrĤX#`݈znO#W %~AG!~QuT콞΂3A¹ |0"F҂RP0ݡ4RBJIkFQ]d;!%Uډt#C1۠Gi JIkFQ]d;!U04ɦ~qb3<dN?Y.8ϫoIGxc3%8F!}8] :VISP(8mySv/S5g_k L1kOx?VؒĻ=ڦY)hr}kՇ|ʰ1tUݺA=qdk6Atɓy;V}Yq EvW}FΞ])n<]6B!'33M~tYMRbºAOy;8 X\?ͯK_5-sw]oyM07|EH 辡;5T4Y}#<$A,,1f֧31J ^@#jm1NJRXa ڇ JKA7:<|y^XԟEm^si?_wZs:=rMjZ%KMհKM.{( 1 ' gEF8⽧7 8fSNYP?8r:펤EUjQޯ% V]-0p &]s]8aWgq vXu%ި1ܛs2 1vܔ^@+jYi$?-ONnjI]*9x2>kD %Uډt#his->file_id(); $args['status'] = true; // Note the file for later cleanup $_SESSION['plugins']['filesystem_attachments'][$group][$args['id']] = $args['path']; return $args; } /** * Remove an attachment from storage * This is triggered by the remove attachment button on the compose screen */ function remove($args) { $args['status'] = $this->verify_path($args['path']) && @unlink($args['path']); return $args; } /** * When composing an html message, image attachments may be shown * For this plugin, the file is already in place, just check for * the existence of the proper metadata */ function display($args) { $args['status'] = $this->verify_path($args['path']) && file_exists($args['path']); return $args; } /** * This attachment plugin doesn't require any steps to put the file * on disk for use. This stub function is kept here to make this * class handy as a parent class for other plugins which may need it. */ function get($args) { if (!$this->verify_path($args['path'])) { $args['path'] = null; } return $args; } /** * Delete all temp files associated with this user */ function cleanup($args) { // $_SESSION['compose']['attachments'] is not a complete record of // temporary files because loading a draft or starting a forward copies // the file to disk, but does not make an entry in that array if (!empty($_SESSION['plugins']['filesystem_attachments'])) { foreach ($_SESSION['plugins']['filesystem_attachments'] as $group => $files) { if (!empty($args['group']) && $args['group'] != $group) { continue; } foreach ((array) $files as $filename) { if (file_exists($filename)) { unlink($filename); } } unset($_SESSION['plugins']['filesystem_attachments'][$group]); } } return $args; } protected static function file_id() { $userid = rcube::get_instance()->user->ID; list($usec, $sec) = explode(' ', microtime()); $id = preg_replace('/[^0-9]/', '', $userid . $sec . $usec); // make sure the ID is really unique (#1489546) while (self::find_file_by_id($id)) { // increment last four characters $x = substr($id, -4) + 1; $id = substr($id, 0, -4) . sprintf('%04d', ($x > 9999 ? $x - 9999 : $x)); } return $id; } private static function find_file_by_id($id) { if (!empty($_SESSION['plugins']['filesystem_attachments'])) { foreach ((array) $_SESSION['plugins']['filesystem_attachments'] as $files) { if (isset($files[$id])) { return true; } } } } /** * For security we'll always verify the file path stored in session, * as session entries can be faked in various ways e.g. #6026. * We allow only files in Roundcube temp dir */ protected static function verify_path($path) { if (empty($path)) { return false; } $rcmail = rcube::get_instance(); $temp_dir = $rcmail->config->get('temp_dir'); $file_path = pathinfo($path, PATHINFO_DIRNAME); if ($temp_dir !== $file_path) { // When the configured directory is not writable, or out of open_basedir path // tempnam() fallbacks to system temp without a warning. // We allow that, but we'll let to know the user about the misconfiguration. if ($file_path == sys_get_temp_dir()) { rcube::raise_error([ 'file' => __FILE__, 'line' => __LINE__, 'message' => "Detected 'temp_dir' change. " . "Access to '$temp_dir' restricted by filesystem permissions or open_basedir", ], true, false ); return true; } rcube::raise_error([ 'file' => __FILE__, 'line' => __LINE__, 'message' => sprintf("%s can't read %s (not in temp_dir)", $rcmail->get_user_name(), substr($path, 0, 512)) ], true, false ); return false; } return true; } }