diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..9e25198
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,4 @@
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule ^(.*)$ index.php/$1 [L]
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..311f0d2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,47 @@
+# ImgURL
+ImgURL是一款简单、好用的图床程序,使用PHP + SQLite 3开发,不需要复杂的配置,开箱即用。
+
+![](https://i.bmp.ovh/imgs/2018/12/06cf0ac3b7625b6b.png)
+
+![](https://i.bmp.ovh/imgs/2018/12/f0b565e2e0ffa166.png)
+
+![](https://i.bmp.ovh/imgs/2018/12/017c5e66b53db4d1.png)
+
+### 主要功能
+* 支持拽拖上传、多图上传、Ctrl + V粘贴上传、URL上传
+* 支持图片裁剪,自动生成缩略图
+* 限制访客上传数量
+* 图片压缩
+* 图片鉴黄
+* 友好的后台管理界面
+
+### 环境要求
+* PHP >= 5.6
+* PDO_SQLite
+* GD2
+* ImageMagick
+* fileinfo
+* pathinfo
+
+### 安装
+请参考帮助文档:[https://doc.xiaoz.me/](https://doc.xiaoz.me/#/imgurl2/)
+
+### Demo
+* [http://test.imgurl.org/](http://test.imgurl.org/)
+* 账号:xiaoz
+* 密码:xiaoz.me
+
+### 请我喝一杯咖啡
+![](https://www.xiaoz.me/wp-content/uploads/2013/12/juanzeng260.png)
+
+### 鸣谢
+* [LayUI](https://github.com/sentsin/layui)
+* [CodeIgniter](https://github.com/bcit-ci/CodeIgniter)
+* [clipBoard.js](https://github.com/baixuexiyang/clipBoard.js)
+* [Parsedown](https://github.com/erusev/parsedown)
+* [jQuery](https://github.com/jquery/jquery)
+
+### 联系我
+* Blog:[https://www.xiaoz.me/](https://www.xiaoz.me/)
+* 社区支持:[https://ttt.sh/](https://ttt.sh/category/6/)
+* QQ:337003006
\ No newline at end of file
diff --git a/application/.htaccess b/application/.htaccess
new file mode 100644
index 0000000..6c63ed4
--- /dev/null
+++ b/application/.htaccess
@@ -0,0 +1,6 @@
+
+ Require all denied
+
+
+ Deny from all
+
\ No newline at end of file
diff --git a/application/cache/index.html b/application/cache/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/cache/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/config/autoload.php b/application/config/autoload.php
new file mode 100644
index 0000000..7cdc901
--- /dev/null
+++ b/application/config/autoload.php
@@ -0,0 +1,135 @@
+ 'ua');
+*/
+$autoload['libraries'] = array();
+
+/*
+| -------------------------------------------------------------------
+| Auto-load Drivers
+| -------------------------------------------------------------------
+| These classes are located in system/libraries/ or in your
+| application/libraries/ directory, but are also placed inside their
+| own subdirectory and they extend the CI_Driver_Library class. They
+| offer multiple interchangeable driver options.
+|
+| Prototype:
+|
+| $autoload['drivers'] = array('cache');
+|
+| You can also supply an alternative property name to be assigned in
+| the controller:
+|
+| $autoload['drivers'] = array('cache' => 'cch');
+|
+*/
+$autoload['drivers'] = array();
+
+/*
+| -------------------------------------------------------------------
+| Auto-load Helper Files
+| -------------------------------------------------------------------
+| Prototype:
+|
+| $autoload['helper'] = array('url', 'file');
+*/
+$autoload['helper'] = array();
+
+/*
+| -------------------------------------------------------------------
+| Auto-load Config files
+| -------------------------------------------------------------------
+| Prototype:
+|
+| $autoload['config'] = array('config1', 'config2');
+|
+| NOTE: This item is intended for use ONLY if you have created custom
+| config files. Otherwise, leave it blank.
+|
+*/
+$autoload['config'] = array();
+
+/*
+| -------------------------------------------------------------------
+| Auto-load Language files
+| -------------------------------------------------------------------
+| Prototype:
+|
+| $autoload['language'] = array('lang1', 'lang2');
+|
+| NOTE: Do not include the "_lang" part of your file. For example
+| "codeigniter_lang.php" would be referenced as array('codeigniter');
+|
+*/
+$autoload['language'] = array();
+
+/*
+| -------------------------------------------------------------------
+| Auto-load Models
+| -------------------------------------------------------------------
+| Prototype:
+|
+| $autoload['model'] = array('first_model', 'second_model');
+|
+| You can also supply an alternative model name to be assigned
+| in the controller:
+|
+| $autoload['model'] = array('first_model' => 'first');
+*/
+$autoload['model'] = array();
diff --git a/application/config/config.php b/application/config/config.php
new file mode 100644
index 0000000..3a28882
--- /dev/null
+++ b/application/config/config.php
@@ -0,0 +1,523 @@
+]+$/i
+|
+| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
+|
+*/
+$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
+
+/*
+|--------------------------------------------------------------------------
+| Enable Query Strings
+|--------------------------------------------------------------------------
+|
+| By default CodeIgniter uses search-engine friendly segment based URLs:
+| example.com/who/what/where/
+|
+| You can optionally enable standard query string based URLs:
+| example.com?who=me&what=something&where=here
+|
+| Options are: TRUE or FALSE (boolean)
+|
+| The other items let you set the query string 'words' that will
+| invoke your controllers and its functions:
+| example.com/index.php?c=controller&m=function
+|
+| Please note that some of the helpers won't work as expected when
+| this feature is enabled, since CodeIgniter is designed primarily to
+| use segment based URLs.
+|
+*/
+$config['enable_query_strings'] = FALSE;
+$config['controller_trigger'] = 'c';
+$config['function_trigger'] = 'm';
+$config['directory_trigger'] = 'd';
+
+/*
+|--------------------------------------------------------------------------
+| Allow $_GET array
+|--------------------------------------------------------------------------
+|
+| By default CodeIgniter enables access to the $_GET array. If for some
+| reason you would like to disable it, set 'allow_get_array' to FALSE.
+|
+| WARNING: This feature is DEPRECATED and currently available only
+| for backwards compatibility purposes!
+|
+*/
+$config['allow_get_array'] = TRUE;
+
+/*
+|--------------------------------------------------------------------------
+| Error Logging Threshold
+|--------------------------------------------------------------------------
+|
+| You can enable error logging by setting a threshold over zero. The
+| threshold determines what gets logged. Threshold options are:
+|
+| 0 = Disables logging, Error logging TURNED OFF
+| 1 = Error Messages (including PHP errors)
+| 2 = Debug Messages
+| 3 = Informational Messages
+| 4 = All Messages
+|
+| You can also pass an array with threshold levels to show individual error types
+|
+| array(2) = Debug Messages, without Error Messages
+|
+| For a live site you'll usually only enable Errors (1) to be logged otherwise
+| your log files will fill up very fast.
+|
+*/
+$config['log_threshold'] = 0;
+
+/*
+|--------------------------------------------------------------------------
+| Error Logging Directory Path
+|--------------------------------------------------------------------------
+|
+| Leave this BLANK unless you would like to set something other than the default
+| application/logs/ directory. Use a full server path with trailing slash.
+|
+*/
+$config['log_path'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Log File Extension
+|--------------------------------------------------------------------------
+|
+| The default filename extension for log files. The default 'php' allows for
+| protecting the log files via basic scripting, when they are to be stored
+| under a publicly accessible directory.
+|
+| Note: Leaving it blank will default to 'php'.
+|
+*/
+$config['log_file_extension'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Log File Permissions
+|--------------------------------------------------------------------------
+|
+| The file system permissions to be applied on newly created log files.
+|
+| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
+| integer notation (i.e. 0700, 0644, etc.)
+*/
+$config['log_file_permissions'] = 0644;
+
+/*
+|--------------------------------------------------------------------------
+| Date Format for Logs
+|--------------------------------------------------------------------------
+|
+| Each item that is logged has an associated date. You can use PHP date
+| codes to set your own date formatting
+|
+*/
+$config['log_date_format'] = 'Y-m-d H:i:s';
+
+/*
+|--------------------------------------------------------------------------
+| Error Views Directory Path
+|--------------------------------------------------------------------------
+|
+| Leave this BLANK unless you would like to set something other than the default
+| application/views/errors/ directory. Use a full server path with trailing slash.
+|
+*/
+$config['error_views_path'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Cache Directory Path
+|--------------------------------------------------------------------------
+|
+| Leave this BLANK unless you would like to set something other than the default
+| application/cache/ directory. Use a full server path with trailing slash.
+|
+*/
+$config['cache_path'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Cache Include Query String
+|--------------------------------------------------------------------------
+|
+| Whether to take the URL query string into consideration when generating
+| output cache files. Valid options are:
+|
+| FALSE = Disabled
+| TRUE = Enabled, take all query parameters into account.
+| Please be aware that this may result in numerous cache
+| files generated for the same page over and over again.
+| array('q') = Enabled, but only take into account the specified list
+| of query parameters.
+|
+*/
+$config['cache_query_string'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Encryption Key
+|--------------------------------------------------------------------------
+|
+| If you use the Encryption class, you must set an encryption key.
+| See the user guide for more info.
+|
+| https://codeigniter.com/user_guide/libraries/encryption.html
+|
+*/
+$config['encryption_key'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Session Variables
+|--------------------------------------------------------------------------
+|
+| 'sess_driver'
+|
+| The storage driver to use: files, database, redis, memcached
+|
+| 'sess_cookie_name'
+|
+| The session cookie name, must contain only [0-9a-z_-] characters
+|
+| 'sess_expiration'
+|
+| The number of SECONDS you want the session to last.
+| Setting to 0 (zero) means expire when the browser is closed.
+|
+| 'sess_save_path'
+|
+| The location to save sessions to, driver dependent.
+|
+| For the 'files' driver, it's a path to a writable directory.
+| WARNING: Only absolute paths are supported!
+|
+| For the 'database' driver, it's a table name.
+| Please read up the manual for the format with other session drivers.
+|
+| IMPORTANT: You are REQUIRED to set a valid save path!
+|
+| 'sess_match_ip'
+|
+| Whether to match the user's IP address when reading the session data.
+|
+| WARNING: If you're using the database driver, don't forget to update
+| your session table's PRIMARY KEY when changing this setting.
+|
+| 'sess_time_to_update'
+|
+| How many seconds between CI regenerating the session ID.
+|
+| 'sess_regenerate_destroy'
+|
+| Whether to destroy session data associated with the old session ID
+| when auto-regenerating the session ID. When set to FALSE, the data
+| will be later deleted by the garbage collector.
+|
+| Other session cookie settings are shared with the rest of the application,
+| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
+|
+*/
+$config['sess_driver'] = 'files';
+$config['sess_cookie_name'] = 'ci_session';
+$config['sess_expiration'] = 7200;
+$config['sess_save_path'] = NULL;
+$config['sess_match_ip'] = FALSE;
+$config['sess_time_to_update'] = 300;
+$config['sess_regenerate_destroy'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Cookie Related Variables
+|--------------------------------------------------------------------------
+|
+| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
+| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
+| 'cookie_path' = Typically will be a forward slash
+| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
+| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
+|
+| Note: These settings (with the exception of 'cookie_prefix' and
+| 'cookie_httponly') will also affect sessions.
+|
+*/
+$config['cookie_prefix'] = '';
+$config['cookie_domain'] = '';
+$config['cookie_path'] = '/';
+$config['cookie_secure'] = FALSE;
+$config['cookie_httponly'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Standardize newlines
+|--------------------------------------------------------------------------
+|
+| Determines whether to standardize newline characters in input data,
+| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
+|
+| WARNING: This feature is DEPRECATED and currently available only
+| for backwards compatibility purposes!
+|
+*/
+$config['standardize_newlines'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Global XSS Filtering
+|--------------------------------------------------------------------------
+|
+| Determines whether the XSS filter is always active when GET, POST or
+| COOKIE data is encountered
+|
+| WARNING: This feature is DEPRECATED and currently available only
+| for backwards compatibility purposes!
+|
+*/
+$config['global_xss_filtering'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Cross Site Request Forgery
+|--------------------------------------------------------------------------
+| Enables a CSRF cookie token to be set. When set to TRUE, token will be
+| checked on a submitted form. If you are accepting user data, it is strongly
+| recommended CSRF protection be enabled.
+|
+| 'csrf_token_name' = The token name
+| 'csrf_cookie_name' = The cookie name
+| 'csrf_expire' = The number in seconds the token should expire.
+| 'csrf_regenerate' = Regenerate token on every submission
+| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
+*/
+$config['csrf_protection'] = FALSE;
+$config['csrf_token_name'] = 'csrf_test_name';
+$config['csrf_cookie_name'] = 'csrf_cookie_name';
+$config['csrf_expire'] = 7200;
+$config['csrf_regenerate'] = TRUE;
+$config['csrf_exclude_uris'] = array();
+
+/*
+|--------------------------------------------------------------------------
+| Output Compression
+|--------------------------------------------------------------------------
+|
+| Enables Gzip output compression for faster page loads. When enabled,
+| the output class will test whether your server supports Gzip.
+| Even if it does, however, not all browsers support compression
+| so enable only if you are reasonably sure your visitors can handle it.
+|
+| Only used if zlib.output_compression is turned off in your php.ini.
+| Please do not use it together with httpd-level output compression.
+|
+| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
+| means you are prematurely outputting something to your browser. It could
+| even be a line of whitespace at the end of one of your scripts. For
+| compression to work, nothing can be sent before the output buffer is called
+| by the output class. Do not 'echo' any values with compression enabled.
+|
+*/
+$config['compress_output'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Master Time Reference
+|--------------------------------------------------------------------------
+|
+| Options are 'local' or any PHP supported timezone. This preference tells
+| the system whether to use your server's local time as the master 'now'
+| reference, or convert it to the configured one timezone. See the 'date
+| helper' page of the user guide for information regarding date handling.
+|
+*/
+$config['time_reference'] = 'local';
+
+/*
+|--------------------------------------------------------------------------
+| Rewrite PHP Short Tags
+|--------------------------------------------------------------------------
+|
+| If your PHP installation does not have short tag support enabled CI
+| can rewrite the tags on-the-fly, enabling you to utilize that syntax
+| in your view files. Options are TRUE or FALSE (boolean)
+|
+| Note: You need to have eval() enabled for this to work.
+|
+*/
+$config['rewrite_short_tags'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
+| Reverse Proxy IPs
+|--------------------------------------------------------------------------
+|
+| If your server is behind a reverse proxy, you must whitelist the proxy
+| IP addresses from which CodeIgniter should trust headers such as
+| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
+| the visitor's IP address.
+|
+| You can use both an array or a comma-separated list of proxy addresses,
+| as well as specifying whole subnets. Here are a few examples:
+|
+| Comma-separated: '10.0.1.200,192.168.5.0/24'
+| Array: array('10.0.1.200', '192.168.5.0/24')
+*/
+$config['proxy_ips'] = '';
diff --git a/application/config/constants.php b/application/config/constants.php
new file mode 100644
index 0000000..18d3b4b
--- /dev/null
+++ b/application/config/constants.php
@@ -0,0 +1,85 @@
+db->last_query() and profiling of DB queries.
+| When you run a query, with this setting set to TRUE (default),
+| CodeIgniter will store the SQL statement for debugging purposes.
+| However, this may cause high memory usage, especially if you run
+| a lot of SQL queries ... disable this to avoid that problem.
+|
+| The $active_group variable lets you choose which connection group to
+| make active. By default there is only one group (the 'default' group).
+|
+| The $query_builder variables lets you determine whether or not to load
+| the query builder class.
+*/
+$active_group = 'default';
+$query_builder = TRUE;
+
+$db['default'] = array(
+ 'dsn' => 'sqlite:'.FCPATH.'data/imgurl.db3',
+ 'hostname' => 'localhost',
+ 'username' => '',
+ 'password' => '',
+ 'database' => '',
+ 'dbdriver' => 'pdo',
+ 'dbprefix' => 'img_',
+ 'pconnect' => FALSE,
+ 'db_debug' => (ENVIRONMENT !== 'production'),
+ 'cache_on' => FALSE,
+ 'cachedir' => '',
+ 'char_set' => 'utf8',
+ 'dbcollat' => 'utf8_general_ci',
+ 'swap_pre' => '',
+ 'encrypt' => FALSE,
+ 'compress' => FALSE,
+ 'stricton' => FALSE,
+ 'failover' => array(),
+ 'save_queries' => TRUE
+);
diff --git a/application/config/doctypes.php b/application/config/doctypes.php
new file mode 100644
index 0000000..59a7991
--- /dev/null
+++ b/application/config/doctypes.php
@@ -0,0 +1,24 @@
+ '',
+ 'xhtml1-strict' => '',
+ 'xhtml1-trans' => '',
+ 'xhtml1-frame' => '',
+ 'xhtml-basic11' => '',
+ 'html5' => '',
+ 'html4-strict' => '',
+ 'html4-trans' => '',
+ 'html4-frame' => '',
+ 'mathml1' => '',
+ 'mathml2' => '',
+ 'svg10' => '',
+ 'svg11' => '',
+ 'svg11-basic' => '',
+ 'svg11-tiny' => '',
+ 'xhtml-math-svg-xh' => '',
+ 'xhtml-math-svg-sh' => '',
+ 'xhtml-rdfa-1' => '',
+ 'xhtml-rdfa-2' => ''
+);
diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php
new file mode 100644
index 0000000..995f483
--- /dev/null
+++ b/application/config/foreign_chars.php
@@ -0,0 +1,103 @@
+ 'ae',
+ '/ö|œ/' => 'oe',
+ '/ü/' => 'ue',
+ '/Ä/' => 'Ae',
+ '/Ü/' => 'Ue',
+ '/Ö/' => 'Oe',
+ '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',
+ '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',
+ '/Б/' => 'B',
+ '/б/' => 'b',
+ '/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
+ '/ç|ć|ĉ|ċ|č/' => 'c',
+ '/Д/' => 'D',
+ '/д/' => 'd',
+ '/Ð|Ď|Đ|Δ/' => 'Dj',
+ '/ð|ď|đ|δ/' => 'dj',
+ '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E',
+ '/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е|э/' => 'e',
+ '/Ф/' => 'F',
+ '/ф/' => 'f',
+ '/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',
+ '/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
+ '/Ĥ|Ħ/' => 'H',
+ '/ĥ|ħ/' => 'h',
+ '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',
+ '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',
+ '/Ĵ/' => 'J',
+ '/ĵ/' => 'j',
+ '/Ķ|Κ|К/' => 'K',
+ '/ķ|κ|к/' => 'k',
+ '/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
+ '/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
+ '/М/' => 'M',
+ '/м/' => 'm',
+ '/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',
+ '/ñ|ń|ņ|ň|ʼn|ν|н/' => 'n',
+ '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',
+ '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',
+ '/П/' => 'P',
+ '/п/' => 'p',
+ '/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',
+ '/ŕ|ŗ|ř|ρ|р/' => 'r',
+ '/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',
+ '/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
+ '/Ț|Ţ|Ť|Ŧ|τ|Т/' => 'T',
+ '/ț|ţ|ť|ŧ|т/' => 't',
+ '/Þ|þ/' => 'th',
+ '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
+ '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
+ '/Ƴ|Ɏ|Ỵ|Ẏ|Ӳ|Ӯ|Ў|Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
+ '/ẙ|ʏ|ƴ|ɏ|ỵ|ẏ|ӳ|ӯ|ў|ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',
+ '/В/' => 'V',
+ '/в/' => 'v',
+ '/Ŵ/' => 'W',
+ '/ŵ/' => 'w',
+ '/Ź|Ż|Ž|Ζ|З/' => 'Z',
+ '/ź|ż|ž|ζ|з/' => 'z',
+ '/Æ|Ǽ/' => 'AE',
+ '/ß/' => 'ss',
+ '/IJ/' => 'IJ',
+ '/ij/' => 'ij',
+ '/Œ/' => 'OE',
+ '/ƒ/' => 'f',
+ '/ξ/' => 'ks',
+ '/π/' => 'p',
+ '/β/' => 'v',
+ '/μ/' => 'm',
+ '/ψ/' => 'ps',
+ '/Ё/' => 'Yo',
+ '/ё/' => 'yo',
+ '/Є/' => 'Ye',
+ '/є/' => 'ye',
+ '/Ї/' => 'Yi',
+ '/Ж/' => 'Zh',
+ '/ж/' => 'zh',
+ '/Х/' => 'Kh',
+ '/х/' => 'kh',
+ '/Ц/' => 'Ts',
+ '/ц/' => 'ts',
+ '/Ч/' => 'Ch',
+ '/ч/' => 'ch',
+ '/Ш/' => 'Sh',
+ '/ш/' => 'sh',
+ '/Щ/' => 'Shch',
+ '/щ/' => 'shch',
+ '/Ъ|ъ|Ь|ь/' => '',
+ '/Ю/' => 'Yu',
+ '/ю/' => 'yu',
+ '/Я/' => 'Ya',
+ '/я/' => 'ya'
+);
diff --git a/application/config/hooks.php b/application/config/hooks.php
new file mode 100644
index 0000000..a8f38a5
--- /dev/null
+++ b/application/config/hooks.php
@@ -0,0 +1,13 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/config/memcached.php b/application/config/memcached.php
new file mode 100644
index 0000000..5c23b39
--- /dev/null
+++ b/application/config/memcached.php
@@ -0,0 +1,19 @@
+ array(
+ 'hostname' => '127.0.0.1',
+ 'port' => '11211',
+ 'weight' => '1',
+ ),
+);
diff --git a/application/config/migration.php b/application/config/migration.php
new file mode 100644
index 0000000..4b585a6
--- /dev/null
+++ b/application/config/migration.php
@@ -0,0 +1,84 @@
+migration->current() this is the version that schema will
+| be upgraded / downgraded to.
+|
+*/
+$config['migration_version'] = 0;
+
+/*
+|--------------------------------------------------------------------------
+| Migrations Path
+|--------------------------------------------------------------------------
+|
+| Path to your migrations folder.
+| Typically, it will be within your application path.
+| Also, writing permission is required within the migrations path.
+|
+*/
+$config['migration_path'] = APPPATH.'migrations/';
diff --git a/application/config/mimes.php b/application/config/mimes.php
new file mode 100644
index 0000000..0ec9db0
--- /dev/null
+++ b/application/config/mimes.php
@@ -0,0 +1,184 @@
+ array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
+ 'cpt' => 'application/mac-compactpro',
+ 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'),
+ 'bin' => array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
+ 'dms' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'exe' => array('application/octet-stream', 'application/x-msdownload'),
+ 'class' => 'application/octet-stream',
+ 'psd' => array('application/x-photoshop', 'image/vnd.adobe.photoshop'),
+ 'so' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'),
+ 'ai' => array('application/pdf', 'application/postscript'),
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'),
+ 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'),
+ 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'),
+ 'wbxml' => 'application/wbxml',
+ 'wmlc' => 'application/wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'gz' => 'application/x-gzip',
+ 'gzip' => 'application/x-gzip',
+ 'php' => array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),
+ 'php4' => 'application/x-httpd-php',
+ 'php3' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'js' => array('application/x-javascript', 'text/plain'),
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
+ 'z' => 'application/x-compress',
+ 'xhtml' => 'application/xhtml+xml',
+ 'xht' => 'application/xhtml+xml',
+ 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),
+ 'rar' => array('application/x-rar', 'application/rar', 'application/x-rar-compressed'),
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mpga' => 'audio/mpeg',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
+ 'aif' => array('audio/x-aiff', 'audio/aiff'),
+ 'aiff' => array('audio/x-aiff', 'audio/aiff'),
+ 'aifc' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
+ 'bmp' => array('image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'),
+ 'gif' => 'image/gif',
+ 'jpeg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpe' => array('image/jpeg', 'image/pjpeg'),
+ 'jp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'j2k' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpf' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpg2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpx' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpm' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'mj2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'mjp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'png' => array('image/png', 'image/x-png'),
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'css' => array('text/css', 'text/plain'),
+ 'html' => array('text/html', 'text/plain'),
+ 'htm' => array('text/html', 'text/plain'),
+ 'shtml' => array('text/html', 'text/plain'),
+ 'txt' => 'text/plain',
+ 'text' => 'text/plain',
+ 'log' => array('text/plain', 'text/x-log'),
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'xml' => array('application/xml', 'text/xml', 'text/plain'),
+ 'xsl' => array('application/xml', 'text/xsl', 'text/xml'),
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'qt' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+ 'avi' => array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
+ 'movie' => 'video/x-sgi-movie',
+ 'doc' => array('application/msword', 'application/vnd.ms-office'),
+ 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'),
+ 'dot' => array('application/msword', 'application/vnd.ms-office'),
+ 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
+ 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'),
+ 'word' => array('application/msword', 'application/octet-stream'),
+ 'xl' => 'application/excel',
+ 'eml' => 'message/rfc822',
+ 'json' => array('application/json', 'text/json'),
+ 'pem' => array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
+ 'p10' => array('application/x-pkcs10', 'application/pkcs10'),
+ 'p12' => 'application/x-pkcs12',
+ 'p7a' => 'application/x-pkcs7-signature',
+ 'p7c' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+ 'p7m' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+ 'p7r' => 'application/x-pkcs7-certreqresp',
+ 'p7s' => 'application/pkcs7-signature',
+ 'crt' => array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
+ 'crl' => array('application/pkix-crl', 'application/pkcs-crl'),
+ 'der' => 'application/x-x509-ca-cert',
+ 'kdb' => 'application/octet-stream',
+ 'pgp' => 'application/pgp',
+ 'gpg' => 'application/gpg-keys',
+ 'sst' => 'application/octet-stream',
+ 'csr' => 'application/octet-stream',
+ 'rsa' => 'application/x-pkcs7',
+ 'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'),
+ '3g2' => 'video/3gpp2',
+ '3gp' => array('video/3gp', 'video/3gpp'),
+ 'mp4' => 'video/mp4',
+ 'm4a' => 'audio/x-m4a',
+ 'f4v' => array('video/mp4', 'video/x-f4v'),
+ 'flv' => 'video/x-flv',
+ 'webm' => 'video/webm',
+ 'aac' => 'audio/x-acc',
+ 'm4u' => 'application/vnd.mpegurl',
+ 'm3u' => 'text/plain',
+ 'xspf' => 'application/xspf+xml',
+ 'vlc' => 'application/videolan',
+ 'wmv' => array('video/x-ms-wmv', 'video/x-ms-asf'),
+ 'au' => 'audio/x-au',
+ 'ac3' => 'audio/ac3',
+ 'flac' => 'audio/x-flac',
+ 'ogg' => array('audio/ogg', 'video/ogg', 'application/ogg'),
+ 'kmz' => array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
+ 'kml' => array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),
+ 'ics' => 'text/calendar',
+ 'ical' => 'text/calendar',
+ 'zsh' => 'text/x-scriptzsh',
+ '7z' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
+ '7zip' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
+ 'cdr' => array('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'),
+ 'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'),
+ 'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),
+ 'svg' => array('image/svg+xml', 'application/xml', 'text/xml'),
+ 'vcf' => 'text/x-vcard',
+ 'srt' => array('text/srt', 'text/plain'),
+ 'vtt' => array('text/vtt', 'text/plain'),
+ 'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'),
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
+ 'otc' => 'application/vnd.oasis.opendocument.chart-template',
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
+ 'otf' => 'application/vnd.oasis.opendocument.formula-template',
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+ 'odi' => 'application/vnd.oasis.opendocument.image',
+ 'oti' => 'application/vnd.oasis.opendocument.image-template',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
+ 'oth' => 'application/vnd.oasis.opendocument.text-web'
+);
diff --git a/application/config/profiler.php b/application/config/profiler.php
new file mode 100644
index 0000000..3db22e3
--- /dev/null
+++ b/application/config/profiler.php
@@ -0,0 +1,14 @@
+ my_controller/index
+| my-controller/my-method -> my_controller/my_method
+*/
+$route['default_controller'] = 'home';
+$route['404_override'] = '';
+$route['translate_uri_dashes'] = FALSE;
diff --git a/application/config/smileys.php b/application/config/smileys.php
new file mode 100644
index 0000000..abf9a89
--- /dev/null
+++ b/application/config/smileys.php
@@ -0,0 +1,64 @@
+ array('grin.gif', '19', '19', 'grin'),
+ ':lol:' => array('lol.gif', '19', '19', 'LOL'),
+ ':cheese:' => array('cheese.gif', '19', '19', 'cheese'),
+ ':)' => array('smile.gif', '19', '19', 'smile'),
+ ';-)' => array('wink.gif', '19', '19', 'wink'),
+ ';)' => array('wink.gif', '19', '19', 'wink'),
+ ':smirk:' => array('smirk.gif', '19', '19', 'smirk'),
+ ':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'),
+ ':-S' => array('confused.gif', '19', '19', 'confused'),
+ ':wow:' => array('surprise.gif', '19', '19', 'surprised'),
+ ':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'),
+ ':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'),
+ '%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'),
+ ';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'),
+ ':P' => array('raspberry.gif', '19', '19', 'raspberry'),
+ ':blank:' => array('blank.gif', '19', '19', 'blank stare'),
+ ':long:' => array('longface.gif', '19', '19', 'long face'),
+ ':ohh:' => array('ohh.gif', '19', '19', 'ohh'),
+ ':grrr:' => array('grrr.gif', '19', '19', 'grrr'),
+ ':gulp:' => array('gulp.gif', '19', '19', 'gulp'),
+ '8-/' => array('ohoh.gif', '19', '19', 'oh oh'),
+ ':down:' => array('downer.gif', '19', '19', 'downer'),
+ ':red:' => array('embarrassed.gif', '19', '19', 'red face'),
+ ':sick:' => array('sick.gif', '19', '19', 'sick'),
+ ':shut:' => array('shuteye.gif', '19', '19', 'shut eye'),
+ ':-/' => array('hmm.gif', '19', '19', 'hmmm'),
+ '>:(' => array('mad.gif', '19', '19', 'mad'),
+ ':mad:' => array('mad.gif', '19', '19', 'mad'),
+ '>:-(' => array('angry.gif', '19', '19', 'angry'),
+ ':angry:' => array('angry.gif', '19', '19', 'angry'),
+ ':zip:' => array('zip.gif', '19', '19', 'zipper'),
+ ':kiss:' => array('kiss.gif', '19', '19', 'kiss'),
+ ':ahhh:' => array('shock.gif', '19', '19', 'shock'),
+ ':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'),
+ ':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'),
+ ':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'),
+ ':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'),
+ ':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'),
+ ':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'),
+ ':vampire:' => array('vampire.gif', '19', '19', 'vampire'),
+ ':snake:' => array('snake.gif', '19', '19', 'snake'),
+ ':exclaim:' => array('exclaim.gif', '19', '19', 'exclaim'),
+ ':question:' => array('question.gif', '19', '19', 'question')
+
+);
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
new file mode 100644
index 0000000..b6c8563
--- /dev/null
+++ b/application/config/user_agents.php
@@ -0,0 +1,214 @@
+ 'Windows 10',
+ 'windows nt 6.3' => 'Windows 8.1',
+ 'windows nt 6.2' => 'Windows 8',
+ 'windows nt 6.1' => 'Windows 7',
+ 'windows nt 6.0' => 'Windows Vista',
+ 'windows nt 5.2' => 'Windows 2003',
+ 'windows nt 5.1' => 'Windows XP',
+ 'windows nt 5.0' => 'Windows 2000',
+ 'windows nt 4.0' => 'Windows NT 4.0',
+ 'winnt4.0' => 'Windows NT 4.0',
+ 'winnt 4.0' => 'Windows NT',
+ 'winnt' => 'Windows NT',
+ 'windows 98' => 'Windows 98',
+ 'win98' => 'Windows 98',
+ 'windows 95' => 'Windows 95',
+ 'win95' => 'Windows 95',
+ 'windows phone' => 'Windows Phone',
+ 'windows' => 'Unknown Windows OS',
+ 'android' => 'Android',
+ 'blackberry' => 'BlackBerry',
+ 'iphone' => 'iOS',
+ 'ipad' => 'iOS',
+ 'ipod' => 'iOS',
+ 'os x' => 'Mac OS X',
+ 'ppc mac' => 'Power PC Mac',
+ 'freebsd' => 'FreeBSD',
+ 'ppc' => 'Macintosh',
+ 'linux' => 'Linux',
+ 'debian' => 'Debian',
+ 'sunos' => 'Sun Solaris',
+ 'beos' => 'BeOS',
+ 'apachebench' => 'ApacheBench',
+ 'aix' => 'AIX',
+ 'irix' => 'Irix',
+ 'osf' => 'DEC OSF',
+ 'hp-ux' => 'HP-UX',
+ 'netbsd' => 'NetBSD',
+ 'bsdi' => 'BSDi',
+ 'openbsd' => 'OpenBSD',
+ 'gnu' => 'GNU/Linux',
+ 'unix' => 'Unknown Unix OS',
+ 'symbian' => 'Symbian OS'
+);
+
+
+// The order of this array should NOT be changed. Many browsers return
+// multiple browser types so we want to identify the sub-type first.
+$browsers = array(
+ 'OPR' => 'Opera',
+ 'Flock' => 'Flock',
+ 'Edge' => 'Edge',
+ 'Chrome' => 'Chrome',
+ // Opera 10+ always reports Opera/9.80 and appends Version/ to the user agent string
+ 'Opera.*?Version' => 'Opera',
+ 'Opera' => 'Opera',
+ 'MSIE' => 'Internet Explorer',
+ 'Internet Explorer' => 'Internet Explorer',
+ 'Trident.* rv' => 'Internet Explorer',
+ 'Shiira' => 'Shiira',
+ 'Firefox' => 'Firefox',
+ 'Chimera' => 'Chimera',
+ 'Phoenix' => 'Phoenix',
+ 'Firebird' => 'Firebird',
+ 'Camino' => 'Camino',
+ 'Netscape' => 'Netscape',
+ 'OmniWeb' => 'OmniWeb',
+ 'Safari' => 'Safari',
+ 'Mozilla' => 'Mozilla',
+ 'Konqueror' => 'Konqueror',
+ 'icab' => 'iCab',
+ 'Lynx' => 'Lynx',
+ 'Links' => 'Links',
+ 'hotjava' => 'HotJava',
+ 'amaya' => 'Amaya',
+ 'IBrowse' => 'IBrowse',
+ 'Maxthon' => 'Maxthon',
+ 'Ubuntu' => 'Ubuntu Web Browser'
+);
+
+$mobiles = array(
+ // legacy array, old values commented out
+ 'mobileexplorer' => 'Mobile Explorer',
+// 'openwave' => 'Open Wave',
+// 'opera mini' => 'Opera Mini',
+// 'operamini' => 'Opera Mini',
+// 'elaine' => 'Palm',
+ 'palmsource' => 'Palm',
+// 'digital paths' => 'Palm',
+// 'avantgo' => 'Avantgo',
+// 'xiino' => 'Xiino',
+ 'palmscape' => 'Palmscape',
+// 'nokia' => 'Nokia',
+// 'ericsson' => 'Ericsson',
+// 'blackberry' => 'BlackBerry',
+// 'motorola' => 'Motorola'
+
+ // Phones and Manufacturers
+ 'motorola' => 'Motorola',
+ 'nokia' => 'Nokia',
+ 'palm' => 'Palm',
+ 'iphone' => 'Apple iPhone',
+ 'ipad' => 'iPad',
+ 'ipod' => 'Apple iPod Touch',
+ 'sony' => 'Sony Ericsson',
+ 'ericsson' => 'Sony Ericsson',
+ 'blackberry' => 'BlackBerry',
+ 'cocoon' => 'O2 Cocoon',
+ 'blazer' => 'Treo',
+ 'lg' => 'LG',
+ 'amoi' => 'Amoi',
+ 'xda' => 'XDA',
+ 'mda' => 'MDA',
+ 'vario' => 'Vario',
+ 'htc' => 'HTC',
+ 'samsung' => 'Samsung',
+ 'sharp' => 'Sharp',
+ 'sie-' => 'Siemens',
+ 'alcatel' => 'Alcatel',
+ 'benq' => 'BenQ',
+ 'ipaq' => 'HP iPaq',
+ 'mot-' => 'Motorola',
+ 'playstation portable' => 'PlayStation Portable',
+ 'playstation 3' => 'PlayStation 3',
+ 'playstation vita' => 'PlayStation Vita',
+ 'hiptop' => 'Danger Hiptop',
+ 'nec-' => 'NEC',
+ 'panasonic' => 'Panasonic',
+ 'philips' => 'Philips',
+ 'sagem' => 'Sagem',
+ 'sanyo' => 'Sanyo',
+ 'spv' => 'SPV',
+ 'zte' => 'ZTE',
+ 'sendo' => 'Sendo',
+ 'nintendo dsi' => 'Nintendo DSi',
+ 'nintendo ds' => 'Nintendo DS',
+ 'nintendo 3ds' => 'Nintendo 3DS',
+ 'wii' => 'Nintendo Wii',
+ 'open web' => 'Open Web',
+ 'openweb' => 'OpenWeb',
+
+ // Operating Systems
+ 'android' => 'Android',
+ 'symbian' => 'Symbian',
+ 'SymbianOS' => 'SymbianOS',
+ 'elaine' => 'Palm',
+ 'series60' => 'Symbian S60',
+ 'windows ce' => 'Windows CE',
+
+ // Browsers
+ 'obigo' => 'Obigo',
+ 'netfront' => 'Netfront Browser',
+ 'openwave' => 'Openwave Browser',
+ 'mobilexplorer' => 'Mobile Explorer',
+ 'operamini' => 'Opera Mini',
+ 'opera mini' => 'Opera Mini',
+ 'opera mobi' => 'Opera Mobile',
+ 'fennec' => 'Firefox Mobile',
+
+ // Other
+ 'digital paths' => 'Digital Paths',
+ 'avantgo' => 'AvantGo',
+ 'xiino' => 'Xiino',
+ 'novarra' => 'Novarra Transcoder',
+ 'vodafone' => 'Vodafone',
+ 'docomo' => 'NTT DoCoMo',
+ 'o2' => 'O2',
+
+ // Fallback
+ 'mobile' => 'Generic Mobile',
+ 'wireless' => 'Generic Mobile',
+ 'j2me' => 'Generic Mobile',
+ 'midp' => 'Generic Mobile',
+ 'cldc' => 'Generic Mobile',
+ 'up.link' => 'Generic Mobile',
+ 'up.browser' => 'Generic Mobile',
+ 'smartphone' => 'Generic Mobile',
+ 'cellphone' => 'Generic Mobile'
+);
+
+// There are hundreds of bots but these are the most common.
+$robots = array(
+ 'googlebot' => 'Googlebot',
+ 'msnbot' => 'MSNBot',
+ 'baiduspider' => 'Baiduspider',
+ 'bingbot' => 'Bing',
+ 'slurp' => 'Inktomi Slurp',
+ 'yahoo' => 'Yahoo',
+ 'ask jeeves' => 'Ask Jeeves',
+ 'fastcrawler' => 'FastCrawler',
+ 'infoseek' => 'InfoSeek Robot 1.0',
+ 'lycos' => 'Lycos',
+ 'yandex' => 'YandexBot',
+ 'mediapartners-google' => 'MediaPartners Google',
+ 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
+ 'adsbot-google' => 'AdsBot Google',
+ 'feedfetcher-google' => 'Feedfetcher Google',
+ 'curious george' => 'Curious George',
+ 'ia_archiver' => 'Alexa Crawler',
+ 'MJ12bot' => 'Majestic-12',
+ 'Uptimebot' => 'Uptimebot'
+);
diff --git a/application/controllers/Admin.php b/application/controllers/Admin.php
new file mode 100644
index 0000000..89d5f64
--- /dev/null
+++ b/application/controllers/Admin.php
@@ -0,0 +1,41 @@
+load->model('query','',TRUE);
+ //加载辅助函数
+ $this->load->helper('basic');
+ $info = $this->query->userinfo()->values;
+ $info = json_decode($info);
+
+ //验证用户是否登录
+ is_login($info->username,$info->password);
+ }
+ //后台首页
+ public function index(){
+ $this->load->library('basic');
+ $data = $this->basic->analyze();
+ $data['admin_title'] = '后台首页';
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/index');
+ $this->load->view('admin/footer');
+ }
+ //URL上传
+ public function urlup(){
+ $data['admin_title'] = 'URL上传';
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/urlup');
+ $this->load->view('admin/footer');
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Compress.php b/application/controllers/Compress.php
new file mode 100644
index 0000000..50579e9
--- /dev/null
+++ b/application/controllers/Compress.php
@@ -0,0 +1,107 @@
+load->model('query','',TRUE);
+ $img = $this->query->img($id);
+
+ //如果图片没有压缩过,则调用压缩接口
+ if($img->compression == 0){
+ //获取图片完整路径
+ $fullpath = FCPATH.$img->path;
+ $this->tinypng($fullpath);
+ //更新数据库
+ $this->load->model('update','',TRUE);
+ $this->update->compress($id);
+ $t2 = microtime(true);
+ //计算执行时间
+ $used_time = round($t2 - $t1).'s';
+ $info = array(
+ "code" => 200,
+ "used_time" => $used_time,
+ "msg" => 'compressing.'
+ );
+ $info = json_encode($info);
+ echo $info;
+ }
+ //图片已经压缩过情况
+ else{
+ $info = array(
+ "code" => 0,
+ "msg" => 'error:The image has been compressed!'
+ );
+ $info = json_encode($info);
+ echo $info;
+ }
+
+
+ }
+ //请求tinypng压缩接口,传入图片完整路径
+ protected function tinypng($path){
+
+ //tinypng API地址
+ $api_url = "https://api.tinify.com/shrink";
+ $data = file_get_contents($path);
+ //$post_data = array ("username" => "bob","key" => "12345");
+ //$ch = curl_init();
+ $ch = curl_init();
+ $user = "api";
+ $pass = "F8rNr5lh25WYcOECQvAqvcilBMAkhtIM";
+ curl_setopt($ch, CURLOPT_URL, $api_url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ //curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+ curl_setopt($ch, CURLOPT_USERPWD, "{$user}:{$pass}");
+ // post数据
+ curl_setopt($ch, CURLOPT_POST, 1);
+ // post的变量
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+ //https
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ $output = curl_exec($ch);
+ curl_close($ch);
+ //打印获得的数据
+ $data = json_decode($output);
+ //获取图片压缩后的URL
+ $url = $data->output->url;
+ //保存图片
+ $this->save($url,$path);
+ }
+ //传递图片URL,并保存文件
+ protected function save($url,$path){
+ //获取图片数据并保存
+ $curl = curl_init($url);
+
+ curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36");
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+ #设置超时时间,最小为1s(可选)
+ #curl_setopt($curl , CURLOPT_TIMEOUT, 1);
+
+ $filedata = curl_exec($curl);
+ curl_close($curl);
+
+ //将图片数据覆盖源文件
+ file_put_contents($path,$filedata);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Deal.php b/application/controllers/Deal.php
new file mode 100644
index 0000000..abcfc57
--- /dev/null
+++ b/application/controllers/Deal.php
@@ -0,0 +1,195 @@
+load->database();
+ }
+ //图片压缩服务,传入images的id
+ public function compress($id){
+ //只有管理员才有压缩权限
+ $this->load->library('basic');
+ $this->basic->is_login(TRUE);
+ $id = (int)$id;
+ //生成SQL语句
+ $sql = "SELECT a.`id`,a.`path`,a.`compression`,b.`mime` FROM img_images AS a INNER JOIN img_imginfo AS b ON a.imgid = b.imgid AND a.id = $id";
+ $query = $this->db->query($sql);
+ $row = $query->row_array();
+ //图片绝对路径
+ $path = FCPATH.$row['path'];
+
+ //如果图片已经压缩过,就不要再压缩了
+ if($row['compression'] == 1){
+ $this->err_msg('此图片已经压缩!');
+ }
+ //检查MIME类型,仅压缩jpeg & png
+ if(($row['mime'] != 'image/jpeg') && ($row['mime'] != 'image/png')){
+ $this->err_msg('此图片类型不支持压缩!');
+ }
+
+ //先取出tinypng信息
+ $sql = "SELECT * FROM img_options WHERE name = 'tinypng' LIMIT 1";
+ $row = $this->db->query($sql)->row_array();
+
+ //验证是否启用了压缩功能
+ if($row['switch'] == 'OFF'){
+ $this->err_msg('您没有启用压缩功能!');
+ }
+ //上面验证通过,继续执行
+ //取出tinypng key
+ $kyes = $row['values'];
+ $kyes = json_decode($kyes);
+ $i = 'api'.rand(1,2);
+ $key = $kyes->$i;
+
+ $url = "https://api.tinify.com/shrink";
+ $data = file_get_contents($path);
+
+ $ch = curl_init();
+ $user = "api";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ //curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+ curl_setopt($ch, CURLOPT_USERPWD, "{$user}:{$key}");
+ // post数据
+ curl_setopt($ch, CURLOPT_POST, 1);
+ // post的变量
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+ //https
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ $output = curl_exec($ch);
+ curl_close($ch);
+ $output = json_decode($output);
+ //获取压缩后的图片链接
+ $outurl = $output->output->url;
+
+ //先判断是否是正常的URL,万一请求接口失败了呢
+ if(!filter_var($outurl, FILTER_VALIDATE_URL)){
+ //糟糕,没有验证通过,那就结束了
+ $this->err_msg('请求接口失败!');
+ }
+
+ //下载图片并保存覆盖
+ $curl = curl_init($outurl);
+ curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36");
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+ #设置超时时间,最小为1s(可选)
+
+ $data = curl_exec($curl);
+ curl_close($curl);
+
+ //重新保存图片
+ file_put_contents($path,$data);
+
+ //最后别忘了更新数据库呀
+ $sql = "UPDATE img_images SET compression = 1 WHERE `id` = $id";
+ if($this->db->query($sql)){
+ $this->suc_msg('压缩完成!');
+ }
+ }
+ //图片鉴黄识别
+ public function identify($id){
+ $id = (int)$id;
+ //生成SQL语句
+ $sql = "SELECT a.id,a.path,a.level,b.domains FROM img_images AS a INNER JOIN img_storage AS b ON b.engine = 'localhost' AND a.id = $id";
+ $query = $this->db->query($sql);
+ $row = $query->row_array();
+ $imgurl = $row['domains'].$row['path'];
+ //echo $row['level'];
+ //如果图片已经识别过,就不要再次识别了
+ if(($row['level'] != 'unknown') && ($row['level'] != NULL)){
+ $this->err_msg('此图片已经识别过!');
+ }
+ //继续执行
+ $sql = "SELECT * FROM img_options WHERE name = 'moderate' LIMIT 1";
+ $row = $this->db->query($sql)->row_array();
+ //如果没有启用压缩功能
+ if($row['switch'] == 'OFF'){
+ $this->err_msg('未启用图片鉴黄识别!');
+ }
+
+ $apiurl = "https://www.moderatecontent.com/api/v2?key=".$row['values']."&url=".$imgurl;
+ $curl = curl_init($apiurl);
+ curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36");
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+
+ $html = curl_exec($curl);
+ curl_close($curl);
+
+ $html = json_decode($html);
+ @$level = $html->rating_label;
+ @$error_code = (int)$html->error_code;
+
+ //最后更新数据库
+ $sql = "UPDATE img_images SET level = '$level' WHERE `id` = $id";
+
+ //如果执行成功才进行更新
+ if($error_code === 0){
+ //更新数据库
+ $this->db->query($sql);
+ if($level == 'adult'){
+ $arr = array(
+ "code" => 400,
+ "msg" => '您的IP已记录,请不要上传违规图片!'
+ );
+ $arr = json_encode($arr);
+ echo $arr;
+ }
+ else{
+ $this->suc_msg('识别完成!');
+ }
+ }
+ else{
+ $this->err_msg('识别失败!');
+ }
+ }
+ //批量识别图片
+ public function identify_more(){
+ $sql = "SELECT * FROM img_images WHERE (`level` = 'unknown') OR (`level` = NULL) ORDER BY `id` DESC LIMIT 5";
+ //查询数据库
+ $query = $this->db->query($sql);
+ $result = $query->result();
+
+ //循环识别图片
+ foreach($result as $row){
+ $this->identify($row->id);
+ }
+ }
+
+ //返回错误码
+ protected function err_msg($msg){
+ $arr = array(
+ "code" => 0,
+ "msg" => $msg
+ );
+ $arr = json_encode($arr);
+ echo $arr;
+ exit;
+ }
+ //成功,返回正确的状态码
+ protected function suc_msg($msg){
+ $arr = array(
+ "code" => 200,
+ "msg" => $msg
+ );
+ $arr = json_encode($arr);
+ echo $arr;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Del.php b/application/controllers/Del.php
new file mode 100644
index 0000000..7536a67
--- /dev/null
+++ b/application/controllers/Del.php
@@ -0,0 +1,42 @@
+load->model('query','',TRUE);
+ //加载类
+ $this->load->library('basic');
+ //检测是否登录
+ $this->basic->is_login(TRUE);
+
+ }
+ //根据img_images ID删除图片
+ public function id($id){
+ @$id = (int)$id;
+
+ $img = $this->query->img_id($id);
+ //加载数据库模型
+ $this->load->model('delete','',TRUE);
+ //从数据库中删除
+ $this->delete->del_img($img->imgid);
+ //从磁盘中删除
+ $path = FCPATH.$img->path;
+ $thumbnail_path = FCPATH.$img->thumb_path;
+ //缩略图地址
+ unlink($path);
+ unlink($thumbnail_path);
+
+ $re = array(
+ "code" => 200,
+ "id" => $id,
+ "msg" => "删除成功!"
+ );
+ $re = json_encode($re);
+ echo $re;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Found.php b/application/controllers/Found.php
new file mode 100644
index 0000000..978c91b
--- /dev/null
+++ b/application/controllers/Found.php
@@ -0,0 +1,52 @@
+load->model('query','',TRUE);
+ //加载辅助函数
+ }
+ //探索发现页面
+ public function index(){
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = $siteinfo->values;
+ $siteinfo = json_decode($siteinfo);
+
+ $siteinfo->title = '探索发现 - '.$siteinfo->title;
+
+ //查询图片信息,返回对象
+ $data['imgs'] = $this->query->found(32);
+ //查询域名
+ $data['domain'] = $this->query->domain('localhost');
+
+ //加载视图
+ $this->load->view('user/header',$siteinfo);
+ $this->load->view('user/found',$data);
+
+ $this->load->view('user/footer');
+ }
+ //链接页面
+ public function link($id){
+ $id = strip_tags($id);
+ $id = (int)$id;
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = $siteinfo->values;
+ $siteinfo = json_decode($siteinfo);
+
+ //查询图片信息,返回对象
+ $data['imgs'] = $this->query->found(32);
+ //查询域名
+ $data['domain'] = $this->query->domain('localhost');
+
+ //加载视图
+ $this->load->view('user/header',$siteinfo);
+ $this->load->view('user/link',$data);
+
+ $this->load->view('user/footer');
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Home.php b/application/controllers/Home.php
new file mode 100644
index 0000000..ba5f652
--- /dev/null
+++ b/application/controllers/Home.php
@@ -0,0 +1,44 @@
+load->model('query','',TRUE);
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = json_decode($siteinfo->values);
+ //echo $siteinfo->title;
+ //$data['title'] = '图片上传';
+ $this->load->view('user/header.php',$siteinfo);
+ $this->load->view('user/home.php');
+ $this->load->view('user/footer.php');
+ }
+ //首页多图上传
+ public function multiple(){
+ //加载数据库模型
+ $this->load->model('query','',TRUE);
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = json_decode($siteinfo->values);
+ //echo $siteinfo->title;
+ //$data['title'] = '图片上传';
+ $this->load->view('user/header.php',$siteinfo);
+ $this->load->view('user/multiple.php');
+ $this->load->view('user/footer.php');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Img.php b/application/controllers/Img.php
new file mode 100644
index 0000000..a6890b1
--- /dev/null
+++ b/application/controllers/Img.php
@@ -0,0 +1,75 @@
+load->model('query','',TRUE);
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = json_decode($siteinfo->values);
+
+ //过滤imgid
+ $imgid = strip_tags($imgid);
+ //计算imgid长度
+ $id_length = strlen($imgid);
+ //判断是否是有效的ID
+ if($id_length != 16){
+ show_404();
+ }
+ //继续执行
+ //加载模型
+ $this->load->model('query','',TRUE);
+ $this->load->model('update','',TRUE);
+ //浏览测试+1
+ $this->update->views($imgid);
+ //查询图片信息
+ $imginfo = $this->query->onepic($imgid);
+ //查询的img_imginfo
+ $picinfo = $this->query->imginfo($imgid);
+ //查询图片域名
+ @$domain = $this->query->domain($imginfo->storage);
+
+ //如果没有查询到结果
+ if(!$domain){
+ show_404();
+ }
+
+ //var_dump($siteinfo);
+ //获取文件大小
+ $this->load->helper('basic');
+ $fullpath = FCPATH.$imginfo->path;
+
+ $size = file_size($fullpath);
+
+ //重组数组
+ $datas = array(
+ "logo" => $siteinfo->logo,
+ "title" => $picinfo->client_name,
+ "url" => $domain.$imginfo->path,
+ "date" => $imginfo->date,
+ "mime" => $picinfo->mime,
+ "width" => $picinfo->width,
+ "height" => $picinfo->height,
+ "views" => $picinfo->views,
+ "tags" => $picinfo->tags,
+ "keywords" => $picinfo->client_name,
+ "analytics" => $siteinfo->analytics,
+ "description" => $siteinfo->description,
+ "comments" => $siteinfo->comments,
+ "ext" => $picinfo->ext,
+ "size" => $size
+ );
+
+ // $data['title'] = '图片浏览';
+ // $data['url'] = $domain.$imginfo->path;
+ // $data['date'] = $imginfo->date;
+
+ //echo $domain.$imginfo->path;
+
+ //加载视图
+ $this->load->view('user/header',$datas);
+ $this->load->view('user/img',$datas);
+ $this->load->view('user/footer');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Install.php b/application/controllers/Install.php
new file mode 100644
index 0000000..110fd0d
--- /dev/null
+++ b/application/controllers/Install.php
@@ -0,0 +1,273 @@
+is_install();
+ $setup = (int)$_GET['setup'];
+ $data['env'] = $this->check('full');
+ $data['sum'] = $this->check('part');
+ //var_dump($data['sum']);
+ $data['title'] = "ImgURL安装向导";
+ $data['logo'] = "/static/images/logo.png";
+
+ //安装步骤
+ switch ($setup) {
+ //安装步骤1
+ case 1:
+ $this->load->view('user/header.php',$data);
+ $this->load->view('user/install1.php',$data);
+ $this->load->view('user/footer.php');
+ break;
+ //安装步骤2
+ case 2:
+ //获取网站域名
+ $data['domain'] = $this->get_domain();
+ //加载视图
+ $this->load->view('user/header.php',$data);
+ $this->load->view('user/install2.php',$data);
+ $this->load->view('user/footer.php');
+ break;
+ case 3:
+ //获取域名
+ @$info['domain'] = $this->input->post('domain',TRUE);
+ //获取用户名
+ @$info['user'] = $this->input->post('user',TRUE);
+ //获取密码
+ @$info['pass1'] = $this->input->post('pass1',TRUE);
+ @$info['pass2'] = $this->input->post('pass2',TRUE);
+ //验证信息
+ $this->verify($info,'domain');
+ $this->verify($info,'user');
+ $this->verify($info,'pass');
+ $this->verify($info,'pass2');
+ //开始安装ImgURL
+ $this->setup($info);
+ //加载视图
+ $this->load->view('user/header.php',$data);
+ $this->load->view('user/install3.php',$data);
+ $this->load->view('user/footer.php');
+ break;
+ default:
+ header("location:/install/?setup=1");
+ break;
+ }
+ }
+
+ //环境检测
+ protected function check($type){
+ //检测通过
+ $yes = '通过!';
+ $no = '未通过!';
+ //获取组件信息
+ $ext = get_loaded_extensions();
+ //PHP版本信息
+ $env['php'] = array(
+ "name" => 'PHP',
+ "requir" => 'PHP >= 5.6',
+ "info" => PHP_VERSION,
+ "result" => is_php('5.6') ? $yes : $no
+ );
+ //PDO_SQLite
+ $env['sqlite'] = array(
+ "name" => 'PDO_SQLite',
+ "requir" => '必须支持',
+ "info" => array_search('pdo_sqlite',$ext) ? 'Yes':'No',
+ "result" => array_search('pdo_sqlite',$ext) ? $yes : $no
+ );
+ //GD2
+ $env['gd'] = array(
+ "name" => 'GD2',
+ "requir" => '必须支持',
+ "info" => array_search('gd',$ext) ? 'Yes':'No',
+ "result" => array_search('gd',$ext) ? $yes : $no
+ );
+ //imagick
+ $env['imagick'] = array(
+ "name" => 'ImageMagick',
+ "requir" => '必须支持',
+ "info" => array_search('imagick',$ext) ? 'Yes':'No',
+ "result" => array_search('imagick',$ext) ? $yes : $no
+ );
+ //fileinfo
+ $env['fileinfo'] = array(
+ "name" => 'Fileinfo',
+ "requir" => '必须支持',
+ "info" => array_search('fileinfo',$ext) ? 'Yes':'No',
+ "result" => array_search('fileinfo',$ext) ? $yes : $no
+ );
+ //检查目录是否可写
+ $env['data'] = array(
+ "name" => '/data',
+ "requir" => '可写',
+ "info" => is_writable(FCPATH.'data') ? 'Yes':'No',
+ "result" => is_writable(FCPATH.'data') ? $yes : $no
+ );
+ $env['upload'] = array(
+ "name" => '/imgs',
+ "requir" => '可写',
+ "info" => is_writable(FCPATH.'imgs') ? 'Yes':'No',
+ "result" => is_writable(FCPATH.'imgs') ? $yes : $no
+ );
+
+ //遍历结果
+ if($type == 'part'){
+ //检测不通过
+ foreach($env as $value){
+ //echo $value['result'];
+ if($value['result'] == $no){
+ return FALSE;
+ exit;
+ }
+ }
+ }
+ else{
+ return $env;
+ }
+ }
+ //获取网站域名
+ protected function get_domain(){
+ $port = $_SERVER["SERVER_PORT"];
+ //对端口进行判断
+ switch ( $port )
+ {
+ case 80:
+ $protocol = "http://";
+ $port = '';
+ break;
+ case 443:
+ $protocol = "https://";
+ $port = '';
+ break;
+ default:
+ $protocol = "http://";
+ $port = ":".$port;
+ break;
+ }
+ $uri = $_SERVER["REQUEST_URI"];
+ $uri = str_replace("check.php","",$uri);
+ //组合为完整的URL
+ $domain = $protocol.$_SERVER['SERVER_NAME'].$port;
+ //$domain = str_replace("install.php?setup=2","",$domain);
+ return $domain;
+ }
+ //验证函数
+ protected function verify($data,$type){
+ switch ($type) {
+ //检查用户名
+ case 'user':
+ $pattern = '/^[a-zA-Z0-9]+$/';
+ if($data['user'] == ''){
+ echo '请填写用户名!';
+ exit;
+ }
+ if(!preg_match($pattern,$data['user'])){
+ echo '用户名格式有误!';
+ exit;
+ }
+ break;
+ case 'pass':
+ $pattern = '/^[a-zA-Z0-9!@#$%^&*.]+$/';
+ if(!preg_match($pattern,$data['pass1'])){
+ echo '密码格式有误!';
+ exit;
+ }
+ break;
+ case 'pass2':
+ $pass1 = $data['pass1'];
+ $pass2 = $data['pass2'];
+
+ if($pass1 != $pass2){
+ echo '两次密码不一致!';
+ exit;
+ }
+ break;
+ case 'domain':
+ $domain = $data['domain'];
+ if(!filter_var($domain, FILTER_VALIDATE_URL)){
+ echo '域名格式有误!(需要包含https://)';
+ exit;
+ }
+ break;
+ default:
+ # code...
+ break;
+ }
+ }
+ //安装函数
+ protected function setup($data){
+ //默认数据库路径
+ $default_db = FCPATH."data/imgurl-simple.db3";
+ //数据库路径
+ $db_path = FCPATH."data/imgurl.db3";
+ //锁文件
+ $lock_file = FCPATH."data/install.lock";
+ //用户密码
+ $password = md5($data['pass2'].'imgurl');
+ //用户信息,json格式
+ $user_values = array(
+ "username" => $data['user'],
+ "password" => $password
+ );
+ $user_values = json_encode($user_values);
+
+ //拷贝数据库
+ copy($default_db,$db_path);
+ //写入默认数据
+ //连接数据库
+ $this->load->database();
+ //用户信息
+ $userinfo = array(
+ 'name' => 'userinfo',
+ 'values' => $user_values
+ );
+
+ //本地存储信息
+ $local_storage = array(
+ "engine" => "localhost",
+ "domains" => $data['domain'],
+ "switch" => 'ON'
+ );
+ //站点地址
+ $site_url = array(
+ "name" => 'site_url',
+ "values" => $data['domain']
+ );
+ //$where = "name = 'site_url'";
+
+ // var_dump($this->db->update_string('options', $site_url, $where));
+ // exit;
+ // var_dump($site_url);
+ // exit;
+ //设置用户信息
+ $this->db->insert('options', $userinfo);
+ $this->db->insert('options', $site_url);
+
+ //设置默认存储
+ $this->db->insert('storage', $local_storage);
+
+ //创建锁文件
+ $myfile = fopen($lock_file, "w") or die("Unable to open file!");
+ $txt = "ImgURL";
+ fwrite($myfile, $txt);
+ fclose($myfile);
+
+ return TRUE;
+ }
+ //检查是否已经安装过
+ protected function is_install(){
+ //锁文件
+ $lock_file = FCPATH."data/install.lock";
+ if(is_file($lock_file)){
+ header("location:/");
+ exit;
+ }
+ }
+ public function test(){
+ echo $this->get_domain();
+
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Maintain.php b/application/controllers/Maintain.php
new file mode 100644
index 0000000..62293af
--- /dev/null
+++ b/application/controllers/Maintain.php
@@ -0,0 +1,63 @@
+load->library('basic');
+ $this->basic->is_login(TRUE);
+ //加载模型
+ $this->load->model('query','',TRUE);
+ }
+ //后台首页
+ public function index(){
+
+ $data['admin_title'] = '管理维护';
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/index');
+ $this->load->view('admin/footer');
+ }
+ //当前版本
+ public function version(){
+ $v_file = FCPATH.'data/version.txt';
+ $version = file_get_contents($v_file);
+ echo $version;
+ }
+ //升级至2.0
+ public function upto2(){
+ $data['admin_title'] = '1.x升级至2.x';
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/upto2');
+ $this->load->view('admin/footer');
+ }
+ //导入1.x的数据
+ public function import($id){
+ $id = (int)$id;
+
+ //加载medoo
+ include_once(APPPATH.'libraries/Medoo.php');
+
+ echo APPPATH.'libraries/Medoo.php';
+
+ $database = new Medoo([
+ 'database_type' => 'sqlite',
+ 'database_file' => FCPATH.'/data/temp/imgurl.db3'
+ ]);
+
+ //$this->load->database($db['old']);
+
+ // $sql = "SELECT * FROM imginfo WHERE id = $id";
+ // $query = $this->db->query($sql)->row();
+ // var_dump($query);
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Manage.php b/application/controllers/Manage.php
new file mode 100644
index 0000000..152ec3f
--- /dev/null
+++ b/application/controllers/Manage.php
@@ -0,0 +1,92 @@
+load->library('basic');
+ //验证用户是否登录
+ $this->basic->is_login(TRUE);
+ //加载查询模型
+ $this->load->model('query','',TRUE);
+ }
+ //管理员上传
+ public function images($type = 'all',$page = 0){
+ $type = strip_tags($type);
+ $page = (int)strip_tags($page);
+ $limit = 16; //要查询的条数
+ $data['admin_title'] = '图片管理';
+ $sql1 = "SELECT a.id,a.imgid,a.path,a.thumb_path,a.date,a.compression,a.level,b.mime,b.width,b.height,b.views,b.ext,b.client_name FROM img_images AS a INNER JOIN img_imginfo AS b ON a.imgid = b.imgid ";
+ //根据不同的条件生成不同的SQL语句
+ switch ($type) {
+ case 'all':
+ $sql = $sql1."ORDER BY a.id DESC LIMIT $limit OFFSET $page";
+ $num = $this->db->count_all("images");
+ break;
+ case 'admin':
+ $sql = $sql1."AND a.user = 'admin' ORDER BY a.id DESC LIMIT $limit OFFSET $page";
+ $num = $this->query->count_num('admin')->num;
+ break;
+ case 'visitor':
+ $sql = $sql1."AND a.user = 'visitor' ORDER BY a.id DESC LIMIT $limit OFFSET $page";
+ $num = $this->query->count_num('visitor')->num;
+ break;
+ case 'dubious':
+ $sql = $sql1."AND a.level = 'adult' ORDER BY a.id DESC";
+ //$num = $this->query->count_num('visitor')->num;
+ break;
+ default:
+ $sql = $sql1."AND a.user = '$type' ORDER BY a.id DESC LIMIT $limit OFFSET $page";
+ break;
+ }
+ //连接数据库
+ $this->load->database();
+ $data['imgs'] = $this->db->query($sql)->result_array();
+
+ //调用分页类
+ $this->load->library('pagination');
+ $config['base_url'] = "/manage/images/$type/";
+ $config['total_rows'] = $num;
+ $config['per_page'] = $limit;
+ $config['first_url'] = 0;
+ $config['first_link'] = '首页';
+ $config['last_link'] = '尾页';
+ $config['attributes'] = array('class' => 'paging'); //设置分页的class
+ $config['next_link'] = '下一页'; //下一页文本
+ $config['prev_link'] = '上一页'; //上一页文本
+
+ $this->pagination->initialize($config);
+ $data['page'] = $this->pagination->create_links();
+
+ //获取域名
+ $data['domain'] = $this->query->domain('localhost');
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/images',$data);
+ $this->load->view('admin/footer');
+ }
+ //获取单张图片信息
+ public function imginfo($imgid){
+ $imgid = strip_tags($imgid);
+ $row = $this->query->picinfo($imgid);
+ //获取文件大小
+ $this->load->helper('basic');
+ $fullpath = FCPATH.$row->path;
+
+ $size = file_size($fullpath);
+ $row->size = $size;
+
+ //加载视图
+ $this->load->view("admin/imginfo",$row);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Page.php b/application/controllers/Page.php
new file mode 100644
index 0000000..b698458
--- /dev/null
+++ b/application/controllers/Page.php
@@ -0,0 +1,37 @@
+load->library("parsedown");
+ $content = $this->parsedown->text($content);
+
+ //$data['content'] = $content;
+ //加载数据库视图
+ $this->load->model('query','',TRUE);
+ $data = $this->query->site_setting('1');
+ $data->content = $content;
+ //载入页面视图
+ $this->load->view('/user/header',$data);
+ $this->load->view('/user/page',$data);
+ $this->load->view('/user/footer');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Set.php b/application/controllers/Set.php
new file mode 100644
index 0000000..4c69590
--- /dev/null
+++ b/application/controllers/Set.php
@@ -0,0 +1,172 @@
+load->model('update','',TRUE);
+ //加载类
+ $this->load->library('basic');
+ //验证用户是否登录
+ $this->basic->is_login(TRUE);
+ }
+ //更新站点设置
+ public function site(){
+ //var_dump($data);
+ //接收表单数据
+ $data['logo'] = $this->input->post('logo',TRUE);
+ $data['title'] = $this->input->post('title',TRUE);
+ $data['keywords'] = $this->input->post('keywords',TRUE);
+ $data['description'] = $this->input->post('description',TRUE);
+ $data['analytics'] = $this->input->post('analytics');
+ //$data['comments'] = $this->input->post('comments');
+
+
+ $data = json_encode($data);
+
+
+ //如果更新成功
+ if($this->update->site('site_setting',$data)){
+ $ref = $_SERVER["HTTP_REFERER"];
+ echo '更新成功,3s后返回上一页!';
+ header("Refresh:3;url=$ref");
+ }
+ else{
+ echo '更新发生错误!';
+ exit;
+ }
+ }
+ //更新上传限制
+ public function uplimit(){
+ $data['max_size'] = (int)$this->input->post('max_size',TRUE);
+ $data['limit'] = (int)$this->input->post('limit',TRUE);
+
+ $data = json_encode($data);
+
+ //如果更新成功
+ if($this->update->site('uplimit',$data)){
+ $ref = $_SERVER["HTTP_REFERER"];
+ echo '更新成功,3s后返回上一页!';
+ header("Refresh:3;url=$ref");
+ }
+ else{
+ echo '更新发生错误!';
+ exit;
+ }
+ }
+ //更新tinypng设置
+ public function tinypng(){
+ $data['api1'] = $this->input->post('api1',TRUE);
+ $data['api2'] = $this->input->post('api2',TRUE);
+ @$switch = $this->input->post('switch',TRUE);
+ if($switch != 'on'){
+ $switch = 'OFF';
+ }
+ else{
+ $switch = 'ON';
+ }
+
+ $data = json_encode($data);
+ //如果更新成功
+ if($this->update->tinypng($data,$switch)){
+ $ref = $_SERVER["HTTP_REFERER"];
+ echo '更新成功,3s后返回上一页!';
+ header("Refresh:3;url=$ref");
+ }
+ else{
+ echo '更新发生错误!';
+ exit;
+ }
+ }
+ //更新moderate
+ public function moderate(){
+ //获取API key
+ $data['api'] = $this->input->post('api',TRUE);
+ //获取开关
+ @$switch = $this->input->post('switch',TRUE);
+ if($switch != 'on'){
+ $switch = 'OFF';
+ }
+ else{
+ $switch = 'ON';
+ }
+ //更新数据库
+ //如果更新成功
+ if($this->update->moderate($data['api'],$switch)){
+ $ref = $_SERVER["HTTP_REFERER"];
+ echo '更新成功,3s后返回上一页!';
+ header("Refresh:3;url=$ref");
+ }
+ else{
+ echo '更新发生错误!';
+ exit;
+ }
+ }
+ //更新存储引擎
+ public function storage($engine){
+ //获取API key
+ $data['domains'] = $this->input->post('domain',TRUE);
+ $data['switch'] = 'ON';
+
+ //更新数据库
+ //如果更新成功
+ if($this->update->storage($data,$engine)){
+ $ref = $_SERVER["HTTP_REFERER"];
+ echo '更新成功,3s后返回上一页!';
+ header("Refresh:3;url=$ref");
+ }
+ else{
+ echo '更新发生错误!';
+ exit;
+ }
+ }
+ //删除单张图片,需传入图片ID,及文件路径
+ public function del_img(){
+ //获取数据
+ @$imgid = $this->input->post('imgid',TRUE);
+ @$path = $this->input->post('path',TRUE);
+ @$thumbnail_path = $this->input->post('thumbnail_path',TRUE);
+ //加载数据库模型
+ $this->load->model('delete','',TRUE);
+ //从数据库中删除
+ $this->delete->del_img($imgid);
+ //从磁盘中删除
+ $path = FCPATH.$path;
+ $thumbnail_path = FCPATH.$thumbnail_path;
+ //缩略图地址
+ unlink($path);
+ unlink($thumbnail_path);
+
+ $re = array(
+ "code" => 200,
+ "msg" => "删除成功!"
+ );
+ $re = json_encode($re);
+ echo $re;
+ }
+ //取消图片可疑状态
+ public function cancel($id){
+ $id = (int)$id;
+
+ $sql = "UPDATE img_images SET level = 'everyone' WHERE `id` = $id";
+ $this->load->database();
+ if($this->db->query($sql)){
+ $this->suc_msg('操作成功!');
+ }
+ }
+ //操作成功返回json
+ protected function suc_msg($msg){
+ $arr = array(
+ "code" => 200,
+ "msg" => $msg
+ );
+ $info = json_encode($arr);
+ echo $info;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Setting.php b/application/controllers/Setting.php
new file mode 100644
index 0000000..9c072c3
--- /dev/null
+++ b/application/controllers/Setting.php
@@ -0,0 +1,106 @@
+load->model('query','',TRUE);
+ //加载辅助函数
+ $this->load->helper('basic');
+ $info = $this->query->userinfo()->values;
+ $info = json_decode($info);
+
+ //验证用户是否登录
+ is_login($info->username,$info->password);
+ }
+ //站点设置
+ public function site(){
+ $siteinfo = $this->query->site_setting();
+ $siteinfo = json_decode($siteinfo->values);
+
+ //页面标题
+ $siteinfo->admin_title = '站点设置';
+
+ //加载视图
+ $this->load->view('admin/header',$siteinfo);
+ $this->load->view('admin/left');
+ $this->load->view('admin/site');
+ $this->load->view('admin/footer');
+ }
+ //上传限制
+ public function uplimit(){
+
+ $siteinfo = $this->query->option('uplimit');
+
+ $siteinfo = json_decode($siteinfo->values);
+ if($siteinfo->limit != 0){
+ $switch = 'checked';
+ }
+ else{
+ $switch = '';
+ }
+ //页面标题
+ $siteinfo->admin_title = '上传限制';
+ $siteinfo->switch = $switch;
+ //var_dump($siteinfo);
+ //加载视图
+ $this->load->view('admin/header',$siteinfo);
+ $this->load->view('admin/left');
+ $this->load->view('admin/uplimit');
+ $this->load->view('admin/footer');
+ }
+ //图片压缩
+ public function compress(){
+ //页面标题
+ $data['admin_title'] = '图片压缩';
+
+ //加载模型
+ $this->load->model('query','',TRUE);
+
+ $tinypng = $this->query->option('tinypng');
+ $data['switch'] = $tinypng->switch;
+ if($data['switch'] == 'OFF'){
+ $data['switch'] = '';
+ }
+ else{
+ $data['switch'] = 'checked';
+ }
+
+ $data['values'] = json_decode($tinypng->values);
+
+ //var_dump($data['values']->api1);
+ //exit;
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/tinypng',$data);
+ $this->load->view('admin/footer');
+ }
+ //图片鉴黄
+ public function identify(){
+ //页面标题
+ $data['admin_title'] = '图片鉴黄';
+ //加载模型
+ $this->load->model('query','',TRUE);
+ $moderate = $this->query->option('moderate');
+
+ $data['switch'] = $moderate->switch;
+ $data['values'] = $moderate->values;
+ if($data['switch'] == 'OFF'){
+ $data['switch'] = '';
+ }
+ else{
+ $data['switch'] = 'checked';
+ }
+
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/identify');
+ $this->load->view('admin/footer');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Storage.php b/application/controllers/Storage.php
new file mode 100644
index 0000000..6f94c07
--- /dev/null
+++ b/application/controllers/Storage.php
@@ -0,0 +1,32 @@
+load->model('query','',TRUE);
+ //加载辅助函数
+ $this->load->helper('basic');
+ $info = $this->query->userinfo()->values;
+ $info = json_decode($info);
+
+ //验证用户是否登录
+ is_login($info->username,$info->password);
+ }
+ //后台首页
+ public function localhost(){
+ $localhost = $this->query->storage('localhost');
+
+ $data['admin_title'] = '存储设置(localhost)';
+ $data['domains'] = $localhost->domains;
+ //加载视图
+ $this->load->view('admin/header',$data);
+ $this->load->view('admin/left');
+ $this->load->view('admin/localhost',$data);
+ $this->load->view('admin/footer');
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Upload.php b/application/controllers/Upload.php
new file mode 100644
index 0000000..8d161ee
--- /dev/null
+++ b/application/controllers/Upload.php
@@ -0,0 +1,402 @@
+upload_path = FCPATH.'imgs/'.date('Y',time()).'/'.date('m',time()).'/';
+ $this->upload_path = str_replace('\\','/',$this->upload_path);
+ $this->relative_path = "/imgs/".date('Y',time()).'/'.date('m',time()).'/';
+ $this->relative_path = str_replace('\\','/',$this->relative_path);
+ $this->temp = FCPATH.'data/temp/';
+ //如果文件夹不存在,则创建文件夹
+ if(!is_dir($this->upload_path)){
+ //递归模式创建目录
+ mkdir($this->upload_path,0777,TRUE);
+ }
+ $this->date = date('Y-m-d H:i',time());
+ //加载辅助函数
+ $this->load->helper('basic');
+ $ip = get_ip();
+ //加载基本类
+ $this->load->library('basic');
+ //加载查询模型
+ $this->load->model('query','',TRUE);
+ //用户已经登录
+ if($this->basic->is_login(FALSE)){
+ $this->user = 'admin';
+ }
+ else{
+ $this->user = 'visitor';
+ //限制上传数量
+ if($this->query->uplimit($ip) === FALSE){
+ $this->error_msg("上传达到上限!");
+ }
+ }
+ }
+ //通用上传设置
+ protected function config($upload_path = ''){
+ //设置上传路径
+ if($upload_path == ''){
+ $upload_path = $this->upload_path;
+ }
+ // var_dump();
+ $config['upload_path'] = $upload_path;
+ $config['allowed_types'] = 'gif|jpg|png|bmp|webp';
+ $config['max_size'] = 5120;
+ $config['file_ext_tolower'] = TRUE; //文件名转换为小写
+ $config['overwrite'] = TRUE; //覆盖同名文件
+ $config['encrypt_name'] = TRUE; //随机命名图片
+ return $config;
+ }
+ public function localhost(){
+ //加载上传的配置选项
+ $config = $this->config();
+ //加载上传类
+ $this->load->library('upload', $config);
+
+ //上传失败
+ if ( ! $this->upload->do_upload('file'))
+ {
+ $msg = $this->upload->display_errors();
+ $msg = strip_tags($msg);
+
+ $this->error_msg($msg);
+ }
+ else
+ {
+ $data = $this->upload->data();
+ //加载模型
+ $this->load->model('insert','',TRUE);
+ $this->load->model('query','',TRUE);
+ //计算文件MD5
+ $file_name = md5_file($data['full_path']);
+ $file_name = substr($file_name,8,16);
+ //图片唯一ID
+ $imgid = $file_name;
+ $file_name = $file_name.$data['file_ext'];
+ //新图片完整路径
+ $full_path = $this->upload_path.$file_name;
+ $full_path = str_replace("\\","/",$full_path);
+ //新图片相对路径
+ $relative_path = $this->relative_path.$file_name;
+ //缩略图相对路径
+ $thumbnail_path = $this->relative_path.$imgid.'_thumb'.$data['file_ext'];
+ //获取域名
+ $domain = $this->query->domain('localhost');
+
+ //获取图片URL地址
+ $url = $domain.$relative_path;
+ //缩略图地址
+ $thumbnail_url = $domain.$thumbnail_path;
+
+ //重命名文件
+ rename($data['full_path'],$full_path);
+
+ //生成缩略图
+ $this->load->library('image');
+ $this->image->thumbnail($full_path,290,175);
+
+ //查询图片是否上传过
+ if($imginfo = $this->query->repeat($imgid)){
+ $id = $imginfo->id;
+ //重组数组
+ $info = array(
+ "code" => 200,
+ "id" => $id,
+ "imgid" => $imgid,
+ "relative_path" => $relative_path,
+ "url" => $url,
+ "thumbnail_url" => $thumbnail_url,
+ "width" => $data['image_width'],
+ "height" => $data['image_height']
+ );
+ $this->succeed_msg($info);
+ }
+ //图片没有上传过
+ else{
+ //需要插入到images表的数据
+ $datas = array(
+ "imgid" => $imgid,
+ "path" => $relative_path,
+ "thumb_path"=> $thumbnail_path,
+ "storage" => "localhost",
+ "ip" => get_ip(),
+ "ua" => get_ua(),
+ "date" => $this->date,
+ "user" => $this->user,
+ "level" => 'unknown'
+ );
+ //需要插入到imginfo表的数据
+ $imginfo = array(
+ "imgid" => $imgid,
+ "mime" => $data['file_type'],
+ "width" => $data['image_width'],
+ "height" => $data['image_height'],
+ "ext" => $data['file_ext'],
+ "client_name" => $data['client_name']
+ );
+
+ //插入数据到img_images表
+ $id = $this->insert->images($datas);
+ $this->insert->imginfo($imginfo);
+ //重组数组
+ $info = array(
+ "code" => 200,
+ "id" => $id,
+ "imgid" => $imgid,
+ "relative_path" => $relative_path,
+ "url" => $url,
+ "thumbnail_url" => $thumbnail_url,
+ "width" => $data['image_width'],
+ "height" => $data['image_height']
+ );
+ }
+ //var_dump($info);
+ //exit;
+ $this->succeed_msg($info);
+ }
+ }
+ //上传成功返回json
+ protected function succeed_msg($data){
+ $info = json_encode($data);
+ echo $info;
+ exit;
+ }
+ //上传失败返回json
+ protected function error_msg($msg){
+ $data = array(
+ "code" => 0,
+ "msg" => $msg
+ );
+
+ $data = json_encode($data);
+ echo $data;
+ exit;
+ }
+ //URL上传
+ public function url(){
+ $url = @$this->input->post('url',TRUE);
+ $url = trim($url);
+ //检测用户是否登录
+ $this->load->library('basic');
+ $this->basic->is_login(TRUE);
+ //判断URL是否合法
+ if(!filter_var($url, FILTER_VALIDATE_URL)){
+ $this->error_msg('不是有效的URL地址!');
+ }
+ //继续执行
+ //获取图片后缀名
+ $url_arr = explode('.',$url);
+ $ext = strtolower(end($url_arr));
+
+
+ //判断是否是允许的后缀
+ switch($ext){
+ case 'png':
+ case 'jpg':
+ case 'jpeg':
+ case 'bmp':
+ case 'gif':
+ case 'bmp':
+ break;
+ default:
+ $this->error_msg('不是有效的图片地址!');
+ exit;
+ }
+
+ //继续执行
+ //下载图片
+ $pic_data = $this->basic->dl_pic($url);
+ //临时文件路径
+ $tmp_name = $this->temp.md5($url);
+ //写入临时文件
+ file_put_contents($tmp_name,$pic_data);
+ //计算文件MD5
+ $imgid = md5_file($tmp_name);
+ $imgid = substr($imgid,8,16);
+ $file_name = $imgid.'.'.$ext;
+ //图片相对路径
+ $relative_path = $this->relative_path.$file_name;
+ $ext = '.'.$ext;
+ //查询图片是否已经上传过
+ if($this->query->repeat($imgid)){
+ //删除临时文件
+ unlink($tmp_name);
+ $this->error_msg('文件已经上传过!');
+ exit;
+ }
+ //没有上传过继续执行
+ //复制图片到上传目录
+ $full_path = $this->upload_path.$file_name;
+ copy($tmp_name,$full_path);
+ //删除临时文件
+ unlink($tmp_name);
+ //生成缩略图
+ $this->load->library('image');
+ $this->image->thumbnail($full_path,290,175);
+
+ //获取图片信息
+ $img_info = getimagesize($full_path);
+ //缩略图相对地址
+ $thumbnail_path = $this->relative_path.$imgid.'_thumb'.$ext;
+
+ //需要插入到images表的数据
+ $datas = array(
+ "imgid" => $imgid,
+ "path" => $relative_path,
+ "thumb_path"=> $thumbnail_path,
+ "storage" => "localhost",
+ "ip" => get_ip(),
+ "ua" => get_ua(),
+ "date" => $this->date,
+ "user" => $this->user,
+ "level" => 'unknown'
+ );
+ //需要插入到imginfo表的数据
+ $imginfo = array(
+ "imgid" => $imgid,
+ "mime" => $img_info['mime'],
+ "width" => $img_info[0],
+ "height" => $img_info[1],
+ "ext" => $ext,
+ "client_name" => $file_name
+ );
+ //加载数据库模型
+ $this->load->model('insert','',TRUE);
+ //插入数据到img_images表
+ $id = $this->insert->images($datas);
+ $this->insert->imginfo($imginfo);
+ //获取域名
+ $domain = $this->query->domain('localhost');
+ //获取图片URL地址
+ $url = $domain.$relative_path;
+ //返回成功的信息
+ $re = array(
+ "code" => 200,
+ "msg" => $url
+ );
+ $re = json_encode($re);
+ echo $re;
+ }
+ //粘贴上传
+ public function parse(){
+ $date = date('Y-m-d H:i:s',time());
+ //临时文件名
+ $tmp_name = get_ip().get_ua().$date;
+ $tmp_name = md5($tmp_name);
+ //图片临时路径
+ $tmp_file = $this->temp.$tmp_name;
+ //接接收ase64图片
+ $picfile = $_POST['content'];
+ $picfile = base64_decode($picfile);
+ //echo $picfile;
+ //存储图片
+ file_put_contents($tmp_file, $picfile);
+
+ //判断图片MIME类型
+ if(!mime($tmp_file)){
+ unlink($tmp_file);
+ $this->error_msg('不允许的文件类型!');
+ exit;
+ }
+ //继续执行
+ //计算文件MD5
+ $imgid = md5_file($tmp_file);
+ $imgid = substr($imgid,8,16);
+ //获取文件后缀
+ $ext = ext($tmp_file);
+ $file_name = $imgid.$ext;
+ //图片相对路径
+ $relative_path = $this->relative_path.$file_name;
+ //图片完整路径
+ $full_path = $this->upload_path.$file_name;
+ //查询图片是否已经上传过
+ if($this->query->repeat($imgid)){
+ //删除临时文件
+ unlink($tmp_file);
+ $this->error_msg('文件已经上传过!');
+ exit;
+ }
+ //没有上传过继续执行
+ //复制图片到上传目录
+ copy($tmp_file,$full_path);
+ $file_name = $imgid.$ext;
+ //删除临时文件
+ unlink($tmp_file);
+ //生成缩略图
+ $this->load->library('image');
+ $this->image->thumbnail($full_path,290,175);
+ //缩略图地址
+ $thumbnail_path = $this->relative_path.$imgid.'_thumb.'.$ext;
+
+ //获取图片信息
+ $img_info = getimagesize($full_path);
+
+ //需要插入到images表的数据
+ $datas = array(
+ "imgid" => $imgid,
+ "path" => $relative_path,
+ "thumb_path"=> $thumbnail_path,
+ "storage" => "localhost",
+ "ip" => get_ip(),
+ "ua" => get_ua(),
+ "date" => $this->date,
+ "user" => $this->user,
+ "level" => 'unknown'
+ );
+ //需要插入到imginfo表的数据
+ $imginfo = array(
+ "imgid" => $imgid,
+ "mime" => $img_info['mime'],
+ "width" => $img_info[0],
+ "height" => $img_info[1],
+ "ext" => $ext,
+ "client_name" => $file_name
+ );
+ //加载数据库模型
+ $this->load->model('insert','',TRUE);
+ //插入数据到img_images表
+ $id = $this->insert->images($datas);
+ $this->insert->imginfo($imginfo);
+ //获取域名
+ $domain = $this->query->domain('localhost');
+ //获取图片URL地址
+ $url = $domain.$relative_path;
+ $thumbnail_url = $domain.$this->relative_path.$imgid.'_thumb'.$ext;
+ //返回成功的信息
+ //重组数组
+ $info = array(
+ "code" => 200,
+ "id" => $id,
+ "imgid" => $imgid,
+ "relative_path" => $relative_path,
+ "url" => $url,
+ "thumbnail_url" => $thumbnail_url,
+ "width" => $img_info[0],
+ "height" => $img_info[1]
+ );
+ $this->succeed_msg($info);
+ //echo $re;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/User.php b/application/controllers/User.php
new file mode 100644
index 0000000..8fee6a0
--- /dev/null
+++ b/application/controllers/User.php
@@ -0,0 +1,91 @@
+load->helper('basic');
+ }
+ //用户登录
+ public function login(){
+ //加载基础类
+ $this->load->library('basic');
+ //判断用户是否登录
+ if($this->basic->is_login(FALSE)){
+ //如果已经登录,则跳转到后台
+ header("location:/admin/");
+ }
+ //加载数据库模型
+ $this->load->model('query','',TRUE);
+ //查询站点信息
+ $siteinfo = $this->query->site_setting('1');
+ $siteinfo->title = '管理员登录 - '.$siteinfo->title;
+
+ //加载登录视图
+ $this->load->view('user/header',$siteinfo);
+ $this->load->view('user/login');
+ $this->load->view('user/footer');
+ }
+ //验证用户名、密码是否正确
+ public function verify(){
+ //获取用户输入的信息
+ $user = $this->input->post('user',TRUE);
+ $pass = $this->input->post('password',TRUE);
+ $pass = md5($pass.'imgurl');
+
+ //加载模型
+ $this->load->model("query",'',TRUE);
+ $info = $this->query->userinfo()->values;
+
+ $info = json_decode($info);
+
+ //获取真正的用户名
+ $username = $info->username;
+
+ $password = $info->password;
+
+ if(($user == $username) && ($pass == $password)){
+ $token = token($username,$password);
+ //生成COOKIE
+ setcookie("user", $username, time()+ 7 * 24 * 60 * 60,"/");
+ setcookie("token", $token, time()+ 7 * 24 * 60 * 60,"/");
+ //跳转到后台
+ $data = array(
+ "code" => 200,
+ "msg" => '登录成功!'
+ );
+ $data = json_encode($data);
+ echo $data;
+ exit;
+ }
+ else{
+ $this->err_msg('用户名或密码不正确');
+ //清除cookie
+ $this->clean_cookies();
+ exit;
+ }
+ }
+ public function logout(){
+ echo '您已退出,将在5s后返回首页!';
+ $this->clean_cookies();
+ header("Refresh:5;url=/");
+ exit;
+ }
+ //清除COOKIE
+ protected function clean_cookies(){
+ setcookie("user", '', time()-3600,"/");
+ setcookie("token", '', time()-3600,"/");
+ }
+ //错误消息
+ protected function err_msg($msg){
+ $data = array(
+ "code" => 0,
+ "msg" => $msg
+ );
+ $data = json_encode($data);
+ echo $data;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/controllers/Welcome.php b/application/controllers/Welcome.php
new file mode 100644
index 0000000..9213c0c
--- /dev/null
+++ b/application/controllers/Welcome.php
@@ -0,0 +1,25 @@
+
+ * @see https://codeigniter.com/user_guide/general/urls.html
+ */
+ public function index()
+ {
+ $this->load->view('welcome_message');
+ }
+}
diff --git a/application/controllers/index.html b/application/controllers/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/controllers/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/core/index.html b/application/core/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/core/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/helpers/basic_helper.php b/application/helpers/basic_helper.php
new file mode 100644
index 0000000..2820b52
--- /dev/null
+++ b/application/helpers/basic_helper.php
@@ -0,0 +1,142 @@
+= 1024){
+ $size = $size / 1024;
+ $size = round($size,1);
+ $name = $size.' MB';
+ }
+
+ }
+ return $name;
+ }
+ //缩略图函数
+ function thumbnail($img){
+ //返回路径
+ $dir = dirname($img['path']);
+ $thumbnail_name = $dir.'/'.$img['imgid'].'_thumb'.$img['ext'];
+
+
+ //缩略图完整地址
+ $fullpath = FCPATH.$thumbnail_name;
+ //echo $fullpath;
+ //判断缩略图是否存在
+ if(is_file($fullpath)){
+ return $thumbnail_name;
+ }
+ //$thumbnail_name = $dir.$name.'_thumb';
+ //返回缩略图地址,不带文件名
+ //返回原图
+ else{
+ return $img['path'];
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/helpers/index.html b/application/helpers/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/helpers/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/hooks/index.html b/application/hooks/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/hooks/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/index.html b/application/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/language/english/index.html b/application/language/english/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/language/english/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/language/index.html b/application/language/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/language/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/libraries/Basic.php b/application/libraries/Basic.php
new file mode 100644
index 0000000..040861a
--- /dev/null
+++ b/application/libraries/Basic.php
@@ -0,0 +1,106 @@
+CI = & get_instance();
+ }
+
+ /*
+ 该函数检测用户是否已经登录,只需要一个参数
+ 如果参数为FALSE时,不会exit中断只执行,仅返回bool类型结果
+ 如果参数为TURE时,如果没有登录会exit终止执行
+ */
+ public function is_login($type = FALSE){
+ //获取COOKIE信息
+ @$user = $_COOKIE['user'];
+ @$token = $_COOKIE['token'];
+
+ //加载模型
+ $this->CI->load->model('query','',TRUE);
+ //加载辅助函数
+ $this->CI->load->helper('basic');
+
+ //如果查询成功
+ if($this->CI->query->userinfo()){
+ $userinfo = $this->CI->query->userinfo();
+ $userinfo = json_decode($userinfo->values);
+
+ $username = $userinfo->username;
+ $password = $userinfo->password;
+ //echo get_ip();
+ $password = $username.$password.get_ip().get_ua();
+ $password = md5($password);
+
+
+ //判断用户名是否正确,用户名密码正确的情况
+ if(($user == $username) && ($token == $password)){
+ //判断需要的类型
+ return TRUE;
+ }
+ //用户名和密码不正确的情况下
+ else{
+ if($type === FALSE){
+
+ return false;
+ }
+ else{
+ echo '权限不足!';
+ exit;
+ }
+ }
+ }
+ else{
+ echo '数据库查询错误!';
+ exit;
+ }
+ }
+ //查询上传数量限制,需要传入访客IP
+ public function uplimit($ip){
+
+ }
+ //CURL下载图片
+ public function dl_pic($url){
+ $curl = curl_init($url);
+
+ curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36");
+ //伪造reffer
+ curl_setopt ($ch, CURLOPT_REFERER, $url);
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+ #设置超时时间,最小为1s(可选)
+ curl_setopt($curl , CURLOPT_TIMEOUT, 60);
+
+ $html = curl_exec($curl);
+ curl_close($curl);
+ //返回数据
+ return $html;
+ }
+ //网站数据分析
+ public function analyze(){
+ //图片总数
+ $data['num'] = $this->CI->db->count_all("images");
+ //本月总数
+ $data['month'] = $this->CI->query->count_num('month')->num;
+ //今日总数
+ $data['day'] = $this->CI->query->count_num('day')->num;
+ //管理员上传总数
+ $data['admin'] = $this->CI->query->count_num('admin')->num;
+ //游客上传总数
+ $data['visitor'] = $this->CI->query->count_num('visitor')->num;
+ //可疑图片总数
+ $data['dubious'] = $this->CI->query->count_num('dubious')->num;
+
+ return $data;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/libraries/Image.php b/application/libraries/Image.php
new file mode 100644
index 0000000..23f283a
--- /dev/null
+++ b/application/libraries/Image.php
@@ -0,0 +1,46 @@
+ $width) || ($img_h > $height)){
+ //$image->setImageCompressionQuality(90);
+ $image->cropThumbnailImage( $width, $height );
+ }
+
+ //将缩略图输出到文件
+ $image->writeImage( $thumbnail_full );
+
+ //清理工作
+ $image->clear();
+ }
+ //压缩图片
+ public function compress($source){
+
+ }
+ }
+
+?>
\ No newline at end of file
diff --git a/application/libraries/Medoo.php b/application/libraries/Medoo.php
new file mode 100644
index 0000000..dcdcb04
--- /dev/null
+++ b/application/libraries/Medoo.php
@@ -0,0 +1,1692 @@
+type = strtolower($options[ 'database_type' ]);
+
+ if ($this->type === 'mariadb')
+ {
+ $this->type = 'mysql';
+ }
+ }
+
+ if (isset($options[ 'prefix' ]))
+ {
+ $this->prefix = $options[ 'prefix' ];
+ }
+
+ if (isset($options[ 'logging' ]) && is_bool($options[ 'logging' ]))
+ {
+ $this->logging = $options[ 'logging' ];
+ }
+
+ $option = isset($options[ 'option' ]) ? $options[ 'option' ] : [];
+ $commands = (isset($options[ 'command' ]) && is_array($options[ 'command' ])) ? $options[ 'command' ] : [];
+
+ switch ($this->type)
+ {
+ case 'mysql':
+ // Make MySQL using standard quoted identifier
+ $commands[] = 'SET SQL_MODE=ANSI_QUOTES';
+
+ break;
+
+ case 'mssql':
+ // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting
+ $commands[] = 'SET QUOTED_IDENTIFIER ON';
+
+ // Make ANSI_NULLS is ON for NULL value
+ $commands[] = 'SET ANSI_NULLS ON';
+
+ break;
+ }
+
+ if (isset($options[ 'pdo' ]))
+ {
+ if (!$options[ 'pdo' ] instanceof PDO)
+ {
+ throw new InvalidArgumentException('Invalid PDO object supplied');
+ }
+
+ $this->pdo = $options[ 'pdo' ];
+
+ foreach ($commands as $value)
+ {
+ $this->pdo->exec($value);
+ }
+
+ return;
+ }
+
+ if (isset($options[ 'dsn' ]))
+ {
+ if (is_array($options[ 'dsn' ]) && isset($options[ 'dsn' ][ 'driver' ]))
+ {
+ $attr = $options[ 'dsn' ];
+ }
+ else
+ {
+ throw new InvalidArgumentException('Invalid DSN option supplied');
+ }
+ }
+ else
+ {
+ if (
+ isset($options[ 'port' ]) &&
+ is_int($options[ 'port' ] * 1)
+ )
+ {
+ $port = $options[ 'port' ];
+ }
+
+ $is_port = isset($port);
+
+ switch ($this->type)
+ {
+ case 'mysql':
+ $attr = [
+ 'driver' => 'mysql',
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'socket' ]))
+ {
+ $attr[ 'unix_socket' ] = $options[ 'socket' ];
+ }
+ else
+ {
+ $attr[ 'host' ] = $options[ 'server' ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+ }
+
+ break;
+
+ case 'pgsql':
+ $attr = [
+ 'driver' => 'pgsql',
+ 'host' => $options[ 'server' ],
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+
+ break;
+
+ case 'sybase':
+ $attr = [
+ 'driver' => 'dblib',
+ 'host' => $options[ 'server' ],
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+
+ break;
+
+ case 'oracle':
+ $attr = [
+ 'driver' => 'oci',
+ 'dbname' => $options[ 'server' ] ?
+ '//' . $options[ 'server' ] . ($is_port ? ':' . $port : ':1521') . '/' . $options[ 'database_name' ] :
+ $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'charset' ]))
+ {
+ $attr[ 'charset' ] = $options[ 'charset' ];
+ }
+
+ break;
+
+ case 'mssql':
+ if (isset($options[ 'driver' ]) && $options[ 'driver' ] === 'dblib')
+ {
+ $attr = [
+ 'driver' => 'dblib',
+ 'host' => $options[ 'server' ] . ($is_port ? ':' . $port : ''),
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'appname' ]))
+ {
+ $attr[ 'appname' ] = $options[ 'appname' ];
+ }
+
+ if (isset($options[ 'charset' ]))
+ {
+ $attr[ 'charset' ] = $options[ 'charset' ];
+ }
+ }
+ else
+ {
+ $attr = [
+ 'driver' => 'sqlsrv',
+ 'Server' => $options[ 'server' ] . ($is_port ? ',' . $port : ''),
+ 'Database' => $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'appname' ]))
+ {
+ $attr[ 'APP' ] = $options[ 'appname' ];
+ }
+
+ $config = [
+ 'ApplicationIntent',
+ 'AttachDBFileName',
+ 'Authentication',
+ 'ColumnEncryption',
+ 'ConnectionPooling',
+ 'Encrypt',
+ 'Failover_Partner',
+ 'KeyStoreAuthentication',
+ 'KeyStorePrincipalId',
+ 'KeyStoreSecret',
+ 'LoginTimeout',
+ 'MultipleActiveResultSets',
+ 'MultiSubnetFailover',
+ 'Scrollable',
+ 'TraceFile',
+ 'TraceOn',
+ 'TransactionIsolation',
+ 'TransparentNetworkIPResolution',
+ 'TrustServerCertificate',
+ 'WSID',
+ ];
+
+ foreach ($config as $value)
+ {
+ $keyname = strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $value));
+
+ if (isset($options[ $keyname ]))
+ {
+ $attr[ $value ] = $options[ $keyname ];
+ }
+ }
+ }
+
+ break;
+
+ case 'sqlite':
+ $attr = [
+ 'driver' => 'sqlite',
+ $options[ 'database_file' ]
+ ];
+
+ break;
+ }
+ }
+
+ if (!isset($attr))
+ {
+ throw new InvalidArgumentException('Incorrect connection options');
+ }
+
+ $driver = $attr[ 'driver' ];
+
+ if (!in_array($driver, PDO::getAvailableDrivers()))
+ {
+ throw new InvalidArgumentException("Unsupported PDO driver: {$driver}");
+ }
+
+ unset($attr[ 'driver' ]);
+
+ $stack = [];
+
+ foreach ($attr as $key => $value)
+ {
+ $stack[] = is_int($key) ? $value : $key . '=' . $value;
+ }
+
+ $dsn = $driver . ':' . implode($stack, ';');
+
+ if (
+ in_array($this->type, ['mysql', 'pgsql', 'sybase', 'mssql']) &&
+ isset($options[ 'charset' ])
+ )
+ {
+ $commands[] = "SET NAMES '{$options[ 'charset' ]}'" . (
+ $this->type === 'mysql' && isset($options[ 'collation' ]) ?
+ " COLLATE '{$options[ 'collation' ]}'" : ''
+ );
+ }
+
+ $this->dsn = $dsn;
+
+ try {
+ $this->pdo = new PDO(
+ $dsn,
+ isset($options[ 'username' ]) ? $options[ 'username' ] : null,
+ isset($options[ 'password' ]) ? $options[ 'password' ] : null,
+ $option
+ );
+
+ foreach ($commands as $value)
+ {
+ $this->pdo->exec($value);
+ }
+ }
+ catch (PDOException $e) {
+ throw new PDOException($e->getMessage());
+ }
+ }
+
+ public function query($query, $map = [])
+ {
+ $raw = $this->raw($query, $map);
+
+ $query = $this->buildRaw($raw, $map);
+
+ return $this->exec($query, $map);
+ }
+
+ public function exec($query, $map = [])
+ {
+ if ($this->debug_mode)
+ {
+ echo $this->generate($query, $map);
+
+ $this->debug_mode = false;
+
+ return false;
+ }
+
+ if ($this->logging)
+ {
+ $this->logs[] = [$query, $map];
+ }
+ else
+ {
+ $this->logs = [[$query, $map]];
+ }
+
+ $statement = $this->pdo->prepare($query);
+
+ if ($statement)
+ {
+ foreach ($map as $key => $value)
+ {
+ $statement->bindValue($key, $value[ 0 ], $value[ 1 ]);
+ }
+
+ $statement->execute();
+
+ $this->statement = $statement;
+
+ return $statement;
+ }
+
+ return false;
+ }
+
+ protected function generate($query, $map)
+ {
+ $identifier = [
+ 'mysql' => '`$1`',
+ 'mssql' => '[$1]'
+ ];
+
+ $query = preg_replace(
+ '/"([a-zA-Z0-9_]+)"/i',
+ isset($identifier[ $this->type ]) ? $identifier[ $this->type ] : '"$1"',
+ $query
+ );
+
+ foreach ($map as $key => $value)
+ {
+ if ($value[ 1 ] === PDO::PARAM_STR)
+ {
+ $replace = $this->quote($value[ 0 ]);
+ }
+ elseif ($value[ 1 ] === PDO::PARAM_NULL)
+ {
+ $replace = 'NULL';
+ }
+ elseif ($value[ 1 ] === PDO::PARAM_LOB)
+ {
+ $replace = '{LOB_DATA}';
+ }
+ else
+ {
+ $replace = $value[ 0 ];
+ }
+
+ $query = str_replace($key, $replace, $query);
+ }
+
+ return $query;
+ }
+
+ public static function raw($string, $map = [])
+ {
+ $raw = new Raw();
+
+ $raw->map = $map;
+ $raw->value = $string;
+
+ return $raw;
+ }
+
+ protected function isRaw($object)
+ {
+ return $object instanceof Raw;
+ }
+
+ protected function buildRaw($raw, &$map)
+ {
+ if (!$this->isRaw($raw))
+ {
+ return false;
+ }
+
+ $query = preg_replace_callback(
+ '/((FROM|TABLE|INTO|UPDATE)\s*)?\<([a-zA-Z0-9_\.]+)\>/i',
+ function ($matches)
+ {
+ if (!empty($matches[ 2 ]))
+ {
+ return $matches[ 2 ] . ' ' . $this->tableQuote($matches[ 3 ]);
+ }
+
+ return $this->columnQuote($matches[ 3 ]);
+ },
+ $raw->value);
+
+ $raw_map = $raw->map;
+
+ if (!empty($raw_map))
+ {
+ foreach ($raw_map as $key => $value)
+ {
+ $map[ $key ] = $this->typeMap($value, gettype($value));
+ }
+ }
+
+ return $query;
+ }
+
+ public function quote($string)
+ {
+ return $this->pdo->quote($string);
+ }
+
+ protected function tableQuote($table)
+ {
+ return '"' . $this->prefix . $table . '"';
+ }
+
+ protected function mapKey()
+ {
+ return ':MeDoO_' . $this->guid++ . '_mEdOo';
+ }
+
+ protected function typeMap($value, $type)
+ {
+ $map = [
+ 'NULL' => PDO::PARAM_NULL,
+ 'integer' => PDO::PARAM_INT,
+ 'double' => PDO::PARAM_STR,
+ 'boolean' => PDO::PARAM_BOOL,
+ 'string' => PDO::PARAM_STR,
+ 'object' => PDO::PARAM_STR,
+ 'resource' => PDO::PARAM_LOB
+ ];
+
+ if ($type === 'boolean')
+ {
+ $value = ($value ? '1' : '0');
+ }
+ elseif ($type === 'NULL')
+ {
+ $value = null;
+ }
+
+ return [$value, $map[ $type ]];
+ }
+
+ protected function columnQuote($string)
+ {
+ if (strpos($string, '.') !== false)
+ {
+ return '"' . $this->prefix . str_replace('.', '"."', $string) . '"';
+ }
+
+ return '"' . $string . '"';
+ }
+
+ protected function columnPush(&$columns, &$map)
+ {
+ if ($columns === '*')
+ {
+ return $columns;
+ }
+
+ $stack = [];
+
+ if (is_string($columns))
+ {
+ $columns = [$columns];
+ }
+
+ foreach ($columns as $key => $value)
+ {
+ if (is_array($value))
+ {
+ $stack[] = $this->columnPush($value, $map);
+ }
+ elseif (!is_int($key) && $raw = $this->buildRaw($value, $map))
+ {
+ preg_match('/(?[a-zA-Z0-9_\.]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $match);
+
+ $stack[] = $raw . ' AS ' . $this->columnQuote( $match[ 'column' ] );
+ }
+ elseif (is_int($key) && is_string($value))
+ {
+ preg_match('/(?[a-zA-Z0-9_\.]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $match);
+
+ if (!empty($match[ 'alias' ]))
+ {
+ $stack[] = $this->columnQuote( $match[ 'column' ] ) . ' AS ' . $this->columnQuote( $match[ 'alias' ] );
+
+ $columns[ $key ] = $match[ 'alias' ];
+
+ if (!empty($match[ 'type' ]))
+ {
+ $columns[ $key ] .= ' [' . $match[ 'type' ] . ']';
+ }
+ }
+ else
+ {
+ $stack[] = $this->columnQuote( $match[ 'column' ] );
+ }
+ }
+ }
+
+ return implode($stack, ',');
+ }
+
+ protected function arrayQuote($array)
+ {
+ $stack = [];
+
+ foreach ($array as $value)
+ {
+ $stack[] = is_int($value) ? $value : $this->pdo->quote($value);
+ }
+
+ return implode($stack, ',');
+ }
+
+ protected function innerConjunct($data, $map, $conjunctor, $outer_conjunctor)
+ {
+ $stack = [];
+
+ foreach ($data as $value)
+ {
+ $stack[] = '(' . $this->dataImplode($value, $map, $conjunctor) . ')';
+ }
+
+ return implode($outer_conjunctor . ' ', $stack);
+ }
+
+ protected function dataImplode($data, &$map, $conjunctor)
+ {
+ $stack = [];
+
+ foreach ($data as $key => $value)
+ {
+ $type = gettype($value);
+
+ if (
+ $type === 'array' &&
+ preg_match("/^(AND|OR)(\s+#.*)?$/", $key, $relation_match)
+ )
+ {
+ $relationship = $relation_match[ 1 ];
+
+ $stack[] = $value !== array_keys(array_keys($value)) ?
+ '(' . $this->dataImplode($value, $map, ' ' . $relationship) . ')' :
+ '(' . $this->innerConjunct($value, $map, ' ' . $relationship, $conjunctor) . ')';
+
+ continue;
+ }
+
+ $map_key = $this->mapKey();
+
+ if (
+ is_int($key) &&
+ preg_match('/([a-zA-Z0-9_\.]+)\[(?\>\=?|\<\=?|\!?\=)\]([a-zA-Z0-9_\.]+)/i', $value, $match)
+ )
+ {
+ $stack[] = $this->columnQuote($match[ 1 ]) . ' ' . $match[ 'operator' ] . ' ' . $this->columnQuote($match[ 3 ]);
+ }
+ else
+ {
+ preg_match('/([a-zA-Z0-9_\.]+)(\[(?\>\=?|\<\=?|\!|\<\>|\>\<|\!?~|REGEXP)\])?/i', $key, $match);
+ $column = $this->columnQuote($match[ 1 ]);
+
+ if (isset($match[ 'operator' ]))
+ {
+ $operator = $match[ 'operator' ];
+
+ if (in_array($operator, ['>', '>=', '<', '<=']))
+ {
+ $condition = $column . ' ' . $operator . ' ';
+
+ if (is_numeric($value))
+ {
+ $condition .= $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_INT];
+ }
+ elseif ($raw = $this->buildRaw($value, $map))
+ {
+ $condition .= $raw;
+ }
+ else
+ {
+ $condition .= $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_STR];
+ }
+
+ $stack[] = $condition;
+ }
+ elseif ($operator === '!')
+ {
+ switch ($type)
+ {
+ case 'NULL':
+ $stack[] = $column . ' IS NOT NULL';
+ break;
+
+ case 'array':
+ $placeholders = [];
+
+ foreach ($value as $index => $item)
+ {
+ $placeholders[] = $map_key . $index . '_i';
+ $map[ $map_key . $index . '_i' ] = $this->typeMap($item, gettype($item));
+ }
+
+ $stack[] = $column . ' NOT IN (' . implode(', ', $placeholders) . ')';
+ break;
+
+ case 'object':
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $stack[] = $column . ' != ' . $raw;
+ }
+ break;
+
+ case 'integer':
+ case 'double':
+ case 'boolean':
+ case 'string':
+ $stack[] = $column . ' != ' . $map_key;
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ elseif ($operator === '~' || $operator === '!~')
+ {
+ if ($type !== 'array')
+ {
+ $value = [ $value ];
+ }
+
+ $connector = ' OR ';
+ $data = array_values($value);
+
+ if (is_array($data[ 0 ]))
+ {
+ if (isset($value[ 'AND' ]) || isset($value[ 'OR' ]))
+ {
+ $connector = ' ' . array_keys($value)[ 0 ] . ' ';
+ $value = $data[ 0 ];
+ }
+ }
+
+ $like_clauses = [];
+
+ foreach ($value as $index => $item)
+ {
+ $item = strval($item);
+
+ if (!preg_match('/(\[.+\]|_|%.+|.+%)/', $item))
+ {
+ $item = '%' . $item . '%';
+ }
+
+ $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $map_key . 'L' . $index;
+ $map[ $map_key . 'L' . $index ] = [$item, PDO::PARAM_STR];
+ }
+
+ $stack[] = '(' . implode($connector, $like_clauses) . ')';
+ }
+ elseif ($operator === '<>' || $operator === '><')
+ {
+ if ($type === 'array')
+ {
+ if ($operator === '><')
+ {
+ $column .= ' NOT';
+ }
+
+ $stack[] = '(' . $column . ' BETWEEN ' . $map_key . 'a AND ' . $map_key . 'b)';
+
+ $data_type = (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ])) ? PDO::PARAM_INT : PDO::PARAM_STR;
+
+ $map[ $map_key . 'a' ] = [$value[ 0 ], $data_type];
+ $map[ $map_key . 'b' ] = [$value[ 1 ], $data_type];
+ }
+ }
+ elseif ($operator === 'REGEXP')
+ {
+ $stack[] = $column . ' REGEXP ' . $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_STR];
+ }
+ }
+ else
+ {
+ switch ($type)
+ {
+ case 'NULL':
+ $stack[] = $column . ' IS NULL';
+ break;
+
+ case 'array':
+ $placeholders = [];
+
+ foreach ($value as $index => $item)
+ {
+ $placeholders[] = $map_key . $index . '_i';
+ $map[ $map_key . $index . '_i' ] = $this->typeMap($item, gettype($item));
+ }
+
+ $stack[] = $column . ' IN (' . implode(', ', $placeholders) . ')';
+ break;
+
+ case 'object':
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $stack[] = $column . ' = ' . $raw;
+ }
+ break;
+
+ case 'integer':
+ case 'double':
+ case 'boolean':
+ case 'string':
+ $stack[] = $column . ' = ' . $map_key;
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+ }
+
+ return implode($conjunctor . ' ', $stack);
+ }
+
+ protected function whereClause($where, &$map)
+ {
+ $where_clause = '';
+
+ if (is_array($where))
+ {
+ $where_keys = array_keys($where);
+
+ $conditions = array_diff_key($where, array_flip(
+ ['GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH']
+ ));
+
+ if (!empty($conditions))
+ {
+ $where_clause = ' WHERE ' . $this->dataImplode($conditions, $map, ' AND');
+ }
+
+ if (isset($where[ 'MATCH' ]) && $this->type === 'mysql')
+ {
+ $MATCH = $where[ 'MATCH' ];
+
+ if (is_array($MATCH) && isset($MATCH[ 'columns' ], $MATCH[ 'keyword' ]))
+ {
+ $mode = '';
+
+ $mode_array = [
+ 'natural' => 'IN NATURAL LANGUAGE MODE',
+ 'natural+query' => 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION',
+ 'boolean' => 'IN BOOLEAN MODE',
+ 'query' => 'WITH QUERY EXPANSION'
+ ];
+
+ if (isset($MATCH[ 'mode' ], $mode_array[ $MATCH[ 'mode' ] ]))
+ {
+ $mode = ' ' . $mode_array[ $MATCH[ 'mode' ] ];
+ }
+
+ $columns = implode(array_map([$this, 'columnQuote'], $MATCH[ 'columns' ]), ', ');
+ $map_key = $this->mapKey();
+ $map[ $map_key ] = [$MATCH[ 'keyword' ], PDO::PARAM_STR];
+
+ $where_clause .= ($where_clause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $map_key . $mode . ')';
+ }
+ }
+
+ if (isset($where[ 'GROUP' ]))
+ {
+ $GROUP = $where[ 'GROUP' ];
+
+ if (is_array($GROUP))
+ {
+ $stack = [];
+
+ foreach ($GROUP as $column => $value)
+ {
+ $stack[] = $this->columnQuote($value);
+ }
+
+ $where_clause .= ' GROUP BY ' . implode($stack, ',');
+ }
+ elseif ($raw = $this->buildRaw($GROUP, $map))
+ {
+ $where_clause .= ' GROUP BY ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' GROUP BY ' . $this->columnQuote($GROUP);
+ }
+
+ if (isset($where[ 'HAVING' ]))
+ {
+ if ($raw = $this->buildRaw($where[ 'HAVING' ], $map))
+ {
+ $where_clause .= ' HAVING ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' HAVING ' . $this->dataImplode($where[ 'HAVING' ], $map, ' AND');
+ }
+ }
+ }
+
+ if (isset($where[ 'ORDER' ]))
+ {
+ $ORDER = $where[ 'ORDER' ];
+
+ if (is_array($ORDER))
+ {
+ $stack = [];
+
+ foreach ($ORDER as $column => $value)
+ {
+ if (is_array($value))
+ {
+ $stack[] = 'FIELD(' . $this->columnQuote($column) . ', ' . $this->arrayQuote($value) . ')';
+ }
+ elseif ($value === 'ASC' || $value === 'DESC')
+ {
+ $stack[] = $this->columnQuote($column) . ' ' . $value;
+ }
+ elseif (is_int($column))
+ {
+ $stack[] = $this->columnQuote($value);
+ }
+ }
+
+ $where_clause .= ' ORDER BY ' . implode($stack, ',');
+ }
+ elseif ($raw = $this->buildRaw($ORDER, $map))
+ {
+ $where_clause .= ' ORDER BY ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' ORDER BY ' . $this->columnQuote($ORDER);
+ }
+
+ if (
+ isset($where[ 'LIMIT' ]) &&
+ in_array($this->type, ['oracle', 'mssql'])
+ )
+ {
+ $LIMIT = $where[ 'LIMIT' ];
+
+ if (is_numeric($LIMIT))
+ {
+ $LIMIT = [0, $LIMIT];
+ }
+
+ if (
+ is_array($LIMIT) &&
+ is_numeric($LIMIT[ 0 ]) &&
+ is_numeric($LIMIT[ 1 ])
+ )
+ {
+ $where_clause .= ' OFFSET ' . $LIMIT[ 0 ] . ' ROWS FETCH NEXT ' . $LIMIT[ 1 ] . ' ROWS ONLY';
+ }
+ }
+ }
+
+ if (isset($where[ 'LIMIT' ]) && !in_array($this->type, ['oracle', 'mssql']))
+ {
+ $LIMIT = $where[ 'LIMIT' ];
+
+ if (is_numeric($LIMIT))
+ {
+ $where_clause .= ' LIMIT ' . $LIMIT;
+ }
+ elseif (
+ is_array($LIMIT) &&
+ is_numeric($LIMIT[ 0 ]) &&
+ is_numeric($LIMIT[ 1 ])
+ )
+ {
+ $where_clause .= ' LIMIT ' . $LIMIT[ 1 ] . ' OFFSET ' . $LIMIT[ 0 ];
+ }
+ }
+ }
+ elseif ($raw = $this->buildRaw($where, $map))
+ {
+ $where_clause .= ' ' . $raw;
+ }
+
+ return $where_clause;
+ }
+
+ protected function selectContext($table, &$map, $join, &$columns = null, $where = null, $column_fn = null)
+ {
+ preg_match('/(?[a-zA-Z0-9_]+)\s*\((?[a-zA-Z0-9_]+)\)/i', $table, $table_match);
+
+ if (isset($table_match[ 'table' ], $table_match[ 'alias' ]))
+ {
+ $table = $this->tableQuote($table_match[ 'table' ]);
+
+ $table_query = $table . ' AS ' . $this->tableQuote($table_match[ 'alias' ]);
+ }
+ else
+ {
+ $table = $this->tableQuote($table);
+
+ $table_query = $table;
+ }
+
+ $join_key = is_array($join) ? array_keys($join) : null;
+
+ if (
+ isset($join_key[ 0 ]) &&
+ strpos($join_key[ 0 ], '[') === 0
+ )
+ {
+ $table_join = [];
+
+ $join_array = [
+ '>' => 'LEFT',
+ '<' => 'RIGHT',
+ '<>' => 'FULL',
+ '><' => 'INNER'
+ ];
+
+ foreach($join as $sub_table => $relation)
+ {
+ preg_match('/(\[(?\<\>?|\>\)\])?(?[a-zA-Z0-9_]+)\s?(\((?[a-zA-Z0-9_]+)\))?/', $sub_table, $match);
+
+ if ($match[ 'join' ] !== '' && $match[ 'table' ] !== '')
+ {
+ if (is_string($relation))
+ {
+ $relation = 'USING ("' . $relation . '")';
+ }
+
+ if (is_array($relation))
+ {
+ // For ['column1', 'column2']
+ if (isset($relation[ 0 ]))
+ {
+ $relation = 'USING ("' . implode($relation, '", "') . '")';
+ }
+ else
+ {
+ $joins = [];
+
+ foreach ($relation as $key => $value)
+ {
+ $joins[] = (
+ strpos($key, '.') > 0 ?
+ // For ['tableB.column' => 'column']
+ $this->columnQuote($key) :
+
+ // For ['column1' => 'column2']
+ $table . '."' . $key . '"'
+ ) .
+ ' = ' .
+ $this->tableQuote(isset($match[ 'alias' ]) ? $match[ 'alias' ] : $match[ 'table' ]) . '."' . $value . '"';
+ }
+
+ $relation = 'ON ' . implode($joins, ' AND ');
+ }
+ }
+
+ $table_name = $this->tableQuote($match[ 'table' ]) . ' ';
+
+ if (isset($match[ 'alias' ]))
+ {
+ $table_name .= 'AS ' . $this->tableQuote($match[ 'alias' ]) . ' ';
+ }
+
+ $table_join[] = $join_array[ $match[ 'join' ] ] . ' JOIN ' . $table_name . $relation;
+ }
+ }
+
+ $table_query .= ' ' . implode($table_join, ' ');
+ }
+ else
+ {
+ if (is_null($columns))
+ {
+ if (
+ !is_null($where) ||
+ (is_array($join) && isset($column_fn))
+ )
+ {
+ $where = $join;
+ $columns = null;
+ }
+ else
+ {
+ $where = null;
+ $columns = $join;
+ }
+ }
+ else
+ {
+ $where = $columns;
+ $columns = $join;
+ }
+ }
+
+ if (isset($column_fn))
+ {
+ if ($column_fn === 1)
+ {
+ $column = '1';
+
+ if (is_null($where))
+ {
+ $where = $columns;
+ }
+ }
+ elseif ($raw = $this->buildRaw($column_fn, $map))
+ {
+ $column = $raw;
+ }
+ else
+ {
+ if (empty($columns) || $this->isRaw($columns))
+ {
+ $columns = '*';
+ $where = $join;
+ }
+
+ $column = $column_fn . '(' . $this->columnPush($columns, $map) . ')';
+ }
+ }
+ else
+ {
+ $column = $this->columnPush($columns, $map);
+ }
+
+ return 'SELECT ' . $column . ' FROM ' . $table_query . $this->whereClause($where, $map);
+ }
+
+ protected function columnMap($columns, &$stack)
+ {
+ if ($columns === '*')
+ {
+ return $stack;
+ }
+
+ foreach ($columns as $key => $value)
+ {
+ if (is_int($key))
+ {
+ preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $key_match);
+
+ $column_key = !empty($key_match[ 'alias' ]) ?
+ $key_match[ 'alias' ] :
+ $key_match[ 'column' ];
+
+ if (isset($key_match[ 'type' ]))
+ {
+ $stack[ $value ] = [$column_key, $key_match[ 'type' ]];
+ }
+ else
+ {
+ $stack[ $value ] = [$column_key, 'String'];
+ }
+ }
+ elseif ($this->isRaw($value))
+ {
+ preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $key_match);
+
+ $column_key = $key_match[ 'column' ];
+
+ if (isset($key_match[ 'type' ]))
+ {
+ $stack[ $key ] = [$column_key, $key_match[ 'type' ]];
+ }
+ else
+ {
+ $stack[ $key ] = [$column_key, 'String'];
+ }
+ }
+ elseif (!is_int($key) && is_array($value))
+ {
+ $this->columnMap($value, $stack);
+ }
+ }
+
+ return $stack;
+ }
+
+ protected function dataMap($data, $columns, $column_map, &$stack)
+ {
+ foreach ($columns as $key => $value)
+ {
+ $isRaw = $this->isRaw($value);
+
+ if (is_int($key) || $isRaw)
+ {
+ $map = $column_map[ $isRaw ? $key : $value ];
+
+ $column_key = $map[ 0 ];
+
+ $result = $data[ $column_key ];
+
+ if (isset($map[ 1 ]))
+ {
+ if ($isRaw && in_array($map[ 1 ], ['Object', 'JSON']))
+ {
+ continue;
+ }
+
+ if (is_null($result))
+ {
+ $stack[ $column_key ] = null;
+ continue;
+ }
+
+ switch ($map[ 1 ])
+ {
+ case 'Number':
+ $stack[ $column_key ] = (double) $result;
+ break;
+
+ case 'Int':
+ $stack[ $column_key ] = (int) $result;
+ break;
+
+ case 'Bool':
+ $stack[ $column_key ] = (bool) $result;
+ break;
+
+ case 'Object':
+ $stack[ $column_key ] = unserialize($result);
+ break;
+
+ case 'JSON':
+ $stack[ $column_key ] = json_decode($result, true);
+ break;
+
+ case 'String':
+ $stack[ $column_key ] = $result;
+ break;
+ }
+ }
+ else
+ {
+ $stack[ $column_key ] = $result;
+ }
+ }
+ else
+ {
+ $current_stack = [];
+
+ $this->dataMap($data, $value, $column_map, $current_stack);
+
+ $stack[ $key ] = $current_stack;
+ }
+ }
+ }
+
+ public function select($table, $join, $columns = null, $where = null)
+ {
+ $map = [];
+ $stack = [];
+ $column_map = [];
+
+ $index = 0;
+
+ $column = $where === null ? $join : $columns;
+
+ $is_single = (is_string($column) && $column !== '*');
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map);
+
+ $this->columnMap($columns, $column_map);
+
+ if (!$query)
+ {
+ return false;
+ }
+
+ if ($columns === '*')
+ {
+ return $query->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ if ($is_single)
+ {
+ return $query->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ while ($data = $query->fetch(PDO::FETCH_ASSOC))
+ {
+ $current_stack = [];
+
+ $this->dataMap($data, $columns, $column_map, $current_stack);
+
+ $stack[ $index ] = $current_stack;
+
+ $index++;
+ }
+
+ return $stack;
+ }
+
+ public function insert($table, $datas)
+ {
+ $stack = [];
+ $columns = [];
+ $fields = [];
+ $map = [];
+
+ if (!isset($datas[ 0 ]))
+ {
+ $datas = [$datas];
+ }
+
+ foreach ($datas as $data)
+ {
+ foreach ($data as $key => $value)
+ {
+ $columns[] = $key;
+ }
+ }
+
+ $columns = array_unique($columns);
+
+ foreach ($datas as $data)
+ {
+ $values = [];
+
+ foreach ($columns as $key)
+ {
+ if ($raw = $this->buildRaw($data[ $key ], $map))
+ {
+ $values[] = $raw;
+ continue;
+ }
+
+ $map_key =$this->mapKey();
+
+ $values[] = $map_key;
+
+ if (!isset($data[ $key ]))
+ {
+ $map[ $map_key ] = [null, PDO::PARAM_NULL];
+ }
+ else
+ {
+ $value = $data[ $key ];
+
+ $type = gettype($value);
+
+ switch ($type)
+ {
+ case 'array':
+ $map[ $map_key ] = [
+ strpos($key, '[JSON]') === strlen($key) - 6 ?
+ json_encode($value) :
+ serialize($value),
+ PDO::PARAM_STR
+ ];
+ break;
+
+ case 'object':
+ $value = serialize($value);
+
+ case 'NULL':
+ case 'resource':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+
+ $stack[] = '(' . implode($values, ', ') . ')';
+ }
+
+ foreach ($columns as $key)
+ {
+ $fields[] = $this->columnQuote(preg_replace("/(\s*\[JSON\]$)/i", '', $key));
+ }
+
+ return $this->exec('INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack), $map);
+ }
+
+ public function update($table, $data, $where = null)
+ {
+ $fields = [];
+ $map = [];
+
+ foreach ($data as $key => $value)
+ {
+ $column = $this->columnQuote(preg_replace("/(\s*\[(JSON|\+|\-|\*|\/)\]$)/i", '', $key));
+
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $fields[] = $column . ' = ' . $raw;
+ continue;
+ }
+
+ $map_key = $this->mapKey();
+
+ preg_match('/(?[a-zA-Z0-9_]+)(\[(?\+|\-|\*|\/)\])?/i', $key, $match);
+
+ if (isset($match[ 'operator' ]))
+ {
+ if (is_numeric($value))
+ {
+ $fields[] = $column . ' = ' . $column . ' ' . $match[ 'operator' ] . ' ' . $value;
+ }
+ }
+ else
+ {
+ $fields[] = $column . ' = ' . $map_key;
+
+ $type = gettype($value);
+
+ switch ($type)
+ {
+ case 'array':
+ $map[ $map_key ] = [
+ strpos($key, '[JSON]') === strlen($key) - 6 ?
+ json_encode($value) :
+ serialize($value),
+ PDO::PARAM_STR
+ ];
+ break;
+
+ case 'object':
+ $value = serialize($value);
+
+ case 'NULL':
+ case 'resource':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+
+ return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where, $map), $map);
+ }
+
+ public function delete($table, $where)
+ {
+ $map = [];
+
+ return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where, $map), $map);
+ }
+
+ public function replace($table, $columns, $where = null)
+ {
+ if (!is_array($columns) || empty($columns))
+ {
+ return false;
+ }
+
+ $map = [];
+ $stack = [];
+
+ foreach ($columns as $column => $replacements)
+ {
+ if (is_array($replacements))
+ {
+ foreach ($replacements as $old => $new)
+ {
+ $map_key = $this->mapKey();
+
+ $stack[] = $this->columnQuote($column) . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $map_key . 'a, ' . $map_key . 'b)';
+
+ $map[ $map_key . 'a' ] = [$old, PDO::PARAM_STR];
+ $map[ $map_key . 'b' ] = [$new, PDO::PARAM_STR];
+ }
+ }
+ }
+
+ if (!empty($stack))
+ {
+ return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $stack) . $this->whereClause($where, $map), $map);
+ }
+
+ return false;
+ }
+
+ public function get($table, $join = null, $columns = null, $where = null)
+ {
+ $map = [];
+ $stack = [];
+ $column_map = [];
+
+ if ($where === null)
+ {
+ $column = $join;
+ unset($columns[ 'LIMIT' ]);
+ }
+ else
+ {
+ $column = $columns;
+ unset($where[ 'LIMIT' ]);
+ }
+
+ $is_single = (is_string($column) && $column !== '*');
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where) . ' LIMIT 1', $map);
+
+ if ($query)
+ {
+ $data = $query->fetchAll(PDO::FETCH_ASSOC);
+
+ if (isset($data[ 0 ]))
+ {
+ if ($column === '*')
+ {
+ return $data[ 0 ];
+ }
+
+ $this->columnMap($columns, $column_map);
+
+ $this->dataMap($data[ 0 ], $columns, $column_map, $stack);
+
+ if ($is_single)
+ {
+ return $stack[ $column_map[ $column ][ 0 ] ];
+ }
+
+ return $stack;
+ }
+ }
+ }
+
+ public function has($table, $join, $where = null)
+ {
+ $map = [];
+ $column = null;
+
+ if ($this->type === 'mssql')
+ {
+ $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, Medoo::raw('TOP 1 1')), $map);
+ }
+ else
+ {
+ $query = $this->exec('SELECT EXISTS(' . $this->selectContext($table, $map, $join, $column, $where, 1) . ')', $map);
+ }
+
+ if ($query)
+ {
+ $result = $query->fetchColumn();
+
+ return $result === '1' || $result === 1 || $result === true;
+ }
+
+ return false;
+ }
+
+ public function rand($table, $join = null, $columns = null, $where = null)
+ {
+ $type = $this->type;
+
+ $order = 'RANDOM()';
+
+ if ($type === 'mysql')
+ {
+ $order = 'RAND()';
+ }
+ elseif ($type === 'mssql')
+ {
+ $order = 'NEWID()';
+ }
+
+ $order_raw = $this->raw($order);
+
+ if ($where === null)
+ {
+ if ($columns === null)
+ {
+ $columns = [
+ 'ORDER' => $order_raw
+ ];
+ }
+ else
+ {
+ $column = $join;
+ unset($columns[ 'ORDER' ]);
+
+ $columns[ 'ORDER' ] = $order_raw;
+ }
+ }
+ else
+ {
+ unset($where[ 'ORDER' ]);
+
+ $where[ 'ORDER' ] = $order_raw;
+ }
+
+ return $this->select($table, $join, $columns, $where);
+ }
+
+ private function aggregate($type, $table, $join = null, $column = null, $where = null)
+ {
+ $map = [];
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, strtoupper($type)), $map);
+
+ if ($query)
+ {
+ $number = $query->fetchColumn();
+
+ return is_numeric($number) ? $number + 0 : $number;
+ }
+
+ return false;
+ }
+
+ public function count($table, $join = null, $column = null, $where = null)
+ {
+ return $this->aggregate('count', $table, $join, $column, $where);
+ }
+
+ public function avg($table, $join, $column = null, $where = null)
+ {
+ return $this->aggregate('avg', $table, $join, $column, $where);
+ }
+
+ public function max($table, $join, $column = null, $where = null)
+ {
+ return $this->aggregate('max', $table, $join, $column, $where);
+ }
+
+ public function min($table, $join, $column = null, $where = null)
+ {
+ return $this->aggregate('min', $table, $join, $column, $where);
+ }
+
+ public function sum($table, $join, $column = null, $where = null)
+ {
+ return $this->aggregate('sum', $table, $join, $column, $where);
+ }
+
+ public function action($actions)
+ {
+ if (is_callable($actions))
+ {
+ $this->pdo->beginTransaction();
+
+ try {
+ $result = $actions($this);
+
+ if ($result === false)
+ {
+ $this->pdo->rollBack();
+ }
+ else
+ {
+ $this->pdo->commit();
+ }
+ }
+ catch (Exception $e) {
+ $this->pdo->rollBack();
+
+ throw $e;
+ }
+
+ return $result;
+ }
+
+ return false;
+ }
+
+ public function id()
+ {
+ $type = $this->type;
+
+ if ($type === 'oracle')
+ {
+ return 0;
+ }
+ elseif ($type === 'pgsql')
+ {
+ return $this->pdo->query('SELECT LASTVAL()')->fetchColumn();
+ }
+
+ return $this->pdo->lastInsertId();
+ }
+
+ public function debug()
+ {
+ $this->debug_mode = true;
+
+ return $this;
+ }
+
+ public function error()
+ {
+ return $this->statement ? $this->statement->errorInfo() : null;
+ }
+
+ public function last()
+ {
+ $log = end($this->logs);
+
+ return $this->generate($log[ 0 ], $log[ 1 ]);
+ }
+
+ public function log()
+ {
+ return array_map(function ($log)
+ {
+ return $this->generate($log[ 0 ], $log[ 1 ]);
+ },
+ $this->logs
+ );
+ }
+
+ public function info()
+ {
+ $output = [
+ 'server' => 'SERVER_INFO',
+ 'driver' => 'DRIVER_NAME',
+ 'client' => 'CLIENT_VERSION',
+ 'version' => 'SERVER_VERSION',
+ 'connection' => 'CONNECTION_STATUS'
+ ];
+
+ foreach ($output as $key => $value)
+ {
+ $output[ $key ] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value));
+ }
+
+ $output[ 'dsn' ] = $this->dsn;
+
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/application/libraries/Parsedown.php b/application/libraries/Parsedown.php
new file mode 100644
index 0000000..87d612a
--- /dev/null
+++ b/application/libraries/Parsedown.php
@@ -0,0 +1,1679 @@
+DefinitionData = array();
+
+ # standardize line breaks
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+ # remove surrounding line breaks
+ $text = trim($text, "\n");
+
+ # split text into lines
+ $lines = explode("\n", $text);
+
+ # iterate through lines to identify blocks
+ $markup = $this->lines($lines);
+
+ # trim line breaks
+ $markup = trim($markup, "\n");
+
+ return $markup;
+ }
+
+ #
+ # Setters
+ #
+
+ function setBreaksEnabled($breaksEnabled)
+ {
+ $this->breaksEnabled = $breaksEnabled;
+
+ return $this;
+ }
+
+ protected $breaksEnabled;
+
+ function setMarkupEscaped($markupEscaped)
+ {
+ $this->markupEscaped = $markupEscaped;
+
+ return $this;
+ }
+
+ protected $markupEscaped;
+
+ function setUrlsLinked($urlsLinked)
+ {
+ $this->urlsLinked = $urlsLinked;
+
+ return $this;
+ }
+
+ protected $urlsLinked = true;
+
+ function setSafeMode($safeMode)
+ {
+ $this->safeMode = (bool) $safeMode;
+
+ return $this;
+ }
+
+ protected $safeMode;
+
+ protected $safeLinksWhitelist = array(
+ 'http://',
+ 'https://',
+ 'ftp://',
+ 'ftps://',
+ 'mailto:',
+ 'data:image/png;base64,',
+ 'data:image/gif;base64,',
+ 'data:image/jpeg;base64,',
+ 'irc:',
+ 'ircs:',
+ 'git:',
+ 'ssh:',
+ 'news:',
+ 'steam:',
+ );
+
+ #
+ # Lines
+ #
+
+ protected $BlockTypes = array(
+ '#' => array('Header'),
+ '*' => array('Rule', 'List'),
+ '+' => array('List'),
+ '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
+ '0' => array('List'),
+ '1' => array('List'),
+ '2' => array('List'),
+ '3' => array('List'),
+ '4' => array('List'),
+ '5' => array('List'),
+ '6' => array('List'),
+ '7' => array('List'),
+ '8' => array('List'),
+ '9' => array('List'),
+ ':' => array('Table'),
+ '<' => array('Comment', 'Markup'),
+ '=' => array('SetextHeader'),
+ '>' => array('Quote'),
+ '[' => array('Reference'),
+ '_' => array('Rule'),
+ '`' => array('FencedCode'),
+ '|' => array('Table'),
+ '~' => array('FencedCode'),
+ );
+
+ # ~
+
+ protected $unmarkedBlockTypes = array(
+ 'Code',
+ );
+
+ #
+ # Blocks
+ #
+
+ protected function lines(array $lines)
+ {
+ $CurrentBlock = null;
+
+ foreach ($lines as $line)
+ {
+ if (chop($line) === '')
+ {
+ if (isset($CurrentBlock))
+ {
+ $CurrentBlock['interrupted'] = true;
+ }
+
+ continue;
+ }
+
+ if (strpos($line, "\t") !== false)
+ {
+ $parts = explode("\t", $line);
+
+ $line = $parts[0];
+
+ unset($parts[0]);
+
+ foreach ($parts as $part)
+ {
+ $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
+
+ $line .= str_repeat(' ', $shortage);
+ $line .= $part;
+ }
+ }
+
+ $indent = 0;
+
+ while (isset($line[$indent]) and $line[$indent] === ' ')
+ {
+ $indent ++;
+ }
+
+ $text = $indent > 0 ? substr($line, $indent) : $line;
+
+ # ~
+
+ $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+ # ~
+
+ if (isset($CurrentBlock['continuable']))
+ {
+ $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $CurrentBlock = $Block;
+
+ continue;
+ }
+ else
+ {
+ if ($this->isBlockCompletable($CurrentBlock['type']))
+ {
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+ }
+ }
+ }
+
+ # ~
+
+ $marker = $text[0];
+
+ # ~
+
+ $blockTypes = $this->unmarkedBlockTypes;
+
+ if (isset($this->BlockTypes[$marker]))
+ {
+ foreach ($this->BlockTypes[$marker] as $blockType)
+ {
+ $blockTypes []= $blockType;
+ }
+ }
+
+ #
+ # ~
+
+ foreach ($blockTypes as $blockType)
+ {
+ $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $Block['type'] = $blockType;
+
+ if ( ! isset($Block['identified']))
+ {
+ $Blocks []= $CurrentBlock;
+
+ $Block['identified'] = true;
+ }
+
+ if ($this->isBlockContinuable($blockType))
+ {
+ $Block['continuable'] = true;
+ }
+
+ $CurrentBlock = $Block;
+
+ continue 2;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
+ {
+ $CurrentBlock['element']['text'] .= "\n".$text;
+ }
+ else
+ {
+ $Blocks []= $CurrentBlock;
+
+ $CurrentBlock = $this->paragraph($Line);
+
+ $CurrentBlock['identified'] = true;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
+ {
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+ }
+
+ # ~
+
+ $Blocks []= $CurrentBlock;
+
+ unset($Blocks[0]);
+
+ # ~
+
+ $markup = '';
+
+ foreach ($Blocks as $Block)
+ {
+ if (isset($Block['hidden']))
+ {
+ continue;
+ }
+
+ $markup .= "\n";
+ $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
+ }
+
+ $markup .= "\n";
+
+ # ~
+
+ return $markup;
+ }
+
+ protected function isBlockContinuable($Type)
+ {
+ return method_exists($this, 'block'.$Type.'Continue');
+ }
+
+ protected function isBlockCompletable($Type)
+ {
+ return method_exists($this, 'block'.$Type.'Complete');
+ }
+
+ #
+ # Code
+
+ protected function blockCode($Line, $Block = null)
+ {
+ if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if ($Line['indent'] >= 4)
+ {
+ $text = substr($Line['body'], 4);
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeContinue($Line, $Block)
+ {
+ if ($Line['indent'] >= 4)
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text']['text'] .= "\n";
+
+ $text = substr($Line['body'], 4);
+
+ $Block['element']['text']['text'] .= $text;
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeComplete($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # Comment
+
+ protected function blockComment($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
+ if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
+ {
+ $Block = array(
+ 'markup' => $Line['body'],
+ );
+
+ if (preg_match('/-->$/', $Line['text']))
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function blockCommentContinue($Line, array $Block)
+ {
+ if (isset($Block['closed']))
+ {
+ return;
+ }
+
+ $Block['markup'] .= "\n" . $Line['body'];
+
+ if (preg_match('/-->$/', $Line['text']))
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+
+ #
+ # Fenced Code
+
+ protected function blockFencedCode($Line)
+ {
+ if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches))
+ {
+ $Element = array(
+ 'name' => 'code',
+ 'text' => '',
+ );
+
+ if (isset($matches[1]))
+ {
+ $class = 'language-'.$matches[1];
+
+ $Element['attributes'] = array(
+ 'class' => $class,
+ );
+ }
+
+ $Block = array(
+ 'char' => $Line['text'][0],
+ 'element' => array(
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => $Element,
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockFencedCodeContinue($Line, $Block)
+ {
+ if (isset($Block['complete']))
+ {
+ return;
+ }
+
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
+ {
+ $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
+
+ $Block['complete'] = true;
+
+ return $Block;
+ }
+
+ $Block['element']['text']['text'] .= "\n".$Line['body'];
+
+ return $Block;
+ }
+
+ protected function blockFencedCodeComplete($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # Header
+
+ protected function blockHeader($Line)
+ {
+ if (isset($Line['text'][1]))
+ {
+ $level = 1;
+
+ while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
+ {
+ $level ++;
+ }
+
+ if ($level > 6)
+ {
+ return;
+ }
+
+ $text = trim($Line['text'], '# ');
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'h' . min(6, $level),
+ 'text' => $text,
+ 'handler' => 'line',
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # List
+
+ protected function blockList($Line)
+ {
+ list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
+
+ if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'indent' => $Line['indent'],
+ 'pattern' => $pattern,
+ 'element' => array(
+ 'name' => $name,
+ 'handler' => 'elements',
+ ),
+ );
+
+ if($name === 'ol')
+ {
+ $listStart = stristr($matches[0], '.', true);
+
+ if($listStart !== '1')
+ {
+ $Block['element']['attributes'] = array('start' => $listStart);
+ }
+ }
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => array(
+ $matches[2],
+ ),
+ );
+
+ $Block['element']['text'] []= & $Block['li'];
+
+ return $Block;
+ }
+ }
+
+ protected function blockListContinue($Line, array $Block)
+ {
+ if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['li']['text'] []= '';
+
+ $Block['loose'] = true;
+
+ unset($Block['interrupted']);
+ }
+
+ unset($Block['li']);
+
+ $text = isset($matches[1]) ? $matches[1] : '';
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => array(
+ $text,
+ ),
+ );
+
+ $Block['element']['text'] []= & $Block['li'];
+
+ return $Block;
+ }
+
+ if ($Line['text'][0] === '[' and $this->blockReference($Line))
+ {
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] []= $text;
+
+ return $Block;
+ }
+
+ if ($Line['indent'] > 0)
+ {
+ $Block['li']['text'] []= '';
+
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] []= $text;
+
+ unset($Block['interrupted']);
+
+ return $Block;
+ }
+ }
+
+ protected function blockListComplete(array $Block)
+ {
+ if (isset($Block['loose']))
+ {
+ foreach ($Block['element']['text'] as &$li)
+ {
+ if (end($li['text']) !== '')
+ {
+ $li['text'] []= '';
+ }
+ }
+ }
+
+ return $Block;
+ }
+
+ #
+ # Quote
+
+ protected function blockQuote($Line)
+ {
+ if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'blockquote',
+ 'handler' => 'lines',
+ 'text' => (array) $matches[1],
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockQuoteContinue($Line, array $Block)
+ {
+ if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['text'] []= '';
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text'] []= $matches[1];
+
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $Block['element']['text'] []= $Line['text'];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Rule
+
+ protected function blockRule($Line)
+ {
+ if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'hr'
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Setext
+
+ protected function blockSetextHeader($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (chop($Line['text'], $Line['text'][0]) === '')
+ {
+ $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+ return $Block;
+ }
+ }
+
+ #
+ # Markup
+
+ protected function blockMarkup($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
+ if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
+ {
+ $element = strtolower($matches[1]);
+
+ if (in_array($element, $this->textLevelElements))
+ {
+ return;
+ }
+
+ $Block = array(
+ 'name' => $matches[1],
+ 'depth' => 0,
+ 'markup' => $Line['text'],
+ );
+
+ $length = strlen($matches[0]);
+
+ $remainder = substr($Line['text'], $length);
+
+ if (trim($remainder) === '')
+ {
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+ {
+ $Block['closed'] = true;
+
+ $Block['void'] = true;
+ }
+ }
+ else
+ {
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+ {
+ return;
+ }
+
+ if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
+ {
+ $Block['closed'] = true;
+ }
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function blockMarkupContinue($Line, array $Block)
+ {
+ if (isset($Block['closed']))
+ {
+ return;
+ }
+
+ if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
+ {
+ $Block['depth'] ++;
+ }
+
+ if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
+ {
+ if ($Block['depth'] > 0)
+ {
+ $Block['depth'] --;
+ }
+ else
+ {
+ $Block['closed'] = true;
+ }
+ }
+
+ if (isset($Block['interrupted']))
+ {
+ $Block['markup'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['markup'] .= "\n".$Line['body'];
+
+ return $Block;
+ }
+
+ #
+ # Reference
+
+ protected function blockReference($Line)
+ {
+ if (preg_match('/^\[(.+?)\]:[ ]*(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
+ {
+ $id = strtolower($matches[1]);
+
+ $Data = array(
+ 'url' => $matches[2],
+ 'title' => null,
+ );
+
+ if (isset($matches[3]))
+ {
+ $Data['title'] = $matches[3];
+ }
+
+ $this->DefinitionData['Reference'][$id] = $Data;
+
+ $Block = array(
+ 'hidden' => true,
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Table
+
+ protected function blockTable($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
+ {
+ $alignments = array();
+
+ $divider = $Line['text'];
+
+ $divider = trim($divider);
+ $divider = trim($divider, '|');
+
+ $dividerCells = explode('|', $divider);
+
+ foreach ($dividerCells as $dividerCell)
+ {
+ $dividerCell = trim($dividerCell);
+
+ if ($dividerCell === '')
+ {
+ continue;
+ }
+
+ $alignment = null;
+
+ if ($dividerCell[0] === ':')
+ {
+ $alignment = 'left';
+ }
+
+ if (substr($dividerCell, - 1) === ':')
+ {
+ $alignment = $alignment === 'left' ? 'center' : 'right';
+ }
+
+ $alignments []= $alignment;
+ }
+
+ # ~
+
+ $HeaderElements = array();
+
+ $header = $Block['element']['text'];
+
+ $header = trim($header);
+ $header = trim($header, '|');
+
+ $headerCells = explode('|', $header);
+
+ foreach ($headerCells as $index => $headerCell)
+ {
+ $headerCell = trim($headerCell);
+
+ $HeaderElement = array(
+ 'name' => 'th',
+ 'text' => $headerCell,
+ 'handler' => 'line',
+ );
+
+ if (isset($alignments[$index]))
+ {
+ $alignment = $alignments[$index];
+
+ $HeaderElement['attributes'] = array(
+ 'style' => 'text-align: '.$alignment.';',
+ );
+ }
+
+ $HeaderElements []= $HeaderElement;
+ }
+
+ # ~
+
+ $Block = array(
+ 'alignments' => $alignments,
+ 'identified' => true,
+ 'element' => array(
+ 'name' => 'table',
+ 'handler' => 'elements',
+ ),
+ );
+
+ $Block['element']['text'] []= array(
+ 'name' => 'thead',
+ 'handler' => 'elements',
+ );
+
+ $Block['element']['text'] []= array(
+ 'name' => 'tbody',
+ 'handler' => 'elements',
+ 'text' => array(),
+ );
+
+ $Block['element']['text'][0]['text'] []= array(
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $HeaderElements,
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockTableContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
+ {
+ $Elements = array();
+
+ $row = $Line['text'];
+
+ $row = trim($row);
+ $row = trim($row, '|');
+
+ preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
+
+ foreach ($matches[0] as $index => $cell)
+ {
+ $cell = trim($cell);
+
+ $Element = array(
+ 'name' => 'td',
+ 'handler' => 'line',
+ 'text' => $cell,
+ );
+
+ if (isset($Block['alignments'][$index]))
+ {
+ $Element['attributes'] = array(
+ 'style' => 'text-align: '.$Block['alignments'][$index].';',
+ );
+ }
+
+ $Elements []= $Element;
+ }
+
+ $Element = array(
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $Elements,
+ );
+
+ $Block['element']['text'][1]['text'] []= $Element;
+
+ return $Block;
+ }
+ }
+
+ #
+ # ~
+ #
+
+ protected function paragraph($Line)
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'p',
+ 'text' => $Line['text'],
+ 'handler' => 'line',
+ ),
+ );
+
+ return $Block;
+ }
+
+ #
+ # Inline Elements
+ #
+
+ protected $InlineTypes = array(
+ '"' => array('SpecialCharacter'),
+ '!' => array('Image'),
+ '&' => array('SpecialCharacter'),
+ '*' => array('Emphasis'),
+ ':' => array('Url'),
+ '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
+ '>' => array('SpecialCharacter'),
+ '[' => array('Link'),
+ '_' => array('Emphasis'),
+ '`' => array('Code'),
+ '~' => array('Strikethrough'),
+ '\\' => array('EscapeSequence'),
+ );
+
+ # ~
+
+ protected $inlineMarkerList = '!"*_&[:<>`~\\';
+
+ #
+ # ~
+ #
+
+ public function line($text, $nonNestables=array())
+ {
+ $markup = '';
+
+ # $excerpt is based on the first occurrence of a marker
+
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList))
+ {
+ $marker = $excerpt[0];
+
+ $markerPosition = strpos($text, $marker);
+
+ $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+ foreach ($this->InlineTypes[$marker] as $inlineType)
+ {
+ # check to see if the current inline type is nestable in the current context
+
+ if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
+ {
+ continue;
+ }
+
+ $Inline = $this->{'inline'.$inlineType}($Excerpt);
+
+ if ( ! isset($Inline))
+ {
+ continue;
+ }
+
+ # makes sure that the inline belongs to "our" marker
+
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
+ {
+ continue;
+ }
+
+ # sets a default inline position
+
+ if ( ! isset($Inline['position']))
+ {
+ $Inline['position'] = $markerPosition;
+ }
+
+ # cause the new element to 'inherit' our non nestables
+
+ foreach ($nonNestables as $non_nestable)
+ {
+ $Inline['element']['nonNestables'][] = $non_nestable;
+ }
+
+ # the text that comes before the inline
+ $unmarkedText = substr($text, 0, $Inline['position']);
+
+ # compile the unmarked text
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ # compile the inline
+ $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
+
+ # remove the examined text
+ $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+ continue 2;
+ }
+
+ # the marker does not belong to an inline
+
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ $text = substr($text, $markerPosition + 1);
+ }
+
+ $markup .= $this->unmarkedText($text);
+
+ return $markup;
+ }
+
+ #
+ # ~
+ #
+
+ protected function inlineCode($Excerpt)
+ {
+ $marker = $Excerpt['text'][0];
+
+ if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ );
+ }
+ }
+
+ protected function inlineEmailTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
+ {
+ $url = $matches[1];
+
+ if ( ! isset($matches[2]))
+ {
+ $url = 'mailto:' . $url;
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function inlineEmphasis($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ $marker = $Excerpt['text'][0];
+
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'strong';
+ }
+ elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'em';
+ }
+ else
+ {
+ return;
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => $emphasis,
+ 'handler' => 'line',
+ 'text' => $matches[1],
+ ),
+ );
+ }
+
+ protected function inlineEscapeSequence($Excerpt)
+ {
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+ {
+ return array(
+ 'markup' => $Excerpt['text'][1],
+ 'extent' => 2,
+ );
+ }
+ }
+
+ protected function inlineImage($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
+ {
+ return;
+ }
+
+ $Excerpt['text']= substr($Excerpt['text'], 1);
+
+ $Link = $this->inlineLink($Excerpt);
+
+ if ($Link === null)
+ {
+ return;
+ }
+
+ $Inline = array(
+ 'extent' => $Link['extent'] + 1,
+ 'element' => array(
+ 'name' => 'img',
+ 'attributes' => array(
+ 'src' => $Link['element']['attributes']['href'],
+ 'alt' => $Link['element']['text'],
+ ),
+ ),
+ );
+
+ $Inline['element']['attributes'] += $Link['element']['attributes'];
+
+ unset($Inline['element']['attributes']['href']);
+
+ return $Inline;
+ }
+
+ protected function inlineLink($Excerpt)
+ {
+ $Element = array(
+ 'name' => 'a',
+ 'handler' => 'line',
+ 'nonNestables' => array('Url', 'Link'),
+ 'text' => null,
+ 'attributes' => array(
+ 'href' => null,
+ 'title' => null,
+ ),
+ );
+
+ $extent = 0;
+
+ $remainder = $Excerpt['text'];
+
+ if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
+ {
+ $Element['text'] = $matches[1];
+
+ $extent += strlen($matches[0]);
+
+ $remainder = substr($remainder, $extent);
+ }
+ else
+ {
+ return;
+ }
+
+ if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
+ {
+ $Element['attributes']['href'] = $matches[1];
+
+ if (isset($matches[2]))
+ {
+ $Element['attributes']['title'] = substr($matches[2], 1, - 1);
+ }
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
+ {
+ $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
+ $definition = strtolower($definition);
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ $definition = strtolower($Element['text']);
+ }
+
+ if ( ! isset($this->DefinitionData['Reference'][$definition]))
+ {
+ return;
+ }
+
+ $Definition = $this->DefinitionData['Reference'][$definition];
+
+ $Element['attributes']['href'] = $Definition['url'];
+ $Element['attributes']['title'] = $Definition['title'];
+ }
+
+ return array(
+ 'extent' => $extent,
+ 'element' => $Element,
+ );
+ }
+
+ protected function inlineMarkup($Excerpt)
+ {
+ if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
+ {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+ }
+
+ protected function inlineSpecialCharacter($Excerpt)
+ {
+ if ($Excerpt['text'][0] === '&' and ! preg_match('/^?\w+;/', $Excerpt['text']))
+ {
+ return array(
+ 'markup' => '&',
+ 'extent' => 1,
+ );
+ }
+
+ $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
+
+ if (isset($SpecialCharacter[$Excerpt['text'][0]]))
+ {
+ return array(
+ 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
+ 'extent' => 1,
+ );
+ }
+ }
+
+ protected function inlineStrikethrough($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'del',
+ 'text' => $matches[1],
+ 'handler' => 'line',
+ ),
+ );
+ }
+ }
+
+ protected function inlineUrl($Excerpt)
+ {
+ if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
+ {
+ return;
+ }
+
+ if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
+ {
+ $url = $matches[0][0];
+
+ $Inline = array(
+ 'extent' => strlen($matches[0][0]),
+ 'position' => $matches[0][1],
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+
+ return $Inline;
+ }
+ }
+
+ protected function inlineUrlTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
+ {
+ $url = $matches[1];
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ # ~
+
+ protected function unmarkedText($text)
+ {
+ if ($this->breaksEnabled)
+ {
+ $text = preg_replace('/[ ]*\n/', "
\n", $text);
+ }
+ else
+ {
+ $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text);
+ $text = str_replace(" \n", "\n", $text);
+ }
+
+ return $text;
+ }
+
+ #
+ # Handlers
+ #
+
+ protected function element(array $Element)
+ {
+ if ($this->safeMode)
+ {
+ $Element = $this->sanitiseElement($Element);
+ }
+
+ $markup = '<'.$Element['name'];
+
+ if (isset($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $name => $value)
+ {
+ if ($value === null)
+ {
+ continue;
+ }
+
+ $markup .= ' '.$name.'="'.self::escape($value).'"';
+ }
+ }
+
+ if (isset($Element['text']))
+ {
+ $markup .= '>';
+
+ if (!isset($Element['nonNestables']))
+ {
+ $Element['nonNestables'] = array();
+ }
+
+ if (isset($Element['handler']))
+ {
+ $markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
+ }
+ else
+ {
+ $markup .= self::escape($Element['text'], true);
+ }
+
+ $markup .= ''.$Element['name'].'>';
+ }
+ else
+ {
+ $markup .= ' />';
+ }
+
+ return $markup;
+ }
+
+ protected function elements(array $Elements)
+ {
+ $markup = '';
+
+ foreach ($Elements as $Element)
+ {
+ $markup .= "\n" . $this->element($Element);
+ }
+
+ $markup .= "\n";
+
+ return $markup;
+ }
+
+ # ~
+
+ protected function li($lines)
+ {
+ $markup = $this->lines($lines);
+
+ $trimmedMarkup = trim($markup);
+
+ if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '')
+ {
+ $markup = $trimmedMarkup;
+ $markup = substr($markup, 3);
+
+ $position = strpos($markup, "
");
+
+ $markup = substr_replace($markup, '', $position, 4);
+ }
+
+ return $markup;
+ }
+
+ #
+ # Deprecated Methods
+ #
+
+ function parse($text)
+ {
+ $markup = $this->text($text);
+
+ return $markup;
+ }
+
+ protected function sanitiseElement(array $Element)
+ {
+ static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
+ static $safeUrlNameToAtt = array(
+ 'a' => 'href',
+ 'img' => 'src',
+ );
+
+ if (isset($safeUrlNameToAtt[$Element['name']]))
+ {
+ $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
+ }
+
+ if ( ! empty($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $att => $val)
+ {
+ # filter out badly parsed attribute
+ if ( ! preg_match($goodAttribute, $att))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ # dump onevent attribute
+ elseif (self::striAtStart($att, 'on'))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ }
+ }
+
+ return $Element;
+ }
+
+ protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
+ {
+ foreach ($this->safeLinksWhitelist as $scheme)
+ {
+ if (self::striAtStart($Element['attributes'][$attribute], $scheme))
+ {
+ return $Element;
+ }
+ }
+
+ $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
+
+ return $Element;
+ }
+
+ #
+ # Static Methods
+ #
+
+ protected static function escape($text, $allowQuotes = false)
+ {
+ return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
+ }
+
+ protected static function striAtStart($string, $needle)
+ {
+ $len = strlen($needle);
+
+ if ($len > strlen($string))
+ {
+ return false;
+ }
+ else
+ {
+ return strtolower(substr($string, 0, $len)) === strtolower($needle);
+ }
+ }
+
+ static function instance($name = 'default')
+ {
+ if (isset(self::$instances[$name]))
+ {
+ return self::$instances[$name];
+ }
+
+ $instance = new static();
+
+ self::$instances[$name] = $instance;
+
+ return $instance;
+ }
+
+ private static $instances = array();
+
+ #
+ # Fields
+ #
+
+ protected $DefinitionData;
+
+ #
+ # Read-Only
+
+ protected $specialCharacters = array(
+ '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
+ );
+
+ protected $StrongRegex = array(
+ '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
+ '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
+ );
+
+ protected $EmRegex = array(
+ '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+ '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
+ );
+
+ protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
+
+ protected $voidElements = array(
+ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
+ );
+
+ protected $textLevelElements = array(
+ 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
+ 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
+ 'i', 'rp', 'del', 'code', 'strike', 'marquee',
+ 'q', 'rt', 'ins', 'font', 'strong',
+ 's', 'tt', 'kbd', 'mark',
+ 'u', 'xm', 'sub', 'nobr',
+ 'sup', 'ruby',
+ 'var', 'span',
+ 'wbr', 'time',
+ );
+}
diff --git a/application/libraries/index.html b/application/libraries/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/libraries/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/logs/index.html b/application/logs/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/logs/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/models/Delete.php b/application/models/Delete.php
new file mode 100644
index 0000000..e69fcff
--- /dev/null
+++ b/application/models/Delete.php
@@ -0,0 +1,21 @@
+db->query($sql1);
+ $this->db->query($sql2);
+ }
+
+
+ }
+?>
\ No newline at end of file
diff --git a/application/models/Insert.php b/application/models/Insert.php
new file mode 100644
index 0000000..ba0bfbd
--- /dev/null
+++ b/application/models/Insert.php
@@ -0,0 +1,56 @@
+db->insert('images', $datas)){
+
+ //如果插入成功返回ID
+ return $this->db->insert_id();
+ }
+ else{
+ return false;
+ exit;
+
+ }
+ }
+ //插入到imginfo表
+ public function imginfo($datas){
+ if($this->db->insert('imginfo', $datas)){
+
+ //如果插入成功返回ID
+ return $this->db->insert_id();
+ }
+ else{
+ return false;
+ exit;
+ }
+ }
+ //插入安装的默认数据
+ public function default(){
+ //上传限制初始数据
+ // $uplimit_values = array(
+ // "max_size" => 5,
+ // "number" => 10
+ // );
+ // $uplimit_values = json_encode($uplimit_values);
+ // $uplimit = array(
+ // "name" => "uplimit",
+ // "values" => $uplimit_values,
+ // "switch" => "ON"
+ // );
+ // //插入数据
+ // $this->db->insert('options',$uplimit);
+
+ //图片压缩初始数据
+
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/models/Query.php b/application/models/Query.php
new file mode 100644
index 0000000..56d774a
--- /dev/null
+++ b/application/models/Query.php
@@ -0,0 +1,269 @@
+db->query($sql);
+
+
+ if($query){
+ $row = $query->row();
+ $domain = $row->domains;
+ //var_dump($domain);
+ return $domain;
+
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //查询是否重复
+ public function repeat($imgid){
+ $sql = "SELECT * FROM img_images WHERE `imgid` = '$imgid'";
+
+ $query = $this->db->query($sql);
+ if($query){
+ $row = $query->row();
+ //var_dump($domain);
+ return $row;
+ }
+ else{
+ return false;
+ }
+ }
+ //根据ID查询1张图片
+ public function onepic($imgid){
+ $sql = "SELECT * FROM img_images WHERE `imgid` = '$imgid'";
+
+ $query = $this->db->query($sql);
+ if($query){
+ $row = $query->row();
+ //var_dump($domain);
+ return $row;
+ }
+ else{
+ return false;
+ }
+ }
+ public function img($id){
+ $id = strip_tags($id);
+ $id = (int)$id;
+ $sql = "SELECT * FROM img_images WHERE `id` = '$id'";
+
+ $query = $this->db->query($sql);
+ if($query){
+ $row = $query->row();
+ //var_dump($domain);
+ return $row;
+ }
+ else{
+ return false;
+ }
+ }
+ //查询图片信息
+ public function imginfo($imgid){
+ $sql = "SELECT * FROM img_imginfo WHERE `imgid` = '$imgid'";
+
+ $query = $this->db->query($sql);
+ if($query){
+ $row = $query->row();
+ //var_dump($domain);
+ return $row;
+ }
+ else{
+ return false;
+ }
+ }
+ //查询用户信息
+ public function userinfo(){
+ $sql = "SELECT * FROM `img_options` WHERE `name` = 'userinfo' LIMIT 1";
+
+ $query = $this->db->query($sql);
+
+ if($query){
+ $row = $query->row();
+
+ return $row;
+ }
+ else{
+ return false;
+ }
+ }
+ //查询tinypng设置
+ public function tinypng(){
+ $sql = "SELECT * FROM `img_options` WHERE `name` = 'tinypng' LIMIT 1";
+ @$query = $this->db->query($sql);
+
+ if($query){
+ $row = $query->row();
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //查询站点信息
+ public function site_setting($type = ''){
+ $sql = "SELECT * FROM 'img_options' WHERE name = 'site_setting' LIMIT 1";
+ $query = $this->db->query($sql);
+
+ //如果类型为空,则返回完整对象
+ if($type == '') {
+ if($query){
+ $row = $query->row();
+
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ else{
+ if($query){
+ $row = $query->row();
+ $row = json_decode($row->values);
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+
+
+ }
+ //新版查询站点信息
+ public function siteinfo(){
+ $sql = "SELECT * FROM 'img_options' WHERE name = 'site_setting' LIMIT 1";
+ $query = $this->db->query($sql);
+
+ if($query){
+ $row = $query->row();
+ var_dump($row);
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //查询各种设置
+ public function option($name){
+ $sql = "SELECT * FROM 'img_options' WHERE name = '$name' LIMIT 1";
+ $query = $this->db->query($sql);
+
+ if($query){
+ $row = $query->row();
+
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //查询上传数量限制,传入参数IP
+ public function uplimit($ip){
+ //获取今天的日期
+ $date = date('Y-m-d',time());
+ $date = $date.'%';
+ //查询出今天上传的数量
+ $sql = "select count(*) num from img_images where `ip` = '$ip' AND `user` = 'visitor' AND `date` LIKE '$date'";
+ $query = $this->db->query($sql);
+ //获取用户已经上传的数量
+ $num = (int)$query->row()->num;
+ // var_dump($num);
+
+ // exit;
+ //查询系统限制的条数
+ $sql = "SELECT * FROM 'img_options' WHERE name = 'uplimit' LIMIT 1";
+ $query = $this->db->query($sql);
+ $limit = $query->row();
+ $limit = $limit->values;
+ $limit = json_decode($limit);
+ $limit = $limit->limit;
+
+ //进行判断
+ //上传达到限制了,返回FALSE
+ if($num >= $limit){
+ return FALSE;
+ }
+ else{
+ return TRUE;
+ }
+ }
+ //查询图片完整信息,用于探索发现,$num为要查询的图片数量
+ public function found($num){
+ //先写一个强大的SQL语句
+ $sql = "SELECT a.id,a.imgid,a.path,a.date,b.mime,b.width,b.height,b.views,b.ext,b.client_name FROM img_images AS a INNER JOIN img_imginfo AS b ON a.imgid = b.imgid AND a.user = 'visitor' AND a.level != 'adult' ORDER BY a.id DESC LIMIT $num";
+
+ $query = $this->db->query($sql);
+
+ $query = $query->result_array();
+ return $query;
+ }
+ //查询存储引擎
+ public function storage($name){
+ $sql = "SELECT * FROM `img_storage` WHERE `engine` = '$name' LIMIT 1";
+
+ $query = $this->db->query($sql);
+ if($query){
+ $row = $query->row();
+ return $row;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //统计数量
+ public function count_num($type){
+ switch ($type) {
+ case 'admin':
+ $sql = "SELECT count(*) AS num FROM `img_images` WHERE `user` = 'admin'";
+ break;
+ case 'visitor':
+ $sql = "SELECT count(*) AS num FROM `img_images` WHERE `user` = 'visitor'";
+ break;
+ case 'dubious':
+ $sql = "SELECT count(*) AS num FROM `img_images` WHERE `level` = 'adult'";
+ break;
+ case 'day':
+ $sql = "SELECT count(*) AS num FROM `img_images` WHERE date LIKE date('now') || '%'";
+ break;
+ case 'month':
+ $sql = "SELECT count(*) AS num FROM `img_images` WHERE date LIKE strftime('%Y-%m','now') || '%'";
+ break;
+ default:
+ # code...
+ break;
+ }
+ $query = $this->db->query($sql);
+ $row = $query->row();
+ return $row;
+ }
+ //查询单张图片信息
+ public function picinfo($imgid){
+ $sql = "SELECT a.id,a.ip,a.imgid,a.path,a.date,b.mime,b.width,b.height,b.views,b.ext,b.client_name FROM img_images AS a INNER JOIN img_imginfo AS b ON a.imgid = b.imgid AND b.imgid = '$imgid' LIMIT 1";
+
+ $query = $this->db->query($sql);
+
+ $query = $query->row();
+ return $query;
+ }
+ //根据img_images id查出图片信息
+ public function img_id($id){
+ $id = (int)$id;
+ //先获取img id
+ $sql = "SELECT a.*,b.mime,b.width,b.height,b.views,b.ext,b.client_name FROM img_images AS a INNER JOIN img_imginfo AS b ON a.id = $id AND a.imgid = b.imgid";
+ $imginfo = $this->db->query($sql)->row();
+
+
+ return $imginfo;
+
+ }
+ }
+?>
\ No newline at end of file
diff --git a/application/models/Update.php b/application/models/Update.php
new file mode 100644
index 0000000..4b90a12
--- /dev/null
+++ b/application/models/Update.php
@@ -0,0 +1,86 @@
+db->query($sql);
+ if($query){
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+ //更新图片压缩
+ public function compress($id){
+ $id = strip_tags($id);
+ $id = (int)$id;
+ $sql = "UPDATE img_images SET `compression` = 1";
+ $query = $this->db->query($sql);
+ if($query){
+ return TRUE;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //更新站点信息
+ public function site($name,$data){
+ $id = strip_tags($name);
+
+ $sql = "UPDATE img_options SET `values` = '$data' WHERE `name` = '$name'";
+ $query = $this->db->query($sql);
+ if($query){
+ return TRUE;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //更新tinypng设置
+ public function tinypng($values,$switch){
+ $sql = "UPDATE img_options SET `values` = '$values',`switch` = '$switch' WHERE `name` = 'tinypng'";
+
+ //echo $sql;
+
+ $query = $this->db->query($sql);
+ if($query){
+ return TRUE;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //更新moderate
+ public function moderate($values,$switch){
+ $sql = "UPDATE img_options SET `values` = '$values',`switch` = '$switch' WHERE `name` = 'moderate'";
+ $query = $this->db->query($sql);
+ if($query){
+ return TRUE;
+ }
+ else{
+ return FALSE;
+ }
+ }
+ //更新存储引擎
+ public function storage($data,$engine){
+ $this->db->where('engine', $engine);
+ $up = $this->db->update('storage', $data);
+
+ if($up){
+ return TRUE;
+ }
+ else{
+ return FALSE;
+ }
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/application/models/index.html b/application/models/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/models/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/third_party/index.html b/application/third_party/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/application/third_party/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/application/views/admin/footer.php b/application/views/admin/footer.php
new file mode 100644
index 0000000..9a20a23
--- /dev/null
+++ b/application/views/admin/footer.php
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+