diff --git a/.gitignore b/.gitignore index 2c6f9f6..0d9e230 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store htdocs/static/asset/* +htdocs/application/config/stikked.php +nbproject diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..b5e3aac --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,33 @@ +* Ben McRedmond https://github.com/benofsky (Original author; left stikked inactive) +* Claude Hohl https://github.com/claudehohl (Fixed some bugs, added some features; revived the project) +* Daniel https://github.com/lightswitch05 (Added LDAP authentication) +* Jens Willmer https://github.com/jwillmer (Bugfixing) +* Florian Bruhin http://www.the-compiler.org/ (Maintaining Arch Package: http://aur.archlinux.org/packages.php?ID=60575, small contributions and testing) +* Alexander https://github.com/sanek (Adding JSON-API) +* abma https://github.com/abma (.htaccess improvement) +* Chris https://github.com/ch0wnag3 (favicon URL improvement) +* Gabriel Wanzek https://github.com/GabrielWanzek (gabdark & gabdark3 themes) +* Luc https://github.com/ltribolet (Bootstrap theme) +* Stephan Bergemann https://github.com/oberling (German translation, Bootstrap multilang support + making bootstrap theme HTML5 valid) +* linuxman https://github.com/linuxman (Spanish translation) +* svartbergtroll https://github.com/svartbergtroll (French translation) +* ch0wnag3 https://github.com/ch0wnag3 (Improvement for .htaccess) +* Eric Renfro https://github.com/erenfro and Jhermans https://github.com/jhermans76 (Adjusted pastes for trending updates to be more database agnostic) +* ghost https://github.com/ghost (Turkish translation) +* PSantos10 https://github.com/PSantos10 (Portuguese translation) +* esplor https://github.com/esplor (Norwegian translation and support for table prefixes) +* RamadhanAmizudin https://github.com/RamadhanAmizudin (Fix local file inclusion vulnerability) +* Felix Wong https://github.com/fawong (created default stikked.php config) +* Jordy http://snowkat.nl/ (Snowkat theme) +* Jon Randoem https://github.com/jonrandoem (Stikkedizr theme, webfont support for themes, added QR codes in pastes) +* Team BlueRidge https://teamblueridge.org (Android app) +* Toda Hiroshi https://github.com/hiroshitoda (Japanese translation) +* John Maguire https://github.com/JohnMaguire and Michael Lustfield https://github.com/MTecknology (increased security for Nginx) +* MrC0mmand https://github.com/MrC0mmand (Added correct PostgreSQL queries) +* Petr Bena https://github.com/benapetr (stikkit - a command line tool for Stikked) +* Xiaoxing Ye https://github.com/Yexiaoxing (Chinese-Simplified translations) +* Elan Ruusamäe https://github.com/glensc (robots.txt and making valuable suggestions) +* BalzySte https://github.com/BalzySte (Beautified language name in bootstrap theme tables) +* Kondra007 https://github.com/Kondra007 (Russian translation) +* kiang https://github.com/kiang (Chinese-Traditional translation) +* Abbas A. Elmas https://github.com/abbaselmas (Intense testing and making valuable suggestions) diff --git a/CC0 b/CC0 new file mode 100644 index 0000000..bdfff7a --- /dev/null +++ b/CC0 @@ -0,0 +1,118 @@ + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT + PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT + CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES + THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO + WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION + OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES + RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR + WORKS PROVIDED HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically +confer exclusive Copyright and Related Rights (defined below) upon the +creator and subsequent owner(s) (each and all, an "owner") of an +original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work +for the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without +fear of later claims of infringement build upon, modify, incorporate +in other works, reuse and redistribute as freely as possible in any +form whatsoever and for any purposes, including without limitation +commercial purposes. These owners may contribute to the Commons to +promote the ideal of a free culture and the further production of +creative, cultural and scientific works, or to gain reputation or +greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or +she is an owner of Copyright and Related Rights in the Work, +voluntarily elects to apply CC0 to the Work and publicly distribute +the Work under its terms, with knowledge of his or her Copyright and +Related Rights in the Work and the meaning and intended legal effect +of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may +be protected by copyright and related or neighboring rights +("Copyright and Related Rights"). Copyright and Related Rights +include, but are not limited to, the following: + + the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; moral rights retained by the + original author(s) and/or performer(s); publicity and privacy + rights pertaining to a person's image or likeness depicted in a + Work; rights protecting against unfair competition in regards to a + Work, subject to the limitations in paragraph 4(a), below; rights + protecting the extraction, dissemination, use and reuse of data in + a Work; database rights (such as those arising under Directive + 96/9/EC of the European Parliament and of the Council of 11 March + 1996 on the legal protection of databases, and under any national + implementation thereof, including any amended or successor version + of such directive); and other similar, equivalent or corresponding + rights throughout the world based on applicable law or treaty, and + any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in +contravention of, applicable law, Affirmer hereby overtly, fully, +permanently, irrevocably and unconditionally waives, abandons, and +surrenders all of Affirmer's Copyright and Related Rights and +associated claims and causes of action, whether now known or unknown +(including existing as well as future claims and causes of action), in +the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"Waiver"). Affirmer makes the Waiver for the benefit of each member of +the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal +or equitable action to disrupt the quiet enjoyment of the Work by the +public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any +reason be judged legally invalid or ineffective under applicable law, +then the Waiver shall be preserved to the maximum extent permitted +taking into account Affirmer's express Statement of Purpose. In +addition, to the extent the Waiver is so judged Affirmer hereby grants +to each affected person a royalty-free, non transferable, non +sublicensable, non exclusive, irrevocable and unconditional license to +exercise Affirmer's Copyright and Related Rights in the Work (i) in +all territories worldwide, (ii) for the maximum duration provided by +applicable law or treaty (including future time extensions), (iii) in +any current or future medium and for any number of copies, and (iv) +for any purpose whatsoever, including without limitation commercial, +advertising or promotional purposes (the "License"). The License shall +be deemed effective as of the date CC0 was applied by Affirmer to the +Work. Should any part of the License for any reason be judged legally +invalid or ineffective under applicable law, such partial invalidity +or ineffectiveness shall not invalidate the remainder of the License, +and in such case Affirmer hereby affirms that he or she will not (i) +exercise any of his or her remaining Copyright and Related Rights in +the Work or (ii) assert any associated claims and causes of action +with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + No trademark or patent rights held by Affirmer are waived, + abandoned, surrendered, licensed or otherwise affected by this + document. Affirmer offers the Work as-is and makes no + representations or warranties of any kind concerning the Work, + express, implied, statutory or otherwise, including without + limitation warranties of title, merchantability, fitness for a + particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, + whether or not discoverable, all to the greatest extent + permissible under applicable law. Affirmer disclaims + responsibility for clearing rights of other persons that may apply + to the Work or any use thereof, including without limitation any + person's Copyright and Related Rights in the Work. Further, + Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. Affirmer understands and acknowledges that Creative Commons + is not a party to this document and has no duty or obligation with + respect to this CC0 or use of the Work. diff --git a/README.md b/README.md index c98e0fa..a36444c 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,167 @@ Stikked is an Open-Source PHP Pastebin, with the aim of keeping a simple and easy to use user interface. -Stikked allows you to easily share code with anyone you wish. Stikked was created for the following reasons: +Stikked allows you to easily share code with anyone you wish. Based on the [original Stikked](http://code.google.com/p/stikked/) with lots of bugfixes and improvements. -* IRC and Private Chats were spammed. -* Pastebins were ugly. -* Pastebins were confusing. -* Pastebins were messy and not thought through. -* Stikked rethought code collaboration, by making it easy to paste code. +Here are some features: -Based on the original Stikked (http://code.google.com/p/stikked/) with lots of bugfixes and improvements. +* Easy setup +* Syntax highlighting for many languages, including live syntax highlighting with CodeMirror +* Paste replies +* Diff view between the original paste and the reply +* An API +* Search pastes +* Trending pastes +* Encrypted pastes +* Burn on reading +* Anti-Spam features +* Themes support +* Multilanguage support +* An [Android app](https://play.google.com/store/apps/details?id=org.teamblueridge.pasteit) +* Command line tool to upload paste to Stikked based pastebins: [Stikkit](https://github.com/benapetr/stikkit) +* Another CLI tool requiring only curl program: [pbin](https://github.com/glensc/pbin) +* And many more. View [this review](http://maketecheasier.com/run-your-own-pastebin-with-stikked/2013/01/11) Try it out ---------- + http://paste.scratchbook.ch/ +See an encrypted paste: http://paste.scratchbook.ch/view/1427473f#iP7p05DRH0BC72qQjxv01BjUeOmNV073 + Installation ------------ -1. Download stikked from https://github.com/claudehohl/Stikked/downloads -2. Create a user and database for Stikked -3. Edit configuration settings in application/config/stikked.php - everything is described there -4. You're done! +1. Download stikked from https://github.com/claudehohl/Stikked/tags +2. Create a user and database for Stikked +3. Copy application/config/stikked.php.default to application/config/stikked.php +4. Edit configuration settings in application/config/stikked.php - everything is described there +5. You're done! * The database structure will be created automatically if it doesn't exist. * No special file permissions are needed by default. Optional: If you want to have the JavaScript- and CSS-files minified, the static/asset/ folder has to be writable. +* To ensure that pastes with an expiration set get cleaned up, define the cron key in the config and set up a cronjob, for example: + * */5 * * * * curl --silent http://yoursite.com/cron/[key] + + +Documentation +------------- + +In the folder doc/, you will find: + +* Webserver example configurations for Apache, Nginx, Lighttpd, Cherokee +* A troubleshooting guide +* How to create your own theme +* How to translate Stikked into your language +* How to contribute and improve Stikked Changelog --------- +### Version 0.9.0: + +* New translations: Japanese, Chinese-Simplified, Chinese-Traditional, Russian +* New themes: Stikkedizr, Cleanwhite +* Display QR code in paste +* Multiline highlighter +* Encrypted pastes (yeah!) - see it in action: http://paste.scratchbook.ch/view/1427473f#iP7p05DRH0BC72qQjxv01BjUeOmNV073 +* Added "burn on reading" as expiration +* Search function - search in recent and trending pastes +* Added mockingjay to word list for unknown posters - let the revolution begin! +* Bugfixes and improvements + +#### Upgrade instructions + +Copy your htdocs/application/stikked.php config file away. Upload the new version. Copy it back. + +### Version 0.8.6: + +* New translations: Portuguese, Norwegian, Turkish, French +* New theme: Snowkat +* YOURLS support (http://yourls.org/) +* There is now a stikked.php.dist. You may copy that to config.php and have your own settings +* The API has more possibilities, see API doc +* Captcha must be entered only once, no more for further pastes +* Bugfixes and improvements + +#### Upgrade instructions + +Copy your htdocs/application/stikked.php config file away. Upload the new version. Copy it back. + +### Version 0.8.5: + +* Themes! Configure a different theme in config/stikked.php - or create your own +* Multilanguage support. Configure a different language in config/stikked.php +* Diff view for paste replies! View differences between the original paste and its reply + * see it in action: http://paste.scratchbook.ch/view/de81a093/diff +* Possibility to set default expiration time +* Updated GeSHi to version 1.0.8.11 +* Updated CodeMirror to version 3.11 +* Lots of minor fixes and improvements +* Added guides for troubleshooting, development, translation and creating themes +* Added webserver example configurations +* Added reCaptcha integration for better antispam + +#### Upgrade instructions + +The following lines must be present config/stikked.php + +```php +$config['theme'] = 'default'; +``` + +You can choose between default, bootstrap, gabdark, gabdark3 and a fancy geocities theme ;) + +Create you own theme. See doc/CREATING_THEMES.md + +```php +$config['language'] = 'english'; +``` + +You can choose between english, german and swissgerman ;) + +Help translating Stikked into your language! See doc/TRANSLATING_STIKKED.md + +##### reCaptcha + +```php +$config['recaptcha_publickey'] = ''; +$config['recaptcha_privatekey'] = ''; +``` + +If these lines are filled, reCaptcha will be used. +Get a key from https://www.google.com/recaptcha/admin/create + +### Version 0.8.4: + +* Trending pastes: http://paste.scratchbook.ch/trends +* LDAP authentication (thanks to Daniel, https://github.com/lightswitch05) +* Blocked words; maintain a comma separated list in your config, e.g. '.es.tl, mycraft.com, yourbadword' - pastes with this words will never get pasted +* Spam trap for bots +* Bugfix: Remove\_invisible\_characters removing legitimate paste content (https://github.com/claudehohl/Stikked/issues/28) +* Possibility to manually set the paste's displayed URL (used with mod\_rewrite configurations) +* Print layout for pastes +* Updated to CodeIgniter version 2.1.3 + +### Version 0.8.3: + +* From now on, IPs get logged in the DB +* Added spamadmin: + * Enter credentials in config/stikked.php + * Visit /spamadmin, login + * Click on an IP to list all pastes belonging to it + * You can remove all the pastes listed, and optionally block the IP range +* Updated to CodeIgniter version 2.1.2 + +### Version 0.8.2: + +* Database optimizations: Pastes use less space (if you upgrade from a previous version, execute this SQL statement: "ALTER TABLE pastes DROP paste;" +* Anti spam features: + * Option to disable recent pastes + * Option to require the user to enter a captcha + ### Version 0.8.1: * Cleaner options @@ -38,7 +169,7 @@ Changelog * Simpler API response (non-JSON) * Favicon * gw.gd URL shortener (replaces bit.ly) -* minor fixes +* Minor fixes ### Version 0.8: @@ -77,4 +208,3 @@ Changelog * Fully standards compliant css and xhtml. * Random generating names for anonymous users * Paste downloading - diff --git a/doc/CREATING_THEMES.md b/doc/CREATING_THEMES.md new file mode 100644 index 0000000..5fa6183 --- /dev/null +++ b/doc/CREATING_THEMES.md @@ -0,0 +1,14 @@ +How to create your own theme +---------------------------- + +1. Make a copy of the folder htdocs/themes/default, and name it as you like. +2. Start customizing your files! +3. Delete everything that has not been changed. + +For example: If you've only modified the main.css, create a folder named "css" in your theme folder, and put your main.css in there. +The theme engine will load your css, and falls back to files in the default theme that aren't in your theme folder. + +Examples: + +* gabdark - a theme with only a modified main.css +* bootstrap - a full theme with custom html, css, js and images diff --git a/doc/DEVELOPMENT.md b/doc/DEVELOPMENT.md new file mode 100644 index 0000000..91c5333 --- /dev/null +++ b/doc/DEVELOPMENT.md @@ -0,0 +1,9 @@ +You want to improve Stikked? Always welcome! Send us your pull request and you will be honoured in AUTHORS.md. + +Some guidelines: + +* For PHP only files (not views/themes), please run the PHP Beautifier (http://pear.php.net/package/PHP_Beautifier) with the following parameters: +``` +php_beautifier --indent_tabs -l "IndentStyles(style=bsd) ArrayNested() NewLines(before=function:T_CLASS:if,after=T_COMMENT) ListClassFunction()" +``` +* Other people may modify your contribution. Don't take that personal; we all want to improve Stikked. Your input is always welcome! diff --git a/doc/TRANSLATING_STIKKED.md b/doc/TRANSLATING_STIKKED.md new file mode 100644 index 0000000..43474fc --- /dev/null +++ b/doc/TRANSLATING_STIKKED.md @@ -0,0 +1,7 @@ +How to translate Stikked into your own language +----------------------------------------------- + +1. Make a copy of the folder htdocs/application/language/swissgerman, and name it as you like. +2. Start placing in your texts! + +The date_lang.php and form_validation_lang.php are optional; they lay in the system folder for english fallback. stikked_lang.php is required. If you miss a translation, it will be shown as [tanslation_index] in Stikked. diff --git a/doc/TROUBLESHOOTING_GUIDE.md b/doc/TROUBLESHOOTING_GUIDE.md new file mode 100644 index 0000000..6e3130b --- /dev/null +++ b/doc/TROUBLESHOOTING_GUIDE.md @@ -0,0 +1,53 @@ +Stikked troubleshooting guide +----------------------------- + +### Apache + +#### 404 Not Found after creating a Paste + +Rewrite rules must be enabled in httpd.conf. + +Enable it by executing the following command: + +```a2enmod rewrite``` + +### Nginx + +#### 502 Bad Gateway + +PHP FastCGI must be running. Here's my /etc/init.d/php-fcgi config: + +```bash +#!/bin/bash + +FASTCGI_USER=www-data +FASTCGI_GROUP=www-data +ADDRESS=127.0.0.1 +PORT=9000 +PIDFILE=/var/run/php-fastcgi.pid +CHILDREN=6 +PHP5=/usr/bin/php5-cgi + +/usr/bin/spawn-fcgi -a $ADDRESS -p $PORT -P $PIDFILE -C $CHILDREN -u $FASTCGI_USER -g $FASTCGI_GROUP -f $PHP5 +``` + +You can adapt that to your system. + +### Lighttpd + +#### 503 - Service Not Available + +PHP FastCGI must be running. See the php-fcgi section under Nginx. + +### Cherokee + +### PHP + +#### The QR file isn't created and the image isn't showed + +You need to have the GD extension for PHP installed and enabled so that the QR codes are rendered. + +Still have a problem? +--------------------- + +Report an issue at GitHub, and we will add your problem to this guide. diff --git a/doc/webserver_sampleconfigs/apache-site.conf b/doc/webserver_sampleconfigs/apache-site.conf new file mode 100644 index 0000000..2da6137 --- /dev/null +++ b/doc/webserver_sampleconfigs/apache-site.conf @@ -0,0 +1,12 @@ + + ServerName stikked + DocumentRoot /var/www/stikked/htdocs + ErrorLog ${APACHE_LOG_DIR}/stikked-error_log + CustomLog ${APACHE_LOG_DIR}/stikked-access_log common + + Options +FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + + diff --git a/doc/webserver_sampleconfigs/cherokee.conf b/doc/webserver_sampleconfigs/cherokee.conf new file mode 100644 index 0000000..3244aa4 --- /dev/null +++ b/doc/webserver_sampleconfigs/cherokee.conf @@ -0,0 +1,325 @@ +config!version = 001002101 +server!bind!1!port = 80 +server!keepalive = 1 +server!keepalive_max_requests = 500 +server!panic_action = /opt/cherokee/bin/cherokee-panic +server!pid_file = /opt/cherokee/var/run/cherokee.pid +server!server_tokens = full +server!timeout = 15 +vserver!1!directory_index = index.html +vserver!1!document_root = /opt/cherokee/var/www +vserver!1!error_writer!filename = /opt/cherokee/var/log/cherokee.error +vserver!1!error_writer!type = file +vserver!1!logger = combined +vserver!1!logger!access!buffsize = 16384 +vserver!1!logger!access!filename = /opt/cherokee/var/log/cherokee.access +vserver!1!logger!access!type = file +vserver!1!nick = default +vserver!1!rule!5!encoder!gzip = allow +vserver!1!rule!5!handler = server_info +vserver!1!rule!5!handler!type = just_about +vserver!1!rule!5!match = directory +vserver!1!rule!5!match!directory = /about +vserver!1!rule!4!document_root = /opt/cherokee/lib/cgi-bin +vserver!1!rule!4!handler = cgi +vserver!1!rule!4!match = directory +vserver!1!rule!4!match!directory = /cgi-bin +vserver!1!rule!3!document_root = /opt/cherokee/share/cherokee/themes +vserver!1!rule!3!handler = file +vserver!1!rule!3!match = directory +vserver!1!rule!3!match!directory = /cherokee_themes +vserver!1!rule!2!document_root = /opt/cherokee/share/cherokee/icons +vserver!1!rule!2!handler = file +vserver!1!rule!2!match = directory +vserver!1!rule!2!match!directory = /icons +vserver!1!rule!1!handler = common +vserver!1!rule!1!handler!iocache = 1 +vserver!1!rule!1!match = default +vserver!2!directory_index = index.php,index.html +vserver!2!document_root = /opt/cherokee/var/www/stikked +vserver!2!error_writer!filename = /opt/cherokee/var/log/cherokee.error +vserver!2!error_writer!type = file +vserver!2!logger = combined +vserver!2!logger!access!buffsize = 16384 +vserver!2!logger!access!filename = /opt/cherokee/var/log/cherokee.access +vserver!2!logger!access!type = file +vserver!2!nick = stikked +vserver!2!rule!110!document_root = /opt/cherokee/var/www/stikked/static +vserver!2!rule!110!handler = file +vserver!2!rule!110!handler!iocache = 0 +vserver!2!rule!110!match = directory +vserver!2!rule!110!match!directory = /static +vserver!2!rule!110!match!final = 1 +vserver!2!rule!10!handler = fcgi +vserver!2!rule!10!handler!balancer = round_robin +vserver!2!rule!10!handler!balancer!source!10 = 1 +vserver!2!rule!10!handler!check_file = 1 +vserver!2!rule!10!handler!error_handler = 1 +vserver!2!rule!10!handler!pass_req_headers = 1 +vserver!2!rule!10!handler!script_alias = /opt/cherokee/var/www/stikked/index.php +vserver!2!rule!10!handler!x_real_ip_enabled = 0 +vserver!2!rule!10!handler!xsendfile = 0 +vserver!2!rule!10!match = default +source!1!env!PHP_FCGI_CHILDREN = 5 +source!1!env!PHP_FCGI_MAX_REQUESTS = 490 +source!1!env_inherited = 0 +source!1!host = 127.0.0.1:47990 +source!1!interpreter = /usr/bin/php-cgi -b 127.0.0.1:47990 +source!1!nick = PHP Interpreter +source!1!type = interpreter +icons!default = page_white.png +icons!directory = folder.png +icons!file!bomb.png = core +icons!file!page_white_go.png = *README* +icons!parent_directory = arrow_turn_left.png +icons!suffix!camera.png = jpg,jpeg,jpe +icons!suffix!cd.png = iso,ngr,cue +icons!suffix!color_wheel.png = png,gif,xcf,bmp,pcx,tiff,tif,cdr,psd,xpm,xbm +icons!suffix!control_play.png = bin,exe,com,msi,out +icons!suffix!css.png = css +icons!suffix!cup.png = java,class,jar +icons!suffix!email.png = eml,mbox,box,email,mbx +icons!suffix!film.png = avi,mpeg,mpe,mpg,mpeg3,dl,fli,qt,mov,movie,flv,webm +icons!suffix!font.png = ttf +icons!suffix!html.png = html,htm +icons!suffix!music.png = au,snd,mid,midi,kar,mpga,mpega,mp2,mp3,sid,wav,aif,aiff,aifc,gsm,m3u,wma,wax,ra,rm,ram,pls,sd2,ogg +icons!suffix!package.png = tar,gz,bz2,zip,rar,ace,lha,7z,dmg,cpk +icons!suffix!page_white_acrobat.png = pdf +icons!suffix!page_white_c.png = c,h,cpp +icons!suffix!page_white_office.png = doc,ppt,xls +icons!suffix!page_white_php.png = php +icons!suffix!page_white_text.png = txt,text,rtf,sdw +icons!suffix!printer.png = ps,eps +icons!suffix!ruby.png = rb +icons!suffix!script.png = sh,csh,ksh,tcl,tk,py,pl +mime!application/bzip2!extensions = bz2 +mime!application/gzip!extensions = gz +mime!application/hta!extensions = hta +mime!application/java-archive!extensions = jar +mime!application/java-serialized-object!extensions = ser +mime!application/java-vm!extensions = class +mime!application/json!extensions = json +mime!application/mac-binhex40!extensions = hqx +mime!application/msaccess!extensions = mdb +mime!application/msword!extensions = doc,dot +mime!application/octet-stream!extensions = bin +mime!application/octetstream!extensions = ace +mime!application/oda!extensions = oda +mime!application/ogg!extensions = ogx +mime!application/pdf!extensions = pdf +mime!application/pgp-keys!extensions = key +mime!application/pgp-signature!extensions = pgp +mime!application/pics-rules!extensions = prf +mime!application/postscript!extensions = ps,ai,eps +mime!application/rar!extensions = rar +mime!application/rdf+xml!extensions = rdf +mime!application/rss+xml!extensions = rss +mime!application/smil!extensions = smi,smil +mime!application/vnd.mozilla.xul+xml!extensions = xul +mime!application/vnd.ms-excel!extensions = xls,xlb,xlt +mime!application/vnd.ms-pki.seccat!extensions = cat +mime!application/vnd.ms-pki.stl!extensions = stl +mime!application/vnd.ms-powerpoint!extensions = ppt,pps +mime!application/vnd.oasis.opendocument.chart!extensions = odc +mime!application/vnd.oasis.opendocument.database!extensions = odb +mime!application/vnd.oasis.opendocument.formula!extensions = odf +mime!application/vnd.oasis.opendocument.graphics!extensions = odg +mime!application/vnd.oasis.opendocument.image!extensions = odi +mime!application/vnd.oasis.opendocument.presentation!extensions = odp +mime!application/vnd.oasis.opendocument.spreadsheet!extensions = ods +mime!application/vnd.oasis.opendocument.text!extensions = odt +mime!application/vnd.oasis.opendocument.text-master!extensions = odm +mime!application/vnd.oasis.opendocument.text-web!extensions = oth +mime!application/vnd.pkg5.info!extensions = p5i +mime!application/vnd.visio!extensions = vsd +mime!application/vnd.wap.wbxml!extensions = wbxml +mime!application/vnd.wap.wmlc!extensions = wmlc +mime!application/vnd.wap.wmlscriptc!extensions = wmlsc +mime!application/x-7z-compressed!extensions = 7z +mime!application/x-abiword!extensions = abw +mime!application/x-apple-diskimage!extensions = dmg +mime!application/x-bcpio!extensions = bcpio +mime!application/x-bittorrent!extensions = torrent +mime!application/x-cdf!extensions = cdf +mime!application/x-cpio!extensions = cpio +mime!application/x-csh!extensions = csh +mime!application/x-debian-package!extensions = deb,udeb +mime!application/x-director!extensions = dcr,dir,dxr +mime!application/x-dvi!extensions = dvi +mime!application/x-flac!extensions = flac +mime!application/x-font!extensions = pfa,pfb,gsf,pcf,pcf.Z +mime!application/x-freemind!extensions = mm +mime!application/x-gnumeric!extensions = gnumeric +mime!application/x-gtar!extensions = gtar,tgz,taz +mime!application/x-gzip!extensions = gz,tgz +mime!application/x-httpd-php!extensions = phtml,pht,php +mime!application/x-httpd-php-source!extensions = phps +mime!application/x-httpd-php3!extensions = php3 +mime!application/x-httpd-php3-preprocessed!extensions = php3p +mime!application/x-httpd-php4!extensions = php4 +mime!application/x-internet-signup!extensions = ins,isp +mime!application/x-iphone!extensions = iii +mime!application/x-iso9660-image!extensions = iso +mime!application/x-java-jnlp-file!extensions = jnlp +mime!application/x-javascript!extensions = js +mime!application/x-kchart!extensions = chrt +mime!application/x-killustrator!extensions = kil +mime!application/x-koan!extensions = skp,skd,skt,skm +mime!application/x-kpresenter!extensions = kpr,kpt +mime!application/x-kspread!extensions = ksp +mime!application/x-kword!extensions = kwd,kwt +mime!application/x-latex!extensions = latex +mime!application/x-lha!extensions = lha +mime!application/x-lzh!extensions = lzh +mime!application/x-lzx!extensions = lzx +mime!application/x-ms-wmd!extensions = wmd +mime!application/x-ms-wmz!extensions = wmz +mime!application/x-msdos-program!extensions = com,exe,bat,dll +mime!application/x-msi!extensions = msi +mime!application/x-netcdf!extensions = nc +mime!application/x-ns-proxy-autoconfig!extensions = pac +mime!application/x-nwc!extensions = nwc +mime!application/x-object!extensions = o +mime!application/x-oz-application!extensions = oza +mime!application/x-pkcs7-certreqresp!extensions = p7r +mime!application/x-pkcs7-crl!extensions = crl +mime!application/x-python-code!extensions = pyc,pyo +mime!application/x-quicktimeplayer!extensions = qtl +mime!application/x-redhat-package-manager!extensions = rpm +mime!application/x-sh!extensions = sh +mime!application/x-shar!extensions = shar +mime!application/x-shockwave-flash!extensions = swf,swfl +mime!application/x-stuffit!extensions = sit,sea +mime!application/x-sv4cpio!extensions = sv4cpio +mime!application/x-sv4crc!extensions = sv4crc +mime!application/x-tar!extensions = tar +mime!application/x-tcl!extensions = tcl +mime!application/x-tex-pk!extensions = pk +mime!application/x-texinfo!extensions = texinfo,texi +mime!application/x-trash!extensions = ~,bak,old,sik +mime!application/x-troff!extensions = t,tr,roff +mime!application/x-troff-man!extensions = man +mime!application/x-troff-me!extensions = me +mime!application/x-troff-ms!extensions = ms +mime!application/x-ustar!extensions = ustar +mime!application/x-x509-ca-cert!extensions = crt +mime!application/x-xcf!extensions = xcf +mime!application/x-xfig!extensions = fig +mime!application/x-xpinstall!extensions = xpi +mime!application/xhtml+xml!extensions = xhtml,xht +mime!application/xml!extensions = xml,xsl +mime!application/zip!extensions = zip +mime!audio/basic!extensions = au,snd +mime!audio/midi!extensions = mid,midi,kar +mime!audio/mpeg!extensions = mpga,mpega,mp2,mp3,m4a +mime!audio/ogg!extensions = ogg,oga +mime!audio/prs.sid!extensions = sid +mime!audio/x-aiff!extensions = aif,aiff,aifc +mime!audio/x-gsm!extensions = gsm +mime!audio/x-mpegurl!extensions = m3u +mime!audio/x-ms-wax!extensions = wax +mime!audio/x-ms-wma!extensions = wma +mime!audio/x-pn-realaudio!extensions = ra,rm,ram +mime!audio/x-realaudio!extensions = ra +mime!audio/x-scpls!extensions = pls +mime!audio/x-sd2!extensions = sd2 +mime!audio/x-wav!extensions = wav +mime!chemical/x-cache!extensions = cac,cache +mime!chemical/x-cache-csf!extensions = csf +mime!chemical/x-cdx!extensions = cdx +mime!chemical/x-cif!extensions = cif +mime!chemical/x-cmdf!extensions = cmdf +mime!chemical/x-cml!extensions = cml +mime!chemical/x-compass!extensions = cpa +mime!chemical/x-crossfire!extensions = bsd +mime!chemical/x-csml!extensions = csml,csm +mime!chemical/x-ctx!extensions = ctx +mime!chemical/x-cxf!extensions = cxf,cef +mime!chemical/x-isostar!extensions = istr,ist +mime!chemical/x-jcamp-dx!extensions = jdx,dx +mime!chemical/x-kinemage!extensions = kin +mime!chemical/x-pdb!extensions = pdb,ent +mime!chemical/x-swissprot!extensions = sw +mime!chemical/x-vamas-iso14976!extensions = vms +mime!chemical/x-vmd!extensions = vmd +mime!chemical/x-xtel!extensions = xtel +mime!chemical/x-xyz!extensions = xyz +mime!image/gif!extensions = gif +mime!image/jpeg!extensions = jpeg,jpg,jpe +mime!image/pcx!extensions = pcx +mime!image/png!extensions = png +mime!image/svg+xml!extensions = svg,svgz +mime!image/tiff!extensions = tiff,tif +mime!image/vnd.djvu!extensions = djvu,djv +mime!image/vnd.wap.wbmp!extensions = wbmp +mime!image/x-icon!extensions = ico +mime!image/x-ms-bmp!extensions = bmp +mime!image/x-photoshop!extensions = psd +mime!image/x-portable-anymap!extensions = pnm +mime!image/x-portable-bitmap!extensions = pbm +mime!image/x-portable-graymap!extensions = pgm +mime!image/x-portable-pixmap!extensions = ppm +mime!image/x-xbitmap!extensions = xbm +mime!image/x-xpixmap!extensions = xpm +mime!image/x-xwindowdump!extensions = xwd +mime!model/iges!extensions = igs,iges +mime!model/mesh!extensions = msh,mesh,silo +mime!model/vrml!extensions = wrl,vrml +mime!text/calendar!extensions = ics,icz +mime!text/comma-separated-values!extensions = csv +mime!text/css!extensions = css +mime!text/h323!extensions = 323 +mime!text/html!extensions = html,htm,shtml +mime!text/iuls!extensions = uls +mime!text/mathml!extensions = mml +mime!text/plain!extensions = asc,txt,text,diff,pot +mime!text/richtext!extensions = rtx +mime!text/rtf!extensions = rtf +mime!text/scriptlet!extensions = sct,wsc +mime!text/tab-separated-values!extensions = tsv +mime!text/vnd.sun.j2me.app-descriptor!extensions = jad +mime!text/vnd.wap.wml!extensions = wml +mime!text/vnd.wap.wmlscript!extensions = wmls +mime!text/x-boo!extensions = boo +mime!text/x-c++hdr!extensions = h++,hpp,hxx,hh +mime!text/x-c++src!extensions = c++,cpp,cxx,cc +mime!text/x-chdr!extensions = h +mime!text/x-csh!extensions = csh +mime!text/x-csrc!extensions = c +mime!text/x-dsrc!extensions = d +mime!text/x-haskell!extensions = hs +mime!text/x-java!extensions = java +mime!text/x-literate-haskell!extensions = lhs +mime!text/x-moc!extensions = moc +mime!text/x-pascal!extensions = p,pas +mime!text/x-pcs-gcd!extensions = gcd +mime!text/x-perl!extensions = pl,pm +mime!text/x-python!extensions = py +mime!text/x-setext!extensions = etx +mime!text/x-sh!extensions = sh +mime!text/x-tcl!extensions = tcl,tk +mime!text/x-tex!extensions = tex,ltx,sty,cls +mime!text/x-vcalendar!extensions = vcs +mime!text/x-vcard!extensions = vcf +mime!video/dl!extensions = dl +mime!video/dv!extensions = dif,dv +mime!video/fli!extensions = fli +mime!video/gl!extensions = gl +mime!video/mp4!extensions = mp4 +mime!video/mpeg!extensions = mpeg,mpg,mpe +mime!video/ogg!extensions = ogv +mime!video/quicktime!extensions = qt,mov +mime!video/vnd.mpegurl!extensions = mxu +mime!video/webm!extensions = webm +mime!video/x-flv!extensions = flv +mime!video/x-la-asf!extensions = lsf,lsx +mime!video/x-mng!extensions = mng +mime!video/x-ms-asf!extensions = asf,asx +mime!video/x-ms-wm!extensions = wm +mime!video/x-ms-wmv!extensions = wmv +mime!video/x-ms-wmx!extensions = wmx +mime!video/x-ms-wvx!extensions = wvx +mime!video/x-msvideo!extensions = avi +mime!video/x-sgi-movie!extensions = movie +mime!x-conference/x-cooltalk!extensions = ice +mime!x-world/x-vrml!extensions = vrm,vrml,wrl \ No newline at end of file diff --git a/doc/webserver_sampleconfigs/lighttpd-site.conf b/doc/webserver_sampleconfigs/lighttpd-site.conf new file mode 100644 index 0000000..677bf78 --- /dev/null +++ b/doc/webserver_sampleconfigs/lighttpd-site.conf @@ -0,0 +1,23 @@ +server.modules += ( "mod_fastcgi" ) +server.modules += ( "mod_rewrite" ) + +fastcgi.server += ( ".php" => + (( + "host" => "127.0.0.1", + "port" => "9000", + "broken-scriptfilename" => "enable" + )) +) + +$HTTP["host"] == "stikked" { + url.rewrite-once = ( + "^/static/(.*)$" => "/static/$1", + "^/favicon\.ico$" => "/favicon.ico", + "^/robots\.txt$" => "/robots.txt", + "^/(.*)$" => "/index.php$2", + ) + + server.document-root = "/var/www/stikked/htdocs" + + #accesslog.filename = "/var/log/lighttpd/stikked.access.log" +} diff --git a/doc/webserver_sampleconfigs/nginx-site.conf b/doc/webserver_sampleconfigs/nginx-site.conf new file mode 100644 index 0000000..5ebaf85 --- /dev/null +++ b/doc/webserver_sampleconfigs/nginx-site.conf @@ -0,0 +1,24 @@ +server { + server_name stikked.domain.tld; + root /var/www/stikked/htdocs; + index index.php; + +# access_log /var/log/nginx/localhost.access_log combined; +# error_log /var/log/nginx/localhost.error_log info; + + location / { + try_files $uri /index.php; + } + + location ~ \.php$ { + return 444; + } + + location = /index.php { + # fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; + fastcgi_pass 127.0.0.1:9000; + fastcgi_split_path_info ^(.+\.php)(.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } +} diff --git a/htdocs/.htaccess b/htdocs/.htaccess index 021ca24..2aa8fc5 100644 --- a/htdocs/.htaccess +++ b/htdocs/.htaccess @@ -1,25 +1,32 @@ RewriteEngine on #RewriteBase / -RewriteCond $1 !^(index\.php|static|favicon\.ico) +RewriteCond $1 !^(index\.php|static|favicon\.ico|robots\.txt) RewriteRule ^(.*)$ index.php?/$1 [QSA,L] SetOutputFilter DEFLATE FileETag MTime Size - + ErrorDocument 404 /index.php - + -ExpiresActive On -ExpiresByType text/javascript "access plus 1 year" -ExpiresByType application/x-javascript "access plus 1 year" -ExpiresByType application/javascript "access plus 1 year" -ExpiresByType text/css "access plus 1 year" -ExpiresByType image/jpeg "access plus 1 year" -ExpiresByType image/jpg "access plus 1 year" -ExpiresByType image/gif "access plus 1 year" -ExpiresByType image/png "access plus 1 year" -ExpiresByType image/x-icon "access plus 1 year" + + ExpiresActive On + ExpiresByType text/javascript "access plus 1 year" + ExpiresByType application/x-javascript "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType text/css "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/x-icon "access plus 1 year" + ExpiresByType application/font-woff "access plus 1 year" + ExpiresByType application/vnd.ms-fontobject "access plus 1 year" + ExpiresByType application/x-font-ttf "access plus 1 year" + ExpiresByType font/opentype "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + #AuthType Basic #AuthName "Backend" diff --git a/htdocs/application/.htaccess b/htdocs/application/.htaccess index 14249c5..3a42882 100644 --- a/htdocs/application/.htaccess +++ b/htdocs/application/.htaccess @@ -1 +1 @@ -Deny from all \ No newline at end of file +Deny from all diff --git a/htdocs/application/config/auth_ldap.php b/htdocs/application/config/auth_ldap.php new file mode 100644 index 0000000..d1b1c1d --- /dev/null +++ b/htdocs/application/config/auth_ldap.php @@ -0,0 +1,59 @@ +. + * + */ + +/** + * @author Greg Wojtak + * @copyright Copyright © 2010,2011 by Greg Wojtak + * @package Auth_Ldap + * @subpackage configuration + * @license GNU Lesser General Public License + */ + +/** + * Array Index - Usage + * hosts - Array of ldap servers to try to authenticate against + * ports - The remote port on the ldap server to connect to + * basedn - The base dn of your ldap data store + * login_attribute - LDAP attribute used to check usernames against + * proxy_user - Distinguised name of a proxy user if your LDAP server does not allow anonymous binds + * proxy pass - Password to use with above + * roles - An array of role names to use within your app. The values are arbitrary. + * The keys themselves represent the + * "security level," ie + * if( $security_level >= 3 ) { + * // Is a power user + * echo display_info_for_power_users_or_admins(); + * } + * member_attribute - Attribute to search to determine allowance after successful authentication + * auditlog - Location to log auditable events. Needs to be writeable + * by the web server + */ + +$config['hosts'] = array('ldap.mycompany.com'); +$config['ports'] = array(389); +$config['basedn'] = 'dc=mycompany,dc=com'; +$config['login_attribute'] = 'uid'; +$config['proxy_user'] = ''; +$config['proxy_pass'] = ''; +$config['roles'] = array(1 => 'User', + 3 => 'Power User', + 5 => 'Administrator'); +$config['member_attribute'] = 'memberUid'; +$config['auditlog'] = 'application/logs/audit.log'; // Some place to log attempted logins (separate from message log) +?> diff --git a/htdocs/application/config/autoload.php b/htdocs/application/config/autoload.php index b602db2..7431c5d 100644 --- a/htdocs/application/config/autoload.php +++ b/htdocs/application/config/autoload.php @@ -64,7 +64,7 @@ $autoload['libraries'] = array('database', 'db_session', 'carabiner'); | $autoload['helper'] = array('url', 'file'); */ -$autoload['helper'] = array('url', 'date'); +$autoload['helper'] = array('url', 'date', 'language'); /* @@ -96,7 +96,7 @@ $autoload['config'] = array('stikked'); | */ -$autoload['language'] = array(); +$autoload['language'] = array('stikked'); /* diff --git a/htdocs/application/config/codemirror_languages.php b/htdocs/application/config/codemirror_languages.php index 40d24c3..ea80c18 100644 --- a/htdocs/application/config/codemirror_languages.php +++ b/htdocs/application/config/codemirror_languages.php @@ -135,10 +135,10 @@ $config['codemirror_languages'] = array( ) , ) , 'sql' => array( - 'mode' => 'mysql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/mysql/mysql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -157,6 +157,22 @@ $config['codemirror_languages'] = array( '6502tasm' => 'MOS 6502 TASM/64TASS', '68000devpac' => 'Motorola 68000 Devpac Assembler', 'abap' => 'ABAP', + 'apl' => array( + 'mode' => 'apl', + 'js' => array( + array( + 'codemirror/mode/apl/apl.js' + ) , + ) , + ) , + 'asterisk' => array( + 'mode' => 'asterisk', + 'js' => array( + array( + 'codemirror/mode/asterisk/asterisk.js' + ) , + ) , + ) , 'actionscript' => 'Actionscript', 'actionscript3' => 'ActionScript3', 'ada' => 'Ada', @@ -221,7 +237,14 @@ $config['codemirror_languages'] = array( ) , ) , 'cuesheet' => 'Cuesheet', - 'd' => 'D', + 'd' => array( + 'mode' => 'd', + 'js' => array( + array( + 'codemirror/mode/d/d.js' + ) , + ) , + ) , 'dcs' => 'DCS', 'delphi' => 'Delphi (Object Pascal)', 'div' => 'DIV', @@ -239,6 +262,14 @@ $config['codemirror_languages'] = array( 'eiffel' => 'Eiffel', 'email' => 'Email (mbox/eml/RFC format)', 'epc' => 'Enerscript', + 'erlang' => array( + 'mode' => 'erlang', + 'js' => array( + array( + 'codemirror/mode/erlang/erlang.js' + ) , + ) , + ) , 'euphoria' => 'Euphoria', 'f1' => 'Formula One', 'falcon' => 'Falcon', @@ -254,6 +285,14 @@ $config['codemirror_languages'] = array( 'glsl' => 'glSlang', 'gml' => 'GML', 'gnuplot' => 'Gnuplot script', + 'go' => array( + 'mode' => 'go', + 'js' => array( + array( + 'codemirror/mode/go/go.js' + ) , + ) , + ) , 'groovy' => array( 'mode' => 'text/x-groovy', 'js' => array( @@ -271,6 +310,14 @@ $config['codemirror_languages'] = array( ) , ) , ) , + 'haxe' => array( + 'mode' => 'text/x-haxe', + 'js' => array( + array( + 'codemirror/mode/haxe/haxe.js' + ) , + ) , + ) , 'hicest' => 'HicEst', 'hq9plus' => 'HQ9+', 'html4strict' => array( @@ -340,17 +387,24 @@ $config['codemirror_languages'] = array( 'make' => 'Make', 'mapbasic' => 'MapBasic', 'matlab' => 'Matlab M-file', - 'mirc' => 'mIRC Scripting', + 'mirc' => array( + 'mode' => 'mirc', + 'js' => array( + array( + 'codemirror/mode/mirc/mirc.js' + ) , + ) , + ) , 'mmix' => 'MMIX Assembler', 'modula2' => 'Modula-2', 'modula3' => 'Modula-3', 'mpasm' => 'Microchip Assembler', 'mxml' => 'MXML', 'mysql' => array( - 'mode' => 'mysql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/mysql/mysql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -360,6 +414,14 @@ $config['codemirror_languages'] = array( 'objc' => 'Objective-C', 'objeck' => 'Objeck Programming Language', 'ocaml' => 'OCaml (Objective Caml)', + 'ocaml' => array( + 'mode' => 'text/x-ocaml', + 'js' => array( + array( + 'codemirror/mode/ocaml/ocaml.js' + ) , + ) , + ) , 'oobas' => 'OpenOffice.org Basic', 'oracle11' => 'Oracle 11i', 'oracle8' => 'Oracle 8', @@ -397,10 +459,10 @@ $config['codemirror_languages'] = array( 'pixelbender' => 'Pixel Bender 1.0', 'pli' => 'PL/I', 'plsql' => array( - 'mode' => 'plsql', + 'mode' => 'sql', 'js' => array( array( - 'codemirror/mode/plsql/plsql.js' + 'codemirror/mode/sql/sql.js' ) , ) , ) , @@ -421,7 +483,14 @@ $config['codemirror_languages'] = array( ) , 'providex' => 'ProvideX', 'purebasic' => 'PureBasic', - 'q' => 'q/kdb+', + 'q' => array( + 'mode' => 'text/x-q', + 'js' => array( + array( + 'codemirror/mode/q/q.js' + ) , + ) , + ) , 'qbasic' => 'QBasic/QuickBASIC', 'rails' => 'Ruby (with Ruby on Rails Framework)', 'rebol' => 'Rebol', @@ -430,7 +499,14 @@ $config['codemirror_languages'] = array( 'rpmspec' => 'RPM Spec', 'rsplus' => 'R', 'sas' => 'SAS', - 'scala' => 'Scala', + 'scala' => array( + 'mode' => 'scala', + 'js' => array( + array( + 'codemirror/mode/clike/clike.js' + ) , + ) , + ) , 'scheme' => array( 'mode' => 'text/x-scheme', 'js' => array( @@ -444,7 +520,14 @@ $config['codemirror_languages'] = array( 'smalltalk' => 'Smalltalk', 'smarty' => 'Smarty template', 'systemverilog' => 'SystemVerilog IEEE 1800-2009(draft8)', - 'tcl' => 'TCL/iTCL', + 'tcl' => array( + 'mode' => 'text/x-tcl', + 'js' => array( + array( + 'codemirror/mode/tcl/tcl.js' + ) , + ) , + ) , 'teraterm' => 'Tera Term Macro', 'thinbasic' => 'thinBasic', 'tsql' => 'T-SQL', @@ -453,18 +536,10 @@ $config['codemirror_languages'] = array( 'uscript' => 'UnrealScript', 'vala' => 'Vala', 'vb' => array( - 'mode' => 'text/vbscript', + 'mode' => 'text/x-vb', 'js' => array( array( - 'codemirror/mode/vbscript/vbscript.js' - ) , - ) , - ) , - 'vbnet' => array( - 'mode' => 'text/vbscript', - 'js' => array( - array( - 'codemirror/mode/vbscript/vbscript.js' + 'codemirror/mode/vb/vb.js' ) , ) , ) , @@ -494,6 +569,13 @@ $config['codemirror_languages'] = array( ) , ) , ) , - 'z80' => 'ZiLOG Z80 Assembler', + 'z80' => array( + 'mode' => 'text/x-z80', + 'js' => array( + array( + 'codemirror/mode/z80/z80.js' + ) , + ) , + ) , 'zxbasic' => 'ZXBasic', ); diff --git a/htdocs/application/config/config.php b/htdocs/application/config/config.php index 5a91023..9a1a123 100644 --- a/htdocs/application/config/config.php +++ b/htdocs/application/config/config.php @@ -16,6 +16,16 @@ */ $config['base_url'] = ''; +/* +|-------------------------------------------------------------------------- +| Stikked version +|-------------------------------------------------------------------------- +| +| So that we can track your version. +| +*/ +$config['stikked_version'] = '0.9.0'; + /* |-------------------------------------------------------------------------- | Index File @@ -69,7 +79,7 @@ $config['url_suffix'] = ''; | than english. | */ -$config['language'] = 'english'; +#$config['language'] = 'english'; #this is configured in config/stikked.php /* |-------------------------------------------------------------------------- @@ -244,8 +254,8 @@ $config['encryption_key'] = ''; | 'sess_time_to_update' = how many seconds between CI refreshing Session Information | */ -$config['sess_cookie_name'] = 'ci_session'; -$config['sess_expiration'] = 7200; +$config['sess_cookie_name'] = 'stikked'; +$config['sess_expiration'] = 60*60*24*1; $config['sess_expire_on_close'] = FALSE; $config['sess_encrypt_cookie'] = FALSE; $config['sess_use_database'] = TRUE; diff --git a/htdocs/application/config/database.php b/htdocs/application/config/database.php index af6c2fa..b563046 100644 --- a/htdocs/application/config/database.php +++ b/htdocs/application/config/database.php @@ -54,8 +54,8 @@ $db['default']['hostname'] = $CI->config->item('db_hostname'); $db['default']['username'] = $CI->config->item('db_username'); $db['default']['password'] = $CI->config->item('db_password'); $db['default']['database'] = $CI->config->item('db_database'); -$db['default']['dbdriver'] = 'mysql'; -$db['default']['dbprefix'] = ''; +$db['default']['dbdriver'] = 'mysqli'; +$db['default']['dbprefix'] = $CI->config->item('db_prefix'); $db['default']['pconnect'] = TRUE; $db['default']['db_debug'] = TRUE; $db['default']['cache_on'] = FALSE; diff --git a/htdocs/application/config/geshi_languages.php b/htdocs/application/config/geshi_languages.php index c39c58d..c851f75 100644 --- a/htdocs/application/config/geshi_languages.php +++ b/htdocs/application/config/geshi_languages.php @@ -9,6 +9,7 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); //geshi languages $config['geshi_languages'] = array( + 'text' => 'Plain Text', 'html5' => 'HTML5', 'css' => 'CSS', 'javascript' => 'JavaScript', @@ -25,7 +26,7 @@ $config['geshi_languages'] = array( 'latex' => 'LaTeX', 'sql' => 'SQL', 'xml' => 'XML', - 'text' => 'Plain Text', + '' => '', // separator '4cs' => '4CS', '6502acme' => 'MOS 6502', '6502kickass' => 'MOS 6502 Kick Assembler', @@ -39,7 +40,9 @@ $config['geshi_languages'] = array( 'apache' => 'Apache', 'applescript' => 'AppleScript', 'apt_sources' => 'Apt sources.list', + 'arm' => 'ARM Assembler', 'asm' => 'x86 Assembler', + 'asymptote' => 'asymptote', 'asp' => 'ASP', 'autoconf' => 'autoconf', 'autohotkey' => 'Autohotkey', @@ -69,6 +72,8 @@ $config['geshi_languages'] = array( 'cuesheet' => 'Cuesheet', 'd' => 'D', 'dcs' => 'DCS', + 'dcl' => 'DCL', + 'dcpu16' => 'DCPU/16 Assembly', 'delphi' => 'Delphi (Object Pascal)', 'div' => 'DIV', 'dos' => 'DOS', @@ -84,6 +89,7 @@ $config['geshi_languages'] = array( 'fo' => 'fo', 'fortran' => 'Fortran', 'freebasic' => 'FreeBasic', + 'freeswitch' => 'FreeSWITCH', 'fsharp' => 'F#', 'gambas' => 'GAMBAS', 'gdb' => 'GDB', @@ -96,6 +102,7 @@ $config['geshi_languages'] = array( 'groovy' => 'Groovy', 'gwbasic' => 'GwBasic', 'haskell' => 'Haskell', + 'haxe' => 'Haxe', 'hicest' => 'HicEst', 'hq9plus' => 'HQ9+', 'html4strict' => 'HTML 4.01 strict', @@ -114,9 +121,11 @@ $config['geshi_languages'] = array( 'klonec' => 'KLone with C', 'klonecpp' => 'KLone with C++', 'lb' => 'Liberty BASIC', + 'ldif' => 'LDIF', 'lisp' => 'Generic Lisp', 'llvm' => 'LLVM', 'locobasic' => 'Locomotive Basic (Amstrad CPC series)', + 'logcat' => 'Logcat', 'logtalk' => 'Logtalk', 'lolcode' => 'LOLcode', 'lotusformulas' => '@Formula/@Command', @@ -135,17 +144,23 @@ $config['geshi_languages'] = array( 'mpasm' => 'Microchip Assembler', 'mxml' => 'MXML', 'mysql' => 'MySQL', + 'nagios' => 'Nagios', + 'netrexx' => 'NetRexx', 'newlisp' => 'newLISP', 'nsis' => 'Nullsoft Scriptable Install System', 'oberon2' => 'Oberon-2', 'objc' => 'Objective-C', 'objeck' => 'Objeck Programming Language', 'ocaml' => 'OCaml (Objective Caml)', + 'octave' => 'GNU Octave M-file', 'oobas' => 'OpenOffice.org Basic', + 'oorexx' => 'ooRexx', 'oracle11' => 'Oracle 11i', 'oracle8' => 'Oracle 8', 'oxygene' => 'Delphi Prism (Oxygene)', 'oz' => 'Oz', + 'parasail' => 'ParaSail', + 'parigp' => 'PARI/GP', 'pascal' => 'Pascal', 'pcre' => 'PCRE', 'per' => 'Per (forms)', @@ -167,11 +182,13 @@ $config['geshi_languages'] = array( 'properties' => 'Property', 'providex' => 'ProvideX', 'purebasic' => 'PureBasic', + 'pys60' => 'Python for S60', 'q' => 'q/kdb+', 'qbasic' => 'QBasic/QuickBASIC', 'rails' => 'Ruby (with Ruby on Rails Framework)', 'rebol' => 'Rebol', 'reg' => 'Microsoft Registry Editor', + 'rexx' => 'Rexx', 'robots' => 'robots.txt', 'rpmspec' => 'RPM Spec', 'rsplus' => 'R', @@ -182,6 +199,9 @@ $config['geshi_languages'] = array( 'sdlbasic' => 'sdlBasic', 'smalltalk' => 'Smalltalk', 'smarty' => 'Smarty template', + 'spark' => 'SPARK', + 'sparql' => 'SPARQL', + 'stonescript' => 'StoneScript', 'systemverilog' => 'SystemVerilog IEEE 1800-2009(draft8)', 'tcl' => 'TCL/iTCL', 'teraterm' => 'Tera Term Macro', @@ -190,9 +210,12 @@ $config['geshi_languages'] = array( 'typoscript' => 'TypoScript', 'unicon' => 'Unicon', 'uscript' => 'UnrealScript', + 'upc' => 'UPC', + 'urbi' => 'Urbi', 'vala' => 'Vala', 'vb' => 'Visual Basic', 'vbnet' => 'VB.NET', + 'vedit' => 'Vedit macro language', 'verilog' => 'Verilog', 'vhdl' => 'VHDL', 'vim' => 'Vim scripting', diff --git a/htdocs/application/config/mimes.php b/htdocs/application/config/mimes.php index f00e5b6..100f7d4 100644 --- a/htdocs/application/config/mimes.php +++ b/htdocs/application/config/mimes.php @@ -93,8 +93,8 @@ $mimes = array( 'hqx' => 'application/mac-binhex40', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'), 'word' => array('application/msword', 'application/octet-stream'), 'xl' => 'application/excel', 'eml' => 'message/rfc822', diff --git a/htdocs/application/config/routes.php b/htdocs/application/config/routes.php index f007610..1dac8f0 100644 --- a/htdocs/application/config/routes.php +++ b/htdocs/application/config/routes.php @@ -42,21 +42,38 @@ $route['default_controller'] = "main"; $route['scaffolding_trigger'] = ""; $route['cron/:any'] = "main/cron"; +$route['post_encrypted'] = "main/post_encrypted"; $route['view/raw/:any'] = 'main/raw'; $route['view/rss/:any'] = 'main/rss'; $route['view/embed/:any'] = 'main/embed'; +$route['view/qr/:any'] = 'main/qr'; $route['view/download/:any'] = 'main/download'; +$route['view/captcha'] = 'main/captcha'; $route['view/:any'] = 'main/view'; $route['lists'] = 'main/lists'; $route['lists/rss'] = 'main/lists/rss'; $route['lists/:num'] = 'main/lists/$1'; +$route['trends'] = 'main/trends'; +$route['trends/:any'] = 'main/trends/$1'; +$route['spamadmin/:num'] = 'spamadmin/index'; +$route['spamadmin/blacklist'] = 'spamadmin/blacklist'; +$route['spamadmin/blacklist/(:num)'] = 'spamadmin/blacklist'; +$route['spamadmin/blacklist/unblock/(:any)'] = 'spamadmin/unblock_ip'; +$route['spamadmin/:any'] = 'spamadmin/spam_detail'; $route['about'] = 'main/about'; $route['iphone/:num'] = 'iphone'; $route['iphone/view/:any'] = 'iphone/view'; $route['404_override'] = 'main/error_404'; +$route['robots.txt'] = 'main/robots_txt'; + +$route['themes/:any/css/:any'] = 'theme_assets/css'; +$route['themes/:any/images/:any'] = 'theme_assets/images'; +$route['themes/:any/img/:any'] = 'theme_assets/images'; +$route['themes/:any/js/:any'] = 'theme_assets/js'; +$route['themes/:any/fonts/:any'] = 'theme_assets/fonts'; /* End of file routes.php */ /* Location: ./application/config/routes.php */ diff --git a/htdocs/application/config/stikked.php b/htdocs/application/config/stikked.php.dist old mode 100755 new mode 100644 similarity index 51% rename from htdocs/application/config/stikked.php rename to htdocs/application/config/stikked.php.dist index 455eb3f..71cd465 --- a/htdocs/application/config/stikked.php +++ b/htdocs/application/config/stikked.php.dist @@ -2,7 +2,7 @@ /** * Site Name - * + * * The name of your site * */ @@ -10,7 +10,7 @@ $config['site_name'] = 'Stikked'; /** * Database connection - * + * * Credentials for your database * The database structure will be created automatically * @@ -21,8 +21,37 @@ $config['db_username'] = 'stikked'; $config['db_password'] = 'stikked'; /** - * Combine JS & CSS files - * + * Table prefix + * Generate table prefix for stikked db, commonly used if the webhoster only has one db. + * Use underscore as suffix to easily see the tables. + * example: $config['db_prefix'] = 'stikked_'; + * use $config['db_prefix'] = ''; if you don't want to use table prefix. +*/ +$config['db_prefix'] = ''; + +/** + * Theme + * + * Which theme to use + * Folder name in htdocs/themes/ + * Currently: default, bootstrap, gabdark, gabdark3, geocities, snowkat, stikkedizr, cleanwhite + * +*/ +$config['theme'] = 'default'; + +/** + * Language + * + * Which language to use + * Translate Stikked to your own language, see htdocs/application/language files + * Currently: english, german, swissgerman, spanish, norwegian, portuguese, turkish, french, japanese + * +*/ +$config['language'] = 'english'; + +/** + * Combine JS & CSS files (recommended) + * * htdocs/static/asset/ folder must be writeable * */ @@ -31,20 +60,33 @@ $config['combine_assets'] = false; /** * Key for Cron * - * The password required to run the cron job - * Example cron: 00, 30 * * * * curl --silent --compressed http://yoursite.com/cron/key &> /dev/null - * -**/ + * The password required to run the cron job */ +// Example cron: */5 * * * * curl --silent http://yoursite.com/cron/[key] +// +// $config['cron_key'] = ''; /** - * gw.gd config + * url shortener config * - * Your own instance of the gw.gd URL-shortener (Download: https://github.com/neofutur/gwgd) + * If yourls_url is set, yourls is used instead of gw.gd + * + * yourls_url: Your own instance of yourls URL-shortener (Download: http://yourls.org/) + * Example: http://example.com/yourls/ + * + * yourls_signature: Your signature, used to authenticate API requests. + * You can find your signature under http://your-yourls-installation.com/admin/tools.php + * + * OR + * + * gwgd_url: Your own instance of the gw.gd URL-shortener (Download: https://github.com/neofutur/gwgd) * Default: http://gw.gd/ * **/ +$config['yourls_url'] = ''; +$config['yourls_signature'] = ''; $config['gwgd_url'] = ''; +$config['shorturl_selected'] = false; /** * Credentials for the backup URL @@ -61,15 +103,58 @@ $config['backup_pass'] = ''; * Number of pastes per page, on the recent pastes listings. * **/ -$config['per_page'] = 10; +$config['per_page'] = 15; /** - * Disable public pastes + * API key * - * No recent pastes will be displayed. + * Require a key to interact with the API. + * Append to all API requests: ?apikey=[yourkey] + * +**/ +$config['apikey'] = ''; + +/** + * Anti spam + * + * private_only: No recent pastes will be displayed. + * enable_captcha: Users must enter a captcha to post. + * recaptcha_publickey & recaptcha_privatekey: If filled, reCaptcha will be used (get a key from https://www.google.com/recaptcha/admin/create) + * disable_api: Don't allow pasting via API (because we can't use a captcha there...) + * disable_keep_forever: Don't allow pasting without expiration + * blocked_words: Comma separated list, e.g. '.es.tl, mycraft.com, yourbadword' + * disable_shorturl: "Create Shorturl" option will be disabled + * disallow_search_engines: displays a robots.txt that forbids indexing * **/ $config['private_only'] = false; +$config['enable_captcha'] = true; +$config['recaptcha_publickey'] = ''; +$config['recaptcha_privatekey'] = ''; +$config['disable_api'] = false; +$config['disable_keep_forever'] = false; +$config['blocked_words'] = ''; +$config['disable_shorturl'] = false; +$config['disallow_search_engines'] = false; + +//spamadmin: accessible via /spamadmin (only active when user + pass is set) +$config['spamadmin_user'] = ''; +$config['spamadmin_pass'] = ''; + +/** + * Default paste expiration time (minutes) + * + * Possible values: + * burn (burn on reading) + * 5 (5 minutes) + * 60 (1 hour) + * 1440 (1 day) + * 10080 (1 week) + * 40320 (1 month) + * 483840 (1 year) + * 0 (keep forever) +**/ +$config['default_expiration'] = 0; /** * Default language @@ -99,6 +184,25 @@ $config['unknown_poster'] = 'random'; **/ $config['unknown_title'] = 'Untitled'; +/** + * To require LDAP authentication or not. + * + * Weather to require LDAP authenticaiton or not. + * Set to either 'true' to require authentication or 'false' not to. + * NOTE: if changed, set LDAP settings in auth_ldap.php +**/ +$config['require_auth'] = false; + +/** + * Override the displayed URL + * + * Display this URL in a paste's detail view instead of the main URL - e.g. if you use mod_rewrite + * Variable $id: the paste_id + * Example: 'http://example.com/$id' + * +**/ +$config['displayurl_override'] = ''; + /** * * @@ -106,7 +210,6 @@ $config['unknown_title'] = 'Untitled'; * * **/ - $config['nouns'] = array('Hornbill', 'Elephant', 'Bison', 'Lion', 'Camel', 'Sheep', 'Monkey', 'Prairie Dog', 'Plover', 'Tapir', 'Capybara', 'Cheetah', 'Flamingo', 'Peccary', 'Eider', 'Porcupine', 'Pelican', 'Dove', 'Crane', 'Tortoise', 'Agouti', 'Tamarin', 'Pheasant', 'Owl', 'Gibbon', 'Goose', 'Baboon', 'Hamerkop', 'Zebra', @@ -120,7 +223,7 @@ $config['nouns'] = array('Hornbill', 'Elephant', 'Bison', 'Lion', 'Camel', 'Shee 'Pig', 'Anoa', 'Octupus', 'Butterfly', 'Cat', 'Kitten', 'Coyote', 'Crocodile', 'Cockroach', 'Crow', 'Bird', 'Dolphin', 'Earthworm', 'Frog', 'Hamster', 'Hedgehog', 'Hog', 'Human', 'Hummingbird', 'Iguana', 'Leech', 'Leopard', ' Marten', -'Mockingbird', 'Mosquito', 'Moth', 'Partdridge', 'Bee', 'Penguin'); +'Mockingbird', 'Mockingjay', 'Mosquito', 'Moth', 'Partdridge', 'Bee', 'Penguin'); $config['adjectives'] = array('Ample', 'Mature', 'Bulky', 'Burly', 'Capacious', 'Colossal', 'Commodious', 'Thundering', 'Mammoth', 'Mungo', 'Voluminous', diff --git a/htdocs/application/controllers/api.php b/htdocs/application/controllers/api.php old mode 100755 new mode 100644 index 47b8e5e..afe2be6 --- a/htdocs/application/controllers/api.php +++ b/htdocs/application/controllers/api.php @@ -5,6 +5,11 @@ * - __construct() * - index() * - create() + * - paste() + * - random() + * - recent() + * - trending() + * - langs() * Classes list: * - Api extends Main */ @@ -16,6 +21,16 @@ class Api extends Main function __construct() { parent::__construct(); + + if (config_item('disable_api')) + { + die("The API has been disabled\n"); + } + + if (config_item('apikey') != $this->input->get('apikey')) + { + die("Invalid API key\n"); + } } function index() @@ -30,6 +45,8 @@ class Api extends Main function create() { $this->load->model('pastes'); + $this->load->library('form_validation'); //needed by parent class + if (!$this->input->post('text')) { @@ -44,9 +61,118 @@ class Api extends Main $_POST['lang'] = 'text'; } $_POST['code'] = $this->input->post('text'); + + if ($this->config->item('private_only')) + { + $_POST['private'] = 1; + } + + //validations + + if (!$this->_valid_ip()) + { + die("You are not allowed to paste\n"); + } + + if (!$this->_blockwords_check()) + { + die("Your paste contains blocked words\n"); + } + + //create paste $paste_url = $this->pastes->createPaste(); $data['msg'] = base_url() . $paste_url; $this->load->view('view/api', $data); } } + + function paste() + { + + if (config_item('private_only')) + { + show_404(); + } + $this->load->model('pastes'); + $check = $this->pastes->checkPaste(3); + + if ($check) + { + $data = $this->pastes->getPaste(3); + } + else + { + $data = array( + 'message' => 'Not found', + ); + } + echo json_encode($data); + } + + function random() + { + + if (config_item('private_only')) + { + show_404(); + } + $this->load->model('pastes'); + $data = $this->pastes->random_paste(); + echo json_encode($data); + } + + function recent() + { + + if (config_item('private_only')) + { + show_404(); + } + $this->load->model('pastes'); + $pastes = $this->pastes->getLists(); + $pastes = $pastes['pastes']; + $data = array(); + foreach ($pastes as $paste) + { + $data[] = array( + 'pid' => $paste['pid'], + 'title' => $paste['title'], + 'name' => $paste['name'], + 'created' => $paste['created'], + 'lang' => $paste['lang'], + ); + } + echo json_encode($data); + } + + function trending() + { + + if (config_item('private_only')) + { + show_404(); + } + $this->load->model('pastes'); + $pastes = $this->pastes->getTrends(); + $pastes = $pastes['pastes']; + $data = array(); + foreach ($pastes as $paste) + { + $data[] = array( + 'pid' => $paste['pid'], + 'title' => $paste['title'], + 'name' => $paste['name'], + 'created' => $paste['created'], + 'lang' => $paste['lang'], + 'hits' => $paste['hits'], + ); + } + echo json_encode($data); + } + + function langs() + { + $languages = $this->languages->get_languages(); + echo json_encode($languages); + } } diff --git a/htdocs/application/controllers/auth.php b/htdocs/application/controllers/auth.php new file mode 100644 index 0000000..af46863 --- /dev/null +++ b/htdocs/application/controllers/auth.php @@ -0,0 +1,126 @@ +. + * +*/ +/** + * @author Greg Wojtak + * @copyright Copyright © 2010,2011 by Greg Wojtak + * @package Auth_Ldap + * @subpackage auth demo + * @license GNU Lesser General Public License + */ + +class Auth extends CI_Controller +{ + + function __construct() + { + parent::__construct(); + $this->load->helper('form'); + $this->load->library('Form_validation'); + $this->load->library('auth_ldap'); + $this->load->helper('url'); + $this->load->library('table'); + } + + function index() + { + $this->db_session->keep_flashdata('tried_to'); + $this->login(); + } + + function login($errorMsg = NULL) + { + $this->db_session->keep_flashdata('tried_to'); + + if (!$this->auth_ldap->is_authenticated()) + { + + // Set up rules for form validation + $rules = $this->form_validation; + $rules->set_rules('username', 'Username', 'required|callback_alpha_dash_dot'); + $rules->set_rules('password', 'Password', 'required'); + + // Do the login... + + if ($rules->run() && $this->auth_ldap->login($rules->set_value('username') , $rules->set_value('password'))) + { + + // Login WIN! + + if ($this->db_session->flashdata('tried_to')) + { + redirect($this->db_session->flashdata('tried_to')); + } + else + { + redirect('/'); + } + } + else + { + + // Login FAIL + $this->db_session->set_flashdata('login_error', 'Incorrect username or password.'); + $this->load->view('auth/login_form'); + } + } + else + { + + // Already logged in... + redirect('/'); + } + } + + function logout() + { + + if ($this->db_session->userdata('logged_in')) + { + $data['name'] = $this->db_session->userdata('cn'); + $data['username'] = $this->db_session->userdata('username'); + $data['logged_in'] = TRUE; + $this->auth_ldap->logout(); + } + else + { + $data['logged_in'] = FALSE; + } + redirect('/'); + } + public + function alpha_dash_dot($str) + { + return (!preg_match("/^([-a-z0-9_-\.])+$/i", $str)) ? FALSE : TRUE; + } +} +?> diff --git a/htdocs/application/controllers/backup.php b/htdocs/application/controllers/backup.php old mode 100755 new mode 100644 diff --git a/htdocs/application/controllers/index.html b/htdocs/application/controllers/index.html old mode 100755 new mode 100644 diff --git a/htdocs/application/controllers/iphone.php b/htdocs/application/controllers/iphone.php old mode 100755 new mode 100644 diff --git a/htdocs/application/controllers/main.php b/htdocs/application/controllers/main.php old mode 100755 new mode 100644 index 167fe07..cd92a6e --- a/htdocs/application/controllers/main.php +++ b/htdocs/application/controllers/main.php @@ -5,17 +5,30 @@ * - __construct() * - _form_prep() * - index() + * - post_encrypted() * - raw() * - rss() * - embed() + * - qr() * - download() * - lists() + * - trends() * - view() * - cron() * - about() + * - captcha() * - _valid_lang() + * - _valid_captcha() + * - _valid_recaptcha() + * - _valid_ip() + * - _valid_ipv4() + * - _valid_ipv6() + * - _blockwords_check() + * - _autofill_check() + * - _valid_authentication() * - get_cm_js() * - error_404() + * - robots_txt() * Classes list: * - Main extends CI_Controller */ @@ -26,8 +39,25 @@ class Main extends CI_Controller function __construct() { parent::__construct(); + $this->output->enable_profiler(false); $this->load->model('languages'); + if (config_item('require_auth')) + { + $this->load->library('auth_ldap'); + } + + //recaptcha + $this->recaptcha_publickey = config_item('recaptcha_publickey'); + $this->recaptcha_privatekey = config_item('recaptcha_privatekey'); + $this->use_recaptcha = false; + + if ($this->recaptcha_publickey != '' && $this->recaptcha_privatekey != '') + { + $this->load->helper('recaptcha'); + $this->use_recaptcha = true; + } + if (!$this->db->table_exists('ci_sessions')) { $this->load->dbforge(); @@ -39,7 +69,7 @@ class Main extends CI_Controller ) , 'ip_address' => array( 'type' => 'VARCHAR', - 'constraint' => 16, + 'constraint' => 45, 'default' => 0, ) , 'user_agent' => array( @@ -77,7 +107,7 @@ class Main extends CI_Controller ) , 'title' => array( 'type' => 'VARCHAR', - 'constraint' => 32, + 'constraint' => 50, ) , 'name' => array( 'type' => 'VARCHAR', @@ -118,6 +148,21 @@ class Main extends CI_Controller 'type' => 'VARCHAR', 'constraint' => 8, ) , + 'ip_address' => array( + 'type' => 'VARCHAR', + 'constraint' => 45, + 'null' => TRUE, + ) , + 'hits' => array( + 'type' => 'INT', + 'constraint' => 10, + 'default' => 0, + ) , + 'hits_updated' => array( + 'type' => 'INT', + 'constraint' => 10, + 'default' => 0, + ) , ); $this->dbforge->add_field($fields); $this->dbforge->add_key('id', true); @@ -125,8 +170,116 @@ class Main extends CI_Controller $this->dbforge->add_key('private'); $this->dbforge->add_key('replyto'); $this->dbforge->add_key('created'); + $this->dbforge->add_key('ip_address'); + $this->dbforge->add_key('hits'); + $this->dbforge->add_key('hits_updated'); $this->dbforge->create_table('pastes', true); } + + if (!$this->db->table_exists('blocked_ips')) + { + $this->load->dbforge(); + $fields = array( + 'ip_address' => array( + 'type' => 'VARCHAR', + 'constraint' => 45, + 'default' => 0, + ) , + 'blocked_at' => array( + 'type' => 'INT', + 'constraint' => 10, + ) , + 'spam_attempts' => array( + 'type' => 'INT', + 'constraint' => 6, + 'default' => 0, + ) , + ); + $this->dbforge->add_field($fields); + $this->dbforge->add_key('ip_address', true); + $this->dbforge->create_table('blocked_ips', true); + } + + if (!$this->db->table_exists('trending')) + { + $this->load->dbforge(); + $fields = array( + 'paste_id' => array( + 'type' => 'VARCHAR', + 'constraint' => 8, + ) , + 'ip_address' => array( + 'type' => 'VARCHAR', + 'constraint' => 45, + 'default' => 0, + ) , + 'created' => array( + 'type' => 'INT', + 'constraint' => 10, + ) , + ); + $this->dbforge->add_field($fields); + $this->dbforge->add_key('paste_id', true); + $this->dbforge->add_key('ip_address', true); + $this->dbforge->add_key('created'); + $this->dbforge->create_table('trending', true); + } + + if (!$this->db->field_exists('ip_address', 'pastes')) + { + $this->load->dbforge(); + $fields = array( + 'ip_address' => array( + 'type' => 'VARCHAR', + 'constraint' => 45, + 'null' => TRUE, + ) , + ); + $this->dbforge->add_column('pastes', $fields); + } + + if (!$this->db->field_exists('hits', 'pastes')) + { + $this->load->dbforge(); + $fields = array( + 'hits' => array( + 'type' => 'INT', + 'constraint' => 10, + 'default' => 0, + ) , + 'hits_updated' => array( + 'type' => 'INT', + 'constraint' => 10, + 'default' => 0, + ) , + ); + $this->dbforge->add_key('hits'); + $this->dbforge->add_key('hits_updated'); + $this->dbforge->add_column('pastes', $fields); + } + + //ipv6 migration + $fields = $this->db->field_data('trending'); + + if ($fields[1]->max_length < 45) + { + $db_prefix = config_item('db_prefix'); + + if ($this->db->dbdriver == "postgre") + { + $this->db->query("ALTER TABLE " . $db_prefix . "trending ALTER COLUMN ip_address TYPE VARCHAR(45), ALTER COLUMN ip_address SET NOT NULL, ALTER COLUMN ip_address SET DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "pastes ALTER COLUMN ip_address TYPE VARCHAR(45), ALTER COLUMN ip_address SET NOT NULL, ALTER COLUMN ip_address SET DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "blocked_ips ALTER COLUMN ip_address TYPE VARCHAR(45), ALTER COLUMN ip_address SET NOT NULL, ALTER COLUMN ip_address SET DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "ci_sessions ALTER COLUMN ip_address TYPE VARCHAR(45), ALTER COLUMN ip_address SET NOT NULL, ALTER COLUMN ip_address SET DEFAULT '0'"); + } + else + { + $this->db->query("ALTER TABLE " . $db_prefix . "trending CHANGE COLUMN ip_address ip_address VARCHAR(45) NOT NULL DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "pastes CHANGE COLUMN ip_address ip_address VARCHAR(45) NOT NULL DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "blocked_ips CHANGE COLUMN ip_address ip_address VARCHAR(45) NOT NULL DEFAULT '0'"); + $this->db->query("ALTER TABLE " . $db_prefix . "ci_sessions CHANGE COLUMN ip_address ip_address VARCHAR(45) NOT NULL DEFAULT '0'"); + } + } } function _form_prep($lang = false, $title = '', $paste = '', $reply = false) @@ -137,7 +290,7 @@ class Main extends CI_Controller //codemirror languages $this->load->config('codemirror_languages'); - $codemirror_languages = $this->config->item('codemirror_languages'); + $codemirror_languages = config_item('codemirror_languages'); $data['codemirror_languages'] = $codemirror_languages; //codemirror modes @@ -151,10 +304,26 @@ class Main extends CI_Controller } } $data['codemirror_modes'] = $cmm; + + //recaptcha + $data['use_recaptcha'] = $this->use_recaptcha; + $data['recaptcha_publickey'] = $this->recaptcha_publickey; if (!$this->input->post('submit')) { + if (!$this->db_session->userdata('expire')) + { + $default_expiration = config_item('default_expiration'); + $this->db_session->set_userdata('expire', $default_expiration); + } + + if (!$this->db_session->userdata('snipurl')) + { + $shorturl_selected = config_item('shorturl_selected'); + $this->db_session->set_userdata('snipurl', $shorturl_selected); + } + if ($this->db_session->flashdata('settings_changed')) { $data['status_message'] = 'Settings successfully changed'; @@ -169,7 +338,7 @@ class Main extends CI_Controller if (!$lang) { - $lang = $this->config->item('default_language'); + $lang = config_item('default_language'); } $data['lang_set'] = $lang; } @@ -179,7 +348,7 @@ class Main extends CI_Controller $data['expire_set'] = $this->input->post('expire'); $data['private_set'] = $this->input->post('private'); $data['snipurl_set'] = $this->input->post('snipurl'); - $data['paste_set'] = $this->input->post('paste'); + $data['paste_set'] = $this->input->post('code'); $data['title_set'] = $this->input->post('title'); $data['reply'] = $this->input->post('reply'); $data['lang_set'] = $this->input->post('lang'); @@ -189,6 +358,7 @@ class Main extends CI_Controller function index() { + $this->_valid_authentication(); $this->load->helper('json'); if (!$this->input->post('submit')) @@ -213,11 +383,31 @@ class Main extends CI_Controller 'label' => 'Language', 'rules' => 'min_length[1]|required|callback__valid_lang', ) , + array( + 'field' => 'captcha', + 'label' => 'Captcha', + 'rules' => 'callback__valid_captcha', + ) , + array( + 'field' => 'valid_ip', + 'label' => 'Valid IP', + 'rules' => 'callback__valid_ip', + ) , + array( + 'field' => 'blockwords_check', + 'label' => 'No blocked words', + 'rules' => 'callback__blockwords_check', + ) , + array( + 'field' => 'email', + 'label' => 'Field must remain empty', + 'rules' => 'callback__autofill_check', + ) , ); //form validation $this->form_validation->set_rules($rules); - $this->form_validation->set_message('min_length', 'The %s field can not be empty'); + $this->form_validation->set_message('min_length', lang('empty')); $this->form_validation->set_error_delimiters('
', '
'); if ($this->form_validation->run() == FALSE) @@ -228,6 +418,16 @@ class Main extends CI_Controller else { + if (config_item('private_only')) + { + $_POST['private'] = 1; + } + + if (config_item('disable_shorturl')) + { + $_POST['snipurl'] = 0; + } + if ($this->input->post('reply') == false) { $user_data = array( @@ -244,8 +444,18 @@ class Main extends CI_Controller } } + function post_encrypted() + { + $this->load->model('pastes'); + $_POST['private'] = 1; + $_POST['snipurl'] = 0; + $ret_url = $this->pastes->createPaste(); + echo $ret_url; + } + function raw() { + $this->_valid_authentication(); $this->load->model('pastes'); $check = $this->pastes->checkPaste(3); @@ -262,6 +472,7 @@ class Main extends CI_Controller function rss() { + $this->_valid_authentication(); $this->load->model('pastes'); $check = $this->pastes->checkPaste(3); @@ -270,7 +481,7 @@ class Main extends CI_Controller $this->load->helper('text'); $paste = $this->pastes->getPaste(3); $data = $this->pastes->getReplies(3); - $data['page_title'] = $paste['title'] . ' - ' . $this->config->item('site_name'); + $data['page_title'] = $paste['title'] . ' - ' . config_item('site_name'); $data['feed_url'] = site_url('view/rss/' . $this->uri->segment(3)); $this->load->view('view/rss', $data); } @@ -282,12 +493,13 @@ class Main extends CI_Controller function embed() { + $this->_valid_authentication(); $this->load->model('pastes'); $check = $this->pastes->checkPaste(3); if ($check) { - $data = $this->pastes->getPaste(3); + $data = $this->pastes->getPaste(3, true, $this->uri->segment(4) == 'diff'); $this->load->view('view/embed', $data); } else @@ -296,8 +508,16 @@ class Main extends CI_Controller } } + function qr() + { + $this->load->model('pastes'); + $data = $this->pastes->getPaste(3); + $this->load->view('view/qr', $data); + } + function download() { + $this->_valid_authentication(); $this->load->model('pastes'); $check = $this->pastes->checkPaste(3); @@ -314,20 +534,21 @@ class Main extends CI_Controller function lists() { + $this->_valid_authentication(); - if ($this->config->item('private_only')) + if (config_item('private_only')) { show_404(); } else { $this->load->model('pastes'); - $data = $this->pastes->getLists(); if ($this->uri->segment(2) == 'rss') { $this->load->helper('text'); - $data['page_title'] = $this->config->item('site_name'); + $data = $this->pastes->getLists('lists/', 3); + $data['page_title'] = config_item('site_name'); $data['feed_url'] = site_url('lists/rss'); $data['replies'] = $data['pastes']; unset($data['pastes']); @@ -335,13 +556,31 @@ class Main extends CI_Controller } else { + $data = $this->pastes->getLists('lists/', 2); $this->load->view('list', $data); } } } + function trends() + { + $this->_valid_authentication(); + + if (config_item('private_only')) + { + show_404(); + } + else + { + $this->load->model('pastes'); + $data = $this->pastes->getTrends(); + $this->load->view('trends', $data); + } + } + function view() { + $this->_valid_authentication(); $this->load->helper('json'); $this->load->model('pastes'); $check = $this->pastes->checkPaste(2); @@ -353,7 +592,7 @@ class Main extends CI_Controller { redirect('view/raw/' . $this->uri->segment(2)); } - $data = $this->pastes->getPaste(2, true); + $data = $this->pastes->getPaste(2, true, $this->uri->segment(3) == 'diff'); $data['reply_form'] = $this->_form_prep($data['lang_code'], 'Re: ' . $data['title'], $data['raw'], $data['pid']); $this->load->view('view/view', $data); } @@ -368,7 +607,7 @@ class Main extends CI_Controller $this->load->model('pastes'); $key = $this->uri->segment(2); - if ($key != $this->config->item('cron_key')) + if ($key != config_item('cron_key')) { show_404(); } @@ -384,25 +623,257 @@ class Main extends CI_Controller $this->load->view('about'); } + function captcha() + { + $this->load->helper('captcha'); + + //get "word" + $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ@'; + $str = ''; + for ($i = 0;$i < 8;$i++) + { + $str.= substr($pool, mt_rand(0, strlen($pool) - 1) , 1); + } + $word = $str; + + //save + $this->db_session->set_userdata(array( + 'captcha' => $word + )); + + //view + $this->load->view('view/captcha', array( + 'word' => $word + )); + } + function _valid_lang($lang) { $this->load->model('languages'); - $this->form_validation->set_message('_valid_lang', 'Please select your language'); + $this->form_validation->set_message('_valid_lang', lang('valid_lang')); return $this->languages->valid_language($lang); } + function _valid_captcha($text) + { + + if (config_item('enable_captcha') && $this->db_session->userdata('is_human') === false) + { + $this->form_validation->set_message('_valid_captcha', lang('captcha')); + + if ($this->use_recaptcha) + { + + if ($this->_valid_recaptcha()) + { + $this->db_session->set_userdata('is_human', true); + return true; + } + else + { + return false; + } + } + else + { + + if (strtolower($text) == strtolower($this->db_session->userdata('captcha'))) + { + $this->db_session->set_userdata('is_human', true); + return true; + } + else + { + return false; + } + } + } + else + { + return true; + } + } + + function _valid_recaptcha() + { + + if ($this->input->post('recaptcha_response_field')) + { + $pk = $this->recaptcha_privatekey; + $ra = $_SERVER['REMOTE_ADDR']; + $cf = $this->input->post('recaptcha_challenge_field'); + $rf = $this->input->post('recaptcha_response_field'); + + //check + $resp = recaptcha_check_answer($pk, $ra, $cf, $rf); + return $resp->is_valid; + } + else + { + return false; + } + } + + function _valid_ip() + { + + //get ip + $ip_address = $this->input->ip_address(); + + if (stristr($ip_address, ':')) + { + return $this->_valid_ipv6($ip_address); + } + else + { + return $this->_valid_ipv4($ip_address); + } + } + + function _valid_ipv4($ip_address) + { + + //get ip range + $ip = explode('.', $ip_address); + $ip_firstpart = $ip[0] . '.' . $ip[1] . '.'; + + //setup message + $this->form_validation->set_message('_valid_ip', lang('not_allowed')); + + //lookup + $this->db->select('ip_address, spam_attempts'); + $this->db->like('ip_address', $ip_firstpart, 'after'); + $query = $this->db->get('blocked_ips'); + + //check + + if ($query->num_rows() > 0) + { + + //update spamcount + $blocked_ips = $query->result_array(); + $spam_attempts = $blocked_ips[0]['spam_attempts']; + $this->db->where('ip_address', $ip_address); + $this->db->update('blocked_ips', array( + 'spam_attempts' => $spam_attempts + 1, + )); + + //return for the validation + return false; + } + else + { + return true; + } + } + + function _valid_ipv6($ip_address) + { + + //setup message + $this->form_validation->set_message('_valid_ip', lang('not_allowed')); + + //lookup + $this->db->select('ip_address, spam_attempts'); + $this->db->where('ip_address', $ip_address); + $query = $this->db->get('blocked_ips'); + + //check + + if ($query->num_rows() > 0) + { + + //update spamcount + $blocked_ips = $query->result_array(); + $spam_attempts = $blocked_ips[0]['spam_attempts']; + $this->db->where('ip_address', $ip_address); + $this->db->update('blocked_ips', array( + 'spam_attempts' => $spam_attempts + 1, + )); + + //return for the validation + return false; + } + else + { + return true; + } + } + + function _blockwords_check() + { + + //setup message + $this->form_validation->set_message('_blockwords_check', lang('blocked_words')); + + //check + $blocked_words = config_item('blocked_words'); + $post = $this->input->post(); + $raw = $post['code']; + + if (!$blocked_words) + { + return true; + } + + //we have blocked words + foreach (explode(',', $blocked_words) as $word) + { + $word = trim($word); + + if (stristr($raw, $word)) + { + return false; + } + } + return true; + } + + function _autofill_check() + { + + //setup message + $this->form_validation->set_message('_autofill_check', lang('robot')); + + //check + return (!$this->input->post('email') && !$this->input->post('url')); + } + + function _valid_authentication() + { + + if (config_item('require_auth')) + { + + if (!$this->auth_ldap->is_authenticated()) + { + $this->db_session->set_flashdata('tried_to', "/" . $this->uri->uri_string()); + redirect('/auth'); + } + } + } + function get_cm_js() { $lang = $this->uri->segment(3); $this->load->config('codemirror_languages'); - $cml = $this->config->item('codemirror_languages'); + $cml = config_item('codemirror_languages'); + + //file path + $file_path = 'themes/' . config_item('theme') . '/js/'; + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/js/'; + } if (isset($cml[$lang]) && gettype($cml[$lang]) == 'array') { header('Content-Type: application/x-javascript; charset=utf-8'); + header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 60 * 60 * 24 * 30)); foreach ($cml[$lang]['js'] as $js) { - echo file_get_contents('./static/js/' . $js[0]); + echo file_get_contents($file_path . $js[0]); } } exit; @@ -412,4 +883,18 @@ class Main extends CI_Controller { show_404(); } + + function robots_txt() + { + + if (config_item('disallow_search_engines')) + { + header('Content-Type: text/plain; charset=utf-8'); + $this->load->view('robots_txt'); + } + else + { + echo ''; + } + } } diff --git a/htdocs/application/controllers/spamadmin.php b/htdocs/application/controllers/spamadmin.php new file mode 100644 index 0000000..a44faaf --- /dev/null +++ b/htdocs/application/controllers/spamadmin.php @@ -0,0 +1,116 @@ +config->item('spamadmin_user'); + $pass = $this->config->item('spamadmin_pass'); + + if ($user == '' || $pass == '' || !isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] != $user || $_SERVER['PHP_AUTH_PW'] != $pass) + { + header('WWW-Authenticate: Basic realm="Spamadmin"'); + header('HTTP/1.0 401 Unauthorized'); + exit; + } + } + + function index() + { + $this->load->model('pastes'); + $data = $this->pastes->getSpamLists(); + $this->load->view('list_ips', $data); + } + + function spam_detail() + { + $this->load->model('pastes'); + $ip_address = $this->uri->segment(2); + + if ($this->input->post('confirm_remove') && $ip_address != '') + { + $this->db->where('ip_address', $ip_address); + $this->db->delete('pastes'); + $paste_count = $this->db->affected_rows(); + + if ($this->input->post('block_ip')) + { + $query = $this->db->get_where('blocked_ips', array( + 'ip_address' => $ip_address + )); + + if ($query->num_rows() == 0) + { + $this->db->insert('blocked_ips', array( + 'ip_address' => $ip_address, + 'blocked_at' => mktime() , + 'spam_attempts' => $paste_count, + )); + } + } + } + + //fill data + $data = $this->pastes->getSpamLists('spamadmin/' . $ip_address, $seg = 3, $ip_address); + $data['ip_address'] = $ip_address; + $ip = explode('.', $ip_address); + $ip_firstpart = $ip[0] . '.' . $ip[1] . '.'; + $data['ip_range'] = $ip_firstpart . '*.*'; + + //view + $this->load->view('spam_detail', $data); + } + + function blacklist() + { + + //pagination + $amount = $this->config->item('per_page'); + $page = ($this->uri->segment(3) ? $this->uri->segment(3) : 0); + + //get + $this->db->select('ip_address, blocked_at, spam_attempts'); + $this->db->order_by('blocked_at desc, ip_address asc'); + $query = $this->db->get('blocked_ips', $amount, $page); + $data['blocked_ips'] = $query->result_array(); + + //pagination + $config['base_url'] = site_url('spamadmin/blacklist'); + $query = $this->db->get('blocked_ips'); + $config['total_rows'] = $query->num_rows(); + $config['per_page'] = $amount; + $config['num_links'] = 9; + $config['full_tag_open'] = '
'; + $config['full_tag_close'] = '
'; + $config['uri_segment'] = 3; + $this->load->library('pagination'); + $this->pagination->initialize($config); + $data['pages'] = $this->pagination->create_links(); + + //view + $this->load->view('list_blocked_ips', $data); + } + + function unblock_ip() + { + $ip_address = $this->uri->segment(4); + $this->db->where('ip_address', $ip_address); + $this->db->delete('blocked_ips'); + redirect('spamadmin/blacklist'); + } +} diff --git a/htdocs/application/controllers/theme_assets.php b/htdocs/application/controllers/theme_assets.php new file mode 100644 index 0000000..7049648 --- /dev/null +++ b/htdocs/application/controllers/theme_assets.php @@ -0,0 +1,142 @@ +theme = config_item('theme'); + } + + function css() + { + $css_file = $this->uri->segment(4); + $css_file = basename( $css_file ); // Fix LFI Vulnerability + //file path + $file_path = 'themes/' . $this->theme . '/css/' . $css_file; + + //fallback to default css if view in theme not found + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/css/' . $css_file; + } + + // Double checking file + if( !file_exists( $file_path ) ) { + return false; + } + + //send + header('Content-type: text/css'); + $this->_expires_header(1); + readfile($file_path); + } + + function fonts() + { + $font_file = $this->uri->segment(4); + //file path + $file_path = 'themes/' . $this->theme . '/fonts/' . $font_file; + + //no fallback to default, since default has no such fonts + //since no fallbcack, there is no doucle checking for file + if (!file_exists($file_path)) + { + return false; + } + + //send + $path_parts = pathinfo(dirname(dirname(dirname(__FILE__))) . '/' . $file_path ); + if ( $path_parts['extension'] == "woff" ) { + header('Content-type: application/font-woff'); + } + if ( $path_parts['extension'] == "eot" ) { + header('Content-type: application/vnd.ms-fontobject'); + } + if ( $path_parts['extension'] == "ttf" || $path_parts['extension'] == "ttc" ) { + header('Content-type: application/x-font-ttf'); + } + if ( $path_parts['extension'] == "otf" ) { + header('Content-type: font/opentype'); + } + if ( $path_parts['extension'] == "svg" ) { + header('Content-type: image/svg+xml'); + } + if ( $path_parts['extension'] == "svgz" ) { + header("Content-Encoding: gzip"); + header('Content-type: image/svg+xml'); + } + $this->_expires_header(1); + readfile($file_path); + } + + function images() + { + $image_file = $this->uri->segment(4); + $image_file = basename( $image_file ); + //file path + $file_path = 'themes/' . $this->theme . '/images/' . $image_file; + + //fallback to default css if view in theme not found + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/images/' . $image_file; + } + + // double checking file + if( !file_exists( $file_path ) ) { + return false; + } + //send + $size = getimagesize($file_path); + header('Content-type: ' . $size['mime']); + $this->_expires_header(30); + readfile($file_path); + } + + function js() + { + + //get js + $segments = $this->uri->segment_array(); + array_shift($segments); + array_shift($segments); + array_shift($segments); + $js_file = implode('/', $segments); + $js_file = str_replace('../', '', $js_file); + + //file path + $file_path = 'themes/' . $this->theme . '/js/' . $js_file; + + //fallback to default js if js in theme not found + + if (!file_exists($file_path)) + { + $file_path = 'themes/default/js/' . $js_file; + } + + //send + header('Content-Type: application/x-javascript; charset=utf-8'); + $this->_expires_header(30); + readfile($file_path); + } + private + function _expires_header($days) + { + header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 60 * 60 * 24 * $days)); + } +} diff --git a/htdocs/application/controllers/unittest.php b/htdocs/application/controllers/unittest.php new file mode 100644 index 0000000..3a12be0 --- /dev/null +++ b/htdocs/application/controllers/unittest.php @@ -0,0 +1,57 @@ +load->library('unit_test'); + $this->load->model('pastes'); + + //self test + $test = 1 + 1; + $expected_result = 2; + $test_name = 'Self test: Adds one plus one'; + $this->unit->run($test, $expected_result, $test_name); + + //manipulation: create paste + $_POST['code'] = 'pastes->createPaste(); + + //paste created, has pid + $test = $pid; + $expected_result = 'is_string'; + $test_name = 'Create paste, has pid'; + $this->unit->run($test, $expected_result, $test_name); + $pid = str_replace('view/', '', $pid); + + //manipulation: delete paste + $this->pastes->delete_paste($pid); + + //report + echo $this->unit->report(); + } +} diff --git a/htdocs/application/core/Config.php b/htdocs/application/core/Config.php new file mode 100644 index 0000000..2372e0a --- /dev/null +++ b/htdocs/application/core/Config.php @@ -0,0 +1,379 @@ +config =& get_config(); + log_message('debug', "Config Class Initialized"); + + // Set the base_url automatically if none was provided + if ($this->config['base_url'] == '') + { + if (isset($_SERVER['HTTP_HOST'])) + { + $base_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http'; + $base_url .= '://'. $_SERVER['HTTP_HOST']; + $base_url .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); + } + + else + { + $base_url = 'http://localhost/'; + } + + $this->set_item('base_url', $base_url); + } + } + + // -------------------------------------------------------------------- + + /** + * Load Config File + * + * @access public + * @param string the config file name + * @param boolean if configuration values should be loaded into their own section + * @param boolean true if errors should just return false, false if an error message should be displayed + * @return boolean if the file was loaded correctly + */ + function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) + { + $file = ($file == '') ? 'config' : str_replace('.php', '', $file); + $found = FALSE; + $loaded = FALSE; + + $check_locations = defined('ENVIRONMENT') + ? array(ENVIRONMENT.'/'.$file, $file) + : array($file); + + foreach ($this->_config_paths as $path) + { + foreach ($check_locations as $location) + { + $file_path = $path.'config/'.$location.'.php'; + + if (in_array($file_path, $this->is_loaded, TRUE)) + { + $loaded = TRUE; + continue 2; + } + + if (file_exists($file_path)) + { + $found = TRUE; + break; + } + } + + if ($found === FALSE) + { + continue; + } + + include($file_path); + + if ( ! isset($config) OR ! is_array($config)) + { + if ($fail_gracefully === TRUE) + { + return FALSE; + } + show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.'); + } + + if ($use_sections === TRUE) + { + if (isset($this->config[$file])) + { + $this->config[$file] = array_merge($this->config[$file], $config); + } + else + { + $this->config[$file] = $config; + } + } + else + { + $this->config = array_merge($this->config, $config); + } + + $this->is_loaded[] = $file_path; + unset($config); + + $loaded = TRUE; + log_message('debug', 'Config file loaded: '.$file_path); + break; + } + + if ($loaded === FALSE) + { + if ($fail_gracefully === TRUE) + { + return FALSE; + } + show_error('The configuration file '.$file.'.php does not exist. Please copy '.$file.'.php.dist to '.$file.'.php and make the changes as needed and refresh the page.'); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a config file item + * + * + * @access public + * @param string the config item name + * @param string the index name + * @param bool + * @return string + */ + function item($item, $index = '') + { + if ($index == '') + { + if ( ! isset($this->config[$item])) + { + return FALSE; + } + + $pref = $this->config[$item]; + } + else + { + if ( ! isset($this->config[$index])) + { + return FALSE; + } + + if ( ! isset($this->config[$index][$item])) + { + return FALSE; + } + + $pref = $this->config[$index][$item]; + } + + return $pref; + } + + // -------------------------------------------------------------------- + + /** + * Fetch a config file item - adds slash after item (if item is not empty) + * + * @access public + * @param string the config item name + * @param bool + * @return string + */ + function slash_item($item) + { + if ( ! isset($this->config[$item])) + { + return FALSE; + } + if( trim($this->config[$item]) == '') + { + return ''; + } + + return rtrim($this->config[$item], '/').'/'; + } + + // -------------------------------------------------------------------- + + /** + * Site URL + * Returns base_url . index_page [. uri_string] + * + * @access public + * @param string the URI string + * @return string + */ + function site_url($uri = '') + { + if ($uri == '') + { + return $this->slash_item('base_url').$this->item('index_page'); + } + + if ($this->item('enable_query_strings') == FALSE) + { + $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix'); + return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix; + } + else + { + return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri); + } + } + + // ------------------------------------------------------------- + + /** + * Base URL + * Returns base_url [. uri_string] + * + * @access public + * @param string $uri + * @return string + */ + function base_url($uri = '') + { + return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/'); + } + + // ------------------------------------------------------------- + + /** + * Build URI string for use in Config::site_url() and Config::base_url() + * + * @access protected + * @param $uri + * @return string + */ + protected function _uri_string($uri) + { + if ($this->item('enable_query_strings') == FALSE) + { + if (is_array($uri)) + { + $uri = implode('/', $uri); + } + $uri = trim($uri, '/'); + } + else + { + if (is_array($uri)) + { + $i = 0; + $str = ''; + foreach ($uri as $key => $val) + { + $prefix = ($i == 0) ? '' : '&'; + $str .= $prefix.$key.'='.$val; + $i++; + } + $uri = $str; + } + } + return $uri; + } + + // -------------------------------------------------------------------- + + /** + * System URL + * + * @access public + * @return string + */ + function system_url() + { + $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH)); + return $this->slash_item('base_url').end($x).'/'; + } + + // -------------------------------------------------------------------- + + /** + * Set a config file item + * + * @access public + * @param string the config item key + * @param string the config item value + * @return void + */ + function set_item($item, $value) + { + $this->config[$item] = $value; + } + + // -------------------------------------------------------------------- + + /** + * Assign to Config + * + * This function is called by the front controller (CodeIgniter.php) + * after the Config class is instantiated. It permits config items + * to be assigned or overriden by variables contained in the index.php file + * + * @access private + * @param array + * @return void + */ + function _assign_to_config($items = array()) + { + if (is_array($items)) + { + foreach ($items as $key => $val) + { + $this->set_item($key, $val); + } + } + } +} + +// END CI_Config class + +/* End of file Config.php */ +/* Location: ./system/core/Config.php */ diff --git a/htdocs/application/core/Input.php b/htdocs/application/core/Input.php new file mode 100644 index 0000000..2521c20 --- /dev/null +++ b/htdocs/application/core/Input.php @@ -0,0 +1,850 @@ +_allow_get_array = (config_item('allow_get_array') === TRUE); + $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + $this->_enable_csrf = (config_item('csrf_protection') === TRUE); + + global $SEC; + $this->security =& $SEC; + + // Do we need the UTF-8 class? + if (UTF8_ENABLED === TRUE) + { + global $UNI; + $this->uni =& $UNI; + } + + // Sanitize global arrays + $this->_sanitize_globals(); + } + + // -------------------------------------------------------------------- + + /** + * Fetch from array + * + * This is a helper function to retrieve values from global arrays + * + * @access private + * @param array + * @param string + * @param bool + * @return string + */ + function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) + { + if ( ! isset($array[$index])) + { + return FALSE; + } + + if ($xss_clean === TRUE) + { + return $this->security->xss_clean($array[$index]); + } + + return $array[$index]; + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the GET array + * + * @access public + * @param string + * @param bool + * @return string + */ + function get($index = NULL, $xss_clean = FALSE) + { + // Check if a field has been provided + if ($index === NULL AND ! empty($_GET)) + { + $get = array(); + + // loop through the full _GET array + foreach (array_keys($_GET) as $key) + { + $get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean); + } + return $get; + } + + return $this->_fetch_from_array($_GET, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the POST array + * + * @access public + * @param string + * @param bool + * @return string + */ + function post($index = NULL, $xss_clean = FALSE) + { + // Check if a field has been provided + if ($index === NULL AND ! empty($_POST)) + { + $post = array(); + + // Loop through the full _POST array and return it + foreach (array_keys($_POST) as $key) + { + $post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean); + } + return $post; + } + + return $this->_fetch_from_array($_POST, $index, $xss_clean); + } + + + // -------------------------------------------------------------------- + + /** + * Fetch an item from either the GET array or the POST + * + * @access public + * @param string The index key + * @param bool XSS cleaning + * @return string + */ + function get_post($index = '', $xss_clean = FALSE) + { + if ( ! isset($_POST[$index]) ) + { + return $this->get($index, $xss_clean); + } + else + { + return $this->post($index, $xss_clean); + } + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the COOKIE array + * + * @access public + * @param string + * @param bool + * @return string + */ + function cookie($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); + } + + // ------------------------------------------------------------------------ + + /** + * Set cookie + * + * Accepts six parameter, or you can submit an associative + * array in the first parameter containing all the values. + * + * @access public + * @param mixed + * @param string the value of the cookie + * @param string the number of seconds until expiration + * @param string the cookie domain. Usually: .yourdomain.com + * @param string the cookie path + * @param string the cookie prefix + * @param bool true makes the cookie secure + * @return void + */ + function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE) + { + if (is_array($name)) + { + // always leave 'name' in last place, as the loop will break otherwise, due to $$item + foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item) + { + if (isset($name[$item])) + { + $$item = $name[$item]; + } + } + } + + if ($prefix == '' AND config_item('cookie_prefix') != '') + { + $prefix = config_item('cookie_prefix'); + } + if ($domain == '' AND config_item('cookie_domain') != '') + { + $domain = config_item('cookie_domain'); + } + if ($path == '/' AND config_item('cookie_path') != '/') + { + $path = config_item('cookie_path'); + } + if ($secure == FALSE AND config_item('cookie_secure') != FALSE) + { + $secure = config_item('cookie_secure'); + } + + if ( ! is_numeric($expire)) + { + $expire = time() - 86500; + } + else + { + $expire = ($expire > 0) ? time() + $expire : 0; + } + + setcookie($prefix.$name, $value, $expire, $path, $domain, $secure); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the SERVER array + * + * @access public + * @param string + * @param bool + * @return string + */ + function server($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_SERVER, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the IP Address + * + * @return string + */ + public function ip_address() + { + if ($this->ip_address !== FALSE) + { + return $this->ip_address; + } + + $proxy_ips = config_item('proxy_ips'); + if ( ! empty($proxy_ips)) + { + $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); + foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) + { + if (($spoof = $this->server($header)) !== FALSE) + { + // Some proxies typically list the whole chain of IP + // addresses through which the client has reached us. + // e.g. client_ip, proxy_ip1, proxy_ip2, etc. + if (strpos($spoof, ',') !== FALSE) + { + $spoof = explode(',', $spoof, 2); + $spoof = $spoof[0]; + } + + if ( ! $this->valid_ip($spoof)) + { + $spoof = FALSE; + } + else + { + break; + } + } + } + + $this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE)) + ? $spoof : $_SERVER['REMOTE_ADDR']; + } + else + { + $this->ip_address = $_SERVER['REMOTE_ADDR']; + } + + if ( ! $this->valid_ip($this->ip_address)) + { + $this->ip_address = '0.0.0.0'; + } + + return $this->ip_address; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * @access public + * @param string + * @param string ipv4 or ipv6 + * @return bool + */ + public function valid_ip($ip, $which = '') + { + $which = strtolower($which); + + // First check if filter_var is available + if (is_callable('filter_var')) + { + switch ($which) { + case 'ipv4': + $flag = FILTER_FLAG_IPV4; + break; + case 'ipv6': + $flag = FILTER_FLAG_IPV6; + break; + default: + $flag = ''; + break; + } + + return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag); + } + + if ($which !== 'ipv6' && $which !== 'ipv4') + { + if (strpos($ip, ':') !== FALSE) + { + $which = 'ipv6'; + } + elseif (strpos($ip, '.') !== FALSE) + { + $which = 'ipv4'; + } + else + { + return FALSE; + } + } + + $func = '_valid_'.$which; + return $this->$func($ip); + } + + // -------------------------------------------------------------------- + + /** + * Validate IPv4 Address + * + * Updated version suggested by Geert De Deckere + * + * @access protected + * @param string + * @return bool + */ + protected function _valid_ipv4($ip) + { + $ip_segments = explode('.', $ip); + + // Always 4 segments needed + if (count($ip_segments) !== 4) + { + return FALSE; + } + // IP can not start with 0 + if ($ip_segments[0][0] == '0') + { + return FALSE; + } + + // Check each segment + foreach ($ip_segments as $segment) + { + // IP segments must be digits and can not be + // longer than 3 digits or greater then 255 + if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Validate IPv6 Address + * + * @access protected + * @param string + * @return bool + */ + protected function _valid_ipv6($str) + { + // 8 groups, separated by : + // 0-ffff per group + // one set of consecutive 0 groups can be collapsed to :: + + $groups = 8; + $collapsed = FALSE; + + $chunks = array_filter( + preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE) + ); + + // Rule out easy nonsense + if (current($chunks) == ':' OR end($chunks) == ':') + { + return FALSE; + } + + // PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well + if (strpos(end($chunks), '.') !== FALSE) + { + $ipv4 = array_pop($chunks); + + if ( ! $this->_valid_ipv4($ipv4)) + { + return FALSE; + } + + $groups--; + } + + while ($seg = array_pop($chunks)) + { + if ($seg[0] == ':') + { + if (--$groups == 0) + { + return FALSE; // too many groups + } + + if (strlen($seg) > 2) + { + return FALSE; // long separator + } + + if ($seg == '::') + { + if ($collapsed) + { + return FALSE; // multiple collapsed + } + + $collapsed = TRUE; + } + } + elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4) + { + return FALSE; // invalid segment + } + } + + return $collapsed OR $groups == 1; + } + + // -------------------------------------------------------------------- + + /** + * User Agent + * + * @access public + * @return string + */ + function user_agent() + { + if ($this->user_agent !== FALSE) + { + return $this->user_agent; + } + + $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT']; + + return $this->user_agent; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Globals + * + * This function does the following: + * + * Unsets $_GET data (if query strings are not enabled) + * + * Unsets all globals if register_globals is enabled + * + * Standardizes newline characters to \n + * + * @access private + * @return void + */ + function _sanitize_globals() + { + // It would be "wrong" to unset any of these GLOBALS. + $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', + '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA', + 'system_folder', 'application_folder', 'BM', 'EXT', + 'CFG', 'URI', 'RTR', 'OUT', 'IN'); + + // Unset globals for securiy. + // This is effectively the same as register_globals = off + foreach (array($_GET, $_POST, $_COOKIE) as $global) + { + if ( ! is_array($global)) + { + if ( ! in_array($global, $protected)) + { + global $$global; + $$global = NULL; + } + } + else + { + foreach ($global as $key => $val) + { + if ( ! in_array($key, $protected)) + { + global $$key; + $$key = NULL; + } + } + } + } + + // Is $_GET data allowed? If not we'll set the $_GET to an empty array + if ($this->_allow_get_array == FALSE) + { + $_GET = array(); + } + else + { + if (is_array($_GET) AND count($_GET) > 0) + { + foreach ($_GET as $key => $val) + { + $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + } + } + + // Clean $_POST Data + if (is_array($_POST) AND count($_POST) > 0) + { + foreach ($_POST as $key => $val) + { + $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + } + + // Clean $_COOKIE Data + if (is_array($_COOKIE) AND count($_COOKIE) > 0) + { + // Also get rid of specially treated cookies that might be set by a server + // or silly application, that are of no use to a CI application anyway + // but that when present will trip our 'Disallowed Key Characters' alarm + // http://www.ietf.org/rfc/rfc2109.txt + // note that the key names below are single quoted strings, and are not PHP variables + unset($_COOKIE['$Version']); + unset($_COOKIE['$Path']); + unset($_COOKIE['$Domain']); + + foreach ($_COOKIE as $key => $val) + { + $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + } + + // Sanitize PHP_SELF + $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); + + + // CSRF Protection check on HTTP requests + if ($this->_enable_csrf == TRUE && ! $this->is_cli_request()) + { + $this->security->csrf_verify(); + } + + log_message('debug', "Global POST and COOKIE data sanitized"); + } + + // -------------------------------------------------------------------- + + /** + * Clean Input Data + * + * This is a helper function. It escapes data and + * standardizes newline characters to \n + * + * @access private + * @param string + * @return string + */ + function _clean_input_data($str) + { + if (is_array($str)) + { + $new_array = array(); + foreach ($str as $key => $val) + { + $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + return $new_array; + } + + /* We strip slashes if magic quotes is on to keep things consistent + + NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and + it will probably not exist in future versions at all. + */ + if ( ! is_php('5.4') && get_magic_quotes_gpc()) + { + $str = stripslashes($str); + } + + // Clean UTF-8 if supported + if (UTF8_ENABLED === TRUE) + { + $str = $this->uni->clean_string($str); + } + + // Remove control characters + // Commented out by Claude for Stikked (https://github.com/claudehohl/Stikked/issues/28) + //$str = remove_invisible_characters($str); + + // Should we filter the input data? + if ($this->_enable_xss === TRUE) + { + $str = $this->security->xss_clean($str); + } + + // Standardize newlines if needed + if ($this->_standardize_newlines == TRUE) + { + if (strpos($str, "\r") !== FALSE) + { + $str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str); + } + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Clean Keys + * + * This is a helper function. To prevent malicious users + * from trying to exploit keys we make sure that keys are + * only named with alpha-numeric text and a few other items. + * + * @access private + * @param string + * @return string + */ + function _clean_input_keys($str) + { + if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str)) + { + exit('Disallowed Key Characters.'); + } + + // Clean UTF-8 if supported + if (UTF8_ENABLED === TRUE) + { + $str = $this->uni->clean_string($str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Request Headers + * + * In Apache, you can simply call apache_request_headers(), however for + * people running other webservers the function is undefined. + * + * @param bool XSS cleaning + * + * @return array + */ + public function request_headers($xss_clean = FALSE) + { + // Look at Apache go! + if (function_exists('apache_request_headers')) + { + $headers = apache_request_headers(); + } + else + { + $headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE'); + + foreach ($_SERVER as $key => $val) + { + if (strncmp($key, 'HTTP_', 5) === 0) + { + $headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); + } + } + } + + // take SOME_HEADER and turn it into Some-Header + foreach ($headers as $key => $val) + { + $key = str_replace('_', ' ', strtolower($key)); + $key = str_replace(' ', '-', ucwords($key)); + + $this->headers[$key] = $val; + } + + return $this->headers; + } + + // -------------------------------------------------------------------- + + /** + * Get Request Header + * + * Returns the value of a single member of the headers class member + * + * @param string array key for $this->headers + * @param boolean XSS Clean or not + * @return mixed FALSE on failure, string on success + */ + public function get_request_header($index, $xss_clean = FALSE) + { + if (empty($this->headers)) + { + $this->request_headers(); + } + + if ( ! isset($this->headers[$index])) + { + return FALSE; + } + + if ($xss_clean === TRUE) + { + return $this->security->xss_clean($this->headers[$index]); + } + + return $this->headers[$index]; + } + + // -------------------------------------------------------------------- + + /** + * Is ajax Request? + * + * Test to see if a request contains the HTTP_X_REQUESTED_WITH header + * + * @return boolean + */ + public function is_ajax_request() + { + return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest'); + } + + // -------------------------------------------------------------------- + + /** + * Is cli Request? + * + * Test to see if a request was made from the command line + * + * @return bool + */ + public function is_cli_request() + { + return (php_sapi_name() === 'cli' OR defined('STDIN')); + } + +} + +/* End of file Input.php */ +/* Location: ./system/core/Input.php */ diff --git a/htdocs/application/core/MY_Loader.php b/htdocs/application/core/MY_Loader.php new file mode 100644 index 0000000..54b77e1 --- /dev/null +++ b/htdocs/application/core/MY_Loader.php @@ -0,0 +1,204 @@ +_ci_load(array( + '_ci_view' => $view_path, + '_ci_vars' => $this->_ci_object_to_array($vars) , + '_ci_return' => $return + )); + } + /** + * Loader + * + * This function is used to load views and files. + * Variables are prefixed with _ci_ to avoid symbol collision with + * variables made available to view files + * + * @param array + * @return void + */ + protected + function _ci_load($_ci_data) + { + + // Set the default data variables + foreach (array( + '_ci_view', + '_ci_vars', + '_ci_path', + '_ci_return' + ) as $_ci_val) + { + $$_ci_val = (!isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val]; + } + $file_exists = FALSE; + + // Set the path to the requested file + + if ($_ci_path != '') + { + $_ci_x = explode('/', $_ci_path); + $_ci_file = end($_ci_x); + } + else + { + $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); + $_ci_file = ($_ci_ext == '') ? $_ci_view . '.php' : $_ci_view; + foreach ($this->_ci_view_paths as $view_file => $cascade) + { + + /* *** modification for stikked themes *** + * + * we are by default in the htdocs/application/views folder, which is bad. + * for security reasons, themes folder should be outside the application dir. + * but file_exists() doesn't work with ../../ in filenames :-( + * so, applying the full FrontControllerPATH here, making ../../ superfluous. + * + */ + + if (file_exists(FCPATH . $_ci_file)) + { + $_ci_path = FCPATH . $_ci_file; + $file_exists = TRUE; + break; + } + + if (!$cascade) + { + break; + } + } + } + + if (!$file_exists && !file_exists($_ci_path)) + { + show_error('Unable to load the requested file: ' . $_ci_file); + } + + // This allows anything loaded using $this->load (views, files, etc.) + // to become accessible from within the Controller and Model functions. + + $_ci_CI = & get_instance(); + foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var) + { + + if (!isset($this->$_ci_key)) + { + $this->$_ci_key = & $_ci_CI->$_ci_key; + } + } + + /* + * Extract and cache variables + * + * You can either set variables using the dedicated $this->load_vars() + * function or via the second parameter of this function. We'll merge + * the two types and cache them so that views that are embedded within + * other views can have access to these variables. + */ + + if (is_array($_ci_vars)) + { + $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); + } + extract($this->_ci_cached_vars); + + /* + * Buffer the output + * + * We buffer the output for two reasons: + * 1. Speed. You get a significant speed boost. + * 2. So that the final rendered template can be + * post-processed by the output class. Why do we + * need post processing? For one thing, in order to + * show the elapsed page load time. Unless we + * can intercept the content right before it's sent to + * the browser and then stop the timer it won't be accurate. + */ + ob_start(); + + // If the PHP installation does not support short tags we'll + // do a little string replacement, changing the short tags + + // to standard PHP echo statements. + + + if ((bool)@ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE) + { + echo eval('?>' . preg_replace("/;*\s*\?>/", "; ?>", str_replace(' $this->_ci_ob_level + 1) + { + ob_end_flush(); + } + else + { + $_ci_CI->output->append_output(ob_get_contents()); + @ob_end_clean(); + } + } +} diff --git a/htdocs/application/errors/error_404.php b/htdocs/application/errors/error_404.php old mode 100755 new mode 100644 index 0b2aef9..8beb033 --- a/htdocs/application/errors/error_404.php +++ b/htdocs/application/errors/error_404.php @@ -9,9 +9,9 @@ header("HTTP/1.1 404 Not Found"); Stikked - - - + + + @@ -19,14 +19,6 @@ header("HTTP/1.1 404 Not Found");
@@ -41,10 +33,6 @@ header("HTTP/1.1 404 Not Found");
- diff --git a/htdocs/application/errors/error_db.php b/htdocs/application/errors/error_db.php old mode 100755 new mode 100644 index 45225c7..b396cda --- a/htdocs/application/errors/error_db.php +++ b/htdocs/application/errors/error_db.php @@ -1,51 +1,62 @@ - + + + +Database Error + + + +
+

+ +
+ + \ No newline at end of file diff --git a/htdocs/application/errors/error_general.php b/htdocs/application/errors/error_general.php old mode 100755 new mode 100644 index 45225c7..fd63ce2 --- a/htdocs/application/errors/error_general.php +++ b/htdocs/application/errors/error_general.php @@ -1,51 +1,62 @@ - + + + +Error + + + +
+

+ +
+ + \ No newline at end of file diff --git a/htdocs/application/errors/error_php.php b/htdocs/application/errors/error_php.php old mode 100755 new mode 100644 diff --git a/htdocs/application/errors/index.html b/htdocs/application/errors/index.html old mode 100755 new mode 100644 diff --git a/htdocs/application/helpers/captcha_helper.php b/htdocs/application/helpers/captcha_helper.php new file mode 100644 index 0000000..770b6a5 --- /dev/null +++ b/htdocs/application/helpers/captcha_helper.php @@ -0,0 +1,471 @@ + '', + 'img_path' => '', + 'img_url' => '', + 'img_width' => '150', + 'img_height' => '30', + 'font_path' => '', + 'expiration' => 7200 + ); + foreach ($defaults as $key => $val) + { + + if (!is_array($data)) + { + + if (!isset($$key) OR $$key == '') + { + $$key = $val; + } + } + else + { + $$key = (!isset($data[$key])) ? $val : $data[$key]; + } + } + + if ($img_path == '' OR $img_url == '') + { + return FALSE; + } + + if (!@is_dir($img_path)) + { + return FALSE; + } + + if (!is_writable($img_path)) + { + return FALSE; + } + + if (!extension_loaded('gd')) + { + return FALSE; + } + + // ----------------------------------- + // Remove old images + + // ----------------------------------- + + list($usec, $sec) = explode(" ", microtime()); + $now = ((float)$usec + (float)$sec); + $current_dir = @opendir($img_path); + while ($filename = @readdir($current_dir)) + { + + if ($filename != "." and $filename != ".." and $filename != "index.html") + { + $name = str_replace(".jpg", "", $filename); + + if (($name + $expiration) < $now) + { + @unlink($img_path . $filename); + } + } + } + @closedir($current_dir); + + // ----------------------------------- + // Do we have a "word" yet? + + // ----------------------------------- + + + if ($word == '') + { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $str = ''; + for ($i = 0;$i < 8;$i++) + { + $str.= substr($pool, mt_rand(0, strlen($pool) - 1) , 1); + } + $word = $str; + } + + // ----------------------------------- + // Determine angle and position + + // ----------------------------------- + + $length = strlen($word); + $angle = ($length >= 6) ? rand(-($length - 6) , ($length - 6)) : 0; + $x_axis = rand(6, (360 / $length) - 16); + $y_axis = ($angle >= 0) ? rand($img_height, $img_width) : rand(6, $img_height); + + // ----------------------------------- + // Create image + + // ----------------------------------- + + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + + + if (function_exists('imagecreatetruecolor')) + { + $im = imagecreatetruecolor($img_width, $img_height); + } + else + { + $im = imagecreate($img_width, $img_height); + } + + // ----------------------------------- + // Assign colors + + // ----------------------------------- + + $bg_color = imagecolorallocate($im, 255, 255, 255); + $border_color = imagecolorallocate($im, 153, 102, 102); + $text_color = imagecolorallocate($im, 204, 153, 153); + $grid_color = imagecolorallocate($im, 255, 182, 182); + $shadow_color = imagecolorallocate($im, 255, 240, 240); + + // ----------------------------------- + // Create the rectangle + + // ----------------------------------- + + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color); + + // ----------------------------------- + // Create the spiral pattern + + // ----------------------------------- + + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + for ($i = 0;$i < ($circles * $points) - 1;$i++) + { + $theta = $theta + $thetac; + $rad = $radius * ($i / $points); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta = $theta + $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta)) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $grid_color); + $theta = $theta - $thetac; + } + + // ----------------------------------- + // Write the text + + // ----------------------------------- + + $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE; + + if ($use_font == FALSE) + { + $font_size = 5; + $x = rand(0, $img_width / ($length / 3)); + $y = 0; + } + else + { + $font_size = 16; + $x = rand(0, $img_width / ($length / 1.5)); + $y = $font_size + 2; + } + for ($i = 0;$i < strlen($word);$i++) + { + + if ($use_font == FALSE) + { + $y = rand(0, $img_height / 2); + imagestring($im, $font_size, $x, $y, substr($word, $i, 1) , $text_color); + $x+= ($font_size * 2); + } + else + { + $y = rand($img_height / 2, $img_height - 3); + imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1)); + $x+= $font_size; + } + } + + // ----------------------------------- + // Create the border + + // ----------------------------------- + + imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $border_color); + + // ----------------------------------- + // Generate the image + + // ----------------------------------- + + $img_name = $now . '.jpg'; + ImageJPEG($im, $img_path . $img_name); + $img = "\""; + ImageDestroy($im); + return array( + 'word' => $word, + 'time' => $now, + 'image' => $img + ); + } +} +/** + * Display CAPTCHA + * + * @access public + * @return string + */ + +if (!function_exists('display_captcha')) +{ + + function display_captcha($word = '') + { + $data = ''; + list($usec, $sec) = explode(" ", microtime()); + $now = ((float)$usec + (float)$sec); + $defaults = array( + 'word' => $word, + 'img_path' => '', + 'img_url' => '', + 'img_width' => '180', + 'img_height' => '40', + 'font_path' => '', + 'expiration' => 7200 + ); + foreach ($defaults as $key => $val) + { + + if (!is_array($data)) + { + + if (!isset($$key) OR $$key == '') + { + $$key = $val; + } + } + else + { + $$key = (!isset($data[$key])) ? $val : $data[$key]; + } + } + + if (!extension_loaded('gd')) + { + return FALSE; + } + + // ----------------------------------- + // Do we have a "word" yet? + + // ----------------------------------- + + + if ($word == '') + { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $str = ''; + for ($i = 0;$i < 4;$i++) + { + $str.= substr($pool, mt_rand(0, strlen($pool) - 1) , 1); + } + $word = $str; + } + + // ----------------------------------- + // Determine angle and position + + // ----------------------------------- + + $length = strlen($word); + $angle = ($length >= 6) ? mt_rand(-($length - 6) , ($length - 6)) : 0; + $angle = 10; + $x_axis = mt_rand(6, (360 / $length) - 16); + $y_axis = ($angle >= 0) ? mt_rand($img_height, $img_width) : mt_rand(6, $img_height); + + // ----------------------------------- + // Create image + + // ----------------------------------- + + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + + + if (function_exists('imagecreatetruecolor')) + { + $im = imagecreatetruecolor($img_width, $img_height); + } + else + { + $im = imagecreate($img_width, $img_height); + } + + // ----------------------------------- + // Assign colors + + // ----------------------------------- + + + function b() + { + return mt_rand(240, 255); + } + $bg_color = imagecolorallocate($im, b() , b() , b()); + $border_color = imagecolorallocate($im, 153, 102, 102); + $text_color = imagecolorallocate($im, 204, 153, 153); + $grid_color = imagecolorallocate($im, 255, 182, 182); + $shadow_color = imagecolorallocate($im, 255, 240, 240); + + // ----------------------------------- + // Create the rectangle + + // ----------------------------------- + + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color); + + // ----------------------------------- + // Create the spiral pattern + + // ----------------------------------- + + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + for ($i = 0;$i < ($circles * $points) - 1;$i++) + { + $theta = $theta + $thetac; + $rad = $radius * ($i / $points); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta = $theta + $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta)) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $grid_color); + $theta = $theta - $thetac; + } + + // ----------------------------------- + // Write the text + + // ----------------------------------- + + //get random font + + $fn = explode(',', '4,5,6,8,16,17,19,24,26'); + $f = mt_rand(0, count($fn) - 1); + $font_path = './static/fonts/font' . $fn[$f] . '.ttf'; + $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE; + + if ($use_font == FALSE) + { + $font_size = 5; + $x = mt_rand(0, $img_width / ($length / 3)); + $y = 0; + } + else + { + $font_size = 16; + $x = mt_rand(0, $img_width / ($length / 1.5)); + $y = $font_size + 2; + } + for ($i = 0;$i < strlen($word);$i++) + { + + if ($use_font == FALSE) + { + $y = mt_rand(0, $img_height / 2); + imagestring($im, $font_size, $x, $y, substr($word, $i, 1) , $text_color); + $x+= ($font_size * 2); + } + else + { + $y = mt_rand($img_height / 2, $img_height - 3); + imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1)); + $x+= $font_size; + } + } + + // ----------------------------------- + // Create the border + + // ----------------------------------- + + imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $border_color); + + // ----------------------------------- + // Generate the image + + // ----------------------------------- + + $img_name = $now . '.jpg'; + echo ImageJPEG($im); + ImageDestroy($im); + } +} + +// ------------------------------------------------------------------------ +/* End of file captcha_helper.php */ + + +/* Location: ./system/heleprs/captcha_helper.php */ diff --git a/htdocs/application/helpers/json_helper.php b/htdocs/application/helpers/json_helper.php old mode 100755 new mode 100644 diff --git a/htdocs/application/helpers/language_helper.php b/htdocs/application/helpers/language_helper.php new file mode 100644 index 0000000..567a41a --- /dev/null +++ b/htdocs/application/helpers/language_helper.php @@ -0,0 +1,58 @@ +lang->line($index); + + if ($id != '') + { + $line = '"; + } + + return ($line != '' ? $line : '[' . $index . ']'); + } +} + +// ------------------------------------------------------------------------ +/* End of file language_helper.php */ +/* Location: ./system/helpers/language_helper.php */ diff --git a/htdocs/application/helpers/recaptcha_helper.php b/htdocs/application/helpers/recaptcha_helper.php new file mode 100644 index 0000000..32c4f4d --- /dev/null +++ b/htdocs/application/helpers/recaptcha_helper.php @@ -0,0 +1,277 @@ + $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; +} + + + +/** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ +function _recaptcha_http_post($host, $path, $data, $port = 80) { + + $req = _recaptcha_qsencode ($data); + + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; +} + + + +/** + * Gets the challenge HTML (javascript and non-javascript version). + * This is called from the browser, and the resulting reCAPTCHA HTML widget + * is embedded within the HTML form it was called from. + * @param string $pubkey A public key for reCAPTCHA + * @param string $error The error given by reCAPTCHA (optional, default is null) + * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) + + * @return string - The HTML to be embedded in the user's form. + */ +function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) +{ + if ($pubkey == null || $pubkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($use_ssl) { + $server = RECAPTCHA_API_SECURE_SERVER; + } else { + $server = RECAPTCHA_API_SERVER; + } + + $errorpart = ""; + if ($error) { + $errorpart = "&error=" . $error; + } + return ' + + '; +} + + + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} + + +/** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ +function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) +{ + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + + $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } + else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + +} + +/** + * gets a URL where the user can sign up for reCAPTCHA. If your application + * has a configuration page where you enter a key, you should provide a link + * using this function. + * @param string $domain The domain where the page is hosted + * @param string $appname The name of your application + */ +function recaptcha_get_signup_url ($domain = null, $appname = null) { + return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); +} + +function _recaptcha_aes_pad($val) { + $block_size = 16; + $numpad = $block_size - (strlen ($val) % $block_size); + return str_pad($val, strlen ($val) + $numpad, chr($numpad)); +} + +/* Mailhide related code */ + +function _recaptcha_aes_encrypt($val,$ky) { + if (! function_exists ("mcrypt_encrypt")) { + die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); + } + $mode=MCRYPT_MODE_CBC; + $enc=MCRYPT_RIJNDAEL_128; + $val=_recaptcha_aes_pad($val); + return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + + +function _recaptcha_mailhide_urlbase64 ($x) { + return strtr(base64_encode ($x), '+/', '-_'); +} + +/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ +function recaptcha_mailhide_url($pubkey, $privkey, $email) { + if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { + die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . + "you can do so at http://www.google.com/recaptcha/mailhide/apikey"); + } + + + $ky = pack('H*', $privkey); + $cryptmail = _recaptcha_aes_encrypt ($email, $ky); + + return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); +} + +/** + * gets the parts of the email to expose to the user. + * eg, given johndoe@example,com return ["john", "example.com"]. + * the email is then displayed as john...@example.com + */ +function _recaptcha_mailhide_email_parts ($email) { + $arr = preg_split("/@/", $email ); + + if (strlen ($arr[0]) <= 4) { + $arr[0] = substr ($arr[0], 0, 1); + } else if (strlen ($arr[0]) <= 6) { + $arr[0] = substr ($arr[0], 0, 3); + } else { + $arr[0] = substr ($arr[0], 0, 4); + } + return $arr; +} + +/** + * Gets html to display an email address given a public an private key. + * to get a key, go to: + * + * http://www.google.com/recaptcha/mailhide/apikey + */ +function recaptcha_mailhide_html($pubkey, $privkey, $email) { + $emailparts = _recaptcha_mailhide_email_parts ($email); + $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); + + return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); + +} + + +?> diff --git a/htdocs/application/language/chinese-simplified/date_lang.php b/htdocs/application/language/chinese-simplified/date_lang.php new file mode 100644 index 0000000..ad13207 --- /dev/null +++ b/htdocs/application/language/chinese-simplified/date_lang.php @@ -0,0 +1,61 @@ + - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/htdocs/application/language/english/stikked_lang.php b/htdocs/application/language/english/stikked_lang.php new file mode 100644 index 0000000..af28829 --- /dev/null +++ b/htdocs/application/language/english/stikked_lang.php @@ -0,0 +1,72 @@ +. + * + */ +/** + * Auth_Ldap Class + * + * Simple LDAP Authentication library for Code Igniter. + * + * @package Auth_Ldap + * @author Greg Wojtak + * @version 0.6 + * @link http://www.techrockdo.com/projects/auth_ldap + * @license GNU Lesser General Public License (LGPL) + * @copyright Copyright © 2010,2011 by Greg Wojtak + * @todo Allow for privileges in groups of groups in AD + * @todo Rework roles system a little bit to a "auth level" paradigm + */ +class Auth_Ldap { + function __construct() { + $this->ci =& get_instance(); + + log_message('debug', 'Auth_Ldap initialization commencing...'); + + // Load the session library + $this->ci->load->library('db_session'); + + // Load the configuration + $this->ci->load->config('auth_ldap'); + + // Load the language file + // $this->ci->lang->load('auth_ldap'); + + $this->_init(); + } + + + /** + * @access private + * @return void + */ + private function _init() { + + // Verify that the LDAP extension has been loaded/built-in + // No sense continuing if we can't + if (! function_exists('ldap_connect')) { + show_error('LDAP functionality not present. Either load the module ldap php module or use a php with ldap support compiled in.'); + log_message('error', 'LDAP functionality not present in php.'); + } + + $this->hosts = $this->ci->config->item('hosts'); + $this->ports = $this->ci->config->item('ports'); + $this->basedn = $this->ci->config->item('basedn'); + $this->account_ou = $this->ci->config->item('account_ou'); + $this->login_attribute = $this->ci->config->item('login_attribute'); + $this->use_ad = $this->ci->config->item('use_ad'); + $this->ad_domain = $this->ci->config->item('ad_domain'); + $this->proxy_user = $this->ci->config->item('proxy_user'); + $this->proxy_pass = $this->ci->config->item('proxy_pass'); + $this->roles = $this->ci->config->item('roles'); + $this->auditlog = $this->ci->config->item('auditlog'); + $this->member_attribute = $this->ci->config->item('member_attribute'); + } + + /** + * @access public + * @param string $username + * @param string $password + * @return bool + */ + function login($username, $password) { + /* + * For now just pass this along to _authenticate. We could do + * something else here before hand in the future. + */ + + $user_info = $this->_authenticate($username,$password); + if(empty($user_info['role'])) { + log_message('info', $username." has no role to play."); + //show_error($username.' succssfully authenticated, but is not allowed because the username was not found in an allowed access group.'); + return FALSE; + } + // Record the login + $this->_audit("Successful login: ".$user_info['cn']."(".$username.") from ".$this->ci->input->ip_address()); + + // Set the session data + $customdata = array('username' => $username, + 'cn' => $user_info['cn'], + 'role' => $user_info['role'], + 'logged_in' => TRUE); + + $this->ci->db_session->set_userdata($customdata); + return TRUE; + } + + /** + * @access public + * @return bool + */ + function is_authenticated() { + if($this->ci->db_session->userdata('logged_in')) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * @access public + */ + function logout() { + // Just set logged_in to FALSE and then destroy everything for good measure + $this->ci->db_session->set_userdata(array('logged_in' => FALSE)); + $this->ci->db_session->sess_destroy(); + } + + /** + * @access private + * @param string $msg + * @return bool + */ + private function _audit($msg){ + $date = date('Y/m/d H:i:s'); + if( ! file_put_contents($this->auditlog, $date.": ".$msg."\n",FILE_APPEND)) { + log_message('info', 'Error opening audit log '.$this->auditlog); + return FALSE; + } + return TRUE; + } + + /** + * @access private + * @param string $username + * @param string $password + * @return array + */ + private function _authenticate($username, $password) { + $needed_attrs = array('dn', 'cn', $this->login_attribute); + + foreach($this->hosts as $host) { + $this->ldapconn = ldap_connect($host); + if($this->ldapconn) { + break; + }else { + log_message('info', 'Error connecting to '.$uri); + } + } + // At this point, $this->ldapconn should be set. If not... DOOM! + if(! $this->ldapconn) { + log_message('error', "Couldn't connect to any LDAP servers. Bailing..."); + show_error('Error connecting to your LDAP server(s). Please check the connection and try again.'); + } + + // We've connected, now we can attempt the login... + + // These to ldap_set_options are needed for binding to AD properly + // They should also work with any modern LDAP service. + ldap_set_option($this->ldapconn, LDAP_OPT_REFERRALS, 0); + ldap_set_option($this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); + + // Find the DN of the user we are binding as + // If proxy_user and proxy_pass are set, use those, else bind anonymously + if($this->proxy_user) { + $bind = ldap_bind($this->ldapconn, $this->proxy_user, $this->proxy_pass); + }else { + $bind = ldap_bind($this->ldapconn); + } + + if(!$bind){ + log_message('error', 'Unable to perform anonymous/proxy bind'); + show_error('Unable to bind for user id lookup'); + } + + log_message('debug', 'Successfully bound to directory. Performing dn lookup for '.$username); + $filter = '('.$this->login_attribute.'='.$username.')'; + $search = ldap_search($this->ldapconn, $this->basedn, $filter, + array('dn', $this->login_attribute, 'cn')); + $entries = ldap_get_entries($this->ldapconn, $search); + + if(!isset($entries[0])){ + //User either does not exist or has no permissions + $this->_audit("Failed login attempt: ".$username." from ".$_SERVER['REMOTE_ADDR']); + return FALSE; + } + + $binddn = $entries[0]['dn']; + + // Now actually try to bind as the user + $bind = ldap_bind($this->ldapconn, $binddn, $password); + if(! $bind) { + $this->_audit("Failed login attempt: ".$username." from ".$_SERVER['REMOTE_ADDR']); + return FALSE; + } + $cn = $entries[0]['cn'][0]; + $dn = stripslashes($entries[0]['dn']); + $id = $entries[0][$this->login_attribute][0]; + + $get_role_arg = $id; + + return array('cn' => $cn, 'dn' => $dn, 'id' => $id, + 'role' => $this->_get_role($get_role_arg)); + } + + /** + * @access private + * @param string $str + * @param bool $for_dn + * @return string + */ + private function ldap_escape($str, $for_dn = false) { + /** + * This function courtesy of douglass_davis at earthlink dot net + * Posted in comments at + * http://php.net/manual/en/function.ldap-search.php on 2009/04/08 + */ + // see: + // RFC2254 + // http://msdn.microsoft.com/en-us/library/ms675768(VS.85).aspx + // http://www-03.ibm.com/systems/i/software/ldap/underdn.html + + if ($for_dn) + $metaChars = array(',','=', '+', '<','>',';', '\\', '"', '#'); + else + $metaChars = array('*', '(', ')', '\\', chr(0)); + + $quotedMetaChars = array(); + foreach ($metaChars as $key => $value) $quotedMetaChars[$key] = '\\'.str_pad(dechex(ord($value)), 2, '0'); + $str=str_replace($metaChars,$quotedMetaChars,$str); //replace them + return ($str); + } + + /** + * @access private + * @param string $username + * @return int + */ + private function _get_role($username) { + + $filter = '('.$this->member_attribute.'='.$username.')'; + $search = ldap_search($this->ldapconn, $this->basedn, $filter, array('cn')); + if(! $search ) { + log_message('error', "Error searching for group:".ldap_error($this->ldapconn)); + show_error('Couldn\'t find groups: '.ldap_error($this->ldapconn)); + } + $results = ldap_get_entries($this->ldapconn, $search); + if($results['count'] != 0) { + for($i = 0; $i < $results['count']; $i++) { + $role = array_search($results[$i]['cn'][0], $this->roles); + if($role !== FALSE) { + return $role; + } + } + } + return false; + } +} + +?> diff --git a/htdocs/application/libraries/carabiner.php b/htdocs/application/libraries/carabiner.php index a33bdcc..f8037cf 100644 --- a/htdocs/application/libraries/carabiner.php +++ b/htdocs/application/libraries/carabiner.php @@ -896,6 +896,12 @@ class Carabiner { $path = ($flag == 'css') ? $this->style_path : $this->script_path; $ref = ( $this->isURL($file_ref) ) ? $file_ref : realpath($path.$file_ref); + //hack for stikked themes + if(!file_exists($ref)){ + $path = ($flag == 'css') ? 'themes/default/css/' : $this->script_path; + $ref = ( $this->isURL($file_ref) ) ? $file_ref : realpath($path.$file_ref); + } + switch($flag){ case 'js': diff --git a/htdocs/application/libraries/db_session.php b/htdocs/application/libraries/db_session.php index 72e78d1..48db300 100644 --- a/htdocs/application/libraries/db_session.php +++ b/htdocs/application/libraries/db_session.php @@ -292,6 +292,15 @@ class DB_Session { $this->userdata['last_activity'] = $this->now; // format query array to update database $ud = $this->userdata; + + //fix for https://github.com/claudehohl/Stikked/issues/52 + if(!isset($ud['user_agent']) + || !isset($ud['ip_address']) + || !isset($ud['session_id'])) + { + return false; + } + $query_array = array( 'last_activity' => $ud['last_activity'], 'user_agent' => $ud['user_agent'], 'ip_address' => $ud['ip_address'] ); diff --git a/htdocs/application/libraries/finediff.php b/htdocs/application/libraries/finediff.php new file mode 100644 index 0000000..2e38b77 --- /dev/null +++ b/htdocs/application/libraries/finediff.php @@ -0,0 +1,688 @@ +copy->insert +* command (swap) for when the inserted segment is exactly the same +* as the deleted one, and with only a copy operation in between. +* TODO: How often this case occurs? Is it worth it? Can only +* be done as a postprocessing method (->optimize()?) +*/ +abstract class FineDiffOp { + abstract public function getFromLen(); + abstract public function getToLen(); + abstract public function getOpcode(); + } + +class FineDiffDeleteOp extends FineDiffOp { + public function __construct($len) { + $this->fromLen = $len; + } + public function getFromLen() { + return $this->fromLen; + } + public function getToLen() { + return 0; + } + public function getOpcode() { + if ( $this->fromLen === 1 ) { + return 'd'; + } + return "d{$this->fromLen}"; + } + } + +class FineDiffInsertOp extends FineDiffOp { + public function __construct($text) { + $this->text = $text; + } + public function getFromLen() { + return 0; + } + public function getToLen() { + return strlen($this->text); + } + public function getText() { + return $this->text; + } + public function getOpcode() { + $to_len = strlen($this->text); + if ( $to_len === 1 ) { + return "i:{$this->text}"; + } + return "i{$to_len}:{$this->text}"; + } + } + +class FineDiffReplaceOp extends FineDiffOp { + public function __construct($fromLen, $text) { + $this->fromLen = $fromLen; + $this->text = $text; + } + public function getFromLen() { + return $this->fromLen; + } + public function getToLen() { + return strlen($this->text); + } + public function getText() { + return $this->text; + } + public function getOpcode() { + if ( $this->fromLen === 1 ) { + $del_opcode = 'd'; + } + else { + $del_opcode = "d{$this->fromLen}"; + } + $to_len = strlen($this->text); + if ( $to_len === 1 ) { + return "{$del_opcode}i:{$this->text}"; + } + return "{$del_opcode}i{$to_len}:{$this->text}"; + } + } + +class FineDiffCopyOp extends FineDiffOp { + public function __construct($len) { + $this->len = $len; + } + public function getFromLen() { + return $this->len; + } + public function getToLen() { + return $this->len; + } + public function getOpcode() { + if ( $this->len === 1 ) { + return 'c'; + } + return "c{$this->len}"; + } + public function increase($size) { + return $this->len += $size; + } + } + +/** +* FineDiff ops +* +* Collection of ops +*/ +class FineDiffOps { + public function appendOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' ) { + $edits[] = new FineDiffCopyOp($from_len); + } + else if ( $opcode === 'd' ) { + $edits[] = new FineDiffDeleteOp($from_len); + } + else /* if ( $opcode === 'i' ) */ { + $edits[] = new FineDiffInsertOp(substr($from, $from_offset, $from_len)); + } + } + public $edits = array(); + } + +/** +* FineDiff class +* +* TODO: Document +* +*/ +class FineDiff { + + /**------------------------------------------------------------------------ + * + * Public section + * + */ + + /** + * Constructor + * ... + * The $granularityStack allows FineDiff to be configurable so that + * a particular stack tailored to the specific content of a document can + * be passed. + */ + public function __construct($from_text = '', $to_text = '', $granularityStack = null) { + // setup stack for generic text documents by default + $this->granularityStack = $granularityStack ? $granularityStack : FineDiff::$characterGranularity; + $this->edits = array(); + $this->from_text = $from_text; + $this->doDiff($from_text, $to_text); + } + + public function getOps() { + return $this->edits; + } + + public function getOpcodes() { + $opcodes = array(); + foreach ( $this->edits as $edit ) { + $opcodes[] = $edit->getOpcode(); + } + return implode('', $opcodes); + } + + public function renderDiffToHTML() { + $in_offset = 0; + ob_start(); + foreach ( $this->edits as $edit ) { + $n = $edit->getFromLen(); + if ( $edit instanceof FineDiffCopyOp ) { + FineDiff::renderDiffToHTMLFromOpcode('c', $this->from_text, $in_offset, $n); + } + else if ( $edit instanceof FineDiffDeleteOp ) { + FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); + } + else if ( $edit instanceof FineDiffInsertOp ) { + FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); + } + else /* if ( $edit instanceof FineDiffReplaceOp ) */ { + FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); + FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); + } + $in_offset += $n; + } + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Return an opcodes string describing the diff between a "From" and a + * "To" string + */ + public static function getDiffOpcodes($from, $to, $granularities = null) { + $diff = new FineDiff($from, $to, $granularities); + return $diff->getOpcodes(); + } + + /**------------------------------------------------------------------------ + * Return an iterable collection of diff ops from an opcodes string + */ + public static function getDiffOpsFromOpcodes($opcodes) { + $diffops = new FineDiffOps(); + FineDiff::renderFromOpcodes(null, $opcodes, array($diffops,'appendOpcode')); + return $diffops->edits; + } + + /**------------------------------------------------------------------------ + * Re-create the "To" string from the "From" string and an "Opcodes" string + */ + public static function renderToTextFromOpcodes($from, $opcodes) { + ob_start(); + FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderToTextFromOpcode')); + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Render the diff to an HTML string + */ + public static function renderDiffToHTMLFromOpcodes($from, $opcodes) { + ob_start(); + FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderDiffToHTMLFromOpcode')); + return ob_get_clean(); + } + + /**------------------------------------------------------------------------ + * Generic opcodes parser, user must supply callback for handling + * single opcode + */ + public static function renderFromOpcodes($from, $opcodes, $callback) { + if ( !is_callable($callback) ) { + return; + } + $opcodes_len = strlen($opcodes); + $from_offset = $opcodes_offset = 0; + while ( $opcodes_offset < $opcodes_len ) { + $opcode = substr($opcodes, $opcodes_offset, 1); + $opcodes_offset++; + $n = intval(substr($opcodes, $opcodes_offset)); + if ( $n ) { + $opcodes_offset += strlen(strval($n)); + } + else { + $n = 1; + } + if ( $opcode === 'c' ) { // copy n characters from source + call_user_func($callback, 'c', $from, $from_offset, $n, ''); + $from_offset += $n; + } + else if ( $opcode === 'd' ) { // delete n characters from source + call_user_func($callback, 'd', $from, $from_offset, $n, ''); + $from_offset += $n; + } + else /* if ( $opcode === 'i' ) */ { // insert n characters from opcodes + call_user_func($callback, 'i', $opcodes, $opcodes_offset + 1, $n); + $opcodes_offset += 1 + $n; + } + } + } + + /** + * Stock granularity stacks and delimiters + */ + + const paragraphDelimiters = "\n\r"; + public static $paragraphGranularity = array( + FineDiff::paragraphDelimiters + ); + const sentenceDelimiters = ".\n\r"; + public static $sentenceGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters + ); + const wordDelimiters = " \t.\n\r"; + public static $wordGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters, + FineDiff::wordDelimiters + ); + const characterDelimiters = ""; + public static $characterGranularity = array( + FineDiff::paragraphDelimiters, + FineDiff::sentenceDelimiters, + FineDiff::wordDelimiters, + FineDiff::characterDelimiters + ); + + public static $textStack = array( + ".", + " \t.\n\r", + "" + ); + + /**------------------------------------------------------------------------ + * + * Private section + * + */ + + /** + * Entry point to compute the diff. + */ + private function doDiff($from_text, $to_text) { + $this->last_edit = false; + $this->stackpointer = 0; + $this->from_text = $from_text; + $this->from_offset = 0; + // can't diff without at least one granularity specifier + if ( empty($this->granularityStack) ) { + return; + } + $this->_processGranularity($from_text, $to_text); + } + + /** + * This is the recursive function which is responsible for + * handling/increasing granularity. + * + * Incrementally increasing the granularity is key to compute the + * overall diff in a very efficient way. + */ + private function _processGranularity($from_segment, $to_segment) { + $delimiters = $this->granularityStack[$this->stackpointer++]; + $has_next_stage = $this->stackpointer < count($this->granularityStack); + foreach ( FineDiff::doFragmentDiff($from_segment, $to_segment, $delimiters) as $fragment_edit ) { + // increase granularity + if ( $fragment_edit instanceof FineDiffReplaceOp && $has_next_stage ) { + $this->_processGranularity( + substr($this->from_text, $this->from_offset, $fragment_edit->getFromLen()), + $fragment_edit->getText() + ); + } + // fuse copy ops whenever possible + else if ( $fragment_edit instanceof FineDiffCopyOp && $this->last_edit instanceof FineDiffCopyOp ) { + $this->edits[count($this->edits)-1]->increase($fragment_edit->getFromLen()); + $this->from_offset += $fragment_edit->getFromLen(); + } + else { + /* $fragment_edit instanceof FineDiffCopyOp */ + /* $fragment_edit instanceof FineDiffDeleteOp */ + /* $fragment_edit instanceof FineDiffInsertOp */ + $this->edits[] = $this->last_edit = $fragment_edit; + $this->from_offset += $fragment_edit->getFromLen(); + } + } + $this->stackpointer--; + } + + /** + * This is the core algorithm which actually perform the diff itself, + * fragmenting the strings as per specified delimiters. + * + * This function is naturally recursive, however for performance purpose + * a local job queue is used instead of outright recursivity. + */ + private static function doFragmentDiff($from_text, $to_text, $delimiters) { + // Empty delimiter means character-level diffing. + // In such case, use code path optimized for character-level + // diffing. + if ( empty($delimiters) ) { + return FineDiff::doCharDiff($from_text, $to_text); + } + + $result = array(); + + // fragment-level diffing + $from_text_len = strlen($from_text); + $to_text_len = strlen($to_text); + $from_fragments = FineDiff::extractFragments($from_text, $delimiters); + $to_fragments = FineDiff::extractFragments($to_text, $delimiters); + + $jobs = array(array(0, $from_text_len, 0, $to_text_len)); + + $cached_array_keys = array(); + + while ( $job = array_pop($jobs) ) { + + // get the segments which must be diff'ed + list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; + + // catch easy cases first + $from_segment_length = $from_segment_end - $from_segment_start; + $to_segment_length = $to_segment_end - $to_segment_start; + if ( !$from_segment_length || !$to_segment_length ) { + if ( $from_segment_length ) { + $result[$from_segment_start * 4] = new FineDiffDeleteOp($from_segment_length); + } + else if ( $to_segment_length ) { + $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_length)); + } + continue; + } + + // find longest copy operation for the current segments + $best_copy_length = 0; + + $from_base_fragment_index = $from_segment_start; + + $cached_array_keys_for_current_segment = array(); + + while ( $from_base_fragment_index < $from_segment_end ) { + $from_base_fragment = $from_fragments[$from_base_fragment_index]; + $from_base_fragment_length = strlen($from_base_fragment); + // performance boost: cache array keys + if ( !isset($cached_array_keys_for_current_segment[$from_base_fragment]) ) { + if ( !isset($cached_array_keys[$from_base_fragment]) ) { + $to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true); + } + else { + $to_all_fragment_indices = $cached_array_keys[$from_base_fragment]; + } + // get only indices which falls within current segment + if ( $to_segment_start > 0 || $to_segment_end < $to_text_len ) { + $to_fragment_indices = array(); + foreach ( $to_all_fragment_indices as $to_fragment_index ) { + if ( $to_fragment_index < $to_segment_start ) { continue; } + if ( $to_fragment_index >= $to_segment_end ) { break; } + $to_fragment_indices[] = $to_fragment_index; + } + $cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices; + } + else { + $to_fragment_indices = $to_all_fragment_indices; + } + } + else { + $to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment]; + } + // iterate through collected indices + foreach ( $to_fragment_indices as $to_base_fragment_index ) { + $fragment_index_offset = $from_base_fragment_length; + // iterate until no more match + for (;;) { + $fragment_from_index = $from_base_fragment_index + $fragment_index_offset; + if ( $fragment_from_index >= $from_segment_end ) { + break; + } + $fragment_to_index = $to_base_fragment_index + $fragment_index_offset; + if ( $fragment_to_index >= $to_segment_end ) { + break; + } + if ( $from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index] ) { + break; + } + $fragment_length = strlen($from_fragments[$fragment_from_index]); + $fragment_index_offset += $fragment_length; + } + if ( $fragment_index_offset > $best_copy_length ) { + $best_copy_length = $fragment_index_offset; + $best_from_start = $from_base_fragment_index; + $best_to_start = $to_base_fragment_index; + } + } + $from_base_fragment_index += strlen($from_base_fragment); + // If match is larger than half segment size, no point trying to find better + // TODO: Really? + if ( $best_copy_length >= $from_segment_length / 2) { + break; + } + // no point to keep looking if what is left is less than + // current best match + if ( $from_base_fragment_index + $best_copy_length >= $from_segment_end ) { + break; + } + } + + if ( $best_copy_length ) { + $jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start); + $result[$best_from_start * 4 + 2] = new FineDiffCopyOp($best_copy_length); + $jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end); + } + else { + $result[$from_segment_start * 4 ] = new FineDiffReplaceOp($from_segment_length, substr($to_text, $to_segment_start, $to_segment_length)); + } + } + + ksort($result, SORT_NUMERIC); + return array_values($result); + } + + /** + * Perform a character-level diff. + * + * The algorithm is quite similar to doFragmentDiff(), except that + * the code path is optimized for character-level diff -- strpos() is + * used to find out the longest common subequence of characters. + * + * We try to find a match using the longest possible subsequence, which + * is at most the length of the shortest of the two strings, then incrementally + * reduce the size until a match is found. + * + * I still need to study more the performance of this function. It + * appears that for long strings, the generic doFragmentDiff() is more + * performant. For word-sized strings, doCharDiff() is somewhat more + * performant. + */ + private static function doCharDiff($from_text, $to_text) { + $result = array(); + $jobs = array(array(0, strlen($from_text), 0, strlen($to_text))); + while ( $job = array_pop($jobs) ) { + // get the segments which must be diff'ed + list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; + $from_segment_len = $from_segment_end - $from_segment_start; + $to_segment_len = $to_segment_end - $to_segment_start; + + // catch easy cases first + if ( !$from_segment_len || !$to_segment_len ) { + if ( $from_segment_len ) { + $result[$from_segment_start * 4 + 0] = new FineDiffDeleteOp($from_segment_len); + } + else if ( $to_segment_len ) { + $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_len)); + } + continue; + } + if ( $from_segment_len >= $to_segment_len ) { + $copy_len = $to_segment_len; + while ( $copy_len ) { + $to_copy_start = $to_segment_start; + $to_copy_start_max = $to_segment_end - $copy_len; + while ( $to_copy_start <= $to_copy_start_max ) { + $from_copy_start = strpos(substr($from_text, $from_segment_start, $from_segment_len), substr($to_text, $to_copy_start, $copy_len)); + if ( $from_copy_start !== false ) { + $from_copy_start += $from_segment_start; + break 2; + } + $to_copy_start++; + } + $copy_len--; + } + } + else { + $copy_len = $from_segment_len; + while ( $copy_len ) { + $from_copy_start = $from_segment_start; + $from_copy_start_max = $from_segment_end - $copy_len; + while ( $from_copy_start <= $from_copy_start_max ) { + $to_copy_start = strpos(substr($to_text, $to_segment_start, $to_segment_len), substr($from_text, $from_copy_start, $copy_len)); + if ( $to_copy_start !== false ) { + $to_copy_start += $to_segment_start; + break 2; + } + $from_copy_start++; + } + $copy_len--; + } + } + // match found + if ( $copy_len ) { + $jobs[] = array($from_segment_start, $from_copy_start, $to_segment_start, $to_copy_start); + $result[$from_copy_start * 4 + 2] = new FineDiffCopyOp($copy_len); + $jobs[] = array($from_copy_start + $copy_len, $from_segment_end, $to_copy_start + $copy_len, $to_segment_end); + } + // no match, so delete all, insert all + else { + $result[$from_segment_start * 4] = new FineDiffReplaceOp($from_segment_len, substr($to_text, $to_segment_start, $to_segment_len)); + } + } + ksort($result, SORT_NUMERIC); + return array_values($result); + } + + /** + * Efficiently fragment the text into an array according to + * specified delimiters. + * No delimiters means fragment into single character. + * The array indices are the offset of the fragments into + * the input string. + * A sentinel empty fragment is always added at the end. + * Careful: No check is performed as to the validity of the + * delimiters. + */ + private static function extractFragments($text, $delimiters) { + // special case: split into characters + if ( empty($delimiters) ) { + $chars = str_split($text, 1); + $chars[strlen($text)] = ''; + return $chars; + } + $fragments = array(); + $start = $end = 0; + for (;;) { + $end += strcspn($text, $delimiters, $end); + $end += strspn($text, $delimiters, $end); + if ( $end === $start ) { + break; + } + $fragments[$start] = substr($text, $start, $end - $start); + $start = $end; + } + $fragments[$start] = ''; + return $fragments; + } + + /** + * Stock opcode renderers + */ + private static function renderToTextFromOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' || $opcode === 'i' ) { + echo substr($from, $from_offset, $from_len); + } + } + + private static function renderDiffToHTMLFromOpcode($opcode, $from, $from_offset, $from_len) { + if ( $opcode === 'c' ) { + echo htmlentities(htmlentities(substr($from, $from_offset, $from_len))); + } + else if ( $opcode === 'd' ) { + $deletion = substr($from, $from_offset, $from_len); + if ( strcspn($deletion, " \n\r") === 0 ) { + $deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion); + } + echo '', htmlentities(htmlentities($deletion)), ''; + } + else /* if ( $opcode === 'i' ) */ { + echo '', htmlentities(htmlentities(substr($from, $from_offset, $from_len))), ''; + } + } + } + diff --git a/htdocs/application/libraries/geshi/contrib/aliased.php b/htdocs/application/libraries/geshi/contrib/aliased.php index e57b495..cee3128 100644 --- a/htdocs/application/libraries/geshi/contrib/aliased.php +++ b/htdocs/application/libraries/geshi/contrib/aliased.php @@ -13,7 +13,7 @@ * aliased.php/file.name.ext. * * @author Ross Golder - * @version $Id: aliased.php 881 2007-01-10 11:14:38Z oracleshinoda $ + * @version $Id: aliased.php 2533 2012-08-15 18:49:04Z benbe $ */ // Your config here @@ -29,19 +29,18 @@ $path = SOURCE_ROOT.$_SERVER['PATH_INFO']; $base_path_len = strlen(SOURCE_ROOT); $real_path = realpath($path); if(strncmp($real_path, SOURCE_ROOT, $base_path_len)) { - exit("Stop that."); + exit("Access outside acceptable path."); } // Check file exists if(!file_exists($path)) { - exit("File not found ($path)."); + exit("File not found ($path)."); } -// Gather contents -$contents = file_get_contents($path); - // Prepare GeSHi instance -$geshi =& new GeSHi($contents, "PHP"); +$geshi = new GeSHi(); +$geshi->set_language('text'); +$geshi->load_from_file($path); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->enable_classes(); $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 10); @@ -50,7 +49,7 @@ $geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monosp $geshi->set_code_style('color: #000020;', 'color: #000020;'); $geshi->set_link_styles(GESHI_LINK, 'color: #000060;'); $geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;'); -$geshi->set_header_content('Source code viewer'); +$geshi->set_header_content('Source code viewer - ' . $path . ' - ' . $geshi->get_language_name()); $geshi->set_header_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;'); $geshi->set_footer_content('Parsed in