ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ _allow_get_array = (config_item('allow_get_array') !== FALSE); $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); $this->_enable_csrf = (config_item('csrf_protection') === TRUE); $this->_standardize_newlines = (bool) config_item('standardize_newlines'); $this->security =& load_class('Security', 'core'); // Do we need the UTF-8 class? if (UTF8_ENABLED === TRUE) { $this->uni =& load_class('Utf8', 'core'); } // Sanitize global arrays $this->_sanitize_globals(); // CSRF Protection check if ($this->_enable_csrf === TRUE && ! is_cli()) { $this->security->csrf_verify(); } log_message('info', 'Input Class Initialized'); } // -------------------------------------------------------------------- /** * Fetch from array * * Internal method used to retrieve values from global arrays. * * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc. * @param mixed $index Index for item to be fetched from $array * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL) { is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; // If $index is NULL, it means that the whole $array is requested isset($index) OR $index = array_keys($array); // allow fetching multiple keys at once if (is_array($index)) { $output = array(); foreach ($index as $key) { $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean); } return $output; } if (isset($array[$index])) { $value = $array[$index]; } elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation { $value = $array; for ($i = 0; $i < $count; $i++) { $key = trim($matches[0][$i], '[]'); if ($key === '') // Empty notation will return the value as array { break; } if (isset($value[$key])) { $value = $value[$key]; } else { return NULL; } } } else { return NULL; } return ($xss_clean === TRUE) ? $this->security->xss_clean($value) : $value; } // -------------------------------------------------------------------- /** * Fetch an item from the GET array * * @param mixed $index Index for item to be fetched from $_GET * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function get($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_GET, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the POST array * * @param mixed $index Index for item to be fetched from $_POST * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function post($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_POST, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from POST data with fallback to GET * * @param string $index Index for item to be fetched from $_POST or $_GET * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function post_get($index, $xss_clean = NULL) { return isset($_POST[$index]) ? $this->post($index, $xss_clean) : $this->get($index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from GET data with fallback to POST * * @param string $index Index for item to be fetched from $_GET or $_POST * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function get_post($index, $xss_clean = NULL) { return isset($_GET[$index]) ? $this->get($index, $xss_clean) : $this->post($index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the COOKIE array * * @param mixed $index Index for item to be fetched from $_COOKIE * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function cookie($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the SERVER array * * @param mixed $index Index for item to be fetched from $_SERVER * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function server($index, $xss_clean = NULL) { return $this->_fetch_from_array($_SERVER, $index, $xss_clean); } // ------------------------------------------------------------------------ /** * Fetch an item from the php://input stream * * Useful when you need to access PUT, DELETE or PATCH request data. * * @param string $index Index for item to be fetched * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function input_stream($index = NULL, $xss_clean = NULL) { // Prior to PHP 5.6, the input stream can only be read once, // so we'll need to check if we have already done that first. if ( ! is_array($this->_input_stream)) { // $this->raw_input_stream will trigger __get(). parse_str($this->raw_input_stream, $this->_input_stream); is_array($this->_input_stream) OR $this->_input_stream = array(); } return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); } // ------------------------------------------------------------------------ /** * Set cookie * * Accepts an arbitrary number of parameters (up to 7) or an associative * array in the first parameter containing all the values. * * @param string|mixed[] $name Cookie name or an array containing parameters * @param string $value Cookie value * @param int $expire Cookie expiration time in seconds * @param string $domain Cookie domain (e.g.: '.yourdomain.com') * @param string $path Cookie path (default: '/') * @param string $prefix Cookie name prefix * @param bool $secure Whether to only transfer cookies via SSL * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript) * @return void */ public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL) { if (is_array($name)) { // always leave 'name' in last place, as the loop will break otherwise, due to $$item foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item) { if (isset($name[$item])) { $$item = $name[$item]; } } } if ($prefix === '' && config_item('cookie_prefix') !== '') { $prefix = config_item('cookie_prefix'); } if ($domain == '' && config_item('cookie_domain') != '') { $domain = config_item('cookie_domain'); } if ($path === '/' && config_item('cookie_path') !== '/') { $path = config_item('cookie_path'); } $secure = ($secure === NULL && config_item('cookie_secure') !== NULL) ? (bool) config_item('cookie_secure') : (bool) $secure; $httponly = ($httponly === NULL && config_item('cookie_httponly') !== NULL) ? (bool) config_item('cookie_httponly') : (bool) $httponly; if ( ! is_numeric($expire)) { $expire = time() - 86500; } else { $expire = ($expire > 0) ? time() + $expire : 0; } setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly); } // -------------------------------------------------------------------- /** * Fetch the IP Address * * Determines and validates the visitor's IP address. * * @return string IP address */ public function ip_address() { if ($this->ip_address !== FALSE) { return $this->ip_address; } $proxy_ips = config_item('proxy_ips'); if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) { $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); } $this->ip_address = $this->server('REMOTE_ADDR'); if ($proxy_ips) { foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) { if (($spoof = $this->server($header)) !== NULL) { // Some proxies typically list the whole chain of IP // addresses through which the client has reached us. // e.g. client_ip, proxy_ip1, proxy_ip2, etc. sscanf($spoof, '%[^,]', $spoof); if ( ! $this->valid_ip($spoof)) { $spoof = NULL; } else { break; } } } if ($spoof) { for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) { // Check if we have an IP address or a subnet if (strpos($proxy_ips[$i], '/') === FALSE) { // An IP address (and not a subnet) is specified. // We can compare right away. if ($proxy_ips[$i] === $this->ip_address) { $this->ip_address = $spoof; break; } continue; } // We have a subnet ... now the heavy lifting begins isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; // If the proxy entry doesn't match the IP protocol - skip it if (strpos($proxy_ips[$i], $separator) === FALSE) { continue; } // Convert the REMOTE_ADDR IP address to binary, if needed if ( ! isset($ip, $sprintf)) { if ($separator === ':') { // Make sure we're have the "full" IPv6 format $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ip_address, ':')), $this->ip_address ) ); for ($j = 0; $j < 8; $j++) { $ip[$j] = intval($ip[$j], 16); } $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; } else { $ip = explode('.', $this->ip_address); $sprintf = '%08b%08b%08b%08b'; } $ip = vsprintf($sprintf, $ip); } // Split the netmask length off the network address sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen); // Again, an IPv6 address is most likely in a compressed form if ($separator === ':') { $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); for ($j = 0; $j < 8; $j++) { $netaddr[$j] = intval($netaddr[$j], 16); } } else { $netaddr = explode('.', $netaddr); } // Convert to binary and finally compare if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) { $this->ip_address = $spoof; break; } } } } if ( ! $this->valid_ip($this->ip_address)) { return $this->ip_address = '0.0.0.0'; } return $this->ip_address; } // -------------------------------------------------------------------- /** * Validate IP Address * * @param string $ip IP address * @param string $which IP protocol: 'ipv4' or 'ipv6' * @return bool */ public function valid_ip($ip, $which = '') { switch (strtolower($which)) { case 'ipv4': $which = FILTER_FLAG_IPV4; break; case 'ipv6': $which = FILTER_FLAG_IPV6; break; default: $which = NULL; break; } return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which); } // -------------------------------------------------------------------- /** * Fetch User Agent string * * @return string|null User Agent string or NULL if it doesn't exist */ public function user_agent($xss_clean = NULL) { return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean); } // -------------------------------------------------------------------- /** * Sanitize Globals * * Internal method serving for the following purposes: * * - Unsets $_GET data, if query strings are not enabled * - Cleans POST, COOKIE and SERVER data * - Standardizes newline characters to PHP_EOL * * @return void */ protected function _sanitize_globals() { // Is $_GET data allowed? If not we'll set the $_GET to an empty array if ($this->_allow_get_array === FALSE) { $_GET = array(); } elseif (is_array($_GET)) { foreach ($_GET as $key => $val) { $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } } // Clean $_POST Data if (is_array($_POST)) { foreach ($_POST as $key => $val) { $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } } // Clean $_COOKIE Data if (is_array($_COOKIE)) { // Also get rid of specially treated cookies that might be set by a server // or silly application, that are of no use to a CI application anyway // but that when present will trip our 'Disallowed Key Characters' alarm // http://www.ietf.org/rfc/rfc2109.txt // note that the key names below are single quoted strings, and are not PHP variables unset( $_COOKIE['$Version'], $_COOKIE['$Path'], $_COOKIE['$Domain'] ); foreach ($_COOKIE as $key => $val) { if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) { $_COOKIE[$cookie_key] = $this->_clean_input_data($val); } else { unset($_COOKIE[$key]); } } } // Sanitize PHP_SELF $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } // -------------------------------------------------------------------- /** * Clean Input Data * * Internal method that aids in escaping data and * standardizing newline characters to PHP_EOL. * * @param string|string[] $str Input string(s) * @return string */ protected function _clean_input_data($str) { if (is_array($str)) { $new_array = array(); foreach (array_keys($str) as $key) { $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]); } return $new_array; } /* We strip slashes if magic quotes is on to keep things consistent NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and it will probably not exist in future versions at all. */ if ( ! is_php('5.4') && get_magic_quotes_gpc()) { $str = stripslashes($str); } // Clean UTF-8 if supported if (UTF8_ENABLED === TRUE) { $str = $this->uni->clean_string($str); } // Remove control characters $str = remove_invisible_characters($str, FALSE); // Standardize newlines if needed if ($this->_standardize_newlines === TRUE) { return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str); } return $str; } // -------------------------------------------------------------------- /** * Clean Keys * * Internal method that helps to prevent malicious users * from trying to exploit keys we make sure that keys are * only named with alpha-numeric text and a few other items. * * @param string $str Input string * @param bool $fatal Whether to terminate script exection * or to return FALSE if an invalid * key is encountered * @return string|bool */ protected function _clean_input_keys($str, $fatal = TRUE) { if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) { if ($fatal === TRUE) { return FALSE; } else { set_status_header(503); echo 'Disallowed Key Characters.'; exit(7); // EXIT_USER_INPUT } } // Clean UTF-8 if supported if (UTF8_ENABLED === TRUE) { return $this->uni->clean_string($str); } return $str; } // -------------------------------------------------------------------- /** * Request Headers * * @param bool $xss_clean Whether to apply XSS filtering * @return array */ public function request_headers($xss_clean = FALSE) { // If header is already defined, return it immediately if ( ! empty($this->headers)) { return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // In Apache, you can simply call apache_request_headers() if (function_exists('apache_request_headers')) { $this->headers = apache_request_headers(); } else { isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE']; foreach ($_SERVER as $key => $val) { if (sscanf($key, 'HTTP_%s', $header) === 1) { // take SOME_HEADER and turn it into Some-Header $header = str_replace('_', ' ', strtolower($header)); $header = str_replace(' ', '-', ucwords($header)); $this->headers[$header] = $_SERVER[$key]; } } } return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // -------------------------------------------------------------------- /** * Get Request Header * * Returns the value of a single member of the headers class member * * @param string $index Header name * @param bool $xss_clean Whether to apply XSS filtering * @return string|null The requested header on success or NULL on failure */ public function get_request_header($index, $xss_clean = FALSE) { static $headers; if ( ! isset($headers)) { empty($this->headers) && $this->request_headers(); foreach ($this->headers as $key => $value) { $headers[strtolower($key)] = $value; } } $index = strtolower($index); if ( ! isset($headers[$index])) { return NULL; } return ($xss_clean === TRUE) ? $this->security->xss_clean($headers[$index]) : $headers[$index]; } // -------------------------------------------------------------------- /** * Is AJAX request? * * Test to see if a request contains the HTTP_X_REQUESTED_WITH header. * * @return bool */ public function is_ajax_request() { return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); } // -------------------------------------------------------------------- /** * Is CLI request? * * Test to see if a request was made from the command line. * * @deprecated 3.0.0 Use is_cli() instead * @return bool */ public function is_cli_request() { return is_cli(); } // -------------------------------------------------------------------- /** * Get Request Method * * Return the request method * * @param bool $upper Whether to return in upper or lower case * (default: FALSE) * @return string */ public function method($upper = FALSE) { return ($upper) ? strtoupper($this->server('REQUEST_METHOD')) : strtolower($this->server('REQUEST_METHOD')); } // ------------------------------------------------------------------------ /** * Magic __get() * * Allows read access to protected properties * * @param string $name * @return mixed */ public function __get($name) { if ($name === 'raw_input_stream') { isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); return $this->_raw_input_stream; } elseif ($name === 'ip_address') { return $this->ip_address; } } }